{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "A muted code surface with line numbers, focused rows, filename metadata, and copy feedback.",
  "dependencies": [
    "@solar-icons/react@^2.2.0",
    "cn@^0.3.0",
    "prism-react-renderer@^2.4.1"
  ],
  "registryDependencies": [
    "button",
    "https://pastaui.com/r/pastaui-styles.json"
  ],
  "files": [
    {
      "path": "src/components/ai/code-block.tsx",
      "content": "import { CheckCircleIcon as CheckIcon } from '@solar-icons/react/linear/check-circle'\nimport { CopyIcon } from '@solar-icons/react/linear/copy'\nimport { Highlight, type PrismTheme } from 'prism-react-renderer'\nimport * as React from 'react'\nimport { useState } from 'react'\n\nimport { Button } from '@/components/ui/button'\nimport { cn } from 'cn'\n\nconst codeTheme: PrismTheme = {\n  plain: {\n    backgroundColor: 'transparent',\n    color: 'var(--foreground)',\n  },\n  styles: [\n    {\n      types: ['comment', 'prolog', 'doctype', 'cdata'],\n      style: { color: 'var(--syntax-comment)', fontStyle: 'italic' },\n    },\n    {\n      types: ['keyword', 'atrule'],\n      style: { color: 'var(--syntax-keyword)' },\n    },\n    {\n      types: ['string', 'char', 'attr-value', 'inserted'],\n      style: { color: 'var(--syntax-string)' },\n    },\n    {\n      types: ['function', 'class-name'],\n      style: { color: 'var(--syntax-function)' },\n    },\n    {\n      types: ['number', 'boolean', 'constant'],\n      style: { color: 'var(--syntax-number)' },\n    },\n    {\n      types: ['tag', 'property', 'attr-name', 'selector', 'symbol', 'deleted'],\n      style: { color: 'var(--syntax-property)' },\n    },\n    {\n      types: ['punctuation', 'operator'],\n      style: { color: 'var(--syntax-punctuation)' },\n    },\n  ],\n}\n\ninterface CodeBlockProps extends React.ComponentProps<'figure'> {\n  code: string\n  filename?: string\n  highlightLines?: number[]\n  language?: string\n  showLineNumbers?: boolean\n}\n\nfunction CodeBlock({\n  className,\n  code,\n  filename,\n  highlightLines = [],\n  language = 'text',\n  showLineNumbers = true,\n  ...props\n}: CodeBlockProps) {\n  const [copied, setCopied] = useState(false)\n  const highlightedLines = new Set(highlightLines)\n  const syntaxLanguage = language === 'shell' ? 'bash' : language\n\n  async function copyCode() {\n    await navigator.clipboard.writeText(code)\n    setCopied(true)\n    window.setTimeout(() => setCopied(false), 1600)\n  }\n\n  return (\n    <figure\n      data-slot=\"agent-code-block\"\n      className={cn(\n        'overflow-hidden rounded-[16px] bg-card font-sans text-sm font-[450] tracking-[-0.05px] text-muted-foreground shadow-card',\n        className,\n      )}\n      {...props}\n    >\n      <figcaption className=\"flex h-10 items-center gap-2 px-3 metadata-label\">\n        {(/\\.tsx?$/i.test(filename ?? '') || ['ts', 'tsx', 'typescript'].includes(language)) && (\n          <TypeScriptIcon aria-hidden=\"true\" className=\"size-4 shrink-0 text-[#3178c6]\" />\n        )}\n        <span className=\"min-w-0 truncate text-[13px]\">{filename ?? language}</span>\n        <Button\n          aria-label=\"Copy code\"\n          className=\"ml-auto\"\n          onClick={copyCode}\n          size=\"icon-xs\"\n          type=\"button\"\n          variant=\"ghost\"\n        >\n          <span className=\"grid *:col-start-1 *:row-start-1 *:transition-[opacity,scale] *:duration-150 *:ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:*:transition-opacity\">\n            <CopyIcon className={cn(copied && 'scale-75 opacity-0')} />\n            <CheckIcon className={cn(!copied && 'scale-75 opacity-0')} />\n          </span>\n        </Button>\n      </figcaption>\n      <Highlight code={code.trim()} language={syntaxLanguage} theme={codeTheme}>\n        {({ getLineProps, getTokenProps, style, tokens }) => (\n          <pre className=\"overflow-x-auto pb-4 font-mono text-[13px] leading-6\" style={style}>\n            <code>\n              {tokens.map((line, lineIndex) => {\n                const lineNumber = lineIndex + 1\n\n                return (\n                  <span\n                    {...getLineProps({ line })}\n                    className={cn(\n                      'block min-w-max px-4',\n                      highlightedLines.has(lineNumber) && 'bg-foreground/[0.045]',\n                    )}\n                    key={lineIndex}\n                  >\n                    {showLineNumbers && (\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"mr-5 inline-block w-4 text-right text-muted-foreground/55 select-none\"\n                      >\n                        {lineNumber}\n                      </span>\n                    )}\n                    {line.map((token, tokenIndex) => (\n                      <span {...getTokenProps({ token })} key={tokenIndex} />\n                    ))}\n                  </span>\n                )\n              })}\n            </code>\n          </pre>\n        )}\n      </Highlight>\n    </figure>\n  )\n}\n\nexport { CodeBlock }\nexport type { CodeBlockProps }\n\nfunction TypeScriptIcon(props: React.ComponentProps<'svg'>) {\n  return (\n    <svg fill=\"currentColor\" height=\"1em\" viewBox=\"0 0 24 24\" width=\"1em\" {...props}>\n      <path d=\"M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75q.918 0 1.627.111a6.4 6.4 0 0 1 1.306.34v2.458a4 4 0 0 0-.643-.361a5 5 0 0 0-.717-.26a5.5 5.5 0 0 0-1.426-.2q-.45 0-.819.086a2.1 2.1 0 0 0-.623.242q-.254.156-.393.374a.9.9 0 0 0-.14.49q0 .294.156.529q.156.234.443.444c.287.21.423.276.696.41q.41.203.926.416q.705.296 1.266.628q.561.333.963.753q.402.418.614.957q.213.538.214 1.253q0 .986-.373 1.656a3 3 0 0 1-1.012 1.085a4.4 4.4 0 0 1-1.487.596q-.85.18-1.79.18a10 10 0 0 1-1.84-.164a5.5 5.5 0 0 1-1.512-.493v-2.63a5.03 5.03 0 0 0 3.237 1.2q.5 0 .872-.09q.373-.09.623-.25q.249-.162.373-.38a1.02 1.02 0 0 0-.074-1.089a2.1 2.1 0 0 0-.537-.5a5.6 5.6 0 0 0-.807-.444a28 28 0 0 0-1.007-.436q-1.377-.575-2.053-1.405t-.676-2.005q0-.92.369-1.582q.368-.662 1.004-1.089a4.5 4.5 0 0 1 1.47-.629a7.5 7.5 0 0 1 1.77-.201m-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z\" />\n    </svg>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/ai/code-block.tsx"
    }
  ],
  "cssVars": {
    "light": {
      "syntax-comment": "oklch(0.556 0 0)",
      "syntax-keyword": "oklch(0.49 0.2 292)",
      "syntax-string": "oklch(0.45 0.13 153)",
      "syntax-function": "oklch(0.48 0.15 255)",
      "syntax-number": "oklch(0.53 0.16 55)",
      "syntax-property": "oklch(0.48 0.14 20)",
      "syntax-punctuation": "oklch(0.4 0 0)"
    },
    "dark": {
      "syntax-comment": "oklch(0.708 0 0)",
      "syntax-keyword": "oklch(0.75 0.16 292)",
      "syntax-string": "oklch(0.76 0.13 153)",
      "syntax-function": "oklch(0.76 0.12 255)",
      "syntax-number": "oklch(0.78 0.13 65)",
      "syntax-property": "oklch(0.75 0.12 20)",
      "syntax-punctuation": "oklch(0.78 0 0)"
    }
  },
  "type": "registry:component"
}