import { useState, useCallback } from 'react';
interface JsonViewerProps {
content: string;
}
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
function parseJson(content: string): JsonValue | null {
try {
return JSON.parse(content);
} catch {
return null;
}
}
export default function JsonViewer({ content }: JsonViewerProps) {
const parsed = parseJson(content);
const handleCopy = () => {
navigator.clipboard.writeText(content);
};
if (parsed === null) {
return (
);
}
return (
);
}
function JsonNode({
value,
path,
depth,
defaultOpen = false,
keyName,
}: {
value: JsonValue;
path: string;
depth: number;
defaultOpen?: boolean;
keyName?: string;
}) {
if (value === null) return ;
if (typeof value === 'boolean') return ;
if (typeof value === 'number') return ;
if (typeof value === 'string') return ;
if (Array.isArray(value)) return ;
if (typeof value === 'object') return } path={path} defaultOpen={defaultOpen} />;
return null;
}
function JsonPrimitive({ keyName, depth, value, className }: { keyName?: string; depth: number; value: string; className: string }) {
return (
{keyName !== undefined && (
"{keyName}":
)}
{value}
);
}
function JsonString({ keyName, depth, value }: { keyName?: string; depth: number; value: string }) {
const isLong = value.length > 120;
const [expanded, setExpanded] = useState(!isLong);
const display = expanded ? value : value.slice(0, 80) + '...';
return (
{keyName !== undefined && (
"{keyName}":
)}
"{display}"
{isLong && (
)}
);
}
function JsonObject({
keyName,
depth,
value,
path,
defaultOpen,
}: {
keyName?: string;
depth: number;
value: Record;
path: string;
defaultOpen?: boolean;
}) {
const keys = Object.keys(value);
const [open, setOpen] = useState(defaultOpen || depth < 2);
const handleCopySection = useCallback(() => {
navigator.clipboard.writeText(JSON.stringify(value, null, 2));
}, [value]);
if (keys.length === 0) {
return (
{keyName !== undefined && (
"{keyName}":
)}
{'{}'}
);
}
return (
setOpen(!open)}
>
{keyName !== undefined && (
"{keyName}":
)}
{'{'}
{!open && (
{keys.length} key{keys.length !== 1 ? 's' : ''}
)}
{!open &&
{'}'}}
{open && (
<>
{keys.map((k) => (
))}
{'}'}
>
)}
);
}
function JsonArray({
keyName,
depth,
value,
path,
defaultOpen,
}: {
keyName?: string;
depth: number;
value: JsonValue[];
path: string;
defaultOpen?: boolean;
}) {
const [open, setOpen] = useState(defaultOpen || depth < 2);
const handleCopySection = useCallback(() => {
navigator.clipboard.writeText(JSON.stringify(value, null, 2));
}, [value]);
if (value.length === 0) {
return (
{keyName !== undefined && (
"{keyName}":
)}
{'[]'}
);
}
return (
setOpen(!open)}
>
{keyName !== undefined && (
"{keyName}":
)}
{'['}
{!open && (
{value.length} item{value.length !== 1 ? 's' : ''}
)}
{!open &&
{']'}}
{open && (
<>
{value.map((item, i) => (
))}
{']'}
>
)}
);
}