import katex from "katex"; import { marked } from "marked"; marked.setOptions({ breaks: true, gfm: true, }); function renderInlineMath(expression: string) { try { return katex.renderToString(expression, { displayMode: false, throwOnError: false, }); } catch { return expression; } } function renderBlockMath(expression: string) { try { return `
${katex.renderToString(expression, { displayMode: true, throwOnError: false, })}
`; } catch { return expression; } } function replaceMathSegments(markdown: string) { const codeTokens: string[] = []; const codeTokenPattern = /```[\s\S]*?```|`[^`\n]+`/g; let prepared = markdown.replace(codeTokenPattern, (match) => { const token = `@@ADMIN_CODE_TOKEN_${codeTokens.length}@@`; codeTokens.push(match); return token; }); prepared = prepared.replace(/\$\$([\s\S]+?)\$\$/g, (_, expression: string) => renderBlockMath(expression.trim()), ); prepared = prepared.replace( /(^|[^\$\\])\$([^\n$]+?)\$/g, (_, prefix: string, expression: string) => `${prefix}${renderInlineMath(expression.trim())}`, ); return prepared.replace(/@@ADMIN_CODE_TOKEN_(\d+)@@/g, (_, index: string) => { const tokenIndex = Number.parseInt(index, 10); return codeTokens[tokenIndex] ?? ""; }); } export function renderMarkdownPreview(markdown: string) { return marked.parse(replaceMathSegments(markdown || "")) as string; }