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.
This commit is contained in:
ZhenYi 2026-04-18 00:39:06 +08:00
parent b8a61b0802
commit 245384ef50

View File

@ -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;
}