import katex from "katex"; /** * Renders $…$ / $$…$$ / \( \) / \[ \] segments of a plain-text field (abstracts, findings) to KaTeX HTML. * Text outside math is HTML-escaped. Used on the server only; output is trusted markup built from escaped input. */ export function renderMath(text: string): { html: string; hasMath: boolean } { const esc = (s: string) => s.replace(/&/g, "&").replace(//g, ">"); const re = /\$\$([\s\S]+?)\$\$|\\\[([\s\S]+?)\\\]|\\\(([\s\S]+?)\\\)|\$([^$\n]+?)\$/g; let out = ""; let last = 0; let hasMath = false; for (const m of text.matchAll(re)) { const idx = m.index ?? 0; out += esc(text.slice(last, idx)); const display = m[1] !== undefined || m[2] !== undefined; const src = (m[1] ?? m[2] ?? m[3] ?? m[4] ?? "").trim(); try { const html = katex.renderToString(src, { displayMode: display, throwOnError: false, output: "htmlAndMathml", strict: "ignore", }); out += display ? `${html}` : `${html}`; hasMath = true; } catch { out += esc(m[0]); } last = idx + m[0].length; } out += esc(text.slice(last)); return { html: out, hasMath }; }