{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "type": "registry:ui",
  "description": "Enhanced code block component with syntax highlighting",
  "files": [
    {
      "path": "packages/code/block.tsx",
      "content": "'use client';\n\nimport {\n  type IconType,\n  SiAstro,\n  SiBiome,\n  SiBower,\n  SiBun,\n  SiC,\n  SiCircleci,\n  SiCoffeescript,\n  SiCplusplus,\n  SiCss,\n  SiCssmodules,\n  SiDart,\n  SiDocker,\n  SiDocusaurus,\n  SiDotenv,\n  SiEditorconfig,\n  SiEslint,\n  SiGatsby,\n  SiGitignoredotio,\n  SiGnubash,\n  SiGo,\n  SiGraphql,\n  SiGrunt,\n  SiGulp,\n  SiHandlebarsdotjs,\n  SiHtml5,\n  SiJavascript,\n  SiJest,\n  SiJson,\n  SiLess,\n  SiMarkdown,\n  SiMdx,\n  SiMintlify,\n  SiMocha,\n  SiMysql,\n  SiNextdotjs,\n  SiPerl,\n  SiPhp,\n  SiPostcss,\n  SiPrettier,\n  SiPrisma,\n  SiPug,\n  SiPython,\n  SiR,\n  SiReact,\n  SiReadme,\n  SiRedis,\n  SiRemix,\n  SiRive,\n  SiRollupdotjs,\n  SiRuby,\n  SiSanity,\n  SiSass,\n  SiScala,\n  SiSentry,\n  SiShadcnui,\n  SiStorybook,\n  SiStylelint,\n  SiSublimetext,\n  SiSvelte,\n  SiSvg,\n  SiSwift,\n  SiTailwindcss,\n  SiToml,\n  SiTypescript,\n  SiVercel,\n  SiVite,\n  SiVuedotjs,\n  SiWebassembly,\n} from '@icons-pack/react-simple-icons';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport {\n  transformerNotationDiff,\n  transformerNotationErrorLevel,\n  transformerNotationFocus,\n  transformerNotationHighlight,\n  transformerNotationWordHighlight,\n} from '@shikijs/transformers';\nimport { CheckIcon, CopyIcon } from 'lucide-react';\nimport type {\n  ComponentProps,\n  HTMLAttributes,\n  ReactElement,\n  ReactNode,\n} from 'react';\nimport {\n  cloneElement,\n  createContext,\n  useContext,\n  useEffect,\n  useState,\n} from 'react';\nimport {\n  type BundledLanguage,\n  type CodeOptionsMultipleThemes,\n  codeToHtml,\n} from 'shiki';\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 '@repo/shadcn-ui/lib/utils';\n\nexport type { BundledLanguage } from 'shiki';\n\nconst filenameIconMap = {\n  '.env': SiDotenv,\n  '*.astro': SiAstro,\n  'biome.json': SiBiome,\n  '.bowerrc': SiBower,\n  'bun.lockb': SiBun,\n  '*.c': SiC,\n  '*.cpp': SiCplusplus,\n  '.circleci/config.yml': SiCircleci,\n  '*.coffee': SiCoffeescript,\n  '*.module.css': SiCssmodules,\n  '*.css': SiCss,\n  '*.dart': SiDart,\n  Dockerfile: SiDocker,\n  'docusaurus.config.js': SiDocusaurus,\n  '.editorconfig': SiEditorconfig,\n  '.eslintrc': SiEslint,\n  'eslint.config.*': SiEslint,\n  'gatsby-config.*': SiGatsby,\n  '.gitignore': SiGitignoredotio,\n  '*.go': SiGo,\n  '*.graphql': SiGraphql,\n  '*.sh': SiGnubash,\n  'Gruntfile.*': SiGrunt,\n  'gulpfile.*': SiGulp,\n  '*.hbs': SiHandlebarsdotjs,\n  '*.html': SiHtml5,\n  '*.js': SiJavascript,\n  '*.json': SiJson,\n  '*.test.js': SiJest,\n  '*.less': SiLess,\n  '*.md': SiMarkdown,\n  '*.mdx': SiMdx,\n  'mintlify.json': SiMintlify,\n  'mocha.opts': SiMocha,\n  '*.mustache': SiHandlebarsdotjs,\n  '*.sql': SiMysql,\n  'next.config.*': SiNextdotjs,\n  '*.pl': SiPerl,\n  '*.php': SiPhp,\n  'postcss.config.*': SiPostcss,\n  'prettier.config.*': SiPrettier,\n  '*.prisma': SiPrisma,\n  '*.pug': SiPug,\n  '*.py': SiPython,\n  '*.r': SiR,\n  '*.rb': SiRuby,\n  '*.jsx': SiReact,\n  '*.tsx': SiReact,\n  'readme.md': SiReadme,\n  '*.rdb': SiRedis,\n  'remix.config.*': SiRemix,\n  '*.riv': SiRive,\n  'rollup.config.*': SiRollupdotjs,\n  'sanity.config.*': SiSanity,\n  '*.sass': SiSass,\n  '*.scss': SiSass,\n  '*.sc': SiScala,\n  '*.scala': SiScala,\n  'sentry.client.config.*': SiSentry,\n  'components.json': SiShadcnui,\n  'storybook.config.*': SiStorybook,\n  'stylelint.config.*': SiStylelint,\n  '.sublime-settings': SiSublimetext,\n  '*.svelte': SiSvelte,\n  '*.svg': SiSvg,\n  '*.swift': SiSwift,\n  'tailwind.config.*': SiTailwindcss,\n  '*.toml': SiToml,\n  '*.ts': SiTypescript,\n  'vercel.json': SiVercel,\n  'vite.config.*': SiVite,\n  '*.vue': SiVuedotjs,\n  '*.wasm': SiWebassembly,\n};\n\nconst lineNumberClassNames = cn(\n  '[&_code]:[counter-reset:line]',\n  '[&_code]:[counter-increment:line_0]',\n  '[&_.line]:before:content-[counter(line)]',\n  '[&_.line]:before:inline-block',\n  '[&_.line]:before:[counter-increment:line]',\n  '[&_.line]:before:w-4',\n  '[&_.line]:before:mr-4',\n  '[&_.line]:before:text-[13px]',\n  '[&_.line]:before:text-right',\n  '[&_.line]:before:text-muted-foreground/50',\n  '[&_.line]:before:font-mono',\n  '[&_.line]:before:select-none'\n);\n\nconst darkModeClassNames = cn(\n  'dark:[&_.shiki]:!text-[var(--shiki-dark)]',\n  'dark:[&_.shiki]:!bg-[var(--shiki-dark-bg)]',\n  'dark:[&_.shiki]:![font-style:var(--shiki-dark-font-style)]',\n  'dark:[&_.shiki]:![font-weight:var(--shiki-dark-font-weight)]',\n  'dark:[&_.shiki]:![text-decoration:var(--shiki-dark-text-decoration)]',\n  'dark:[&_.shiki_span]:!text-[var(--shiki-dark)]',\n  'dark:[&_.shiki_span]:![font-style:var(--shiki-dark-font-style)]',\n  'dark:[&_.shiki_span]:![font-weight:var(--shiki-dark-font-weight)]',\n  'dark:[&_.shiki_span]:![text-decoration:var(--shiki-dark-text-decoration)]'\n);\n\nconst lineHighlightClassNames = cn(\n  '[&_.line.highlighted]:bg-blue-50',\n  '[&_.line.highlighted]:after:bg-blue-500',\n  '[&_.line.highlighted]:after:absolute',\n  '[&_.line.highlighted]:after:left-0',\n  '[&_.line.highlighted]:after:top-0',\n  '[&_.line.highlighted]:after:bottom-0',\n  '[&_.line.highlighted]:after:w-0.5',\n  'dark:[&_.line.highlighted]:!bg-blue-500/10'\n);\n\nconst lineDiffClassNames = cn(\n  '[&_.line.diff]:after:absolute',\n  '[&_.line.diff]:after:left-0',\n  '[&_.line.diff]:after:top-0',\n  '[&_.line.diff]:after:bottom-0',\n  '[&_.line.diff]:after:w-0.5',\n  '[&_.line.diff.add]:bg-emerald-50',\n  '[&_.line.diff.add]:after:bg-emerald-500',\n  '[&_.line.diff.remove]:bg-rose-50',\n  '[&_.line.diff.remove]:after:bg-rose-500',\n  'dark:[&_.line.diff.add]:!bg-emerald-500/10',\n  'dark:[&_.line.diff.remove]:!bg-rose-500/10'\n);\n\nconst lineFocusedClassNames = cn(\n  '[&_code:has(.focused)_.line]:blur-[2px]',\n  '[&_code:has(.focused)_.line.focused]:blur-none'\n);\n\nconst wordHighlightClassNames = cn(\n  '[&_.highlighted-word]:bg-blue-50',\n  'dark:[&_.highlighted-word]:!bg-blue-500/10'\n);\n\nconst codeBlockClassName = cn(\n  'mt-0 bg-background text-sm',\n  '[&_pre]:py-4',\n  '[&_.shiki]:!bg-[var(--shiki-bg)]',\n  '[&_code]:w-full',\n  '[&_code]:grid',\n  // Prevent horizontal scroll and allow wrapping\n  '[&_pre]:overflow-x-hidden',\n  '[&_code]:overflow-x-hidden',\n  '[&_code]:whitespace-pre-wrap',\n  '[&_code]:bg-transparent',\n  '[&_.line]:px-4',\n  '[&_.line]:w-full',\n  // Wrap long lines to avoid x-axis scroll\n  '[&_.line]:whitespace-pre-wrap',\n  '[&_.line]:break-words',\n  '[&_.line]:relative'\n);\n\nconst highlight = (\n  html: string,\n  language?: BundledLanguage,\n  themes?: CodeOptionsMultipleThemes['themes']\n) =>\n  codeToHtml(html, {\n    lang: language ?? 'typescript',\n    themes: themes ?? {\n      light: 'github-light',\n      dark: 'github-dark-default',\n    },\n    transformers: [\n      transformerNotationDiff({\n        matchAlgorithm: 'v3',\n      }),\n      transformerNotationHighlight({\n        matchAlgorithm: 'v3',\n      }),\n      transformerNotationWordHighlight({\n        matchAlgorithm: 'v3',\n      }),\n      transformerNotationFocus({\n        matchAlgorithm: 'v3',\n      }),\n      transformerNotationErrorLevel({\n        matchAlgorithm: 'v3',\n      }),\n    ],\n  });\n\ntype CodeBlockData = {\n  language: string;\n  filename: string;\n  code: string;\n};\n\ntype CodeBlockContextType = {\n  value: string | undefined;\n  onValueChange: ((value: string) => void) | undefined;\n  data: CodeBlockData[];\n};\n\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n  value: undefined,\n  onValueChange: undefined,\n  data: [],\n});\n\nexport type CodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n  defaultValue?: string;\n  value?: string;\n  onValueChange?: (value: string) => void;\n  data: CodeBlockData[];\n};\n\nexport const CodeBlock = ({\n  value: controlledValue,\n  onValueChange: controlledOnValueChange,\n  defaultValue,\n  className,\n  data,\n  ...props\n}: CodeBlockProps) => {\n  const [value, onValueChange] = useControllableState({\n    defaultProp: defaultValue ?? '',\n    prop: controlledValue,\n    onChange: controlledOnValueChange,\n  });\n\n  return (\n    <CodeBlockContext.Provider value={{ value, onValueChange, data }}>\n      <div\n        className={cn('size-full overflow-hidden rounded-md border', className)}\n        {...props}\n      />\n    </CodeBlockContext.Provider>\n  );\n};\n\nexport type CodeBlockHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const CodeBlockHeader = ({\n  className,\n  ...props\n}: CodeBlockHeaderProps) => (\n  <div\n    className={cn(\n      'flex flex-row items-center border-b bg-secondary p-1',\n      className\n    )}\n    {...props}\n  />\n);\n\nexport type CodeBlockFilesProps = Omit<\n  HTMLAttributes<HTMLDivElement>,\n  'children'\n> & {\n  children: (item: CodeBlockData) => ReactNode;\n};\n\nexport const CodeBlockFiles = ({\n  className,\n  children,\n  ...props\n}: CodeBlockFilesProps) => {\n  const { data } = useContext(CodeBlockContext);\n\n  return (\n    <div\n      className={cn('flex grow flex-row items-center gap-2', className)}\n      {...props}\n    >\n      {data.map(children)}\n    </div>\n  );\n};\n\nexport type CodeBlockFilenameProps = HTMLAttributes<HTMLDivElement> & {\n  icon?: IconType;\n  value?: string;\n};\n\nexport const CodeBlockFilename = ({\n  className,\n  icon,\n  value,\n  children,\n  ...props\n}: CodeBlockFilenameProps) => {\n  const { value: activeValue } = useContext(CodeBlockContext);\n  const defaultIcon = Object.entries(filenameIconMap).find(([pattern]) => {\n    const regex = new RegExp(\n      `^${pattern.replace(/\\\\/g, '\\\\\\\\').replace(/\\./g, '\\\\.').replace(/\\*/g, '.*')}$`\n    );\n    return regex.test(children as string);\n  })?.[1];\n  const Icon = icon ?? defaultIcon;\n\n  if (value !== activeValue) {\n    return null;\n  }\n\n  return (\n    <div\n      className=\"flex items-center gap-2 bg-secondary px-4 py-1.5 text-muted-foreground text-xs\"\n      {...props}\n    >\n      {Icon && <Icon className=\"h-4 w-4 shrink-0\" />}\n      <span className=\"flex-1 truncate\">{children}</span>\n    </div>\n  );\n};\n\nexport type CodeBlockSelectProps = ComponentProps<typeof Select>;\n\nexport const CodeBlockSelect = (props: CodeBlockSelectProps) => {\n  const { value, onValueChange } = useContext(CodeBlockContext);\n\n  return <Select onValueChange={onValueChange} value={value} {...props} />;\n};\n\nexport type CodeBlockSelectTriggerProps = ComponentProps<typeof SelectTrigger>;\n\nexport const CodeBlockSelectTrigger = ({\n  className,\n  ...props\n}: CodeBlockSelectTriggerProps) => (\n  <SelectTrigger\n    className={cn(\n      'w-fit border-none text-muted-foreground text-xs shadow-none',\n      className\n    )}\n    {...props}\n  />\n);\n\nexport type CodeBlockSelectValueProps = ComponentProps<typeof SelectValue>;\n\nexport const CodeBlockSelectValue = (props: CodeBlockSelectValueProps) => (\n  <SelectValue {...props} />\n);\n\nexport type CodeBlockSelectContentProps = Omit<\n  ComponentProps<typeof SelectContent>,\n  'children'\n> & {\n  children: (item: CodeBlockData) => ReactNode;\n};\n\nexport const CodeBlockSelectContent = ({\n  children,\n  ...props\n}: CodeBlockSelectContentProps) => {\n  const { data } = useContext(CodeBlockContext);\n\n  return <SelectContent {...props}>{data.map(children)}</SelectContent>;\n};\n\nexport type CodeBlockSelectItemProps = ComponentProps<typeof SelectItem>;\n\nexport const CodeBlockSelectItem = ({\n  className,\n  ...props\n}: CodeBlockSelectItemProps) => (\n  <SelectItem className={cn('text-sm', className)} {...props} />\n);\n\nexport type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n  onCopy?: () => void;\n  onError?: (error: Error) => void;\n  timeout?: number;\n};\n\nexport const CodeBlockCopyButton = ({\n  asChild,\n  onCopy,\n  onError,\n  timeout = 2000,\n  children,\n  className,\n  ...props\n}: CodeBlockCopyButtonProps) => {\n  const [isCopied, setIsCopied] = useState(false);\n  const { data, value } = useContext(CodeBlockContext);\n  const code = data.find((item) => item.language === value)?.code;\n\n  const copyToClipboard = () => {\n    if (\n      typeof window === 'undefined' ||\n      !navigator.clipboard.writeText ||\n      !code\n    ) {\n      return;\n    }\n\n    navigator.clipboard.writeText(code).then(() => {\n      setIsCopied(true);\n      onCopy?.();\n\n      setTimeout(() => setIsCopied(false), timeout);\n    }, onError);\n  };\n\n  if (asChild) {\n    return cloneElement(children as ReactElement, {\n      // @ts-expect-error - we know this is a button\n      onClick: copyToClipboard,\n    });\n  }\n\n  const Icon = isCopied ? CheckIcon : CopyIcon;\n\n  return (\n    <Button\n      className={cn('shrink-0', className)}\n      onClick={copyToClipboard}\n      size=\"icon\"\n      variant=\"ghost\"\n      {...props}\n    >\n      {children ?? <Icon className=\"text-muted-foreground\" size={14} />}\n    </Button>\n  );\n};\n\ntype CodeBlockFallbackProps = HTMLAttributes<HTMLDivElement>;\n\nconst CodeBlockFallback = ({ children, ...props }: CodeBlockFallbackProps) => (\n  <div {...props}>\n    <pre className=\"w-full overflow-x-hidden\">\n      <code className=\"whitespace-pre-wrap break-words\">\n        {children\n          ?.toString()\n          .split('\\n')\n          .map((line, i) => (\n            <span className=\"line whitespace-pre-wrap break-words\" key={i}>\n              {line}\n            </span>\n          ))}\n      </code>\n    </pre>\n  </div>\n);\n\nexport type CodeBlockBodyProps = Omit<\n  HTMLAttributes<HTMLDivElement>,\n  'children'\n> & {\n  children: (item: CodeBlockData) => ReactNode;\n};\n\nexport const CodeBlockBody = ({ children, ...props }: CodeBlockBodyProps) => {\n  const { data } = useContext(CodeBlockContext);\n\n  return <div {...props}>{data.map(children)}</div>;\n};\n\nexport type CodeBlockItemProps = HTMLAttributes<HTMLDivElement> & {\n  value: string;\n  lineNumbers?: boolean;\n  showCopy?: boolean;\n};\n\nexport const CodeBlockItem = ({\n  children,\n  lineNumbers = true,\n  showCopy = true,\n  className,\n  value,\n  ...props\n}: CodeBlockItemProps) => {\n  const { value: activeValue } = useContext(CodeBlockContext);\n\n  if (value !== activeValue) {\n    return null;\n  }\n\n  return (\n    <div\n      className={cn(\n        codeBlockClassName,\n        lineHighlightClassNames,\n        lineDiffClassNames,\n        lineFocusedClassNames,\n        wordHighlightClassNames,\n        darkModeClassNames,\n        lineNumbers && lineNumberClassNames,\n        'relative overflow-hidden',\n        className\n      )}\n      {...props}\n    >\n      {showCopy && (\n        <div className=\"absolute right-2 top-2 z-10\">\n          <CodeBlockCopyButton aria-label=\"Copy code\" />\n        </div>\n      )}\n      {children}\n    </div>\n  );\n};\n\nexport type CodeBlockContentProps = HTMLAttributes<HTMLDivElement> & {\n  themes?: CodeOptionsMultipleThemes['themes'];\n  language?: BundledLanguage;\n  syntaxHighlighting?: boolean;\n  children: string;\n};\n\nexport const CodeBlockContent = ({\n  children,\n  themes,\n  language,\n  syntaxHighlighting = true,\n  ...props\n}: CodeBlockContentProps) => {\n  const [html, setHtml] = useState<string | null>(null);\n\n  useEffect(() => {\n    if (!syntaxHighlighting) {\n      return;\n    }\n\n    highlight(children as string, language, themes)\n      .then(setHtml)\n      // biome-ignore lint/suspicious/noConsole: \"it's fine\"\n      .catch(console.error);\n  }, [children, themes, syntaxHighlighting, language]);\n\n  if (!(syntaxHighlighting && html)) {\n    return <CodeBlockFallback>{children}</CodeBlockFallback>;\n  }\n\n  return (\n    <div\n      // biome-ignore lint/security/noDangerouslySetInnerHtml: \"Kinda how Shiki works\"\n      dangerouslySetInnerHTML={{ __html: html }}\n      {...props}\n    />\n  );\n};\n",
      "type": "registry:component"
    }
  ]
}