Fix/chinese ime enter send (#21)

* fix: prevent message send when Chinese IME is composing

- Add e.isComposing check to Enter key handler in ChatInput
- This prevents accidental message sending when pressing Enter to confirm Chinese/Japanese input
- Fixes issue where messages would be sent prematurely when using IME composition

Refs: #issue

* fix: prevent message send when Chinese IME is composing

- Add isComposing state to track IME composition status
- Add onCompositionStart/onCompositionEnd event handlers to textarea
- Check isComposing state in Enter key handler to prevent accidental send

This fixes the issue where pressing Enter to confirm Chinese/Japanese input
would prematurely send the message instead of just confirming the IME selection.

The fix uses explicit composition state tracking instead of relying solely on
e.isComposing, which provides more reliable cross-browser behavior.

---------

Co-authored-by: 代码专家 <coder@openclaw.ai>
This commit is contained in:
ppzzxx
2026-03-08 20:15:11 +08:00
committed by GitHub
parent 463a73b6ab
commit c9dc2a671a

View File

@@ -98,6 +98,7 @@ export function ChatInput({ onSend, onNewSession, onAbort, isGenerating, disable
const [files, setFiles] = useState<FileAttachment[]>([]); const [files, setFiles] = useState<FileAttachment[]>([]);
const [isDragOver, setIsDragOver] = useState(false); const [isDragOver, setIsDragOver] = useState(false);
const [showPreview, setShowPreview] = useState(() => localStorage.getItem('pinchchat-md-preview') === '1'); const [showPreview, setShowPreview] = useState(() => localStorage.getItem('pinchchat-md-preview') === '1');
const [isComposing, setIsComposing] = useState(false);
const [showSlash, setShowSlash] = useState(false); const [showSlash, setShowSlash] = useState(false);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
@@ -207,6 +208,8 @@ export function ChatInput({ onSend, onNewSession, onAbort, isGenerating, disable
const handleKeyDown = (e: React.KeyboardEvent) => { const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
// Prevent sending when IME is composing (e.g., Chinese/Japanese input)
if (isComposing) return;
if (sendOnEnter) { if (sendOnEnter) {
// Enter sends, Shift+Enter for newline // Enter sends, Shift+Enter for newline
if (!e.shiftKey && !e.ctrlKey && !e.metaKey) { if (!e.shiftKey && !e.ctrlKey && !e.metaKey) {
@@ -383,6 +386,8 @@ export function ChatInput({ onSend, onNewSession, onAbort, isGenerating, disable
value={text} value={text}
onChange={(e) => { setText(e.target.value); setShowSlash(shouldShowSlashMenu(e.target.value)); }} onChange={(e) => { setText(e.target.value); setShowSlash(shouldShowSlashMenu(e.target.value)); }}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onCompositionStart={() => setIsComposing(true)}
onCompositionEnd={() => setIsComposing(false)}
onPaste={handlePaste} onPaste={handlePaste}
placeholder={t('chat.inputPlaceholder')} placeholder={t('chat.inputPlaceholder')}
aria-label={t('chat.inputLabel')} aria-label={t('chat.inputLabel')}