File size: 1,809 Bytes
eb1202c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | import katex from "katex"
import { Marked, type MarkedExtension, type Tokens } from "marked"
import markedShiki from "marked-shiki"
export function createMarkdownParser(highlight: (code: string, language: string) => string | Promise<string>) {
return new Marked(
{
renderer: {
link({ href, title, text }) {
const titleAttr = title ? ` title="${title}"` : ""
return `<a href="${href}"${titleAttr} class="external-link" target="_blank" rel="noopener noreferrer">${text}</a>`
},
},
},
katexExtension,
markedShiki({ highlight }),
)
}
const inlineMathRegex = /^\\\(((?:\\.|[^\\\n])*?)\\\)/
const blockMathRegex = /^\$\$\n([\s\S]+?)\n\$\$(?:\n|$)/
const katexExtension: MarkedExtension = {
extensions: [
{
name: "inlineKatex",
level: "inline",
start(src) {
const index = src.indexOf("\\(")
if (index === -1) return
return index
},
tokenizer(src) {
const match = src.match(inlineMathRegex)
if (!match) return
return {
type: "inlineKatex",
raw: match[0],
text: match[1].trim(),
displayMode: false,
}
},
renderer: renderKatexToken,
},
{
name: "blockKatex",
level: "block",
tokenizer(src) {
const match = src.match(blockMathRegex)
if (!match) return
return {
type: "blockKatex",
raw: match[0],
text: match[1].trim(),
displayMode: true,
}
},
renderer: renderKatexToken,
},
],
}
function renderKatexToken(token: Tokens.Generic) {
return katex.renderToString(typeof token.text === "string" ? token.text : "", {
displayMode: token.displayMode === true,
throwOnError: false,
})
}
|