{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "Syntax-highlighted code display with copy support.",
  "dependencies": [
    "@vllnt/ui@^0.3.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/code-block/code-block.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentType,\n  type ReactNode,\n  useEffect,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport { useTheme } from \"next-themes\";\nimport type { SyntaxHighlighterProps } from \"react-syntax-highlighter\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { Button } from \"@vllnt/ui\";\nimport { useCopyToClipboard } from \"@vllnt/ui\";\n\ntype PrismStyle = SyntaxHighlighterProps[\"style\"];\n\ntype LoadedHighlighter = {\n  oneDark: PrismStyle;\n  oneLight: PrismStyle;\n  SyntaxHighlighter: ComponentType<SyntaxHighlighterProps>;\n};\n\ntype CodeBlockProps = {\n  children: ReactNode;\n  className?: string;\n  language?: string;\n  showLanguage?: boolean;\n};\n\nfunction extractTextFromChildren(node: ReactNode): string {\n  if (typeof node === \"string\") {\n    return node;\n  }\n  if (typeof node === \"number\") {\n    return String(node);\n  }\n  if (Array.isArray(node)) {\n    return node.map(extractTextFromChildren).join(\"\");\n  }\n  if (\n    node &&\n    typeof node === \"object\" &&\n    \"props\" in node &&\n    node.props &&\n    typeof node.props === \"object\" &&\n    \"children\" in node.props\n  ) {\n    return extractTextFromChildren(node.props.children as ReactNode);\n  }\n  return String(node ?? \"\");\n}\n\nfunction findScrollableParent(\n  element: HTMLElement | null,\n): HTMLElement | undefined {\n  if (!element) return undefined;\n  if (element.scrollHeight > element.clientHeight) return element;\n  return findScrollableParent(element.parentElement);\n}\n\nexport function CodeBlock({\n  children,\n  className,\n  language = \"typescript\",\n  showLanguage = false,\n}: CodeBlockProps) {\n  const { copied, copy } = useCopyToClipboard();\n  // react-syntax-highlighter (~10MB) is dynamic-imported on mount so the\n  // @vllnt/ui barrel's static graph never reaches it — barrel consumers that\n  // never render a CodeBlock ship zero bytes of it. Null until the chunk loads.\n  const [highlighter, setHighlighter] = useState<LoadedHighlighter | null>(\n    null,\n  );\n  const { systemTheme, theme } = useTheme();\n\n  const resolvedTheme = theme === \"system\" ? systemTheme : theme;\n  const isDark = resolvedTheme !== \"light\";\n  const code = extractTextFromChildren(children);\n\n  const scrollRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    let active = true;\n    void Promise.all([\n      import(\"react-syntax-highlighter\"),\n      import(\"react-syntax-highlighter/dist/esm/styles/prism\"),\n    ]).then(([module_, styles]) => {\n      if (!active) return;\n      setHighlighter({\n        oneDark: styles.oneDark,\n        oneLight: styles.oneLight,\n        SyntaxHighlighter: module_.Prism,\n      });\n    });\n    return () => {\n      active = false;\n    };\n  }, []);\n\n  useEffect(() => {\n    const element = scrollRef.current;\n    if (!element) return;\n\n    const onWheel = (event: WheelEvent) => {\n      if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;\n      const scrollable = findScrollableParent(element);\n      if (scrollable) {\n        scrollable.scrollTop += event.deltaY;\n        event.preventDefault();\n      }\n    };\n\n    element.addEventListener(\"wheel\", onWheel, { passive: false });\n    return () => {\n      element.removeEventListener(\"wheel\", onWheel);\n    };\n  }, []);\n\n  const handleCopy = () => {\n    void copy(code);\n  };\n\n  const SyntaxHighlighter = highlighter?.SyntaxHighlighter;\n  const codeStyle = isDark ? highlighter?.oneDark : highlighter?.oneLight;\n\n  return (\n    <div\n      className={cn(\n        \"relative w-full overflow-hidden rounded-md border bg-background\",\n        className,\n      )}\n    >\n      <div\n        className=\"relative overflow-x-auto overflow-y-hidden touch-pan-y\"\n        ref={scrollRef}\n      >\n        {SyntaxHighlighter ? (\n          <SyntaxHighlighter\n            codeTagProps={{\n              className: \"font-mono text-sm\",\n              style: {\n                background: \"transparent\",\n                display: \"block\",\n              },\n            }}\n            customStyle={{\n              background: \"oklch(var(--background))\",\n              fontSize: \"0.875rem\",\n              margin: 0,\n              minWidth: \"fit-content\",\n              overflowY: \"hidden\",\n              padding: \"1rem\",\n            }}\n            language={language}\n            style={codeStyle}\n          >\n            {code}\n          </SyntaxHighlighter>\n        ) : (\n          <pre className=\"m-0 min-w-fit overflow-y-hidden p-4 font-mono text-sm\">\n            <code className=\"block bg-transparent\">{code}</code>\n          </pre>\n        )}\n        <div className=\"absolute right-2 top-2 flex items-center gap-2\">\n          {showLanguage ? (\n            <span className=\"text-xs font-mono text-muted-foreground uppercase tracking-wider\">\n              {language}\n            </span>\n          ) : null}\n          <Button\n            aria-label={copied ? \"Copied\" : \"Copy code\"}\n            className=\"size-8\"\n            onClick={handleCopy}\n            size=\"icon\"\n            variant=\"ghost\"\n          >\n            {copied ? (\n              <Check className=\"size-3\" />\n            ) : (\n              <Copy className=\"size-3\" />\n            )}\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.3.0",
  "stability": "stable"
}
