diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 7d4dbc79..3330f641 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -1143,6 +1143,7 @@ "Current context size": "Current context size", "AI agent": "AI agent", "AI agent is typing…": "AI agent is typing…", + "{{name}} is typing…": "{{name}} is typing…", "Send": "Send", "Stop": "Stop", "Chat menu": "Chat menu", diff --git a/apps/client/public/locales/ru-RU/translation.json b/apps/client/public/locales/ru-RU/translation.json index 414e75b8..233ced69 100644 --- a/apps/client/public/locales/ru-RU/translation.json +++ b/apps/client/public/locales/ru-RU/translation.json @@ -666,6 +666,7 @@ "AI search": "Поиск ИИ", "AI Answer": "Ответ ИИ", "Ask AI": "Спросить ИИ", + "{{name}} is typing…": "{{name}} печатает…", "AI is thinking...": "ИИ обрабатывает запрос...", "Thinking": "Думаю", "Ask a question...": "Задайте вопрос...", diff --git a/apps/client/src/features/ai-chat/components/message-item.tsx b/apps/client/src/features/ai-chat/components/message-item.tsx index e8709d5c..a0c37089 100644 --- a/apps/client/src/features/ai-chat/components/message-item.tsx +++ b/apps/client/src/features/ai-chat/components/message-item.tsx @@ -22,6 +22,11 @@ interface MessageItemProps { * UUIDs/routes in the assistant's markdown don't leak as clickable links. */ neutralizeInternalLinks?: boolean; + /** + * Display name for the dimmed assistant label. Defaults to "AI agent" when + * absent; the public share passes the configured identity (agent role) name. + */ + assistantName?: string; } /** @@ -40,6 +45,7 @@ export default function MessageItem({ message, showCitations = true, neutralizeInternalLinks = false, + assistantName, }: MessageItemProps) { const { t } = useTranslation(); const isUser = message.role === "user"; @@ -61,7 +67,7 @@ export default function MessageItem({ return ( - {t("AI agent")} + {assistantName?.trim() || t("AI agent")} {message.parts.map((part, index) => { if (part.type === "text") { diff --git a/apps/client/src/features/ai-chat/components/message-list.tsx b/apps/client/src/features/ai-chat/components/message-list.tsx index 3d9c5024..b4b4101d 100644 --- a/apps/client/src/features/ai-chat/components/message-list.tsx +++ b/apps/client/src/features/ai-chat/components/message-list.tsx @@ -30,6 +30,12 @@ interface MessageListProps { * UUIDs/routes don't leak as clickable links to anonymous readers. */ neutralizeInternalLinks?: boolean; + /** + * Display name for the assistant's dimmed row label and typing indicator. + * Defaults to "AI agent" when absent. The public share passes the configured + * identity (agent role) name; the internal chat omits it. + */ + assistantName?: string; } // Distance (px) from the bottom within which the viewport still counts as @@ -67,6 +73,7 @@ export default function MessageList({ emptyState, showCitations = true, neutralizeInternalLinks = false, + assistantName, }: MessageListProps) { const { t } = useTranslation(); const viewportRef = useRef(null); @@ -148,9 +155,10 @@ export default function MessageList({ message={message} showCitations={showCitations} neutralizeInternalLinks={neutralizeInternalLinks} + assistantName={assistantName} /> ))} - {typing && } + {typing && } ); diff --git a/apps/client/src/features/ai-chat/components/typing-indicator.tsx b/apps/client/src/features/ai-chat/components/typing-indicator.tsx index 443fe1e1..ba6c6db0 100644 --- a/apps/client/src/features/ai-chat/components/typing-indicator.tsx +++ b/apps/client/src/features/ai-chat/components/typing-indicator.tsx @@ -2,22 +2,33 @@ import { Box, Group, Text } from "@mantine/core"; import { useTranslation } from "react-i18next"; import classes from "@/features/ai-chat/components/ai-chat.module.css"; +interface TypingIndicatorProps { + /** + * Display name for the dimmed label and the "… is typing…" line. Defaults to + * "AI agent" when absent; the public share passes the configured identity + * (agent role) name. + */ + assistantName?: string; +} + /** - * Live "AI agent is typing…" placeholder shown while a turn is in flight but the - * latest assistant message has no visible content yet (no rendered text/tool - * parts). It covers the gap between sending and the first streamed token, and is - * replaced by the real assistant message once content starts arriving. + * Live "… is typing…" placeholder shown while a turn is in flight but the latest + * assistant message has no visible content yet (no rendered text/tool parts). It + * covers the gap between sending and the first streamed token, and is replaced by + * the real assistant message once content starts arriving. * - * Mirrors the assistant row layout in MessageItem (the dimmed "AI agent" label), - * so it reads as the assistant's bubble taking shape. + * Mirrors the assistant row layout in MessageItem (the dimmed label), so it reads + * as the assistant's bubble taking shape. The label and typing line use the + * configured identity name when provided, otherwise the generic "AI agent". */ -export default function TypingIndicator() { +export default function TypingIndicator({ assistantName }: TypingIndicatorProps) { const { t } = useTranslation(); + const name = assistantName?.trim(); return ( - {t("AI agent")} + {name || t("AI agent")} diff --git a/apps/client/src/features/share/components/share-ai-widget.tsx b/apps/client/src/features/share/components/share-ai-widget.tsx index 5212e2c4..a75ad5fe 100644 --- a/apps/client/src/features/share/components/share-ai-widget.tsx +++ b/apps/client/src/features/share/components/share-ai-widget.tsx @@ -27,6 +27,8 @@ interface ShareAiWidgetProps { shareId: string; /** The page the reader currently has open (context for "this page"). */ pageId: string; + /** Display name of the configured assistant identity; falls back to 'AI agent' when absent. */ + assistantName?: string; } /** @@ -47,7 +49,11 @@ interface ShareAiWidgetProps { * links (so internal UUIDs/auth-gated routes in the answer don't leak as * clickable links), and a documentation-focused empty state. */ -export default function ShareAiWidget({ shareId, pageId }: ShareAiWidgetProps) { +export default function ShareAiWidget({ + shareId, + pageId, + assistantName, +}: ShareAiWidgetProps) { const { t } = useTranslation(); const [open, setOpen] = useState(false); const [input, setInput] = useState(""); @@ -152,6 +158,7 @@ export default function ShareAiWidget({ shareId, pageId }: ShareAiWidgetProps) { + )} );