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