{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "An AI code block with a file header, copy, and line collapse.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/code-block.tsx",
      "content": "'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Check, ChevronDown, Copy, FileCode2 } from 'lucide-react';\nimport { cn } from '@/lib/utils';\n\nexport type CodeBlockVariant = 'Default' | 'Numbered';\n\nexport interface CodeBlockProps {\n  code: string;\n  filename?: string;\n  language?: string;\n  collapsedLines?: number;\n  variant?: CodeBlockVariant;\n  className?: string;\n}\n\nexport function CodeBlock({\n  code,\n  filename,\n  language = 'tsx',\n  collapsedLines = 8,\n  variant = 'Default',\n  className,\n}: CodeBlockProps) {\n  const [copied, setCopied] = useState(false);\n  const [expanded, setExpanded] = useState(false);\n  const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  useEffect(() => () => clearTimeout(timer.current), []);\n\n  const lines = code.replace(/\\n$/, '').split('\\n');\n  const collapsible = lines.length > collapsedLines;\n  const visible = expanded || !collapsible ? lines : lines.slice(0, collapsedLines);\n\n  async function copy() {\n    try {\n      await navigator.clipboard.writeText(code);\n    } catch {\n      return;\n    }\n    setCopied(true);\n    clearTimeout(timer.current);\n    timer.current = setTimeout(() => setCopied(false), 1600);\n  }\n\n  return (\n    <figure\n      className={cn(\n        'w-full max-w-[500px] overflow-hidden rounded-2xl border border-black/[0.07] bg-white shadow-xs dark:border-white/[0.08] dark:bg-[#0B0B0D]',\n        className,\n      )}\n    >\n      <div className=\"flex items-center justify-between gap-3 border-b border-black/[0.06] px-3 py-1.5 dark:border-white/[0.07]\">\n        <span className=\"flex min-w-0 items-center gap-1.5\">\n          <FileCode2 className=\"size-3.5 shrink-0 text-neutral-400 dark:text-neutral-500\" />\n          <span className=\"truncate font-mono text-[11px] text-neutral-500 dark:text-neutral-400\">\n            {filename ?? language}\n          </span>\n        </span>\n        <button\n          type=\"button\"\n          onClick={copy}\n          aria-label={copied ? 'Code copied' : 'Copy code'}\n          className=\"grid size-7 shrink-0 place-items-center rounded-md text-neutral-400 transition-[color,transform] duration-150 hover:text-neutral-700 active:scale-[0.94] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400 dark:text-neutral-500 dark:hover:text-neutral-200\"\n        >\n          <span className=\"relative grid size-3.5 place-items-center\">\n            <Copy\n              className={cn(\n                'absolute size-3.5 transition-[opacity,filter] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]',\n                copied ? 'opacity-0 blur-[2px]' : 'opacity-100 blur-0',\n              )}\n            />\n            <Check\n              className={cn(\n                'absolute size-3.5 text-emerald-600 transition-[opacity,filter] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] dark:text-emerald-400',\n                copied ? 'opacity-100 blur-0' : 'opacity-0 blur-[2px]',\n              )}\n            />\n          </span>\n        </button>\n      </div>\n\n      <div className=\"overflow-x-auto py-2\" tabIndex={0} aria-label={filename ?? 'Code'}>\n        {visible.map((line, index) => (\n          <div key={index} className=\"flex whitespace-nowrap px-3 font-mono text-[11.5px] leading-[1.8]\">\n            {variant === 'Numbered' && (\n              <span\n                aria-hidden\n                className=\"mr-3 w-5 select-none text-right tabular-nums text-neutral-300 dark:text-neutral-700\"\n              >\n                {index + 1}\n              </span>\n            )}\n            <span className=\"text-neutral-700 dark:text-neutral-300\">{line || ' '}</span>\n          </div>\n        ))}\n      </div>\n\n      {collapsible && (\n        <button\n          type=\"button\"\n          aria-expanded={expanded}\n          onClick={() => setExpanded((value) => !value)}\n          className=\"flex w-full items-center justify-center gap-1 border-t border-black/[0.06] py-1.5 font-mono text-[10.5px] text-neutral-400 transition-colors duration-150 hover:bg-black/[0.02] hover:text-neutral-700 dark:hover:bg-white/[0.03] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-neutral-400 dark:border-white/[0.07] dark:text-neutral-500 dark:hover:text-neutral-300\"\n        >\n          {expanded ? 'collapse' : `${lines.length - collapsedLines} more lines`}\n          <ChevronDown\n            className={cn(\n              'size-3 transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]',\n              expanded && 'rotate-180',\n            )}\n          />\n        </button>\n      )}\n    </figure>\n  );\n}\n\nexport default CodeBlock;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/code-block.tsx"
    }
  ],
  "type": "registry:block"
}
