From c85f291c72b168cc4714feb911144ddaec371bcc Mon Sep 17 00:00:00 2001 From: dqruid <31595220+dqruid@users.noreply.github.com> Date: Sat, 1 Aug 2026 01:25:43 +0800 Subject: [PATCH] Support WeChat data-formula math --- src/elements/math.base.ts | 36 +++++- src/elements/math.core.ts | 4 +- src/elements/math.full.ts | 4 +- tests/expected/math--wechat-data-formula.md | 106 ++++++++++++++++++ tests/fixtures/math--wechat-data-formula.html | 84 ++++++++++++++ 5 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 tests/expected/math--wechat-data-formula.md create mode 100644 tests/fixtures/math--wechat-data-formula.html diff --git a/src/elements/math.base.ts b/src/elements/math.base.ts index 768886c36..1e1b2a301 100644 --- a/src/elements/math.base.ts +++ b/src/elements/math.base.ts @@ -7,6 +7,19 @@ export interface MathData { isBlock: boolean; } +const DATA_FORMULA_BLOCK_TAGS = new Set([ + 'address', 'article', 'aside', 'blockquote', 'div', 'figure', + 'footer', 'header', 'main', 'p', 'section' +]); + +/** + * WeChat/mdnice renders the same formula in nested wrappers. Only the + * outermost data-formula element represents a formula in document flow; + * processing descendants as well would emit duplicate Markdown equations. + */ +export const isNestedDataFormula = (el: Element): boolean => + el.hasAttribute('data-formula') && Boolean(el.parentElement?.closest('[data-formula]')); + // --- MathJax CHTML (mjx-*) → MathML reconstruction ------------------------- // // When MathJax v3 renders with the CHTML output and the page ships no @@ -304,6 +317,10 @@ export const getBasicLatexFromElement = (el: Element): string | null => { if (dataMath) { return dataMath; } + const dataFormula = el.getAttribute('data-formula'); + if (dataFormula) { + return dataFormula; + } const parentDataEntry = el.parentElement?.classList.contains('hurmet-tex') ? el.parentElement.getAttribute('data-entry') : null; @@ -375,6 +392,22 @@ export const isBlockDisplay = (el: Element): boolean => { return true; } + // WeChat/mdnice uses semantic block wrappers (commonly section/div) for + // display equations and span wrappers for inline equations. Explicit + // display hints take precedence over the wrapper tag. + if (el.hasAttribute('data-formula')) { + const dataDisplayAttr = el.getAttribute('data-display'); + if (displayAttr === 'inline' || displayAttr === 'false' || dataDisplayAttr === 'inline' || dataDisplayAttr === 'false') { + return false; + } + if (displayAttr === 'true' || dataDisplayAttr === 'block' || dataDisplayAttr === 'true') { + return true; + } + if (DATA_FORMULA_BLOCK_TAGS.has(el.tagName.toLowerCase())) { + return true; + } + } + // Check common class names const classNames = getClassName(el).toLowerCase(); if (classNames.includes('display') || classNames.includes('block')) { @@ -426,7 +459,7 @@ export const isBlockDisplay = (el: Element): boolean => { // Cheap presence check before running the full mathSelectors scan. // Must remain a subset of mathSelectors — every selector here should also appear there. -export const mathFastCheck = 'math, mjx-container, .MathJax, .katex, img.latex, [data-math], [data-latex], script[type^="math/"]'; +export const mathFastCheck = 'math, mjx-container, .MathJax, .katex, img.latex, [data-math], [data-latex], [data-formula], script[type^="math/"]'; // Shared selector for math elements export const mathSelectors = [ @@ -462,6 +495,7 @@ export const mathSelectors = [ 'math', '[data-math]', '[data-latex]', + '[data-formula]', '[data-tex]', 'script[type^="math/"]', 'annotation[encoding="application/x-tex"]' diff --git a/src/elements/math.core.ts b/src/elements/math.core.ts index 9299d64af..7790a2434 100644 --- a/src/elements/math.core.ts +++ b/src/elements/math.core.ts @@ -3,6 +3,7 @@ import { getMathMLFromElement, getBasicLatexFromElement as getLatexFromElement, isBlockDisplay, + isNestedDataFormula, mathSelectors, mathFastCheck } from './math.base'; @@ -43,6 +44,7 @@ export const mathRules = [ fastCheck: mathFastCheck, transform: (el: Element, doc: Document): Element => { if (!hasHTMLElementProps(el)) return el; + if (isNestedDataFormula(el)) return el; const mathData = getMathMLFromElement(el); const latex = getLatexFromElement(el); @@ -62,4 +64,4 @@ export const mathRules = [ return cleanMathEl; } } -]; \ No newline at end of file +]; diff --git a/src/elements/math.full.ts b/src/elements/math.full.ts index 8b358ff3f..ad35a40fd 100644 --- a/src/elements/math.full.ts +++ b/src/elements/math.full.ts @@ -4,6 +4,7 @@ import { getMathMLFromElement, getBasicLatexFromElement, isBlockDisplay, + isNestedDataFormula, mathSelectors, mathFastCheck } from './math.base'; @@ -79,6 +80,7 @@ export const mathRules = [ if (!('classList' in el) || !('getAttribute' in el) || !('querySelector' in el)) { return el; } + if (isNestedDataFormula(el)) return el; const mathData = getMathMLFromElement(el); const latex = getLatexFromElement(el); @@ -98,4 +100,4 @@ export const mathRules = [ return cleanMathEl; } } -]; \ No newline at end of file +]; diff --git a/tests/expected/math--wechat-data-formula.md b/tests/expected/math--wechat-data-formula.md new file mode 100644 index 000000000..37e8afe04 --- /dev/null +++ b/tests/expected/math--wechat-data-formula.md @@ -0,0 +1,106 @@ +```json +{ + "title": "WeChat data-formula", + "author": "", + "site": "", + "published": "" +} +``` + +Inline formula $x_1$ should appear once. + +Equation 1. + +$$ +E_1 = x_1 \tag{1} +$$ + +Equation 2. + +$$ +E_2 = x_2 \tag{2} +$$ + +Equation 3. + +$$ +E_3 = x_3 \tag{3} +$$ + +Equation 4. + +$$ +E_4 = x_4 \tag{4} +$$ + +Equation 5. + +$$ +E_5 = x_5 \tag{5} +$$ + +Equation 6. + +$$ +E_6 = x_6 \tag{6} +$$ + +Equation 7. + +$$ +E_7 = x_7 \tag{7} +$$ + +Equation 8. + +$$ +E_8 = x_8 \tag{8} +$$ + +Equation 9. + +$$ +E_9 = x_9 \tag{9} +$$ + +Equation 10. + +$$ +E_{10} = x_{10} \tag{10} +$$ + +Equation 11. + +$$ +E_{11} = x_{11} \tag{11} +$$ + +Equation 12. + +$$ +E_{12} = x_{12} \tag{12} +$$ + +Equation 13. + +$$ +E_{13} = x_{13} \tag{13} +$$ + +Equation 14. + +$$ +E_{14} = x_{14} \tag{14} +$$ + +Equation 15. + +$$ +E_{15} = x_{15} \tag{15} +$$ + +Equation 16. + +$$ +E_{16} = x_{16} \tag{16} +$$ diff --git a/tests/fixtures/math--wechat-data-formula.html b/tests/fixtures/math--wechat-data-formula.html new file mode 100644 index 000000000..8e4ee33e8 --- /dev/null +++ b/tests/fixtures/math--wechat-data-formula.html @@ -0,0 +1,84 @@ + + + + + + WeChat data-formula + + +
+

WeChat data-formula

+

+ Inline formula + + + + should appear once. +

+
+

Equation 1.

+
+
+
+

Equation 2.

+
+
+
+

Equation 3.

+
+
+
+

Equation 4.

+
+
+
+

Equation 5.

+
+
+
+

Equation 6.

+
+
+
+

Equation 7.

+
+
+
+

Equation 8.

+
+
+
+

Equation 9.

+
+
+
+

Equation 10.

+
+
+
+

Equation 11.

+
+
+
+

Equation 12.

+
+
+
+

Equation 13.

+
+
+
+

Equation 14.

+
+
+
+

Equation 15.

+
+
+
+

Equation 16.

+
+
+
+ +