mathpix-markdown-it 2.0.34 → 2.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -1
- package/doc/changelog.md +42 -0
- package/es5/browser/add-speech.js +1 -0
- package/es5/browser/auto-render.js +33 -0
- package/es5/bundle.js +1 -1
- package/es5/index.js +1 -1
- package/lib/browser/add-speech.d.ts +17 -0
- package/lib/browser/add-speech.js +97 -0
- package/lib/browser/add-speech.js.map +1 -0
- package/lib/browser/auto-render.d.ts +20 -0
- package/lib/browser/auto-render.js +319 -0
- package/lib/browser/auto-render.js.map +1 -0
- package/lib/browser/utils.d.ts +4 -0
- package/lib/browser/utils.js +11 -0
- package/lib/browser/utils.js.map +1 -0
- package/lib/markdown/common/convert-math-to-html.d.ts +4 -1
- package/lib/markdown/common/convert-math-to-html.js +174 -93
- package/lib/markdown/common/convert-math-to-html.js.map +1 -1
- package/lib/markdown/common/render-table-cell-content.js +14 -8
- package/lib/markdown/common/render-table-cell-content.js.map +1 -1
- package/lib/markdown/highlight/highlight-math-token.js +5 -3
- package/lib/markdown/highlight/highlight-math-token.js.map +1 -1
- package/lib/mathjax/index.d.ts +6 -0
- package/lib/mathjax/index.js +103 -33
- package/lib/mathjax/index.js.map +1 -1
- package/lib/mathpix-markdown-model/index.d.ts +1 -0
- package/lib/mathpix-markdown-model/index.js +1 -0
- package/lib/mathpix-markdown-model/index.js.map +1 -1
- package/lib/sre/index.d.ts +8 -0
- package/lib/sre/index.js +38 -20
- package/lib/sre/index.js.map +1 -1
- package/lib/sre/sre-browser.d.ts +16 -0
- package/lib/sre/sre-browser.js +26 -1
- package/lib/sre/sre-browser.js.map +1 -1
- package/lib/styles/index.js +1 -1
- package/lib/styles/index.js.map +1 -1
- package/package.json +2 -2
- package/pr-specs/2026-01-html-math-output-options.md +529 -0
- package/pr-specs/2026-02-formd-delegate-image-rendering-in-table-cells.md +126 -0
package/lib/styles/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var MathpixStyle = function (setTextAlignJustify, useColors, maxWidth, scaleEqua
|
|
|
9
9
|
if (maxWidth === void 0) { maxWidth = ''; }
|
|
10
10
|
if (scaleEquation === void 0) { scaleEquation = true; }
|
|
11
11
|
if (isPptx === void 0) { isPptx = false; }
|
|
12
|
-
return "\n #setText > div {\n justify-content: inherit;\n margin-top: 0;\n margin-bottom: 1em;\n ".concat(setTextAlignJustify ? 'text-align: justify;' : '', "\n ").concat(maxWidth ? 'overflow-x: auto;' : '', "\n }\n \n ").concat(maxWidth ? '#setText > blockquote, h1, h2, h3, h4, h5, h6 { overflow-x: auto; }' : '', "\n \n ").concat(!isPptx ? '#setText div:last-child {\n margin-bottom: 0 !important;\n }' : '', "\n\n #setText > br, #preview-content br {\n line-height: 1.2;\n }\n\n #preview-content > div {\n margin-top: 0;\n margin-bottom: 1em;\n ").concat(setTextAlignJustify ? 'text-align: justify;' : '', "\n } \n \n .proof > div, .theorem > div {\n margin-top: 1rem;\n }\n\n #preview-content table {\n margin-bottom: 1em;\n }\n\n #setText table {\n margin-bottom: 1em;\n }\n \n #preview-content .sub-table table, #setText .sub-table table {\n margin-bottom: 0;\n }\n\n mjx-container {\n text-indent: 0;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 1px;\n padding-bottom: 1px;\n ").concat(maxWidth ? 'max-width:' + maxWidth + ';' : '', "\n ").concat(maxWidth ? 'overflow-x: auto;' : '', "\n }\n \n ").concat(maxWidth ? 'mjx-container[jax="SVG"] > svg { overflow-x: auto; }' : '', "\n \n .math-inline mjx-container {\n display: inline-block !important;\n page-break-inside: avoid;\n max-width: 100%;\n padding: 0;\n line-height: 0;\n }\n .math-inline[data-overflow=\"visible\"] mjx-container {\n overflow: visible;\n }\n .math-inline mjx-container mjx-assistive-mml {\n max-width: 100%;\n }\n .math-block {\n align-items: center;\n page-break-after: auto;\n page-break-inside: avoid;\n margin: 0;\n display: block; /* mjx-container has block */\n }\n \n .math-inline {\n display: inline-flex; /* mjx-container has inline-block. To prevent displacement during use overflow-x: auto;*/\n max-width: 100%;\n }\n \n .math-block[data-width=\"full\"] {\n overflow-x: auto;\n display: flex; /* mjx-container has flex */\n }\n \n svg .math-inline {\n display: initial;\n max-width: initial;\n }\n \n svg .math-inline mjx-container {\n max-width: initial;\n }\n \n svg mjx-container {\n overflow: visible;\n padding: 0;\n }\n \n svg math-block[data-width=\"full\"] {\n overflow: visible;\n }\n \n .math-block,.math-inline {\n --mmd-highlight-color: ").concat(consts_1.HIGHLIGHT_COLOR, ";\n --mmd-highlight-text-color: ").concat(consts_1.HIGHLIGHT_TEXT_COLOR, ";\n }\n\n .math-block[data-highlight-color] mjx-container[jax=\"SVG\"] > svg {\n background-color: var(--mmd-highlight-color);\n } \n \n .math-block[data-highlight-text-color] mjx-container[jax=\"SVG\"] > svg {\n color: var(--mmd-highlight-text-color);\n } \n .math-inline[data-highlight-color] mjx-container[jax=\"SVG\"] {\n background-color: var(--mmd-highlight-color);\n } \n \n .math-inline[data-highlight-text-color] mjx-container[jax=\"SVG\"] {\n color: var(--mmd-highlight-text-color);\n }\n \n .math-block p {\n flex-shrink: 1;\n }\n .math-block mjx-container {\n margin: 0 !important;\n }\n .math-error {\n background-color: yellow;\n color: red;\n }\n\n #preview-content img, #setText img {\n max-width: 100%;\n }\n \n #preview-content blockquote, #setText blockquote {\n page-break-inside: avoid;\n ").concat(useColors ? 'color: #666;' : '', "\n margin: 0 0 1em 0;\n padding-left: 3em;\n border-left: .5em solid #eee;\n }\n\n #preview-content pre, #setText pre {\n border: none;\n padding: 0;\n overflow: auto;\n font-size: 85%;\n line-height: 1.45;\n border-radius: 6px;\n box-sizing: border-box;\n ").concat(useColors ? 'background: #f8f8fa;' : '', "\n }\n #preview-content pre code, #setText pre code{\n padding: 1rem;\n display: block;\n overflow-x: auto;\n line-height: 24px;\n }\n .empty {\n text-align: center;\n font-size: 18px;\n padding: 50px 0 !important;\n }\n\n #setText table, #preview-content table {\n display: table; \n overflow: auto;\n max-width: 100%;\n border-collapse: collapse;\n page-break-inside: avoid;\n }\n \n #setText table th, #preview-content table th {\n text-align: center;\n font-weight: bold;\n }\n \n #setText table td, #preview-content table td,\n #setText table th, #preview-content table th {\n border: 1px solid #dfe2e5;\n padding: 6px 13px;\n }\n \n #setText table tr, #preview-content table tr {\n ").concat(useColors ? 'background-color: #fff;' : '', "\n border-top: 1px solid ").concat(useColors ? '#c6cbd1' : 'currentColor', ";\n }\n \n #setText table tr:nth-child(2n), #preview-content table tr:nth-child(2n) {\n ").concat(useColors ? 'background-color: #f6f8fa;' : '', "\n }\n \n #preview-content .main-title, #setText .main-title {\n text-align: center;\n line-height: 1.2;\n margin: 0 auto 1em auto;\n }\n\n #preview-content .author, #setText .author {\n text-align: center;\n margin: 0 auto;\n display: flex;\n justify-content: center;\n flex-wrap: wrap;\n }\n\n #preview-content .author p, #setText .author p {\n min-width: 30%;\n max-width: 50%;\n padding: 0 7px;\n }\n\n #preview-content .author > p > span, #setText .author > p > span {\n display: block;\n text-align: center;\n }\n\n #preview-content .section-title, #setText .section-title {\n margin-top: 1.5em;\n }\n\n #preview-content .abstract, #setText .abstract {\n text-align: justify;\n margin-bottom: 1em;\n }\n\n #preview-content .abstract p, #setText .abstract p {\n margin-bottom: 0;\n }\n\n @media print {\n\n #preview {\n font-size: 10pt!important;\n }\n\n svg {\n shape-rendering: crispEdges;\n }\n\n .math-block svg, math-inline svg {\n margin-top: 1px;\n }\n\n #preview-content img, #setText img {\n display: block;\n }\n \n #preview-content .figure_img img, #setText .figure_img img {\n display: inline;\n }\n\n .preview-right {\n word-break: break-word;\n }\n\n #preview-content h1, #setText h1 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h1::after, #setText h1::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h2, #setText h2 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h2::after, #setText h2::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h3, #setText h3 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h3::after, #setText h3::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h4, #setText h4 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h4::after, #setText h4::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h5, #setText h5 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h5::after, #setText h5::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h6, #setText h6 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h6::after, #setText h6::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n }\n #preview-content sup, #setText sup {\n top: -.5em;\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n }\n \n #preview-content .text-url, #setText .text-url {\n ").concat(useColors ? 'color: #0B93ff;' : '', "\n cursor: text;\n pointer-events: none;\n }\n \n #preview-content .text-url a:hover, #setText .text-url a:hover {\n ").concat(useColors ? 'color: #0B93ff;' : '', "\n }\n \n mark {\n background-color: #feffe6;\n }\n \n span[data-underline-type] mark {\n background: inherit;\n background-color: #feffe6;\n padding-top: 0;\n padding-bottom: 0;\n }\n \n *[data-has-dotfill] {\n position: relative;\n overflow: hidden;\n }\n \n *[data-has-dotfill] .dotfill::after {\n position: absolute;\n padding-left: .25ch;\n content: \" . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \";\n text-align: right;\n }\n \n .smiles {\n text-align: center;\n }\n\n div.svg-container, #setText > div.svg-container {\n display: flex;\n justify-content: center;\n }\n");
|
|
12
|
+
return "\n #setText > div {\n justify-content: inherit;\n margin-top: 0;\n margin-bottom: 1em;\n ".concat(setTextAlignJustify ? 'text-align: justify;' : '', "\n ").concat(maxWidth ? 'overflow-x: auto;' : '', "\n }\n \n ").concat(maxWidth ? '#setText > blockquote, h1, h2, h3, h4, h5, h6 { overflow-x: auto; }' : '', "\n \n ").concat(!isPptx ? '#setText div:last-child {\n margin-bottom: 0 !important;\n }' : '', "\n\n #setText > br, #preview-content br {\n line-height: 1.2;\n }\n\n #preview-content > div {\n margin-top: 0;\n margin-bottom: 1em;\n ").concat(setTextAlignJustify ? 'text-align: justify;' : '', "\n } \n \n .proof > div, .theorem > div {\n margin-top: 1rem;\n }\n\n #preview-content table {\n margin-bottom: 1em;\n }\n\n #setText table {\n margin-bottom: 1em;\n }\n \n #preview-content .sub-table table, #setText .sub-table table {\n margin-bottom: 0;\n }\n\n mjx-container {\n text-indent: 0;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 1px;\n padding-bottom: 1px;\n ").concat(maxWidth ? 'max-width:' + maxWidth + ';' : '', "\n ").concat(maxWidth ? 'overflow-x: auto;' : '', "\n }\n \n ").concat(maxWidth ? 'mjx-container[jax="SVG"] > svg { overflow-x: auto; }' : '', "\n \n .math-inline mjx-container {\n display: inline-block !important;\n page-break-inside: avoid;\n max-width: 100%;\n padding: 0;\n line-height: 0;\n }\n .math-inline[data-overflow=\"visible\"] mjx-container {\n overflow: visible;\n }\n .math-inline mjx-container mjx-assistive-mml {\n max-width: 100%;\n }\n .math-block {\n align-items: center;\n page-break-after: auto;\n page-break-inside: avoid;\n margin: 0;\n display: block; /* mjx-container has block */\n }\n \n .math-inline {\n display: inline-flex; /* mjx-container has inline-block. To prevent displacement during use overflow-x: auto;*/\n max-width: 100%;\n }\n \n .math-block[data-width=\"full\"] {\n overflow-x: auto;\n display: flex; /* mjx-container has flex */\n }\n\n .math-block[data-width=\"full\"] mjx-container[jax=SVG][display=true] {\n display: flex;\n flex: 1 1;\n justify-content: center;\n }\n \n svg .math-inline {\n display: initial;\n max-width: initial;\n }\n \n svg .math-inline mjx-container {\n max-width: initial;\n }\n \n svg mjx-container {\n overflow: visible;\n padding: 0;\n }\n \n svg math-block[data-width=\"full\"] {\n overflow: visible;\n }\n \n .math-block,.math-inline {\n --mmd-highlight-color: ").concat(consts_1.HIGHLIGHT_COLOR, ";\n --mmd-highlight-text-color: ").concat(consts_1.HIGHLIGHT_TEXT_COLOR, ";\n }\n\n .math-block[data-highlight-color] mjx-container[jax=\"SVG\"] > svg {\n background-color: var(--mmd-highlight-color);\n } \n \n .math-block[data-highlight-text-color] mjx-container[jax=\"SVG\"] > svg {\n color: var(--mmd-highlight-text-color);\n } \n .math-inline[data-highlight-color] mjx-container[jax=\"SVG\"] {\n background-color: var(--mmd-highlight-color);\n } \n \n .math-inline[data-highlight-text-color] mjx-container[jax=\"SVG\"] {\n color: var(--mmd-highlight-text-color);\n }\n \n .math-block p {\n flex-shrink: 1;\n }\n .math-block mjx-container {\n margin: 0 !important;\n }\n .math-error {\n background-color: yellow;\n color: red;\n }\n\n #preview-content img, #setText img {\n max-width: 100%;\n }\n \n #preview-content blockquote, #setText blockquote {\n page-break-inside: avoid;\n ").concat(useColors ? 'color: #666;' : '', "\n margin: 0 0 1em 0;\n padding-left: 3em;\n border-left: .5em solid #eee;\n }\n\n #preview-content pre, #setText pre {\n border: none;\n padding: 0;\n overflow: auto;\n font-size: 85%;\n line-height: 1.45;\n border-radius: 6px;\n box-sizing: border-box;\n ").concat(useColors ? 'background: #f8f8fa;' : '', "\n }\n #preview-content pre code, #setText pre code{\n padding: 1rem;\n display: block;\n overflow-x: auto;\n line-height: 24px;\n }\n .empty {\n text-align: center;\n font-size: 18px;\n padding: 50px 0 !important;\n }\n\n #setText table, #preview-content table {\n display: table; \n overflow: auto;\n max-width: 100%;\n border-collapse: collapse;\n page-break-inside: avoid;\n }\n \n #setText table th, #preview-content table th {\n text-align: center;\n font-weight: bold;\n }\n \n #setText table td, #preview-content table td,\n #setText table th, #preview-content table th {\n border: 1px solid #dfe2e5;\n padding: 6px 13px;\n }\n \n #setText table tr, #preview-content table tr {\n ").concat(useColors ? 'background-color: #fff;' : '', "\n border-top: 1px solid ").concat(useColors ? '#c6cbd1' : 'currentColor', ";\n }\n \n #setText table tr:nth-child(2n), #preview-content table tr:nth-child(2n) {\n ").concat(useColors ? 'background-color: #f6f8fa;' : '', "\n }\n \n #preview-content .main-title, #setText .main-title {\n text-align: center;\n line-height: 1.2;\n margin: 0 auto 1em auto;\n }\n\n #preview-content .author, #setText .author {\n text-align: center;\n margin: 0 auto;\n display: flex;\n justify-content: center;\n flex-wrap: wrap;\n }\n\n #preview-content .author p, #setText .author p {\n min-width: 30%;\n max-width: 50%;\n padding: 0 7px;\n }\n\n #preview-content .author > p > span, #setText .author > p > span {\n display: block;\n text-align: center;\n }\n\n #preview-content .section-title, #setText .section-title {\n margin-top: 1.5em;\n }\n\n #preview-content .abstract, #setText .abstract {\n text-align: justify;\n margin-bottom: 1em;\n }\n\n #preview-content .abstract p, #setText .abstract p {\n margin-bottom: 0;\n }\n\n @media print {\n\n #preview {\n font-size: 10pt!important;\n }\n\n svg {\n shape-rendering: crispEdges;\n }\n\n .math-block svg, math-inline svg {\n margin-top: 1px;\n }\n\n #preview-content img, #setText img {\n display: block;\n }\n \n #preview-content .figure_img img, #setText .figure_img img {\n display: inline;\n }\n\n .preview-right {\n word-break: break-word;\n }\n\n #preview-content h1, #setText h1 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h1::after, #setText h1::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h2, #setText h2 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h2::after, #setText h2::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h3, #setText h3 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h3::after, #setText h3::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h4, #setText h4 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h4::after, #setText h4::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h5, #setText h5 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h5::after, #setText h5::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n \n #preview-content h6, #setText h6 {\n page-break-inside: avoid;\n position: relative;\n border: 2px solid transparent;\n }\n \n #preview-content h6::after, #setText h6::after {\n content: \"\";\n display: block;\n height: 100px;\n margin-bottom: -100px;\n position: relative;\n }\n }\n #preview-content sup, #setText sup {\n top: -.5em;\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n }\n \n #preview-content .text-url, #setText .text-url {\n ").concat(useColors ? 'color: #0B93ff;' : '', "\n cursor: text;\n pointer-events: none;\n }\n \n #preview-content .text-url a:hover, #setText .text-url a:hover {\n ").concat(useColors ? 'color: #0B93ff;' : '', "\n }\n \n mark {\n background-color: #feffe6;\n }\n \n span[data-underline-type] mark {\n background: inherit;\n background-color: #feffe6;\n padding-top: 0;\n padding-bottom: 0;\n }\n \n *[data-has-dotfill] {\n position: relative;\n overflow: hidden;\n }\n \n *[data-has-dotfill] .dotfill::after {\n position: absolute;\n padding-left: .25ch;\n content: \" . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \"\n \". . . . . . . . . . . . . . . . . . . . . . . \";\n text-align: right;\n }\n \n .smiles {\n text-align: center;\n }\n\n div.svg-container, #setText > div.svg-container {\n display: flex;\n justify-content: center;\n }\n");
|
|
13
13
|
};
|
|
14
14
|
exports.MathpixStyle = MathpixStyle;
|
|
15
15
|
exports.PreviewStyle = "\n #preview {\n font-family: 'CMU Serif', 'Georgia', Helvetica, Arial, sans-serif;\n font-size: 17px;\n visibility: visible;\n word-break: break-word;\n padding: 2.5em;\n max-width: 800px;\n margin: auto;\n box-sizing: content-box;\n }\n\n #preview h1, #preview h2, #preview h3, #preview h4, #preview h5, #preview strong {\n font-family: 'CMU Serif Bold', 'Georgia', Helvetica, Arial, sans-serif;\n }\n\n #preview i, #preview em {\n font-family: 'CMU Serif Italic', 'Georgia', Helvetica, Arial, sans-serif;\n }\n";
|
package/lib/styles/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/styles/index.ts"],"names":[],"mappings":";;;AAAA,oDAAkF;AAErE,QAAA,eAAe,GAAG,6DAK9B,CAAC;AACK,IAAM,YAAY,GAAG,UAC1B,mBAAoC,EACpC,SAAyB,EACzB,QAAqB,EACrB,aAAoB,EACpB,MAAc;IAJd,oCAAA,EAAA,2BAAoC;IACpC,0BAAA,EAAA,gBAAyB;IACzB,yBAAA,EAAA,aAAqB;IACrB,8BAAA,EAAA,oBAAoB;IACpB,uBAAA,EAAA,cAAc;IAEd,OAAO,kIAKC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,uBACjD,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,gCAGvC,QAAQ,CAAC,CAAC,CAAC,qEAAqE,CAAC,CAAC,CAAC,EAAE,yBAErF,CAAC,MAAM,CAAC,CAAC,CAAC,wEAAwE,CAAC,CAAC,CAAC,EAAE,0LASnF,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,yeAyBnD,QAAQ,CAAC,CAAC,CAAC,YAAY,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,qBAC7C,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,gCAGrC,QAAQ,CAAC,CAAC,CAAC,sDAAsD,CAAC,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/styles/index.ts"],"names":[],"mappings":";;;AAAA,oDAAkF;AAErE,QAAA,eAAe,GAAG,6DAK9B,CAAC;AACK,IAAM,YAAY,GAAG,UAC1B,mBAAoC,EACpC,SAAyB,EACzB,QAAqB,EACrB,aAAoB,EACpB,MAAc;IAJd,oCAAA,EAAA,2BAAoC;IACpC,0BAAA,EAAA,gBAAyB;IACzB,yBAAA,EAAA,aAAqB;IACrB,8BAAA,EAAA,oBAAoB;IACpB,uBAAA,EAAA,cAAc;IAEd,OAAO,kIAKC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,uBACjD,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,gCAGvC,QAAQ,CAAC,CAAC,CAAC,qEAAqE,CAAC,CAAC,CAAC,EAAE,yBAErF,CAAC,MAAM,CAAC,CAAC,CAAC,wEAAwE,CAAC,CAAC,CAAC,EAAE,0LASnF,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,yeAyBnD,QAAQ,CAAC,CAAC,CAAC,YAAY,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,qBAC7C,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,gCAGrC,QAAQ,CAAC,CAAC,CAAC,sDAAsD,CAAC,CAAC,CAAC,EAAE,06CA0D7C,wBAAe,kDACV,6BAAoB,w8BAmC9C,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,iWAc/B,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,w2BAkCvC,SAAS,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,6CACpB,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,qHAI5D,SAAS,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,EAAE,myHAgK/C,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,0JAMlC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,yiCAwCzC,CAAA;AAAA,CAAC,CAAC;AAzZU,QAAA,YAAY,gBAyZtB;AAEU,QAAA,YAAY,GAAG,wiBAmB3B,CAAC;AAEK,IAAM,QAAQ,GAAG,UAAC,aAA6B;IAA7B,8BAAA,EAAA,qBAA6B;IAAK,OAAA,eACtD,aAAa,qsCAoDjB;AArD0D,CAqD1D,CAAC;AArDW,QAAA,QAAQ,YAqDnB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mathpix-markdown-it",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.36",
|
|
4
4
|
"description": "Mathpix-markdown-it is an open source implementation of the mathpix-markdown spec written in Typescript. It relies on the following open source libraries: MathJax v3 (to render math with SVGs), markdown-it (for standard Markdown parsing)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MathJax",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"react": "^18.2.0",
|
|
70
70
|
"react-dom": "^18.2.0",
|
|
71
71
|
"react-emotion": "^10.0.0",
|
|
72
|
-
"speech-rule-engine": "
|
|
72
|
+
"speech-rule-engine": "4.0.7",
|
|
73
73
|
"tslib": "^2.6.0",
|
|
74
74
|
"uuid": "^9.0.1"
|
|
75
75
|
},
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
# PR: HTML Math Output Options and Browser Render Script
|
|
2
|
+
|
|
3
|
+
Status: Implemented
|
|
4
|
+
Owner: @OlgaRedozubova
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Users exporting HTML from Mathpix products (Snip apps, v3/converter API) receive math rendered as SVG images. Many users want alternative formats:
|
|
11
|
+
- **MathML** for smaller file size and client-side rendering
|
|
12
|
+
- **LaTeX source** for smaller file size and client-side rendering
|
|
13
|
+
|
|
14
|
+
The current rendering pipeline already generates hidden MathML and LaTeX in the output, but only SVG is visible. Users cannot access the semantic formats without inspecting the HTML source.
|
|
15
|
+
|
|
16
|
+
Additionally, when users export HTML and open it in a browser, they lose the ability to right-click and copy math in different formats (a feature available in Snip preview).
|
|
17
|
+
|
|
18
|
+
This PR adds:
|
|
19
|
+
1. Options to control which math format is placed in HTML output
|
|
20
|
+
2. A browser bundle (`auto-render.js`) for client-side rendering with Mathpix's MathJax customizations
|
|
21
|
+
|
|
22
|
+
Note: Context menu functionality already exists as a separate bundle (`es5/context-menu.js`).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Goal
|
|
27
|
+
|
|
28
|
+
1. Add `output_format` option to `TOutputMath` interface to control which math format is placed in HTML (SVG, MathML, or LaTeX)
|
|
29
|
+
2. Create a browser bundle (`auto-render.js`) for client-side LaTeX/MathML rendering
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Non-Goals
|
|
34
|
+
|
|
35
|
+
- Changing default behavior (SVG remains default)
|
|
36
|
+
- Changes to DOCX, PDF, or other format rendering
|
|
37
|
+
- Offline bundling of MathJax (CDN approach for LaTeX option)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Current Behavior
|
|
42
|
+
|
|
43
|
+
### Math Rendering Flow
|
|
44
|
+
|
|
45
|
+
1. LaTeX input is parsed by markdown-it with mathpix plugin
|
|
46
|
+
2. Math tokens are passed to MathJax for rendering
|
|
47
|
+
3. `OuterHTML` function assembles output with multiple formats
|
|
48
|
+
4. All formats except SVG are hidden with `style="display: none"`
|
|
49
|
+
|
|
50
|
+
### Current Output Structure
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<span class="math-inline">
|
|
54
|
+
<latex style="display: none">\( x^2 + y = 1 \)</latex>
|
|
55
|
+
<mathml style="display: none"><math>...</math></mathml>
|
|
56
|
+
<asciimath style="display: none">x^2 + y = 1</asciimath>
|
|
57
|
+
<mathmlword style="display: none">...</mathmlword>
|
|
58
|
+
<mjx-container jax="SVG"><!-- Visible SVG --></mjx-container>
|
|
59
|
+
</span>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Current Options
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
interface TOutputMath {
|
|
66
|
+
include_mathml?: boolean;
|
|
67
|
+
include_mathml_word?: boolean;
|
|
68
|
+
include_asciimath?: boolean;
|
|
69
|
+
include_latex?: boolean;
|
|
70
|
+
include_svg?: boolean;
|
|
71
|
+
include_speech?: boolean;
|
|
72
|
+
// ... other options
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Key Files
|
|
77
|
+
|
|
78
|
+
| File | Purpose |
|
|
79
|
+
|------|---------|
|
|
80
|
+
| `src/mathpix-markdown-model/index.ts` | Options interface, main API |
|
|
81
|
+
| `src/mathjax/index.ts` | MathJax rendering, `OuterHTML` assembly |
|
|
82
|
+
| `src/markdown/mdPluginRaw.ts` | `renderMath()` function |
|
|
83
|
+
| `src/markdown/common/convert-math-to-html.ts` | Math token to HTML conversion |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Desired Behavior
|
|
88
|
+
|
|
89
|
+
### New Option
|
|
90
|
+
|
|
91
|
+
Add to `TOutputMath` interface:
|
|
92
|
+
|
|
93
|
+
| Option | Type | Default | Description |
|
|
94
|
+
|--------|------|---------|-------------|
|
|
95
|
+
| `output_format` | `'svg' \| 'mathml' \| 'latex'` | `'svg'` | Math format placed in HTML output |
|
|
96
|
+
|
|
97
|
+
### Behavior by Format
|
|
98
|
+
|
|
99
|
+
| `output_format` | Server Output | Script Required | Offline |
|
|
100
|
+
|---------------------|---------------|-----------------|---------|
|
|
101
|
+
| `'svg'` (default) | Pre-rendered SVG with hidden formats | No | Yes |
|
|
102
|
+
| `'mathml'` | Native `<math>` elements only | Yes (rendering + features) | No |
|
|
103
|
+
| `'latex'` | Raw LaTeX with original delimiters | Yes (rendering) | No |
|
|
104
|
+
|
|
105
|
+
### Why Script is Needed for MathML
|
|
106
|
+
|
|
107
|
+
- **Chrome has no native MathML support** - script provides polyfill
|
|
108
|
+
- **Context menu** - script generates hidden formats for copy functionality
|
|
109
|
+
- **Accessibility** - script adds `<mjx-assistive-mml>` and ARIA attributes (see below)
|
|
110
|
+
- **Consistent rendering** - ensures same appearance across browsers
|
|
111
|
+
|
|
112
|
+
### Accessibility Options
|
|
113
|
+
|
|
114
|
+
The `accessibility` config for `auto-render.js` controls how math is made accessible to screen readers.
|
|
115
|
+
|
|
116
|
+
**Note:** This config format is specific to the browser bundle. Server-side rendering uses the pre-existing `TAccessibility` interface with `assistiveMml: boolean` and `sre: object`.
|
|
117
|
+
|
|
118
|
+
**Option 1: `{ assistive_mml: true, include_speech: true }` - Speech label + Assistive MathML**
|
|
119
|
+
```html
|
|
120
|
+
<mjx-container class="MathJax" jax="SVG" role="math" tabindex="0"
|
|
121
|
+
aria-label="a x squared plus b x plus c equals 0">
|
|
122
|
+
<svg>...</svg>
|
|
123
|
+
<mjx-assistive-mml unselectable="on" display="inline">
|
|
124
|
+
<math xmlns="http://www.w3.org/1998/Math/MathML">...</math>
|
|
125
|
+
</mjx-assistive-mml>
|
|
126
|
+
</mjx-container>
|
|
127
|
+
```
|
|
128
|
+
- `aria-label` contains SRE-generated speech text
|
|
129
|
+
- `<mjx-assistive-mml>` exposes MathML to assistive technologies
|
|
130
|
+
|
|
131
|
+
**Option 2: `{ assistive_mml: true }` - Assistive MathML only (no speech)**
|
|
132
|
+
```html
|
|
133
|
+
<mjx-container class="MathJax" jax="SVG" role="math" tabindex="0"
|
|
134
|
+
aria-labelledby="mjx-mml-ml89vyqgk858vbmasab-2">
|
|
135
|
+
<svg aria-hidden="true">...</svg>
|
|
136
|
+
<mjx-assistive-mml unselectable="on" display="inline" id="mjx-mml-ml89vyqgk858vbmasab-2">
|
|
137
|
+
<math xmlns="http://www.w3.org/1998/Math/MathML">...</math>
|
|
138
|
+
</mjx-assistive-mml>
|
|
139
|
+
</mjx-container>
|
|
140
|
+
```
|
|
141
|
+
- `aria-labelledby` references the `<mjx-assistive-mml>` element by ID
|
|
142
|
+
- Screen readers read the MathML directly
|
|
143
|
+
|
|
144
|
+
**Option 3: No accessibility config - Not accessible**
|
|
145
|
+
```html
|
|
146
|
+
<mjx-container class="MathJax" jax="SVG">
|
|
147
|
+
<svg xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" viewBox="...">
|
|
148
|
+
...
|
|
149
|
+
</svg>
|
|
150
|
+
</mjx-container>
|
|
151
|
+
```
|
|
152
|
+
- No `role="math"` or `tabindex` on container
|
|
153
|
+
- No `aria-label` or `aria-labelledby`
|
|
154
|
+
- No `<mjx-assistive-mml>` element
|
|
155
|
+
- SVG has `role="img"` and `focusable="false"` but math is not accessible to screen readers
|
|
156
|
+
|
|
157
|
+
### Hidden Formats and Context Menu
|
|
158
|
+
|
|
159
|
+
| `output_format` | Hidden Formats Source | Context Menu |
|
|
160
|
+
|---------------------|----------------------|--------------|
|
|
161
|
+
| `'svg'` | Server-generated via `include_*` options | Reads hidden elements |
|
|
162
|
+
| `'mathml'` | Client-generated by `auto-render.js` | Reads hidden elements |
|
|
163
|
+
| `'latex'` | Client-generated by `auto-render.js` | Reads hidden elements |
|
|
164
|
+
|
|
165
|
+
**Important:** When `output_format` is `'mathml'` or `'latex'`, the server outputs ONLY the raw format (minimal HTML, no hidden elements, no SVG). The `auto-render.js` script then transforms this into the full structure:
|
|
166
|
+
1. Renders math to SVG via MathJax (`<mjx-container>`)
|
|
167
|
+
2. Generates all hidden format elements for context menu (`<latex>`, `<mathml>`, `<mathmlword>`, `<asciimath>`, `<speech>` with `style="display: none;"`)
|
|
168
|
+
3. Adds accessibility via `<mjx-assistive-mml>` element inside `<mjx-container>`
|
|
169
|
+
|
|
170
|
+
The pre-existing `context-menu.js` script provides the right-click copy menu by reading these hidden elements.
|
|
171
|
+
|
|
172
|
+
### Output Examples
|
|
173
|
+
|
|
174
|
+
**SVG output (`output_format: 'svg'`, default) - Server generates full structure:**
|
|
175
|
+
```html
|
|
176
|
+
<span class="math-inline">
|
|
177
|
+
<latex style="display: none">ax^2 + bx + c = 0</latex>
|
|
178
|
+
<mathml style="display: none"><math>...</math></mathml>
|
|
179
|
+
<mjx-container jax="SVG"><svg>...</svg></mjx-container>
|
|
180
|
+
</span>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**MathML output (`output_format: 'mathml'`) - Server generates minimal output:**
|
|
184
|
+
```html
|
|
185
|
+
<span class="math-inline">
|
|
186
|
+
<math>...</math>
|
|
187
|
+
</span>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**LaTeX output (`output_format: 'latex'`) - Server generates minimal output:**
|
|
191
|
+
```html
|
|
192
|
+
<!-- Original delimiters from MMD content are preserved -->
|
|
193
|
+
<span class="math-inline">$ ax^2 + bx + c = 0 $</span>
|
|
194
|
+
<!-- or -->
|
|
195
|
+
<span class="math-inline">\( ax^2 + bx + c = 0 \)</span>
|
|
196
|
+
<!-- or for display math -->
|
|
197
|
+
<span class="math-block">$$ ax^2 + bx + c = 0 $$</span>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**After `auto-render.js` processes MathML or LaTeX output - Full structure with all formats:**
|
|
201
|
+
```html
|
|
202
|
+
<span class="math-inline" data-mathpix-typeset="true">
|
|
203
|
+
<mathml style="display: none;"><math>...</math></mathml>
|
|
204
|
+
<mathmlword style="display: none;"><math>...</math></mathmlword>
|
|
205
|
+
<asciimath style="display: none;">ax^(2)+bx+c=0</asciimath>
|
|
206
|
+
<latex style="display: none;">ax^2 + bx + c = 0</latex>
|
|
207
|
+
<speech style="display: none;">a x squared plus b x plus c equals 0</speech>
|
|
208
|
+
<mjx-container class="MathJax" jax="SVG" role="math" tabindex="0"
|
|
209
|
+
aria-label="a x squared plus b x plus c equals 0">
|
|
210
|
+
<svg>...</svg>
|
|
211
|
+
<mjx-assistive-mml unselectable="on" display="inline">
|
|
212
|
+
<math xmlns="http://www.w3.org/1998/Math/MathML">...</math>
|
|
213
|
+
</mjx-assistive-mml>
|
|
214
|
+
</mjx-container>
|
|
215
|
+
</span>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Key elements after client-side rendering:**
|
|
219
|
+
| Element | Purpose |
|
|
220
|
+
|---------|---------|
|
|
221
|
+
| `<latex style="display: none;">` | Hidden LaTeX source for context menu copy |
|
|
222
|
+
| `<mathml style="display: none;">` | Hidden MathML for context menu copy |
|
|
223
|
+
| `<mathmlword style="display: none;">` | Hidden Word-compatible MathML for context menu copy |
|
|
224
|
+
| `<asciimath style="display: none;">` | Hidden AsciiMath for context menu copy |
|
|
225
|
+
| `<speech style="display: none;">` | Hidden speech text for context menu copy |
|
|
226
|
+
| `<mjx-container>` | Visible SVG rendering with `role="math"`, `tabindex="0"` |
|
|
227
|
+
| `aria-label` | Speech text (when `include_speech: true`) |
|
|
228
|
+
| `aria-labelledby` | References `<mjx-assistive-mml>` ID (when `assistive_mml: true` only) |
|
|
229
|
+
| `<mjx-assistive-mml>` | Exposes MathML to screen readers (only added when `assistive_mml: true`) |
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Implementation
|
|
234
|
+
|
|
235
|
+
### Part 1: New Option in Interface
|
|
236
|
+
|
|
237
|
+
**File:** `src/mathpix-markdown-model/index.ts`
|
|
238
|
+
|
|
239
|
+
Add `output_format` option to `TOutputMath` interface:
|
|
240
|
+
- Type: `'svg' | 'mathml' | 'latex'`
|
|
241
|
+
- Default: `'svg'`
|
|
242
|
+
|
|
243
|
+
Update `optionsMathpixMarkdown` default values.
|
|
244
|
+
|
|
245
|
+
### Part 2: Rendering Logic Changes
|
|
246
|
+
|
|
247
|
+
**File:** `src/markdown/mdPluginRaw.ts`
|
|
248
|
+
|
|
249
|
+
Modify `renderMath()` function:
|
|
250
|
+
- Check `output_format` option
|
|
251
|
+
- When `'latex'`: return raw LaTeX with original delimiters from the MMD content (preserves `$$`, `$`, `\[\]`, `\(\)`, etc.)
|
|
252
|
+
- When `'svg'` or `'mathml'`: proceed with MathJax rendering
|
|
253
|
+
- Preserve equation numbering context for all formats
|
|
254
|
+
|
|
255
|
+
**File:** `src/mathjax/index.ts`
|
|
256
|
+
|
|
257
|
+
Modify `OuterHTML()` function:
|
|
258
|
+
- Check `output_format` option
|
|
259
|
+
- When `'mathml'`: show MathML as primary, hide SVG container
|
|
260
|
+
- When `'svg'`: show SVG as primary (current behavior)
|
|
261
|
+
- Ensure MathML is unwrapped from `<mathml>` wrapper when it's the primary format
|
|
262
|
+
|
|
263
|
+
**File:** `src/markdown/common/convert-math-to-html.ts`
|
|
264
|
+
|
|
265
|
+
- Pass `output_format` option through to rendering functions
|
|
266
|
+
- When `'latex'`: bypass MathJax entirely, return raw delimiters
|
|
267
|
+
|
|
268
|
+
### Part 3: Browser Bundle
|
|
269
|
+
|
|
270
|
+
Create new directory: `src/browser/`
|
|
271
|
+
|
|
272
|
+
The browser bundle (`auto-render.js`) serves different purposes depending on the server output format:
|
|
273
|
+
|
|
274
|
+
| Format | Script Role |
|
|
275
|
+
|--------|-------------|
|
|
276
|
+
| `'svg'` | Not needed - already rendered; script skips elements with existing MathJax output |
|
|
277
|
+
| `'mathml'` | Required - renders MathML to SVG with hidden formats for context menu |
|
|
278
|
+
| `'latex'` | Required - renders LaTeX to SVG with hidden formats for context menu |
|
|
279
|
+
|
|
280
|
+
**File:** `src/browser/auto-render.ts`
|
|
281
|
+
|
|
282
|
+
Entry point for client-side processing:
|
|
283
|
+
|
|
284
|
+
| Export | Description |
|
|
285
|
+
|--------|-------------|
|
|
286
|
+
| `renderMathInElement(element, config)` | Scan and render math in DOM element |
|
|
287
|
+
| `MathpixRender` | Global object exposed on window |
|
|
288
|
+
|
|
289
|
+
**Configuration interface (`MathpixRenderConfig`) for browser bundle:**
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
interface MathpixAccessibilityConfig {
|
|
293
|
+
/** Expose MathJax assistive MathML for screen readers */
|
|
294
|
+
assistive_mml?: boolean;
|
|
295
|
+
/** Add aria-label speech string generated by SRE */
|
|
296
|
+
include_speech?: boolean;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
interface MathpixRenderConfig {
|
|
300
|
+
accessibility: MathpixAccessibilityConfig;
|
|
301
|
+
outMath: TOutputMath;
|
|
302
|
+
/** Container width used for layout metrics (cwidth) */
|
|
303
|
+
width?: number;
|
|
304
|
+
previewUuid?: string;
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Note:** The browser bundle uses a different accessibility interface than the server-side rendering functions:
|
|
309
|
+
|
|
310
|
+
| Context | Interface | Fields |
|
|
311
|
+
|---------|-----------|--------|
|
|
312
|
+
| Browser (`auto-render.js`) | `MathpixAccessibilityConfig` | `assistive_mml: boolean`, `include_speech: boolean` |
|
|
313
|
+
| Server/Programmatic | `TAccessibility` | `assistiveMml: boolean`, `sre: object` |
|
|
314
|
+
|
|
315
|
+
For server-side rendering, you pass the SRE object instance directly rather than a boolean flag.
|
|
316
|
+
|
|
317
|
+
**Behavior:**
|
|
318
|
+
- On DOMContentLoaded, auto-renders math in `document.body`
|
|
319
|
+
- Searches for `.math-inline` and `.math-block` elements
|
|
320
|
+
- Detects content type: MathML elements or TeX with delimiters (`$$`, `\[\]`, `\(\)`, `$`)
|
|
321
|
+
- Skips already-rendered elements (checks for `data-mathpix-typeset` attribute or MathJax containers)
|
|
322
|
+
- Renders using `MathJax.Typeset()` or `MathJax.TypesetMathML()`
|
|
323
|
+
- Adds accessibility attributes (`aria-label`, `aria-labelledby`, `role="math"`)
|
|
324
|
+
- Marks processed elements with `data-mathpix-typeset="true"`
|
|
325
|
+
|
|
326
|
+
**Note:** Context menu is a separate pre-existing bundle (`es5/context-menu.js`) that can be included independently.
|
|
327
|
+
|
|
328
|
+
### Part 4: Build Configuration
|
|
329
|
+
|
|
330
|
+
**File:** `webpack.config.js`
|
|
331
|
+
|
|
332
|
+
Added `autoRenderConfig` to existing webpack configuration:
|
|
333
|
+
- Input: `src/browser/auto-render.ts`
|
|
334
|
+
- Output: `es5/browser/auto-render.js`
|
|
335
|
+
- Uses existing TypeScript and Babel loaders
|
|
336
|
+
- Includes NodePolyfillPlugin for browser compatibility
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Distribution
|
|
341
|
+
|
|
342
|
+
Browser bundles included in npm package, accessible via jsdelivr:
|
|
343
|
+
|
|
344
|
+
| Bundle | Path | Purpose |
|
|
345
|
+
|--------|------|---------|
|
|
346
|
+
| `auto-render.js` | `es5/browser/auto-render.js` | Render MathML/LaTeX to SVG (for `output_format: 'mathml'` or `'latex'`) |
|
|
347
|
+
| `add-speech.js` | `es5/browser/add-speech.js` | Add speech to already-rendered SVG (requires `mjx-assistive-mml` present) |
|
|
348
|
+
| `context-menu.js` | `es5/context-menu.js` | Right-click copy menu (pre-existing, separate bundle) |
|
|
349
|
+
|
|
350
|
+
CDN URLs:
|
|
351
|
+
- `https://cdn.jsdelivr.net/npm/mathpix-markdown-it@{version}/es5/browser/auto-render.js`
|
|
352
|
+
- `https://cdn.jsdelivr.net/npm/mathpix-markdown-it@{version}/es5/browser/add-speech.js`
|
|
353
|
+
- `https://cdn.jsdelivr.net/npm/mathpix-markdown-it@{version}/es5/context-menu.js`
|
|
354
|
+
|
|
355
|
+
This follows the existing pattern for `es5/bundle.js`.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Usage Examples
|
|
360
|
+
|
|
361
|
+
### Server-Side: MathML Output
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { MathpixMarkdownModel } from 'mathpix-markdown-it';
|
|
365
|
+
|
|
366
|
+
const html = MathpixMarkdownModel.markdownToHTML(mmd, {
|
|
367
|
+
outMath: {
|
|
368
|
+
output_format: 'mathml',
|
|
369
|
+
// Note: include_* options are ignored for 'mathml' format
|
|
370
|
+
// Hidden formats are generated client-side by auto-render.js
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
// HTML contains only: <span class="math-inline"><math>...</math></span>
|
|
374
|
+
// Include auto-render.js to render SVG and generate hidden formats for context menu
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Server-Side: LaTeX Output (for Client Rendering)
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
const html = MathpixMarkdownModel.markdownToHTML(mmd, {
|
|
381
|
+
outMath: {
|
|
382
|
+
output_format: 'latex',
|
|
383
|
+
// Note: include_* options are ignored for 'latex' format
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
// HTML contains only: <span class="math-inline">$ ax^2 + bx + c = 0 $</span>
|
|
387
|
+
// Original delimiters from MMD are preserved ($$, $, \[\], \(\), etc.)
|
|
388
|
+
// Include auto-render.js to render SVG and generate hidden formats for context menu
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Client-Side: Auto-Render
|
|
392
|
+
|
|
393
|
+
```html
|
|
394
|
+
<script>
|
|
395
|
+
window.MathpixRenderConfig = {
|
|
396
|
+
accessibility: {
|
|
397
|
+
assistive_mml: true,
|
|
398
|
+
include_speech: true
|
|
399
|
+
},
|
|
400
|
+
outMath: {
|
|
401
|
+
output_format: 'svg',
|
|
402
|
+
include_svg: true,
|
|
403
|
+
include_latex: true,
|
|
404
|
+
include_mathml: true,
|
|
405
|
+
include_asciimath: true,
|
|
406
|
+
include_mathml_word: true
|
|
407
|
+
},
|
|
408
|
+
width: 1200
|
|
409
|
+
};
|
|
410
|
+
</script>
|
|
411
|
+
<script src="https://cdn.jsdelivr.net/npm/mathpix-markdown-it@latest/es5/browser/auto-render.js"></script>
|
|
412
|
+
<!-- Optional: include context menu for copy functionality -->
|
|
413
|
+
<script src="https://cdn.jsdelivr.net/npm/mathpix-markdown-it@latest/es5/context-menu.js"></script>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Client-Side: Manual Render
|
|
417
|
+
|
|
418
|
+
```javascript
|
|
419
|
+
MathpixRender.renderMathInElement(document.getElementById('content'), {
|
|
420
|
+
accessibility: {
|
|
421
|
+
assistive_mml: true,
|
|
422
|
+
include_speech: true
|
|
423
|
+
},
|
|
424
|
+
outMath: {
|
|
425
|
+
output_format: 'svg',
|
|
426
|
+
include_latex: true,
|
|
427
|
+
include_mathml: true
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Client-Side: Add Speech to Already-Rendered SVG
|
|
433
|
+
|
|
434
|
+
Use `add-speech.js` when math was rendered server-side with `output_format: 'svg'` but without accessibility:
|
|
435
|
+
|
|
436
|
+
```html
|
|
437
|
+
<script src="https://cdn.jsdelivr.net/npm/mathpix-markdown-it@latest/es5/browser/add-speech.js"></script>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Or manually:
|
|
441
|
+
|
|
442
|
+
```javascript
|
|
443
|
+
// After add-speech.js loads
|
|
444
|
+
MathpixSpeech.addSpeechToRenderedMath(document.getElementById('content'));
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
This script:
|
|
448
|
+
- Loads SRE (Speech Rule Engine) dynamically
|
|
449
|
+
- Finds all `mjx-container` elements with `mjx-assistive-mml`
|
|
450
|
+
- Adds `aria-label`, `role="math"`, `tabindex="0"` attributes
|
|
451
|
+
- Creates hidden `<speech>` elements for context menu
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Constraints / Invariants
|
|
456
|
+
|
|
457
|
+
- Default behavior unchanged (`output_format: 'svg'`)
|
|
458
|
+
- When `output_format: 'svg'`: server generates full structure with hidden formats via `include_*` options
|
|
459
|
+
- When `output_format: 'mathml'` or `'latex'`: server outputs ONLY the raw format (minimal HTML), `include_*` options are ignored
|
|
460
|
+
- When `output_format: 'latex'`: original delimiters from MMD content are preserved
|
|
461
|
+
- Browser bundle (`auto-render.js`) must work standalone (no external dependencies except DOM)
|
|
462
|
+
- `auto-render.js` is **required** for `'mathml'` and `'latex'` formats to render SVG and generate hidden formats
|
|
463
|
+
- `auto-render.js` skips already-rendered elements (detects MathJax containers, `data-mathpix-typeset` attribute)
|
|
464
|
+
- Only `'svg'` format works fully offline; `'mathml'` and `'latex'` require client-side script
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Testing
|
|
469
|
+
|
|
470
|
+
### Unit Tests
|
|
471
|
+
|
|
472
|
+
- `output_format: 'svg'` produces full structure with SVG and hidden formats (default behavior)
|
|
473
|
+
- `output_format: 'mathml'` produces minimal output with only native MathML element
|
|
474
|
+
- `output_format: 'latex'` produces minimal output with only raw LaTeX delimiters
|
|
475
|
+
- `include_*` options work only with `'svg'` format (ignored for `'mathml'` and `'latex'`)
|
|
476
|
+
- Inline vs display math handled correctly for all formats
|
|
477
|
+
- Equation numbering preserved with all formats
|
|
478
|
+
|
|
479
|
+
### Browser Bundle Tests (`auto-render.js`)
|
|
480
|
+
|
|
481
|
+
- Correctly detects MathML elements inside `.math-inline` / `.math-block`
|
|
482
|
+
- Correctly detects LaTeX delimiters (`$$`, `\[\]`, `\(\)`, `$`)
|
|
483
|
+
- Generates all hidden format elements (`<latex>`, `<mathml>`, `<mathmlword>`, `<asciimath>`, `<speech>`)
|
|
484
|
+
- Renders SVG via `<mjx-container>`
|
|
485
|
+
- Accessibility when `{ assistive_mml: true, include_speech: true }`: adds `role="math"`, `tabindex="0"`, `aria-label`, `<mjx-assistive-mml>`
|
|
486
|
+
- Accessibility when `{ assistive_mml: true }` only: adds `role="math"`, `tabindex="0"`, `aria-labelledby`, `<mjx-assistive-mml>` with ID
|
|
487
|
+
- No accessibility when config not set: no `<mjx-assistive-mml>`, no ARIA attributes
|
|
488
|
+
- Marks processed elements with `data-mathpix-typeset="true"`
|
|
489
|
+
- Skips already-rendered elements
|
|
490
|
+
|
|
491
|
+
### Integration Tests
|
|
492
|
+
|
|
493
|
+
- Browser bundle loads without errors
|
|
494
|
+
- Auto-render finds and renders `.math-inline` and `.math-block` elements
|
|
495
|
+
- Already-rendered elements are skipped (detects `data-mathpix-typeset` attribute)
|
|
496
|
+
- MathML input elements are correctly detected and rendered
|
|
497
|
+
|
|
498
|
+
### Manual Tests
|
|
499
|
+
|
|
500
|
+
- Verify rendered output matches expected format
|
|
501
|
+
- Test accessibility with screen readers
|
|
502
|
+
- Test MathML and LaTeX input rendering in browser
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Done When
|
|
507
|
+
|
|
508
|
+
- [x] `output_format` option added to `TOutputMath` interface
|
|
509
|
+
- [x] `renderByFormat()` handles all three format values (`'svg'`, `'mathml'`, `'latex'`)
|
|
510
|
+
- [x] `OuterHTML()` shows correct primary format based on option
|
|
511
|
+
- [x] Browser bundle entry point created (`src/browser/auto-render.ts`)
|
|
512
|
+
- [x] Webpack config for browser bundle added to `webpack.config.js`
|
|
513
|
+
- [x] Bundle files included in npm package (`es5/browser/auto-render.js`)
|
|
514
|
+
- [x] Accessibility improvements (`applyMathJaxA11y`, `aria-labelledby` support)
|
|
515
|
+
- [x] `Status` updated to `Implemented`
|
|
516
|
+
- [x] Separate `add-speech.js` bundle for adding speech to already-rendered SVG
|
|
517
|
+
- [x] Shared `addSpeechToMathContainer()` function in `src/sre/index.ts`
|
|
518
|
+
- [x] Changelog updated
|
|
519
|
+
- [x] Unit tests for new options
|
|
520
|
+
- [x] README updated with new options and browser bundle usage
|
|
521
|
+
|
|
522
|
+
Note: Context menu was already implemented separately (`src/context-menu.tsx` → `es5/context-menu.js`)
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Related
|
|
527
|
+
|
|
528
|
+
- Monorepo PR spec: `monorepo/pr-specs/2026-01-snip-html-export-mathml-option.md`
|
|
529
|
+
- Consumers: mmd-converter (v3/converter API), snip-web, desktop apps
|