{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code Block",
  "description": "A copyable code block with line numbers, language selection, and lightweight language-aware syntax highlighting.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "select"
  ],
  "files": [
    {
      "path": "registry/corr/components/code-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { CheckIcon, CopyIcon, TerminalIcon } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\nimport { cn } from \"@/lib/utils\"\n\nexport type CodeBlockLanguage =\n  | \"tsx\"\n  | \"ts\"\n  | \"jsx\"\n  | \"js\"\n  | \"json\"\n  | \"css\"\n  | \"bash\"\n  | \"python\"\n  | \"curl\"\n  | \"text\"\n\nexport type CodeBlockSnippet = {\n  language: CodeBlockLanguage\n  label?: string\n  code: string\n}\n\nexport type CodeBlockProps = {\n  code?: string\n  language?: CodeBlockLanguage\n  snippets?: CodeBlockSnippet[]\n  defaultLanguage?: CodeBlockLanguage\n  label?: string\n  showLineNumbers?: boolean\n  allowLanguageSelection?: boolean\n  className?: string\n  contentClassName?: string\n}\n\nconst languageLabels: Record<CodeBlockLanguage, string> = {\n  tsx: \"TSX\",\n  ts: \"TypeScript\",\n  jsx: \"JSX\",\n  js: \"JavaScript\",\n  json: \"JSON\",\n  css: \"CSS\",\n  bash: \"Shell\",\n  python: \"Python\",\n  curl: \"cURL\",\n  text: \"Text\",\n}\n\nconst languageKeywords: Record<CodeBlockLanguage, string[]> = {\n  tsx: [\n    \"import\",\n    \"from\",\n    \"export\",\n    \"function\",\n    \"return\",\n    \"const\",\n    \"let\",\n    \"type\",\n    \"interface\",\n    \"extends\",\n    \"satisfies\",\n    \"as\",\n  ],\n  ts: [\n    \"import\",\n    \"from\",\n    \"export\",\n    \"function\",\n    \"return\",\n    \"const\",\n    \"let\",\n    \"type\",\n    \"interface\",\n    \"extends\",\n    \"satisfies\",\n    \"as\",\n  ],\n  jsx: [\"import\", \"from\", \"export\", \"function\", \"return\", \"const\", \"let\"],\n  js: [\"import\", \"from\", \"export\", \"function\", \"return\", \"const\", \"let\"],\n  json: [\"true\", \"false\", \"null\"],\n  css: [\n    \"display\",\n    \"grid\",\n    \"flex\",\n    \"color\",\n    \"background\",\n    \"border\",\n    \"padding\",\n    \"margin\",\n  ],\n  bash: [\"npm\", \"npx\", \"pnpm\", \"bun\", \"cd\", \"cp\", \"ssh\", \"sudo\"],\n  python: [\n    \"import\",\n    \"from\",\n    \"def\",\n    \"return\",\n    \"if\",\n    \"else\",\n    \"for\",\n    \"in\",\n    \"with\",\n    \"as\",\n    \"True\",\n    \"False\",\n    \"None\",\n  ],\n  curl: [\"curl\", \"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"],\n  text: [],\n}\n\nconst tokenPattern =\n  /(\\/\\/.*|#.*|\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'|`(?:\\\\.|[^`\\\\])*`|\\b[A-Za-z_$][A-Za-z0-9_$-]*\\b|\\b\\d+(?:\\.\\d+)?\\b|[{}()[\\].,;:<>/=+\\-*|&!])/g\n\nfunction getTokenClass(token: string, language: CodeBlockLanguage) {\n  const keywords = languageKeywords[language] ?? []\n\n  if (\n    token.startsWith(\"//\") ||\n    ([\"bash\", \"python\", \"curl\"].includes(language) && token.startsWith(\"#\"))\n  ) {\n    return \"text-muted-foreground\"\n  }\n\n  if (token.startsWith('\"') || token.startsWith(\"'\") || token.startsWith(\"`\")) {\n    return language === \"json\"\n      ? \"text-emerald-700 dark:text-emerald-300\"\n      : \"text-green-700 dark:text-green-300\"\n  }\n\n  if (keywords.includes(token)) {\n    return \"text-sky-700 dark:text-sky-300\"\n  }\n\n  if (language === \"json\" && /^(true|false|null)$/.test(token)) {\n    return \"text-purple-700 dark:text-purple-300\"\n  }\n\n  if (/^\\d/.test(token)) {\n    return \"text-amber-700 dark:text-amber-300\"\n  }\n\n  if (/^[A-Z]/.test(token) && [\"tsx\", \"jsx\", \"ts\", \"js\"].includes(language)) {\n    return \"text-violet-700 dark:text-violet-300\"\n  }\n\n  if (/^[{}()[\\].,;:<>/=+\\-*|&!]$/.test(token)) {\n    return \"text-muted-foreground\"\n  }\n\n  if (language === \"css\" && token.startsWith(\"--\")) {\n    return \"text-purple-700 dark:text-purple-300\"\n  }\n\n  return \"text-foreground\"\n}\n\nfunction highlightLine(line: string, language: CodeBlockLanguage) {\n  const nodes: React.ReactNode[] = []\n  let cursor = 0\n\n  for (const match of line.matchAll(tokenPattern)) {\n    const token = match[0]\n    const index = match.index ?? 0\n\n    if (index > cursor) {\n      nodes.push(line.slice(cursor, index))\n    }\n\n    nodes.push(\n      <span key={`${index}-${token}`} className={getTokenClass(token, language)}>\n        {token}\n      </span>\n    )\n\n    cursor = index + token.length\n  }\n\n  if (cursor < line.length) {\n    nodes.push(line.slice(cursor))\n  }\n\n  return nodes\n}\n\nfunction CodeLine({\n  line,\n  lineNumber,\n  language,\n  showLineNumbers,\n}: {\n  line: string\n  lineNumber: number\n  language: CodeBlockLanguage\n  showLineNumbers: boolean\n}) {\n  return (\n    <div className=\"table-row\">\n      {showLineNumbers ? (\n        <span className=\"table-cell w-10 pr-5 text-right text-muted-foreground select-none\">\n          {lineNumber}\n        </span>\n      ) : null}\n      <span className=\"table-cell min-w-0 whitespace-pre\">\n        {highlightLine(line || \" \", language)}\n      </span>\n    </div>\n  )\n}\n\nexport function CodeBlock({\n  code,\n  language = \"tsx\",\n  snippets,\n  defaultLanguage,\n  label = \"Code\",\n  showLineNumbers = true,\n  allowLanguageSelection = true,\n  className,\n  contentClassName,\n}: CodeBlockProps) {\n  const availableSnippets = React.useMemo<CodeBlockSnippet[]>(\n    () =>\n      snippets?.length\n        ? snippets\n        : [\n            {\n              language,\n              code: code ?? \"\",\n            },\n          ],\n    [code, language, snippets]\n  )\n  const initialLanguage =\n    defaultLanguage ?? availableSnippets[0]?.language ?? language\n  const [activeLanguage, setActiveLanguage] =\n    React.useState<CodeBlockLanguage>(initialLanguage)\n  const [copied, setCopied] = React.useState(false)\n  const activeSnippet =\n    availableSnippets.find((snippet) => snippet.language === activeLanguage) ??\n    availableSnippets[0]\n  const activeCode = activeSnippet?.code ?? \"\"\n  const activeLabel =\n    activeSnippet?.label ?? languageLabels[activeSnippet?.language ?? language]\n  const selectable = allowLanguageSelection && availableSnippets.length > 1\n\n  async function copyCode() {\n    await navigator.clipboard.writeText(activeCode)\n    setCopied(true)\n    window.setTimeout(() => setCopied(false), 1400)\n  }\n\n  return (\n    <div\n      className={cn(\n        \"overflow-hidden rounded-lg border bg-card text-card-foreground\",\n        className\n      )}\n    >\n      <div className=\"flex min-w-0 flex-wrap items-center justify-between gap-2 border-b bg-muted/30 px-3 py-2\">\n        <div className=\"flex min-w-0 items-center gap-2 text-xs font-medium text-muted-foreground\">\n          <TerminalIcon className=\"size-3.5 shrink-0\" />\n          <span className=\"truncate\">{label}</span>\n        </div>\n        <div className=\"flex items-center gap-2\">\n          {selectable ? (\n            <Select\n              value={activeLanguage}\n              onValueChange={(value) =>\n                setActiveLanguage(value as CodeBlockLanguage)\n              }\n            >\n              <SelectTrigger size=\"sm\" className=\"h-7\">\n                <SelectValue />\n              </SelectTrigger>\n              <SelectContent align=\"end\">\n                {availableSnippets.map((snippet) => (\n                  <SelectItem\n                    key={snippet.language}\n                    value={snippet.language}\n                  >\n                    {snippet.label ?? languageLabels[snippet.language]}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n          ) : (\n            <span className=\"rounded-md border bg-background px-2 py-1 text-[11px] font-medium text-muted-foreground\">\n              {activeLabel}\n            </span>\n          )}\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"icon-sm\"\n            onClick={copyCode}\n            className=\"rounded-md\"\n          >\n            <span\n              className=\"t-icon-swap inline-grid size-3.5 place-items-center\"\n              data-state={copied ? \"b\" : \"a\"}\n              aria-hidden=\"true\"\n            >\n              <span className=\"t-icon inline-flex size-3.5 items-center justify-center\" data-icon=\"a\">\n                <CopyIcon className=\"size-3.5\" />\n              </span>\n              <span className=\"t-icon inline-flex size-3.5 items-center justify-center\" data-icon=\"b\">\n                <CheckIcon className=\"size-3.5\" />\n              </span>\n            </span>\n            <span className=\"sr-only\">Copy code</span>\n          </Button>\n        </div>\n      </div>\n      <pre\n        className={cn(\n          \"max-h-[28rem] min-w-0 overflow-auto p-4 text-xs leading-6\",\n          contentClassName\n        )}\n      >\n        <code className=\"table min-w-max border-spacing-0 font-mono\">\n          {activeCode.split(\"\\n\").map((line, index) => (\n            <CodeLine\n              key={`${index}-${line}`}\n              line={line}\n              lineNumber={index + 1}\n              language={activeSnippet?.language ?? language}\n              showLineNumbers={showLineNumbers}\n            />\n          ))}\n        </code>\n      </pre>\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}