// Prose is written as markdown with TeX between dollar signs. KaTeX renders // the maths first, into placeholders that marked will not touch, and the // result is put in with dangerouslySetInnerHTML: the source is ours, written // at build time, not anything a reader supplies. import { useMemo } from 'react' import { marked } from 'marked' import katex from 'katex' import 'katex/dist/katex.min.css' function renderMath(src: string): { text: string; slots: string[] } { const slots: string[] = [] const keep = (html: string) => { slots.push(html) return `@@MATH${slots.length - 1}@@` } // display maths first, so that $$...$$ is not eaten by the inline rule let text = src.replace(/\$\$([\s\S]+?)\$\$/g, (_m, tex: string) => keep(katex.renderToString(tex.trim(), { displayMode: true, throwOnError: false })), ) text = text.replace(/(^|[^\\$])\$([^$\n]+?)\$/g, (_m, pre: string, tex: string) => pre + keep(katex.renderToString(tex.trim(), { displayMode: false, throwOnError: false })), ) return { text, slots } } export function markdownToHtml(src: string): string { const { text, slots } = renderMath(src) const html = marked.parse(text, { async: false }) as string return html.replace(/@@MATH(\d+)@@/g, (_m, i: string) => slots[Number(i)]) } export default function Markdown({ src }: { src: string }) { const html = useMemo(() => markdownToHtml(src), [src]) return
}