{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "registryDependencies": [
    "https://taki-ui.com/r/button.json"
  ],
  "files": [
    {
      "path": "registry/new-york/ai-elements/code-block.tsx",
      "content": "\"use client\"\n\nimport type { ComponentProps, HTMLAttributes, ReactNode } from \"react\"\nimport { createContext, useContext, useEffect, useState } from \"react\"\nimport {\n  transformerNotationDiff,\n  transformerNotationErrorLevel,\n  transformerNotationFocus,\n  transformerNotationHighlight,\n  transformerNotationWordHighlight,\n} from \"@shikijs/transformers\"\nimport { CheckIcon, CopyIcon } from \"lucide-react\"\nimport type { IconType } from \"react-icons\"\nimport {\n  SiAstro,\n  SiBiome,\n  SiBower,\n  SiBun,\n  SiC,\n  SiCircleci,\n  SiCoffeescript,\n  SiCplusplus,\n  SiCss3,\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 \"react-icons/si\"\nimport {\n  codeToHtml,\n  type BundledLanguage,\n  type CodeOptionsMultipleThemes,\n} from \"shiki\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useControllableState } from \"@/registry/new-york/hooks/use-controllable-state\"\nimport { Button } from \"@/registry/new-york/ui/button\"\nimport { Select, SelectItem } from \"@/registry/new-york/ui/select\"\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\": SiCss3,\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 text-sm\",\n  \"[&_pre]:py-4\",\n  // \"[&_.shiki]:!bg-[var(--shiki-bg)]\",\n  \"[&_.shiki]:!bg-transparent\",\n  \"[&_code]:w-full\",\n  \"[&_code]:grid\",\n  // \"[&_code]:overflow-hidden\",\n  \"[&_code]:bg-transparent\",\n  \"[&_.line]:px-4\",\n  \"[&_.line]:w-full\",\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    defaultValue: defaultValue ?? data[0]?.filename ?? \"\",\n    value: controlledValue,\n    onChange: controlledOnValueChange,\n  })\n\n  return (\n    <CodeBlockContext.Provider value={{ value, onValueChange, data }}>\n      <div\n        className={cn(\n          \"grid size-full grid-rows-[auto_1fr] overflow-hidden rounded-md border\",\n          className\n        )}\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      \"bg-secondary flex flex-row items-center border-b 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={cn(\n        \"bg-secondary text-muted-foreground flex flex-grow items-center gap-2 px-4 py-1.5 text-xs\",\n        className\n      )}\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 = Omit<\n  ComponentProps<typeof Select>,\n  \"children\" | \"selectedKey\" | \"onSelectionChange\"\n> & {\n  children: (item: CodeBlockData) => ReactNode\n}\n\nexport const CodeBlockSelect = ({\n  children,\n  className,\n  ...props\n}: CodeBlockSelectProps) => {\n  const { value, onValueChange, data } = useContext(CodeBlockContext)\n\n  return (\n    <Select\n      size=\"sm\"\n      selectedKey={value}\n      onSelectionChange={(key) => onValueChange?.(key as string)}\n      className={cn(\n        \"text-muted-foreground w-fit border-none text-xs shadow-none\",\n        className\n      )}\n      {...props}\n    >\n      {data.map(children)}\n    </Select>\n  )\n}\n\nexport type CodeBlockSelectItemProps = ComponentProps<typeof SelectItem>\n\nexport const CodeBlockSelectItem = ({\n  className,\n  ...props\n}: CodeBlockSelectItemProps) => (\n  <SelectItem className={cn(\"text-xs\", 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  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  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\">\n      <code>\n        {children\n          ?.toString()\n          .split(\"\\n\")\n          .map((line, i) => (\n            <span className=\"line\" 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 = ({\n  children,\n  className,\n  ...props\n}: CodeBlockBodyProps) => {\n  const { data } = useContext(CodeBlockContext)\n\n  return (\n    <div\n      data-slot=\"code-block-body\"\n      className={cn(\"bg-background overflow-auto\", className)}\n      {...props}\n    >\n      {data.map(children)}\n    </div>\n  )\n}\n\nexport type CodeBlockItemProps = HTMLAttributes<HTMLDivElement> & {\n  value: string\n  lineNumbers?: boolean\n}\n\nexport const CodeBlockItem = ({\n  children,\n  lineNumbers = 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        className\n      )}\n      {...props}\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:ui"
    }
  ],
  "type": "registry:ui"
}