diff --git a/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/ChatRow/index.jsx b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/ChatRow/index.jsx index 115b09a9e7a..bf50e61f57d 100644 --- a/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/ChatRow/index.jsx +++ b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/ChatRow/index.jsx @@ -1,9 +1,11 @@ import truncate from "truncate"; -import { X, Trash, LinkSimple } from "@phosphor-icons/react"; +import { X } from "@phosphor-icons/react"; import ModalWrapper from "@/components/ModalWrapper"; import { useModal } from "@/hooks/useModal"; import paths from "@/utils/paths"; import Embed from "@/models/embed"; +import MarkdownRenderer from "../MarkdownRenderer"; +import { safeJsonParse } from "@/utils/request"; export default function ChatRow({ chat, onDelete }) { const { @@ -83,7 +85,11 @@ export default function ChatRow({ chat, onDelete }) { + } closeModal={closeResponseModal} /> @@ -118,9 +124,9 @@ const TextPreview = ({ text, closeModal }) => {
-
+          
{text} -
+
@@ -132,11 +138,7 @@ const ConnectionDetails = ({ verbose = false, connection_information, }) => { - let details = {}; - try { - details = JSON.parse(connection_information); - } catch {} - + const details = safeJsonParse(connection_information, {}); if (Object.keys(details).length === 0) return null; if (verbose) { diff --git a/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/MarkdownRenderer.jsx b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/MarkdownRenderer.jsx new file mode 100644 index 00000000000..11b4ca51c1e --- /dev/null +++ b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/MarkdownRenderer.jsx @@ -0,0 +1,87 @@ +import { useState } from "react"; +import MarkdownIt from "markdown-it"; +import { CaretDown } from "@phosphor-icons/react"; +import "highlight.js/styles/github-dark.css"; +import DOMPurify from "@/utils/chat/purify"; + +const md = new MarkdownIt({ + html: true, + breaks: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(str, { language: lang }).value; + } catch (__) {} + } + return ""; // use external default escaping + }, +}); + +const ThoughtBubble = ({ thought }) => { + const [isExpanded, setIsExpanded] = useState(false); + + if (!thought) return null; + + const cleanThought = thought.replace(/<\/?think>/g, "").trim(); + if (!cleanThought) return null; + + return ( +
+
setIsExpanded(!isExpanded)} + className="cursor-pointer flex items-center gap-x-2 text-theme-text-secondary hover:text-theme-text-primary transition-colors mb-2" + > + + View thoughts +
+ {isExpanded && ( +
+
+ {cleanThought} +
+
+ )} +
+ ); +}; + +function parseContent(content) { + const parts = []; + let lastIndex = 0; + content.replace(/([^]*?)<\/think>/g, (match, thinkContent, offset) => { + if (offset > lastIndex) { + parts.push({ type: "normal", text: content.slice(lastIndex, offset) }); + } + parts.push({ type: "think", text: thinkContent }); + lastIndex = offset + match.length; + }); + if (lastIndex < content.length) { + parts.push({ type: "normal", text: content.slice(lastIndex) }); + } + return parts; +} + +export default function MarkdownRenderer({ content }) { + if (!content) return null; + + const parts = parseContent(content); + return ( +
+ {parts.map((part, index) => { + const html = md.render(part.text); + if (part.type === "think") + return ; + return ( +
+ ); + })} +
+ ); +} diff --git a/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/index.jsx b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/index.jsx index 154094e297b..cd242422e71 100644 --- a/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/index.jsx +++ b/frontend/src/pages/GeneralSettings/ChatEmbedWidgets/EmbedChats/index.jsx @@ -55,6 +55,7 @@ export default function EmbedChatsView() { const query = useQuery(); const [offset, setOffset] = useState(Number(query.get("offset") || 0)); const [canNext, setCanNext] = useState(false); + const [showThinking, setShowThinking] = useState(true); const handleDumpChats = async (exportType) => { const chats = await System.exportChats(exportType, "embed"); @@ -92,10 +93,15 @@ export default function EmbedChatsView() { useEffect(() => { async function fetchChats() { - const { chats: _chats, hasPages = false } = await Embed.chats(offset); - setChats(_chats); - setCanNext(hasPages); - setLoading(false); + setLoading(true); + await Embed.chats(offset) + .then(({ chats: _chats, hasPages = false }) => { + setChats(_chats); + setCanNext(hasPages); + }) + .finally(() => { + setLoading(false); + }); } fetchChats(); }, [offset]); @@ -211,7 +217,7 @@ export default function EmbedChatsView() { : "bg-theme-bg-secondary text-theme-text-primary hover:bg-theme-hover" }`} > - {t("embed-chats.previous")} + {t("common.previous")}
)}