@newtonschool/tex-html-parser 0.1.0
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/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +523 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderTexStatement.d.ts +29 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Newton School
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# tex-html-parser
|
|
2
|
+
|
|
3
|
+
TypeScript parser/transformer that converts TeX into sanitized HTML.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install tex-html-parser
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { renderTexStatement } from 'tex-html-parser'
|
|
15
|
+
|
|
16
|
+
const html = renderTexStatement(tex)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Input: `tex: string`
|
|
20
|
+
- Output: `string` (sanitized HTML)
|
|
21
|
+
- `renderTexStatement` is side-effect free by default.
|
|
22
|
+
|
|
23
|
+
To opt into MathJax rendering in browsers:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const html = renderTexStatement(tex, { typeset: true })
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To scope MathJax work to a specific container:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
const html = renderTexStatement(tex, {
|
|
33
|
+
typeset: true,
|
|
34
|
+
typesetTarget: containerElement,
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
MathJax loader security notes:
|
|
39
|
+
|
|
40
|
+
- The package injects a pinned MathJax CDN URL (`jsDelivr`) at runtime.
|
|
41
|
+
- Script loading uses `crossorigin="anonymous"` with timeout fallback.
|
|
42
|
+
- You can set a strict CSP to restrict script sources, or provide `window.MathJax` before calling `renderTexStatement` to avoid dynamic script injection.
|
|
43
|
+
|
|
44
|
+
## React Usage (`dangerouslySetInnerHTML`)
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { renderTexStatement } from 'tex-html-parser'
|
|
48
|
+
|
|
49
|
+
function Statement({ tex }: { tex: string }) {
|
|
50
|
+
const html = renderTexStatement(tex)
|
|
51
|
+
return <div dangerouslySetInnerHTML={{ __html: html }} />
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Supported TeX Subset
|
|
56
|
+
|
|
57
|
+
- Paragraph splitting by blank line
|
|
58
|
+
- Inline/display math delimiters: `$...$`, `$$...$$` (preserved)
|
|
59
|
+
- Styles: `\bf`, `\textbf`, `\it`, `\textit`, `\t`, `\tt`, `\texttt`, `\emph`, `\underline`, `\sout`, `\textsc`
|
|
60
|
+
- Sizes: `\tiny`, `\scriptsize`, `\small`, `\normalsize`, `\large`, `\Large`, `\LARGE`, `\huge`, `\Huge`
|
|
61
|
+
- Environments: `itemize`, `enumerate`, `lstlisting`, `center`, `tabular`
|
|
62
|
+
- Table helpers: `\hline`, `\cline`, `\multicolumn`, `\multirow`
|
|
63
|
+
- Links: `\url`, `\href`
|
|
64
|
+
- `\epigraph`
|
|
65
|
+
- Typography replacements for TeX quote/dash tokens
|
|
66
|
+
|
|
67
|
+
## Not Supported (Dropped)
|
|
68
|
+
|
|
69
|
+
- `defs.toml` custom command expansion
|
|
70
|
+
- `\includegraphics`
|
|
71
|
+
- `\def \htmlPixelsInCm`
|
|
72
|
+
- React preview component export
|
|
73
|
+
- CSS export
|
|
74
|
+
- warnings/meta return object
|
|
75
|
+
|
|
76
|
+
Unsupported or malformed input is rendered best-effort with escaped fallback text.
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm test
|
|
82
|
+
npm run build
|
|
83
|
+
npm run test:package
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Publish checks:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run prepublishOnly
|
|
90
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v={bf:"strong",textbf:"strong",it:"em",textit:"em",t:"code",tt:"code",texttt:"code",emph:"u",underline:"u",sout:"s",textsc:"span"},_=new Set(["tiny","scriptsize","small","normalsize","large","Large","LARGE","huge","Huge"]),C=new Set(["a","blockquote","br","code","div","em","footer","li","ol","p","pre","s","span","strong","table","tbody","td","tr","u","ul"]),z={a:new Set(["href","target","rel"]),td:new Set(["colspan","rowspan"])};let $=null,y=!1,w=null,M=!1;const I="https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js",j=8e3;function k(e,r={}){const t=String(e??"").replace(/\r\n/g,`
|
|
2
|
+
`),n=J(t).join(""),i=F(n);if(r.typeset){const s=x(r.typesetTarget);X(s)}return i}function x(e){if(!e)return;const t=(Array.isArray(e)?e:[e]).filter(n=>!!n);return t.length>0?t:void 0}function J(e){const r=[],t=/\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;let n=0,i;for(;(i=t.exec(e))!==null;){if(i.index>n&&r.push(...b(e.slice(n,i.index))),i[1]){const s=i[1],o=H(e,i.index,s);if(!o){r.push(...b(e.slice(i.index)));break}if(s==="tabular"){let l="",c=o.innerStart;const a=P(e,c);a&&(l=a.content,c=a.end);const u=e.slice(c,o.innerEnd);r.push(R(l,u))}else if(s==="itemize"||s==="enumerate"){const l=e.slice(o.innerStart,o.innerEnd);r.push(W(s,l))}else if(s==="lstlisting"){const l=e.slice(o.innerStart,o.innerEnd).replace(/^\n+|\n+$/g,"");r.push(`<pre><code>${d(l)}</code></pre>`)}else if(s==="center"){const l=e.slice(o.innerStart,o.innerEnd);r.push(`<div>${J(l).join("")}</div>`)}n=o.end,t.lastIndex=n;continue}if(e.startsWith("\\epigraph{",i.index)){const s=g(e,i.index+9);if(!s){r.push(...b(e.slice(i.index,i.index+9))),n=i.index+9,t.lastIndex=n;continue}const o=g(e,s.end);if(!o){r.push(...b(e.slice(i.index,s.end))),n=s.end,t.lastIndex=n;continue}const l=m(s.content.trim()),c=m(o.content.trim());r.push(`<blockquote><p>${l}</p><footer>${c}</footer></blockquote>`),n=o.end,t.lastIndex=n;continue}n=i.index+i[0].length}return n<e.length&&r.push(...b(e.slice(n))),r}function H(e,r,t){const n=`\\begin{${t}}`,i=`\\end{${t}}`;let s=1,o=r+n.length;for(;o<=e.length;){const l=e.indexOf(n,o),c=e.indexOf(i,o);if(c===-1)return null;if(l!==-1&&l<c){s+=1,o=l+n.length;continue}if(s-=1,s===0)return{innerStart:r+n.length,innerEnd:c,end:c+i.length};o=c+i.length}return null}function b(e){const r=[],t=/\n\s*\n/g;let n=0,i;for(;(i=t.exec(e))!==null;)E(r,e.slice(n,i.index)),n=i.index+i[0].length;return E(r,e.slice(n)),r}function E(e,r){var l,c;const t=((l=r.match(/^\s*/))==null?void 0:l[0].length)??0,n=((c=r.match(/\s*$/))==null?void 0:c[0].length)??0,i=r.slice(t,r.length-n);if(!i)return;const s=i.replace(/\n/g," "),o=m(s);o.trim()&&e.push(`<p>${o}</p>`)}function W(e,r){const t=/\\item\b/g,n=[];let i;for(;(i=t.exec(r))!==null;)n.push(i.index);if(n.length===0)return"";const s=[];for(let l=0;l<n.length;l+=1){const c=n[l]+5,a=l+1<n.length?n[l+1]:r.length,f=r.slice(c,a).trim();if(!f)continue;const p=f.replace(/\n/g," "),h=m(p);s.push(`<li>${h}</li>`)}const o=e==="enumerate"?"ol":"ul";return`<${o}>${s.join("")}</${o}>`}function R(e,r){const t=L(r,"\\\\"),n=[],i=new Map;let s=!0;for(const l of t){const c=l.replace(/\\hline/g,"").replace(/\\cline\{[^}]*\}/g,"").trim();if(!c)continue;s||q(i),s=!1;const a=L(c,"&");let u=0;const f=[];for(const p of a){const h=O(p);for(;i.has(u);)u+=1;let S="";if(h.colspan>1&&(S+=` colspan="${h.colspan}"`),h.rowspan>1){S+=` rowspan="${h.rowspan}"`;for(let T=0;T<h.colspan;T+=1)i.set(u+T,h.rowspan-1)}f.push(`<td${S}>${h.html}</td>`),u+=h.colspan}n.push(`<tr>${f.join("")}</tr>`)}return`<table${e?` data-spec="${d(e)}"`:""}><tbody>${n.join("")}</tbody></table>`}function q(e){for(const[r,t]of e.entries()){const n=t-1;n<=0?e.delete(r):e.set(r,n)}}function O(e){let r=e.trim(),t=1,n=1,i=!0;for(;i;){i=!1;const s=r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);s&&(t*=Number.parseInt(s[1],10),r=s[2].trim(),i=!0);const o=r.match(/^\\multirow\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);o&&(n*=Number.parseInt(o[1],10),r=o[2].trim(),i=!0)}return{html:m(r),colspan:t,rowspan:n}}function m(e){let r="",t="";const n=d('"'),i=()=>{t&&(r+=d(B(t)),t="")};for(let s=0;s<e.length;s+=1){if(e.startsWith("$$",s)){const o=G(e,"$$",s+2);if(o!==-1){i();const l=e.slice(s,o+2);r+=d(l),s=o+1;continue}}if(e[s]==="$"){const o=N(e,s+1);if(o!==-1){i();const l=e.slice(s,o+1);r+=d(l),s=o;continue}}if(e[s]==="\\"&&e[s+1]==="\\"){i(),r+="<br/>",s+=1;continue}if(e.startsWith("``",s)||e.startsWith("''",s)){i(),r+=n,s+=1;continue}if(e[s]==="\\"){const o=D(e,s);if(o){i(),r+=o.html,s=o.end-1;continue}}t+=e[s]}return i(),r}function D(e,r){let t=r+1;for(;t<e.length&&/[A-Za-z]/.test(e[t]);)t+=1;if(t===r+1)return null;const n=e.slice(r+1,t);if(n in v){const i=g(e,t);if(!i)return{html:d(`\\${n}`),end:t};const s=m(i.content),o=v[n];return n==="textsc"?{html:`<span>${s}</span>`,end:i.end}:{html:`<${o}>${s}</${o}>`,end:i.end}}if(_.has(n)){const i=g(e,t);return i?{html:`<span>${m(i.content)}</span>`,end:i.end}:{html:d(`\\${n}`),end:t}}if(n==="url"){const i=g(e,t);if(!i)return{html:d("\\url"),end:t};const s=A(i.content.trim()),o=d(i.content.trim());return{html:`<a href="${d(s)}" target="_blank" rel="noopener noreferrer">${o}</a>`,end:i.end}}if(n==="href"){const i=g(e,t);if(!i)return{html:d("\\href"),end:t};const s=g(e,i.end);if(!s)return{html:d("\\href"),end:i.end};const o=A(i.content.trim()),l=m(s.content);return{html:`<a href="${d(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,end:s.end}}return{html:d(`\\${n}`),end:t}}function g(e,r){let t=r;for(;t<e.length&&/\s/.test(e[t]);)t+=1;const n=P(e,t);return n?{...n,contentStart:t+1}:null}function P(e,r){if(e[r]!=="{")return null;let t=0;for(let n=r;n<e.length;n+=1)if(e[n]==="{"&&e[n-1]!=="\\")t+=1;else if(e[n]==="}"&&e[n-1]!=="\\"&&(t-=1,t===0))return{content:e.slice(r+1,n),start:r,end:n+1};return null}function L(e,r){const t=[];let n="",i=0;for(let s=0;s<e.length;s+=1){const o=e[s];if(o==="{"&&e[s-1]!=="\\"?i+=1:o==="}"&&e[s-1]!=="\\"&&i>0&&(i-=1),i===0&&r==="&"&&o==="&"){t.push(n),n="";continue}if(i===0&&r==="\\\\"&&o==="\\"&&e[s+1]==="\\"){t.push(n),n="",s+=1;continue}n+=o}return n.trim()&&t.push(n),t}function G(e,r,t){for(let n=t;n<=e.length-r.length;n+=1){if(e[n]==="\\"){n+=1;continue}if(e.startsWith(r,n))return n}return-1}function N(e,r){for(let t=r;t<e.length;t+=1){if(e[t]==="\\"){t+=1;continue}if(e[t]==="$"&&e[t+1]!=="$")return t}return-1}function B(e){return e.replace(/<<([\s\S]*?)>>/g,"«$1»").replace(/`(.)'/g,"'$1'").replace(/~---/g," — ").replace(/"---/g," — ")}function F(e){return e.replace(/<[^>]*>/g,r=>U(r))}function U(e){const r=e.match(/^<\s*(\/?)\s*([a-zA-Z0-9]+)([^>]*)>$/);if(!r)return"";const t=r[1]==="/",n=r[2].toLowerCase(),i=r[3]??"";if(!C.has(n))return"";if(t)return`</${n}>`;if(n==="br")return"<br/>";const s=Z(i),o=z[n]??new Set,l=[];for(const[a,u]of s)if(o.has(a)){if(n==="a"&&a==="href"){const f=A(u);if(!f)continue;l.push(["href",f]);continue}if(n==="a"&&a==="target"){u==="_blank"&&l.push(["target","_blank"]);continue}if(!(n==="a"&&a==="rel")&&n==="td"&&(a==="colspan"||a==="rowspan")){const f=Number.parseInt(u,10);Number.isFinite(f)&&f>0&&l.push([a,String(f)]);continue}}n==="a"&&l.some(([a,u])=>a==="target"&&u==="_blank")&&l.push(["rel","noopener noreferrer"]);const c=l.map(([a,u])=>` ${a}="${d(u)}"`).join("");return`<${n}${c}>`}function Z(e){const r=[],t=/([a-zA-Z_:][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;let n;for(;(n=t.exec(e))!==null;){const s=n[1].toLowerCase(),o=n[2]??n[3]??n[4]??"";r.push([s,o])}return r}function A(e){const r=String(e??"").trim();if(!r)return"";const t=/^(https?:\/\/|mailto:|#)/i.test(r),n=/^\/(?!\/)/.test(r);return t||n?r:""}function d(e){return String(e).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function X(e){if(typeof window>"u"||typeof document>"u")return;if(e&&e.length>0){const t=new Set(w??[]);for(const n of e)t.add(n);w=[...t]}else M=!0,w=null;if(y)return;y=!0;const r=async()=>{y=!1;const t=await K(),n=Y();if(t!=null&&t.typesetPromise)try{t.typesetClear&&t.typesetClear(n),await t.typesetPromise(n)}catch{}};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(()=>{r()});return}setTimeout(()=>{r()},0)}function Y(){if(M){M=!1,w=null;return}const e=w??void 0;return w=null,e}function Q(){const e=['script[data-mathjax="tex-renderer"]',"script#MathJax-script",'script[src*="mathjax"][src*="tex-mml-chtml"]','script[src*="MathJax.js"]'];for(const r of e){const t=document.querySelector(r);if(t instanceof HTMLScriptElement)return t}return null}function K(){var e;return typeof window>"u"||typeof document>"u"?Promise.resolve(null):(e=window.MathJax)!=null&&e.typesetPromise?Promise.resolve(window.MathJax):$||(window.MathJax=window.MathJax||{tex:{inlineMath:[["$","$"]],displayMath:[["$$","$$"]],processEscapes:!0},svg:{fontCache:"global"}},$=new Promise((r,t)=>{var f;const n=Q();let i=null,s=!1;const o=()=>{i!==null&&(clearTimeout(i),i=null)},l=p=>{s||(s=!0,o(),p())},c=()=>{l(()=>t(new Error("Failed to load MathJax script")))},a=()=>{l(()=>r(window.MathJax??null))};if(i=setTimeout(()=>{c()},j),n){if((f=window.MathJax)!=null&&f.typesetPromise){a();return}n.addEventListener("load",a,{once:!0}),n.addEventListener("error",c,{once:!0}),setTimeout(()=>{var p;(p=window.MathJax)!=null&&p.typesetPromise&&a()},0);return}const u=document.createElement("script");u.id="MathJax-script",u.src=I,u.async=!0,u.crossOrigin="anonymous",u.dataset.mathjax="tex-renderer",u.addEventListener("load",a,{once:!0}),u.addEventListener("error",c,{once:!0}),document.head.append(u)}).catch(()=>($=null,null)),$)}exports.renderTexStatement=k;
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/renderTexStatement.ts"],"sourcesContent":["// TeX block environments currently supported by this renderer.\nconst SUPPORTED_BLOCK_ENVS = ['itemize', 'enumerate', 'lstlisting', 'center', 'tabular'] as const\n\ntype BlockEnvironment = (typeof SUPPORTED_BLOCK_ENVS)[number]\n\ntype InlineCommandResult = {\n html: string\n end: number\n}\n\nexport type RenderTexStatementOptions = {\n // MathJax typesetting is opt-in to keep parsing side-effect free by default.\n typeset?: boolean\n // Optional container(s) to scope MathJax typesetting instead of whole document.\n typesetTarget?: Element | Element[] | null\n}\n\ntype MathJaxLike = {\n typesetPromise?: (elements?: Element[]) => Promise<unknown>\n typesetClear?: (elements?: Element[]) => void\n tex?: {\n inlineMath?: Array<[string, string]>\n displayMath?: Array<[string, string]>\n processEscapes?: boolean\n }\n svg?: {\n fontCache?: string\n }\n}\n\ndeclare global {\n interface Window {\n MathJax?: MathJaxLike\n }\n}\n\nconst STYLE_TAGS: Record<string, 'strong' | 'em' | 'code' | 'u' | 's' | 'span'> = {\n bf: 'strong',\n textbf: 'strong',\n it: 'em',\n textit: 'em',\n t: 'code',\n tt: 'code',\n texttt: 'code',\n emph: 'u',\n underline: 'u',\n sout: 's',\n textsc: 'span',\n}\n\nconst SIZE_COMMANDS = new Set([\n 'tiny',\n 'scriptsize',\n 'small',\n 'normalsize',\n 'large',\n 'Large',\n 'LARGE',\n 'huge',\n 'Huge',\n])\n\n// Strict HTML allowlist used by sanitizeHtml.\nconst ALLOWED_TAGS = new Set([\n 'a',\n 'blockquote',\n 'br',\n 'code',\n 'div',\n 'em',\n 'footer',\n 'li',\n 'ol',\n 'p',\n 'pre',\n 's',\n 'span',\n 'strong',\n 'table',\n 'tbody',\n 'td',\n 'tr',\n 'u',\n 'ul',\n])\n\n// Attribute allowlist per tag. Any missing attribute is dropped.\nconst ALLOWED_ATTRS_BY_TAG: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']),\n td: new Set(['colspan', 'rowspan']),\n}\n\nlet mathJaxLoadPromise: Promise<MathJaxLike | null> | null = null\nlet mathJaxTypesetScheduled = false\nlet pendingMathJaxTargets: Element[] | null = null\nlet pendingMathJaxGlobalTypeset = false\nconst MATHJAX_SCRIPT_URL = 'https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js'\nconst MATHJAX_SCRIPT_INTEGRITY = ''\nconst MATHJAX_LOAD_TIMEOUT_MS = 8000\n\n/**\n * Convert TeX/LaTeX text into sanitized HTML.\n *\n * The function keeps math payloads (`$...$`, `$$...$$`) intact in output.\n * MathJax typesetting is opt-in through options to keep this parser pure.\n */\nexport function renderTexStatement(tex: string, options: RenderTexStatementOptions = {}): string {\n const normalized = String(tex ?? '').replace(/\\r\\n/g, '\\n')\n const rawHtml = parseBlocks(normalized).join('')\n const html = sanitizeHtml(rawHtml)\n\n if (options.typeset) {\n const scopedTargets = normalizeTypesetTargets(options.typesetTarget)\n scheduleGlobalMathTypeset(scopedTargets)\n }\n\n return html\n}\n\nfunction normalizeTypesetTargets(typesetTarget: RenderTexStatementOptions['typesetTarget']): Element[] | undefined {\n if (!typesetTarget) {\n return undefined\n }\n\n const candidates = Array.isArray(typesetTarget) ? typesetTarget : [typesetTarget]\n const targets = candidates.filter((target): target is Element => Boolean(target))\n return targets.length > 0 ? targets : undefined\n}\n\n/**\n * Parse top-level block structures (paragraphs, environments, epigraph).\n * Falls back to paragraph rendering if malformed blocks are encountered.\n */\nfunction parseBlocks(text: string): string[] {\n const html: string[] = []\n const blockStartRegex = /\\\\begin\\{(itemize|enumerate|lstlisting|center|tabular)\\}|\\\\epigraph\\{/g\n let last = 0\n let match: RegExpExecArray | null\n\n while ((match = blockStartRegex.exec(text)) !== null) {\n if (match.index > last) {\n html.push(...renderParagraphChunk(text.slice(last, match.index)))\n }\n\n if (match[1]) {\n const envName = match[1] as BlockEnvironment\n const env = findEnvironment(text, match.index, envName)\n if (!env) {\n html.push(...renderParagraphChunk(text.slice(match.index)))\n break\n }\n\n if (envName === 'tabular') {\n let spec = ''\n let contentStart = env.innerStart\n const specGroup = parseBraced(text, contentStart)\n if (specGroup) {\n spec = specGroup.content\n contentStart = specGroup.end\n }\n\n const tabularContent = text.slice(contentStart, env.innerEnd)\n html.push(renderTabular(spec, tabularContent))\n } else if (envName === 'itemize' || envName === 'enumerate') {\n const listContent = text.slice(env.innerStart, env.innerEnd)\n html.push(renderList(envName, listContent))\n } else if (envName === 'lstlisting') {\n const code = text.slice(env.innerStart, env.innerEnd).replace(/^\\n+|\\n+$/g, '')\n html.push(`<pre><code>${escapeHtml(code)}</code></pre>`)\n } else if (envName === 'center') {\n const centered = text.slice(env.innerStart, env.innerEnd)\n html.push(`<div>${parseBlocks(centered).join('')}</div>`)\n }\n\n last = env.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n if (text.startsWith('\\\\epigraph{', match.index)) {\n const first = parseBracedWithWhitespace(text, match.index + '\\\\epigraph'.length)\n if (!first) {\n html.push(...renderParagraphChunk(text.slice(match.index, match.index + '\\\\epigraph'.length)))\n last = match.index + '\\\\epigraph'.length\n blockStartRegex.lastIndex = last\n continue\n }\n\n const second = parseBracedWithWhitespace(text, first.end)\n if (!second) {\n html.push(...renderParagraphChunk(text.slice(match.index, first.end)))\n last = first.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n const quoteHtml = parseInline(first.content.trim())\n const authorHtml = parseInline(second.content.trim())\n html.push(`<blockquote><p>${quoteHtml}</p><footer>${authorHtml}</footer></blockquote>`)\n\n last = second.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n last = match.index + match[0].length\n }\n\n if (last < text.length) {\n html.push(...renderParagraphChunk(text.slice(last)))\n }\n\n return html\n}\n\nfunction findEnvironment(text: string, beginIndex: number, envName: BlockEnvironment): null | {\n innerStart: number\n innerEnd: number\n end: number\n} {\n const beginToken = `\\\\begin{${envName}}`\n const endToken = `\\\\end{${envName}}`\n\n let depth = 1\n let cursor = beginIndex + beginToken.length\n\n // Support nested same-name environments by tracking depth.\n while (cursor <= text.length) {\n const nextBegin = text.indexOf(beginToken, cursor)\n const nextEnd = text.indexOf(endToken, cursor)\n\n if (nextEnd === -1) {\n return null\n }\n\n if (nextBegin !== -1 && nextBegin < nextEnd) {\n depth += 1\n cursor = nextBegin + beginToken.length\n continue\n }\n\n depth -= 1\n if (depth === 0) {\n return {\n innerStart: beginIndex + beginToken.length,\n innerEnd: nextEnd,\n end: nextEnd + endToken.length,\n }\n }\n\n cursor = nextEnd + endToken.length\n }\n\n return null\n}\n\nfunction renderParagraphChunk(chunk: string): string[] {\n const out: string[] = []\n const separator = /\\n\\s*\\n/g\n let cursor = 0\n let match: RegExpExecArray | null\n\n while ((match = separator.exec(chunk)) !== null) {\n pushParagraph(out, chunk.slice(cursor, match.index))\n cursor = match.index + match[0].length\n }\n\n pushParagraph(out, chunk.slice(cursor))\n return out\n}\n\n// This renderer uses a blank line as a paragraph separator.\nfunction pushParagraph(output: string[], paragraphRaw: string): void {\n const leadingWhitespace = paragraphRaw.match(/^\\s*/)?.[0].length ?? 0\n const trailingWhitespace = paragraphRaw.match(/\\s*$/)?.[0].length ?? 0\n\n const trimmed = paragraphRaw.slice(leadingWhitespace, paragraphRaw.length - trailingWhitespace)\n if (!trimmed) {\n return\n }\n\n const normalized = trimmed.replace(/\\n/g, ' ')\n const inline = parseInline(normalized)\n if (inline.trim()) {\n output.push(`<p>${inline}</p>`)\n }\n}\n\nfunction renderList(kind: 'itemize' | 'enumerate', content: string): string {\n const itemRegex = /\\\\item\\b/g\n const itemStarts: number[] = []\n let match: RegExpExecArray | null\n while ((match = itemRegex.exec(content)) !== null) {\n itemStarts.push(match.index)\n }\n\n if (itemStarts.length === 0) {\n return ''\n }\n\n const items: string[] = []\n for (let i = 0; i < itemStarts.length; i += 1) {\n const start = itemStarts[i] + '\\\\item'.length\n const end = i + 1 < itemStarts.length ? itemStarts[i + 1] : content.length\n const rawItem = content.slice(start, end)\n const trimmed = rawItem.trim()\n if (!trimmed) {\n continue\n }\n\n const normalized = trimmed.replace(/\\n/g, ' ')\n const itemHtml = parseInline(normalized)\n items.push(`<li>${itemHtml}</li>`)\n }\n\n const tag = kind === 'enumerate' ? 'ol' : 'ul'\n return `<${tag}>${items.join('')}</${tag}>`\n}\n\n/**\n * Render a simple HTML table from tabular rows/cells.\n * Handles \\multicolumn and \\multirow, including carry-over row spans.\n */\nfunction renderTabular(spec: string, content: string): string {\n const rows = splitTopLevel(content, '\\\\\\\\')\n const htmlRows: string[] = []\n // Column index -> remaining rows covered by an active rowspan.\n const activeRowspans = new Map<number, number>()\n let firstPhysicalRow = true\n\n for (const rowToken of rows) {\n const row = rowToken.replace(/\\\\hline/g, '').replace(/\\\\cline\\{[^}]*\\}/g, '').trim()\n if (!row) {\n continue\n }\n\n if (!firstPhysicalRow) {\n decrementRowspanMap(activeRowspans)\n }\n firstPhysicalRow = false\n\n const cells = splitTopLevel(row, '&')\n let col = 0\n const renderedCells: string[] = []\n\n for (const rawCell of cells) {\n const cell = parseTableCell(rawCell)\n // Skip synthetic columns occupied by previous rowspans.\n while (activeRowspans.has(col)) {\n col += 1\n }\n\n let attrs = ''\n if (cell.colspan > 1) {\n attrs += ` colspan=\"${cell.colspan}\"`\n }\n if (cell.rowspan > 1) {\n attrs += ` rowspan=\"${cell.rowspan}\"`\n // Reserve covered columns for upcoming rows.\n for (let step = 0; step < cell.colspan; step += 1) {\n activeRowspans.set(col + step, cell.rowspan - 1)\n }\n }\n\n renderedCells.push(`<td${attrs}>${cell.html}</td>`)\n col += cell.colspan\n }\n\n htmlRows.push(`<tr>${renderedCells.join('')}</tr>`)\n }\n\n const specData = spec ? ` data-spec=\"${escapeHtml(spec)}\"` : ''\n return `<table${specData}><tbody>${htmlRows.join('')}</tbody></table>`\n}\n\nfunction decrementRowspanMap(map: Map<number, number>): void {\n for (const [key, value] of map.entries()) {\n const next = value - 1\n if (next <= 0) {\n map.delete(key)\n } else {\n map.set(key, next)\n }\n }\n}\n\n// Peel nested \\multicolumn/\\multirow wrappers and compute effective spans.\nfunction parseTableCell(rawCell: string): { html: string; colspan: number; rowspan: number } {\n let text = rawCell.trim()\n let colspan = 1\n let rowspan = 1\n\n let changed = true\n while (changed) {\n changed = false\n\n const multiColumn = text.match(/^\\\\multicolumn\\{(\\d+)\\}\\{[^}]*\\}\\{([\\s\\S]*)\\}$/)\n if (multiColumn) {\n colspan *= Number.parseInt(multiColumn[1], 10)\n text = multiColumn[2].trim()\n changed = true\n }\n\n const multiRow = text.match(/^\\\\multirow\\{(\\d+)\\}\\{[^}]*\\}\\{([\\s\\S]*)\\}$/)\n if (multiRow) {\n rowspan *= Number.parseInt(multiRow[1], 10)\n text = multiRow[2].trim()\n changed = true\n }\n }\n\n return {\n html: parseInline(text),\n colspan,\n rowspan,\n }\n}\n\n/**\n * Parse inline text commands and preserve raw math delimiters for MathJax.\n * Unknown commands are escaped and emitted as plain text.\n */\nfunction parseInline(text: string): string {\n let output = ''\n let plain = ''\n const escapedDoubleQuote = escapeHtml('\"')\n\n const flushPlain = (): void => {\n if (!plain) {\n return\n }\n output += escapeHtml(applyTypography(plain))\n plain = ''\n }\n\n for (let i = 0; i < text.length; i += 1) {\n if (text.startsWith('$$', i)) {\n const end = findUnescaped(text, '$$', i + 2)\n if (end !== -1) {\n flushPlain()\n const rawMath = text.slice(i, end + 2)\n output += escapeHtml(rawMath)\n i = end + 1\n continue\n }\n }\n\n if (text[i] === '$') {\n const end = findInlineMathEnd(text, i + 1)\n if (end !== -1) {\n flushPlain()\n const rawMath = text.slice(i, end + 1)\n output += escapeHtml(rawMath)\n i = end\n continue\n }\n }\n\n if (text[i] === '\\\\' && text[i + 1] === '\\\\') {\n flushPlain()\n // TeX line break inside paragraph/list/table cell.\n output += '<br/>'\n i += 1\n continue\n }\n\n if (text.startsWith('``', i) || text.startsWith(\"''\", i)) {\n flushPlain()\n output += escapedDoubleQuote\n i += 1\n continue\n }\n\n if (text[i] === '\\\\') {\n const command = parseInlineCommand(text, i)\n if (command) {\n flushPlain()\n output += command.html\n i = command.end - 1\n continue\n }\n }\n\n plain += text[i]\n }\n\n flushPlain()\n return output\n}\n\nfunction parseInlineCommand(text: string, start: number): InlineCommandResult | null {\n let cursor = start + 1\n while (cursor < text.length && /[A-Za-z]/.test(text[cursor])) {\n cursor += 1\n }\n\n if (cursor === start + 1) {\n return null\n }\n\n const command = text.slice(start + 1, cursor)\n\n if (command in STYLE_TAGS) {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n }\n\n const parsed = parseInline(arg.content)\n const tag = STYLE_TAGS[command]\n if (command === 'textsc') {\n return { html: `<span>${parsed}</span>`, end: arg.end }\n }\n\n return { html: `<${tag}>${parsed}</${tag}>`, end: arg.end }\n }\n\n if (SIZE_COMMANDS.has(command)) {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n }\n\n const parsed = parseInline(arg.content)\n return { html: `<span>${parsed}</span>`, end: arg.end }\n }\n\n if (command === 'url') {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml('\\\\url'), end: cursor }\n }\n\n const href = sanitizeUrl(arg.content.trim())\n const caption = escapeHtml(arg.content.trim())\n return {\n html: `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${caption}</a>`,\n end: arg.end,\n }\n }\n\n if (command === 'href') {\n const first = parseBracedWithWhitespace(text, cursor)\n if (!first) {\n return { html: escapeHtml('\\\\href'), end: cursor }\n }\n\n const second = parseBracedWithWhitespace(text, first.end)\n if (!second) {\n return { html: escapeHtml('\\\\href'), end: first.end }\n }\n\n const href = sanitizeUrl(first.content.trim())\n const caption = parseInline(second.content)\n return {\n html: `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${caption}</a>`,\n end: second.end,\n }\n }\n\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n}\n\n// Parse a braced argument while allowing optional whitespace before it.\nfunction parseBracedWithWhitespace(text: string, start: number): null | {\n content: string\n start: number\n end: number\n contentStart: number\n} {\n let cursor = start\n while (cursor < text.length && /\\s/.test(text[cursor])) {\n cursor += 1\n }\n\n const braced = parseBraced(text, cursor)\n if (!braced) {\n return null\n }\n\n return {\n ...braced,\n contentStart: cursor + 1,\n }\n}\n\n// Parse a balanced {...} group, honoring escaped braces.\nfunction parseBraced(text: string, start: number): null | {\n content: string\n start: number\n end: number\n} {\n if (text[start] !== '{') {\n return null\n }\n\n let depth = 0\n for (let i = start; i < text.length; i += 1) {\n if (text[i] === '{' && text[i - 1] !== '\\\\') {\n depth += 1\n } else if (text[i] === '}' && text[i - 1] !== '\\\\') {\n depth -= 1\n if (depth === 0) {\n return {\n content: text.slice(start + 1, i),\n start,\n end: i + 1,\n }\n }\n }\n }\n\n return null\n}\n\n/**\n * Split by delimiter only at brace-depth zero.\n * Used for tabular row (`\\\\`) and cell (`&`) splitting.\n */\nfunction splitTopLevel(text: string, delimiter: '&' | '\\\\\\\\'): string[] {\n const out: string[] = []\n let current = ''\n let depth = 0\n\n for (let i = 0; i < text.length; i += 1) {\n const char = text[i]\n\n if (char === '{' && text[i - 1] !== '\\\\') {\n depth += 1\n } else if (char === '}' && text[i - 1] !== '\\\\' && depth > 0) {\n depth -= 1\n }\n\n if (depth === 0 && delimiter === '&' && char === '&') {\n out.push(current)\n current = ''\n continue\n }\n\n if (depth === 0 && delimiter === '\\\\\\\\' && char === '\\\\' && text[i + 1] === '\\\\') {\n out.push(current)\n current = ''\n i += 1\n continue\n }\n\n current += char\n }\n\n if (current.trim()) {\n out.push(current)\n }\n\n return out\n}\n\nfunction findUnescaped(text: string, marker: '$$' | '$', start: number): number {\n for (let i = start; i <= text.length - marker.length; i += 1) {\n if (text[i] === '\\\\') {\n i += 1\n continue\n }\n\n if (text.startsWith(marker, i)) {\n return i\n }\n }\n\n return -1\n}\n\nfunction findInlineMathEnd(text: string, start: number): number {\n for (let i = start; i < text.length; i += 1) {\n if (text[i] === '\\\\') {\n i += 1\n continue\n }\n\n if (text[i] === '$' && text[i + 1] !== '$') {\n return i\n }\n }\n\n return -1\n}\n\n// TeX-specific typography token replacements.\nfunction applyTypography(value: string): string {\n return value\n .replace(/<<([\\s\\S]*?)>>/g, '«$1»')\n .replace(/`(.)'/g, \"'$1'\")\n .replace(/~---/g, ' — ')\n .replace(/\"---/g, ' — ')\n}\n\n/**\n * Lightweight sanitizer: preserve only allowlisted tags/attrs\n * and enforce safe URL protocols.\n */\nfunction sanitizeHtml(html: string): string {\n return html.replace(/<[^>]*>/g, (rawTag) => sanitizeTag(rawTag))\n}\n\nfunction sanitizeTag(rawTag: string): string {\n const match = rawTag.match(/^<\\s*(\\/?)\\s*([a-zA-Z0-9]+)([^>]*)>$/)\n if (!match) {\n return ''\n }\n\n const isClosing = match[1] === '/'\n const tag = match[2].toLowerCase()\n const attrText = match[3] ?? ''\n\n if (!ALLOWED_TAGS.has(tag)) {\n return ''\n }\n\n if (isClosing) {\n return `</${tag}>`\n }\n\n if (tag === 'br') {\n return '<br/>'\n }\n\n const attrs = parseAttributes(attrText)\n const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tag] ?? new Set<string>()\n const sanitizedAttrs: Array<[string, string]> = []\n\n for (const [name, value] of attrs) {\n if (!allowedAttrs.has(name)) {\n continue\n }\n\n if (tag === 'a' && name === 'href') {\n const safeHref = sanitizeUrl(value)\n if (!safeHref) {\n continue\n }\n sanitizedAttrs.push(['href', safeHref])\n continue\n }\n\n if (tag === 'a' && name === 'target') {\n if (value === '_blank') {\n sanitizedAttrs.push(['target', '_blank'])\n }\n continue\n }\n\n if (tag === 'a' && name === 'rel') {\n // rel is managed centrally below when target=_blank is present.\n continue\n }\n\n if (tag === 'td' && (name === 'colspan' || name === 'rowspan')) {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed) && parsed > 0) {\n sanitizedAttrs.push([name, String(parsed)])\n }\n continue\n }\n }\n\n if (tag === 'a' && sanitizedAttrs.some(([name, value]) => name === 'target' && value === '_blank')) {\n sanitizedAttrs.push(['rel', 'noopener noreferrer'])\n }\n\n const attrsString = sanitizedAttrs\n .map(([name, value]) => ` ${name}=\"${escapeHtml(value)}\"`)\n .join('')\n\n return `<${tag}${attrsString}>`\n}\n\nfunction parseAttributes(attributeText: string): Array<[string, string]> {\n const attributes: Array<[string, string]> = []\n const regex = /([a-zA-Z_:][\\w:.-]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n\n let match: RegExpExecArray | null\n while ((match = regex.exec(attributeText)) !== null) {\n const rawName = match[1]\n const name = rawName.toLowerCase()\n const value = match[2] ?? match[3] ?? match[4] ?? ''\n attributes.push([name, value])\n }\n\n return attributes\n}\n\nfunction sanitizeUrl(url: string): string {\n const trimmed = String(url ?? '').trim()\n if (!trimmed) {\n return ''\n }\n\n const isAllowedProtocol = /^(https?:\\/\\/|mailto:|#)/i.test(trimmed)\n const isSafeRelativePath = /^\\/(?!\\/)/.test(trimmed)\n\n return isAllowedProtocol || isSafeRelativePath ? trimmed : ''\n}\n\nfunction escapeHtml(value: string): string {\n return String(value)\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''')\n}\n\n/**\n * Debounced global typeset trigger. It runs once per animation frame\n * regardless of how many render calls happen in that frame.\n */\nfunction scheduleGlobalMathTypeset(targets?: Element[]): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n if (targets && targets.length > 0) {\n const uniqueTargets = new Set(pendingMathJaxTargets ?? [])\n for (const target of targets) {\n uniqueTargets.add(target)\n }\n pendingMathJaxTargets = [...uniqueTargets]\n } else {\n pendingMathJaxGlobalTypeset = true\n pendingMathJaxTargets = null\n }\n\n if (mathJaxTypesetScheduled) {\n return\n }\n mathJaxTypesetScheduled = true\n\n const runTypeset = async (): Promise<void> => {\n mathJaxTypesetScheduled = false\n const mathJax = await ensureMathJax()\n const scopedTargets = selectTypesetTargetsForRun()\n if (!mathJax?.typesetPromise) {\n return\n }\n\n try {\n if (mathJax.typesetClear) {\n // Avoid stale state from previous typesets.\n mathJax.typesetClear(scopedTargets)\n }\n await mathJax.typesetPromise(scopedTargets)\n } catch {\n // Keep rendering non-blocking if MathJax fails to typeset.\n }\n }\n\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(() => {\n void runTypeset()\n })\n return\n }\n\n setTimeout(() => {\n void runTypeset()\n }, 0)\n}\n\nfunction selectTypesetTargetsForRun(): Element[] | undefined {\n if (pendingMathJaxGlobalTypeset) {\n pendingMathJaxGlobalTypeset = false\n pendingMathJaxTargets = null\n return undefined\n }\n\n const scopedTargets = pendingMathJaxTargets ?? undefined\n pendingMathJaxTargets = null\n return scopedTargets\n}\n\nfunction findExistingMathJaxScript(): HTMLScriptElement | null {\n const selectors = [\n 'script[data-mathjax=\"tex-renderer\"]',\n 'script#MathJax-script',\n 'script[src*=\"mathjax\"][src*=\"tex-mml-chtml\"]',\n 'script[src*=\"MathJax.js\"]',\n ]\n\n for (const selector of selectors) {\n const candidate = document.querySelector(selector)\n if (candidate instanceof HTMLScriptElement) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Ensure MathJax v3 is loaded exactly once in browser environments.\n * Returns null when unavailable/failing to keep rendering resilient.\n */\nfunction ensureMathJax(): Promise<MathJaxLike | null> {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.resolve(null)\n }\n\n if (window.MathJax?.typesetPromise) {\n return Promise.resolve(window.MathJax)\n }\n\n if (mathJaxLoadPromise) {\n return mathJaxLoadPromise\n }\n\n window.MathJax = window.MathJax || {\n tex: {\n inlineMath: [['$', '$']],\n displayMath: [['$$', '$$']],\n processEscapes: true,\n },\n svg: {\n fontCache: 'global',\n },\n }\n\n mathJaxLoadPromise = new Promise<MathJaxLike | null>((resolve, reject) => {\n const existing = findExistingMathJaxScript()\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n let settled = false\n\n const clearPendingTimeout = (): void => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId)\n timeoutId = null\n }\n }\n\n const settle = (callback: () => void): void => {\n if (settled) {\n return\n }\n settled = true\n clearPendingTimeout()\n callback()\n }\n\n const fail = (): void => {\n settle(() => reject(new Error('Failed to load MathJax script')))\n }\n\n const succeed = (): void => {\n settle(() => resolve(window.MathJax ?? null))\n }\n\n timeoutId = setTimeout(() => {\n fail()\n }, MATHJAX_LOAD_TIMEOUT_MS)\n\n if (existing) {\n if (window.MathJax?.typesetPromise) {\n succeed()\n return\n }\n existing.addEventListener('load', succeed, { once: true })\n existing.addEventListener('error', fail, { once: true })\n // Existing scripts can already be loaded before listeners are attached.\n setTimeout(() => {\n if (window.MathJax?.typesetPromise) {\n succeed()\n }\n }, 0)\n return\n }\n\n const script = document.createElement('script')\n script.id = 'MathJax-script'\n script.src = MATHJAX_SCRIPT_URL\n script.async = true\n script.crossOrigin = 'anonymous'\n if (MATHJAX_SCRIPT_INTEGRITY) {\n script.integrity = MATHJAX_SCRIPT_INTEGRITY\n }\n script.dataset.mathjax = 'tex-renderer'\n script.addEventListener('load', succeed, { once: true })\n script.addEventListener('error', fail, { once: true })\n document.head.append(script)\n }).catch(() => {\n mathJaxLoadPromise = null\n return null\n })\n\n return mathJaxLoadPromise\n}\n"],"names":["STYLE_TAGS","SIZE_COMMANDS","ALLOWED_TAGS","ALLOWED_ATTRS_BY_TAG","mathJaxLoadPromise","mathJaxTypesetScheduled","pendingMathJaxTargets","pendingMathJaxGlobalTypeset","MATHJAX_SCRIPT_URL","MATHJAX_LOAD_TIMEOUT_MS","renderTexStatement","tex","options","normalized","rawHtml","parseBlocks","html","sanitizeHtml","scopedTargets","normalizeTypesetTargets","scheduleGlobalMathTypeset","typesetTarget","targets","target","text","blockStartRegex","last","match","renderParagraphChunk","envName","env","findEnvironment","spec","contentStart","specGroup","parseBraced","tabularContent","renderTabular","listContent","renderList","code","escapeHtml","centered","first","parseBracedWithWhitespace","second","quoteHtml","parseInline","authorHtml","beginIndex","beginToken","endToken","depth","cursor","nextBegin","nextEnd","chunk","out","separator","pushParagraph","output","paragraphRaw","leadingWhitespace","_a","trailingWhitespace","_b","trimmed","inline","kind","content","itemRegex","itemStarts","items","i","start","end","itemHtml","tag","rows","splitTopLevel","htmlRows","activeRowspans","firstPhysicalRow","rowToken","row","decrementRowspanMap","cells","col","renderedCells","rawCell","cell","parseTableCell","attrs","step","map","key","value","next","colspan","rowspan","changed","multiColumn","multiRow","plain","escapedDoubleQuote","flushPlain","applyTypography","findUnescaped","rawMath","findInlineMathEnd","command","parseInlineCommand","arg","parsed","href","sanitizeUrl","caption","braced","delimiter","current","char","marker","rawTag","sanitizeTag","isClosing","attrText","parseAttributes","allowedAttrs","sanitizedAttrs","name","safeHref","attrsString","attributeText","attributes","regex","url","isAllowedProtocol","isSafeRelativePath","uniqueTargets","runTypeset","mathJax","ensureMathJax","selectTypesetTargetsForRun","findExistingMathJaxScript","selectors","selector","candidate","resolve","reject","existing","timeoutId","settled","clearPendingTimeout","settle","callback","fail","succeed","script"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
const v = {
|
|
2
|
+
bf: "strong",
|
|
3
|
+
textbf: "strong",
|
|
4
|
+
it: "em",
|
|
5
|
+
textit: "em",
|
|
6
|
+
t: "code",
|
|
7
|
+
tt: "code",
|
|
8
|
+
texttt: "code",
|
|
9
|
+
emph: "u",
|
|
10
|
+
underline: "u",
|
|
11
|
+
sout: "s",
|
|
12
|
+
textsc: "span"
|
|
13
|
+
}, _ = /* @__PURE__ */ new Set([
|
|
14
|
+
"tiny",
|
|
15
|
+
"scriptsize",
|
|
16
|
+
"small",
|
|
17
|
+
"normalsize",
|
|
18
|
+
"large",
|
|
19
|
+
"Large",
|
|
20
|
+
"LARGE",
|
|
21
|
+
"huge",
|
|
22
|
+
"Huge"
|
|
23
|
+
]), C = /* @__PURE__ */ new Set([
|
|
24
|
+
"a",
|
|
25
|
+
"blockquote",
|
|
26
|
+
"br",
|
|
27
|
+
"code",
|
|
28
|
+
"div",
|
|
29
|
+
"em",
|
|
30
|
+
"footer",
|
|
31
|
+
"li",
|
|
32
|
+
"ol",
|
|
33
|
+
"p",
|
|
34
|
+
"pre",
|
|
35
|
+
"s",
|
|
36
|
+
"span",
|
|
37
|
+
"strong",
|
|
38
|
+
"table",
|
|
39
|
+
"tbody",
|
|
40
|
+
"td",
|
|
41
|
+
"tr",
|
|
42
|
+
"u",
|
|
43
|
+
"ul"
|
|
44
|
+
]), z = {
|
|
45
|
+
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
46
|
+
td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
|
|
47
|
+
};
|
|
48
|
+
let $ = null, y = !1, w = null, M = !1;
|
|
49
|
+
const I = "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js", j = 8e3;
|
|
50
|
+
function K(e, r = {}) {
|
|
51
|
+
const t = String(e ?? "").replace(/\r\n/g, `
|
|
52
|
+
`), n = J(t).join(""), i = B(n);
|
|
53
|
+
if (r.typeset) {
|
|
54
|
+
const s = k(r.typesetTarget);
|
|
55
|
+
Z(s);
|
|
56
|
+
}
|
|
57
|
+
return i;
|
|
58
|
+
}
|
|
59
|
+
function k(e) {
|
|
60
|
+
if (!e)
|
|
61
|
+
return;
|
|
62
|
+
const t = (Array.isArray(e) ? e : [e]).filter((n) => !!n);
|
|
63
|
+
return t.length > 0 ? t : void 0;
|
|
64
|
+
}
|
|
65
|
+
function J(e) {
|
|
66
|
+
const r = [], t = /\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;
|
|
67
|
+
let n = 0, i;
|
|
68
|
+
for (; (i = t.exec(e)) !== null; ) {
|
|
69
|
+
if (i.index > n && r.push(...b(e.slice(n, i.index))), i[1]) {
|
|
70
|
+
const s = i[1], o = x(e, i.index, s);
|
|
71
|
+
if (!o) {
|
|
72
|
+
r.push(...b(e.slice(i.index)));
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
if (s === "tabular") {
|
|
76
|
+
let l = "", c = o.innerStart;
|
|
77
|
+
const a = P(e, c);
|
|
78
|
+
a && (l = a.content, c = a.end);
|
|
79
|
+
const u = e.slice(c, o.innerEnd);
|
|
80
|
+
r.push(W(l, u));
|
|
81
|
+
} else if (s === "itemize" || s === "enumerate") {
|
|
82
|
+
const l = e.slice(o.innerStart, o.innerEnd);
|
|
83
|
+
r.push(H(s, l));
|
|
84
|
+
} else if (s === "lstlisting") {
|
|
85
|
+
const l = e.slice(o.innerStart, o.innerEnd).replace(/^\n+|\n+$/g, "");
|
|
86
|
+
r.push(`<pre><code>${d(l)}</code></pre>`);
|
|
87
|
+
} else if (s === "center") {
|
|
88
|
+
const l = e.slice(o.innerStart, o.innerEnd);
|
|
89
|
+
r.push(`<div>${J(l).join("")}</div>`);
|
|
90
|
+
}
|
|
91
|
+
n = o.end, t.lastIndex = n;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (e.startsWith("\\epigraph{", i.index)) {
|
|
95
|
+
const s = g(e, i.index + 9);
|
|
96
|
+
if (!s) {
|
|
97
|
+
r.push(...b(e.slice(i.index, i.index + 9))), n = i.index + 9, t.lastIndex = n;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const o = g(e, s.end);
|
|
101
|
+
if (!o) {
|
|
102
|
+
r.push(...b(e.slice(i.index, s.end))), n = s.end, t.lastIndex = n;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const l = m(s.content.trim()), c = m(o.content.trim());
|
|
106
|
+
r.push(`<blockquote><p>${l}</p><footer>${c}</footer></blockquote>`), n = o.end, t.lastIndex = n;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
n = i.index + i[0].length;
|
|
110
|
+
}
|
|
111
|
+
return n < e.length && r.push(...b(e.slice(n))), r;
|
|
112
|
+
}
|
|
113
|
+
function x(e, r, t) {
|
|
114
|
+
const n = `\\begin{${t}}`, i = `\\end{${t}}`;
|
|
115
|
+
let s = 1, o = r + n.length;
|
|
116
|
+
for (; o <= e.length; ) {
|
|
117
|
+
const l = e.indexOf(n, o), c = e.indexOf(i, o);
|
|
118
|
+
if (c === -1)
|
|
119
|
+
return null;
|
|
120
|
+
if (l !== -1 && l < c) {
|
|
121
|
+
s += 1, o = l + n.length;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (s -= 1, s === 0)
|
|
125
|
+
return {
|
|
126
|
+
innerStart: r + n.length,
|
|
127
|
+
innerEnd: c,
|
|
128
|
+
end: c + i.length
|
|
129
|
+
};
|
|
130
|
+
o = c + i.length;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
function b(e) {
|
|
135
|
+
const r = [], t = /\n\s*\n/g;
|
|
136
|
+
let n = 0, i;
|
|
137
|
+
for (; (i = t.exec(e)) !== null; )
|
|
138
|
+
E(r, e.slice(n, i.index)), n = i.index + i[0].length;
|
|
139
|
+
return E(r, e.slice(n)), r;
|
|
140
|
+
}
|
|
141
|
+
function E(e, r) {
|
|
142
|
+
var l, c;
|
|
143
|
+
const t = ((l = r.match(/^\s*/)) == null ? void 0 : l[0].length) ?? 0, n = ((c = r.match(/\s*$/)) == null ? void 0 : c[0].length) ?? 0, i = r.slice(t, r.length - n);
|
|
144
|
+
if (!i)
|
|
145
|
+
return;
|
|
146
|
+
const s = i.replace(/\n/g, " "), o = m(s);
|
|
147
|
+
o.trim() && e.push(`<p>${o}</p>`);
|
|
148
|
+
}
|
|
149
|
+
function H(e, r) {
|
|
150
|
+
const t = /\\item\b/g, n = [];
|
|
151
|
+
let i;
|
|
152
|
+
for (; (i = t.exec(r)) !== null; )
|
|
153
|
+
n.push(i.index);
|
|
154
|
+
if (n.length === 0)
|
|
155
|
+
return "";
|
|
156
|
+
const s = [];
|
|
157
|
+
for (let l = 0; l < n.length; l += 1) {
|
|
158
|
+
const c = n[l] + 5, a = l + 1 < n.length ? n[l + 1] : r.length, f = r.slice(c, a).trim();
|
|
159
|
+
if (!f)
|
|
160
|
+
continue;
|
|
161
|
+
const p = f.replace(/\n/g, " "), h = m(p);
|
|
162
|
+
s.push(`<li>${h}</li>`);
|
|
163
|
+
}
|
|
164
|
+
const o = e === "enumerate" ? "ol" : "ul";
|
|
165
|
+
return `<${o}>${s.join("")}</${o}>`;
|
|
166
|
+
}
|
|
167
|
+
function W(e, r) {
|
|
168
|
+
const t = L(r, "\\\\"), n = [], i = /* @__PURE__ */ new Map();
|
|
169
|
+
let s = !0;
|
|
170
|
+
for (const l of t) {
|
|
171
|
+
const c = l.replace(/\\hline/g, "").replace(/\\cline\{[^}]*\}/g, "").trim();
|
|
172
|
+
if (!c)
|
|
173
|
+
continue;
|
|
174
|
+
s || R(i), s = !1;
|
|
175
|
+
const a = L(c, "&");
|
|
176
|
+
let u = 0;
|
|
177
|
+
const f = [];
|
|
178
|
+
for (const p of a) {
|
|
179
|
+
const h = q(p);
|
|
180
|
+
for (; i.has(u); )
|
|
181
|
+
u += 1;
|
|
182
|
+
let T = "";
|
|
183
|
+
if (h.colspan > 1 && (T += ` colspan="${h.colspan}"`), h.rowspan > 1) {
|
|
184
|
+
T += ` rowspan="${h.rowspan}"`;
|
|
185
|
+
for (let S = 0; S < h.colspan; S += 1)
|
|
186
|
+
i.set(u + S, h.rowspan - 1);
|
|
187
|
+
}
|
|
188
|
+
f.push(`<td${T}>${h.html}</td>`), u += h.colspan;
|
|
189
|
+
}
|
|
190
|
+
n.push(`<tr>${f.join("")}</tr>`);
|
|
191
|
+
}
|
|
192
|
+
return `<table${e ? ` data-spec="${d(e)}"` : ""}><tbody>${n.join("")}</tbody></table>`;
|
|
193
|
+
}
|
|
194
|
+
function R(e) {
|
|
195
|
+
for (const [r, t] of e.entries()) {
|
|
196
|
+
const n = t - 1;
|
|
197
|
+
n <= 0 ? e.delete(r) : e.set(r, n);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function q(e) {
|
|
201
|
+
let r = e.trim(), t = 1, n = 1, i = !0;
|
|
202
|
+
for (; i; ) {
|
|
203
|
+
i = !1;
|
|
204
|
+
const s = r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);
|
|
205
|
+
s && (t *= Number.parseInt(s[1], 10), r = s[2].trim(), i = !0);
|
|
206
|
+
const o = r.match(/^\\multirow\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);
|
|
207
|
+
o && (n *= Number.parseInt(o[1], 10), r = o[2].trim(), i = !0);
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
html: m(r),
|
|
211
|
+
colspan: t,
|
|
212
|
+
rowspan: n
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function m(e) {
|
|
216
|
+
let r = "", t = "";
|
|
217
|
+
const n = d('"'), i = () => {
|
|
218
|
+
t && (r += d(N(t)), t = "");
|
|
219
|
+
};
|
|
220
|
+
for (let s = 0; s < e.length; s += 1) {
|
|
221
|
+
if (e.startsWith("$$", s)) {
|
|
222
|
+
const o = D(e, "$$", s + 2);
|
|
223
|
+
if (o !== -1) {
|
|
224
|
+
i();
|
|
225
|
+
const l = e.slice(s, o + 2);
|
|
226
|
+
r += d(l), s = o + 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (e[s] === "$") {
|
|
231
|
+
const o = G(e, s + 1);
|
|
232
|
+
if (o !== -1) {
|
|
233
|
+
i();
|
|
234
|
+
const l = e.slice(s, o + 1);
|
|
235
|
+
r += d(l), s = o;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (e[s] === "\\" && e[s + 1] === "\\") {
|
|
240
|
+
i(), r += "<br/>", s += 1;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (e.startsWith("``", s) || e.startsWith("''", s)) {
|
|
244
|
+
i(), r += n, s += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (e[s] === "\\") {
|
|
248
|
+
const o = O(e, s);
|
|
249
|
+
if (o) {
|
|
250
|
+
i(), r += o.html, s = o.end - 1;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
t += e[s];
|
|
255
|
+
}
|
|
256
|
+
return i(), r;
|
|
257
|
+
}
|
|
258
|
+
function O(e, r) {
|
|
259
|
+
let t = r + 1;
|
|
260
|
+
for (; t < e.length && /[A-Za-z]/.test(e[t]); )
|
|
261
|
+
t += 1;
|
|
262
|
+
if (t === r + 1)
|
|
263
|
+
return null;
|
|
264
|
+
const n = e.slice(r + 1, t);
|
|
265
|
+
if (n in v) {
|
|
266
|
+
const i = g(e, t);
|
|
267
|
+
if (!i)
|
|
268
|
+
return { html: d(`\\${n}`), end: t };
|
|
269
|
+
const s = m(i.content), o = v[n];
|
|
270
|
+
return n === "textsc" ? { html: `<span>${s}</span>`, end: i.end } : { html: `<${o}>${s}</${o}>`, end: i.end };
|
|
271
|
+
}
|
|
272
|
+
if (_.has(n)) {
|
|
273
|
+
const i = g(e, t);
|
|
274
|
+
return i ? { html: `<span>${m(i.content)}</span>`, end: i.end } : { html: d(`\\${n}`), end: t };
|
|
275
|
+
}
|
|
276
|
+
if (n === "url") {
|
|
277
|
+
const i = g(e, t);
|
|
278
|
+
if (!i)
|
|
279
|
+
return { html: d("\\url"), end: t };
|
|
280
|
+
const s = A(i.content.trim()), o = d(i.content.trim());
|
|
281
|
+
return {
|
|
282
|
+
html: `<a href="${d(s)}" target="_blank" rel="noopener noreferrer">${o}</a>`,
|
|
283
|
+
end: i.end
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (n === "href") {
|
|
287
|
+
const i = g(e, t);
|
|
288
|
+
if (!i)
|
|
289
|
+
return { html: d("\\href"), end: t };
|
|
290
|
+
const s = g(e, i.end);
|
|
291
|
+
if (!s)
|
|
292
|
+
return { html: d("\\href"), end: i.end };
|
|
293
|
+
const o = A(i.content.trim()), l = m(s.content);
|
|
294
|
+
return {
|
|
295
|
+
html: `<a href="${d(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,
|
|
296
|
+
end: s.end
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return { html: d(`\\${n}`), end: t };
|
|
300
|
+
}
|
|
301
|
+
function g(e, r) {
|
|
302
|
+
let t = r;
|
|
303
|
+
for (; t < e.length && /\s/.test(e[t]); )
|
|
304
|
+
t += 1;
|
|
305
|
+
const n = P(e, t);
|
|
306
|
+
return n ? {
|
|
307
|
+
...n,
|
|
308
|
+
contentStart: t + 1
|
|
309
|
+
} : null;
|
|
310
|
+
}
|
|
311
|
+
function P(e, r) {
|
|
312
|
+
if (e[r] !== "{")
|
|
313
|
+
return null;
|
|
314
|
+
let t = 0;
|
|
315
|
+
for (let n = r; n < e.length; n += 1)
|
|
316
|
+
if (e[n] === "{" && e[n - 1] !== "\\")
|
|
317
|
+
t += 1;
|
|
318
|
+
else if (e[n] === "}" && e[n - 1] !== "\\" && (t -= 1, t === 0))
|
|
319
|
+
return {
|
|
320
|
+
content: e.slice(r + 1, n),
|
|
321
|
+
start: r,
|
|
322
|
+
end: n + 1
|
|
323
|
+
};
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
function L(e, r) {
|
|
327
|
+
const t = [];
|
|
328
|
+
let n = "", i = 0;
|
|
329
|
+
for (let s = 0; s < e.length; s += 1) {
|
|
330
|
+
const o = e[s];
|
|
331
|
+
if (o === "{" && e[s - 1] !== "\\" ? i += 1 : o === "}" && e[s - 1] !== "\\" && i > 0 && (i -= 1), i === 0 && r === "&" && o === "&") {
|
|
332
|
+
t.push(n), n = "";
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (i === 0 && r === "\\\\" && o === "\\" && e[s + 1] === "\\") {
|
|
336
|
+
t.push(n), n = "", s += 1;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
n += o;
|
|
340
|
+
}
|
|
341
|
+
return n.trim() && t.push(n), t;
|
|
342
|
+
}
|
|
343
|
+
function D(e, r, t) {
|
|
344
|
+
for (let n = t; n <= e.length - r.length; n += 1) {
|
|
345
|
+
if (e[n] === "\\") {
|
|
346
|
+
n += 1;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (e.startsWith(r, n))
|
|
350
|
+
return n;
|
|
351
|
+
}
|
|
352
|
+
return -1;
|
|
353
|
+
}
|
|
354
|
+
function G(e, r) {
|
|
355
|
+
for (let t = r; t < e.length; t += 1) {
|
|
356
|
+
if (e[t] === "\\") {
|
|
357
|
+
t += 1;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (e[t] === "$" && e[t + 1] !== "$")
|
|
361
|
+
return t;
|
|
362
|
+
}
|
|
363
|
+
return -1;
|
|
364
|
+
}
|
|
365
|
+
function N(e) {
|
|
366
|
+
return e.replace(/<<([\s\S]*?)>>/g, "«$1»").replace(/`(.)'/g, "'$1'").replace(/~---/g, " — ").replace(/"---/g, " — ");
|
|
367
|
+
}
|
|
368
|
+
function B(e) {
|
|
369
|
+
return e.replace(/<[^>]*>/g, (r) => F(r));
|
|
370
|
+
}
|
|
371
|
+
function F(e) {
|
|
372
|
+
const r = e.match(/^<\s*(\/?)\s*([a-zA-Z0-9]+)([^>]*)>$/);
|
|
373
|
+
if (!r)
|
|
374
|
+
return "";
|
|
375
|
+
const t = r[1] === "/", n = r[2].toLowerCase(), i = r[3] ?? "";
|
|
376
|
+
if (!C.has(n))
|
|
377
|
+
return "";
|
|
378
|
+
if (t)
|
|
379
|
+
return `</${n}>`;
|
|
380
|
+
if (n === "br")
|
|
381
|
+
return "<br/>";
|
|
382
|
+
const s = U(i), o = z[n] ?? /* @__PURE__ */ new Set(), l = [];
|
|
383
|
+
for (const [a, u] of s)
|
|
384
|
+
if (o.has(a)) {
|
|
385
|
+
if (n === "a" && a === "href") {
|
|
386
|
+
const f = A(u);
|
|
387
|
+
if (!f)
|
|
388
|
+
continue;
|
|
389
|
+
l.push(["href", f]);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (n === "a" && a === "target") {
|
|
393
|
+
u === "_blank" && l.push(["target", "_blank"]);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (!(n === "a" && a === "rel") && n === "td" && (a === "colspan" || a === "rowspan")) {
|
|
397
|
+
const f = Number.parseInt(u, 10);
|
|
398
|
+
Number.isFinite(f) && f > 0 && l.push([a, String(f)]);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
n === "a" && l.some(([a, u]) => a === "target" && u === "_blank") && l.push(["rel", "noopener noreferrer"]);
|
|
403
|
+
const c = l.map(([a, u]) => ` ${a}="${d(u)}"`).join("");
|
|
404
|
+
return `<${n}${c}>`;
|
|
405
|
+
}
|
|
406
|
+
function U(e) {
|
|
407
|
+
const r = [], t = /([a-zA-Z_:][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
408
|
+
let n;
|
|
409
|
+
for (; (n = t.exec(e)) !== null; ) {
|
|
410
|
+
const s = n[1].toLowerCase(), o = n[2] ?? n[3] ?? n[4] ?? "";
|
|
411
|
+
r.push([s, o]);
|
|
412
|
+
}
|
|
413
|
+
return r;
|
|
414
|
+
}
|
|
415
|
+
function A(e) {
|
|
416
|
+
const r = String(e ?? "").trim();
|
|
417
|
+
if (!r)
|
|
418
|
+
return "";
|
|
419
|
+
const t = /^(https?:\/\/|mailto:|#)/i.test(r), n = /^\/(?!\/)/.test(r);
|
|
420
|
+
return t || n ? r : "";
|
|
421
|
+
}
|
|
422
|
+
function d(e) {
|
|
423
|
+
return String(e).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
424
|
+
}
|
|
425
|
+
function Z(e) {
|
|
426
|
+
if (typeof window > "u" || typeof document > "u")
|
|
427
|
+
return;
|
|
428
|
+
if (e && e.length > 0) {
|
|
429
|
+
const t = new Set(w ?? []);
|
|
430
|
+
for (const n of e)
|
|
431
|
+
t.add(n);
|
|
432
|
+
w = [...t];
|
|
433
|
+
} else
|
|
434
|
+
M = !0, w = null;
|
|
435
|
+
if (y)
|
|
436
|
+
return;
|
|
437
|
+
y = !0;
|
|
438
|
+
const r = async () => {
|
|
439
|
+
y = !1;
|
|
440
|
+
const t = await Q(), n = X();
|
|
441
|
+
if (t != null && t.typesetPromise)
|
|
442
|
+
try {
|
|
443
|
+
t.typesetClear && t.typesetClear(n), await t.typesetPromise(n);
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
if (typeof window.requestAnimationFrame == "function") {
|
|
448
|
+
window.requestAnimationFrame(() => {
|
|
449
|
+
r();
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
setTimeout(() => {
|
|
454
|
+
r();
|
|
455
|
+
}, 0);
|
|
456
|
+
}
|
|
457
|
+
function X() {
|
|
458
|
+
if (M) {
|
|
459
|
+
M = !1, w = null;
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const e = w ?? void 0;
|
|
463
|
+
return w = null, e;
|
|
464
|
+
}
|
|
465
|
+
function Y() {
|
|
466
|
+
const e = [
|
|
467
|
+
'script[data-mathjax="tex-renderer"]',
|
|
468
|
+
"script#MathJax-script",
|
|
469
|
+
'script[src*="mathjax"][src*="tex-mml-chtml"]',
|
|
470
|
+
'script[src*="MathJax.js"]'
|
|
471
|
+
];
|
|
472
|
+
for (const r of e) {
|
|
473
|
+
const t = document.querySelector(r);
|
|
474
|
+
if (t instanceof HTMLScriptElement)
|
|
475
|
+
return t;
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
function Q() {
|
|
480
|
+
var e;
|
|
481
|
+
return typeof window > "u" || typeof document > "u" ? Promise.resolve(null) : (e = window.MathJax) != null && e.typesetPromise ? Promise.resolve(window.MathJax) : $ || (window.MathJax = window.MathJax || {
|
|
482
|
+
tex: {
|
|
483
|
+
inlineMath: [["$", "$"]],
|
|
484
|
+
displayMath: [["$$", "$$"]],
|
|
485
|
+
processEscapes: !0
|
|
486
|
+
},
|
|
487
|
+
svg: {
|
|
488
|
+
fontCache: "global"
|
|
489
|
+
}
|
|
490
|
+
}, $ = new Promise((r, t) => {
|
|
491
|
+
var f;
|
|
492
|
+
const n = Y();
|
|
493
|
+
let i = null, s = !1;
|
|
494
|
+
const o = () => {
|
|
495
|
+
i !== null && (clearTimeout(i), i = null);
|
|
496
|
+
}, l = (p) => {
|
|
497
|
+
s || (s = !0, o(), p());
|
|
498
|
+
}, c = () => {
|
|
499
|
+
l(() => t(new Error("Failed to load MathJax script")));
|
|
500
|
+
}, a = () => {
|
|
501
|
+
l(() => r(window.MathJax ?? null));
|
|
502
|
+
};
|
|
503
|
+
if (i = setTimeout(() => {
|
|
504
|
+
c();
|
|
505
|
+
}, j), n) {
|
|
506
|
+
if ((f = window.MathJax) != null && f.typesetPromise) {
|
|
507
|
+
a();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
n.addEventListener("load", a, { once: !0 }), n.addEventListener("error", c, { once: !0 }), setTimeout(() => {
|
|
511
|
+
var p;
|
|
512
|
+
(p = window.MathJax) != null && p.typesetPromise && a();
|
|
513
|
+
}, 0);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const u = document.createElement("script");
|
|
517
|
+
u.id = "MathJax-script", u.src = I, u.async = !0, u.crossOrigin = "anonymous", u.dataset.mathjax = "tex-renderer", u.addEventListener("load", a, { once: !0 }), u.addEventListener("error", c, { once: !0 }), document.head.append(u);
|
|
518
|
+
}).catch(() => ($ = null, null)), $);
|
|
519
|
+
}
|
|
520
|
+
export {
|
|
521
|
+
K as renderTexStatement
|
|
522
|
+
};
|
|
523
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/renderTexStatement.ts"],"sourcesContent":["// TeX block environments currently supported by this renderer.\nconst SUPPORTED_BLOCK_ENVS = ['itemize', 'enumerate', 'lstlisting', 'center', 'tabular'] as const\n\ntype BlockEnvironment = (typeof SUPPORTED_BLOCK_ENVS)[number]\n\ntype InlineCommandResult = {\n html: string\n end: number\n}\n\nexport type RenderTexStatementOptions = {\n // MathJax typesetting is opt-in to keep parsing side-effect free by default.\n typeset?: boolean\n // Optional container(s) to scope MathJax typesetting instead of whole document.\n typesetTarget?: Element | Element[] | null\n}\n\ntype MathJaxLike = {\n typesetPromise?: (elements?: Element[]) => Promise<unknown>\n typesetClear?: (elements?: Element[]) => void\n tex?: {\n inlineMath?: Array<[string, string]>\n displayMath?: Array<[string, string]>\n processEscapes?: boolean\n }\n svg?: {\n fontCache?: string\n }\n}\n\ndeclare global {\n interface Window {\n MathJax?: MathJaxLike\n }\n}\n\nconst STYLE_TAGS: Record<string, 'strong' | 'em' | 'code' | 'u' | 's' | 'span'> = {\n bf: 'strong',\n textbf: 'strong',\n it: 'em',\n textit: 'em',\n t: 'code',\n tt: 'code',\n texttt: 'code',\n emph: 'u',\n underline: 'u',\n sout: 's',\n textsc: 'span',\n}\n\nconst SIZE_COMMANDS = new Set([\n 'tiny',\n 'scriptsize',\n 'small',\n 'normalsize',\n 'large',\n 'Large',\n 'LARGE',\n 'huge',\n 'Huge',\n])\n\n// Strict HTML allowlist used by sanitizeHtml.\nconst ALLOWED_TAGS = new Set([\n 'a',\n 'blockquote',\n 'br',\n 'code',\n 'div',\n 'em',\n 'footer',\n 'li',\n 'ol',\n 'p',\n 'pre',\n 's',\n 'span',\n 'strong',\n 'table',\n 'tbody',\n 'td',\n 'tr',\n 'u',\n 'ul',\n])\n\n// Attribute allowlist per tag. Any missing attribute is dropped.\nconst ALLOWED_ATTRS_BY_TAG: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']),\n td: new Set(['colspan', 'rowspan']),\n}\n\nlet mathJaxLoadPromise: Promise<MathJaxLike | null> | null = null\nlet mathJaxTypesetScheduled = false\nlet pendingMathJaxTargets: Element[] | null = null\nlet pendingMathJaxGlobalTypeset = false\nconst MATHJAX_SCRIPT_URL = 'https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js'\nconst MATHJAX_SCRIPT_INTEGRITY = ''\nconst MATHJAX_LOAD_TIMEOUT_MS = 8000\n\n/**\n * Convert TeX/LaTeX text into sanitized HTML.\n *\n * The function keeps math payloads (`$...$`, `$$...$$`) intact in output.\n * MathJax typesetting is opt-in through options to keep this parser pure.\n */\nexport function renderTexStatement(tex: string, options: RenderTexStatementOptions = {}): string {\n const normalized = String(tex ?? '').replace(/\\r\\n/g, '\\n')\n const rawHtml = parseBlocks(normalized).join('')\n const html = sanitizeHtml(rawHtml)\n\n if (options.typeset) {\n const scopedTargets = normalizeTypesetTargets(options.typesetTarget)\n scheduleGlobalMathTypeset(scopedTargets)\n }\n\n return html\n}\n\nfunction normalizeTypesetTargets(typesetTarget: RenderTexStatementOptions['typesetTarget']): Element[] | undefined {\n if (!typesetTarget) {\n return undefined\n }\n\n const candidates = Array.isArray(typesetTarget) ? typesetTarget : [typesetTarget]\n const targets = candidates.filter((target): target is Element => Boolean(target))\n return targets.length > 0 ? targets : undefined\n}\n\n/**\n * Parse top-level block structures (paragraphs, environments, epigraph).\n * Falls back to paragraph rendering if malformed blocks are encountered.\n */\nfunction parseBlocks(text: string): string[] {\n const html: string[] = []\n const blockStartRegex = /\\\\begin\\{(itemize|enumerate|lstlisting|center|tabular)\\}|\\\\epigraph\\{/g\n let last = 0\n let match: RegExpExecArray | null\n\n while ((match = blockStartRegex.exec(text)) !== null) {\n if (match.index > last) {\n html.push(...renderParagraphChunk(text.slice(last, match.index)))\n }\n\n if (match[1]) {\n const envName = match[1] as BlockEnvironment\n const env = findEnvironment(text, match.index, envName)\n if (!env) {\n html.push(...renderParagraphChunk(text.slice(match.index)))\n break\n }\n\n if (envName === 'tabular') {\n let spec = ''\n let contentStart = env.innerStart\n const specGroup = parseBraced(text, contentStart)\n if (specGroup) {\n spec = specGroup.content\n contentStart = specGroup.end\n }\n\n const tabularContent = text.slice(contentStart, env.innerEnd)\n html.push(renderTabular(spec, tabularContent))\n } else if (envName === 'itemize' || envName === 'enumerate') {\n const listContent = text.slice(env.innerStart, env.innerEnd)\n html.push(renderList(envName, listContent))\n } else if (envName === 'lstlisting') {\n const code = text.slice(env.innerStart, env.innerEnd).replace(/^\\n+|\\n+$/g, '')\n html.push(`<pre><code>${escapeHtml(code)}</code></pre>`)\n } else if (envName === 'center') {\n const centered = text.slice(env.innerStart, env.innerEnd)\n html.push(`<div>${parseBlocks(centered).join('')}</div>`)\n }\n\n last = env.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n if (text.startsWith('\\\\epigraph{', match.index)) {\n const first = parseBracedWithWhitespace(text, match.index + '\\\\epigraph'.length)\n if (!first) {\n html.push(...renderParagraphChunk(text.slice(match.index, match.index + '\\\\epigraph'.length)))\n last = match.index + '\\\\epigraph'.length\n blockStartRegex.lastIndex = last\n continue\n }\n\n const second = parseBracedWithWhitespace(text, first.end)\n if (!second) {\n html.push(...renderParagraphChunk(text.slice(match.index, first.end)))\n last = first.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n const quoteHtml = parseInline(first.content.trim())\n const authorHtml = parseInline(second.content.trim())\n html.push(`<blockquote><p>${quoteHtml}</p><footer>${authorHtml}</footer></blockquote>`)\n\n last = second.end\n blockStartRegex.lastIndex = last\n continue\n }\n\n last = match.index + match[0].length\n }\n\n if (last < text.length) {\n html.push(...renderParagraphChunk(text.slice(last)))\n }\n\n return html\n}\n\nfunction findEnvironment(text: string, beginIndex: number, envName: BlockEnvironment): null | {\n innerStart: number\n innerEnd: number\n end: number\n} {\n const beginToken = `\\\\begin{${envName}}`\n const endToken = `\\\\end{${envName}}`\n\n let depth = 1\n let cursor = beginIndex + beginToken.length\n\n // Support nested same-name environments by tracking depth.\n while (cursor <= text.length) {\n const nextBegin = text.indexOf(beginToken, cursor)\n const nextEnd = text.indexOf(endToken, cursor)\n\n if (nextEnd === -1) {\n return null\n }\n\n if (nextBegin !== -1 && nextBegin < nextEnd) {\n depth += 1\n cursor = nextBegin + beginToken.length\n continue\n }\n\n depth -= 1\n if (depth === 0) {\n return {\n innerStart: beginIndex + beginToken.length,\n innerEnd: nextEnd,\n end: nextEnd + endToken.length,\n }\n }\n\n cursor = nextEnd + endToken.length\n }\n\n return null\n}\n\nfunction renderParagraphChunk(chunk: string): string[] {\n const out: string[] = []\n const separator = /\\n\\s*\\n/g\n let cursor = 0\n let match: RegExpExecArray | null\n\n while ((match = separator.exec(chunk)) !== null) {\n pushParagraph(out, chunk.slice(cursor, match.index))\n cursor = match.index + match[0].length\n }\n\n pushParagraph(out, chunk.slice(cursor))\n return out\n}\n\n// This renderer uses a blank line as a paragraph separator.\nfunction pushParagraph(output: string[], paragraphRaw: string): void {\n const leadingWhitespace = paragraphRaw.match(/^\\s*/)?.[0].length ?? 0\n const trailingWhitespace = paragraphRaw.match(/\\s*$/)?.[0].length ?? 0\n\n const trimmed = paragraphRaw.slice(leadingWhitespace, paragraphRaw.length - trailingWhitespace)\n if (!trimmed) {\n return\n }\n\n const normalized = trimmed.replace(/\\n/g, ' ')\n const inline = parseInline(normalized)\n if (inline.trim()) {\n output.push(`<p>${inline}</p>`)\n }\n}\n\nfunction renderList(kind: 'itemize' | 'enumerate', content: string): string {\n const itemRegex = /\\\\item\\b/g\n const itemStarts: number[] = []\n let match: RegExpExecArray | null\n while ((match = itemRegex.exec(content)) !== null) {\n itemStarts.push(match.index)\n }\n\n if (itemStarts.length === 0) {\n return ''\n }\n\n const items: string[] = []\n for (let i = 0; i < itemStarts.length; i += 1) {\n const start = itemStarts[i] + '\\\\item'.length\n const end = i + 1 < itemStarts.length ? itemStarts[i + 1] : content.length\n const rawItem = content.slice(start, end)\n const trimmed = rawItem.trim()\n if (!trimmed) {\n continue\n }\n\n const normalized = trimmed.replace(/\\n/g, ' ')\n const itemHtml = parseInline(normalized)\n items.push(`<li>${itemHtml}</li>`)\n }\n\n const tag = kind === 'enumerate' ? 'ol' : 'ul'\n return `<${tag}>${items.join('')}</${tag}>`\n}\n\n/**\n * Render a simple HTML table from tabular rows/cells.\n * Handles \\multicolumn and \\multirow, including carry-over row spans.\n */\nfunction renderTabular(spec: string, content: string): string {\n const rows = splitTopLevel(content, '\\\\\\\\')\n const htmlRows: string[] = []\n // Column index -> remaining rows covered by an active rowspan.\n const activeRowspans = new Map<number, number>()\n let firstPhysicalRow = true\n\n for (const rowToken of rows) {\n const row = rowToken.replace(/\\\\hline/g, '').replace(/\\\\cline\\{[^}]*\\}/g, '').trim()\n if (!row) {\n continue\n }\n\n if (!firstPhysicalRow) {\n decrementRowspanMap(activeRowspans)\n }\n firstPhysicalRow = false\n\n const cells = splitTopLevel(row, '&')\n let col = 0\n const renderedCells: string[] = []\n\n for (const rawCell of cells) {\n const cell = parseTableCell(rawCell)\n // Skip synthetic columns occupied by previous rowspans.\n while (activeRowspans.has(col)) {\n col += 1\n }\n\n let attrs = ''\n if (cell.colspan > 1) {\n attrs += ` colspan=\"${cell.colspan}\"`\n }\n if (cell.rowspan > 1) {\n attrs += ` rowspan=\"${cell.rowspan}\"`\n // Reserve covered columns for upcoming rows.\n for (let step = 0; step < cell.colspan; step += 1) {\n activeRowspans.set(col + step, cell.rowspan - 1)\n }\n }\n\n renderedCells.push(`<td${attrs}>${cell.html}</td>`)\n col += cell.colspan\n }\n\n htmlRows.push(`<tr>${renderedCells.join('')}</tr>`)\n }\n\n const specData = spec ? ` data-spec=\"${escapeHtml(spec)}\"` : ''\n return `<table${specData}><tbody>${htmlRows.join('')}</tbody></table>`\n}\n\nfunction decrementRowspanMap(map: Map<number, number>): void {\n for (const [key, value] of map.entries()) {\n const next = value - 1\n if (next <= 0) {\n map.delete(key)\n } else {\n map.set(key, next)\n }\n }\n}\n\n// Peel nested \\multicolumn/\\multirow wrappers and compute effective spans.\nfunction parseTableCell(rawCell: string): { html: string; colspan: number; rowspan: number } {\n let text = rawCell.trim()\n let colspan = 1\n let rowspan = 1\n\n let changed = true\n while (changed) {\n changed = false\n\n const multiColumn = text.match(/^\\\\multicolumn\\{(\\d+)\\}\\{[^}]*\\}\\{([\\s\\S]*)\\}$/)\n if (multiColumn) {\n colspan *= Number.parseInt(multiColumn[1], 10)\n text = multiColumn[2].trim()\n changed = true\n }\n\n const multiRow = text.match(/^\\\\multirow\\{(\\d+)\\}\\{[^}]*\\}\\{([\\s\\S]*)\\}$/)\n if (multiRow) {\n rowspan *= Number.parseInt(multiRow[1], 10)\n text = multiRow[2].trim()\n changed = true\n }\n }\n\n return {\n html: parseInline(text),\n colspan,\n rowspan,\n }\n}\n\n/**\n * Parse inline text commands and preserve raw math delimiters for MathJax.\n * Unknown commands are escaped and emitted as plain text.\n */\nfunction parseInline(text: string): string {\n let output = ''\n let plain = ''\n const escapedDoubleQuote = escapeHtml('\"')\n\n const flushPlain = (): void => {\n if (!plain) {\n return\n }\n output += escapeHtml(applyTypography(plain))\n plain = ''\n }\n\n for (let i = 0; i < text.length; i += 1) {\n if (text.startsWith('$$', i)) {\n const end = findUnescaped(text, '$$', i + 2)\n if (end !== -1) {\n flushPlain()\n const rawMath = text.slice(i, end + 2)\n output += escapeHtml(rawMath)\n i = end + 1\n continue\n }\n }\n\n if (text[i] === '$') {\n const end = findInlineMathEnd(text, i + 1)\n if (end !== -1) {\n flushPlain()\n const rawMath = text.slice(i, end + 1)\n output += escapeHtml(rawMath)\n i = end\n continue\n }\n }\n\n if (text[i] === '\\\\' && text[i + 1] === '\\\\') {\n flushPlain()\n // TeX line break inside paragraph/list/table cell.\n output += '<br/>'\n i += 1\n continue\n }\n\n if (text.startsWith('``', i) || text.startsWith(\"''\", i)) {\n flushPlain()\n output += escapedDoubleQuote\n i += 1\n continue\n }\n\n if (text[i] === '\\\\') {\n const command = parseInlineCommand(text, i)\n if (command) {\n flushPlain()\n output += command.html\n i = command.end - 1\n continue\n }\n }\n\n plain += text[i]\n }\n\n flushPlain()\n return output\n}\n\nfunction parseInlineCommand(text: string, start: number): InlineCommandResult | null {\n let cursor = start + 1\n while (cursor < text.length && /[A-Za-z]/.test(text[cursor])) {\n cursor += 1\n }\n\n if (cursor === start + 1) {\n return null\n }\n\n const command = text.slice(start + 1, cursor)\n\n if (command in STYLE_TAGS) {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n }\n\n const parsed = parseInline(arg.content)\n const tag = STYLE_TAGS[command]\n if (command === 'textsc') {\n return { html: `<span>${parsed}</span>`, end: arg.end }\n }\n\n return { html: `<${tag}>${parsed}</${tag}>`, end: arg.end }\n }\n\n if (SIZE_COMMANDS.has(command)) {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n }\n\n const parsed = parseInline(arg.content)\n return { html: `<span>${parsed}</span>`, end: arg.end }\n }\n\n if (command === 'url') {\n const arg = parseBracedWithWhitespace(text, cursor)\n if (!arg) {\n return { html: escapeHtml('\\\\url'), end: cursor }\n }\n\n const href = sanitizeUrl(arg.content.trim())\n const caption = escapeHtml(arg.content.trim())\n return {\n html: `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${caption}</a>`,\n end: arg.end,\n }\n }\n\n if (command === 'href') {\n const first = parseBracedWithWhitespace(text, cursor)\n if (!first) {\n return { html: escapeHtml('\\\\href'), end: cursor }\n }\n\n const second = parseBracedWithWhitespace(text, first.end)\n if (!second) {\n return { html: escapeHtml('\\\\href'), end: first.end }\n }\n\n const href = sanitizeUrl(first.content.trim())\n const caption = parseInline(second.content)\n return {\n html: `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${caption}</a>`,\n end: second.end,\n }\n }\n\n return { html: escapeHtml(`\\\\${command}`), end: cursor }\n}\n\n// Parse a braced argument while allowing optional whitespace before it.\nfunction parseBracedWithWhitespace(text: string, start: number): null | {\n content: string\n start: number\n end: number\n contentStart: number\n} {\n let cursor = start\n while (cursor < text.length && /\\s/.test(text[cursor])) {\n cursor += 1\n }\n\n const braced = parseBraced(text, cursor)\n if (!braced) {\n return null\n }\n\n return {\n ...braced,\n contentStart: cursor + 1,\n }\n}\n\n// Parse a balanced {...} group, honoring escaped braces.\nfunction parseBraced(text: string, start: number): null | {\n content: string\n start: number\n end: number\n} {\n if (text[start] !== '{') {\n return null\n }\n\n let depth = 0\n for (let i = start; i < text.length; i += 1) {\n if (text[i] === '{' && text[i - 1] !== '\\\\') {\n depth += 1\n } else if (text[i] === '}' && text[i - 1] !== '\\\\') {\n depth -= 1\n if (depth === 0) {\n return {\n content: text.slice(start + 1, i),\n start,\n end: i + 1,\n }\n }\n }\n }\n\n return null\n}\n\n/**\n * Split by delimiter only at brace-depth zero.\n * Used for tabular row (`\\\\`) and cell (`&`) splitting.\n */\nfunction splitTopLevel(text: string, delimiter: '&' | '\\\\\\\\'): string[] {\n const out: string[] = []\n let current = ''\n let depth = 0\n\n for (let i = 0; i < text.length; i += 1) {\n const char = text[i]\n\n if (char === '{' && text[i - 1] !== '\\\\') {\n depth += 1\n } else if (char === '}' && text[i - 1] !== '\\\\' && depth > 0) {\n depth -= 1\n }\n\n if (depth === 0 && delimiter === '&' && char === '&') {\n out.push(current)\n current = ''\n continue\n }\n\n if (depth === 0 && delimiter === '\\\\\\\\' && char === '\\\\' && text[i + 1] === '\\\\') {\n out.push(current)\n current = ''\n i += 1\n continue\n }\n\n current += char\n }\n\n if (current.trim()) {\n out.push(current)\n }\n\n return out\n}\n\nfunction findUnescaped(text: string, marker: '$$' | '$', start: number): number {\n for (let i = start; i <= text.length - marker.length; i += 1) {\n if (text[i] === '\\\\') {\n i += 1\n continue\n }\n\n if (text.startsWith(marker, i)) {\n return i\n }\n }\n\n return -1\n}\n\nfunction findInlineMathEnd(text: string, start: number): number {\n for (let i = start; i < text.length; i += 1) {\n if (text[i] === '\\\\') {\n i += 1\n continue\n }\n\n if (text[i] === '$' && text[i + 1] !== '$') {\n return i\n }\n }\n\n return -1\n}\n\n// TeX-specific typography token replacements.\nfunction applyTypography(value: string): string {\n return value\n .replace(/<<([\\s\\S]*?)>>/g, '«$1»')\n .replace(/`(.)'/g, \"'$1'\")\n .replace(/~---/g, ' — ')\n .replace(/\"---/g, ' — ')\n}\n\n/**\n * Lightweight sanitizer: preserve only allowlisted tags/attrs\n * and enforce safe URL protocols.\n */\nfunction sanitizeHtml(html: string): string {\n return html.replace(/<[^>]*>/g, (rawTag) => sanitizeTag(rawTag))\n}\n\nfunction sanitizeTag(rawTag: string): string {\n const match = rawTag.match(/^<\\s*(\\/?)\\s*([a-zA-Z0-9]+)([^>]*)>$/)\n if (!match) {\n return ''\n }\n\n const isClosing = match[1] === '/'\n const tag = match[2].toLowerCase()\n const attrText = match[3] ?? ''\n\n if (!ALLOWED_TAGS.has(tag)) {\n return ''\n }\n\n if (isClosing) {\n return `</${tag}>`\n }\n\n if (tag === 'br') {\n return '<br/>'\n }\n\n const attrs = parseAttributes(attrText)\n const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tag] ?? new Set<string>()\n const sanitizedAttrs: Array<[string, string]> = []\n\n for (const [name, value] of attrs) {\n if (!allowedAttrs.has(name)) {\n continue\n }\n\n if (tag === 'a' && name === 'href') {\n const safeHref = sanitizeUrl(value)\n if (!safeHref) {\n continue\n }\n sanitizedAttrs.push(['href', safeHref])\n continue\n }\n\n if (tag === 'a' && name === 'target') {\n if (value === '_blank') {\n sanitizedAttrs.push(['target', '_blank'])\n }\n continue\n }\n\n if (tag === 'a' && name === 'rel') {\n // rel is managed centrally below when target=_blank is present.\n continue\n }\n\n if (tag === 'td' && (name === 'colspan' || name === 'rowspan')) {\n const parsed = Number.parseInt(value, 10)\n if (Number.isFinite(parsed) && parsed > 0) {\n sanitizedAttrs.push([name, String(parsed)])\n }\n continue\n }\n }\n\n if (tag === 'a' && sanitizedAttrs.some(([name, value]) => name === 'target' && value === '_blank')) {\n sanitizedAttrs.push(['rel', 'noopener noreferrer'])\n }\n\n const attrsString = sanitizedAttrs\n .map(([name, value]) => ` ${name}=\"${escapeHtml(value)}\"`)\n .join('')\n\n return `<${tag}${attrsString}>`\n}\n\nfunction parseAttributes(attributeText: string): Array<[string, string]> {\n const attributes: Array<[string, string]> = []\n const regex = /([a-zA-Z_:][\\w:.-]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n\n let match: RegExpExecArray | null\n while ((match = regex.exec(attributeText)) !== null) {\n const rawName = match[1]\n const name = rawName.toLowerCase()\n const value = match[2] ?? match[3] ?? match[4] ?? ''\n attributes.push([name, value])\n }\n\n return attributes\n}\n\nfunction sanitizeUrl(url: string): string {\n const trimmed = String(url ?? '').trim()\n if (!trimmed) {\n return ''\n }\n\n const isAllowedProtocol = /^(https?:\\/\\/|mailto:|#)/i.test(trimmed)\n const isSafeRelativePath = /^\\/(?!\\/)/.test(trimmed)\n\n return isAllowedProtocol || isSafeRelativePath ? trimmed : ''\n}\n\nfunction escapeHtml(value: string): string {\n return String(value)\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''')\n}\n\n/**\n * Debounced global typeset trigger. It runs once per animation frame\n * regardless of how many render calls happen in that frame.\n */\nfunction scheduleGlobalMathTypeset(targets?: Element[]): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n if (targets && targets.length > 0) {\n const uniqueTargets = new Set(pendingMathJaxTargets ?? [])\n for (const target of targets) {\n uniqueTargets.add(target)\n }\n pendingMathJaxTargets = [...uniqueTargets]\n } else {\n pendingMathJaxGlobalTypeset = true\n pendingMathJaxTargets = null\n }\n\n if (mathJaxTypesetScheduled) {\n return\n }\n mathJaxTypesetScheduled = true\n\n const runTypeset = async (): Promise<void> => {\n mathJaxTypesetScheduled = false\n const mathJax = await ensureMathJax()\n const scopedTargets = selectTypesetTargetsForRun()\n if (!mathJax?.typesetPromise) {\n return\n }\n\n try {\n if (mathJax.typesetClear) {\n // Avoid stale state from previous typesets.\n mathJax.typesetClear(scopedTargets)\n }\n await mathJax.typesetPromise(scopedTargets)\n } catch {\n // Keep rendering non-blocking if MathJax fails to typeset.\n }\n }\n\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(() => {\n void runTypeset()\n })\n return\n }\n\n setTimeout(() => {\n void runTypeset()\n }, 0)\n}\n\nfunction selectTypesetTargetsForRun(): Element[] | undefined {\n if (pendingMathJaxGlobalTypeset) {\n pendingMathJaxGlobalTypeset = false\n pendingMathJaxTargets = null\n return undefined\n }\n\n const scopedTargets = pendingMathJaxTargets ?? undefined\n pendingMathJaxTargets = null\n return scopedTargets\n}\n\nfunction findExistingMathJaxScript(): HTMLScriptElement | null {\n const selectors = [\n 'script[data-mathjax=\"tex-renderer\"]',\n 'script#MathJax-script',\n 'script[src*=\"mathjax\"][src*=\"tex-mml-chtml\"]',\n 'script[src*=\"MathJax.js\"]',\n ]\n\n for (const selector of selectors) {\n const candidate = document.querySelector(selector)\n if (candidate instanceof HTMLScriptElement) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Ensure MathJax v3 is loaded exactly once in browser environments.\n * Returns null when unavailable/failing to keep rendering resilient.\n */\nfunction ensureMathJax(): Promise<MathJaxLike | null> {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.resolve(null)\n }\n\n if (window.MathJax?.typesetPromise) {\n return Promise.resolve(window.MathJax)\n }\n\n if (mathJaxLoadPromise) {\n return mathJaxLoadPromise\n }\n\n window.MathJax = window.MathJax || {\n tex: {\n inlineMath: [['$', '$']],\n displayMath: [['$$', '$$']],\n processEscapes: true,\n },\n svg: {\n fontCache: 'global',\n },\n }\n\n mathJaxLoadPromise = new Promise<MathJaxLike | null>((resolve, reject) => {\n const existing = findExistingMathJaxScript()\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n let settled = false\n\n const clearPendingTimeout = (): void => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId)\n timeoutId = null\n }\n }\n\n const settle = (callback: () => void): void => {\n if (settled) {\n return\n }\n settled = true\n clearPendingTimeout()\n callback()\n }\n\n const fail = (): void => {\n settle(() => reject(new Error('Failed to load MathJax script')))\n }\n\n const succeed = (): void => {\n settle(() => resolve(window.MathJax ?? null))\n }\n\n timeoutId = setTimeout(() => {\n fail()\n }, MATHJAX_LOAD_TIMEOUT_MS)\n\n if (existing) {\n if (window.MathJax?.typesetPromise) {\n succeed()\n return\n }\n existing.addEventListener('load', succeed, { once: true })\n existing.addEventListener('error', fail, { once: true })\n // Existing scripts can already be loaded before listeners are attached.\n setTimeout(() => {\n if (window.MathJax?.typesetPromise) {\n succeed()\n }\n }, 0)\n return\n }\n\n const script = document.createElement('script')\n script.id = 'MathJax-script'\n script.src = MATHJAX_SCRIPT_URL\n script.async = true\n script.crossOrigin = 'anonymous'\n if (MATHJAX_SCRIPT_INTEGRITY) {\n script.integrity = MATHJAX_SCRIPT_INTEGRITY\n }\n script.dataset.mathjax = 'tex-renderer'\n script.addEventListener('load', succeed, { once: true })\n script.addEventListener('error', fail, { once: true })\n document.head.append(script)\n }).catch(() => {\n mathJaxLoadPromise = null\n return null\n })\n\n return mathJaxLoadPromise\n}\n"],"names":["STYLE_TAGS","SIZE_COMMANDS","ALLOWED_TAGS","ALLOWED_ATTRS_BY_TAG","mathJaxLoadPromise","mathJaxTypesetScheduled","pendingMathJaxTargets","pendingMathJaxGlobalTypeset","MATHJAX_SCRIPT_URL","MATHJAX_LOAD_TIMEOUT_MS","renderTexStatement","tex","options","normalized","rawHtml","parseBlocks","html","sanitizeHtml","scopedTargets","normalizeTypesetTargets","scheduleGlobalMathTypeset","typesetTarget","targets","target","text","blockStartRegex","last","match","renderParagraphChunk","envName","env","findEnvironment","spec","contentStart","specGroup","parseBraced","tabularContent","renderTabular","listContent","renderList","code","escapeHtml","centered","first","parseBracedWithWhitespace","second","quoteHtml","parseInline","authorHtml","beginIndex","beginToken","endToken","depth","cursor","nextBegin","nextEnd","chunk","out","separator","pushParagraph","output","paragraphRaw","_a","_b","leadingWhitespace","trailingWhitespace","trimmed","inline","kind","content","itemRegex","itemStarts","items","i","start","end","itemHtml","tag","rows","splitTopLevel","htmlRows","activeRowspans","firstPhysicalRow","rowToken","row","decrementRowspanMap","cells","col","renderedCells","rawCell","cell","parseTableCell","attrs","step","map","key","value","next","colspan","rowspan","changed","multiColumn","multiRow","plain","escapedDoubleQuote","flushPlain","applyTypography","findUnescaped","rawMath","findInlineMathEnd","command","parseInlineCommand","arg","parsed","href","sanitizeUrl","caption","braced","delimiter","current","char","marker","rawTag","sanitizeTag","isClosing","attrText","parseAttributes","allowedAttrs","sanitizedAttrs","name","safeHref","attrsString","attributeText","attributes","regex","url","isAllowedProtocol","isSafeRelativePath","uniqueTargets","runTypeset","mathJax","ensureMathJax","selectTypesetTargetsForRun","findExistingMathJaxScript","selectors","selector","candidate","resolve","reject","existing","timeoutId","settled","clearPendingTimeout","settle","callback","fail","succeed","script"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type RenderTexStatementOptions = {
|
|
2
|
+
typeset?: boolean;
|
|
3
|
+
typesetTarget?: Element | Element[] | null;
|
|
4
|
+
};
|
|
5
|
+
type MathJaxLike = {
|
|
6
|
+
typesetPromise?: (elements?: Element[]) => Promise<unknown>;
|
|
7
|
+
typesetClear?: (elements?: Element[]) => void;
|
|
8
|
+
tex?: {
|
|
9
|
+
inlineMath?: Array<[string, string]>;
|
|
10
|
+
displayMath?: Array<[string, string]>;
|
|
11
|
+
processEscapes?: boolean;
|
|
12
|
+
};
|
|
13
|
+
svg?: {
|
|
14
|
+
fontCache?: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
declare global {
|
|
18
|
+
interface Window {
|
|
19
|
+
MathJax?: MathJaxLike;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert TeX/LaTeX text into sanitized HTML.
|
|
24
|
+
*
|
|
25
|
+
* The function keeps math payloads (`$...$`, `$$...$$`) intact in output.
|
|
26
|
+
* MathJax typesetting is opt-in through options to keep this parser pure.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderTexStatement(tex: string, options?: RenderTexStatementOptions): string;
|
|
29
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@newtonschool/tex-html-parser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript parser/transformer for TeX-to-HTML rendering.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "vite build --config vite.lib.config.js && tsc --project tsconfig.build.json",
|
|
23
|
+
"test": "node scripts/run-tests.mjs",
|
|
24
|
+
"test:package": "node scripts/run-tests.mjs test/package-artifacts.test.ts",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^25.3.0",
|
|
33
|
+
"esbuild": "^0.25.0",
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vite": "^5.4.10"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"tex",
|
|
39
|
+
"latex",
|
|
40
|
+
"html",
|
|
41
|
+
"parser",
|
|
42
|
+
"sanitizer"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|