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 };
}