// HighlightedCode — Shiki-powered syntax highlighting as a reusable // primitive. Apps drop `` anywhere and // get dual-theme (light + dark) output that flips with the // `.dark` class on . // // Lazy + shared singleton: the first usage triggers an async Shiki // load (~200ms WASM bootstrap), subsequent renders share the same // highlighter instance. Until Shiki is ready, the component renders // plain escaped code so the layout doesn't jump. // // Browser-safe via dynamic import; runs on the Node side too (SSR / // `voltro build` waits for it via `whenReady`). import { useEffect, useMemo, useState, type ReactNode, } from 'react' import type { Highlighter } from 'shiki' import { cn } from '../cn' declare global { var __voltroShikiHighlighter: Highlighter | null | undefined var __voltroShikiHighlighterPromise: Promise | null | undefined } // Languages we ship support for. Add to this list as new doc + landing // snippets need them; loading is one-shot at highlighter init. export const SHIKI_LANGS = [ 'text', 'ts', 'tsx', 'typescript', 'js', 'jsx', 'json', 'bash', 'sh', 'shell', 'sql', 'yaml', 'toml', 'md', 'mdx', 'html', 'css', 'diff', ] as const export type ShikiLang = typeof SHIKI_LANGS[number] // Global singleton — shared across HighlightedCode calls, HMR reloads, // and docs-content rendering so we only pay the WASM bootstrap once. const ensure = async (): Promise => { if (globalThis.__voltroShikiHighlighter) return globalThis.__voltroShikiHighlighter if (globalThis.__voltroShikiHighlighterPromise) { return globalThis.__voltroShikiHighlighterPromise } globalThis.__voltroShikiHighlighterPromise = (async () => { const { createHighlighter } = await import('shiki') const highlighter = await createHighlighter({ themes: ['github-dark-dimmed', 'github-light'], langs: SHIKI_LANGS as unknown as string[], }) globalThis.__voltroShikiHighlighter = highlighter return highlighter })() return globalThis.__voltroShikiHighlighterPromise } /** Promise that resolves once Shiki has loaded — for SSR/build callers * that want to await before rendering. */ export const whenHighlighterReady = (): Promise => ensure().then(() => { /* discard */ }) const escapeHtml = (s: string): string => s.replace(/&/g, '&').replace(//g, '>') interface HighlightedCodeProps { readonly code: string readonly lang?: ShikiLang /** Outer wrapper class. */ readonly className?: string } export const HighlightedCode = ({ code, lang = 'ts', className, }: HighlightedCodeProps): ReactNode => { const [html, setHtml] = useState(null) // Render the highlighted HTML when Shiki is ready. Re-runs only when // code/lang changes — the highlighter itself is stable. useEffect(() => { let cancelled = false ensure().then((hl) => { if (cancelled || !hl) return try { const out = hl.codeToHtml(code, { lang, themes: { light: 'github-light', dark: 'github-dark-dimmed' }, defaultColor: false, }) setHtml(out) } catch { // Lang missing → fall through to plain escaped code. } }) return () => { cancelled = true } }, [code, lang]) // Cached escaped fallback — what renders before Shiki ships its // first highlighted batch. const fallback = useMemo(() => escapeHtml(code), [code]) if (html) { return (
) } return (
      
    
) }