diff --git a/src/components/room/MentionPopover.tsx b/src/components/room/MentionPopover.tsx index 5b4c82c..f2549d3 100644 --- a/src/components/room/MentionPopover.tsx +++ b/src/components/room/MentionPopover.tsx @@ -62,10 +62,6 @@ interface MentionPopoverProps { onSelect: (newValue: string, newCursorPosition: number) => void; textareaRef: React.RefObject; onOpenChange: (open: boolean) => void; - /** Mutable ref: write the confirm fn here so ChatInputArea can call it on Enter */ - onMentionConfirmRef?: React.MutableRefObject<() => void>; - /** Simpler alternative to onMentionConfirmRef — parent provides a callback directly */ - onMentionConfirm?: () => void; } // Category configuration with icons and colors @@ -377,8 +373,6 @@ export function MentionPopover({ onSelect, textareaRef, onOpenChange, - onMentionConfirmRef, - onMentionConfirm, }: MentionPopoverProps) { const [position, setPosition] = useState({ top: 0, left: 0 }); const [selectedIndex, setSelectedIndex] = useState(0); @@ -509,11 +503,20 @@ export function MentionPopover({ const handleSelect = useCallback( (suggestion: MentionSuggestion) => { - const ms = mentionStateRef.current; - if (!ms || suggestion.type !== 'item') return; + if (!textareaRef.current || suggestion.type !== 'item') return; + + const textarea = textareaRef.current; + const cursorPos = textarea.selectionStart; + const textBefore = textarea.value.substring(0, cursorPos); + const atMatch = textBefore.match(/@([^:@\s<]*)(:([^\s<]*))?$/); + 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 before = inputValue.slice(0, ms.startPos); - const after = inputValue.slice(cursorPosition); const html = buildMentionHtml( suggestion.category!, suggestion.mentionId!, @@ -521,23 +524,17 @@ export function MentionPopover({ ); const spacer = ' '; const newValue = before + html + spacer + after; - const newCursorPos = ms.startPos + html.length + spacer.length; + const newCursorPos = startPos + html.length + spacer.length; + onSelect(newValue, newCursorPos); closePopover(); }, - [inputValue, cursorPosition, onSelect, closePopover], + // eslint-disable-next-line react-hooks/exhaustive-deps + [onSelect, closePopover], ); handleSelectRef.current = handleSelect; - // Register confirm fn so ChatInputArea can call it on Enter - if (onMentionConfirmRef) { - onMentionConfirmRef.current = () => { - const item = visibleSuggestionsRef.current[selectedIndexRef.current]; - if (item) handleSelectRef.current(item); - }; - } - // Position calculation useEffect(() => { if (!mentionState || !textareaRef.current) return; diff --git a/src/components/room/RoomChatPanel.tsx b/src/components/room/RoomChatPanel.tsx index 4411e15..af6ead6 100644 --- a/src/components/room/RoomChatPanel.tsx +++ b/src/components/room/RoomChatPanel.tsx @@ -31,7 +31,6 @@ import { RoomThreadPanel } from './RoomThreadPanel'; const MENTION_PATTERN = /@([^:@\s]*)(:([^\s]*))?$/; const MENTION_POPOVER_KEYS = ['Enter', 'Tab', 'ArrowUp', 'ArrowDown']; -export const mentionConfirmRef = { current: (() => {}) as () => void }; export interface ChatInputAreaHandle { @@ -52,8 +51,6 @@ interface ChatInputAreaProps { draft: string; onDraftChange: (content: string) => void; onClearDraft: () => void; - /** Called when Enter is pressed with mention popover open — parent triggers mention selection */ - onMentionConfirm?: () => void; ref?: React.Ref; } @@ -71,7 +68,6 @@ const ChatInputArea = memo(function ChatInputArea({ draft, onDraftChange, onClearDraft, - onMentionConfirm, ref, }: ChatInputAreaProps) { const textareaRef = useRef(null); @@ -161,9 +157,7 @@ const ChatInputArea = memo(function ChatInputArea({ if (hasMention && MENTION_POPOVER_KEYS.includes(e.key) && !e.ctrlKey && !e.shiftKey) { e.preventDefault(); - if ((e.key === 'Enter' || e.key === 'Tab') && onMentionConfirm) { - onMentionConfirm(); - } + // Enter/Tab is handled by MentionPopover's native keydown listener return; } @@ -609,7 +603,6 @@ export function RoomChatPanel({ room, isAdmin, onClose, onDelete }: RoomChatPane draft={draft} onDraftChange={setDraft} onClearDraft={clearDraft} - onMentionConfirm={() => { console.log('[RoomChatPanel] onMentionConfirm called'); mentionConfirmRef.current(); }} />