From 245384ef509c462edf50461a9d4558de13c10a78 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sat, 18 Apr 2026 00:39:06 +0800 Subject: [PATCH] fix(room): handle Enter/Tab mention selection directly in ChatInputArea.handleKeyDown Previous approach used a native addEventListener in MentionPopover to handle Enter, but it wasn't firing reliably due to complex event ordering. New approach: ChatInputArea.handleKeyDown detects @ mention directly from the DOM (not React state), reads the selected item from module-level mentionVisibleRef/mentionSelectedIdxRef, and performs the insertion directly. This completely bypasses the native listener timing issues. --- src/components/room/RoomChatPanel.tsx | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/components/room/RoomChatPanel.tsx b/src/components/room/RoomChatPanel.tsx index af6ead6..f8a7632 100644 --- a/src/components/room/RoomChatPanel.tsx +++ b/src/components/room/RoomChatPanel.tsx @@ -157,7 +157,35 @@ const ChatInputArea = memo(function ChatInputArea({ if (hasMention && MENTION_POPOVER_KEYS.includes(e.key) && !e.ctrlKey && !e.shiftKey) { e.preventDefault(); - // Enter/Tab is handled by MentionPopover's native keydown listener + // Enter/Tab: do mention selection directly here using module-level refs + if ((e.key === 'Enter' || e.key === 'Tab')) { + const suggestion = mentionVisibleRef.current[mentionSelectedIdxRef.current]; + if (suggestion && suggestion.type === 'item') { + const textarea = textareaRef.current; + if (!textarea) return; + const cursorPos = textarea.selectionStart; + const textBefore = textarea.value.substring(0, cursorPos); + const atMatch = textBefore.match(MENTION_PATTERN); + if (!atMatch) return; + const [fullMatch] = atMatch; + const startPos = cursorPos - fullMatch.length; + const before = textarea.value.substring(0, startPos); + const after = textarea.value.substring(cursorPos); + const html = buildMentionHtml(suggestion.category!, suggestion.mentionId!, suggestion.label); + const spacer = ' '; + const newValue = before + html + spacer + after; + const newCursorPos = startPos + html.length + spacer.length; + onDraftChange(newValue); + setShowMentionPopover(false); + setTimeout(() => { + if (textareaRef.current) { + textareaRef.current.value = newValue; + textareaRef.current.setSelectionRange(newCursorPos, newCursorPos); + textareaRef.current.focus(); + } + }, 0); + } + } return; }