{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "codeblock",
  "type": "registry:ui",
  "title": "CodeBlock",
  "description": "Syntax-highlighted codeblock component built on top of react-syntax-highlighter.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": [
    "lucide-react",
    "react-syntax-highlighter",
    "@types/react-syntax-highlighter"
  ],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/codeblock.tsx",
      "content": "'use client';\nimport React from 'react';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';\nimport {\n  Check,\n  Copy,\n  ChevronRight,\n  File,\n  Folder,\n  Terminal,\n  Code2,\n  Download,\n  Maximize2,\n  Settings,\n} from 'lucide-react';\n\ntype CodeBlockProps = {\n  language: string;\n  filename: string;\n  highlightLines?: number[];\n  breadcrumb?: string[];\n  showStats?: boolean;\n  theme?: 'dark' | 'light';\n} & (\n  | {\n      code: string;\n      tabs?: never;\n    }\n  | {\n      code?: never;\n      tabs: Array<{\n        name: string;\n        code: string;\n        language?: string;\n        highlightLines?: number[];\n      }>;\n    }\n);\n\nexport const CodeBlock = ({\n  language,\n  filename,\n  code,\n  highlightLines = [],\n  tabs = [],\n  breadcrumb = [],\n  showStats = true,\n  theme = 'dark',\n}: CodeBlockProps) => {\n  const [copied, setCopied] = React.useState(false);\n  const [activeTab, setActiveTab] = React.useState(0);\n  const [isExpanded, setIsExpanded] = React.useState(false);\n\n  const tabsExist = tabs.length > 0;\n\n  const copyToClipboard = async () => {\n    const textToCopy = tabsExist ? tabs[activeTab].code : code;\n    if (textToCopy) {\n      await navigator.clipboard.writeText(textToCopy);\n      setCopied(true);\n      setTimeout(() => setCopied(false), 2000);\n    }\n  };\n\n  const downloadCode = () => {\n    const textToDownload = tabsExist ? tabs[activeTab].code : code;\n    const activeFilename = tabsExist ? tabs[activeTab].name : filename;\n    if (textToDownload) {\n      const blob = new Blob([textToDownload], { type: 'text/plain' });\n      const url = URL.createObjectURL(blob);\n      const a = document.createElement('a');\n      a.href = url;\n      a.download = activeFilename;\n      document.body.appendChild(a);\n      a.click();\n      document.body.removeChild(a);\n      URL.revokeObjectURL(url);\n    }\n  };\n\n  const activeCode = tabsExist ? tabs[activeTab].code : code;\n  const activeLanguage = tabsExist\n    ? tabs[activeTab].language || language\n    : language;\n  const activeHighlightLines = tabsExist\n    ? tabs[activeTab].highlightLines || []\n    : highlightLines;\n\n  const getLanguageIcon = (lang: string) => {\n    switch (lang.toLowerCase()) {\n      case 'javascript':\n      case 'jsx':\n      case 'typescript':\n      case 'tsx':\n        return <Code2 size='1em' className='text-yellow-400' />;\n      case 'bash':\n      case 'shell':\n        return <Terminal size='1em' className='text-green-400' />;\n      default:\n        return <File size='1em' className='text-blue-400' />;\n    }\n  };\n\n  const getCodeStats = (code: string) => {\n    const lines = code.split('\\n').length;\n    const chars = code.length;\n    const words = code.split(/\\s+/).filter((word) => word.length > 0).length;\n    return { lines, chars, words };\n  };\n\n  const stats = showStats ? getCodeStats(activeCode || '') : null;\n\n  return (\n    <div\n      className={`relative w-full rounded-xl overflow-hidden shadow-2xl ${\n        theme === 'dark' ? 'bg-slate-900' : 'bg-white'\n      } border ${theme === 'dark' ? 'border-slate-700' : 'border-gray-200'}`}\n    >\n      <div\n        className={`flex items-stretch min-h-12 ${\n          theme === 'dark'\n            ? 'bg-slate-800 border-b border-slate-700'\n            : 'bg-gray-50 border-b border-gray-200'\n        }`}\n      >\n        <div className='flex-1 flex items-center min-w-0 px-3'>\n          <div className='flex gap-2 mr-3 shrink-0'>\n            <div className='w-3 h-3 rounded-full bg-red-500'></div>\n            <div className='w-3 h-3 rounded-full bg-yellow-500'></div>\n            <div className='w-3 h-3 rounded-full bg-green-500'></div>\n          </div>\n\n          {breadcrumb.length > 0 && (\n            <div className='flex items-center min-w-0'>\n              <Folder\n                size='1em'\n                className={`shrink-0 ${\n                  theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n                }`}\n              />\n              <div className='flex items-center min-w-0 ml-2'>\n                {breadcrumb.map((crumb, index) => (\n                  <React.Fragment key={index}>\n                    <span\n                      className={`text-xs truncate ${\n                        theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n                      }`}\n                    >\n                      {crumb}\n                    </span>\n                    {index < breadcrumb.length - 1 && (\n                      <ChevronRight\n                        size='0.75em'\n                        className={`shrink-0 mx-1 ${\n                          theme === 'dark' ? 'text-slate-500' : 'text-gray-400'\n                        }`}\n                      />\n                    )}\n                  </React.Fragment>\n                ))}\n              </div>\n            </div>\n          )}\n        </div>\n\n        <div className='flex items-center justify-end shrink-0 px-2'>\n          {stats && (\n            <div\n              className={`text-xs mx-2 ${\n                theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n              } truncate hidden md:block`}\n            >\n              {stats.lines}L • {stats.words}W\n            </div>\n          )}\n\n          <div className='flex'>\n            <button\n              onClick={() => setIsExpanded(!isExpanded)}\n              className={`p-2 hover:${\n                theme === 'dark' ? 'bg-slate-700' : 'bg-gray-200'\n              } transition-colors`}\n              title='Toggle fullscreen'\n            >\n              <Maximize2\n                size='1em'\n                className={\n                  theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n                }\n              />\n            </button>\n            <button\n              onClick={downloadCode}\n              className={`p-2 hover:${\n                theme === 'dark' ? 'bg-slate-700' : 'bg-gray-200'\n              } transition-colors`}\n              title='Download code'\n            >\n              <Download\n                size='1em'\n                className={\n                  theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n                }\n              />\n            </button>\n            <button\n              onClick={copyToClipboard}\n              className={`p-2 hover:${\n                theme === 'dark' ? 'bg-slate-700' : 'bg-gray-200'\n              } transition-colors`}\n              title='Copy code'\n            >\n              {copied ? (\n                <Check size='1em' className='text-green-400' />\n              ) : (\n                <Copy\n                  size='1em'\n                  className={\n                    theme === 'dark' ? 'text-slate-400' : 'text-gray-500'\n                  }\n                />\n              )}\n            </button>\n          </div>\n        </div>\n      </div>\n\n      {tabsExist && (\n        <div\n          className={`flex border-b ${\n            theme === 'dark'\n              ? 'border-slate-700 bg-slate-800'\n              : 'border-gray-200 bg-gray-50'\n          } overflow-x-auto`}\n        >\n          {tabs.map((tab, index) => (\n            <button\n              key={index}\n              onClick={() => setActiveTab(index)}\n              className={`flex items-center gap-2 px-4 py-2 text-sm transition-all duration-200 border-b-2 shrink-0 ${\n                activeTab === index\n                  ? theme === 'dark'\n                    ? 'text-white border-blue-400 bg-slate-900'\n                    : 'text-gray-900 border-blue-500 bg-white'\n                  : theme === 'dark'\n                    ? 'text-slate-400 border-transparent hover:text-slate-200 hover:bg-slate-700'\n                    : 'text-gray-600 border-transparent hover:text-gray-800 hover:bg-gray-100'\n              }`}\n            >\n              {getLanguageIcon(tab.language || language)}\n              <span className='truncate max-w-40'>{tab.name}</span>\n            </button>\n          ))}\n        </div>\n      )}\n\n      {!tabsExist && filename && (\n        <div\n          className={`flex items-center px-3 py-2 border-b ${\n            theme === 'dark'\n              ? 'border-slate-700 bg-slate-800'\n              : 'border-gray-200 bg-gray-50'\n          }`}\n        >\n          <div className='flex items-center gap-2 min-w-0'>\n            {getLanguageIcon(language)}\n            <span\n              className={`text-sm font-medium truncate ${\n                theme === 'dark' ? 'text-slate-200' : 'text-gray-700'\n              }`}\n            >\n              {filename}\n            </span>\n          </div>\n        </div>\n      )}\n\n      <div\n        className={`relative ${\n          isExpanded ? 'max-h-screen overflow-auto' : 'max-h-96 overflow-auto'\n        }`}\n      >\n        <SyntaxHighlighter\n          language={activeLanguage}\n          style={theme === 'dark' ? atomDark : undefined}\n          customStyle={{\n            margin: 0,\n            padding: '1rem',\n            background: 'transparent',\n            fontSize: '0.875rem',\n            lineHeight: '1.5',\n          }}\n          wrapLines={true}\n          showLineNumbers={true}\n          lineNumberStyle={{\n            minWidth: '3em',\n            paddingRight: '1em',\n            color: theme === 'dark' ? '#64748b' : '#9ca3af',\n            borderRight: `1px solid ${\n              theme === 'dark' ? '#334155' : '#e5e7eb'\n            }`,\n            marginRight: '1em',\n          }}\n          lineProps={(lineNumber: number) => ({\n            style: {\n              backgroundColor: activeHighlightLines.includes(lineNumber)\n                ? theme === 'dark'\n                  ? 'rgba(59, 130, 246, 0.1)'\n                  : 'rgba(59, 130, 246, 0.05)'\n                : 'transparent',\n              display: 'block',\n              width: '100%',\n              borderLeft: activeHighlightLines.includes(lineNumber)\n                ? '3px solid #3b82f6'\n                : '3px solid transparent',\n              paddingLeft: '0.5rem',\n            },\n          })}\n          PreTag='div'\n        >\n          {String(activeCode)}\n        </SyntaxHighlighter>\n      </div>\n\n      {showStats && stats && (\n        <div\n          className={`px-3 py-2 border-t text-xs ${\n            theme === 'dark'\n              ? 'border-slate-700 bg-slate-800 text-slate-400'\n              : 'border-gray-200 bg-gray-50 text-gray-500'\n          } flex items-center justify-between min-h-10`}\n        >\n          <div className='flex items-center gap-3 min-w-0'>\n            <span className='truncate'>{activeLanguage.toUpperCase()}</span>\n            <span className='truncate hidden sm:inline'>\n              {stats.lines} lines\n            </span>\n            <span className='truncate hidden md:inline'>\n              {stats.chars} chars\n            </span>\n          </div>\n          <div className='flex items-center gap-1 shrink-0'>\n            <Settings size='0.75em' />\n            <span>UTF-8</span>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n"
    }
  ]
}
