import katex from "katex"; import { marked } from "marked"; marked.setOptions({ breaks: true, gfm: true, }); function replaceAll(value: string, entries: Array<{ token: string; replacement: string }>) { let next = value; for (const entry of entries) { next = next.replaceAll(entry.token, entry.replacement); } return next; } export function renderAdminMarkdown(markdown: string): string { const codeBlocks: Array<{ token: string; replacement: string }> = []; const mathTokens: Array<{ token: string; replacement: string }> = []; let next = markdown || ""; next = next.replace(/```[\s\S]*?```/g, (block) => { const token = `@@CODE_BLOCK_${codeBlocks.length}@@`; codeBlocks.push({ token, replacement: block }); return token; }); next = next.replace(/\$\$([\s\S]+?)\$\$/g, (_match, expression: string) => { const token = `@@BLOCK_MATH_${mathTokens.length}@@`; mathTokens.push({ token, replacement: katex.renderToString(expression.trim(), { displayMode: true, throwOnError: false, strict: "ignore", }), }); return `\n\n${token}\n\n`; }); next = next.replace(/(? { const token = `@@INLINE_MATH_${mathTokens.length}@@`; mathTokens.push({ token, replacement: katex.renderToString(expression.trim(), { displayMode: false, throwOnError: false, strict: "ignore", }), }); return token; }); next = replaceAll(next, codeBlocks); const html = marked.parse(next) as string; return replaceAll(html, mathTokens); }