/** * JsonView — pretty-printed, syntax-highlighted JSON with a copy button. * No dependencies — a small tokenizer that hits the keys/strings/numbers/ * booleans we care about. Style-tunable via CSS variables so the palette * shifts cleanly between light and dark. */ import { useMemo, useState } from "react"; interface Props { data: unknown; maxHeight?: string; } export function JsonView({ data, maxHeight = "420px" }: Props) { const pretty = useMemo(() => JSON.stringify(data, null, 2), [data]); const [copied, setCopied] = useState(false); const onCopy = async () => { try { await navigator.clipboard.writeText(pretty); setCopied(true); setTimeout(() => setCopied(false), 1400); } catch { /* clipboard denied */ } }; return (
extraction_result.json
        
      
); } /* ------------------------------------------------------------------------ */ // Minimal JSON syntax highlighter — enough to distinguish keys, strings, // numbers, booleans, and null. Uses spans with CSS variable colors. function highlight(json: string): string { const esc = json .replace(/&/g, "&") .replace(//g, ">"); return esc.replace( /("(?:\\.|[^"\\])*"(?:\s*:)?)|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, (m) => { if (/^"/.test(m)) { if (/:$/.test(m)) { return `${m.replace(/:$/, "")}:`; } return `${m}`; } if (/true|false/.test(m)) { return `${m}`; } if (/null/.test(m)) { return `${m}`; } // number return `${m}`; } ); }