{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "json-view",
  "title": "JsonView",
  "description": "JSON as a tree you can fold.",
  "registryDependencies": [
    "https://creance.pages.dev/r/copy-button.json",
    "https://creance.pages.dev/r/creance-base.json"
  ],
  "files": [
    {
      "path": "json-view/JsonView.css",
      "content": ".cr-json {\n  position: relative;\n  padding: var(--cr-space-4) 18px;\n  border-radius: var(--cr-radius-lg);\n  background: var(--cr-color-glass-tile);\n  color: var(--cr-color-text-default);\n  font-family: var(--cr-font-family-mono);\n  font-size: var(--cr-font-size-sm);\n  line-height: 1.8;\n}\n\n.cr-json__copy {\n  position: absolute;\n  top: var(--cr-space-2);\n  right: var(--cr-space-2);\n}\n\n.cr-json__tree {\n  overflow-x: auto;\n}\n\n.cr-json__line {\n  display: flex;\n  align-items: center;\n  white-space: pre;\n}\n\n.cr-json__children {\n  padding-inline-start: var(--cr-space-6);\n}\n\n.cr-json__children > .cr-json__line {\n  padding-inline-start: 20px;\n}\n\n.cr-json__close {\n  padding-inline-start: 20px;\n}\n\n.cr-json__toggle {\n  display: grid;\n  place-items: center;\n  width: 20px;\n  height: 20px;\n  padding: 0;\n  border: 0;\n  border-radius: 6px;\n  background: none;\n  color: var(--cr-color-text-muted);\n  cursor: pointer;\n}\n\n.cr-json__toggle .cr-icon {\n  transition: transform var(--cr-motion-duration-fast) var(--cr-motion-easing-standard);\n}\n\n.cr-json__toggle[aria-expanded='true'] .cr-icon {\n  transform: rotate(90deg);\n}\n\n.cr-json__toggle:focus-visible {\n  outline: var(--cr-focus-width) solid var(--cr-color-focus);\n  outline-offset: 1px;\n}\n\n.cr-json__key {\n  font-weight: var(--cr-font-weight-semibold);\n}\n\n.cr-json__string {\n  color: var(--cr-color-text-secondary);\n}\n\n.cr-json__punct {\n  color: var(--cr-color-text-muted);\n}\n\n.cr-json__count {\n  margin: 0 4px;\n  padding: 0 6px;\n  border-radius: 6px;\n  background: var(--cr-color-bg-subtle);\n  color: var(--cr-color-text-muted);\n  font-family: var(--cr-font-family-sans);\n  font-size: var(--cr-font-size-xs);\n}\n",
      "type": "registry:file",
      "target": "@components/creance/json-view/JsonView.css"
    },
    {
      "path": "json-view/JsonView.tsx",
      "content": "'use client';\n\nimport { useState } from 'react';\nimport { cx } from '@/lib/creance/cx';\nimport { ChevronIcon } from '@/lib/creance/icons';\nimport { CopyButton } from '@/components/creance/copy-button/CopyButton';\nimport './JsonView.css';\n\ntype Json = string | number | boolean | null | Json[] | { [key: string]: Json };\n\nexport interface JsonViewProps {\n  value: Json;\n  /** Objects and arrays deeper than this start folded. Default 2. */\n  expandDepth?: number;\n  /** Show a copy button for the whole value. Default true. */\n  copyable?: boolean;\n  /** Names the region, such as \"Tool result\". */\n  label?: string;\n  className?: string;\n}\n\n/** JSON as a tree you can fold. Keys in ink, strings in grey; numbers and booleans stay ink. */\nexport function JsonView({ value, expandDepth = 2, copyable = true, label = 'JSON', className }: JsonViewProps) {\n  return (\n    <div className={cx('cr-json', className)}>\n      {copyable && <CopyButton className=\"cr-json__copy\" value={() => JSON.stringify(value, null, 2)} label={`Copy ${label}`} />}\n      <div role=\"group\" aria-label={label} className=\"cr-json__tree\">\n        <Node value={value} depth={0} expandDepth={expandDepth} last />\n      </div>\n    </div>\n  );\n}\n\nfunction Node({ name, value, depth, expandDepth, last }: { name?: string; value: Json; depth: number; expandDepth: number; last: boolean }) {\n  const branch = value !== null && typeof value === 'object';\n  const [open, setOpen] = useState(depth < expandDepth);\n  const comma = last ? '' : ',';\n  const key = name !== undefined && (\n    <>\n      <span className=\"cr-json__key\">&quot;{name}&quot;</span>\n      <span className=\"cr-json__punct\">: </span>\n    </>\n  );\n\n  if (!branch) {\n    return (\n      <div className=\"cr-json__line\">\n        {key}\n        <Leaf value={value} />\n        <span className=\"cr-json__punct\">{comma}</span>\n      </div>\n    );\n  }\n\n  const isArray = Array.isArray(value);\n  const entries: [string, Json][] = isArray ? value.map((v, i) => [String(i), v]) : Object.entries(value);\n  const [l, r] = isArray ? ['[', ']'] : ['{', '}'];\n  const count = `${entries.length} ${isArray ? (entries.length === 1 ? 'item' : 'items') : entries.length === 1 ? 'key' : 'keys'}`;\n\n  return (\n    <div className=\"cr-json__branch\">\n      <div className=\"cr-json__line\">\n        <button type=\"button\" className=\"cr-json__toggle\" aria-expanded={open} aria-label={`${name ?? 'root'}, ${count}`} onClick={() => setOpen(!open)}>\n          <ChevronIcon />\n        </button>\n        {key}\n        <span className=\"cr-json__punct\">{l}</span>\n        {!open && (\n          <>\n            <span className=\"cr-json__count\">{count}</span>\n            <span className=\"cr-json__punct\">\n              {r}\n              {comma}\n            </span>\n          </>\n        )}\n      </div>\n      {open && (\n        <>\n          <div className=\"cr-json__children\">\n            {entries.map(([k, v], i) => (\n              <Node key={k} name={isArray ? undefined : k} value={v} depth={depth + 1} expandDepth={expandDepth} last={i === entries.length - 1} />\n            ))}\n          </div>\n          <div className=\"cr-json__line cr-json__close\">\n            <span className=\"cr-json__punct\">\n              {r}\n              {comma}\n            </span>\n          </div>\n        </>\n      )}\n    </div>\n  );\n}\n\nfunction Leaf({ value }: { value: Json }) {\n  if (typeof value === 'string') return <span className=\"cr-json__string\">&quot;{value}&quot;</span>;\n  return <span className=\"cr-json__literal\">{String(value)}</span>;\n}\n",
      "type": "registry:ui",
      "target": "@components/creance/json-view/JsonView.tsx"
    }
  ],
  "categories": [
    "molecule",
    "data-display"
  ],
  "type": "registry:ui"
}