From 8e773ffcbe0a74776cb54852814e5c6732d251c1 Mon Sep 17 00:00:00 2001 From: Felix <24791380+vcfgv@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:41:50 +0800 Subject: [PATCH 1/2] fix(chat-input): prevent sending message during composition --- src/pages/Chat/ChatInput.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pages/Chat/ChatInput.tsx b/src/pages/Chat/ChatInput.tsx index eddca86..9d86e17 100644 --- a/src/pages/Chat/ChatInput.tsx +++ b/src/pages/Chat/ChatInput.tsx @@ -59,6 +59,7 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp const [attachments, setAttachments] = useState([]); const textareaRef = useRef(null); const fileInputRef = useRef(null); + const isComposingRef = useRef(false); // Auto-resize textarea useEffect(() => { @@ -99,6 +100,10 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { + const nativeEvent = e.nativeEvent as KeyboardEvent; + if (isComposingRef.current || nativeEvent.isComposing || nativeEvent.keyCode === 229) { + return; + } e.preventDefault(); handleSend(); } @@ -221,6 +226,12 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={handleKeyDown} + onCompositionStart={() => { + isComposingRef.current = true; + }} + onCompositionEnd={() => { + isComposingRef.current = false; + }} onPaste={handlePaste} placeholder={disabled ? 'Gateway not connected...' : 'Message (Enter to send, Shift+Enter for new line)'} disabled={disabled} From ec5eac9f998abf7fd82ada2ceda69ba5d4a1faa0 Mon Sep 17 00:00:00 2001 From: Felix <24791380+vcfgv@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:03:06 +0800 Subject: [PATCH 2/2] feat: chat stop button and tool typing indicator --- src/pages/Chat/ChatInput.tsx | 14 ++++++-- src/pages/Chat/index.tsx | 2 ++ src/stores/chat.ts | 64 ++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/pages/Chat/ChatInput.tsx b/src/pages/Chat/ChatInput.tsx index 9d86e17..c26d58b 100644 --- a/src/pages/Chat/ChatInput.tsx +++ b/src/pages/Chat/ChatInput.tsx @@ -19,6 +19,7 @@ export interface ChatAttachment { interface ChatInputProps { onSend: (text: string, attachments?: ChatAttachment[]) => void; + onStop?: () => void; disabled?: boolean; sending?: boolean; } @@ -54,7 +55,7 @@ function fileToAttachment(file: File): Promise { }); } -export function ChatInput({ onSend, disabled = false, sending = false }: ChatInputProps) { +export function ChatInput({ onSend, onStop, disabled = false, sending = false }: ChatInputProps) { const [input, setInput] = useState(''); const [attachments, setAttachments] = useState([]); const textareaRef = useRef(null); @@ -86,6 +87,7 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp }, []); const canSend = (input.trim() || attachments.length > 0) && !disabled && !sending; + const canStop = sending && !disabled && !!onStop; const handleSend = useCallback(() => { if (!canSend) return; @@ -97,6 +99,11 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp } }, [input, attachments, canSend, onSend]); + const handleStop = useCallback(() => { + if (!canStop) return; + onStop?.(); + }, [canStop, onStop]); + const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { @@ -242,11 +249,12 @@ export function ChatInput({ onSend, disabled = false, sending = false }: ChatInp {/* Send Button */}