@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 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 v={bf:"strong",textbf:"strong",it:"em",textit:"em",t:"code",tt:"code",texttt:"code",emph:"u",underline:"u",sout:"s",textsc:"span"},_=new Set(["tiny","scriptsize","small","normalsize","large","Large","LARGE","huge","Huge"]),C=new Set(["a","blockquote","br","code","div","em","footer","li","ol","p","pre","s","span","strong","table","tbody","td","tr","u","ul"]),z={a:new Set(["href","target","rel"]),td:new Set(["colspan","rowspan"])};let $=null,y=!1,w=null,M=!1;const I="https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js",j=8e3;function k(e,r={}){const t=String(e??"").replace(/\r\n/g,`
2
- `),n=J(t).join(""),i=F(n);if(r.typeset){const s=x(r.typesetTarget);X(s)}return i}function x(e){if(!e)return;const t=(Array.isArray(e)?e:[e]).filter(n=>!!n);return t.length>0?t:void 0}function J(e){const r=[],t=/\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;let n=0,i;for(;(i=t.exec(e))!==null;){if(i.index>n&&r.push(...b(e.slice(n,i.index))),i[1]){const s=i[1],o=H(e,i.index,s);if(!o){r.push(...b(e.slice(i.index)));break}if(s==="tabular"){let l="",c=o.innerStart;const a=P(e,c);a&&(l=a.content,c=a.end);const u=e.slice(c,o.innerEnd);r.push(R(l,u))}else if(s==="itemize"||s==="enumerate"){const l=e.slice(o.innerStart,o.innerEnd);r.push(W(s,l))}else if(s==="lstlisting"){const l=e.slice(o.innerStart,o.innerEnd).replace(/^\n+|\n+$/g,"");r.push(`<pre><code>${d(l)}</code></pre>`)}else if(s==="center"){const l=e.slice(o.innerStart,o.innerEnd);r.push(`<div>${J(l).join("")}</div>`)}n=o.end,t.lastIndex=n;continue}if(e.startsWith("\\epigraph{",i.index)){const s=g(e,i.index+9);if(!s){r.push(...b(e.slice(i.index,i.index+9))),n=i.index+9,t.lastIndex=n;continue}const o=g(e,s.end);if(!o){r.push(...b(e.slice(i.index,s.end))),n=s.end,t.lastIndex=n;continue}const l=m(s.content.trim()),c=m(o.content.trim());r.push(`<blockquote><p>${l}</p><footer>${c}</footer></blockquote>`),n=o.end,t.lastIndex=n;continue}n=i.index+i[0].length}return n<e.length&&r.push(...b(e.slice(n))),r}function H(e,r,t){const n=`\\begin{${t}}`,i=`\\end{${t}}`;let s=1,o=r+n.length;for(;o<=e.length;){const l=e.indexOf(n,o),c=e.indexOf(i,o);if(c===-1)return null;if(l!==-1&&l<c){s+=1,o=l+n.length;continue}if(s-=1,s===0)return{innerStart:r+n.length,innerEnd:c,end:c+i.length};o=c+i.length}return null}function b(e){const r=[],t=/\n\s*\n/g;let n=0,i;for(;(i=t.exec(e))!==null;)E(r,e.slice(n,i.index)),n=i.index+i[0].length;return E(r,e.slice(n)),r}function E(e,r){var l,c;const t=((l=r.match(/^\s*/))==null?void 0:l[0].length)??0,n=((c=r.match(/\s*$/))==null?void 0:c[0].length)??0,i=r.slice(t,r.length-n);if(!i)return;const s=i.replace(/\n/g," "),o=m(s);o.trim()&&e.push(`<p>${o}</p>`)}function W(e,r){const t=/\\item\b/g,n=[];let i;for(;(i=t.exec(r))!==null;)n.push(i.index);if(n.length===0)return"";const s=[];for(let l=0;l<n.length;l+=1){const c=n[l]+5,a=l+1<n.length?n[l+1]:r.length,f=r.slice(c,a).trim();if(!f)continue;const p=f.replace(/\n/g," "),h=m(p);s.push(`<li>${h}</li>`)}const o=e==="enumerate"?"ol":"ul";return`<${o}>${s.join("")}</${o}>`}function R(e,r){const t=L(r,"\\\\"),n=[],i=new Map;let s=!0;for(const l of t){const c=l.replace(/\\hline/g,"").replace(/\\cline\{[^}]*\}/g,"").trim();if(!c)continue;s||q(i),s=!1;const a=L(c,"&");let u=0;const f=[];for(const p of a){const h=O(p);for(;i.has(u);)u+=1;let S="";if(h.colspan>1&&(S+=` colspan="${h.colspan}"`),h.rowspan>1){S+=` rowspan="${h.rowspan}"`;for(let T=0;T<h.colspan;T+=1)i.set(u+T,h.rowspan-1)}f.push(`<td${S}>${h.html}</td>`),u+=h.colspan}n.push(`<tr>${f.join("")}</tr>`)}return`<table${e?` data-spec="${d(e)}"`:""}><tbody>${n.join("")}</tbody></table>`}function q(e){for(const[r,t]of e.entries()){const n=t-1;n<=0?e.delete(r):e.set(r,n)}}function O(e){let r=e.trim(),t=1,n=1,i=!0;for(;i;){i=!1;const s=r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);s&&(t*=Number.parseInt(s[1],10),r=s[2].trim(),i=!0);const o=r.match(/^\\multirow\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);o&&(n*=Number.parseInt(o[1],10),r=o[2].trim(),i=!0)}return{html:m(r),colspan:t,rowspan:n}}function m(e){let r="",t="";const n=d('"'),i=()=>{t&&(r+=d(B(t)),t="")};for(let s=0;s<e.length;s+=1){if(e.startsWith("$$",s)){const o=G(e,"$$",s+2);if(o!==-1){i();const l=e.slice(s,o+2);r+=d(l),s=o+1;continue}}if(e[s]==="$"){const o=N(e,s+1);if(o!==-1){i();const l=e.slice(s,o+1);r+=d(l),s=o;continue}}if(e[s]==="\\"&&e[s+1]==="\\"){i(),r+="<br/>",s+=1;continue}if(e.startsWith("``",s)||e.startsWith("''",s)){i(),r+=n,s+=1;continue}if(e[s]==="\\"){const o=D(e,s);if(o){i(),r+=o.html,s=o.end-1;continue}}t+=e[s]}return i(),r}function D(e,r){let t=r+1;for(;t<e.length&&/[A-Za-z]/.test(e[t]);)t+=1;if(t===r+1)return null;const n=e.slice(r+1,t);if(n in v){const i=g(e,t);if(!i)return{html:d(`\\${n}`),end:t};const s=m(i.content),o=v[n];return n==="textsc"?{html:`<span>${s}</span>`,end:i.end}:{html:`<${o}>${s}</${o}>`,end:i.end}}if(_.has(n)){const i=g(e,t);return i?{html:`<span>${m(i.content)}</span>`,end:i.end}:{html:d(`\\${n}`),end:t}}if(n==="url"){const i=g(e,t);if(!i)return{html:d("\\url"),end:t};const s=A(i.content.trim()),o=d(i.content.trim());return{html:`<a href="${d(s)}" target="_blank" rel="noopener noreferrer">${o}</a>`,end:i.end}}if(n==="href"){const i=g(e,t);if(!i)return{html:d("\\href"),end:t};const s=g(e,i.end);if(!s)return{html:d("\\href"),end:i.end};const o=A(i.content.trim()),l=m(s.content);return{html:`<a href="${d(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,end:s.end}}return{html:d(`\\${n}`),end:t}}function g(e,r){let t=r;for(;t<e.length&&/\s/.test(e[t]);)t+=1;const n=P(e,t);return n?{...n,contentStart:t+1}:null}function P(e,r){if(e[r]!=="{")return null;let t=0;for(let n=r;n<e.length;n+=1)if(e[n]==="{"&&e[n-1]!=="\\")t+=1;else if(e[n]==="}"&&e[n-1]!=="\\"&&(t-=1,t===0))return{content:e.slice(r+1,n),start:r,end:n+1};return null}function L(e,r){const t=[];let n="",i=0;for(let s=0;s<e.length;s+=1){const o=e[s];if(o==="{"&&e[s-1]!=="\\"?i+=1:o==="}"&&e[s-1]!=="\\"&&i>0&&(i-=1),i===0&&r==="&"&&o==="&"){t.push(n),n="";continue}if(i===0&&r==="\\\\"&&o==="\\"&&e[s+1]==="\\"){t.push(n),n="",s+=1;continue}n+=o}return n.trim()&&t.push(n),t}function G(e,r,t){for(let n=t;n<=e.length-r.length;n+=1){if(e[n]==="\\"){n+=1;continue}if(e.startsWith(r,n))return n}return-1}function N(e,r){for(let t=r;t<e.length;t+=1){if(e[t]==="\\"){t+=1;continue}if(e[t]==="$"&&e[t+1]!=="$")return t}return-1}function B(e){return e.replace(/<<([\s\S]*?)>>/g,"«$1»").replace(/`(.)'/g,"'$1'").replace(/~---/g," — ").replace(/"---/g," — ")}function F(e){return e.replace(/<[^>]*>/g,r=>U(r))}function U(e){const r=e.match(/^<\s*(\/?)\s*([a-zA-Z0-9]+)([^>]*)>$/);if(!r)return"";const t=r[1]==="/",n=r[2].toLowerCase(),i=r[3]??"";if(!C.has(n))return"";if(t)return`</${n}>`;if(n==="br")return"<br/>";const s=Z(i),o=z[n]??new Set,l=[];for(const[a,u]of s)if(o.has(a)){if(n==="a"&&a==="href"){const f=A(u);if(!f)continue;l.push(["href",f]);continue}if(n==="a"&&a==="target"){u==="_blank"&&l.push(["target","_blank"]);continue}if(!(n==="a"&&a==="rel")&&n==="td"&&(a==="colspan"||a==="rowspan")){const f=Number.parseInt(u,10);Number.isFinite(f)&&f>0&&l.push([a,String(f)]);continue}}n==="a"&&l.some(([a,u])=>a==="target"&&u==="_blank")&&l.push(["rel","noopener noreferrer"]);const c=l.map(([a,u])=>` ${a}="${d(u)}"`).join("");return`<${n}${c}>`}function Z(e){const r=[],t=/([a-zA-Z_:][\w:.-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;let n;for(;(n=t.exec(e))!==null;){const s=n[1].toLowerCase(),o=n[2]??n[3]??n[4]??"";r.push([s,o])}return r}function A(e){const r=String(e??"").trim();if(!r)return"";const t=/^(https?:\/\/|mailto:|#)/i.test(r),n=/^\/(?!\/)/.test(r);return t||n?r:""}function d(e){return String(e).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function X(e){if(typeof window>"u"||typeof document>"u")return;if(e&&e.length>0){const t=new Set(w??[]);for(const n of e)t.add(n);w=[...t]}else M=!0,w=null;if(y)return;y=!0;const r=async()=>{y=!1;const t=await K(),n=Y();if(t!=null&&t.typesetPromise)try{t.typesetClear&&t.typesetClear(n),await t.typesetPromise(n)}catch{}};if(typeof window.requestAnimationFrame=="function"){window.requestAnimationFrame(()=>{r()});return}setTimeout(()=>{r()},0)}function Y(){if(M){M=!1,w=null;return}const e=w??void 0;return w=null,e}function Q(){const e=['script[data-mathjax="tex-renderer"]',"script#MathJax-script",'script[src*="mathjax"][src*="tex-mml-chtml"]','script[src*="MathJax.js"]'];for(const r of e){const t=document.querySelector(r);if(t instanceof HTMLScriptElement)return t}return null}function K(){var e;return typeof window>"u"||typeof document>"u"?Promise.resolve(null):(e=window.MathJax)!=null&&e.typesetPromise?Promise.resolve(window.MathJax):$||(window.MathJax=window.MathJax||{tex:{inlineMath:[["$","$"]],displayMath:[["$$","$$"]],processEscapes:!0},svg:{fontCache:"global"}},$=new Promise((r,t)=>{var f;const n=Q();let i=null,s=!1;const o=()=>{i!==null&&(clearTimeout(i),i=null)},l=p=>{s||(s=!0,o(),p())},c=()=>{l(()=>t(new Error("Failed to load MathJax script")))},a=()=>{l(()=>r(window.MathJax??null))};if(i=setTimeout(()=>{c()},j),n){if((f=window.MathJax)!=null&&f.typesetPromise){a();return}n.addEventListener("load",a,{once:!0}),n.addEventListener("error",c,{once:!0}),setTimeout(()=>{var p;(p=window.MathJax)!=null&&p.typesetPromise&&a()},0);return}const u=document.createElement("script");u.id="MathJax-script",u.src=I,u.async=!0,u.crossOrigin="anonymous",u.dataset.mathjax="tex-renderer",u.addEventListener("load",a,{once:!0}),u.addEventListener("error",c,{once:!0}),document.head.append(u)}).catch(()=>($=null,null)),$)}exports.renderTexStatement=k;
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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}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
@@ -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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;')\n}\n\n/**\n * Debounced global typeset trigger. It runs once per animation frame\n * regardless of how many render calls happen in that frame.\n */\nfunction scheduleGlobalMathTypeset(targets?: Element[]): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return\n }\n\n if (targets && targets.length > 0) {\n const uniqueTargets = new Set(pendingMathJaxTargets ?? [])\n for (const target of targets) {\n uniqueTargets.add(target)\n }\n pendingMathJaxTargets = [...uniqueTargets]\n } else {\n pendingMathJaxGlobalTypeset = true\n pendingMathJaxTargets = null\n }\n\n if (mathJaxTypesetScheduled) {\n return\n }\n mathJaxTypesetScheduled = true\n\n const runTypeset = async (): Promise<void> => {\n mathJaxTypesetScheduled = false\n const mathJax = await ensureMathJax()\n const scopedTargets = selectTypesetTargetsForRun()\n if (!mathJax?.typesetPromise) {\n return\n }\n\n try {\n if (mathJax.typesetClear) {\n // Avoid stale state from previous typesets.\n mathJax.typesetClear(scopedTargets)\n }\n await mathJax.typesetPromise(scopedTargets)\n } catch {\n // Keep rendering non-blocking if MathJax fails to typeset.\n }\n }\n\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(() => {\n void runTypeset()\n })\n return\n }\n\n setTimeout(() => {\n void runTypeset()\n }, 0)\n}\n\nfunction selectTypesetTargetsForRun(): Element[] | undefined {\n if (pendingMathJaxGlobalTypeset) {\n pendingMathJaxGlobalTypeset = false\n pendingMathJaxTargets = null\n return undefined\n }\n\n const scopedTargets = pendingMathJaxTargets ?? undefined\n pendingMathJaxTargets = null\n return scopedTargets\n}\n\nfunction findExistingMathJaxScript(): HTMLScriptElement | null {\n const selectors = [\n 'script[data-mathjax=\"tex-renderer\"]',\n 'script#MathJax-script',\n 'script[src*=\"mathjax\"][src*=\"tex-mml-chtml\"]',\n 'script[src*=\"MathJax.js\"]',\n ]\n\n for (const selector of selectors) {\n const candidate = document.querySelector(selector)\n if (candidate instanceof HTMLScriptElement) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Ensure MathJax v3 is loaded exactly once in browser environments.\n * Returns null when unavailable/failing to keep rendering resilient.\n */\nfunction ensureMathJax(): Promise<MathJaxLike | null> {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.resolve(null)\n }\n\n if (window.MathJax?.typesetPromise) {\n return Promise.resolve(window.MathJax)\n }\n\n if (mathJaxLoadPromise) {\n return mathJaxLoadPromise\n }\n\n window.MathJax = window.MathJax || {\n tex: {\n inlineMath: [['$', '$']],\n displayMath: [['$$', '$$']],\n processEscapes: true,\n },\n svg: {\n fontCache: 'global',\n },\n }\n\n mathJaxLoadPromise = new Promise<MathJaxLike | null>((resolve, reject) => {\n const existing = findExistingMathJaxScript()\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n let settled = false\n\n const clearPendingTimeout = (): void => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId)\n timeoutId = null\n }\n }\n\n const settle = (callback: () => void): void => {\n if (settled) {\n return\n }\n settled = true\n clearPendingTimeout()\n callback()\n }\n\n const fail = (): void => {\n settle(() => reject(new Error('Failed to load MathJax script')))\n }\n\n const succeed = (): void => {\n settle(() => resolve(window.MathJax ?? null))\n }\n\n timeoutId = setTimeout(() => {\n fail()\n }, MATHJAX_LOAD_TIMEOUT_MS)\n\n if (existing) {\n if (window.MathJax?.typesetPromise) {\n succeed()\n return\n }\n existing.addEventListener('load', succeed, { once: true })\n existing.addEventListener('error', fail, { once: true })\n // Existing scripts can already be loaded before listeners are attached.\n setTimeout(() => {\n if (window.MathJax?.typesetPromise) {\n succeed()\n }\n }, 0)\n return\n }\n\n const script = document.createElement('script')\n script.id = 'MathJax-script'\n script.src = MATHJAX_SCRIPT_URL\n script.async = true\n script.crossOrigin = 'anonymous'\n if (MATHJAX_SCRIPT_INTEGRITY) {\n script.integrity = MATHJAX_SCRIPT_INTEGRITY\n }\n script.dataset.mathjax = 'tex-renderer'\n script.addEventListener('load', succeed, { once: true })\n script.addEventListener('error', fail, { once: true })\n document.head.append(script)\n }).catch(() => {\n mathJaxLoadPromise = null\n return null\n })\n\n return mathJaxLoadPromise\n}\n"],"names":["STYLE_TAGS","SIZE_COMMANDS","ALLOWED_TAGS","ALLOWED_ATTRS_BY_TAG","mathJaxLoadPromise","mathJaxTypesetScheduled","pendingMathJaxTargets","pendingMathJaxGlobalTypeset","MATHJAX_SCRIPT_URL","MATHJAX_LOAD_TIMEOUT_MS","renderTexStatement","tex","options","normalized","rawHtml","parseBlocks","html","sanitizeHtml","scopedTargets","normalizeTypesetTargets","scheduleGlobalMathTypeset","typesetTarget","targets","target","text","blockStartRegex","last","match","renderParagraphChunk","envName","env","findEnvironment","spec","contentStart","specGroup","parseBraced","tabularContent","renderTabular","listContent","renderList","code","escapeHtml","centered","first","parseBracedWithWhitespace","second","quoteHtml","parseInline","authorHtml","beginIndex","beginToken","endToken","depth","cursor","nextBegin","nextEnd","chunk","out","separator","pushParagraph","output","paragraphRaw","leadingWhitespace","_a","trailingWhitespace","_b","trimmed","inline","kind","content","itemRegex","itemStarts","items","i","start","end","itemHtml","tag","rows","splitTopLevel","htmlRows","activeRowspans","firstPhysicalRow","rowToken","row","decrementRowspanMap","cells","col","renderedCells","rawCell","cell","parseTableCell","attrs","step","map","key","value","next","colspan","rowspan","changed","multiColumn","multiRow","plain","escapedDoubleQuote","flushPlain","applyTypography","findUnescaped","rawMath","findInlineMathEnd","command","parseInlineCommand","arg","parsed","href","sanitizeUrl","caption","braced","delimiter","current","char","marker","rawTag","sanitizeTag","isClosing","attrText","parseAttributes","allowedAttrs","sanitizedAttrs","name","safeHref","attrsString","attributeText","attributes","regex","url","isAllowedProtocol","isSafeRelativePath","uniqueTargets","runTypeset","mathJax","ensureMathJax","selectTypesetTargetsForRun","findExistingMathJaxScript","selectors","selector","candidate","resolve","reject","existing","timeoutId","settled","clearPendingTimeout","settle","callback","fail","succeed","script"],"mappings":"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"}
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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;')\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
- ]), z = {
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 I = "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js", j = 8e3;
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(""), i = B(n);
52
+ `), n = J(t, { preserveNewlines: r.preserveNewlines === !0 }).join(""), s = x(n);
53
53
  if (r.typeset) {
54
- const s = k(r.typesetTarget);
55
- Z(s);
54
+ const i = k(r.typesetTarget);
55
+ Z(i);
56
56
  }
57
- return i;
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 r = [], t = /\\begin\{(itemize|enumerate|lstlisting|center|tabular)\}|\\epigraph\{/g;
67
- let n = 0, i;
68
- for (; (i = t.exec(e)) !== null; ) {
69
- if (i.index > n && r.push(...b(e.slice(n, i.index))), i[1]) {
70
- const s = i[1], o = x(e, i.index, s);
71
- if (!o) {
72
- r.push(...b(e.slice(i.index)));
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 (s === "tabular") {
76
- let l = "", c = o.innerStart;
77
- const a = P(e, c);
78
- a && (l = a.content, c = a.end);
79
- const u = e.slice(c, o.innerEnd);
80
- r.push(W(l, u));
81
- } else if (s === "itemize" || s === "enumerate") {
82
- const l = e.slice(o.innerStart, o.innerEnd);
83
- r.push(H(s, l));
84
- } else if (s === "lstlisting") {
85
- const l = e.slice(o.innerStart, o.innerEnd).replace(/^\n+|\n+$/g, "");
86
- r.push(`<pre><code>${d(l)}</code></pre>`);
87
- } else if (s === "center") {
88
- const l = e.slice(o.innerStart, o.innerEnd);
89
- r.push(`<div>${J(l).join("")}</div>`);
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
- n = o.end, t.lastIndex = n;
91
+ s = l.end, n.lastIndex = s;
92
92
  continue;
93
93
  }
94
94
  if (e.startsWith("\\epigraph{", i.index)) {
95
- const s = g(e, i.index + 9);
96
- if (!s) {
97
- r.push(...b(e.slice(i.index, i.index + 9))), n = i.index + 9, t.lastIndex = n;
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 o = g(e, s.end);
101
- if (!o) {
102
- r.push(...b(e.slice(i.index, s.end))), n = s.end, t.lastIndex = n;
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 l = m(s.content.trim()), c = m(o.content.trim());
106
- r.push(`<blockquote><p>${l}</p><footer>${c}</footer></blockquote>`), n = o.end, t.lastIndex = n;
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
- n = i.index + i[0].length;
109
+ s = i.index + i[0].length;
110
110
  }
111
- return n < e.length && r.push(...b(e.slice(n))), r;
111
+ return s < e.length && t.push(...b(e.slice(s), r)), t;
112
112
  }
113
- function x(e, r, t) {
114
- const n = `\\begin{${t}}`, i = `\\end{${t}}`;
115
- let s = 1, o = r + n.length;
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(i, o);
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
- s += 1, o = l + n.length;
121
+ i += 1, o = l + n.length;
122
122
  continue;
123
123
  }
124
- if (s -= 1, s === 0)
124
+ if (i -= 1, i === 0)
125
125
  return {
126
126
  innerStart: r + n.length,
127
127
  innerEnd: c,
128
- end: c + i.length
128
+ end: c + s.length
129
129
  };
130
- o = c + i.length;
130
+ o = c + s.length;
131
131
  }
132
132
  return null;
133
133
  }
134
- function b(e) {
135
- const r = [], t = /\n\s*\n/g;
136
- let n = 0, i;
137
- for (; (i = t.exec(e)) !== null; )
138
- E(r, e.slice(n, i.index)), n = i.index + i[0].length;
139
- return E(r, e.slice(n)), r;
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 t = ((l = r.match(/^\s*/)) == null ? void 0 : l[0].length) ?? 0, n = ((c = r.match(/\s*$/)) == null ? void 0 : c[0].length) ?? 0, i = r.slice(t, r.length - n);
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 s = i.replace(/\n/g, " "), o = m(s);
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 H(e, r) {
150
+ function W(e, r) {
150
151
  const t = /\\item\b/g, n = [];
151
- let i;
152
- for (; (i = t.exec(r)) !== null; )
153
- n.push(i.index);
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 s = [];
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, f = r.slice(c, a).trim();
159
- if (!f)
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 p = f.replace(/\n/g, " "), h = m(p);
162
- s.push(`<li>${h}</li>`);
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}>${s.join("")}</${o}>`;
166
+ return `<${o}>${i.join("")}</${o}>`;
166
167
  }
167
- function W(e, r) {
168
- const t = L(r, "\\\\"), n = [], i = /* @__PURE__ */ new Map();
169
- let s = !0;
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
- s || R(i), s = !1;
175
+ i || R(s), i = !1;
175
176
  const a = L(c, "&");
176
177
  let u = 0;
177
- const f = [];
178
- for (const p of a) {
179
- const h = q(p);
180
- for (; i.has(u); )
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 (h.colspan > 1 && (T += ` colspan="${h.colspan}"`), h.rowspan > 1) {
184
- T += ` rowspan="${h.rowspan}"`;
185
- for (let S = 0; S < h.colspan; S += 1)
186
- i.set(u + S, h.rowspan - 1);
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
- f.push(`<td${T}>${h.html}</td>`), u += h.colspan;
189
+ d.push(`<td${T}>${p.html}</td>`), u += p.colspan;
189
190
  }
190
- n.push(`<tr>${f.join("")}</tr>`);
191
+ n.push(`<tr>${d.join("")}</tr>`);
191
192
  }
192
- return `<table${e ? ` data-spec="${d(e)}"` : ""}><tbody>${n.join("")}</tbody></table>`;
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, i = !0;
202
- for (; i; ) {
203
- i = !1;
204
- const s = r.match(/^\\multicolumn\{(\d+)\}\{[^}]*\}\{([\s\S]*)\}$/);
205
- s && (t *= Number.parseInt(s[1], 10), r = s[2].trim(), i = !0);
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(), i = !0);
208
+ o && (n *= Number.parseInt(o[1], 10), r = o[2].trim(), s = !0);
208
209
  }
209
210
  return {
210
- html: m(r),
211
+ html: h(r),
211
212
  colspan: t,
212
213
  rowspan: n
213
214
  };
214
215
  }
215
- function m(e) {
216
+ function h(e) {
216
217
  let r = "", t = "";
217
- const n = d('"'), i = () => {
218
- t && (r += d(N(t)), t = "");
218
+ const n = f('"'), s = () => {
219
+ t && (r += f(B(t)), t = "");
219
220
  };
220
- for (let s = 0; s < e.length; s += 1) {
221
- if (e.startsWith("$$", s)) {
222
- const o = D(e, "$$", s + 2);
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
- i();
225
- const l = e.slice(s, o + 2);
226
- r += d(l), s = o + 1;
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[s] === "$") {
231
- const o = G(e, s + 1);
231
+ if (e[i] === "$") {
232
+ const o = G(e, i + 1);
232
233
  if (o !== -1) {
233
- i();
234
- const l = e.slice(s, o + 1);
235
- r += d(l), s = o;
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[s] === "\\" && e[s + 1] === "\\") {
240
- i(), r += "<br/>", s += 1;
240
+ if (e[i] === "\\" && e[i + 1] === "\\") {
241
+ s(), r += "<br/>", i += 1;
241
242
  continue;
242
243
  }
243
- if (e.startsWith("``", s) || e.startsWith("''", s)) {
244
- i(), r += n, s += 1;
244
+ if (e.startsWith("``", i) || e.startsWith("''", i)) {
245
+ s(), r += n, i += 1;
245
246
  continue;
246
247
  }
247
- if (e[s] === "\\") {
248
- const o = O(e, s);
248
+ if (e[i] === "\\") {
249
+ const o = O(e, i);
249
250
  if (o) {
250
- i(), r += o.html, s = o.end - 1;
251
+ s(), r += o.html, i = o.end - 1;
251
252
  continue;
252
253
  }
253
254
  }
254
- t += e[s];
255
+ t += e[i];
255
256
  }
256
- return i(), r;
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 i = g(e, t);
267
- if (!i)
268
- return { html: d(`\\${n}`), end: t };
269
- const s = m(i.content), o = v[n];
270
- return n === "textsc" ? { html: `<span>${s}</span>`, end: i.end } : { html: `<${o}>${s}</${o}>`, end: i.end };
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 i = g(e, t);
274
- return i ? { html: `<span>${m(i.content)}</span>`, end: i.end } : { html: d(`\\${n}`), end: t };
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 i = g(e, t);
278
- if (!i)
279
- return { html: d("\\url"), end: t };
280
- const s = A(i.content.trim()), o = d(i.content.trim());
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="${d(s)}" target="_blank" rel="noopener noreferrer">${o}</a>`,
283
- end: i.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 i = g(e, t);
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: d("\\href"), end: i.end };
293
- const o = A(i.content.trim()), l = m(s.content);
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="${d(o)}" target="_blank" rel="noopener noreferrer">${l}</a>`,
296
- end: s.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: d(`\\${n}`), end: t };
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 = "", i = 0;
329
- for (let s = 0; s < e.length; s += 1) {
330
- const o = e[s];
331
- if (o === "{" && e[s - 1] !== "\\" ? i += 1 : o === "}" && e[s - 1] !== "\\" && i > 0 && (i -= 1), i === 0 && r === "&" && o === "&") {
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 (i === 0 && r === "\\\\" && o === "\\" && e[s + 1] === "\\") {
336
- t.push(n), n = "", s += 1;
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 N(e) {
366
+ function B(e) {
366
367
  return e.replace(/<<([\s\S]*?)>>/g, "«$1»").replace(/`(.)'/g, "'$1'").replace(/~---/g, " — ").replace(/"---/g, " — ");
367
368
  }
368
- function B(e) {
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(), i = r[3] ?? "";
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 s = U(i), o = z[n] ?? /* @__PURE__ */ new Set(), l = [];
383
- for (const [a, u] of s)
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 f = A(u);
387
- if (!f)
387
+ const d = A(u);
388
+ if (!d)
388
389
  continue;
389
- l.push(["href", f]);
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 f = Number.parseInt(u, 10);
398
- Number.isFinite(f) && f > 0 && l.push([a, String(f)]);
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}="${d(u)}"`).join("");
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 s = n[1].toLowerCase(), o = n[2] ?? n[3] ?? n[4] ?? "";
411
- r.push([s, o]);
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 d(e) {
423
+ function f(e) {
423
424
  return String(e).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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 f;
492
+ var d;
492
493
  const n = Y();
493
- let i = null, s = !1;
494
+ let s = null, i = !1;
494
495
  const o = () => {
495
- i !== null && (clearTimeout(i), i = null);
496
- }, l = (p) => {
497
- s || (s = !0, o(), p());
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 (i = setTimeout(() => {
504
+ if (s = setTimeout(() => {
504
505
  c();
505
- }, j), n) {
506
- if ((f = window.MathJax) != null && f.typesetPromise) {
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 p;
512
- (p = window.MathJax) != null && p.typesetPromise && a();
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 = I, u.async = !0, u.crossOrigin = "anonymous", u.dataset.mathjax = "tex-renderer", u.addEventListener("load", a, { once: !0 }), u.addEventListener("error", c, { once: !0 }), document.head.append(u);
518
+ 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 {
@@ -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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;')\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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;')\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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"}
@@ -1,6 +1,7 @@
1
1
  export type RenderTexStatementOptions = {
2
2
  typeset?: boolean;
3
3
  typesetTarget?: Element | Element[] | null;
4
+ preserveNewlines?: boolean;
4
5
  };
5
6
  type MathJaxLike = {
6
7
  typesetPromise?: (elements?: Element[]) => Promise<unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtonschool/tex-html-parser",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript parser/transformer for TeX-to-HTML rendering.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",