From f51eb714cf58fddd9ef23a799cca84caa1492d22 Mon Sep 17 00:00:00 2001 From: zzzhizhi <77013105+zzzhizhia@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:50:16 +0800 Subject: [PATCH 1/2] fix: prevent Enter from sending message during IME composition When using a Chinese IME to type English, pressing Enter to confirm the composition was incorrectly sending the chat message. This happened because some browsers fire compositionend before keydown, resetting the isComposing ref before the keydown handler could check it. - Add e.nativeEvent.isComposing check as a secondary guard - Delay resetting isComposingRef in onCompositionEnd via setTimeout(0) so the keydown handler for the same keystroke still sees it as true - Apply the same IME guard to note-editor and transcript-viewer --- components/ai-chat.tsx | 11 +++++++++-- components/note-editor.tsx | 2 +- components/transcript-viewer.tsx | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/components/ai-chat.tsx b/components/ai-chat.tsx index 346204e..905722e 100644 --- a/components/ai-chat.tsx +++ b/components/ai-chat.tsx @@ -1088,7 +1088,7 @@ export function AIChat({ }, [isLoading, sendMessage, followUpQuestions]); const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" && !e.shiftKey && !isComposingRef.current) { + if (e.key === "Enter" && !e.shiftKey && !isComposingRef.current && !e.nativeEvent.isComposing) { e.preventDefault(); sendMessage(); } @@ -1272,7 +1272,14 @@ export function AIChat({ isComposingRef.current = true; }} onCompositionEnd={() => { - isComposingRef.current = false; + // Delay resetting the flag so the keydown handler for the + // same Enter keystroke still sees isComposing as true. + // In some browsers (e.g. Chrome with Chinese IME), + // compositionend fires before keydown for the Enter key + // that confirms the composition. + setTimeout(() => { + isComposingRef.current = false; + }, 0); }} placeholder="Ask about the video..." className="resize-none rounded-[20px] text-xs bg-neutral-100 border-[#ebecee] pr-11" diff --git a/components/note-editor.tsx b/components/note-editor.tsx index 6978efd..1c41c2b 100644 --- a/components/note-editor.tsx +++ b/components/note-editor.tsx @@ -104,7 +104,7 @@ export function NoteEditor({ selectedText, metadata, currentTime, onSave }: Note }, [originalQuote]); const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !e.nativeEvent.isComposing) { e.preventDefault(); handleSave(); } diff --git a/components/transcript-viewer.tsx b/components/transcript-viewer.tsx index b55a71f..14b697d 100644 --- a/components/transcript-viewer.tsx +++ b/components/transcript-viewer.tsx @@ -578,7 +578,7 @@ export function TranscriptViewer({ value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} onKeyDown={(e) => { - if (e.key === 'Enter') navigateSearch('next'); + if (e.key === 'Enter' && !e.nativeEvent.isComposing) navigateSearch('next'); if (e.key === 'Escape') { setIsSearchOpen(false); setSearchQuery(""); From 28dfd7c24ebb4ccf88c11c0449b1c6e36469a1f4 Mon Sep 17 00:00:00 2001 From: zzzhizhi <77013105+zzzhizhia@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:00:34 +0800 Subject: [PATCH 2/2] fix: clear pending composition timeout to prevent race condition --- components/ai-chat.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/ai-chat.tsx b/components/ai-chat.tsx index 905722e..c9c0726 100644 --- a/components/ai-chat.tsx +++ b/components/ai-chat.tsx @@ -155,6 +155,7 @@ export function AIChat({ const followUpQuestionsRef = useRef([]); const followUpRequestIdRef = useRef(0); const isComposingRef = useRef(false); + const compositionTimeoutRef = useRef>(undefined); useEffect(() => { const viewport = scrollViewportRef.current; @@ -1269,6 +1270,7 @@ export function AIChat({ onChange={(e) => setInput(e.target.value)} onKeyDown={handleKeyDown} onCompositionStart={() => { + clearTimeout(compositionTimeoutRef.current); isComposingRef.current = true; }} onCompositionEnd={() => { @@ -1277,7 +1279,7 @@ export function AIChat({ // In some browsers (e.g. Chrome with Chinese IME), // compositionend fires before keydown for the Enter key // that confirms the composition. - setTimeout(() => { + compositionTimeoutRef.current = setTimeout(() => { isComposingRef.current = false; }, 0); }}