{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "code-block",
  "title": "Code-block",
  "description": "A code-block component.",
  "dependencies": [
    "@icons-pack/react-simple-icons",
    "shiki",
    "@hugeicons/react",
    "@hugeicons/core-free-icons",
    "hast-util-to-jsx-runtime"
  ],
  "registryDependencies": [
    "@cubby-ui/copy-button",
    "@cubby-ui/tabs",
    "@cubby-ui/scroll-area",
    "@cubby-ui/elevated"
  ],
  "files": [
    {
      "path": "registry/default/code-block/code-block.tsx",
      "content": "\"use client\";\r\n\r\nimport {\r\n  useLayoutEffect,\r\n  useState,\r\n  useMemo,\r\n  createContext,\r\n  useContext,\r\n} from \"react\";\r\nimport * as React from \"react\";\r\nimport { mergeProps } from \"@base-ui/react/merge-props\";\r\nimport { useRender } from \"@base-ui/react/use-render\";\r\nimport { highlight } from \"@/registry/default/code-block/lib/shiki-shared\";\r\nimport { stripDiffMarker } from \"@/registry/default/code-block/lib/transformers/utils\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { solidSurface } from \"@/registry/default/lib/elevated\";\r\nimport { CopyButton } from \"@/registry/default/copy-button/copy-button\";\r\nimport {\r\n  SiTypescript,\r\n  SiJavascript,\r\n  SiPython,\r\n} from \"@icons-pack/react-simple-icons\";\r\nimport type { BundledLanguage } from \"shiki/langs\";\r\nimport { Tabs, TabsList, TabsTrigger } from \"@/registry/default/tabs/tabs\";\r\nimport {\r\n  ScrollArea,\r\n  type FadeEdges,\r\n} from \"@/registry/default/scroll-area/scroll-area\";\r\n\r\nimport { HugeiconsIcon } from \"@hugeicons/react\";\r\nimport { ComputerTerminal01Icon } from \"@hugeicons/core-free-icons\";\r\n\r\ninterface CodeBlockContextValue {\r\n  code: string;\r\n  language: string;\r\n  nodes: React.ReactElement | undefined;\r\n  lines: string[];\r\n  hasFocus: boolean;\r\n  showDiff: boolean;\r\n  floatingCopy: boolean;\r\n}\r\n\r\nconst CodeBlockContext = createContext<CodeBlockContextValue | null>(null);\r\n\r\nfunction useCodeBlock() {\r\n  const context = useContext(CodeBlockContext);\r\n  if (!context) {\r\n    throw new Error(\r\n      \"CodeBlock subcomponents must be used within a CodeBlock component\",\r\n    );\r\n  }\r\n  return context;\r\n}\r\n\r\ntype LanguageIconRenderer = React.ComponentType<{\r\n  size: number;\r\n  className: string;\r\n}>;\r\n\r\nfunction renderHugeicon(\r\n  icon: typeof ComputerTerminal01Icon,\r\n): LanguageIconRenderer {\r\n  function HugeLanguageIcon({\r\n    size,\r\n    className,\r\n  }: {\r\n    size: number;\r\n    className: string;\r\n  }) {\r\n    return (\r\n      <HugeiconsIcon\r\n        icon={icon}\r\n        size={size}\r\n        className={className}\r\n        strokeWidth={2}\r\n      />\r\n    );\r\n  }\r\n  return HugeLanguageIcon;\r\n}\r\n\r\nconst LANGUAGE_ICONS: Record<string, LanguageIconRenderer> = {\r\n  typescript: SiTypescript,\r\n  ts: SiTypescript,\r\n  tsx: SiTypescript,\r\n  javascript: SiJavascript,\r\n  js: SiJavascript,\r\n  jsx: SiJavascript,\r\n  bash: renderHugeicon(ComputerTerminal01Icon),\r\n  sh: renderHugeicon(ComputerTerminal01Icon),\r\n  shell: renderHugeicon(ComputerTerminal01Icon),\r\n  python: SiPython,\r\n  py: SiPython,\r\n};\r\n\r\nfunction getLanguageIcon(language: string) {\r\n  const normalized = language.toLowerCase();\r\n  const Icon = LANGUAGE_ICONS[normalized];\r\n\r\n  if (Icon) {\r\n    return <Icon size={16} className=\"text-muted-foreground\" />;\r\n  }\r\n\r\n  return <span className=\"text-muted-foreground text-sm\">{language}</span>;\r\n}\r\n\r\ninterface CodeBlockProps extends Omit<\r\n  useRender.ComponentProps<\"div\">,\r\n  \"children\"\r\n> {\r\n  code: string;\r\n  language?: string;\r\n  initial?: React.ReactElement;\r\n  floatingCopy?: boolean;\r\n  highlightLines?: number[] | string;\r\n  showDiff?: boolean;\r\n  focusLines?: number[] | string;\r\n  children: React.ReactNode;\r\n}\r\n\r\nfunction CodeBlock({\r\n  code,\r\n  language = \"javascript\",\r\n  initial,\r\n  floatingCopy = false,\r\n  highlightLines,\r\n  showDiff,\r\n  focusLines,\r\n  className,\r\n  render,\r\n  children,\r\n  ...props\r\n}: CodeBlockProps) {\r\n  const [nodes, setNodes] = useState(initial);\r\n\r\n  // Update during render (React 18+ state-from-props pattern) to avoid a\r\n  // cascading effect render when the `initial` prop changes.\r\n  const [prevInitial, setPrevInitial] = useState(initial);\r\n  if (initial !== prevInitial) {\r\n    setPrevInitial(initial);\r\n    if (initial) {\r\n      setNodes(initial);\r\n    }\r\n  }\r\n\r\n  const lines = useMemo(() => code.split(\"\\n\"), [code]);\r\n\r\n  useLayoutEffect(() => {\r\n    if (!initial) {\r\n      const normalizedLanguage = (language as BundledLanguage) || \"javascript\";\r\n      void highlight(code, normalizedLanguage, {\r\n        highlightLines,\r\n        showDiff,\r\n        focusLines,\r\n      }).then(setNodes);\r\n    }\r\n  }, [code, language, initial, highlightLines, showDiff, focusLines]);\r\n\r\n  const contextValue = useMemo(\r\n    () => ({\r\n      language,\r\n      code,\r\n      nodes,\r\n      lines,\r\n      hasFocus: !!focusLines,\r\n      showDiff: !!showDiff,\r\n      floatingCopy,\r\n    }),\r\n    [language, code, nodes, lines, focusLines, showDiff, floatingCopy],\r\n  );\r\n\r\n  const content = (\r\n    <CodeBlockContext.Provider value={contextValue}>\r\n      {children}\r\n    </CodeBlockContext.Provider>\r\n  );\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block\",\r\n    className: cn(\r\n      \"group max-w-full w-full rounded-2xl p-1 pt-0 relative\",\r\n      // bg-muted follows solidSurface to override its bg-surface-3.\r\n      solidSurface(3, 1),\r\n      \"bg-muted\",\r\n      // Restore top padding when there's no header.\r\n      \"has-[[data-slot='code-block-pre']:first-child]:pt-1\",\r\n      className,\r\n    ),\r\n    children: content,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ninterface CodeBlockHeaderProps\r\n  extends useRender.ComponentProps<\"div\">, Partial<BaseTabsProps> {\r\n  filename?: string;\r\n  tabVariant?: React.ComponentProps<typeof TabsList>[\"variant\"];\r\n  customIcon?: React.ReactNode;\r\n  showCopy?: boolean;\r\n}\r\n\r\nfunction CodeBlockHeader({\r\n  className,\r\n  render,\r\n  children,\r\n  filename,\r\n  tabs,\r\n  activeTab,\r\n  onTabChange,\r\n  tabVariant,\r\n  customIcon,\r\n  showCopy = true,\r\n  ...props\r\n}: CodeBlockHeaderProps) {\r\n  const context = useCodeBlock();\r\n  const language = context.language;\r\n  const code = context.code;\r\n\r\n  const startContent = (\r\n    <div className=\"flex min-w-0 items-center gap-2\">\r\n      {language && (\r\n        <CodeBlockLanguage language={language} customIcon={customIcon} />\r\n      )}\r\n      {filename && <CodeBlockFilename>{filename}</CodeBlockFilename>}\r\n      {tabs && activeTab && onTabChange && (\r\n        <CodeBlockTabs\r\n          tabs={tabs}\r\n          activeTab={activeTab}\r\n          onTabChange={onTabChange}\r\n          variant={tabVariant}\r\n        />\r\n      )}\r\n    </div>\r\n  );\r\n\r\n  const endContent = showCopy && code && (\r\n    <div className=\"flex items-center gap-2\">\r\n      <CopyButton data-slot=\"code-block-copy-button\" content={code} />\r\n    </div>\r\n  );\r\n\r\n  const content = children ?? (\r\n    <>\r\n      {startContent}\r\n      {endContent}\r\n    </>\r\n  );\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-header\",\r\n    className: cn(\r\n      \"flex items-center justify-between bg-transparent px-3 py-1\",\r\n      className,\r\n    ),\r\n    children: content,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ninterface CodeBlockLanguageProps extends useRender.ComponentProps<\"div\"> {\r\n  language: string;\r\n  customIcon?: React.ReactNode;\r\n}\r\n\r\nfunction CodeBlockLanguage({\r\n  language,\r\n  customIcon,\r\n  className,\r\n  render,\r\n  ...props\r\n}: CodeBlockLanguageProps) {\r\n  const icon = customIcon ?? getLanguageIcon(language);\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-language\",\r\n    className: cn(\"flex items-center gap-1.5\", className),\r\n    children: icon,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ntype CodeBlockFilenameProps = useRender.ComponentProps<\"span\">;\r\n\r\nfunction CodeBlockFilename({\r\n  className,\r\n  render,\r\n  children,\r\n  ...props\r\n}: CodeBlockFilenameProps) {\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-filename\",\r\n    className: cn(\"text-sm font-medium\", className),\r\n    children,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"span\",\r\n    render,\r\n    props: mergeProps<\"span\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ninterface HeaderTab {\r\n  value: string;\r\n  label: string;\r\n}\r\n\r\ninterface BaseTabsProps {\r\n  tabs: HeaderTab[];\r\n  activeTab: React.ComponentProps<typeof Tabs>[\"value\"];\r\n  onTabChange: React.ComponentProps<typeof Tabs>[\"onValueChange\"];\r\n}\r\n\r\ninterface CodeBlockTabsProps\r\n  extends useRender.ComponentProps<\"div\">, BaseTabsProps {\r\n  variant?: React.ComponentProps<typeof TabsList>[\"variant\"];\r\n}\r\n\r\nfunction CodeBlockTabs({\r\n  tabs,\r\n  activeTab,\r\n  onTabChange,\r\n  variant = \"capsule\",\r\n  className,\r\n  render,\r\n  ...props\r\n}: CodeBlockTabsProps) {\r\n  const tabsElement = (\r\n    <Tabs value={activeTab} onValueChange={onTabChange} className=\"gap-1\">\r\n      <div className=\"scrollbar-hide flex max-w-full items-center overflow-x-auto\">\r\n        <TabsList\r\n          variant={variant}\r\n          size=\"small\"\r\n          className=\"w-max bg-transparent p-0 shadow-none! ring-0\"\r\n        >\r\n          {tabs.map((tab) => (\r\n            <TabsTrigger key={tab.value} value={tab.value}>\r\n              {tab.label}\r\n            </TabsTrigger>\r\n          ))}\r\n        </TabsList>\r\n      </div>\r\n    </Tabs>\r\n  );\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-tabs\",\r\n    className: cn(\"ml-2 min-w-0 overflow-hidden\", className),\r\n    children: tabsElement,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ntype CodeBlockFloatingCopyProps = useRender.ComponentProps<\"div\">;\r\n\r\nfunction CodeBlockFloatingCopy({\r\n  className,\r\n  render,\r\n  ...props\r\n}: CodeBlockFloatingCopyProps) {\r\n  const context = useCodeBlock();\r\n  const code = context.code;\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-floating-copy\",\r\n    className: cn(\"absolute pointer-events-none z-1 top-2 right-2\", className),\r\n    children: (\r\n      <CopyButton\r\n        data-slot=\"code-block-floating-copy\"\r\n        content={code}\r\n        className=\"pointer-events-auto backdrop-blur-sm\"\r\n      />\r\n    ),\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ninterface CodeBlockPreProps extends useRender.ComponentProps<\"pre\"> {\r\n  /** Show line numbers in the gutter */\r\n  lineNumbers?: boolean;\r\n  /** Configure fade edges to indicate scrollable content. Defaults to true. */\r\n  fadeEdges?: FadeEdges;\r\n  /** Hide scrollbars while keeping scroll functionality */\r\n  hideScrollbar?: boolean;\r\n  /** Use native browser scrolling instead of Base UI ScrollArea */\r\n  nativeScroll?: boolean;\r\n}\r\n\r\nfunction CodeBlockPre({\r\n  className,\r\n  render,\r\n  children,\r\n  lineNumbers = false,\r\n  fadeEdges = true,\r\n  hideScrollbar = false,\r\n  nativeScroll = false,\r\n  ...props\r\n}: CodeBlockPreProps) {\r\n  const context = useCodeBlock();\r\n  const lines = context.lines;\r\n  const hasFocus = context.hasFocus;\r\n  const floatingCopy = context.floatingCopy;\r\n\r\n  const content =\r\n    lineNumbers && lines.length > 0 ? (\r\n      <div className=\"flex\">\r\n        <CodeBlockLineNumbers lines={lines} />\r\n        <div className=\"min-w-0 flex-1\">{children}</div>\r\n      </div>\r\n    ) : (\r\n      children\r\n    );\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-pre\",\r\n    \"data-has-focus\": hasFocus ? \"true\" : undefined,\r\n    className: cn(\r\n      \"relative rounded-lg whitespace-pre overflow-hidden max-h-96 flex flex-col\",\r\n      // Inner code body card sitting in the gray tray.\r\n      solidSurface(3),\r\n      // Focus mode: blur non-focused lines\r\n      \"[&[data-has-focus]_.line:not([data-focused])]:opacity-60 [&[data-has-focus]_.line:not([data-focused])]:blur-[1px] [&[data-has-focus]_.line:not([data-focused])]:transition-all\",\r\n      className,\r\n    ),\r\n    children: (\r\n      <>\r\n        {floatingCopy && <CodeBlockFloatingCopy />}\r\n        <ScrollArea\r\n          fadeEdges={fadeEdges}\r\n          hideScrollbar={hideScrollbar}\r\n          nativeScroll={nativeScroll}\r\n          viewportClassName=\"py-3\"\r\n          className=\"min-h-0 flex-1\"\r\n        >\r\n          {content}\r\n        </ScrollArea>\r\n      </>\r\n    ),\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"pre\",\r\n    render,\r\n    props: mergeProps<\"pre\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ninterface CodeBlockLineNumbersProps extends useRender.ComponentProps<\"div\"> {\r\n  lines: string[];\r\n}\r\n\r\nfunction CodeBlockLineNumbers({\r\n  className,\r\n  render,\r\n  lines,\r\n  ...props\r\n}: CodeBlockLineNumbersProps) {\r\n  const lineNumbers = lines.map((_, index) => (\r\n    <div\r\n      key={index}\r\n      className=\"text-right text-[.8125rem] leading-normal tabular-nums\"\r\n    >\r\n      {index + 1}\r\n    </div>\r\n  ));\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-line-numbers\",\r\n    \"data-line-numbers\": \"true\",\r\n    className: cn(\"text-muted-foreground pl-3 select-none\", className),\r\n    children: lineNumbers,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\ntype CodeBlockCodeProps = useRender.ComponentProps<\"div\">;\r\n\r\nfunction CodeBlockCode({ className, render, ...props }: CodeBlockCodeProps) {\r\n  const context = useCodeBlock();\r\n  const code = context.code;\r\n  const nodes = context.nodes;\r\n  const showDiff = context.showDiff;\r\n\r\n  const prehighlightedCode = useMemo(() => {\r\n    const originalLines = code.split(\"\\n\");\r\n    let lines = [...originalLines];\r\n\r\n    if (showDiff) {\r\n      lines = lines.map((line) => stripDiffMarker(line));\r\n    }\r\n\r\n    // Mirror Shiki's output structure: `.line` spans separated by newline text nodes.\r\n    const elements: React.ReactNode[] = [];\r\n    lines.forEach((line, index) => {\r\n      elements.push(\r\n        <span key={`line-${index}`} className=\"line\">\r\n          {line || \" \"}\r\n        </span>,\r\n      );\r\n      if (index < lines.length - 1) {\r\n        elements.push(\"\\n\");\r\n      }\r\n    });\r\n    return <code>{elements}</code>;\r\n  }, [code, showDiff]);\r\n\r\n  const defaultProps = {\r\n    \"data-slot\": \"code-block-code\",\r\n    className: cn(\r\n      // font-mono on the wrapper plus the Shiki/fallback pre & code elements:\r\n      // the UA stylesheet sets `font-family: monospace` directly on pre/code,\r\n      // which beats inheritance, so Geist Mono must be applied to them too.\r\n      \"block font-mono text-[.8125rem] leading-normal whitespace-pre w-fit min-w-full [&_pre]:font-mono [&_code]:font-mono\",\r\n      // Uniform padding: raw text (no `.line`) vs. Shiki `.line` spans.\r\n      \"[&:not(:has(.line))]:px-3 [&_.line]:!px-3 [&:not(:has(.line))]:pr-8 [&_.line]:!pr-8\",\r\n      // Full-width lines so bg highlights stretch edge-to-edge.\r\n      \"[&_.line]:inline-block [&_.line]:min-w-full\",\r\n      // Left-border highlight: pl offset = px - border-width so text stays aligned.\r\n      \"[&_.line[data-highlighted]]:bg-primary/10 [&_.line[data-highlighted]]:border-l-2 [&_.line[data-highlighted]]:border-primary/50 [&_.line[data-highlighted]]:!pl-[calc(0.75rem-2px)]\",\r\n      // Diff: added lines\r\n      \"[&_.line[data-diff='added']]:bg-green-500/10 [&_.line[data-diff='added']]:border-l-2 [&_.line[data-diff='added']]:border-green-500/70 [&_.line[data-diff='added']]:!pl-[calc(0.75rem-2px)]\",\r\n      // Diff: removed lines\r\n      \"[&_.line[data-diff='removed']]:bg-red-500/10 [&_.line[data-diff='removed']]:border-l-2 [&_.line[data-diff='removed']]:border-red-500/70 [&_.line[data-diff='removed']]:!pl-[calc(0.75rem-2px)]\",\r\n      // Diff: modified lines\r\n      \"[&_.line[data-diff='modified']]:bg-yellow-500/10 [&_.line[data-diff='modified']]:border-l-2 [&_.line[data-diff='modified']]:border-yellow-500/70 [&_.line[data-diff='modified']]:!pl-[calc(0.75rem-2px)]\",\r\n      className,\r\n    ),\r\n    children: nodes || prehighlightedCode,\r\n  };\r\n\r\n  const element = useRender({\r\n    defaultTagName: \"div\",\r\n    render,\r\n    props: mergeProps<\"div\">(defaultProps, props),\r\n  });\r\n\r\n  return element;\r\n}\r\n\r\nexport {\r\n  CodeBlock,\r\n  CodeBlockHeader,\r\n  CodeBlockFloatingCopy,\r\n  CodeBlockPre,\r\n  CodeBlockLineNumbers,\r\n  CodeBlockCode,\r\n  useCodeBlock,\r\n};\r\nexport type { FadeEdges };\r\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/code-block/code-block.tsx"
    },
    {
      "path": "registry/default/code-block/lib/shiki-shared.ts",
      "content": "import type { JSX } from \"react\";\r\nimport type { ShikiTransformer } from \"shiki/core\";\r\nimport type { BundledLanguage } from \"shiki/langs\";\r\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\r\nimport { Fragment } from \"react\";\r\nimport { jsx, jsxs } from \"react/jsx-runtime\";\r\nimport { createHighlighterCore } from \"shiki/core\";\r\nimport { createJavaScriptRegexEngine } from \"shiki/engine/javascript\";\r\nimport { createHighlightLinesTransformer } from \"./transformers/highlight-lines\";\r\nimport { createDiffTransformer } from \"./transformers/diff\";\r\nimport { createFocusTransformer } from \"./transformers/focus\";\r\n\r\n// Global highlighter promise (per Shiki Next.js docs pattern)\r\n// Define without await as a global variable to reference from components\r\n// https://shiki.style/packages/next\r\nconst highlighter = createHighlighterCore({\r\n  themes: [\r\n    import(\"shiki/themes/github-light.mjs\"),\r\n    import(\"shiki/themes/github-dark.mjs\"),\r\n  ],\r\n  langs: [], // Load languages on-demand for optimal performance\r\n  engine: createJavaScriptRegexEngine(), // Smaller bundle, faster startup than WASM\r\n});\r\n\r\n// Track loaded languages to avoid redundant imports\r\nconst loadedLanguages = new Set<string>();\r\n\r\n// Shiki's built-in no-op grammars. These need no on-demand `shiki/langs/*`\r\n// import (there is no module for them) — `codeToHast` handles them directly.\r\n// Used for fenced blocks with no language (file trees, plain output) so they\r\n// still render in a real <pre> rather than collapsing into inline code.\r\nconst PLAINTEXT_LANGS = new Set([\"text\", \"txt\", \"plaintext\", \"plain\", \"ansi\"]);\r\n\r\nconst BASE_TRANSFORMERS: ShikiTransformer[] = [\r\n  {\r\n    name: \"remove-background\",\r\n    pre(node) {\r\n      delete node.properties.style;\r\n    },\r\n    code(node) {\r\n      delete node.properties.style;\r\n    },\r\n  },\r\n  {\r\n    name: \"fix-empty-lines\",\r\n    line(node) {\r\n      // Ensure empty lines have a space to maintain height\r\n      // Shiki creates empty .line spans for blank lines which collapse\r\n      if (!node.children || node.children.length === 0) {\r\n        node.children = [{ type: \"text\", value: \" \" }];\r\n      }\r\n    },\r\n  },\r\n];\r\n\r\nexport interface HighlightOptions {\r\n  highlightLines?: number[] | string;\r\n  showDiff?: boolean;\r\n  focusLines?: number[] | string;\r\n}\r\n\r\nasync function highlightWithLang(\r\n  code: string,\r\n  lang: BundledLanguage,\r\n  options?: HighlightOptions,\r\n) {\r\n  // Await the global highlighter promise (per Shiki Next.js docs)\r\n  const instance = await highlighter;\r\n\r\n  // Load language dynamically if not already loaded (cached Set for performance).\r\n  // Plaintext grammars are built in, so skip the import that would 404.\r\n  if (!PLAINTEXT_LANGS.has(lang) && !loadedLanguages.has(lang)) {\r\n    // Dynamically import only the specific language module needed\r\n    const langModule = await import(`shiki/langs/${lang}.mjs`);\r\n    await instance.loadLanguage(langModule.default);\r\n    loadedLanguages.add(lang);\r\n  }\r\n\r\n  // Build transformers array based on options\r\n  const transformers: ShikiTransformer[] = [...BASE_TRANSFORMERS];\r\n\r\n  if (options?.highlightLines) {\r\n    transformers.push(createHighlightLinesTransformer(options.highlightLines));\r\n  }\r\n\r\n  if (options?.showDiff) {\r\n    transformers.push(createDiffTransformer());\r\n  }\r\n\r\n  if (options?.focusLines) {\r\n    transformers.push(createFocusTransformer(options.focusLines));\r\n  }\r\n\r\n  const out = instance.codeToHast(code, {\r\n    lang,\r\n    themes: {\r\n      light: \"github-light\",\r\n      dark: \"github-dark\",\r\n    },\r\n    defaultColor: \"light-dark()\",\r\n    transformers,\r\n  });\r\n\r\n  return toJsxRuntime(out, { Fragment, jsx, jsxs }) as JSX.Element;\r\n}\r\n\r\nexport async function highlight(\r\n  code: string,\r\n  lang: BundledLanguage,\r\n  options?: HighlightOptions,\r\n) {\r\n  try {\r\n    return await highlightWithLang(code, lang, options);\r\n  } catch {\r\n    // If language isn't supported, try with javascript as fallback\r\n    try {\r\n      return await highlightWithLang(code, \"javascript\", options);\r\n    } catch {\r\n      // Final fallback to plain text\r\n      return jsx(\"pre\", { children: code }) as JSX.Element;\r\n    }\r\n  }\r\n}\r\n",
      "type": "registry:lib",
      "target": "components/ui/cubby-ui/code-block/lib/shiki-shared.ts"
    },
    {
      "path": "registry/default/code-block/lib/transformers/diff.ts",
      "content": "import type { ShikiTransformer } from \"shiki\";\nimport { detectDiffMarker, stripDiffMarker } from \"./utils\";\n\n/**\n * Shiki transformer that detects diff markers and adds data-diff attributes\n * Supports:\n * - \"+\" prefix for added lines\n * - \"-\" prefix for removed lines\n * - \"!\" prefix for modified lines\n * - Comment variants: \"// +\", \"// -\", \"// !\"\n *\n * The markers are stripped from the displayed code for clean output\n *\n * @returns Shiki transformer\n */\nexport function createDiffTransformer(): ShikiTransformer {\n  // Store original code lines for diff detection\n  let codeLines: string[] = [];\n\n  return {\n    name: \"diff\",\n    preprocess(code) {\n      // Store original lines before Shiki processes them\n      codeLines = code.split(\"\\n\");\n      // Strip diff markers from the code that will be highlighted\n      return code\n        .split(\"\\n\")\n        .map((line) => stripDiffMarker(line))\n        .join(\"\\n\");\n    },\n    line(node, lineNumber) {\n      // lineNumber is 1-indexed by Shiki, convert to 0-indexed for array access\n      const originalLine = codeLines[lineNumber - 1];\n      if (!originalLine) return;\n\n      const diffType = detectDiffMarker(originalLine);\n      if (diffType) {\n        node.properties[\"data-diff\"] = diffType;\n      }\n    },\n  };\n}\n",
      "type": "registry:lib",
      "target": "components/ui/cubby-ui/code-block/lib/transformers/diff.ts"
    },
    {
      "path": "registry/default/code-block/lib/transformers/focus.ts",
      "content": "import type { ShikiTransformer } from \"shiki\";\nimport { parseLineRange } from \"./utils\";\n\n/**\n * Shiki transformer that adds focus attributes to lines\n * - Adds data-line-number to all lines for targeting\n * - Adds data-focused to specified lines\n *\n * @param lines - Line numbers to focus (1-indexed) or range string like \"1-3,5\"\n * @returns Shiki transformer\n */\nexport function createFocusTransformer(\n  lines: number[] | string | undefined,\n): ShikiTransformer {\n  const focusedLines = parseLineRange(lines);\n  const focusSet = new Set(focusedLines);\n\n  return {\n    name: \"focus\",\n    line(node, lineNumber) {\n      // lineNumber is already 1-indexed by Shiki\n      // Add line number to all lines for CSS targeting\n      node.properties[\"data-line-number\"] = String(lineNumber);\n\n      // Mark focused lines\n      if (focusSet.has(lineNumber)) {\n        node.properties[\"data-focused\"] = \"true\";\n      }\n    },\n  };\n}\n",
      "type": "registry:lib",
      "target": "components/ui/cubby-ui/code-block/lib/transformers/focus.ts"
    },
    {
      "path": "registry/default/code-block/lib/transformers/highlight-lines.ts",
      "content": "import type { ShikiTransformer } from \"shiki\";\nimport { parseLineRange } from \"./utils\";\n\n/**\n * Shiki transformer that adds data-highlighted attribute to specified lines\n *\n * @param lines - Line numbers to highlight (1-indexed) or range string like \"1-3,5,7-9\"\n * @returns Shiki transformer\n */\nexport function createHighlightLinesTransformer(\n  lines: number[] | string | undefined,\n): ShikiTransformer {\n  const highlightedLines = parseLineRange(lines);\n  const highlightSet = new Set(highlightedLines);\n\n  return {\n    name: \"highlight-lines\",\n    line(node, lineNumber) {\n      // lineNumber is already 1-indexed by Shiki\n      if (highlightSet.has(lineNumber)) {\n        node.properties[\"data-highlighted\"] = \"true\";\n      }\n    },\n  };\n}\n",
      "type": "registry:lib",
      "target": "components/ui/cubby-ui/code-block/lib/transformers/highlight-lines.ts"
    },
    {
      "path": "registry/default/code-block/lib/transformers/utils.ts",
      "content": "/**\n * Parse line range syntax into an array of line numbers\n * Supports formats:\n * - Array: [1, 2, 3]\n * - String: \"1,3,5\" or \"1-3,5,7-9\"\n * - Mixed: \"1-3,5\" becomes [1, 2, 3, 5]\n *\n * @param input - Line numbers as array or range string\n * @returns Array of line numbers (1-indexed)\n */\nexport function parseLineRange(\n  input: number[] | string | undefined,\n): number[] {\n  if (!input) return [];\n  if (Array.isArray(input)) return input;\n\n  const lines = new Set<number>();\n  const parts = input.split(\",\").map((s) => s.trim());\n\n  for (const part of parts) {\n    // Range format: \"1-3\"\n    if (part.includes(\"-\")) {\n      const [start, end] = part.split(\"-\").map((s) => Number.parseInt(s, 10));\n      if (!Number.isNaN(start) && !Number.isNaN(end)) {\n        for (let i = start; i <= end; i++) {\n          lines.add(i);\n        }\n      }\n    } else {\n      // Single number\n      const num = Number.parseInt(part, 10);\n      if (!Number.isNaN(num)) {\n        lines.add(num);\n      }\n    }\n  }\n\n  return Array.from(lines).sort((a, b) => a - b);\n}\n\n/**\n * Detect diff markers in code lines\n * Supports:\n * - Prefix markers: \"+\", \"-\", \"!\"\n * - Comment markers: \"// +\", \"// -\", \"// !\"\n *\n * @param line - Code line to check\n * @returns Diff type or null\n */\nexport function detectDiffMarker(\n  line: string,\n): \"added\" | \"removed\" | \"modified\" | null {\n  const trimmed = line.trim();\n\n  // Direct prefix markers\n  if (trimmed.startsWith(\"+\")) return \"added\";\n  if (trimmed.startsWith(\"-\")) return \"removed\";\n  if (trimmed.startsWith(\"!\")) return \"modified\";\n\n  // Comment markers (for languages that don't support bare +/-)\n  if (trimmed.startsWith(\"// +\")) return \"added\";\n  if (trimmed.startsWith(\"// -\")) return \"removed\";\n  if (trimmed.startsWith(\"// !\")) return \"modified\";\n\n  return null;\n}\n\n/**\n * Strip diff markers from a code line\n *\n * @param line - Code line with potential diff marker\n * @returns Line with marker removed\n */\nexport function stripDiffMarker(line: string): string {\n  const trimmed = line.trim();\n\n  // Direct prefix markers\n  if (trimmed.startsWith(\"+\") || trimmed.startsWith(\"-\") || trimmed.startsWith(\"!\")) {\n    return line.replace(/^(\\s*)[-+!](\\s?)/, \"$1\");\n  }\n\n  // Comment markers\n  if (trimmed.startsWith(\"// +\") || trimmed.startsWith(\"// -\") || trimmed.startsWith(\"// !\")) {\n    return line.replace(/^(\\s*)\\/\\/\\s*[-+!](\\s?)/, \"$1\");\n  }\n\n  return line;\n}\n",
      "type": "registry:lib",
      "target": "components/ui/cubby-ui/code-block/lib/transformers/utils.ts"
    }
  ],
  "type": "registry:ui"
}