{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "A code panel with copy, optional line numbers, highlighted lines, and a collapse for anything long.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/code-block/code-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronDown, Clipboard, WrapText } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type CodeBlockProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  code: string\n  /** Shown in the header, and used as the download-free label for the block. */\n  filename?: string\n  /** A short label such as \"tsx\". Shown when there is no filename. */\n  language?: string\n  showLineNumbers?: boolean\n  /** One-based lines to mark as the interesting ones. */\n  highlightLines?: readonly number[]\n  /** Collapse anything past this many lines behind a toggle. */\n  maxLines?: number\n  wrap?: boolean\n  /** Offers a control to turn wrapping on and off. */\n  wrappable?: boolean\n  copyable?: boolean\n  actions?: React.ReactNode\n}\n\nexport function CodeBlock({\n  code,\n  filename,\n  language,\n  showLineNumbers = false,\n  highlightLines,\n  maxLines,\n  wrap = false,\n  wrappable = false,\n  copyable = true,\n  actions,\n  className,\n  ...rootProps\n}: CodeBlockProps) {\n  const [copyState, setCopyState] = React.useState<\"idle\" | \"copied\" | \"error\">(\n    \"idle\"\n  )\n  const [expanded, setExpanded] = React.useState(false)\n  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n  // The prop keeps deciding until the reader overrides it with the control,\n  // so wrapping stays reactive without a second copy of the state.\n  const [override, setOverride] = React.useState<boolean | null>(null)\n  const wrapped = override ?? wrap\n\n  React.useEffect(\n    () => () => {\n      if (timer.current) clearTimeout(timer.current)\n    },\n    []\n  )\n\n  const lines = React.useMemo(() => code.replace(/\\n$/, \"\").split(\"\\n\"), [code])\n  const marked = React.useMemo(\n    () => new Set(highlightLines ?? []),\n    [highlightLines]\n  )\n\n  const clipped = maxLines !== undefined && !expanded && lines.length > maxLines\n  const shown = clipped ? lines.slice(0, maxLines) : lines\n  const hidden = lines.length - shown.length\n\n  const label = filename ?? language\n  const gutter = String(lines.length).length\n\n  async function copy() {\n    try {\n      await navigator.clipboard.writeText(code)\n      setCopyState(\"copied\")\n    } catch {\n      // Denied permission, an insecure context, or a sandboxed frame.\n      setCopyState(\"error\")\n    }\n    if (timer.current) clearTimeout(timer.current)\n    timer.current = setTimeout(() => setCopyState(\"idle\"), 1400)\n  }\n\n  return (\n    <div\n      data-slot=\"code-block\"\n      className={cn(\n        \"border-border bg-muted/40 min-w-0 overflow-hidden rounded-[calc(var(--radius)+0.15rem)] border\",\n        className\n      )}\n      {...rootProps}\n    >\n      {label || copyable || wrappable || actions ? (\n        <div\n          data-slot=\"code-block-bar\"\n          className=\"border-border flex items-center gap-2 border-b px-3 py-1.5\"\n        >\n          {label ? (\n            <span className=\"text-muted-foreground truncate font-[family-name:var(--font-mono),monospace] text-xs\">\n              {label}\n            </span>\n          ) : null}\n\n          <div className=\"ml-auto flex shrink-0 items-center gap-0.5\">\n            {actions}\n\n            {wrappable ? (\n              <button\n                type=\"button\"\n                aria-pressed={wrapped}\n                aria-label=\"Wrap long lines\"\n                className=\"text-muted-foreground hover:bg-card hover:text-foreground aria-pressed:bg-card aria-pressed:text-foreground inline-flex size-8 items-center justify-center rounded-md transition-colors duration-150 motion-reduce:transition-none\"\n                onClick={() => setOverride(!wrapped)}\n              >\n                <WrapText aria-hidden=\"true\" size={14} />\n              </button>\n            ) : null}\n\n            {copyable ? (\n              <button\n                type=\"button\"\n                data-slot=\"code-block-copy\"\n                aria-label={copyState === \"copied\" ? \"Copied\" : \"Copy code\"}\n                className=\"text-muted-foreground hover:bg-card hover:text-foreground inline-flex size-8 items-center justify-center rounded-md transition-colors duration-150 motion-reduce:transition-none\"\n                onClick={copy}\n              >\n                {copyState === \"copied\" ? (\n                  <Check aria-hidden=\"true\" size={14} />\n                ) : (\n                  <Clipboard aria-hidden=\"true\" size={14} />\n                )}\n              </button>\n            ) : null}\n          </div>\n        </div>\n      ) : null}\n\n      <div className=\"relative\">\n        <pre\n          data-slot=\"code-block-code\"\n          tabIndex={0}\n          className={cn(\n            \"text-foreground m-0 overflow-x-auto rounded-none bg-transparent px-0 py-2.5 font-[family-name:var(--font-mono),monospace] text-xs leading-relaxed\",\n            wrapped && \"overflow-x-visible\"\n          )}\n        >\n          <code className=\"block\">\n            {shown.map((line, index) => {\n              const number = index + 1\n\n              return (\n                <span\n                  key={number}\n                  data-highlighted={marked.has(number) || undefined}\n                  className={cn(\n                    \"flex px-3\",\n                    marked.has(number) &&\n                      \"bg-primary/10 border-primary border-l-2 pl-[calc(0.75rem-2px)]\"\n                  )}\n                >\n                  {showLineNumbers ? (\n                    <span\n                      aria-hidden=\"true\"\n                      className=\"text-muted-foreground/60 mr-3 shrink-0 text-right tabular-nums select-none\"\n                      style={{ width: `${gutter}ch` }}\n                    >\n                      {number}\n                    </span>\n                  ) : null}\n                  <span\n                    className={cn(\n                      \"min-w-0\",\n                      wrapped\n                        ? \"break-words whitespace-pre-wrap\"\n                        : \"whitespace-pre\"\n                    )}\n                  >\n                    {line === \"\" ? \" \" : line}\n                  </span>\n                </span>\n              )\n            })}\n          </code>\n        </pre>\n\n        {clipped ? (\n          <div\n            aria-hidden=\"true\"\n            className=\"from-muted/40 pointer-events-none absolute inset-x-0 bottom-0 h-10 bg-gradient-to-t to-transparent\"\n          />\n        ) : null}\n      </div>\n\n      {maxLines !== undefined && lines.length > maxLines ? (\n        <button\n          type=\"button\"\n          aria-expanded={expanded}\n          className=\"border-border text-muted-foreground hover:text-foreground flex w-full items-center justify-center gap-1.5 border-t py-2 text-xs transition-colors duration-150 motion-reduce:transition-none\"\n          onClick={() => setExpanded((open) => !open)}\n        >\n          <ChevronDown\n            aria-hidden=\"true\"\n            size={13}\n            className={cn(\n              \"transition-transform duration-200 motion-reduce:transition-none\",\n              expanded && \"rotate-180\"\n            )}\n          />\n          {expanded\n            ? \"Show less\"\n            : `Show ${hidden} more line${hidden === 1 ? \"\" : \"s\"}`}\n        </button>\n      ) : null}\n\n      <span aria-live=\"polite\" className=\"sr-only\">\n        {copyState === \"copied\"\n          ? \"Code copied to clipboard.\"\n          : copyState === \"error\"\n            ? \"Code could not be copied.\"\n            : \"\"}\n      </span>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "code",
    "docs",
    "clipboard"
  ],
  "type": "registry:ui"
}