添加对 markdown-it-mathjax3-pro 的支持,增强数学公式渲染功能

This commit is contained in:
2025-10-23 21:39:45 +08:00
parent 7317ac4864
commit c3d2f680c0
3 changed files with 120 additions and 1 deletions

View File

@@ -21,6 +21,7 @@
"highlight.js": "^11.11.1",
"markdown-it": "^14.1.0",
"markdown-it-highlightjs": "^4.2.0",
"markdown-it-mathjax3-pro": "^1.0.0",
"pinia": "^3.0.3",
"tailwindcss": "^4.1.15",
"vue": "^3.5.22",

68
pnpm-lock.yaml generated
View File

@@ -23,6 +23,9 @@ importers:
markdown-it-highlightjs:
specifier: ^4.2.0
version: 4.2.0
markdown-it-mathjax3-pro:
specifier: ^1.0.0
version: 1.0.0(markdown-it@14.1.0)
pinia:
specifier: ^3.0.3
version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))
@@ -916,6 +919,10 @@ packages:
vue:
optional: true
'@xmldom/xmldom@0.9.8':
resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==}
engines: {node: '>=14.6'}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -1108,6 +1115,10 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
commander@13.1.0:
resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==}
engines: {node: '>=18'}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
@@ -1400,6 +1411,10 @@ packages:
jiti:
optional: true
esm@3.2.25:
resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==}
engines: {node: '>=6'}
espree@10.4.0:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -2038,6 +2053,12 @@ packages:
markdown-it-highlightjs@4.2.0:
resolution: {integrity: sha512-NC7pXE8KkOl6xWJVRNt8p6wgJVznXKsE0HgYGdk6DD2tn1l4L9f0ALf3VIoGVkotNU1uGQatSxfBF1zZPUMmuQ==}
markdown-it-mathjax3-pro@1.0.0:
resolution: {integrity: sha512-irEoz78IQ+iHbveeTeOJJ4hM/e29/PT+jFjF6y9PvTFBo96q5rv0F7bJ0/+8ZXhrzor/Njws+QaKZC8p501EAQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
markdown-it: '>=12.0.0'
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
@@ -2046,6 +2067,10 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mathjax-full@3.2.2:
resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==}
deprecated: Version 4 replaces this package with the scoped package @mathjax/src
mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
@@ -2064,6 +2089,9 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
mhchemparser@4.2.1:
resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==}
micromatch@3.1.0:
resolution: {integrity: sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==}
engines: {node: '>=0.10.0'}
@@ -2089,6 +2117,9 @@ packages:
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
engines: {node: '>=0.10.0'}
mj-context-menu@0.6.1:
resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==}
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
@@ -2509,6 +2540,10 @@ packages:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
speech-rule-engine@4.1.2:
resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==}
hasBin: true
split-string@3.1.0:
resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
engines: {node: '>=0.10.0'}
@@ -2876,6 +2911,9 @@ packages:
engines: {node: ^18.17.0 || >=20.5.0}
hasBin: true
wicked-good-xpath@1.3.0:
resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==}
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -3723,6 +3761,8 @@ snapshots:
typescript: 5.9.3
vue: 3.5.22(typescript@5.9.3)
'@xmldom/xmldom@0.9.8': {}
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -3947,6 +3987,8 @@ snapshots:
color-name@1.1.4: {}
commander@13.1.0: {}
commander@7.2.0: {}
component-emitter@1.3.1: {}
@@ -4321,6 +4363,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
esm@3.2.25: {}
espree@10.4.0:
dependencies:
acorn: 8.15.0
@@ -4918,6 +4962,11 @@ snapshots:
dependencies:
highlight.js: 11.11.1
markdown-it-mathjax3-pro@1.0.0(markdown-it@14.1.0):
dependencies:
markdown-it: 14.1.0
mathjax-full: 3.2.2
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
@@ -4929,6 +4978,13 @@ snapshots:
math-intrinsics@1.1.0: {}
mathjax-full@3.2.2:
dependencies:
esm: 3.2.25
mhchemparser: 4.2.1
mj-context-menu: 0.6.1
speech-rule-engine: 4.1.2
mdn-data@2.0.14: {}
mdurl@2.0.0: {}
@@ -4941,6 +4997,8 @@ snapshots:
merge2@1.4.1: {}
mhchemparser@4.2.1: {}
micromatch@3.1.0:
dependencies:
arr-diff: 4.0.0
@@ -4981,6 +5039,8 @@ snapshots:
for-in: 1.0.2
is-extendable: 1.0.1
mj-context-menu@0.6.1: {}
mlly@1.8.0:
dependencies:
acorn: 8.15.0
@@ -5452,6 +5512,12 @@ snapshots:
speakingurl@14.0.1: {}
speech-rule-engine@4.1.2:
dependencies:
'@xmldom/xmldom': 0.9.8
commander: 13.1.0
wicked-good-xpath: 1.3.0
split-string@3.1.0:
dependencies:
extend-shallow: 3.0.2
@@ -5905,6 +5971,8 @@ snapshots:
dependencies:
isexe: 3.1.1
wicked-good-xpath@1.3.0: {}
word-wrap@1.2.5: {}
wsl-utils@0.1.0:

View File

@@ -9,6 +9,7 @@ import { ref, watch, nextTick } from 'vue'
import { message } from 'ant-design-vue'
import MarkdownIt from 'markdown-it'
import markdownItHighlightJs from 'markdown-it-highlightjs'
import MarkdownItMathJaX3PRO from 'markdown-it-mathjax3-pro'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css' // 引入代码高亮样式
@@ -82,6 +83,36 @@ const props = withDefaults(
// 解析后的 HTML
const renderedContent = ref('')
const MATHJAX_STYLE_ATTR = 'data-stream-markdown-mathjax-style'
let mathJaxStyleElement: HTMLStyleElement | null = null
type InjectContentItem = {
type: string
content?: string
}
const applyMathJaxStyles = (injectContent?: InjectContentItem[]) => {
if (typeof document === 'undefined' || !injectContent?.length) return
const styleItem = injectContent.find((item) => item.type === 'style' && item.content)
if (!styleItem?.content) return
if (!mathJaxStyleElement || !document.head.contains(mathJaxStyleElement)) {
mathJaxStyleElement =
document.head.querySelector<HTMLStyleElement>(`style[${MATHJAX_STYLE_ATTR}]`) ??
document.createElement('style')
if (!mathJaxStyleElement.hasAttribute(MATHJAX_STYLE_ATTR)) {
mathJaxStyleElement.setAttribute(MATHJAX_STYLE_ATTR, 'true')
mathJaxStyleElement.type = 'text/css'
document.head.appendChild(mathJaxStyleElement)
}
}
if (mathJaxStyleElement.textContent !== styleItem.content) {
mathJaxStyleElement.textContent = styleItem.content
}
}
// 初始化 MarkdownIt
const md = new MarkdownIt({
@@ -100,6 +131,23 @@ md.use(markdownItHighlightJs, {
code: true, // 高亮内联代码
})
md.use(MarkdownItMathJaX3PRO, {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']],
tags: 'ams',
packages: ['base', 'ams', 'newcommand', 'configmacros']
},
chtml: {
fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2'
},
// 使用 SVG 输出代替 CHTML
svg: {
fontCache: 'local',
displayAlign: 'center'
}
});
type FenceRenderRule = NonNullable<InstanceType<typeof MarkdownIt>['renderer']['rules']['fence']>
// 保存默认的代码块渲染规则
@@ -166,7 +214,9 @@ watch(
(newVal) => {
if (newVal) {
// 渲染为 HTML
const html = md.render(newVal)
const env: { frontmatter?: { inject_content?: InjectContentItem[] } } = {}
const html = md.render(newVal, env)
applyMathJaxStyles(env.frontmatter?.inject_content)
renderedContent.value = html
// 确保复制功能在 DOM 更新后可用