import { useState } from 'react'; import { ChevronDown, ChevronRight } from 'lucide-react'; import { JsonTreeCopyableItem } from './JsonTreeCopyableItem'; export type XmlTreeProps = { root: Element; }; export const XmlTree = ({ root }: XmlTreeProps) => { return (
); }; type XmlNodeProps = { node: Node; depth: number; }; // Whitespace-only text between sibling elements is DOM-pretty-print // noise. Filter it so a 10-element document doesn't render as 21 nodes. // Mixed content like `

Hello world!

` survives because its // fragments contain non-whitespace. const isWhitespaceOnlyText = (node: Node): boolean => node.nodeType === Node.TEXT_NODE && /^\s*$/.test(node.nodeValue ?? ''); const renderableChildren = (node: Node): Node[] => Array.from(node.childNodes).filter((child) => !isWhitespaceOnlyText(child)); const XmlNode = ({ node, depth }: XmlNodeProps) => { if (node.nodeType === Node.ELEMENT_NODE) { return ; } if (node.nodeType === Node.CDATA_SECTION_NODE) { return ; } if (node.nodeType === Node.TEXT_NODE) { return ; } // Comments, processing instructions, DOCTYPE — intentionally not // rendered in the tree. They're rare in API responses; the Raw view // still shows them verbatim. return null; }; type XmlElementNodeProps = { element: Element; depth: number; }; const XmlElementNode = ({ element, depth }: XmlElementNodeProps) => { const [expanded, setExpanded] = useState(true); const children = renderableChildren(element); const hasChildren = children.length > 0; const tagName = element.nodeName; const attributes = Array.from(element.attributes); // Self-closing if no renderable children. const isSelfClosing = !hasChildren; const serializeSubtree = () => new XMLSerializer().serializeToString(element); return (
{hasChildren ? ( ) : null} < {tagName} {attributes.map((attr) => ( ))} {isSelfClosing ? ' />' : '>'}
{hasChildren ? ( <>
{children.map((child, idx) => ( ))}
{/* Collapsed inline closing tag preview — gives a visual hint of what was collapsed. */} {!expanded ? : null}
</ {tagName} >
) : null}
); }; const XmlAttribute = ({ attr }: { attr: Attr }) => ( <> {attr.name} = " {attr.value} " ); type XmlTextNodeProps = { text: Text; depth: number; }; const XmlTextNode = ({ text, depth }: XmlTextNodeProps) => { const value = text.nodeValue ?? ''; return (
value}> {value}
); }; type XmlCDataNodeProps = { cdata: CDATASection; depth: number; }; const XmlCDataNode = ({ cdata, depth }: XmlCDataNodeProps) => { const value = cdata.nodeValue ?? ''; return (
value}> <![CDATA[ {value} ]]>
); };