This commit is contained in:
root
2026-07-05 22:44:04 +00:00
parent 2dac119791
commit b0ae5dd740
+73 -17
View File
@@ -353,37 +353,93 @@ marked.use({
renderer, renderer,
}) })
function decodeHTMLEntities(text: string): string {
return text
.replace(/&/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
}
function renderMarkdown(text: string): string { function renderMarkdown(text: string): string {
if (!text) return '' if (!text) return ''
// 0. Extract and protect Mermaid blocks // 0. Extract and protect Mermaid blocks
const mermaidBlocks: string[] = [] const mermaidBlocks: string[] = []
let processed = text.replace(/```mermaid\r?\\n([\s\S]*?)\r?\\n```/g, (match, code) => { let workingText = text
workingText = workingText.replace(/```mermaid\r?\\n([\s\S]*?)\r?\\n```/g, (match, code) => {
const placeholder = `MERMAIDBLOCKPLACEHOLDER${mermaidBlocks.length}` const placeholder = `MERMAIDBLOCKPLACEHOLDER${mermaidBlocks.length}`
mermaidBlocks.push(code.trim()) mermaidBlocks.push(code.trim())
return placeholder return placeholder
}) })
// 1. Render display math $$...$$ const mathBlocks: { placeholder: string; isBlock: boolean; rawExpr: string; renderedHtml: string }[] = []
processed = processed.replace(/\$\$([\s\S]+?)\$\$/g, (match, math) => { let placeholderCount = 0
try {
return `<div class="math-display">${katex.renderToString(math.trim(), { displayMode: true, throwOnError: false })}</div>` // Normalize slash/backslash ctdot representations to standard cdots
} catch (e) { workingText = workingText
return match .replace(/\\ctdot/g, '\\cdots')
} .replace(/\/ctdot/g, '\\cdots')
// 1. Extract block math: $$...$$ and \[...\]
workingText = workingText.replace(/\$\$(.*?)\$\$/gs, (match, expr) => {
const placeholder = `MATHBLOCKPLACEHOLDER${placeholderCount++}`
mathBlocks.push({ placeholder, isBlock: true, rawExpr: expr, renderedHtml: '' })
return placeholder
}) })
// 2. Render inline math $...$ workingText = workingText.replace(/\\\[(.*?)\\\]/gs, (match, expr) => {
processed = processed.replace(/\$([^\n]+?)\$/g, (match, math) => { const placeholder = `MATHBLOCKPLACEHOLDER${placeholderCount++}`
try { mathBlocks.push({ placeholder, isBlock: true, rawExpr: expr, renderedHtml: '' })
return `<span class="math-inline">${katex.renderToString(math.trim(), { displayMode: false, throwOnError: false })}</span>` return placeholder
} catch (e) {
return match
}
}) })
// 3. Render markdown // 2. Extract inline math: $...$ and \(...\)
let html = marked.parse(processed) as string workingText = workingText.replace(/(?<!\$)\$([^\$]+)\$(?!\$)/g, (match, expr) => {
const placeholder = `INLINEMATHPLACEHOLDER${placeholderCount++}`
mathBlocks.push({ placeholder, isBlock: false, rawExpr: expr, renderedHtml: '' })
return placeholder
})
workingText = workingText.replace(/\\\(.*?\\\)/g, (match) => {
const expr = match.slice(2, -2)
const placeholder = `INLINEMATHPLACEHOLDER${placeholderCount++}`
mathBlocks.push({ placeholder, isBlock: false, rawExpr: expr, renderedHtml: '' })
return placeholder
})
// 3. Render Markdown with marked.parse
let html = ''
try {
html = marked.parse(workingText) as string
} catch (e) {
console.error("Markdown parsing failed:", e)
html = workingText
}
// 4. Render KaTeX and swap placeholders
for (const block of mathBlocks) {
try {
let cleanExpr = decodeHTMLEntities(block.rawExpr.trim())
cleanExpr = cleanExpr.replace(/\\ctdot/g, '\\cdots').replace(/\/ctdot/g, '\\cdots')
const rendered = katex.renderToString(cleanExpr, {
displayMode: block.isBlock,
throwOnError: false
})
block.renderedHtml = block.isBlock
? `<div class="math-display">${rendered}</div>`
: `<span class="math-inline">${rendered}</span>`
} catch (err) {
console.error("KaTeX parse error for expr:", block.rawExpr, err)
const delim = block.isBlock ? '$$' : '$'
block.renderedHtml = block.isBlock
? `<div class="math-display">${delim}${block.rawExpr}${delim}</div>`
: `<span class="math-inline">${delim}${block.rawExpr}${delim}</span>`
}
html = html.replace(block.placeholder, block.renderedHtml)
}
// 5. Restore protected Mermaid blocks // 5. Restore protected Mermaid blocks
mermaidBlocks.forEach((code, idx) => { mermaidBlocks.forEach((code, idx) => {