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 && (