diff --git a/frontend-web/src/components/workspace/thread-title.tsx b/frontend-web/src/components/workspace/thread-title.tsx index a5d86205..d001ca07 100644 --- a/frontend-web/src/components/workspace/thread-title.tsx +++ b/frontend-web/src/components/workspace/thread-title.tsx @@ -1,9 +1,10 @@ import type { BaseStream } from "@langchain/langgraph-sdk"; -import { useEffect } from "react"; - -import { useI18n } from "@/core/i18n/hooks"; -import type { AgentThreadState } from "@/core/threads"; -import { desensitize } from "@/lib/desensitize"; +import { useEffect } from "react"; + +import { useI18n } from "@/core/i18n/hooks"; +import type { AgentThreadState } from "@/core/threads"; +import { sanitizeThreadTitleForDisplay } from "@/core/threads/utils"; +import { desensitize } from "@/lib/desensitize"; import { useThreadChat } from "./chats"; import { FlipDisplay } from "./flip-display"; @@ -16,16 +17,18 @@ export function ThreadTitle({ threadId: string; thread: BaseStream; }) { - const { t } = useI18n(); - const { isNewThread } = useThreadChat(); - useEffect(() => { - let _title = t.pages.untitled; - - if (thread.values?.title) { - _title = thread.values.title; - } else if (isNewThread) { - _title = t.pages.newChat; - } + const { t } = useI18n(); + const { isNewThread } = useThreadChat(); + const displayTitle = sanitizeThreadTitleForDisplay(thread.values?.title); + + useEffect(() => { + let _title = t.pages.untitled; + + if (thread.values?.title) { + _title = displayTitle; + } else if (isNewThread) { + _title = t.pages.newChat; + } if (thread.isThreadLoading) { document.title = `Loading... - ${t.pages.appName}`; } else { @@ -35,17 +38,18 @@ export function ThreadTitle({ isNewThread, t.pages.newChat, t.pages.untitled, - t.pages.appName, - thread.isThreadLoading, - thread.values, - ]); + t.pages.appName, + thread.isThreadLoading, + thread.values, + displayTitle, + ]); if (!thread.values?.title) { return null; } - return ( - - {desensitize(thread.values.title ?? "Untitled")} - - ); -} + return ( + + {desensitize(displayTitle)} + + ); +} diff --git a/frontend-web/src/core/threads/utils.ts b/frontend-web/src/core/threads/utils.ts index 8877eda3..cf25b7b7 100644 --- a/frontend-web/src/core/threads/utils.ts +++ b/frontend-web/src/core/threads/utils.ts @@ -65,3 +65,15 @@ export function textOfMessage(message: Message) { export function titleOfThread(thread: AgentThread) { return thread.values?.title ?? "Untitled"; } + +export function sanitizeThreadTitleForDisplay(title: string | null | undefined) { + if (!title) return "Untitled"; + + const cleaned = title + .replace(/<\/?[^>\n]+>/g, " ") + .replace(/<[^>\n]*$/g, " ") + .replace(/\s+/g, " ") + .trim(); + + return cleaned || "Untitled"; +}