From b96ef0342c50cc828b3d9155f4995cee2376fd41 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sat, 18 Apr 2026 00:57:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(room):=20render=20AI=20mentions=20as=20?= =?UTF-8?q?=F0=9F=A4=96=20button=20with=20click-to-insert=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AI mentions now render as "🤖 AI: name" buttons instead of plain text. Clicking a 🤖 button inserts the @ai:mention into the message input at the cursor position, enabling users to summon that AI model into the conversation. Implementation: - MessageMentions renders AI mentions as styled buttons with 🤖 icon - Click dispatches 'mention-click' CustomEvent on document - ChatInputArea listens and inserts the mention HTML at cursor --- src/components/room/MessageMentions.tsx | 34 ++++++++++++++++++++++++- src/components/room/RoomChatPanel.tsx | 29 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/components/room/MessageMentions.tsx b/src/components/room/MessageMentions.tsx index 324e6ef..51d2b5f 100644 --- a/src/components/room/MessageMentions.tsx +++ b/src/components/room/MessageMentions.tsx @@ -127,8 +127,40 @@ function renderNode( } if (node.type === 'mention') { const displayName = resolveName(node.mentionType, node.id, node.label); + const baseClass = mentionStyles[node.mentionType] ?? mentionStyles.user; + + if (node.mentionType === 'ai') { + return ( + + ); + } + return ( - + @{displayName} ); diff --git a/src/components/room/RoomChatPanel.tsx b/src/components/room/RoomChatPanel.tsx index e239cfb..2d07658 100644 --- a/src/components/room/RoomChatPanel.tsx +++ b/src/components/room/RoomChatPanel.tsx @@ -230,6 +230,35 @@ const ChatInputArea = memo(function ChatInputArea({ } }; + // Listen for mention-click events from message content (e.g. 🤖 AI button) + useEffect(() => { + const onMentionClick = (e: Event) => { + const { type, id, label } = (e as CustomEvent<{ type: string; id: string; label: string }>).detail; + if (!textareaRef.current) return; + const textarea = textareaRef.current; + const cursorPos = textarea.selectionStart; + const textBefore = textarea.value.substring(0, cursorPos); + + if (type === 'ai') { + // Insert @ai:mention at cursor position + const html = buildMentionHtml('ai', id, label); + const spacer = ' '; + const newValue = textBefore + html + spacer + textarea.value.substring(cursorPos); + const newCursorPos = cursorPos + html.length + spacer.length; + onDraftChange(newValue); + setTimeout(() => { + if (textareaRef.current) { + textareaRef.current.value = newValue; + textareaRef.current.setSelectionRange(newCursorPos, newCursorPos); + textareaRef.current.focus(); + } + }, 0); + } + }; + document.addEventListener('mention-click', onMentionClick); + return () => document.removeEventListener('mention-click', onMentionClick); + }, []); + return (
{replyingTo && (