'use client' import { useEffect, useMemo, useState } from 'react' import katex from 'katex' import { marked } from 'marked' import { sanitizeRichHtml } from '@/lib/sanitize-content' import 'katex/dist/katex.min.css' /** * Render demo `speak`: light markdown + inline $KaTeX$ via the note pipeline pieces. */ export function DemoSpeak({ speak, className }: { speak: string; className?: string }) { const html = useMemo(() => { const placeholders: string[] = [] const withSlots = speak.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 md = marked.parse(withSlots, { gfm: true, breaks: true }) as string placeholders.forEach((frag, i) => { md = md.replace(`%%KATEX${i}%%`, frag) }) return sanitizeRichHtml(md) }, [speak]) return (
) } export function useDarkMode(): boolean { const [isDark, setIsDark] = useState(() => typeof document !== 'undefined' ? document.documentElement.classList.contains('dark') : false ) useEffect(() => { const check = () => setIsDark(document.documentElement.classList.contains('dark')) check() const obs = new MutationObserver(check) obs.observe(document.documentElement, { attributes: true, attributeFilter: ['class'], }) return () => obs.disconnect() }, []) return isDark }