'use client' import { useMemo } from 'react' import katex from 'katex' import { marked } from 'marked' import { sanitizeRichHtml } from '@/lib/sanitize-content' import 'katex/dist/katex.min.css' import { cn } from '@/lib/utils' /** Light markdown + inline $KaTeX$ for page prose / callouts / speak. */ export function PageMd({ md, className, }: { md: string className?: string }) { const html = useMemo(() => { const placeholders: string[] = [] const withSlots = md.replace(/\$([^$]+)\$/g, (_, tex: string) => { const i = placeholders.length try { placeholders.push( katex.renderToString(tex, { displayMode: false, throwOnError: false }) ) } catch { placeholders.push(tex) } return `%%KATEX${i}%%` }) let out = marked.parse(withSlots, { gfm: true, breaks: true }) as string placeholders.forEach((frag, i) => { // function replacement — $', $`, $& in KaTeX HTML must not be interpreted out = out.replace(`%%KATEX${i}%%`, () => frag) }) return sanitizeRichHtml(out) }, [md]) return (
) } export function PageFormula({ tex, caption, }: { tex: string caption?: string }) { const html = useMemo(() => { try { return katex.renderToString(tex, { displayMode: true, throwOnError: false, }) } catch { return tex } }, [tex]) return (