From f8bf31a6886fc144ae04d92675a2bf94df02b682 Mon Sep 17 00:00:00 2001 From: dimakis Date: Mon, 6 Apr 2026 20:26:57 +0100 Subject: [PATCH 1/4] feat(ui): add command strip above chat input Move /, +, and mic buttons into a command strip row above the textarea. Branch pill moves from the header to the command strip (center-left). Mic behavior: - Idle (no text): mic sits right-aligned in command strip, above send - Has text: mic shifts left next to + in command strip, send button appears - No voice: disabled send button shown instead Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/ChatInput.tsx | 106 ++++++++++++++------------ frontend/src/pages/ChatView.tsx | 10 +-- frontend/src/styles/global.css | 43 ++++++++--- 3 files changed, 96 insertions(+), 63 deletions(-) diff --git a/frontend/src/components/ChatInput.tsx b/frontend/src/components/ChatInput.tsx index d92ef93..4960944 100644 --- a/frontend/src/components/ChatInput.tsx +++ b/frontend/src/components/ChatInput.tsx @@ -22,6 +22,8 @@ interface Props { initialText?: string; cwd?: string; voice?: UseVoiceReturn; + branch?: string; + isWorktree?: boolean; } export function ChatInput({ @@ -32,6 +34,8 @@ export function ChatInput({ initialText, cwd, voice, + branch, + isWorktree, }: Props) { const [text, setText] = useState(initialText || ''); const [images, setImages] = useState([]); @@ -150,6 +154,24 @@ export function ChatInput({ const canSend = text.trim() || images.length > 0; + const micProps = voice + ? { + available: voice.available, + recording: voice.recording, + transcribing: voice.transcribing, + micBlocked: voice.micBlocked, + onRecordStart: voice.startRecording, + onRecordStop: async () => { + const transcript = await voice.stopRecording(); + if (transcript) { + setText((prev) => (prev ? `${prev} ${transcript}` : transcript)); + textareaRef.current?.focus(); + } + }, + onRecordCancel: voice.cancelRecording, + } + : null; + function handleInterrupt() { if (!onInterrupt) return; const trimmed = text.trim(); @@ -191,54 +213,44 @@ export function ChatInput({ {voice?.recording && voice.partialTranscript && (
{voice.partialTranscript}
)} -
-
- - - -
- {voice && ( - { - const transcript = await voice.stopRecording(); - if (transcript) { - setText((prev) => (prev ? `${prev} ${transcript}` : transcript)); - textareaRef.current?.focus(); - } - }} - onRecordCancel={voice.cancelRecording} - /> +
+ + + + {micProps && canSend && } + {branch && ( + + {branch} + )} + {micProps && !canSend && } +
+