diff --git a/ui/desktop/frontend/src/components/traces/trace-span-tree.tsx b/ui/desktop/frontend/src/components/traces/trace-span-tree.tsx index 921f6c11..13a4a4df 100644 --- a/ui/desktop/frontend/src/components/traces/trace-span-tree.tsx +++ b/ui/desktop/frontend/src/components/traces/trace-span-tree.tsx @@ -1,7 +1,7 @@ // Span tree rendering — builds and displays hierarchical trace spans. +import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { MarkdownRenderer } from '../chat/MarkdownRenderer' import type { SpanData } from '../../types/trace' import { formatDuration, formatTokens, statusClass, spanTypeIcon } from './trace-detail-formatters' @@ -35,22 +35,31 @@ export function buildSpanTree(spans: SpanData[]): SpanNode[] { } function TraceContentPreview({ text }: { text?: string }) { + const [copied, setCopied] = useState(false) if (!text) return null const trimmed = text.trim() - // Auto-format JSON into a markdown code block + // Pretty-print JSON if valid + let display = trimmed + let lang = '' if (trimmed.startsWith('{') || trimmed.startsWith('[')) { - try { - const formatted = JSON.stringify(JSON.parse(trimmed), null, 2) - return ( -
- -
- ) - } catch { /* not valid JSON, fall through */ } + try { display = JSON.stringify(JSON.parse(trimmed), null, 2); lang = 'json' } catch { /* not valid JSON */ } + } + const handleCopy = () => { + navigator.clipboard.writeText(display) + setCopied(true) + setTimeout(() => setCopied(false), 1500) } return ( -
- +
+ {lang && ( +
+ {lang} + +
+ )} +
{display}
) }