{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ui-code-renderer",
  "title": "UI Code Renderer",
  "author": "AlgorizeTech",
  "description": "Shiki-backed syntax highlighting for a single snippet, with matched light and dark themes, a shared cached highlighter and a non-shifting fallback while a cold snippet resolves.",
  "dependencies": [
    "shiki"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "src/components/custom/ui/code-renderer.tsx",
      "content": "\"use client\";\nimport type { BuiltinLanguage } from \"shiki\";\nimport { useEffect, useState } from \"react\";\nimport { highlight, highlightCached, warmHighlighter } from \"./code-renderer-core\";\n\n/* -------------------------------------------------------------------------- */\n/*  Component                                                                 */\n/* -------------------------------------------------------------------------- */\n\ninterface CodeRendererProps {\n  code: string;\n  lang: BuiltinLanguage;\n}\n\n/** What is on screen, tied to the source it was produced from. */\ntype Rendered = {\n  code: string;\n  lang: string;\n  /** null while the highlighter is still resolving this snippet. */\n  html: string | null;\n};\n\nexport const CodeRenderer = ({ code, lang = \"tsx\" }: CodeRendererProps) => {\n  // Highlighted synchronously whenever the shared highlighter can answer (a\n  // cache hit, or a warm instance). That is the whole point: a prefetched\n  // snippet paints coloured on its FIRST frame instead of showing raw text and\n  // reflowing a moment later.\n  const [rendered, setRendered] = useState<Rendered>(() => ({\n    code,\n    lang,\n    html: highlightCached(code, lang),\n  }));\n\n  // New snippet: re-derive during render, so switching panels can never paint\n  // the previous one's markup for a frame.\n  if (rendered.code !== code || rendered.lang !== lang) {\n    setRendered({ code, lang, html: highlightCached(code, lang) });\n  }\n\n  const html =\n    rendered.code === code && rendered.lang === lang ? rendered.html : null;\n\n  useEffect(() => {\n    warmHighlighter();\n  }, []);\n\n  useEffect(() => {\n    if (html !== null) return;\n\n    // A slow highlight must not land after the reader has moved on — panels\n    // switch faster than a cold Shiki boot resolves.\n    let cancelled = false;\n    highlight(code, lang)\n      .then((result) => {\n        if (!cancelled) setRendered({ code, lang, html: result });\n      })\n      .catch((error) => {\n        console.error(\"Error highlighting code:\", error);\n      });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [code, lang, html]);\n\n  // Placeholder mirrors every `.shiki` metric in the stylesheet, so it occupies\n  // exactly the box the highlighted markup will: the swap fills in colour\n  // without moving a single line.\n  if (html === null) {\n    return (\n      <div className=\"font-normal text-sm\">\n        <pre className=\"shiki-pending\">\n          <code>{code}</code>\n        </pre>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"font-normal text-sm\">\n      <div dangerouslySetInnerHTML={{ __html: html }} />\n    </div>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/custom/ui/code-renderer.tsx"
    },
    {
      "path": "src/components/custom/ui/code-renderer-core.ts",
      "content": "\"use client\";\n\nimport { getSingletonHighlighter, type BuiltinLanguage, type Highlighter } from \"shiki\";\n\nconst THEMES = { light: \"github-light\", dark: \"github-dark-default\" } as const;\n\nconst EAGER_LANGS: BuiltinLanguage[] = [\n  \"tsx\",\n  \"jsx\",\n  \"typescript\",\n  \"javascript\",\n  \"bash\",\n  \"json\",\n  \"css\",\n];\n\nconst PLAIN_LANGS = new Set([\"text\", \"plaintext\", \"txt\", \"plain\", \"ansi\"]);\n\nconst MAX_CACHE_ENTRIES = 120;\n\nconst cache = new Map<string, string>();\n\nlet highlighter: Highlighter | null = null;\n\nlet highlighterPromise: Promise<Highlighter> | null = null;\n\nconst cacheKey = (code: string, lang: string) =>\n  `${lang.length}:${lang}${code}`;\n\nfunction remember(key: string, html: string): string {\n  if (cache.size >= MAX_CACHE_ENTRIES) {\n    const oldest = cache.keys().next().value;\n    if (oldest !== undefined) cache.delete(oldest);\n  }\n  cache.set(key, html);\n  return html;\n}\n\nfunction render(instance: Highlighter, code: string, lang: string): string {\n  return instance.codeToHtml(code, {\n    lang,\n    themes: THEMES,\n    defaultColor: false,\n    transformers: [\n      {\n        name: \"remove-pre-bg\",\n        pre(node) {\n          // Drop the inline background so the stylesheet owns the surface.\n          delete node.properties.style;\n        },\n      },\n    ],\n  });\n}\n\nconst isReady = (instance: Highlighter, lang: string) =>\n  PLAIN_LANGS.has(lang) || instance.getLoadedLanguages().includes(lang);\n\nexport function getHighlighter(): Promise<Highlighter> {\n  if (!highlighterPromise) {\n    highlighterPromise = getSingletonHighlighter({\n      themes: [THEMES.light, THEMES.dark],\n      langs: EAGER_LANGS,\n    })\n      .then((instance) => {\n        highlighter = instance;\n        return instance;\n      })\n      .catch((error) => {\n        // A failed boot must not poison every later call.\n        highlighterPromise = null;\n        throw error;\n      });\n  }\n  return highlighterPromise;\n}\n\nexport function highlightCached(code: string, lang: string): string | null {\n  const key = cacheKey(code, lang);\n  const hit = cache.get(key);\n  if (hit !== undefined) return hit;\n\n  const instance = highlighter;\n  if (!instance || !isReady(instance, lang)) return null;\n\n  try {\n    return remember(key, render(instance, code, lang));\n  } catch {\n    return null;\n  }\n}\n\nexport async function highlight(code: string, lang: string): Promise<string> {\n  const key = cacheKey(code, lang);\n  const hit = cache.get(key);\n  if (hit !== undefined) return hit;\n\n  const instance = await getHighlighter();\n\n  if (!isReady(instance, lang)) {\n    try {\n      await instance.loadLanguage(lang as BuiltinLanguage);\n    } catch {\n      // Unknown grammar (mdx fences accept anything) — fall back below.\n    }\n  }\n\n  try {\n    return remember(key, render(instance, code, lang));\n  } catch {\n    return remember(key, render(instance, code, \"text\"));\n  }\n}\n\nfunction onIdle(run: () => void): void {\n  if (typeof window === \"undefined\") return;\n  const requestIdle = (\n    window as Window & {\n      requestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n    }\n  ).requestIdleCallback;\n  if (typeof requestIdle === \"function\") requestIdle(run, { timeout: 2000 });\n  else window.setTimeout(run, 200);\n}\n\nexport function warmHighlighter(): void {\n  if (highlighter || highlighterPromise) return;\n  onIdle(() => {\n    void getHighlighter().catch(() => {});\n  });\n}\n\nconst prefetching = new Set<string>();\n\nexport function prefetchHighlight(code: string, lang: string): void {\n  if (!code) return;\n  const key = cacheKey(code, lang);\n  if (cache.has(key) || prefetching.has(key)) return;\n\n  prefetching.add(key);\n  onIdle(() => {\n    void highlight(code, lang)\n      .catch(() => {})\n      .finally(() => prefetching.delete(key));\n  });\n}\n",
      "type": "registry:ui",
      "target": "components/custom/ui/code-renderer-core.ts"
    }
  ],
  "docs": "Named export: CodeRenderer, plus `warmHighlighter` and `prefetchHighlight` for callers that know a snippet before it is shown — prefetching one makes its first paint already highlighted, with no unstyled flash. Takes `code` and a Shiki `lang`. One Shiki instance and one markup cache are shared by every block on the page. A snippet that is still cold renders inside `pre.shiki-pending`; style that class to match your `.shiki` metrics so the swap does not shift the page. Pulled in automatically by `ui-codeblocks` — install it directly only if you are building your own code surface.",
  "categories": [
    "code"
  ],
  "type": "registry:ui"
}
