@newtonschool/tex-html-parser 0.1.1 → 0.2.1
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 +8 -0
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +166 -165
- package/dist/index.mjs.map +1 -1
- package/dist/renderTexStatement.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,14 @@ const html = renderTexStatement(tex, {
|
|
|
41
41
|
})
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
To preserve single newlines as `<br/>` within paragraphs (useful for literal payloads such as sample input/output blocks):
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const html = renderTexStatement(tex, { preserveNewlines: true })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Blank-line paragraph splitting is unchanged. By default, single newlines collapse to whitespace per TeX prose convention.
|
|
51
|
+
|
|
44
52
|
MathJax loader security notes:
|
|
45
53
|
|
|
46
54
|
- The package injects a pinned MathJax CDN URL (`jsDelivr`) at runtime.
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
`),n=J(t).join(""),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A={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"]),j={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",z=8e3;function k(e,r={}){const t=String(e??"").replace(/\r\n/g,`
|
|
2
|
+
`),n=J(t,{preserveNewlines:r.preserveNewlines===!0}).join(""),s=F(n);if(r.typeset){const i=H(r.typesetTarget);X(i)}return s}function H(e){if(!e)return;const t=(Array.isArray(e)?e:[e]).filter(n=>!!n);return t.length>0?t:void 0}function J(e,r){const t=[],n=/\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;let s=0,i;for(;(i=n.exec(e))!==null;){if(i.index>s&&t.push(...b(e.slice(s,i.index),r)),i[1]){const o=i[1],l=W(e,i.index,o);if(!l){t.push(...b(e.slice(i.index),r));break}if(o==="tabular"){let c="",a=l.innerStart;const u=P(e,a);u&&(c=u.content,a=u.end);const d=e.slice(a,l.innerEnd);t.push(R(c,d))}else if(o==="itemize"||o==="enumerate"){const c=e.slice(l.innerStart,l.innerEnd);t.push(N(o,c))}else if(o==="lstlisting"){const c=e.slice(l.innerStart,l.innerEnd).replace(/^\n+|\n+$/g,"");t.push(`<pre><code>${f(c)}</code></pre>`)}else if(o==="center"){const c=e.slice(l.innerStart,l.innerEnd);t.push(`<div>${J(c,r).join("")}</div>`)}s=l.end,n.lastIndex=s;continue}if(e.startsWith("\\epigraph{",i.index)){const o=g(e,i.index+9);if(!o){t.push(...b(e.slice(i.index,i.index+9),r)),s=i.index+9,n.lastIndex=s;continue}const l=g(e,o.end);if(!l){t.push(...b(e.slice(i.index,o.end),r)),s=o.end,n.lastIndex=s;continue}const c=h(o.content.trim()),a=h(l.content.trim());t.push(`<blockquote><p>${c}</p><footer>${a}</footer></blockquote>`),s=l.end,n.lastIndex=s;continue}s=i.index+i[0].length}return s<e.length&&t.push(...b(e.slice(s),r)),t}function W(e,r,t){const n=`\\begin{${t}}`,s=`\\end{${t}}`;let i=1,o=r+n.length;for(;o<=e.length;){const l=e.indexOf(n,o),c=e.indexOf(s,o);if(c===-1)return null;if(l!==-1&&l<c){i+=1,o=l+n.length;continue}if(i-=1,i===0)return{innerStart:r+n.length,innerEnd:c,end:c+s.length};o=c+s.length}return null}function b(e,r){const t=[],n=/\n\s*\n/g;let s=0,i;for(;(i=n.exec(e))!==null;)E(t,e.slice(s,i.index),r),s=i.index+i[0].length;return E(t,e.slice(s),r),t}function E(e,r,t){var l,c;const n=((l=r.match(/^\s*/))==null?void 0:l[0].length)??0,s=((c=r.match(/\s*$/))==null?void 0:c[0].length)??0,i=r.slice(n,r.length-s);if(!i)return;const o=t.preserveNewlines?i.split(`
|
|
3
|
+
`).map(a=>h(a)).join("<br/>"):h(i.replace(/\n/g," "));o.trim()&&e.push(`<p>${o}</p>`)}function N(e,r){const t=/\\item\b/g,n=[];let s;for(;(s=t.exec(r))!==null;)n.push(s.index);if(n.length===0)return"";const i=[];for(let l=0;l<n.length;l+=1){const c=n[l]+5,a=l+1<n.length?n[l+1]:r.length,d=r.slice(c,a).trim();if(!d)continue;const m=d.replace(/\n/g," "),p=h(m);i.push(`<li>${p}</li>`)}const o=e==="enumerate"?"ol":"ul";return`<${o}>${i.join("")}</${o}>`}function R(e,r){const t=L(r,"\\\\"),n=[],s=new Map;let i=!0;for(const l of t){const c=l.replace(/\\hline/g,"").replace(/\\cline\{[^}]*\}/g,"").trim();if(!c)continue;i||q(s),i=!1;const a=L(c,"&");let u=0;const d=[];for(const m of a){const p=O(m);for(;s.has(u);)u+=1;let S="";if(p.colspan>1&&(S+=` colspan="${p.colspan}"`),p.rowspan>1){S+=` rowspan="${p.rowspan}"`;for(let T=0;T<p.colspan;T+=1)s.set(u+T,p.rowspan-1)}d.push(`<td${S}>${p.html}</td>`),u+=p.colspan}n.push(`<tr>${d.join("")}</tr>`)}return`<table${e?` data-spec="${f(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,s=!0;for(;s;){s=!1;const i=r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);i&&(t*=Number.parseInt(i[1],10),r=i[2].trim(),s=!0);const o=r.match(/^\\multirow\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);o&&(n*=Number.parseInt(o[1],10),r=o[2].trim(),s=!0)}return{html:h(r),colspan:t,rowspan:n}}function h(e){let r="",t="";const n=f('"'),s=()=>{t&&(r+=f(x(t)),t="")};for(let i=0;i<e.length;i+=1){if(e.startsWith("$$",i)){const o=G(e,"$$",i+2);if(o!==-1){s();const l=e.slice(i,o+2);r+=f(l),i=o+1;continue}}if(e[i]==="$"){const o=B(e,i+1);if(o!==-1){s();const l=e.slice(i,o+1);r+=f(l),i=o;continue}}if(e[i]==="\\"&&e[i+1]==="\\"){s(),r+="<br/>",i+=1;continue}if(e.startsWith("``",i)||e.startsWith("''",i)){s(),r+=n,i+=1;continue}if(e[i]==="\\"){const o=D(e,i);if(o){s(),r+=o.html,i=o.end-1;continue}}t+=e[i]}return s(),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 A){const s=g(e,t);if(!s)return{html:f(`\\${n}`),end:t};const i=h(s.content),o=A[n];return n==="textsc"?{html:`<span>${i}</span>`,end:s.end}:{html:`<${o}>${i}</${o}>`,end:s.end}}if(_.has(n)){const s=g(e,t);return s?{html:`<span>${h(s.content)}</span>`,end:s.end}:{html:f(`\\${n}`),end:t}}if(n==="url"){const s=g(e,t);if(!s)return{html:f("\\url"),end:t};const i=v(s.content.trim()),o=f(s.content.trim());return{html:`<a href="${f(i)}" target="_blank" rel="noopener noreferrer">${o}</a>`,end:s.end}}if(n==="href"){const s=g(e,t);if(!s)return{html:f("\\href"),end:t};const i=g(e,s.end);if(!i)return{html:f("\\href"),end:s.end};const o=v(s.content.trim()),l=h(i.content);return{html:`<a href="${f(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,end:i.end}}return{html:f(`\\${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="",s=0;for(let i=0;i<e.length;i+=1){const o=e[i];if(o==="{"&&e[i-1]!=="\\"?s+=1:o==="}"&&e[i-1]!=="\\"&&s>0&&(s-=1),s===0&&r==="&"&&o==="&"){t.push(n),n="";continue}if(s===0&&r==="\\\\"&&o==="\\"&&e[i+1]==="\\"){t.push(n),n="",i+=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 B(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 x(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(),s=r[3]??"";if(!C.has(n))return"";if(t)return`</${n}>`;if(n==="br")return"<br/>";const i=Z(s),o=j[n]??new Set,l=[];for(const[a,u]of i)if(o.has(a)){if(n==="a"&&a==="href"){const d=v(u);if(!d)continue;l.push(["href",d]);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 d=Number.parseInt(u,10);Number.isFinite(d)&&d>0&&l.push([a,String(d)]);continue}}n==="a"&&l.some(([a,u])=>a==="target"&&u==="_blank")&&l.push(["rel","noopener noreferrer"]);const c=l.map(([a,u])=>` ${a}="${f(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 i=n[1].toLowerCase(),o=n[2]??n[3]??n[4]??"";r.push([i,o])}return r}function v(e){const r=String(e??"").trim();if(!r)return"";const t=/^(https?:\/\/|mailto:|#)/i.test(r),n=/^\/(?!\/)/.test(r);return t||n?r:""}function f(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 d;const n=Q();let s=null,i=!1;const o=()=>{s!==null&&(clearTimeout(s),s=null)},l=m=>{i||(i=!0,o(),m())},c=()=>{l(()=>t(new Error("Failed to load MathJax script")))},a=()=>{l(()=>r(window.MathJax??null))};if(s=setTimeout(()=>{c()},z),n){if((d=window.MathJax)!=null&&d.typesetPromise){a();return}n.addEventListener("load",a,{once:!0}),n.addEventListener("error",c,{once:!0}),setTimeout(()=>{var m;(m=window.MathJax)!=null&&m.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
4
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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":"gFAoCA,MAAMA,EAA4E,CAChF,GAAI,SACJ,OAAQ,SACR,GAAI,KACJ,OAAQ,KACR,EAAG,OACH,GAAI,OACJ,OAAQ,OACR,KAAM,IACN,UAAW,IACX,KAAM,IACN,OAAQ,MACV,EAEMC,MAAoB,IAAI,CAC5B,OACA,aACA,QACA,aACA,QACA,QACA,QACA,OACA,MACF,CAAC,EAGKC,MAAmB,IAAI,CAC3B,IACA,aACA,KACA,OACA,MACA,KACA,SACA,KACA,KACA,IACA,MACA,IACA,OACA,SACA,QACA,QACA,KACA,KACA,IACA,IACF,CAAC,EAGKC,EAAoD,CACxD,EAAG,IAAI,IAAI,CAAC,OAAQ,SAAU,KAAK,CAAC,EACpC,GAAI,IAAI,IAAI,CAAC,UAAW,SAAS,CAAC,CACpC,EAEA,IAAIC,EAAyD,KACzDC,EAA0B,GAC1BC,EAA0C,KAC1CC,EAA8B,GAClC,MAAMC,EAAqB,kEAErBC,EAA0B,IAQzB,SAASC,EAAmBC,EAAaC,EAAqC,GAAY,CAC/F,MAAMC,EAAa,OAAOF,GAAO,EAAE,EAAE,QAAQ,QAAS;AAAA,CAAI,EACpDG,EAAUC,EAAYF,CAAU,EAAE,KAAK,EAAE,EACzCG,EAAOC,EAAaH,CAAO,EAEjC,GAAIF,EAAQ,QAAS,CACnB,MAAMM,EAAgBC,EAAwBP,EAAQ,aAAa,EACnEQ,EAA0BF,CAAa,CACzC,CAEA,OAAOF,CACT,CAEA,SAASG,EAAwBE,EAAkF,CACjH,GAAI,CAACA,EACH,OAIF,MAAMC,GADa,MAAM,QAAQD,CAAa,EAAIA,EAAgB,CAACA,CAAa,GACrD,OAAQE,GAA8B,EAAQA,CAAO,EAChF,OAAOD,EAAQ,OAAS,EAAIA,EAAU,MACxC,CAMA,SAASP,EAAYS,EAAwB,CAC3C,MAAMR,EAAiB,CAAA,EACjBS,EAAkB,yEACxB,IAAIC,EAAO,EACPC,EAEJ,MAAQA,EAAQF,EAAgB,KAAKD,CAAI,KAAO,MAAM,CAKpD,GAJIG,EAAM,MAAQD,GAChBV,EAAK,KAAK,GAAGY,EAAqBJ,EAAK,MAAME,EAAMC,EAAM,KAAK,CAAC,CAAC,EAG9DA,EAAM,CAAC,EAAG,CACZ,MAAME,EAAUF,EAAM,CAAC,EACjBG,EAAMC,EAAgBP,EAAMG,EAAM,MAAOE,CAAO,EACtD,GAAI,CAACC,EAAK,CACRd,EAAK,KAAK,GAAGY,EAAqBJ,EAAK,MAAMG,EAAM,KAAK,CAAC,CAAC,EAC1D,KACF,CAEA,GAAIE,IAAY,UAAW,CACzB,IAAIG,EAAO,GACPC,EAAeH,EAAI,WACvB,MAAMI,EAAYC,EAAYX,EAAMS,CAAY,EAC5CC,IACFF,EAAOE,EAAU,QACjBD,EAAeC,EAAU,KAG3B,MAAME,EAAiBZ,EAAK,MAAMS,EAAcH,EAAI,QAAQ,EAC5Dd,EAAK,KAAKqB,EAAcL,EAAMI,CAAc,CAAC,CAC/C,SAAWP,IAAY,WAAaA,IAAY,YAAa,CAC3D,MAAMS,EAAcd,EAAK,MAAMM,EAAI,WAAYA,EAAI,QAAQ,EAC3Dd,EAAK,KAAKuB,EAAWV,EAASS,CAAW,CAAC,CAC5C,SAAWT,IAAY,aAAc,CACnC,MAAMW,EAAOhB,EAAK,MAAMM,EAAI,WAAYA,EAAI,QAAQ,EAAE,QAAQ,aAAc,EAAE,EAC9Ed,EAAK,KAAK,cAAcyB,EAAWD,CAAI,CAAC,eAAe,CACzD,SAAWX,IAAY,SAAU,CAC/B,MAAMa,EAAWlB,EAAK,MAAMM,EAAI,WAAYA,EAAI,QAAQ,EACxDd,EAAK,KAAK,QAAQD,EAAY2B,CAAQ,EAAE,KAAK,EAAE,CAAC,QAAQ,CAC1D,CAEAhB,EAAOI,EAAI,IACXL,EAAgB,UAAYC,EAC5B,QACF,CAEA,GAAIF,EAAK,WAAW,cAAeG,EAAM,KAAK,EAAG,CAC/C,MAAMgB,EAAQC,EAA0BpB,EAAMG,EAAM,MAAQ,CAAmB,EAC/E,GAAI,CAACgB,EAAO,CACV3B,EAAK,KAAK,GAAGY,EAAqBJ,EAAK,MAAMG,EAAM,MAAOA,EAAM,MAAQ,CAAmB,CAAC,CAAC,EAC7FD,EAAOC,EAAM,MAAQ,EACrBF,EAAgB,UAAYC,EAC5B,QACF,CAEA,MAAMmB,EAASD,EAA0BpB,EAAMmB,EAAM,GAAG,EACxD,GAAI,CAACE,EAAQ,CACX7B,EAAK,KAAK,GAAGY,EAAqBJ,EAAK,MAAMG,EAAM,MAAOgB,EAAM,GAAG,CAAC,CAAC,EACrEjB,EAAOiB,EAAM,IACblB,EAAgB,UAAYC,EAC5B,QACF,CAEA,MAAMoB,EAAYC,EAAYJ,EAAM,QAAQ,MAAM,EAC5CK,EAAaD,EAAYF,EAAO,QAAQ,MAAM,EACpD7B,EAAK,KAAK,kBAAkB8B,CAAS,eAAeE,CAAU,wBAAwB,EAEtFtB,EAAOmB,EAAO,IACdpB,EAAgB,UAAYC,EAC5B,QACF,CAEAA,EAAOC,EAAM,MAAQA,EAAM,CAAC,EAAE,MAChC,CAEA,OAAID,EAAOF,EAAK,QACdR,EAAK,KAAK,GAAGY,EAAqBJ,EAAK,MAAME,CAAI,CAAC,CAAC,EAG9CV,CACT,CAEA,SAASe,EAAgBP,EAAcyB,EAAoBpB,EAIzD,CACA,MAAMqB,EAAa,WAAWrB,CAAO,IAC/BsB,EAAW,SAAStB,CAAO,IAEjC,IAAIuB,EAAQ,EACRC,EAASJ,EAAaC,EAAW,OAGrC,KAAOG,GAAU7B,EAAK,QAAQ,CAC5B,MAAM8B,EAAY9B,EAAK,QAAQ0B,EAAYG,CAAM,EAC3CE,EAAU/B,EAAK,QAAQ2B,EAAUE,CAAM,EAE7C,GAAIE,IAAY,GACd,OAAO,KAGT,GAAID,IAAc,IAAMA,EAAYC,EAAS,CAC3CH,GAAS,EACTC,EAASC,EAAYJ,EAAW,OAChC,QACF,CAGA,GADAE,GAAS,EACLA,IAAU,EACZ,MAAO,CACL,WAAYH,EAAaC,EAAW,OACpC,SAAUK,EACV,IAAKA,EAAUJ,EAAS,MAAA,EAI5BE,EAASE,EAAUJ,EAAS,MAC9B,CAEA,OAAO,IACT,CAEA,SAASvB,EAAqB4B,EAAyB,CACrD,MAAMC,EAAgB,CAAA,EAChBC,EAAY,WAClB,IAAIL,EAAS,EACT1B,EAEJ,MAAQA,EAAQ+B,EAAU,KAAKF,CAAK,KAAO,MACzCG,EAAcF,EAAKD,EAAM,MAAMH,EAAQ1B,EAAM,KAAK,CAAC,EACnD0B,EAAS1B,EAAM,MAAQA,EAAM,CAAC,EAAE,OAGlC,OAAAgC,EAAcF,EAAKD,EAAM,MAAMH,CAAM,CAAC,EAC/BI,CACT,CAGA,SAASE,EAAcC,EAAkBC,EAA4B,SACnE,MAAMC,IAAoBC,EAAAF,EAAa,MAAM,MAAM,IAAzB,YAAAE,EAA6B,GAAG,SAAU,EAC9DC,IAAqBC,EAAAJ,EAAa,MAAM,MAAM,IAAzB,YAAAI,EAA6B,GAAG,SAAU,EAE/DC,EAAUL,EAAa,MAAMC,EAAmBD,EAAa,OAASG,CAAkB,EAC9F,GAAI,CAACE,EACH,OAGF,MAAMrD,EAAaqD,EAAQ,QAAQ,MAAO,GAAG,EACvCC,EAASpB,EAAYlC,CAAU,EACjCsD,EAAO,QACTP,EAAO,KAAK,MAAMO,CAAM,MAAM,CAElC,CAEA,SAAS5B,EAAW6B,EAA+BC,EAAyB,CAC1E,MAAMC,EAAY,YACZC,EAAuB,CAAA,EAC7B,IAAI5C,EACJ,MAAQA,EAAQ2C,EAAU,KAAKD,CAAO,KAAO,MAC3CE,EAAW,KAAK5C,EAAM,KAAK,EAG7B,GAAI4C,EAAW,SAAW,EACxB,MAAO,GAGT,MAAMC,EAAkB,CAAA,EACxB,QAASC,EAAI,EAAGA,EAAIF,EAAW,OAAQE,GAAK,EAAG,CAC7C,MAAMC,EAAQH,EAAWE,CAAC,EAAI,EACxBE,EAAMF,EAAI,EAAIF,EAAW,OAASA,EAAWE,EAAI,CAAC,EAAIJ,EAAQ,OAE9DH,EADUG,EAAQ,MAAMK,EAAOC,CAAG,EAChB,KAAA,EACxB,GAAI,CAACT,EACH,SAGF,MAAMrD,EAAaqD,EAAQ,QAAQ,MAAO,GAAG,EACvCU,EAAW7B,EAAYlC,CAAU,EACvC2D,EAAM,KAAK,OAAOI,CAAQ,OAAO,CACnC,CAEA,MAAMC,EAAMT,IAAS,YAAc,KAAO,KAC1C,MAAO,IAAIS,CAAG,IAAIL,EAAM,KAAK,EAAE,CAAC,KAAKK,CAAG,GAC1C,CAMA,SAASxC,EAAcL,EAAcqC,EAAyB,CAC5D,MAAMS,EAAOC,EAAcV,EAAS,MAAM,EACpCW,EAAqB,CAAA,EAErBC,MAAqB,IAC3B,IAAIC,EAAmB,GAEvB,UAAWC,KAAYL,EAAM,CAC3B,MAAMM,EAAMD,EAAS,QAAQ,WAAY,EAAE,EAAE,QAAQ,oBAAqB,EAAE,EAAE,KAAA,EAC9E,GAAI,CAACC,EACH,SAGGF,GACHG,EAAoBJ,CAAc,EAEpCC,EAAmB,GAEnB,MAAMI,EAAQP,EAAcK,EAAK,GAAG,EACpC,IAAIG,EAAM,EACV,MAAMC,EAA0B,CAAA,EAEhC,UAAWC,KAAWH,EAAO,CAC3B,MAAMI,EAAOC,EAAeF,CAAO,EAEnC,KAAOR,EAAe,IAAIM,CAAG,GAC3BA,GAAO,EAGT,IAAIK,EAAQ,GAIZ,GAHIF,EAAK,QAAU,IACjBE,GAAS,aAAaF,EAAK,OAAO,KAEhCA,EAAK,QAAU,EAAG,CACpBE,GAAS,aAAaF,EAAK,OAAO,IAElC,QAASG,EAAO,EAAGA,EAAOH,EAAK,QAASG,GAAQ,EAC9CZ,EAAe,IAAIM,EAAMM,EAAMH,EAAK,QAAU,CAAC,CAEnD,CAEAF,EAAc,KAAK,MAAMI,CAAK,IAAIF,EAAK,IAAI,OAAO,EAClDH,GAAOG,EAAK,OACd,CAEAV,EAAS,KAAK,OAAOQ,EAAc,KAAK,EAAE,CAAC,OAAO,CACpD,CAGA,MAAO,SADUxD,EAAO,eAAeS,EAAWT,CAAI,CAAC,IAAM,EACrC,WAAWgD,EAAS,KAAK,EAAE,CAAC,kBACtD,CAEA,SAASK,EAAoBS,EAAgC,CAC3D,SAAW,CAACC,EAAKC,CAAK,IAAKF,EAAI,UAAW,CACxC,MAAMG,EAAOD,EAAQ,EACjBC,GAAQ,EACVH,EAAI,OAAOC,CAAG,EAEdD,EAAI,IAAIC,EAAKE,CAAI,CAErB,CACF,CAGA,SAASN,EAAeF,EAAqE,CAC3F,IAAIjE,EAAOiE,EAAQ,KAAA,EACfS,EAAU,EACVC,EAAU,EAEVC,EAAU,GACd,KAAOA,GAAS,CACdA,EAAU,GAEV,MAAMC,EAAc7E,EAAK,MAAM,gDAAgD,EAC3E6E,IACFH,GAAW,OAAO,SAASG,EAAY,CAAC,EAAG,EAAE,EAC7C7E,EAAO6E,EAAY,CAAC,EAAE,KAAA,EACtBD,EAAU,IAGZ,MAAME,EAAW9E,EAAK,MAAM,6CAA6C,EACrE8E,IACFH,GAAW,OAAO,SAASG,EAAS,CAAC,EAAG,EAAE,EAC1C9E,EAAO8E,EAAS,CAAC,EAAE,KAAA,EACnBF,EAAU,GAEd,CAEA,MAAO,CACL,KAAMrD,EAAYvB,CAAI,EACtB,QAAA0E,EACA,QAAAC,CAAA,CAEJ,CAMA,SAASpD,EAAYvB,EAAsB,CACzC,IAAIoC,EAAS,GACT2C,EAAQ,GACZ,MAAMC,EAAqB/D,EAAW,GAAG,EAEnCgE,EAAa,IAAY,CACxBF,IAGL3C,GAAUnB,EAAWiE,EAAgBH,CAAK,CAAC,EAC3CA,EAAQ,GACV,EAEA,QAAS9B,EAAI,EAAGA,EAAIjD,EAAK,OAAQiD,GAAK,EAAG,CACvC,GAAIjD,EAAK,WAAW,KAAMiD,CAAC,EAAG,CAC5B,MAAME,EAAMgC,EAAcnF,EAAM,KAAMiD,EAAI,CAAC,EAC3C,GAAIE,IAAQ,GAAI,CACd8B,EAAA,EACA,MAAMG,EAAUpF,EAAK,MAAMiD,EAAGE,EAAM,CAAC,EACrCf,GAAUnB,EAAWmE,CAAO,EAC5BnC,EAAIE,EAAM,EACV,QACF,CACF,CAEA,GAAInD,EAAKiD,CAAC,IAAM,IAAK,CACnB,MAAME,EAAMkC,EAAkBrF,EAAMiD,EAAI,CAAC,EACzC,GAAIE,IAAQ,GAAI,CACd8B,EAAA,EACA,MAAMG,EAAUpF,EAAK,MAAMiD,EAAGE,EAAM,CAAC,EACrCf,GAAUnB,EAAWmE,CAAO,EAC5BnC,EAAIE,EACJ,QACF,CACF,CAEA,GAAInD,EAAKiD,CAAC,IAAM,MAAQjD,EAAKiD,EAAI,CAAC,IAAM,KAAM,CAC5CgC,EAAA,EAEA7C,GAAU,QACVa,GAAK,EACL,QACF,CAEA,GAAIjD,EAAK,WAAW,KAAMiD,CAAC,GAAKjD,EAAK,WAAW,KAAMiD,CAAC,EAAG,CACxDgC,EAAA,EACA7C,GAAU4C,EACV/B,GAAK,EACL,QACF,CAEA,GAAIjD,EAAKiD,CAAC,IAAM,KAAM,CACpB,MAAMqC,EAAUC,EAAmBvF,EAAMiD,CAAC,EAC1C,GAAIqC,EAAS,CACXL,EAAA,EACA7C,GAAUkD,EAAQ,KAClBrC,EAAIqC,EAAQ,IAAM,EAClB,QACF,CACF,CAEAP,GAAS/E,EAAKiD,CAAC,CACjB,CAEA,OAAAgC,EAAA,EACO7C,CACT,CAEA,SAASmD,EAAmBvF,EAAckD,EAA2C,CACnF,IAAIrB,EAASqB,EAAQ,EACrB,KAAOrB,EAAS7B,EAAK,QAAU,WAAW,KAAKA,EAAK6B,CAAM,CAAC,GACzDA,GAAU,EAGZ,GAAIA,IAAWqB,EAAQ,EACrB,OAAO,KAGT,MAAMoC,EAAUtF,EAAK,MAAMkD,EAAQ,EAAGrB,CAAM,EAE5C,GAAIyD,KAAW9G,EAAY,CACzB,MAAMgH,EAAMpE,EAA0BpB,EAAM6B,CAAM,EAClD,GAAI,CAAC2D,EACH,MAAO,CAAE,KAAMvE,EAAW,KAAKqE,CAAO,EAAE,EAAG,IAAKzD,CAAA,EAGlD,MAAM4D,EAASlE,EAAYiE,EAAI,OAAO,EAChCnC,EAAM7E,EAAW8G,CAAO,EAC9B,OAAIA,IAAY,SACP,CAAE,KAAM,SAASG,CAAM,UAAW,IAAKD,EAAI,GAAA,EAG7C,CAAE,KAAM,IAAInC,CAAG,IAAIoC,CAAM,KAAKpC,CAAG,IAAK,IAAKmC,EAAI,GAAA,CACxD,CAEA,GAAI/G,EAAc,IAAI6G,CAAO,EAAG,CAC9B,MAAME,EAAMpE,EAA0BpB,EAAM6B,CAAM,EAClD,OAAK2D,EAKE,CAAE,KAAM,SADAjE,EAAYiE,EAAI,OAAO,CACR,UAAW,IAAKA,EAAI,GAAA,EAJzC,CAAE,KAAMvE,EAAW,KAAKqE,CAAO,EAAE,EAAG,IAAKzD,CAAA,CAKpD,CAEA,GAAIyD,IAAY,MAAO,CACrB,MAAME,EAAMpE,EAA0BpB,EAAM6B,CAAM,EAClD,GAAI,CAAC2D,EACH,MAAO,CAAE,KAAMvE,EAAW,OAAO,EAAG,IAAKY,CAAA,EAG3C,MAAM6D,EAAOC,EAAYH,EAAI,QAAQ,MAAM,EACrCI,EAAU3E,EAAWuE,EAAI,QAAQ,MAAM,EAC7C,MAAO,CACL,KAAM,YAAYvE,EAAWyE,CAAI,CAAC,+CAA+CE,CAAO,OACxF,IAAKJ,EAAI,GAAA,CAEb,CAEA,GAAIF,IAAY,OAAQ,CACtB,MAAMnE,EAAQC,EAA0BpB,EAAM6B,CAAM,EACpD,GAAI,CAACV,EACH,MAAO,CAAE,KAAMF,EAAW,QAAQ,EAAG,IAAKY,CAAA,EAG5C,MAAMR,EAASD,EAA0BpB,EAAMmB,EAAM,GAAG,EACxD,GAAI,CAACE,EACH,MAAO,CAAE,KAAMJ,EAAW,QAAQ,EAAG,IAAKE,EAAM,GAAA,EAGlD,MAAMuE,EAAOC,EAAYxE,EAAM,QAAQ,MAAM,EACvCyE,EAAUrE,EAAYF,EAAO,OAAO,EAC1C,MAAO,CACL,KAAM,YAAYJ,EAAWyE,CAAI,CAAC,+CAA+CE,CAAO,OACxF,IAAKvE,EAAO,GAAA,CAEhB,CAEA,MAAO,CAAE,KAAMJ,EAAW,KAAKqE,CAAO,EAAE,EAAG,IAAKzD,CAAA,CAClD,CAGA,SAAST,EAA0BpB,EAAckD,EAK/C,CACA,IAAIrB,EAASqB,EACb,KAAOrB,EAAS7B,EAAK,QAAU,KAAK,KAAKA,EAAK6B,CAAM,CAAC,GACnDA,GAAU,EAGZ,MAAMgE,EAASlF,EAAYX,EAAM6B,CAAM,EACvC,OAAKgE,EAIE,CACL,GAAGA,EACH,aAAchE,EAAS,CAAA,EALhB,IAOX,CAGA,SAASlB,EAAYX,EAAckD,EAIjC,CACA,GAAIlD,EAAKkD,CAAK,IAAM,IAClB,OAAO,KAGT,IAAItB,EAAQ,EACZ,QAASqB,EAAIC,EAAOD,EAAIjD,EAAK,OAAQiD,GAAK,EACxC,GAAIjD,EAAKiD,CAAC,IAAM,KAAOjD,EAAKiD,EAAI,CAAC,IAAM,KACrCrB,GAAS,UACA5B,EAAKiD,CAAC,IAAM,KAAOjD,EAAKiD,EAAI,CAAC,IAAM,OAC5CrB,GAAS,EACLA,IAAU,GACZ,MAAO,CACL,QAAS5B,EAAK,MAAMkD,EAAQ,EAAGD,CAAC,EAChC,MAAAC,EACA,IAAKD,EAAI,CAAA,EAMjB,OAAO,IACT,CAMA,SAASM,EAAcvD,EAAc8F,EAAmC,CACtE,MAAM7D,EAAgB,CAAA,EACtB,IAAI8D,EAAU,GACVnE,EAAQ,EAEZ,QAASqB,EAAI,EAAGA,EAAIjD,EAAK,OAAQiD,GAAK,EAAG,CACvC,MAAM+C,EAAOhG,EAAKiD,CAAC,EAQnB,GANI+C,IAAS,KAAOhG,EAAKiD,EAAI,CAAC,IAAM,KAClCrB,GAAS,EACAoE,IAAS,KAAOhG,EAAKiD,EAAI,CAAC,IAAM,MAAQrB,EAAQ,IACzDA,GAAS,GAGPA,IAAU,GAAKkE,IAAc,KAAOE,IAAS,IAAK,CACpD/D,EAAI,KAAK8D,CAAO,EAChBA,EAAU,GACV,QACF,CAEA,GAAInE,IAAU,GAAKkE,IAAc,QAAUE,IAAS,MAAQhG,EAAKiD,EAAI,CAAC,IAAM,KAAM,CAChFhB,EAAI,KAAK8D,CAAO,EAChBA,EAAU,GACV9C,GAAK,EACL,QACF,CAEA8C,GAAWC,CACb,CAEA,OAAID,EAAQ,QACV9D,EAAI,KAAK8D,CAAO,EAGX9D,CACT,CAEA,SAASkD,EAAcnF,EAAciG,EAAoB/C,EAAuB,CAC9E,QAASD,EAAIC,EAAOD,GAAKjD,EAAK,OAASiG,EAAO,OAAQhD,GAAK,EAAG,CAC5D,GAAIjD,EAAKiD,CAAC,IAAM,KAAM,CACpBA,GAAK,EACL,QACF,CAEA,GAAIjD,EAAK,WAAWiG,EAAQhD,CAAC,EAC3B,OAAOA,CAEX,CAEA,MAAO,EACT,CAEA,SAASoC,EAAkBrF,EAAckD,EAAuB,CAC9D,QAASD,EAAIC,EAAOD,EAAIjD,EAAK,OAAQiD,GAAK,EAAG,CAC3C,GAAIjD,EAAKiD,CAAC,IAAM,KAAM,CACpBA,GAAK,EACL,QACF,CAEA,GAAIjD,EAAKiD,CAAC,IAAM,KAAOjD,EAAKiD,EAAI,CAAC,IAAM,IACrC,OAAOA,CAEX,CAEA,MAAO,EACT,CAGA,SAASiC,EAAgBV,EAAuB,CAC9C,OAAOA,EACJ,QAAQ,kBAAmB,MAAM,EACjC,QAAQ,SAAU,MAAM,EACxB,QAAQ,QAAS,KAAK,EACtB,QAAQ,QAAS,KAAK,CAC3B,CAMA,SAAS/E,EAAaD,EAAsB,CAC1C,OAAOA,EAAK,QAAQ,WAAa0G,GAAWC,EAAYD,CAAM,CAAC,CACjE,CAEA,SAASC,EAAYD,EAAwB,CAC3C,MAAM/F,EAAQ+F,EAAO,MAAM,sCAAsC,EACjE,GAAI,CAAC/F,EACH,MAAO,GAGT,MAAMiG,EAAYjG,EAAM,CAAC,IAAM,IACzBkD,EAAMlD,EAAM,CAAC,EAAE,YAAA,EACfkG,EAAWlG,EAAM,CAAC,GAAK,GAE7B,GAAI,CAACzB,EAAa,IAAI2E,CAAG,EACvB,MAAO,GAGT,GAAI+C,EACF,MAAO,KAAK/C,CAAG,IAGjB,GAAIA,IAAQ,KACV,MAAO,QAGT,MAAMe,EAAQkC,EAAgBD,CAAQ,EAChCE,EAAe5H,EAAqB0E,CAAG,OAAS,IAChDmD,EAA0C,CAAA,EAEhD,SAAW,CAACC,EAAMjC,CAAK,IAAKJ,EAC1B,GAAKmC,EAAa,IAAIE,CAAI,EAI1B,IAAIpD,IAAQ,KAAOoD,IAAS,OAAQ,CAClC,MAAMC,EAAWf,EAAYnB,CAAK,EAClC,GAAI,CAACkC,EACH,SAEFF,EAAe,KAAK,CAAC,OAAQE,CAAQ,CAAC,EACtC,QACF,CAEA,GAAIrD,IAAQ,KAAOoD,IAAS,SAAU,CAChCjC,IAAU,UACZgC,EAAe,KAAK,CAAC,SAAU,QAAQ,CAAC,EAE1C,QACF,CAEA,GAAI,EAAAnD,IAAQ,KAAOoD,IAAS,QAKxBpD,IAAQ,OAASoD,IAAS,WAAaA,IAAS,WAAY,CAC9D,MAAMhB,EAAS,OAAO,SAASjB,EAAO,EAAE,EACpC,OAAO,SAASiB,CAAM,GAAKA,EAAS,GACtCe,EAAe,KAAK,CAACC,EAAM,OAAOhB,CAAM,CAAC,CAAC,EAE5C,QACF,EAGEpC,IAAQ,KAAOmD,EAAe,KAAK,CAAC,CAACC,EAAMjC,CAAK,IAAMiC,IAAS,UAAYjC,IAAU,QAAQ,GAC/FgC,EAAe,KAAK,CAAC,MAAO,qBAAqB,CAAC,EAGpD,MAAMG,EAAcH,EACjB,IAAI,CAAC,CAACC,EAAMjC,CAAK,IAAM,IAAIiC,CAAI,KAAKxF,EAAWuD,CAAK,CAAC,GAAG,EACxD,KAAK,EAAE,EAEV,MAAO,IAAInB,CAAG,GAAGsD,CAAW,GAC9B,CAEA,SAASL,EAAgBM,EAAgD,CACvE,MAAMC,EAAsC,CAAA,EACtCC,EAAQ,0EAEd,IAAI3G,EACJ,MAAQA,EAAQ2G,EAAM,KAAKF,CAAa,KAAO,MAAM,CAEnD,MAAMH,EADUtG,EAAM,CAAC,EACF,YAAA,EACfqE,EAAQrE,EAAM,CAAC,GAAKA,EAAM,CAAC,GAAKA,EAAM,CAAC,GAAK,GAClD0G,EAAW,KAAK,CAACJ,EAAMjC,CAAK,CAAC,CAC/B,CAEA,OAAOqC,CACT,CAEA,SAASlB,EAAYoB,EAAqB,CACxC,MAAMrE,EAAU,OAAOqE,GAAO,EAAE,EAAE,KAAA,EAClC,GAAI,CAACrE,EACH,MAAO,GAGT,MAAMsE,EAAoB,4BAA4B,KAAKtE,CAAO,EAC5DuE,EAAqB,YAAY,KAAKvE,CAAO,EAEnD,OAAOsE,GAAqBC,EAAqBvE,EAAU,EAC7D,CAEA,SAASzB,EAAWuD,EAAuB,CACzC,OAAO,OAAOA,CAAK,EAChB,WAAW,IAAK,OAAO,EACvB,WAAW,IAAK,MAAM,EACtB,WAAW,IAAK,MAAM,EACtB,WAAW,IAAK,QAAQ,EACxB,WAAW,IAAK,OAAO,CAC5B,CAMA,SAAS5E,EAA0BE,EAA2B,CAC5D,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IACvD,OAGF,GAAIA,GAAWA,EAAQ,OAAS,EAAG,CACjC,MAAMoH,EAAgB,IAAI,IAAIpI,GAAyB,CAAA,CAAE,EACzD,UAAWiB,KAAUD,EACnBoH,EAAc,IAAInH,CAAM,EAE1BjB,EAAwB,CAAC,GAAGoI,CAAa,CAC3C,MACEnI,EAA8B,GAC9BD,EAAwB,KAG1B,GAAID,EACF,OAEFA,EAA0B,GAE1B,MAAMsI,EAAa,SAA2B,CAC5CtI,EAA0B,GAC1B,MAAMuI,EAAU,MAAMC,EAAA,EAChB3H,EAAgB4H,EAAA,EACtB,GAAKF,GAAA,MAAAA,EAAS,eAId,GAAI,CACEA,EAAQ,cAEVA,EAAQ,aAAa1H,CAAa,EAEpC,MAAM0H,EAAQ,eAAe1H,CAAa,CAC5C,MAAQ,CAER,CACF,EAEA,GAAI,OAAO,OAAO,uBAA0B,WAAY,CACtD,OAAO,sBAAsB,IAAM,CAC5ByH,EAAA,CACP,CAAC,EACD,MACF,CAEA,WAAW,IAAM,CACVA,EAAA,CACP,EAAG,CAAC,CACN,CAEA,SAASG,GAAoD,CAC3D,GAAIvI,EAA6B,CAC/BA,EAA8B,GAC9BD,EAAwB,KACxB,MACF,CAEA,MAAMY,EAAgBZ,GAAyB,OAC/C,OAAAA,EAAwB,KACjBY,CACT,CAEA,SAAS6H,GAAsD,CAC7D,MAAMC,EAAY,CAChB,sCACA,wBACA,+CACA,2BAAA,EAGF,UAAWC,KAAYD,EAAW,CAChC,MAAME,EAAY,SAAS,cAAcD,CAAQ,EACjD,GAAIC,aAAqB,kBACvB,OAAOA,CAEX,CAEA,OAAO,IACT,CAMA,SAASL,GAA6C,OACpD,OAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IAChD,QAAQ,QAAQ,IAAI,GAGzB9E,EAAA,OAAO,UAAP,MAAAA,EAAgB,eACX,QAAQ,QAAQ,OAAO,OAAO,EAGnC3D,IAIJ,OAAO,QAAU,OAAO,SAAW,CACjC,IAAK,CACH,WAAY,CAAC,CAAC,IAAK,GAAG,CAAC,EACvB,YAAa,CAAC,CAAC,KAAM,IAAI,CAAC,EAC1B,eAAgB,EAAA,EAElB,IAAK,CACH,UAAW,QAAA,CACb,EAGFA,EAAqB,IAAI,QAA4B,CAAC+I,EAASC,IAAW,OACxE,MAAMC,EAAWN,EAAA,EACjB,IAAIO,EAAkD,KAClDC,EAAU,GAEd,MAAMC,EAAsB,IAAY,CAClCF,IAAc,OAChB,aAAaA,CAAS,EACtBA,EAAY,KAEhB,EAEMG,EAAUC,GAA+B,CACzCH,IAGJA,EAAU,GACVC,EAAA,EACAE,EAAA,EACF,EAEMC,EAAO,IAAY,CACvBF,EAAO,IAAML,EAAO,IAAI,MAAM,+BAA+B,CAAC,CAAC,CACjE,EAEMQ,EAAU,IAAY,CAC1BH,EAAO,IAAMN,EAAQ,OAAO,SAAW,IAAI,CAAC,CAC9C,EAMA,GAJAG,EAAY,WAAW,IAAM,CAC3BK,EAAA,CACF,EAAGlJ,CAAuB,EAEtB4I,EAAU,CACZ,IAAItF,EAAA,OAAO,UAAP,MAAAA,EAAgB,eAAgB,CAClC6F,EAAA,EACA,MACF,CACAP,EAAS,iBAAiB,OAAQO,EAAS,CAAE,KAAM,GAAM,EACzDP,EAAS,iBAAiB,QAASM,EAAM,CAAE,KAAM,GAAM,EAEvD,WAAW,IAAM,QACX5F,EAAA,OAAO,UAAP,MAAAA,EAAgB,gBAClB6F,EAAA,CAEJ,EAAG,CAAC,EACJ,MACF,CAEA,MAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,GAAK,iBACZA,EAAO,IAAMrJ,EACbqJ,EAAO,MAAQ,GACfA,EAAO,YAAc,YAIrBA,EAAO,QAAQ,QAAU,eACzBA,EAAO,iBAAiB,OAAQD,EAAS,CAAE,KAAM,GAAM,EACvDC,EAAO,iBAAiB,QAASF,EAAM,CAAE,KAAM,GAAM,EACrD,SAAS,KAAK,OAAOE,CAAM,CAC7B,CAAC,EAAE,MAAM,KACPzJ,EAAqB,KACd,KACR,EAEMA,EACT"}
|
|
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 // When true, single newlines within a paragraph are emitted as <br/> instead\n // of collapsing to whitespace. Blank-line paragraph breaks are unchanged.\n // Useful for non-prose payloads (e.g. literal sample I/O) that are passed\n // through the renderer but rely on line breaks for structure.\n preserveNewlines?: boolean\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, { preserveNewlines: options.preserveNewlines === true }).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\ntype BlockContext = { preserveNewlines: boolean }\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, context: BlockContext): 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), context))\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), context))\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, context).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), context))\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), context))\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), context))\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, context: BlockContext): 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), context)\n cursor = match.index + match[0].length\n }\n\n pushParagraph(out, chunk.slice(cursor), context)\n return out\n}\n\n// This renderer uses a blank line as a paragraph separator.\nfunction pushParagraph(output: string[], paragraphRaw: string, context: BlockContext): 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 inline = context.preserveNewlines\n ? trimmed\n .split('\\n')\n .map(line => parseInline(line))\n .join('<br/>')\n : parseInline(trimmed.replace(/\\n/g, ' '))\n\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","context","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","line","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.mjs
CHANGED
|
@@ -41,20 +41,20 @@ const v = {
|
|
|
41
41
|
"tr",
|
|
42
42
|
"u",
|
|
43
43
|
"ul"
|
|
44
|
-
]),
|
|
44
|
+
]), I = {
|
|
45
45
|
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
46
46
|
td: /* @__PURE__ */ new Set(["colspan", "rowspan"])
|
|
47
47
|
};
|
|
48
48
|
let $ = null, y = !1, w = null, M = !1;
|
|
49
|
-
const
|
|
49
|
+
const j = "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js", z = 8e3;
|
|
50
50
|
function K(e, r = {}) {
|
|
51
51
|
const t = String(e ?? "").replace(/\r\n/g, `
|
|
52
|
-
`), n = J(t).join(""),
|
|
52
|
+
`), n = J(t, { preserveNewlines: r.preserveNewlines === !0 }).join(""), s = x(n);
|
|
53
53
|
if (r.typeset) {
|
|
54
|
-
const
|
|
55
|
-
Z(
|
|
54
|
+
const i = k(r.typesetTarget);
|
|
55
|
+
Z(i);
|
|
56
56
|
}
|
|
57
|
-
return
|
|
57
|
+
return s;
|
|
58
58
|
}
|
|
59
59
|
function k(e) {
|
|
60
60
|
if (!e)
|
|
@@ -62,134 +62,135 @@ function k(e) {
|
|
|
62
62
|
const t = (Array.isArray(e) ? e : [e]).filter((n) => !!n);
|
|
63
63
|
return t.length > 0 ? t : void 0;
|
|
64
64
|
}
|
|
65
|
-
function J(e) {
|
|
66
|
-
const
|
|
67
|
-
let
|
|
68
|
-
for (; (i =
|
|
69
|
-
if (i.index >
|
|
70
|
-
const
|
|
71
|
-
if (!
|
|
72
|
-
|
|
65
|
+
function J(e, r) {
|
|
66
|
+
const t = [], n = /\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;
|
|
67
|
+
let s = 0, i;
|
|
68
|
+
for (; (i = n.exec(e)) !== null; ) {
|
|
69
|
+
if (i.index > s && t.push(...b(e.slice(s, i.index), r)), i[1]) {
|
|
70
|
+
const o = i[1], l = H(e, i.index, o);
|
|
71
|
+
if (!l) {
|
|
72
|
+
t.push(...b(e.slice(i.index), r));
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
|
-
if (
|
|
76
|
-
let
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
} else if (
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
} else if (
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
} else if (
|
|
88
|
-
const
|
|
89
|
-
|
|
75
|
+
if (o === "tabular") {
|
|
76
|
+
let c = "", a = l.innerStart;
|
|
77
|
+
const u = P(e, a);
|
|
78
|
+
u && (c = u.content, a = u.end);
|
|
79
|
+
const d = e.slice(a, l.innerEnd);
|
|
80
|
+
t.push(N(c, d));
|
|
81
|
+
} else if (o === "itemize" || o === "enumerate") {
|
|
82
|
+
const c = e.slice(l.innerStart, l.innerEnd);
|
|
83
|
+
t.push(W(o, c));
|
|
84
|
+
} else if (o === "lstlisting") {
|
|
85
|
+
const c = e.slice(l.innerStart, l.innerEnd).replace(/^\n+|\n+$/g, "");
|
|
86
|
+
t.push(`<pre><code>${f(c)}</code></pre>`);
|
|
87
|
+
} else if (o === "center") {
|
|
88
|
+
const c = e.slice(l.innerStart, l.innerEnd);
|
|
89
|
+
t.push(`<div>${J(c, r).join("")}</div>`);
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
s = l.end, n.lastIndex = s;
|
|
92
92
|
continue;
|
|
93
93
|
}
|
|
94
94
|
if (e.startsWith("\\epigraph{", i.index)) {
|
|
95
|
-
const
|
|
96
|
-
if (!
|
|
97
|
-
|
|
95
|
+
const o = g(e, i.index + 9);
|
|
96
|
+
if (!o) {
|
|
97
|
+
t.push(...b(e.slice(i.index, i.index + 9), r)), s = i.index + 9, n.lastIndex = s;
|
|
98
98
|
continue;
|
|
99
99
|
}
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
102
|
-
|
|
100
|
+
const l = g(e, o.end);
|
|
101
|
+
if (!l) {
|
|
102
|
+
t.push(...b(e.slice(i.index, o.end), r)), s = o.end, n.lastIndex = s;
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
105
|
+
const c = h(o.content.trim()), a = h(l.content.trim());
|
|
106
|
+
t.push(`<blockquote><p>${c}</p><footer>${a}</footer></blockquote>`), s = l.end, n.lastIndex = s;
|
|
107
107
|
continue;
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
s = i.index + i[0].length;
|
|
110
110
|
}
|
|
111
|
-
return
|
|
111
|
+
return s < e.length && t.push(...b(e.slice(s), r)), t;
|
|
112
112
|
}
|
|
113
|
-
function
|
|
114
|
-
const n = `\\begin{${t}}`,
|
|
115
|
-
let
|
|
113
|
+
function H(e, r, t) {
|
|
114
|
+
const n = `\\begin{${t}}`, s = `\\end{${t}}`;
|
|
115
|
+
let i = 1, o = r + n.length;
|
|
116
116
|
for (; o <= e.length; ) {
|
|
117
|
-
const l = e.indexOf(n, o), c = e.indexOf(
|
|
117
|
+
const l = e.indexOf(n, o), c = e.indexOf(s, o);
|
|
118
118
|
if (c === -1)
|
|
119
119
|
return null;
|
|
120
120
|
if (l !== -1 && l < c) {
|
|
121
|
-
|
|
121
|
+
i += 1, o = l + n.length;
|
|
122
122
|
continue;
|
|
123
123
|
}
|
|
124
|
-
if (
|
|
124
|
+
if (i -= 1, i === 0)
|
|
125
125
|
return {
|
|
126
126
|
innerStart: r + n.length,
|
|
127
127
|
innerEnd: c,
|
|
128
|
-
end: c +
|
|
128
|
+
end: c + s.length
|
|
129
129
|
};
|
|
130
|
-
o = c +
|
|
130
|
+
o = c + s.length;
|
|
131
131
|
}
|
|
132
132
|
return null;
|
|
133
133
|
}
|
|
134
|
-
function b(e) {
|
|
135
|
-
const
|
|
136
|
-
let
|
|
137
|
-
for (; (i =
|
|
138
|
-
E(
|
|
139
|
-
return E(
|
|
134
|
+
function b(e, r) {
|
|
135
|
+
const t = [], n = /\n\s*\n/g;
|
|
136
|
+
let s = 0, i;
|
|
137
|
+
for (; (i = n.exec(e)) !== null; )
|
|
138
|
+
E(t, e.slice(s, i.index), r), s = i.index + i[0].length;
|
|
139
|
+
return E(t, e.slice(s), r), t;
|
|
140
140
|
}
|
|
141
|
-
function E(e, r) {
|
|
141
|
+
function E(e, r, t) {
|
|
142
142
|
var l, c;
|
|
143
|
-
const
|
|
143
|
+
const n = ((l = r.match(/^\s*/)) == null ? void 0 : l[0].length) ?? 0, s = ((c = r.match(/\s*$/)) == null ? void 0 : c[0].length) ?? 0, i = r.slice(n, r.length - s);
|
|
144
144
|
if (!i)
|
|
145
145
|
return;
|
|
146
|
-
const
|
|
146
|
+
const o = t.preserveNewlines ? i.split(`
|
|
147
|
+
`).map((a) => h(a)).join("<br/>") : h(i.replace(/\n/g, " "));
|
|
147
148
|
o.trim() && e.push(`<p>${o}</p>`);
|
|
148
149
|
}
|
|
149
|
-
function
|
|
150
|
+
function W(e, r) {
|
|
150
151
|
const t = /\\item\b/g, n = [];
|
|
151
|
-
let
|
|
152
|
-
for (; (
|
|
153
|
-
n.push(
|
|
152
|
+
let s;
|
|
153
|
+
for (; (s = t.exec(r)) !== null; )
|
|
154
|
+
n.push(s.index);
|
|
154
155
|
if (n.length === 0)
|
|
155
156
|
return "";
|
|
156
|
-
const
|
|
157
|
+
const i = [];
|
|
157
158
|
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,
|
|
159
|
-
if (!
|
|
159
|
+
const c = n[l] + 5, a = l + 1 < n.length ? n[l + 1] : r.length, d = r.slice(c, a).trim();
|
|
160
|
+
if (!d)
|
|
160
161
|
continue;
|
|
161
|
-
const
|
|
162
|
-
|
|
162
|
+
const m = d.replace(/\n/g, " "), p = h(m);
|
|
163
|
+
i.push(`<li>${p}</li>`);
|
|
163
164
|
}
|
|
164
165
|
const o = e === "enumerate" ? "ol" : "ul";
|
|
165
|
-
return `<${o}>${
|
|
166
|
+
return `<${o}>${i.join("")}</${o}>`;
|
|
166
167
|
}
|
|
167
|
-
function
|
|
168
|
-
const t = L(r, "\\\\"), n = [],
|
|
169
|
-
let
|
|
168
|
+
function N(e, r) {
|
|
169
|
+
const t = L(r, "\\\\"), n = [], s = /* @__PURE__ */ new Map();
|
|
170
|
+
let i = !0;
|
|
170
171
|
for (const l of t) {
|
|
171
172
|
const c = l.replace(/\\hline/g, "").replace(/\\cline\{[^}]*\}/g, "").trim();
|
|
172
173
|
if (!c)
|
|
173
174
|
continue;
|
|
174
|
-
|
|
175
|
+
i || R(s), i = !1;
|
|
175
176
|
const a = L(c, "&");
|
|
176
177
|
let u = 0;
|
|
177
|
-
const
|
|
178
|
-
for (const
|
|
179
|
-
const
|
|
180
|
-
for (;
|
|
178
|
+
const d = [];
|
|
179
|
+
for (const m of a) {
|
|
180
|
+
const p = q(m);
|
|
181
|
+
for (; s.has(u); )
|
|
181
182
|
u += 1;
|
|
182
183
|
let T = "";
|
|
183
|
-
if (
|
|
184
|
-
T += ` rowspan="${
|
|
185
|
-
for (let S = 0; S <
|
|
186
|
-
|
|
184
|
+
if (p.colspan > 1 && (T += ` colspan="${p.colspan}"`), p.rowspan > 1) {
|
|
185
|
+
T += ` rowspan="${p.rowspan}"`;
|
|
186
|
+
for (let S = 0; S < p.colspan; S += 1)
|
|
187
|
+
s.set(u + S, p.rowspan - 1);
|
|
187
188
|
}
|
|
188
|
-
|
|
189
|
+
d.push(`<td${T}>${p.html}</td>`), u += p.colspan;
|
|
189
190
|
}
|
|
190
|
-
n.push(`<tr>${
|
|
191
|
+
n.push(`<tr>${d.join("")}</tr>`);
|
|
191
192
|
}
|
|
192
|
-
return `<table${e ? ` data-spec="${
|
|
193
|
+
return `<table${e ? ` data-spec="${f(e)}"` : ""}><tbody>${n.join("")}</tbody></table>`;
|
|
193
194
|
}
|
|
194
195
|
function R(e) {
|
|
195
196
|
for (const [r, t] of e.entries()) {
|
|
@@ -198,62 +199,62 @@ function R(e) {
|
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
function q(e) {
|
|
201
|
-
let r = e.trim(), t = 1, n = 1,
|
|
202
|
-
for (;
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
202
|
+
let r = e.trim(), t = 1, n = 1, s = !0;
|
|
203
|
+
for (; s; ) {
|
|
204
|
+
s = !1;
|
|
205
|
+
const i = r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);
|
|
206
|
+
i && (t *= Number.parseInt(i[1], 10), r = i[2].trim(), s = !0);
|
|
206
207
|
const o = r.match(/^\\multirow\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);
|
|
207
|
-
o && (n *= Number.parseInt(o[1], 10), r = o[2].trim(),
|
|
208
|
+
o && (n *= Number.parseInt(o[1], 10), r = o[2].trim(), s = !0);
|
|
208
209
|
}
|
|
209
210
|
return {
|
|
210
|
-
html:
|
|
211
|
+
html: h(r),
|
|
211
212
|
colspan: t,
|
|
212
213
|
rowspan: n
|
|
213
214
|
};
|
|
214
215
|
}
|
|
215
|
-
function
|
|
216
|
+
function h(e) {
|
|
216
217
|
let r = "", t = "";
|
|
217
|
-
const n =
|
|
218
|
-
t && (r +=
|
|
218
|
+
const n = f('"'), s = () => {
|
|
219
|
+
t && (r += f(B(t)), t = "");
|
|
219
220
|
};
|
|
220
|
-
for (let
|
|
221
|
-
if (e.startsWith("$$",
|
|
222
|
-
const o = D(e, "$$",
|
|
221
|
+
for (let i = 0; i < e.length; i += 1) {
|
|
222
|
+
if (e.startsWith("$$", i)) {
|
|
223
|
+
const o = D(e, "$$", i + 2);
|
|
223
224
|
if (o !== -1) {
|
|
224
|
-
|
|
225
|
-
const l = e.slice(
|
|
226
|
-
r +=
|
|
225
|
+
s();
|
|
226
|
+
const l = e.slice(i, o + 2);
|
|
227
|
+
r += f(l), i = o + 1;
|
|
227
228
|
continue;
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
|
-
if (e[
|
|
231
|
-
const o = G(e,
|
|
231
|
+
if (e[i] === "$") {
|
|
232
|
+
const o = G(e, i + 1);
|
|
232
233
|
if (o !== -1) {
|
|
233
|
-
|
|
234
|
-
const l = e.slice(
|
|
235
|
-
r +=
|
|
234
|
+
s();
|
|
235
|
+
const l = e.slice(i, o + 1);
|
|
236
|
+
r += f(l), i = o;
|
|
236
237
|
continue;
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
|
-
if (e[
|
|
240
|
-
|
|
240
|
+
if (e[i] === "\\" && e[i + 1] === "\\") {
|
|
241
|
+
s(), r += "<br/>", i += 1;
|
|
241
242
|
continue;
|
|
242
243
|
}
|
|
243
|
-
if (e.startsWith("``",
|
|
244
|
-
|
|
244
|
+
if (e.startsWith("``", i) || e.startsWith("''", i)) {
|
|
245
|
+
s(), r += n, i += 1;
|
|
245
246
|
continue;
|
|
246
247
|
}
|
|
247
|
-
if (e[
|
|
248
|
-
const o = O(e,
|
|
248
|
+
if (e[i] === "\\") {
|
|
249
|
+
const o = O(e, i);
|
|
249
250
|
if (o) {
|
|
250
|
-
|
|
251
|
+
s(), r += o.html, i = o.end - 1;
|
|
251
252
|
continue;
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
|
-
t += e[
|
|
255
|
+
t += e[i];
|
|
255
256
|
}
|
|
256
|
-
return
|
|
257
|
+
return s(), r;
|
|
257
258
|
}
|
|
258
259
|
function O(e, r) {
|
|
259
260
|
let t = r + 1;
|
|
@@ -263,40 +264,40 @@ function O(e, r) {
|
|
|
263
264
|
return null;
|
|
264
265
|
const n = e.slice(r + 1, t);
|
|
265
266
|
if (n in v) {
|
|
266
|
-
const
|
|
267
|
-
if (!
|
|
268
|
-
return { html:
|
|
269
|
-
const
|
|
270
|
-
return n === "textsc" ? { html: `<span>${
|
|
267
|
+
const s = g(e, t);
|
|
268
|
+
if (!s)
|
|
269
|
+
return { html: f(`\\${n}`), end: t };
|
|
270
|
+
const i = h(s.content), o = v[n];
|
|
271
|
+
return n === "textsc" ? { html: `<span>${i}</span>`, end: s.end } : { html: `<${o}>${i}</${o}>`, end: s.end };
|
|
271
272
|
}
|
|
272
273
|
if (_.has(n)) {
|
|
273
|
-
const
|
|
274
|
-
return
|
|
274
|
+
const s = g(e, t);
|
|
275
|
+
return s ? { html: `<span>${h(s.content)}</span>`, end: s.end } : { html: f(`\\${n}`), end: t };
|
|
275
276
|
}
|
|
276
277
|
if (n === "url") {
|
|
277
|
-
const
|
|
278
|
-
if (!
|
|
279
|
-
return { html:
|
|
280
|
-
const
|
|
278
|
+
const s = g(e, t);
|
|
279
|
+
if (!s)
|
|
280
|
+
return { html: f("\\url"), end: t };
|
|
281
|
+
const i = A(s.content.trim()), o = f(s.content.trim());
|
|
281
282
|
return {
|
|
282
|
-
html: `<a href="${
|
|
283
|
-
end:
|
|
283
|
+
html: `<a href="${f(i)}" target="_blank" rel="noopener noreferrer">${o}</a>`,
|
|
284
|
+
end: s.end
|
|
284
285
|
};
|
|
285
286
|
}
|
|
286
287
|
if (n === "href") {
|
|
287
|
-
const
|
|
288
|
-
if (!i)
|
|
289
|
-
return { html: d("\\href"), end: t };
|
|
290
|
-
const s = g(e, i.end);
|
|
288
|
+
const s = g(e, t);
|
|
291
289
|
if (!s)
|
|
292
|
-
return { html:
|
|
293
|
-
const
|
|
290
|
+
return { html: f("\\href"), end: t };
|
|
291
|
+
const i = g(e, s.end);
|
|
292
|
+
if (!i)
|
|
293
|
+
return { html: f("\\href"), end: s.end };
|
|
294
|
+
const o = A(s.content.trim()), l = h(i.content);
|
|
294
295
|
return {
|
|
295
|
-
html: `<a href="${
|
|
296
|
-
end:
|
|
296
|
+
html: `<a href="${f(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,
|
|
297
|
+
end: i.end
|
|
297
298
|
};
|
|
298
299
|
}
|
|
299
|
-
return { html:
|
|
300
|
+
return { html: f(`\\${n}`), end: t };
|
|
300
301
|
}
|
|
301
302
|
function g(e, r) {
|
|
302
303
|
let t = r;
|
|
@@ -325,15 +326,15 @@ function P(e, r) {
|
|
|
325
326
|
}
|
|
326
327
|
function L(e, r) {
|
|
327
328
|
const t = [];
|
|
328
|
-
let n = "",
|
|
329
|
-
for (let
|
|
330
|
-
const o = e[
|
|
331
|
-
if (o === "{" && e[
|
|
329
|
+
let n = "", s = 0;
|
|
330
|
+
for (let i = 0; i < e.length; i += 1) {
|
|
331
|
+
const o = e[i];
|
|
332
|
+
if (o === "{" && e[i - 1] !== "\\" ? s += 1 : o === "}" && e[i - 1] !== "\\" && s > 0 && (s -= 1), s === 0 && r === "&" && o === "&") {
|
|
332
333
|
t.push(n), n = "";
|
|
333
334
|
continue;
|
|
334
335
|
}
|
|
335
|
-
if (
|
|
336
|
-
t.push(n), n = "",
|
|
336
|
+
if (s === 0 && r === "\\\\" && o === "\\" && e[i + 1] === "\\") {
|
|
337
|
+
t.push(n), n = "", i += 1;
|
|
337
338
|
continue;
|
|
338
339
|
}
|
|
339
340
|
n += o;
|
|
@@ -362,31 +363,31 @@ function G(e, r) {
|
|
|
362
363
|
}
|
|
363
364
|
return -1;
|
|
364
365
|
}
|
|
365
|
-
function
|
|
366
|
+
function B(e) {
|
|
366
367
|
return e.replace(/<<([\s\S]*?)>>/g, "«$1»").replace(/`(.)'/g, "'$1'").replace(/~---/g, " — ").replace(/"---/g, " — ");
|
|
367
368
|
}
|
|
368
|
-
function
|
|
369
|
+
function x(e) {
|
|
369
370
|
return e.replace(/<[^>]*>/g, (r) => F(r));
|
|
370
371
|
}
|
|
371
372
|
function F(e) {
|
|
372
373
|
const r = e.match(/^<\s*(\/?)\s*([a-zA-Z0-9]+)([^>]*)>$/);
|
|
373
374
|
if (!r)
|
|
374
375
|
return "";
|
|
375
|
-
const t = r[1] === "/", n = r[2].toLowerCase(),
|
|
376
|
+
const t = r[1] === "/", n = r[2].toLowerCase(), s = r[3] ?? "";
|
|
376
377
|
if (!C.has(n))
|
|
377
378
|
return "";
|
|
378
379
|
if (t)
|
|
379
380
|
return `</${n}>`;
|
|
380
381
|
if (n === "br")
|
|
381
382
|
return "<br/>";
|
|
382
|
-
const
|
|
383
|
-
for (const [a, u] of
|
|
383
|
+
const i = U(s), o = I[n] ?? /* @__PURE__ */ new Set(), l = [];
|
|
384
|
+
for (const [a, u] of i)
|
|
384
385
|
if (o.has(a)) {
|
|
385
386
|
if (n === "a" && a === "href") {
|
|
386
|
-
const
|
|
387
|
-
if (!
|
|
387
|
+
const d = A(u);
|
|
388
|
+
if (!d)
|
|
388
389
|
continue;
|
|
389
|
-
l.push(["href",
|
|
390
|
+
l.push(["href", d]);
|
|
390
391
|
continue;
|
|
391
392
|
}
|
|
392
393
|
if (n === "a" && a === "target") {
|
|
@@ -394,21 +395,21 @@ function F(e) {
|
|
|
394
395
|
continue;
|
|
395
396
|
}
|
|
396
397
|
if (!(n === "a" && a === "rel") && n === "td" && (a === "colspan" || a === "rowspan")) {
|
|
397
|
-
const
|
|
398
|
-
Number.isFinite(
|
|
398
|
+
const d = Number.parseInt(u, 10);
|
|
399
|
+
Number.isFinite(d) && d > 0 && l.push([a, String(d)]);
|
|
399
400
|
continue;
|
|
400
401
|
}
|
|
401
402
|
}
|
|
402
403
|
n === "a" && l.some(([a, u]) => a === "target" && u === "_blank") && l.push(["rel", "noopener noreferrer"]);
|
|
403
|
-
const c = l.map(([a, u]) => ` ${a}="${
|
|
404
|
+
const c = l.map(([a, u]) => ` ${a}="${f(u)}"`).join("");
|
|
404
405
|
return `<${n}${c}>`;
|
|
405
406
|
}
|
|
406
407
|
function U(e) {
|
|
407
408
|
const r = [], t = /([a-zA-Z_:][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
408
409
|
let n;
|
|
409
410
|
for (; (n = t.exec(e)) !== null; ) {
|
|
410
|
-
const
|
|
411
|
-
r.push([
|
|
411
|
+
const i = n[1].toLowerCase(), o = n[2] ?? n[3] ?? n[4] ?? "";
|
|
412
|
+
r.push([i, o]);
|
|
412
413
|
}
|
|
413
414
|
return r;
|
|
414
415
|
}
|
|
@@ -419,7 +420,7 @@ function A(e) {
|
|
|
419
420
|
const t = /^(https?:\/\/|mailto:|#)/i.test(r), n = /^\/(?!\/)/.test(r);
|
|
420
421
|
return t || n ? r : "";
|
|
421
422
|
}
|
|
422
|
-
function
|
|
423
|
+
function f(e) {
|
|
423
424
|
return String(e).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
424
425
|
}
|
|
425
426
|
function Z(e) {
|
|
@@ -488,33 +489,33 @@ function Q() {
|
|
|
488
489
|
fontCache: "global"
|
|
489
490
|
}
|
|
490
491
|
}, $ = new Promise((r, t) => {
|
|
491
|
-
var
|
|
492
|
+
var d;
|
|
492
493
|
const n = Y();
|
|
493
|
-
let
|
|
494
|
+
let s = null, i = !1;
|
|
494
495
|
const o = () => {
|
|
495
|
-
|
|
496
|
-
}, l = (
|
|
497
|
-
|
|
496
|
+
s !== null && (clearTimeout(s), s = null);
|
|
497
|
+
}, l = (m) => {
|
|
498
|
+
i || (i = !0, o(), m());
|
|
498
499
|
}, c = () => {
|
|
499
500
|
l(() => t(new Error("Failed to load MathJax script")));
|
|
500
501
|
}, a = () => {
|
|
501
502
|
l(() => r(window.MathJax ?? null));
|
|
502
503
|
};
|
|
503
|
-
if (
|
|
504
|
+
if (s = setTimeout(() => {
|
|
504
505
|
c();
|
|
505
|
-
},
|
|
506
|
-
if ((
|
|
506
|
+
}, z), n) {
|
|
507
|
+
if ((d = window.MathJax) != null && d.typesetPromise) {
|
|
507
508
|
a();
|
|
508
509
|
return;
|
|
509
510
|
}
|
|
510
511
|
n.addEventListener("load", a, { once: !0 }), n.addEventListener("error", c, { once: !0 }), setTimeout(() => {
|
|
511
|
-
var
|
|
512
|
-
(
|
|
512
|
+
var m;
|
|
513
|
+
(m = window.MathJax) != null && m.typesetPromise && a();
|
|
513
514
|
}, 0);
|
|
514
515
|
return;
|
|
515
516
|
}
|
|
516
517
|
const u = document.createElement("script");
|
|
517
|
-
u.id = "MathJax-script", u.src =
|
|
518
|
+
u.id = "MathJax-script", u.src = j, 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
519
|
}).catch(() => ($ = null, null)), $);
|
|
519
520
|
}
|
|
520
521
|
export {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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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"}
|
|
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 // When true, single newlines within a paragraph are emitted as <br/> instead\n // of collapsing to whitespace. Blank-line paragraph breaks are unchanged.\n // Useful for non-prose payloads (e.g. literal sample I/O) that are passed\n // through the renderer but rely on line breaks for structure.\n preserveNewlines?: boolean\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, { preserveNewlines: options.preserveNewlines === true }).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\ntype BlockContext = { preserveNewlines: boolean }\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, context: BlockContext): 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), context))\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), context))\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, context).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), context))\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), context))\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), context))\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, context: BlockContext): 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), context)\n cursor = match.index + match[0].length\n }\n\n pushParagraph(out, chunk.slice(cursor), context)\n return out\n}\n\n// This renderer uses a blank line as a paragraph separator.\nfunction pushParagraph(output: string[], paragraphRaw: string, context: BlockContext): 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 inline = context.preserveNewlines\n ? trimmed\n .split('\\n')\n .map(line => parseInline(line))\n .join('<br/>')\n : parseInline(trimmed.replace(/\\n/g, ' '))\n\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","context","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","line","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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"}
|