diff --git a/src/client/components/MessageBubble.tsx b/src/client/components/MessageBubble.tsx index 942c91b..d52c302 100644 --- a/src/client/components/MessageBubble.tsx +++ b/src/client/components/MessageBubble.tsx @@ -36,7 +36,7 @@ export function MessageBubble({ // Collapsible state for thinking blocks and tool calls/results const isCollapsible = message.category === "thinking" || message.category === "tool_call" || message.category === "tool_result"; const [collapsed, setCollapsed] = useState(isCollapsible); - const [linkCopied, setLinkCopied] = useState(false); + const [contentCopied, setContentCopied] = useState(false); const renderedHtml = useMemo(() => { // Skip expensive rendering when content is collapsed and not visible @@ -46,7 +46,7 @@ export function MessageBubble({ if (msg.category === "tool_call") { const inputHtml = msg.toolInput - ? `
${escapeHtml(msg.toolInput)}`
+ ? `${escapeHtml(tryPrettyJson(msg.toolInput))}`
: "";
const html = `${escapeHtml(msg.content)}`;
+ const html = `${escapeHtml(tryPrettyJson(msg.content))}`;
return searchQuery ? highlightSearchText(html, searchQuery) : html;
}
@@ -157,14 +157,14 @@ export function MessageBubble({
? `${message.toolName || "Tool Call"}\n${message.toolInput || ""}`
: message.content;
navigator.clipboard.writeText(text).then(() => {
- setLinkCopied(true);
- setTimeout(() => setLinkCopied(false), 1500);
+ setContentCopied(true);
+ setTimeout(() => setContentCopied(false), 1500);
}).catch(() => {});
}}
className="flex items-center justify-center w-7 h-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-surface-overlay/60 transition-colors"
title="Copy message content"
>
- {linkCopied ? (
+ {contentCopied ? (
@@ -260,3 +260,14 @@ function formatTimestamp(ts: string): string {
second: "2-digit",
});
}
+
+/** If the string is valid JSON, return it pretty-printed; otherwise return as-is. */
+function tryPrettyJson(text: string): string {
+ const trimmed = text.trimStart();
+ if (trimmed[0] !== "{" && trimmed[0] !== "[") return text;
+ try {
+ return JSON.stringify(JSON.parse(text), null, 2);
+ } catch {
+ return text;
+ }
+}