diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 4a830f8..ddde90f 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -36,6 +36,9 @@ import { Badge } from '@/components/ui/badge' const STORAGE_KEY = 'autocoder-selected-project' const VIEW_MODE_KEY = 'autocoder-view-mode' +// Bottom padding for main content when debug panel is collapsed (40px header + 8px margin) +const COLLAPSED_DEBUG_PANEL_CLEARANCE = 48 + function App() { // Initialize selected project from localStorage const [selectedProject, setSelectedProject] = useState(() => { @@ -331,7 +334,7 @@ function App() { {/* Main Content */}
{!selectedProject ? (
diff --git a/ui/src/components/AssistantChat.tsx b/ui/src/components/AssistantChat.tsx index a2d7ba8..a9d8b5f 100644 --- a/ui/src/components/AssistantChat.tsx +++ b/ui/src/components/AssistantChat.tsx @@ -12,6 +12,7 @@ import { useAssistantChat } from '../hooks/useAssistantChat' import { ChatMessage as ChatMessageComponent } from './ChatMessage' import { ConversationHistory } from './ConversationHistory' import type { ChatMessage } from '../lib/types' +import { isSubmitEnter } from '../lib/keyboard' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' @@ -134,8 +135,7 @@ export function AssistantChat({ } const handleKeyDown = (e: React.KeyboardEvent) => { - // Skip if composing (e.g., Japanese IME input) - if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { + if (isSubmitEnter(e)) { e.preventDefault() handleSend() } diff --git a/ui/src/components/ExpandProjectChat.tsx b/ui/src/components/ExpandProjectChat.tsx index 2eb30b3..d1ccd21 100644 --- a/ui/src/components/ExpandProjectChat.tsx +++ b/ui/src/components/ExpandProjectChat.tsx @@ -11,6 +11,7 @@ import { useExpandChat } from '../hooks/useExpandChat' import { ChatMessage } from './ChatMessage' import { TypingIndicator } from './TypingIndicator' import type { ImageAttachment } from '../lib/types' +import { isSubmitEnter } from '../lib/keyboard' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Card, CardContent } from '@/components/ui/card' @@ -88,8 +89,7 @@ export function ExpandProjectChat({ } const handleKeyDown = (e: React.KeyboardEvent) => { - // Skip if composing (e.g., Japanese IME input) - if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { + if (isSubmitEnter(e)) { e.preventDefault() handleSendMessage() } diff --git a/ui/src/components/FolderBrowser.tsx b/ui/src/components/FolderBrowser.tsx index 8641bdd..2e8171a 100644 --- a/ui/src/components/FolderBrowser.tsx +++ b/ui/src/components/FolderBrowser.tsx @@ -18,6 +18,7 @@ import { ArrowLeft, } from 'lucide-react' import * as api from '../lib/api' +import { isSubmitEnter } from '../lib/keyboard' import type { DirectoryEntry, DriveInfo } from '../lib/types' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' @@ -269,8 +270,7 @@ export function FolderBrowser({ onSelect, onCancel, initialPath }: FolderBrowser className="flex-1" autoFocus onKeyDown={(e) => { - // Skip if composing (e.g., Japanese IME input) - if (e.key === 'Enter' && !e.nativeEvent.isComposing) handleCreateFolder() + if (isSubmitEnter(e, false)) handleCreateFolder() if (e.key === 'Escape') { setIsCreatingFolder(false) setNewFolderName('') diff --git a/ui/src/components/SpecCreationChat.tsx b/ui/src/components/SpecCreationChat.tsx index 24d0c09..c96a1f2 100644 --- a/ui/src/components/SpecCreationChat.tsx +++ b/ui/src/components/SpecCreationChat.tsx @@ -12,6 +12,7 @@ import { ChatMessage } from './ChatMessage' import { QuestionOptions } from './QuestionOptions' import { TypingIndicator } from './TypingIndicator' import type { ImageAttachment } from '../lib/types' +import { isSubmitEnter } from '../lib/keyboard' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Card, CardContent } from '@/components/ui/card' @@ -127,8 +128,7 @@ export function SpecCreationChat({ } const handleKeyDown = (e: React.KeyboardEvent) => { - // Skip if composing (e.g., Japanese IME input) - if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { + if (isSubmitEnter(e)) { e.preventDefault() handleSendMessage() } diff --git a/ui/src/components/TerminalTabs.tsx b/ui/src/components/TerminalTabs.tsx index 760d69d..c53ff22 100644 --- a/ui/src/components/TerminalTabs.tsx +++ b/ui/src/components/TerminalTabs.tsx @@ -8,6 +8,7 @@ import { useState, useRef, useEffect, useCallback } from 'react' import { Plus, X } from 'lucide-react' import type { TerminalInfo } from '@/lib/types' +import { isSubmitEnter } from '@/lib/keyboard' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' @@ -96,8 +97,7 @@ export function TerminalTabs({ // Handle key events during editing const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { - // Skip if composing (e.g., Japanese IME input) - if (e.key === 'Enter' && !e.nativeEvent.isComposing) { + if (isSubmitEnter(e, false)) { e.preventDefault() submitEdit() } else if (e.key === 'Escape') { diff --git a/ui/src/lib/keyboard.ts b/ui/src/lib/keyboard.ts new file mode 100644 index 0000000..4e3cf71 --- /dev/null +++ b/ui/src/lib/keyboard.ts @@ -0,0 +1,38 @@ +/** + * Keyboard event utilities + * + * Helpers for handling keyboard events, particularly for IME-aware input handling. + */ + +/** + * Check if an Enter keypress should trigger form submission. + * + * Returns false during IME composition (e.g., Japanese, Chinese, Korean input) + * to prevent accidental submission while selecting characters. + * + * @param e - The keyboard event from React + * @param allowShiftEnter - If true, Shift+Enter returns false (for multiline input) + * @returns true if Enter should submit, false if it should be ignored + * + * @example + * // In a chat input (Shift+Enter for newline) + * if (isSubmitEnter(e)) { + * e.preventDefault() + * handleSend() + * } + * + * @example + * // In a single-line input (Enter always submits) + * if (isSubmitEnter(e, false)) { + * handleSubmit() + * } + */ +export function isSubmitEnter( + e: React.KeyboardEvent, + allowShiftEnter: boolean = true +): boolean { + if (e.key !== 'Enter') return false + if (allowShiftEnter && e.shiftKey) return false + if (e.nativeEvent.isComposing) return false + return true +}