{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dynamic-codeblock",
  "title": "Dynamic code block",
  "description": "DynamicCodeBlock: a code block highlighted with Shiki in the browser, for code that's only known at runtime.",
  "dependencies": [
    "cn",
    "fumadocs-core@^16.17.8",
    "shiki@^4.6.0"
  ],
  "registryDependencies": [
    "@docscn/codeblock"
  ],
  "files": [
    {
      "path": "registry/base/docs/components/dynamic-codeblock.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport { defaultShikiFactory } from \"fumadocs-core/highlight/shiki/full\";\nimport * as Base from \"./dynamic-codeblock.core\";\n\nexport type DynamicCodeblockProps = Omit<\n  Base.DynamicCodeblockProps,\n  \"highlighter\" | \"options\"\n> & {\n  options?: Base.DynamicCodeblockProps[\"options\"];\n};\n\nexport function DynamicCodeBlock(props: DynamicCodeblockProps) {\n  return (\n    <Base.DynamicCodeBlock\n      highlighter={() => defaultShikiFactory.getOrInit()}\n      options={{ themes: { light: \"github-light\", dark: \"github-dark\" } }}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/dynamic-codeblock.tsx"
    },
    {
      "path": "registry/base/docs/components/dynamic-codeblock.core.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport { CodeBlock, type CodeBlockProps, Pre } from \"./codeblock\";\nimport type { UseShikiOptions } from \"fumadocs-core/highlight/shiki/react\";\nimport type { Highlighter } from \"fumadocs-core/highlight\";\nimport { createShikiHighlighter } from \"fumadocs-core/highlight/shiki\";\nimport { type Components, toJsx } from \"fumadocs-core/utils/hast-to-jsx\";\nimport { cn } from \"cn\";\nimport {\n  type ComponentProps,\n  createContext,\n  type FC,\n  Suspense,\n  use,\n  useState,\n} from \"react\";\nimport type { HighlighterCore } from \"shiki\";\n\nexport interface DynamicCodeblockProps {\n  highlighter:\n    HighlighterCore | (() => HighlighterCore | PromiseLike<HighlighterCore>);\n  lang: string;\n  code: string;\n  /**\n   * Extra props for the underlying `<CodeBlock />` component.\n   *\n   * Ignored if you defined your own `pre` component in `options.components`.\n   */\n  codeblock?: CodeBlockProps;\n  /**\n   * Wrap in React `<Suspense />` and provide a fallback.\n   *\n   * @defaultValue true\n   */\n  wrapInSuspense?: boolean;\n  options: DistributiveOmit<UseShikiOptions, \"lang\">;\n}\n\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown\n  ? Omit<T, K>\n  : never;\n\nconst PropsContext = createContext<CodeBlockProps | undefined>(undefined);\n\nfunction DefaultPre(props: ComponentProps<\"pre\">) {\n  const extraProps = use(PropsContext);\n\n  return (\n    <CodeBlock\n      {...props}\n      {...extraProps}\n      className={cn(\"my-0\", props.className, extraProps?.className)}\n    >\n      <Pre>{props.children}</Pre>\n    </CodeBlock>\n  );\n}\n\n/**\n * Create a code block that highlights with your own highlighter.\n */\nexport function createDynamicCodeBlock({\n  highlighter,\n}: {\n  highlighter: Highlighter;\n}) {\n  return function DynamicCodeBlock(\n    props: Omit<DynamicCodeblockProps, \"highlighter\" | \"options\">,\n  ) {\n    return (\n      <Base\n        highlighter={highlighter}\n        components={{ pre: DefaultPre }}\n        {...props}\n      />\n    );\n  };\n}\n\nexport function DynamicCodeBlock({\n  highlighter,\n  options,\n  wrapInSuspense = true,\n  ...props\n}: DynamicCodeblockProps) {\n  // created once from the initial props, like upstream's `useRef()`\n  const [instance] = useState(() =>\n    createShikiHighlighter(highlighter, {\n      defaultColor: false,\n      ...options,\n    }),\n  );\n  const node = (\n    <Base\n      highlighter={instance}\n      components={{ pre: DefaultPre, ...options?.components }}\n      wrapInSuspense={wrapInSuspense}\n      {...props}\n    />\n  );\n\n  // a parent boundary suspending on it would discard `instance`\n  return wrapInSuspense ? node : <Suspense>{node}</Suspense>;\n}\n\nfunction Base({\n  highlighter,\n  lang,\n  code,\n  codeblock,\n  wrapInSuspense = true,\n  components,\n}: Omit<DynamicCodeblockProps, \"highlighter\" | \"options\"> & {\n  highlighter: Highlighter;\n  components: Partial<Components>;\n}) {\n  let node = (\n    <Highlighted\n      highlighter={highlighter}\n      lang={lang}\n      code={code}\n      components={components}\n    />\n  );\n  // only loading suspends, React keeps the fallback of large boundaries in server HTML\n  if (wrapInSuspense && highlighter.loadLanguage) {\n    node = (\n      <Suspense fallback={<Placeholder code={code} components={components} />}>\n        {node}\n      </Suspense>\n    );\n  }\n\n  return <PropsContext value={codeblock}>{node}</PropsContext>;\n}\n\nconst languages = new WeakMap<Highlighter, Map<string, Promise<void>>>();\n\nfunction Highlighted({\n  highlighter,\n  lang,\n  code,\n  components,\n}: {\n  highlighter: Highlighter;\n  lang: string;\n  code: string;\n  components: Partial<Components>;\n}) {\n  if (highlighter.loadLanguage) {\n    let loading = languages.get(highlighter);\n    if (!loading) languages.set(highlighter, (loading = new Map()));\n    let promise = loading.get(lang);\n    if (!promise) loading.set(lang, (promise = highlighter.loadLanguage(lang)));\n    use(promise);\n  }\n\n  return toJsx(highlighter.codeToHast(code, { lang }), { components });\n}\n\nfunction Placeholder({\n  code,\n  components = {},\n}: {\n  code: string;\n  components: UseShikiOptions[\"components\"];\n}) {\n  const { pre: Pre = \"pre\", code: Code = \"code\" } = components as Record<\n    string,\n    FC\n  >;\n\n  return (\n    <Pre>\n      <Code>\n        {code.split(\"\\n\").map((line, i) => (\n          <span key={i} className=\"line\">\n            {line}\n          </span>\n        ))}\n      </Code>\n    </Pre>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/components/dynamic-codeblock.core.tsx"
    }
  ],
  "type": "registry:component"
}