@mathzetta/inlinemath 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +64 -20
  2. package/dist/cas.external.js +2 -2
  3. package/dist/cas.js +2 -2
  4. package/dist/chunks/calculate-Cz87Lt0N.js +2 -0
  5. package/dist/chunks/draw-Bev17BJJ.js +2 -0
  6. package/dist/chunks/edit-C65xXzo8.js +2 -0
  7. package/dist/chunks/edit-M68aRA5o.js +2 -0
  8. package/dist/chunks/{html-CvuSyhzk.js → html-BimAqn7x.js} +2 -2
  9. package/dist/chunks/html-CupluJSh.js +2 -0
  10. package/dist/chunks/lite-3Fb-OHbt.js +2 -0
  11. package/dist/inlinemath-cas.external.global.min.js +2 -2
  12. package/dist/inlinemath-cas.global.min.js +2 -2
  13. package/dist/inlinemath-lite-calc.global.min.js +2 -2
  14. package/dist/inlinemath-lite.external.global.min.js +2 -2
  15. package/dist/inlinemath-lite.global.min.js +2 -2
  16. package/dist/inlinemath.css +32 -1
  17. package/dist/inlinemath.external.global.min.js +2 -2
  18. package/dist/inlinemath.external.js +2 -2
  19. package/dist/inlinemath.global.min.js +2 -2
  20. package/dist/inlinemath.js +2 -2
  21. package/dist/lite-calc.js +2 -2
  22. package/dist/lite.external.js +2 -2
  23. package/dist/lite.js +2 -2
  24. package/dist/markdown.external.js +2 -2
  25. package/dist/markdown.js +2 -2
  26. package/inlinemath.css +32 -1
  27. package/package.json +3 -3
  28. package/src/cas.js +25 -15
  29. package/src/edit.js +96 -0
  30. package/src/glyphs.js +2 -0
  31. package/src/index.d.ts +2 -2
  32. package/src/index.js +1 -1
  33. package/src/layout.js +55 -11
  34. package/src/markdown.d.ts +2 -2
  35. package/src/markdown.js +354 -39
  36. package/src/mathzetta.js +2 -1
  37. package/src/types.d.ts +77 -6
  38. package/src/variables.js +228 -0
  39. package/src/view.js +226 -6
  40. package/dist/chunks/calculate-BX9lJ7Ff.js +0 -2
  41. package/dist/chunks/draw-Cm4exbg7.js +0 -2
  42. package/dist/chunks/html-DWtfytjA.js +0 -2
  43. package/dist/chunks/lite-Bj_L6w2g.js +0 -2
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
- import{t}from"./chunks/html-DWtfytjA.js";export{a as toHTML}from"./chunks/html-DWtfytjA.js";import{l as e,a as s,s as i,t as h,p as r,b as o}from"./chunks/lite-Bj_L6w2g.js";export{D as DEFAULTS,P as PATHS,n as nodeAt,c as numberTree}from"./chunks/lite-Bj_L6w2g.js";import"./chunks/calculate-BX9lJ7Ff.js";class l{constructor(t,e,s={}){this.element=t,this.options=s,this.p=s.prefix||"mv",this.handlers=[],this.set(e)}set(i,{animate:n=!1,duration:h=600,easing:r="ease-in-out"}={}){const o=n&&this.tree?this.snapshot():null;return this.tree=t(i,this.options),this.element.innerHTML=e(this.tree,s(this.tree,this.options)).html,this.root=this.element.firstElementChild,this.handlers.length&&this.root.classList.add(`${this.p}-clickable`),o&&this.morph(o,{duration:h,easing:r}),this}nodes(t){return i(this.tree,t)}elements(t){return this.nodes(t).map(t=>this.elementOf(t)).filter(Boolean)}elementOf(t){return void 0===t.id?null:this.root.querySelector(`[data-node="${t.id}"]`)}highlight(t,e=`${this.p}-hl`){return this.elements(t).forEach(t=>t.classList.add(e)),this}unhighlight(t=null,e=`${this.p}-hl`){return(null===t?this.root.querySelectorAll(`.${e}`):this.elements(t)).forEach(t=>t.classList.remove(e)),this}color(t,e){return this.elements(t).forEach(t=>{t.style.color=e||""}),this}on(t,e){const s=t=>{const s=t.target.closest?.("[data-node]");if(!s||!this.root.contains(s))return;const n=Number(s.dataset.node),h=i(this.tree,n)[0];e({node:h,id:n,path:r(this.tree,n),element:s,event:t})};return this.element.addEventListener(t,s),this.handlers.push([t,s]),this.root.classList.add(`${this.p}-clickable`),()=>this.element.removeEventListener(t,s)}segments(t={}){return h(this.tree,t)}showSegment(t,e=`${this.p}-hl`){return this.unhighlight(null,e),t&&void 0!==t.id&&this.highlight(t.id,e),this}snapshot(){const t=new Map,e=s=>{const i=this.elementOf(s);if(i){const e=o(s);t.has(e)||t.set(e,[]),t.get(e).push(i.getBoundingClientRect())}"op"===s.type&&s.args.forEach(e)};return e(this.tree),t}morph(t,{duration:e,easing:s}){if("undefined"==typeof Element||!Element.prototype.animate)return;const i=n=>{const h=this.elementOf(n),r=h&&t.get(o(n));if(r&&r.length){const t=r.shift(),i=h.getBoundingClientRect(),n=t.left-i.left,o=t.top-i.top,a=i.width?t.width/i.width:1,l=i.height?t.height/i.height:1;return void((Math.abs(n)+Math.abs(o)>.5||Math.abs(a-1)+Math.abs(l-1)>.01)&&h.animate([{transformOrigin:"0 0",transform:`translate(${n}px, ${o}px) scale(${a}, ${l})`},{transformOrigin:"0 0",transform:"none"}],{duration:e,easing:s}))}"op"===n.type?n.args.forEach(i):h&&h.animate([{opacity:0},{opacity:0},{opacity:1}],{duration:e,easing:s})};i(this.tree)}}function m(t,e,s={}){return new l(t,e,s)}export{l as View,e as layout,r as pathOf,m as render,i as select,o as signature,t as treeOf};
1
+ /* Inline Math 0.2.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
+ import{t}from"./chunks/html-CupluJSh.js";export{a as toHTML}from"./chunks/html-CupluJSh.js";import{l as e,a as s,t as i,s as r,p as n,n as o,b as h,c as l}from"./chunks/lite-3Fb-OHbt.js";export{D as DEFAULTS,P as PATHS,d as numberTree}from"./chunks/lite-3Fb-OHbt.js";import{o as c,r as u}from"./chunks/edit-M68aRA5o.js";import"./chunks/calculate-Cz87Lt0N.js";class f{constructor(t,e,s={}){this.element=t,this.options=s,this.p=s.prefix||"mv",this.handlers=[],this.clicks=[],this.active=null,this.live=p(t,s.announce,this.p),this.set(e)}set(i,{animate:r=!1,duration:n=600,easing:o="ease-in-out"}={}){const h=m(r)&&this.tree?this.snapshot():null,a=void 0!==this.tree;return this.tree=t(i,this.options),this.element.innerHTML=e(this.tree,s(this.tree,this.options)).html,this.root=this.element.firstElementChild,this.handlers.length&&this.root.classList.add(`${this.p}-clickable`),this.editing&&this.root.classList.add(`${this.p}-editable`),this.active=null,(this.clicks.length||this.editing)&&this.focusable(),h&&this.morph(h,{duration:n,easing:o}),a&&this.live&&this.announce(this.spoken()),this}spoken(){const t=this.root&&this.root.getAttribute&&this.root.getAttribute("aria-label");if(t)return t;try{return i(this.tree)}catch{return""}}announce(t){return this.say(this.live,t)}say(t,e){if(!t)return this;t.textContent="";const s=()=>{t.textContent=e};return"function"==typeof requestAnimationFrame?requestAnimationFrame(()=>requestAnimationFrame(s)):setTimeout(s,50),this}nodes(t){return r(this.tree,t)}elements(t){return this.nodes(t).map(t=>this.elementOf(t)).filter(Boolean)}elementOf(t){return void 0===t.id?null:this.root.querySelector(`[data-node="${t.id}"]`)}highlight(t,e=`${this.p}-hl`){return this.elements(t).forEach(t=>t.classList.add(e)),this}unhighlight(t=null,e=`${this.p}-hl`){return(null===t?this.root.querySelectorAll(`.${e}`):this.elements(t)).forEach(t=>t.classList.remove(e)),this}color(t,e){return this.elements(t).forEach(t=>{t.style.color=e||""}),this}on(t,e){const s=t=>{const s=t.target.closest?.("[data-node]");if(!s||!this.root.contains(s))return;const i=Number(s.dataset.node),o=r(this.tree,i)[0];e({node:o,id:i,path:n(this.tree,i),element:s,event:t})};return this.element.addEventListener(t,s),this.handlers.push([t,s]),this.root.classList.add(`${this.p}-clickable`),"click"===t&&(this.clicks.push(e),this.keyboard()),()=>{this.element.removeEventListener(t,s),this.clicks=this.clicks.filter(t=>t!==e)}}editable({toText:t,onEdit:e}={}){if("function"!=typeof t)throw new Error("editable needs { toText } (import { toText } from 'mathzetta/text')");return this.editing={toText:t,onEdit:e},this.root&&this.root.classList&&this.root.classList.add(`${this.p}-editable`),this.keyboard(),!this.editOn&&this.element.addEventListener&&(this.editOn=!0,this.element.addEventListener("dblclick",t=>{const e=t.target.closest?.("[data-node]");e&&this.root.contains(e)&&(t.preventDefault(),this.edit(r(this.tree,Number(e.dataset.node))[0]))})),this}edit(e=this.tree,{returnFocus:s=!1}={}){if(!this.editing)throw new Error("call editable({ toText }) first");const r="object"==typeof e&&e&&e.type?e:this.nodes(e)[0];if(!r)return null;const h=n(this.tree,r.id)||[],a=this.elementOf(r)||this.root;let l="Edit";try{l=`Edit ${i(r)}`}catch{}const d=()=>{s&&this.root.focus&&this.root.focus()};return c(a,this.editing.toText(r),{label:l,parse:e=>t(e,this.options),commit:(t,e)=>{this.set(u(this.tree,h,t),{animate:!0});const i=o(this.tree,h);s&&i&&this.focusPart(i===this.tree?null:i),d(),this.editing.onEdit&&this.editing.onEdit({tree:this.tree,text:this.editing.toText(this.tree),path:h,part:e})},cancel:d})}focusable(){this.root&&this.root.setAttribute&&(this.root.setAttribute("tabindex","0"),this.root.getAttribute("aria-label")||(this.root.setAttribute("role","math"),this.root.setAttribute("aria-label",this.spoken())),this.root.setAttribute("aria-description",this.editing?"Arrow keys move between parts, F2 edits one.":"Arrow keys move between parts, Enter selects one."))}keyboard(){this.focusable(),!this.keysOn&&this.element.addEventListener&&(this.keysOn=!0,this.partsLive=this.live||p(this.element,!0,this.p),this.element.addEventListener("keydown",t=>this.key(t)))}key(t){if(!this.root||!this.root.contains(t.target))return;const e=this.active;let s;switch(t.key){case"ArrowDown":s=e?this.partsOf(e)[0]:this.partsOf(this.tree)[0];break;case"ArrowUp":s=e?this.outerOf(e):void 0;break;case"ArrowRight":s=e?this.neighbour(e,1):this.partsOf(this.tree)[0];break;case"ArrowLeft":s=e?this.neighbour(e,-1):void 0;break;case"Home":case"Escape":s=null;break;case"F2":if(!this.editing)return;return t.preventDefault(),void this.edit(e||this.tree,{returnFocus:!0});case"Enter":case" ":{t.preventDefault();const s=e||this.tree,i=this.elementOf(s)||this.root,r={node:s,id:s.id,path:n(this.tree,s.id),element:i,event:t};for(const t of this.clicks)t(r);return}default:return}t.preventDefault(),void 0!==s&&this.focusPart(s)}focusPart(t){const e=`${this.p}-focus`;this.root.querySelectorAll(`.${e}`).forEach(t=>t.classList.remove(e)),this.active=t;const s=t&&this.elementOf(t);s&&s.classList.add(e);let r="";try{r=t?i(t):this.spoken()}catch{r=""}return this.say(this.partsLive,r),this}partsOf(t){const e=[],s=t=>{this.elementOf(t)?e.push(t):"op"===t.type&&t.args.forEach(s)};return t&&"op"===t.type&&t.args.forEach(s),e}outerOf(t){const e=n(this.tree,t.id);for(let t=e?e.length-1:-1;t>0;t--){const s=o(this.tree,e.slice(0,t));if(s&&this.elementOf(s))return s}return null}neighbour(t,e){const s=this.partsOf(this.outerOf(t)||this.tree),i=s.indexOf(t);return i<0?void 0:s[i+e]}segments(t={}){return h(this.tree,t)}showSegment(t,e=`${this.p}-hl`){return this.unhighlight(null,e),t&&void 0!==t.id&&this.highlight(t.id,e),this}snapshot(){const t=new Map,e=s=>{const i=this.elementOf(s);if(i){const e=l(s);t.has(e)||t.set(e,[]),t.get(e).push(i.getBoundingClientRect())}"op"===s.type&&s.args.forEach(e)};return e(this.tree),t}morph(t,{duration:e,easing:s}){if("undefined"==typeof Element||!Element.prototype.animate)return;const i=r=>{const n=this.elementOf(r),o=n&&t.get(l(r));if(o&&o.length){const t=o.shift(),i=n.getBoundingClientRect(),r=t.left-i.left,h=t.top-i.top,a=i.width?t.width/i.width:1,l=i.height?t.height/i.height:1;return void((Math.abs(r)+Math.abs(h)>.5||Math.abs(a-1)+Math.abs(l-1)>.01)&&n.animate([{transformOrigin:"0 0",transform:`translate(${r}px, ${h}px) scale(${a}, ${l})`},{transformOrigin:"0 0",transform:"none"}],{duration:e,easing:s}))}"op"===r.type?r.args.forEach(i):n&&n.animate([{opacity:0},{opacity:0},{opacity:1}],{duration:e,easing:s})};i(this.tree)}}function p(t,e,s){if(!e)return null;if("object"==typeof e&&1===e.nodeType)return e;if("undefined"==typeof document||!t.insertAdjacentElement)return null;const i=document.createElement("span");return i.className=`${s}-sr-only`,i.setAttribute("aria-live","polite"),i.setAttribute("aria-atomic","true"),t.insertAdjacentElement("afterend",i),i}function m(t){return!!t&&("always"===t||!("function"==typeof matchMedia&&matchMedia("(prefers-reduced-motion: reduce)").matches))}function g(t,e,s={}){return new f(t,e,s)}export{f as View,e as layout,o as nodeAt,n as pathOf,g as render,r as select,m as shouldAnimate,l as signature,t as treeOf};
package/dist/lite-calc.js CHANGED
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
- import{c as o}from"./chunks/calculate-BX9lJ7Ff.js";export{a as calculate}from"./chunks/calculate-BX9lJ7Ff.js";import{d as e,e as t}from"./chunks/lite-Bj_L6w2g.js";export{D as DEFAULTS,l as layout,c as numberTree,s as select}from"./chunks/lite-Bj_L6w2g.js";const r=(s,c={})=>e(s,{...c,blocks:c.blocks??o}),u=(s,c={})=>t(s,{...c,blocks:c.blocks??o});export{o as calculateBlock,r as toHTML,u as treeOf};
1
+ /* Inline Math 0.2.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
+ import{c}from"./chunks/calculate-Cz87Lt0N.js";export{a as calculate}from"./chunks/calculate-Cz87Lt0N.js";import{e as o,f as e}from"./chunks/lite-3Fb-OHbt.js";export{D as DEFAULTS,l as layout,d as numberTree,s as select}from"./chunks/lite-3Fb-OHbt.js";const t=(s,a={})=>o(s,{...a,blocks:a.blocks??c}),r=(s,a={})=>e(s,{...a,blocks:a.blocks??c});export{c as calculateBlock,t as toHTML,r as treeOf};
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; MathZetta not included: map mathzetta, mathzetta/speech and mathzetta/calculate with an import map */
2
- export{D as DEFAULTS,l as layout,n as numberTree,s as select,t as toHTML,a as treeOf}from"./chunks/draw-Cm4exbg7.js";import"mathzetta";import"mathzetta/speech";
1
+ /* Inline Math 0.2.0 — MIT licence; MathZetta not included: map mathzetta, mathzetta/speech and mathzetta/calculate with an import map */
2
+ export{D as DEFAULTS,l as layout,n as numberTree,s as select,t as toHTML,a as treeOf}from"./chunks/draw-Bev17BJJ.js";import"mathzetta";import"mathzetta/speech";
package/dist/lite.js CHANGED
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
- export{D as DEFAULTS,l as layout,c as numberTree,s as select,d as toHTML,e as treeOf}from"./chunks/lite-Bj_L6w2g.js";
1
+ /* Inline Math 0.2.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
+ export{D as DEFAULTS,l as layout,d as numberTree,s as select,e as toHTML,f as treeOf}from"./chunks/lite-3Fb-OHbt.js";
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; MathZetta not included: map mathzetta, mathzetta/speech and mathzetta/calculate with an import map */
2
- import{a as t}from"./chunks/html-CvuSyhzk.js";import{calculate as e}from"mathzetta/calculate";import"./chunks/draw-Cm4exbg7.js";import"mathzetta";import"mathzetta/speech";const n=new RegExp("(\\d+)","g");function s(t,{delimiters:e=["$$","$","\\[","\\("]}={}){const n=[];let s="",i=0;const r=String(t),l=t=>0===t||"\n"===r[t-1],o=(t,e,s)=>(n.push({source:t,display:e,raw:s}),`${n.length-1}`);for(;i<r.length;){if(l(i)){const t=/^( {0,3})(`{3,}|~{3,})([^\n]*)\n?/.exec(r.slice(i));if(t){const e=t[2],n=new RegExp(`\\n {0,3}${"`"===e[0]?"`":"~"}{${e.length},}[ \\t]*(?=\\n|$)`),c=r.slice(i+t[0].length),l=n.exec(`\n${c}`),a=l?c.slice(0,Math.max(0,l.index-1)):c,p=t[0].length+(l?l.index-1+l[0].length:c.length),h=r.slice(i,i+p);"math"===t[3].trim()?s+=o(a,!0,h):s+=h,i+=p;continue}}const t=r[i];if("`"===t){const t=/^`+/.exec(r.slice(i))[0],e=r.indexOf(t,i+t.length);if(e>-1){s+=r.slice(i,e+t.length),i=e+t.length;continue}s+=t,i+=t.length;continue}if("\\"===t){const t=r.slice(i,i+2),n="\\["===t?"\\]":"\\("===t?"\\)":null;if(n&&e.includes(t)){const e=r.indexOf(n,i+2);if(e>-1){s+=o(r.slice(i+2,e),"\\["===t,r.slice(i,e+2)),i=e+2;continue}}s+=t,i+=2;continue}if("$"===t)if("$"===r[i+1]&&e.includes("$$")){const t=r.indexOf("$$",i+2);if(t>-1){s+=o(r.slice(i+2,t),!0,r.slice(i,t+2)),i=t+2;continue}}else if(e.includes("$")&&r[i+1]&&!/\s/.test(r[i+1])){const t=c(r,i+1);if(t>-1){s+=o(r.slice(i+1,t),!1,r.slice(i,t+1)),i=t+1;continue}}s+=t,i++}return{text:s,math:n}}function c(t,e){for(let n=e;n<t.length;n++){const e=t[n];if("\\"!==e){if("`"===e)return-1;if("\n"===e&&/^\n[ \t]*\n/.test(t.slice(n)))return-1;if("$"===e&&!/\s/.test(t[n-1])&&!/\d/.test(t[n+1]??"")&&"$"!==t[n+1])return n}else n++}return-1}const i=t=>String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),r=/^\s*([A-Za-zͰ-Ͽ](?:_\{?\w+\}?)?)\s*(?::=|=)\s*([^=<>≤≥]+)$/;function l(n,s,c){const{source:l,display:o}=n;try{const n=t(l,{...s.math,display:o,blocks:s.blocks,scope:c});return!1!==s.define&&function(t,n){const s=r.exec(t);if(!s||/\{\{/.test(s[2]))return;try{const t=e(s[2],n);"number"!=typeof t.value&&"bigint"!=typeof t.value||(n[s[1].replace(/[{}]/g,"")]=t.exact?t.text:t.value)}catch{}}(l,c),o?`<span class="mv-md-display">${n}</span>`:`<span class="mv-md-math">${n}</span>`}catch(t){return`<span class="mv-md-error" title="${i(t.message)}"><code>${i(n.raw)}</code></span>`}}const o=/^(https?:|mailto:|#|\/|\.{0,2}\/|[^:]*$)/i,a=t=>o.test(t.trim())?i(t.trim()):"#";function p(t,e,n){const s=[],c=t=>(s.push(t),`${s.length-1}`);let r=t;return r=r.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g,(t,e,s)=>c(`<code>${i(n(s.trim()))}</code>`)),r=r.replace(/\\([\\`*_{}[\]()#+\-.!|~$<>"'])/g,(t,e)=>c(i(e))),r=r.replace(/<((?:https?:\/\/|mailto:)[^\s>]+)>/g,(t,e)=>c(`<a href="${a(e)}">${i(e)}</a>`)),e.html&&(r=r.replace(/<\/?[A-Za-z][^>]*>/g,t=>c(t))),r=r.replace(/!\[([^\]]*)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(t,e,n,s)=>c(`<img src="${a(n)}" alt="${i(e)}"${s?` title="${i(s)}"`:""}>`)),r=r.replace(/\[([^\]]+)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(t,s,r,l)=>c(`<a href="${a(r)}"${l?` title="${i(l)}"`:""}>${p(s,e,n)}</a>`)),r=i(r),r=r.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g,"<strong>$2</strong>"),r=r.replace(/(^|[^*\w])\*(?=\S)([\s\S]*?\S)\*(?!\*)/g,"$1<em>$2</em>"),r=r.replace(/(^|[^_\w])_(?=\S)([\s\S]*?\S)_(?![_\w])/g,"$1<em>$2</em>"),r=r.replace(/~~(?=\S)([\s\S]*?\S)~~/g,"<del>$1</del>"),r=r.replace(/( {2,}|\\)\n/g,"<br>\n"),r.replace(/(\d+)/g,(t,e)=>s[Number(e)])}const h=/^( {0,3})(#{1,6}\s|>|```|~~~|([-*+]|\d{1,9}[.)])\s|(\*[ \t]*){3,}$|(-[ \t]*){3,}$|(_[ \t]*){3,}$)/,u=/^( {0,3})([-*+]|\d{1,9}[.)])(\s+|$)/,$=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;function g(t,e,n){const s=[];let c=0;const r=t=>p(t,e,n);for(;c<t.length;){const l=t[c];if(!l.trim()){c++;continue}const o=/^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)/.exec(l);if(o){const e=[];for(c++;c<t.length&&!new RegExp(`^ {0,3}${"`"===o[1][0]?"`":"~"}{${o[1].length},}\\s*$`).test(t[c]);)e.push(t[c++]);c++;const r=o[2]?` class="language-${i(o[2])}"`:"";s.push(`<pre><code${r}>${i(n(e.join("\n")))}</code></pre>`);continue}const a=/^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/.exec(l);if(a){s.push(`<h${a[1].length}>${r(a[2])}</h${a[1].length}>`),c++;continue}if(/^ {0,3}((\*[ \t]*){3,}|(-[ \t]*){3,}|(_[ \t]*){3,})$/.test(l)){s.push("<hr>"),c++;continue}if(/^ {0,3}>/.test(l)){const i=[];for(;c<t.length&&t[c].trim()&&(/^ {0,3}>/.test(t[c])||i.length);)i.push(t[c].replace(/^ {0,3}> ?/,"")),c++;s.push(`<blockquote>\n${g(i,e,n)}\n</blockquote>`);continue}const p=u.exec(l);if(p){const i=/\d/.test(p[2]),r=i?parseInt(p[2],10):1,l=[];let o=!1;for(;c<t.length;){const e=u.exec(t[c]);if(!e||/\d/.test(e[2])!==i)break;const n=e[0].length,s=[t[c].slice(n)];for(c++;c<t.length;){const e=t[c];if(!e.trim()){const e=t[c+1];if(void 0!==e&&e.startsWith(" ".repeat(Math.min(n,4)))&&e.trim()){s.push(""),o=!0,c++;continue}void 0!==e&&u.test(e)&&(o=!0);break}if(e.startsWith(" ".repeat(Math.min(n,4))))s.push(e.slice(Math.min(n,4)));else{if(u.test(e)||h.test(e))break;s.push(e)}c++}for(l.push(s);c<t.length&&!t[c].trim()&&void 0!==t[c+1]&&u.test(t[c+1]);)c++}const a=i?"ol":"ul",$=l.map(t=>{let s=g(t,e,n);return o||(s=s.replace(/^<p>([\s\S]*?)<\/p>/,"$1").replace(/\n<p>([\s\S]*?)<\/p>/g,"\n$1")),`<li>${s}</li>`});s.push(`<${a}${i&&1!==r?` start="${r}"`:""}>\n${$.join("\n")}\n</${a}>`);continue}if(l.includes("|")&&void 0!==t[c+1]&&$.test(t[c+1])&&t[c+1].includes("-")){const e=t=>t.trim().replace(/^\|/,"").replace(/\|$/,"").split(/(?<!\\)\|/).map(t=>t.trim()),n=e(t[c+1]).map(t=>t.startsWith(":")&&t.endsWith(":")?"center":t.endsWith(":")?"right":t.startsWith(":")?"left":""),i=(t,e,s)=>`<${t}${n[s]?` style="text-align:${n[s]}"`:""}>${r(e)}</${t}>`,o=e(l);c+=2;const a=[];for(;c<t.length&&t[c].trim()&&t[c].includes("|");)a.push(e(t[c++]));s.push(`<table>\n<thead><tr>${o.map((t,e)=>i("th",t,e)).join("")}</tr></thead>\n<tbody>\n${a.map(t=>`<tr>${o.map((e,n)=>i("td",t[n]??"",n)).join("")}</tr>`).join("\n")}\n</tbody>\n</table>`);continue}if(/^( {4}|\t)/.test(l)){const e=[];for(;c<t.length&&(/^( {4}|\t)/.test(t[c])||!t[c].trim());)e.push(t[c++].replace(/^( {4}|\t)/,""));for(;e.length&&!e[e.length-1].trim();)e.pop();s.push(`<pre><code>${i(n(e.join("\n")))}</code></pre>`);continue}const m=[];for(;c<t.length&&t[c].trim()&&(!m.length||!h.test(t[c])||/^ {0,3}[-=]+\s*$/.test(t[c]));){if(m.length&&/^ {0,3}(=+|-+)\s*$/.test(t[c])){const e="="===t[c].trim()[0]?1:2;s.push(`<h${e}>${r(m.join("\n"))}</h${e}>`),m.length=0,c++;break}m.push(t[c].replace(/^ {0,3}/,"")),c++}m.length&&s.push(`<p>${r(m.join("\n"))}</p>`)}return s.join("\n")}function m(t,e={},n=t=>t){return g(String(t).replace(/\r\n?/g,"\n").split("\n"),e,n)}function f(t,e={}){const{text:c,math:i}=s(t,e),r={...e.scope??{}},o=i.map(t=>l(t,e,r));return(e.render?e.render(c):m(c,e,t=>t.replace(n,(t,e)=>i[Number(e)].raw))).replace(new RegExp("<p>\\s*(\\d+)\\s*</p>","g"),(t,e)=>i[Number(e)].display?`<div class="mv-md-block">${o[Number(e)]}</div>`:t).replace(n,(t,e)=>o[Number(e)])}function d(t,e,n={}){return t.innerHTML=f(e,n),t}export{s as extractMath,f as markdownToHTML,m as renderMarkdown,d as renderMarkdownInto};
1
+ /* Inline Math 0.2.0 — MIT licence; MathZetta not included: map mathzetta, mathzetta/speech and mathzetta/calculate with an import map */
2
+ import{t as e,a as t}from"./chunks/html-BimAqn7x.js";import{calculate as n}from"mathzetta/calculate";import{v as s,o as r,r as a,u as i,s as o,E as l,b as u,n as d,i as p}from"./chunks/edit-C65xXzo8.js";export{V as Variables,c as createVariables}from"./chunks/edit-C65xXzo8.js";import{toPostfix as m}from"mathzetta";import"mathzetta/speech";import{p as h,c as f,n as $}from"./chunks/draw-Bev17BJJ.js";const g=new RegExp("(\\d+)","g");function v(e,{delimiters:t=["$$","$","\\[","\\("]}={}){const n=[];let s="",r=0;const a=String(e),i=e=>0===e||"\n"===a[e-1],o=(e,t,s)=>(n.push({source:e,display:t,raw:s,start:r,end:r+s.length}),`${n.length-1}`);for(;r<a.length;){if(i(r)){const e=/^( {0,3})(`{3,}|~{3,})([^\n]*)\n?/.exec(a.slice(r));if(e){const t=e[2],n=new RegExp(`\\n {0,3}${"`"===t[0]?"`":"~"}{${t.length},}[ \\t]*(?=\\n|$)`),i=a.slice(r+e[0].length),c=n.exec(`\n${i}`),l=c?i.slice(0,Math.max(0,c.index-1)):i,u=e[0].length+(c?c.index-1+c[0].length:i.length),d=a.slice(r,r+u);"math"===e[3].trim()?s+=o(l,!0,d):s+=d,r+=u;continue}}const e=a[r];if("`"===e){const e=/^`+/.exec(a.slice(r))[0],t=a.indexOf(e,r+e.length);if(t>-1){s+=a.slice(r,t+e.length),r=t+e.length;continue}s+=e,r+=e.length;continue}if("\\"===e){const e=a.slice(r,r+2),n="\\["===e?"\\]":"\\("===e?"\\)":null;if(n&&t.includes(e)){const t=a.indexOf(n,r+2);if(t>-1){s+=o(a.slice(r+2,t),"\\["===e,a.slice(r,t+2)),r=t+2;continue}}s+=e,r+=2;continue}if("$"===e)if("$"===a[r+1]&&t.includes("$$")){const e=a.indexOf("$$",r+2);if(e>-1){s+=o(a.slice(r+2,e),!0,a.slice(r,e+2)),r=e+2;continue}}else if(t.includes("$")&&a[r+1]&&!/\s/.test(a[r+1])){const e=b(a,r+1);if(e>-1){s+=o(a.slice(r+1,e),!1,a.slice(r,e+1)),r=e+1;continue}}s+=e,r++}return{text:s,math:n}}function b(e,t){for(let n=t;n<e.length;n++){const t=e[n];if("\\"!==t){if("`"===t)return-1;if("\n"===t&&/^\n[ \t]*\n/.test(e.slice(n)))return-1;if("$"===t&&!/\s/.test(e[n-1])&&!/\d/.test(e[n+1]??"")&&"$"!==e[n+1])return n}else n++}return-1}const x=e=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),y=/^\s*([A-Za-z\u0370-\u03ff](?:_\{?\w+\}?)?)\s*=\s*([^=<>≤≥]+)$/;function k(s,r,a,i){const{text:c,math:h}=v(s,r),f=a?a.definitions(i):new Map,g=e=>a?a.textOf(e):e.text,b={...r.scope??{}},k={},w=new Map,S=new Set,E=[],j=()=>{for(const e of Object.keys(k))delete k[e];Object.assign(k,b);for(const e of f.keys())delete k[e];let e=[...f.values()];for(;e.length;){const t=e.filter(e=>{try{const t=n(g(e),k);if("number"==typeof t.value||"bigint"==typeof t.value||Array.isArray(t.value))return k[e.name]=t.exact?t.text:t.value,!1}catch{}return!0});if(t.length===e.length)break;e=t}const t={},s=_({});for(const e of f.keys()){const n=s(e);if(n)try{t[e]=m(o(n,t=>t===e?null:s(t)).tree)}catch{}}k[l]=t},_=t=>{const n=new Map;return s=>{const r=f.get(s);if(!r)return null;if(!n.has(s)){let a=null;try{a=e(g(r),t)}catch{}n.set(s,a)}return n.get(s)}};j();const M=h.map((s,a)=>{const{source:i,display:c}=s,l=c?"mv-md-display":"mv-md-math",m=(e,t="")=>`<span class="${l}${t}" data-mv-md="${a}">${e}</span>`;try{const l={...r.math,display:c,blocks:r.blocks,scope:k},h=function(t){let n=t.trim();const s=n.startsWith("_");s&&(n=n.slice(1).trimStart());let r=n.indexOf(":="),a=2;r<0&&s&&(r=n.search(/(?<![<>!≤≥=])=(?!=)/),a=1);const i=s?{error:"a hidden $_…$ saves a variable: $_A := 2+2$"}:null;if(r<=0)return i;const o=n.slice(0,r).trim(),c=n.slice(r+a).trim();let l=null;try{l=/\{\{/.test(o)?null:d(e(o))}catch{}return null!==l&&c?{hidden:s,lhs:o,name:l,rhs:c,input:p(c)}:i}(i);if(h&&h.error)throw new Error(h.error);if(h){const{name:n,input:r,hidden:i}=h,c=r?{name:n,text:r.default,input:r,key:`input:${JSON.stringify(r)}`}:{name:n,text:h.rhs,input:null,key:`expr:${h.rhs}`,start:s.start,end:s.end};let d="";if(r){const e=function(e,t){const{input:n,name:s}=e,r=`class="mv-md-input mv-md-${n.kind}" data-mv-var="${x(s)}" aria-label="${x(s)}"`;if("dropdown"===n.kind)return`<select ${r}>${n.options.map(e=>`<option value="${x(e)}"${e===t?" selected":""}>${x(e)}</option>`).join("")}</select>`;return"slider"===n.kind?`<input type="range" ${r} min="${n.min}" max="${n.max}" step="${n.step}" value="${x(t)}"><output class="mv-md-output" data-mv-out="${x(s)}">${x(t)}</output>`:`<input type="text" ${r} value="${x(t)}" size="${Math.max(4,t.length+1)}" spellcheck="false" autocomplete="off">`}(c,g(c));d=i?e:`${t(h.lhs,l)}<span class="mv-md-assign" aria-hidden="true">≔</span>${e}`,S.add(a)}else{const n=o(e(h.rhs,l),_(l)).tree;if(!i){const s=$({type:"op",op:"=",args:[u(e(h.lhs)),void 0===n.id?n:u(n)]});E[a]=s,d=function(e){return e.replace('<span class="mv-rel">=</span>','<span class="mv-rel mv-rel-def">≔</span>').replace(/(aria-label="[^"]*?) equals /,"$1 is defined as ").replace("<mo>=</mo>","<mo>≔</mo>")}(t(s,l))}}return f.delete(n),f.set(n,c),w.set(n,c),j(),i&&!r?"":m(d,r?" mv-md-input-def":"")}const{tree:v,changed:M}=o(e(i,l),_(l));M&&$(v),E[a]=v;const O=t(v,l);return!M&&!1!==r.define&&function(e,t,s){const r=y.exec(e);if(!r||/\{\{/.test(r[2]))return!1;const a=r[1].replace(/[{}]/g,"");if(s.has(a))return!1;try{const e=n(r[2],t);if("number"==typeof e.value||"bigint"==typeof e.value||Array.isArray(e.value))return t[a]=e.exact?e.text:e.value,!0}catch{}return!1}(i,b,f)&&j(),m(O)}catch(e){return`<span class="mv-md-error" data-mv-md="${a}" title="${x(e.message)}"><code>${x(s.raw)}</code></span>`}});return{text:c,math:h,drawn:M,defs:w,inputs:S,trees:E}}const w=/^(https?:|mailto:|#|\/|\.{0,2}\/|[^:]*$)/i,S=e=>w.test(e.trim())?x(e.trim()):"#";function E(e,t,n){const s=[],r=e=>(s.push(e),`${s.length-1}`);let a=e;return a=a.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g,(e,t,s)=>r(`<code>${x(n(s.trim()))}</code>`)),a=a.replace(/\\([\\`*_{}[\]()#+\-.!|~$<>"'])/g,(e,t)=>r(x(t))),a=a.replace(/<((?:https?:\/\/|mailto:)[^\s>]+)>/g,(e,t)=>r(`<a href="${S(t)}">${x(t)}</a>`)),t.html&&(a=a.replace(/<\/?[A-Za-z][^>]*>/g,e=>r(e))),a=a.replace(/!\[([^\]]*)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,t,n,s)=>r(`<img src="${S(n)}" alt="${x(t)}"${s?` title="${x(s)}"`:""}>`)),a=a.replace(/\[([^\]]+)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,s,a,i)=>r(`<a href="${S(a)}"${i?` title="${x(i)}"`:""}>${E(s,t,n)}</a>`)),a=x(a),a=a.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g,"<strong>$2</strong>"),a=a.replace(/(^|[^*\w])\*(?=\S)([\s\S]*?\S)\*(?!\*)/g,"$1<em>$2</em>"),a=a.replace(/(^|[^_\w])_(?=\S)([\s\S]*?\S)_(?![_\w])/g,"$1<em>$2</em>"),a=a.replace(/~~(?=\S)([\s\S]*?\S)~~/g,"<del>$1</del>"),a=a.replace(/( {2,}|\\)\n/g,"<br>\n"),a.replace(/(\d+)/g,(e,t)=>s[Number(t)])}const j=/^( {0,3})(#{1,6}\s|>|```|~~~|([-*+]|\d{1,9}[.)])\s|(\*[ \t]*){3,}$|(-[ \t]*){3,}$|(_[ \t]*){3,}$)/,_=/^( {0,3})([-*+]|\d{1,9}[.)])(\s+|$)/,M=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;function O(e,t,n){const s=[];let r=0;const a=e=>E(e,t,n);for(;r<e.length;){const i=e[r];if(!i.trim()){r++;continue}const o=/^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)/.exec(i);if(o){const t=[];for(r++;r<e.length&&!new RegExp(`^ {0,3}${"`"===o[1][0]?"`":"~"}{${o[1].length},}\\s*$`).test(e[r]);)t.push(e[r++]);r++;const a=o[2]?` class="language-${x(o[2])}"`:"";s.push(`<pre><code${a}>${x(n(t.join("\n")))}</code></pre>`);continue}const c=/^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/.exec(i);if(c){s.push(`<h${c[1].length}>${a(c[2])}</h${c[1].length}>`),r++;continue}if(/^ {0,3}((\*[ \t]*){3,}|(-[ \t]*){3,}|(_[ \t]*){3,})$/.test(i)){s.push("<hr>"),r++;continue}if(/^ {0,3}>/.test(i)){const a=[];for(;r<e.length&&e[r].trim()&&(/^ {0,3}>/.test(e[r])||a.length);)a.push(e[r].replace(/^ {0,3}> ?/,"")),r++;s.push(`<blockquote>\n${O(a,t,n)}\n</blockquote>`);continue}const l=_.exec(i);if(l){const a=/\d/.test(l[2]),i=a?parseInt(l[2],10):1,o=[];let c=!1;for(;r<e.length;){const t=_.exec(e[r]);if(!t||/\d/.test(t[2])!==a)break;const n=t[0].length,s=[e[r].slice(n)];for(r++;r<e.length;){const t=e[r];if(!t.trim()){const t=e[r+1];if(void 0!==t&&t.startsWith(" ".repeat(Math.min(n,4)))&&t.trim()){s.push(""),c=!0,r++;continue}void 0!==t&&_.test(t)&&(c=!0);break}if(t.startsWith(" ".repeat(Math.min(n,4))))s.push(t.slice(Math.min(n,4)));else{if(_.test(t)||j.test(t))break;s.push(t)}r++}for(o.push(s);r<e.length&&!e[r].trim()&&void 0!==e[r+1]&&_.test(e[r+1]);)r++}const u=a?"ol":"ul",d=o.map(e=>{let s=O(e,t,n);return c||(s=s.replace(/^<p>([\s\S]*?)<\/p>/,"$1").replace(/\n<p>([\s\S]*?)<\/p>/g,"\n$1")),`<li>${s}</li>`});s.push(`<${u}${a&&1!==i?` start="${i}"`:""}>\n${d.join("\n")}\n</${u}>`);continue}if(i.includes("|")&&void 0!==e[r+1]&&M.test(e[r+1])&&e[r+1].includes("-")){const t=e=>e.trim().replace(/^\|/,"").replace(/\|$/,"").split(/(?<!\\)\|/).map(e=>e.trim()),n=t(e[r+1]).map(e=>e.startsWith(":")&&e.endsWith(":")?"center":e.endsWith(":")?"right":e.startsWith(":")?"left":""),o=(e,t,s)=>`<${e}${n[s]?` style="text-align:${n[s]}"`:""}>${a(t)}</${e}>`,c=t(i);r+=2;const l=[];for(;r<e.length&&e[r].trim()&&e[r].includes("|");)l.push(t(e[r++]));s.push(`<table>\n<thead><tr>${c.map((e,t)=>o("th",e,t)).join("")}</tr></thead>\n<tbody>\n${l.map(e=>`<tr>${c.map((t,n)=>o("td",e[n]??"",n)).join("")}</tr>`).join("\n")}\n</tbody>\n</table>`);continue}if(/^( {4}|\t)/.test(i)){const t=[];for(;r<e.length&&(/^( {4}|\t)/.test(e[r])||!e[r].trim());)t.push(e[r++].replace(/^( {4}|\t)/,""));for(;t.length&&!t[t.length-1].trim();)t.pop();s.push(`<pre><code>${x(n(t.join("\n")))}</code></pre>`);continue}const u=[];for(;r<e.length&&e[r].trim()&&(!u.length||!j.test(e[r])||/^ {0,3}[-=]+\s*$/.test(e[r]));){if(u.length&&/^ {0,3}(=+|-+)\s*$/.test(e[r])){const t="="===e[r].trim()[0]?1:2;s.push(`<h${t}>${a(u.join("\n"))}</h${t}>`),u.length=0,r++;break}u.push(e[r].replace(/^ {0,3}/,"")),r++}u.length&&s.push(`<p>${a(u.join("\n"))}</p>`)}return s.join("\n")}function A(e,t={},n=e=>e){return O(String(e).replace(/\r\n?/g,"\n").split("\n"),t,n)}function N({text:e,math:t,drawn:n},s){return(s.render?s.render(e):A(e,s,e=>e.replace(g,(e,n)=>t[Number(n)].raw))).replace(new RegExp("<p>\\s*(\\d+)\\s*</p>","g"),(e,s)=>t[Number(s)].display&&n[Number(s)]?`<div class="mv-md-block">${n[Number(s)]}</div>`:e).replace(g,(e,t)=>n[Number(t)]).replace(/<p>\s*<\/p>\n?/g,"")}function L(e,t={}){return N(k(e,t,t.variables||null),t)}const W=new WeakMap,q=new Set;function z(){for(const e of q)e.connected()&&!e.element.isConnected&&e.stop(!0)}function C(t,n,o={}){const c=void 0===o.variables?s:o.variables||null;z();const l=W.get(t);l&&l.stop(l.store!==c);let u=k(n,o,c,t);if(t.innerHTML=N(u,o),!c)return W.delete(t),t;c.own(t,u.defs);let d=t.isConnected;const p=n=>{const s=n.target,r=s&&s.dataset&&s.dataset.mvVar;if(void 0!==r&&t.contains(s)){if("text"===s.type)try{e(s.value,{blocks:o.blocks,scope:{}}),s.removeAttribute("aria-invalid")}catch{return void s.setAttribute("aria-invalid","true")}c.set(r,s.value)}},m=s=>{const l=s.target.closest?.("[data-node]"),d=l&&l.closest("[data-mv-md]");if(!d||!t.contains(d))return;const p=Number(d.dataset.mvMd),m=u.trees[p];if(!m)return;const $=Number(l.dataset.node),g=h(m,$)||[];let v=null;for(let e=g.length;e>=0&&!v;e--){const t=f(m,g.slice(0,e));t&&void 0!==t.from&&(v={node:t,depth:e})}const b=v&&c.definitions().get(v.node.from);if(b&&b.input&&"textbox"===b.input.kind){s.preventDefault();const n=b.name,u=t.ownerDocument.querySelector(`[data-mv-var="${CSS.escape(n)}"]`),d=o.editable&&o.editable.toText;if(!d)return void(u&&(u.focus(),u.select()));const p=t=>e(t),h=f(m,g);return void r(l,d(i(h,p)),{label:`Edit ${n}`,parse:t=>e(t,{blocks:o.blocks}),commit:e=>{const t=a(i(v.node,p),g.slice(v.depth),e);c.set(n,d(t))}})}const x=b&&u.defs.get(b.name),y=x&&x.key===b.key&&void 0!==x.start?{...u.math[p],start:x.start,end:x.end,raw:n.slice(x.start,x.end)}:u.math[p];t.dispatchEvent(new CustomEvent("inlinemath:select",{bubbles:!0,detail:{index:p,start:y.start,end:y.end,raw:y.raw,source:y.source,id:$,path:g}}))};t.addEventListener("input",p),t.addEventListener("change",p),t.addEventListener("dblclick",m);const $=c.subscribe(e=>{e!==t&&(t.isConnected?(d=!0,z(),(()=>{const e=k(n,o,c,t);e.drawn.forEach((n,s)=>{if(n===u.drawn[s]||e.inputs.has(s))return;const r=t.querySelector(`[data-mv-md="${s}"]`);r&&(r.outerHTML=n)});for(const n of t.querySelectorAll("[data-mv-var], [data-mv-out]")){const t=e.defs.get(n.dataset.mvVar??n.dataset.mvOut);if(!t)continue;const s=c.textOf(t);void 0!==n.dataset.mvOut?n.textContent=s:n.value!==s&&n!==n.ownerDocument?.activeElement&&(n.value=s)}u=e})()):d&&g(!0))});function g(e){$(),t.removeEventListener("input",p),t.removeEventListener("change",p),t.removeEventListener("dblclick",m),q.delete(v),W.delete(t),e&&c.own(t,null)}const v={element:t,store:c,stop:g,connected:()=>d};return q.add(v),W.set(t,v),t}export{v as extractMath,L as markdownToHTML,A as renderMarkdown,C as renderMarkdownInto,s as variables};
package/dist/markdown.js CHANGED
@@ -1,2 +1,2 @@
1
- /* Inline Math 0.1.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
- import{a as e}from"./chunks/html-DWtfytjA.js";import{a as t}from"./chunks/calculate-BX9lJ7Ff.js";import"./chunks/lite-Bj_L6w2g.js";const n=new RegExp("(\\d+)","g");function s(e,{delimiters:t=["$$","$","\\[","\\("]}={}){const n=[];let s="",i=0;const r=String(e),l=e=>0===e||"\n"===r[e-1],o=(e,t,s)=>(n.push({source:e,display:t,raw:s}),`${n.length-1}`);for(;i<r.length;){if(l(i)){const e=/^( {0,3})(`{3,}|~{3,})([^\n]*)\n?/.exec(r.slice(i));if(e){const t=e[2],n=new RegExp(`\\n {0,3}${"`"===t[0]?"`":"~"}{${t.length},}[ \\t]*(?=\\n|$)`),c=r.slice(i+e[0].length),l=n.exec(`\n${c}`),a=l?c.slice(0,Math.max(0,l.index-1)):c,p=e[0].length+(l?l.index-1+l[0].length:c.length),h=r.slice(i,i+p);"math"===e[3].trim()?s+=o(a,!0,h):s+=h,i+=p;continue}}const e=r[i];if("`"===e){const e=/^`+/.exec(r.slice(i))[0],t=r.indexOf(e,i+e.length);if(t>-1){s+=r.slice(i,t+e.length),i=t+e.length;continue}s+=e,i+=e.length;continue}if("\\"===e){const e=r.slice(i,i+2),n="\\["===e?"\\]":"\\("===e?"\\)":null;if(n&&t.includes(e)){const t=r.indexOf(n,i+2);if(t>-1){s+=o(r.slice(i+2,t),"\\["===e,r.slice(i,t+2)),i=t+2;continue}}s+=e,i+=2;continue}if("$"===e)if("$"===r[i+1]&&t.includes("$$")){const e=r.indexOf("$$",i+2);if(e>-1){s+=o(r.slice(i+2,e),!0,r.slice(i,e+2)),i=e+2;continue}}else if(t.includes("$")&&r[i+1]&&!/\s/.test(r[i+1])){const e=c(r,i+1);if(e>-1){s+=o(r.slice(i+1,e),!1,r.slice(i,e+1)),i=e+1;continue}}s+=e,i++}return{text:s,math:n}}function c(e,t){for(let n=t;n<e.length;n++){const t=e[n];if("\\"!==t){if("`"===t)return-1;if("\n"===t&&/^\n[ \t]*\n/.test(e.slice(n)))return-1;if("$"===t&&!/\s/.test(e[n-1])&&!/\d/.test(e[n+1]??"")&&"$"!==e[n+1])return n}else n++}return-1}const i=e=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),r=/^\s*([A-Za-zͰ-Ͽ](?:_\{?\w+\}?)?)\s*(?::=|=)\s*([^=<>≤≥]+)$/;function l(n,s,c){const{source:l,display:o}=n;try{const n=e(l,{...s.math,display:o,blocks:s.blocks,scope:c});return!1!==s.define&&function(e,n){const s=r.exec(e);if(!s||/\{\{/.test(s[2]))return;try{const e=t(s[2],n);"number"!=typeof e.value&&"bigint"!=typeof e.value||(n[s[1].replace(/[{}]/g,"")]=e.exact?e.text:e.value)}catch{}}(l,c),o?`<span class="mv-md-display">${n}</span>`:`<span class="mv-md-math">${n}</span>`}catch(e){return`<span class="mv-md-error" title="${i(e.message)}"><code>${i(n.raw)}</code></span>`}}const o=/^(https?:|mailto:|#|\/|\.{0,2}\/|[^:]*$)/i,a=e=>o.test(e.trim())?i(e.trim()):"#";function p(e,t,n){const s=[],c=e=>(s.push(e),`${s.length-1}`);let r=e;return r=r.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g,(e,t,s)=>c(`<code>${i(n(s.trim()))}</code>`)),r=r.replace(/\\([\\`*_{}[\]()#+\-.!|~$<>"'])/g,(e,t)=>c(i(t))),r=r.replace(/<((?:https?:\/\/|mailto:)[^\s>]+)>/g,(e,t)=>c(`<a href="${a(t)}">${i(t)}</a>`)),t.html&&(r=r.replace(/<\/?[A-Za-z][^>]*>/g,e=>c(e))),r=r.replace(/!\[([^\]]*)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,t,n,s)=>c(`<img src="${a(n)}" alt="${i(t)}"${s?` title="${i(s)}"`:""}>`)),r=r.replace(/\[([^\]]+)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,s,r,l)=>c(`<a href="${a(r)}"${l?` title="${i(l)}"`:""}>${p(s,t,n)}</a>`)),r=i(r),r=r.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g,"<strong>$2</strong>"),r=r.replace(/(^|[^*\w])\*(?=\S)([\s\S]*?\S)\*(?!\*)/g,"$1<em>$2</em>"),r=r.replace(/(^|[^_\w])_(?=\S)([\s\S]*?\S)_(?![_\w])/g,"$1<em>$2</em>"),r=r.replace(/~~(?=\S)([\s\S]*?\S)~~/g,"<del>$1</del>"),r=r.replace(/( {2,}|\\)\n/g,"<br>\n"),r.replace(/(\d+)/g,(e,t)=>s[Number(t)])}const h=/^( {0,3})(#{1,6}\s|>|```|~~~|([-*+]|\d{1,9}[.)])\s|(\*[ \t]*){3,}$|(-[ \t]*){3,}$|(_[ \t]*){3,}$)/,u=/^( {0,3})([-*+]|\d{1,9}[.)])(\s+|$)/,$=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;function g(e,t,n){const s=[];let c=0;const r=e=>p(e,t,n);for(;c<e.length;){const l=e[c];if(!l.trim()){c++;continue}const o=/^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)/.exec(l);if(o){const t=[];for(c++;c<e.length&&!new RegExp(`^ {0,3}${"`"===o[1][0]?"`":"~"}{${o[1].length},}\\s*$`).test(e[c]);)t.push(e[c++]);c++;const r=o[2]?` class="language-${i(o[2])}"`:"";s.push(`<pre><code${r}>${i(n(t.join("\n")))}</code></pre>`);continue}const a=/^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/.exec(l);if(a){s.push(`<h${a[1].length}>${r(a[2])}</h${a[1].length}>`),c++;continue}if(/^ {0,3}((\*[ \t]*){3,}|(-[ \t]*){3,}|(_[ \t]*){3,})$/.test(l)){s.push("<hr>"),c++;continue}if(/^ {0,3}>/.test(l)){const i=[];for(;c<e.length&&e[c].trim()&&(/^ {0,3}>/.test(e[c])||i.length);)i.push(e[c].replace(/^ {0,3}> ?/,"")),c++;s.push(`<blockquote>\n${g(i,t,n)}\n</blockquote>`);continue}const p=u.exec(l);if(p){const i=/\d/.test(p[2]),r=i?parseInt(p[2],10):1,l=[];let o=!1;for(;c<e.length;){const t=u.exec(e[c]);if(!t||/\d/.test(t[2])!==i)break;const n=t[0].length,s=[e[c].slice(n)];for(c++;c<e.length;){const t=e[c];if(!t.trim()){const t=e[c+1];if(void 0!==t&&t.startsWith(" ".repeat(Math.min(n,4)))&&t.trim()){s.push(""),o=!0,c++;continue}void 0!==t&&u.test(t)&&(o=!0);break}if(t.startsWith(" ".repeat(Math.min(n,4))))s.push(t.slice(Math.min(n,4)));else{if(u.test(t)||h.test(t))break;s.push(t)}c++}for(l.push(s);c<e.length&&!e[c].trim()&&void 0!==e[c+1]&&u.test(e[c+1]);)c++}const a=i?"ol":"ul",$=l.map(e=>{let s=g(e,t,n);return o||(s=s.replace(/^<p>([\s\S]*?)<\/p>/,"$1").replace(/\n<p>([\s\S]*?)<\/p>/g,"\n$1")),`<li>${s}</li>`});s.push(`<${a}${i&&1!==r?` start="${r}"`:""}>\n${$.join("\n")}\n</${a}>`);continue}if(l.includes("|")&&void 0!==e[c+1]&&$.test(e[c+1])&&e[c+1].includes("-")){const t=e=>e.trim().replace(/^\|/,"").replace(/\|$/,"").split(/(?<!\\)\|/).map(e=>e.trim()),n=t(e[c+1]).map(e=>e.startsWith(":")&&e.endsWith(":")?"center":e.endsWith(":")?"right":e.startsWith(":")?"left":""),i=(e,t,s)=>`<${e}${n[s]?` style="text-align:${n[s]}"`:""}>${r(t)}</${e}>`,o=t(l);c+=2;const a=[];for(;c<e.length&&e[c].trim()&&e[c].includes("|");)a.push(t(e[c++]));s.push(`<table>\n<thead><tr>${o.map((e,t)=>i("th",e,t)).join("")}</tr></thead>\n<tbody>\n${a.map(e=>`<tr>${o.map((t,n)=>i("td",e[n]??"",n)).join("")}</tr>`).join("\n")}\n</tbody>\n</table>`);continue}if(/^( {4}|\t)/.test(l)){const t=[];for(;c<e.length&&(/^( {4}|\t)/.test(e[c])||!e[c].trim());)t.push(e[c++].replace(/^( {4}|\t)/,""));for(;t.length&&!t[t.length-1].trim();)t.pop();s.push(`<pre><code>${i(n(t.join("\n")))}</code></pre>`);continue}const f=[];for(;c<e.length&&e[c].trim()&&(!f.length||!h.test(e[c])||/^ {0,3}[-=]+\s*$/.test(e[c]));){if(f.length&&/^ {0,3}(=+|-+)\s*$/.test(e[c])){const t="="===e[c].trim()[0]?1:2;s.push(`<h${t}>${r(f.join("\n"))}</h${t}>`),f.length=0,c++;break}f.push(e[c].replace(/^ {0,3}/,"")),c++}f.length&&s.push(`<p>${r(f.join("\n"))}</p>`)}return s.join("\n")}function f(e,t={},n=e=>e){return g(String(e).replace(/\r\n?/g,"\n").split("\n"),t,n)}function m(e,t={}){const{text:c,math:i}=s(e,t),r={...t.scope??{}},o=i.map(e=>l(e,t,r));return(t.render?t.render(c):f(c,t,e=>e.replace(n,(e,t)=>i[Number(t)].raw))).replace(new RegExp("<p>\\s*(\\d+)\\s*</p>","g"),(e,t)=>i[Number(t)].display?`<div class="mv-md-block">${o[Number(t)]}</div>`:e).replace(n,(e,t)=>o[Number(t)])}function d(e,t,n={}){return e.innerHTML=m(t,n),e}export{s as extractMath,m as markdownToHTML,f as renderMarkdown,d as renderMarkdownInto};
1
+ /* Inline Math 0.2.0 — MIT licence; includes mathzetta (and @mathzetta/cas, for the CAS) */
2
+ import{t as e,a as t}from"./chunks/html-CupluJSh.js";import{a as n}from"./chunks/calculate-Cz87Lt0N.js";import{v as s,o as r,r as a,u as i,s as o,E as l,b as u,n as d,i as p}from"./chunks/edit-M68aRA5o.js";export{V as Variables,c as createVariables}from"./chunks/edit-M68aRA5o.js";import{p as m,n as f,g as h,d as $}from"./chunks/lite-3Fb-OHbt.js";const g=new RegExp("(\\d+)","g");function v(e,{delimiters:t=["$$","$","\\[","\\("]}={}){const n=[];let s="",r=0;const a=String(e),i=e=>0===e||"\n"===a[e-1],o=(e,t,s)=>(n.push({source:e,display:t,raw:s,start:r,end:r+s.length}),`${n.length-1}`);for(;r<a.length;){if(i(r)){const e=/^( {0,3})(`{3,}|~{3,})([^\n]*)\n?/.exec(a.slice(r));if(e){const t=e[2],n=new RegExp(`\\n {0,3}${"`"===t[0]?"`":"~"}{${t.length},}[ \\t]*(?=\\n|$)`),i=a.slice(r+e[0].length),c=n.exec(`\n${i}`),l=c?i.slice(0,Math.max(0,c.index-1)):i,u=e[0].length+(c?c.index-1+c[0].length:i.length),d=a.slice(r,r+u);"math"===e[3].trim()?s+=o(l,!0,d):s+=d,r+=u;continue}}const e=a[r];if("`"===e){const e=/^`+/.exec(a.slice(r))[0],t=a.indexOf(e,r+e.length);if(t>-1){s+=a.slice(r,t+e.length),r=t+e.length;continue}s+=e,r+=e.length;continue}if("\\"===e){const e=a.slice(r,r+2),n="\\["===e?"\\]":"\\("===e?"\\)":null;if(n&&t.includes(e)){const t=a.indexOf(n,r+2);if(t>-1){s+=o(a.slice(r+2,t),"\\["===e,a.slice(r,t+2)),r=t+2;continue}}s+=e,r+=2;continue}if("$"===e)if("$"===a[r+1]&&t.includes("$$")){const e=a.indexOf("$$",r+2);if(e>-1){s+=o(a.slice(r+2,e),!0,a.slice(r,e+2)),r=e+2;continue}}else if(t.includes("$")&&a[r+1]&&!/\s/.test(a[r+1])){const e=b(a,r+1);if(e>-1){s+=o(a.slice(r+1,e),!1,a.slice(r,e+1)),r=e+1;continue}}s+=e,r++}return{text:s,math:n}}function b(e,t){for(let n=t;n<e.length;n++){const t=e[n];if("\\"!==t){if("`"===t)return-1;if("\n"===t&&/^\n[ \t]*\n/.test(e.slice(n)))return-1;if("$"===t&&!/\s/.test(e[n-1])&&!/\d/.test(e[n+1]??"")&&"$"!==e[n+1])return n}else n++}return-1}const x=e=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),y=/^\s*([A-Za-z\u0370-\u03ff](?:_\{?\w+\}?)?)\s*=\s*([^=<>≤≥]+)$/;function k(s,r,a,i){const{text:c,math:m}=v(s,r),f=a?a.definitions(i):new Map,g=e=>a?a.textOf(e):e.text,b={...r.scope??{}},k={},w=new Map,S=new Set,j=[],E=()=>{for(const e of Object.keys(k))delete k[e];Object.assign(k,b);for(const e of f.keys())delete k[e];let e=[...f.values()];for(;e.length;){const t=e.filter(e=>{try{const t=n(g(e),k);if("number"==typeof t.value||"bigint"==typeof t.value||Array.isArray(t.value))return k[e.name]=t.exact?t.text:t.value,!1}catch{}return!0});if(t.length===e.length)break;e=t}const t={},s=_({});for(const e of f.keys()){const n=s(e);if(n)try{t[e]=h(o(n,t=>t===e?null:s(t)).tree)}catch{}}k[l]=t},_=t=>{const n=new Map;return s=>{const r=f.get(s);if(!r)return null;if(!n.has(s)){let a=null;try{a=e(g(r),t)}catch{}n.set(s,a)}return n.get(s)}};E();const M=m.map((s,a)=>{const{source:i,display:c}=s,l=c?"mv-md-display":"mv-md-math",m=(e,t="")=>`<span class="${l}${t}" data-mv-md="${a}">${e}</span>`;try{const l={...r.math,display:c,blocks:r.blocks,scope:k},h=function(t){let n=t.trim();const s=n.startsWith("_");s&&(n=n.slice(1).trimStart());let r=n.indexOf(":="),a=2;r<0&&s&&(r=n.search(/(?<![<>!≤≥=])=(?!=)/),a=1);const i=s?{error:"a hidden $_…$ saves a variable: $_A := 2+2$"}:null;if(r<=0)return i;const o=n.slice(0,r).trim(),c=n.slice(r+a).trim();let l=null;try{l=/\{\{/.test(o)?null:d(e(o))}catch{}return null!==l&&c?{hidden:s,lhs:o,name:l,rhs:c,input:p(c)}:i}(i);if(h&&h.error)throw new Error(h.error);if(h){const{name:n,input:r,hidden:i}=h,c=r?{name:n,text:r.default,input:r,key:`input:${JSON.stringify(r)}`}:{name:n,text:h.rhs,input:null,key:`expr:${h.rhs}`,start:s.start,end:s.end};let d="";if(r){const e=function(e,t){const{input:n,name:s}=e,r=`class="mv-md-input mv-md-${n.kind}" data-mv-var="${x(s)}" aria-label="${x(s)}"`;if("dropdown"===n.kind)return`<select ${r}>${n.options.map(e=>`<option value="${x(e)}"${e===t?" selected":""}>${x(e)}</option>`).join("")}</select>`;return"slider"===n.kind?`<input type="range" ${r} min="${n.min}" max="${n.max}" step="${n.step}" value="${x(t)}"><output class="mv-md-output" data-mv-out="${x(s)}">${x(t)}</output>`:`<input type="text" ${r} value="${x(t)}" size="${Math.max(4,t.length+1)}" spellcheck="false" autocomplete="off">`}(c,g(c));d=i?e:`${t(h.lhs,l)}<span class="mv-md-assign" aria-hidden="true">≔</span>${e}`,S.add(a)}else{const n=o(e(h.rhs,l),_(l)).tree;if(!i){const s=$({type:"op",op:"=",args:[u(e(h.lhs)),void 0===n.id?n:u(n)]});j[a]=s,d=function(e){return e.replace('<span class="mv-rel">=</span>','<span class="mv-rel mv-rel-def">≔</span>').replace(/(aria-label="[^"]*?) equals /,"$1 is defined as ").replace("<mo>=</mo>","<mo>≔</mo>")}(t(s,l))}}return f.delete(n),f.set(n,c),w.set(n,c),E(),i&&!r?"":m(d,r?" mv-md-input-def":"")}const{tree:v,changed:M}=o(e(i,l),_(l));M&&$(v),j[a]=v;const O=t(v,l);return!M&&!1!==r.define&&function(e,t,s){const r=y.exec(e);if(!r||/\{\{/.test(r[2]))return!1;const a=r[1].replace(/[{}]/g,"");if(s.has(a))return!1;try{const e=n(r[2],t);if("number"==typeof e.value||"bigint"==typeof e.value||Array.isArray(e.value))return t[a]=e.exact?e.text:e.value,!0}catch{}return!1}(i,b,f)&&E(),m(O)}catch(e){return`<span class="mv-md-error" data-mv-md="${a}" title="${x(e.message)}"><code>${x(s.raw)}</code></span>`}});return{text:c,math:m,drawn:M,defs:w,inputs:S,trees:j}}const w=/^(https?:|mailto:|#|\/|\.{0,2}\/|[^:]*$)/i,S=e=>w.test(e.trim())?x(e.trim()):"#";function j(e,t,n){const s=[],r=e=>(s.push(e),`${s.length-1}`);let a=e;return a=a.replace(/(`+)([\s\S]*?[^`])\1(?!`)/g,(e,t,s)=>r(`<code>${x(n(s.trim()))}</code>`)),a=a.replace(/\\([\\`*_{}[\]()#+\-.!|~$<>"'])/g,(e,t)=>r(x(t))),a=a.replace(/<((?:https?:\/\/|mailto:)[^\s>]+)>/g,(e,t)=>r(`<a href="${S(t)}">${x(t)}</a>`)),t.html&&(a=a.replace(/<\/?[A-Za-z][^>]*>/g,e=>r(e))),a=a.replace(/!\[([^\]]*)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,t,n,s)=>r(`<img src="${S(n)}" alt="${x(t)}"${s?` title="${x(s)}"`:""}>`)),a=a.replace(/\[([^\]]+)\]\(\s*((?:[^\s()]|\([^\s()]*\))+)(?:\s+"([^"]*)")?\s*\)/g,(e,s,a,i)=>r(`<a href="${S(a)}"${i?` title="${x(i)}"`:""}>${j(s,t,n)}</a>`)),a=x(a),a=a.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g,"<strong>$2</strong>"),a=a.replace(/(^|[^*\w])\*(?=\S)([\s\S]*?\S)\*(?!\*)/g,"$1<em>$2</em>"),a=a.replace(/(^|[^_\w])_(?=\S)([\s\S]*?\S)_(?![_\w])/g,"$1<em>$2</em>"),a=a.replace(/~~(?=\S)([\s\S]*?\S)~~/g,"<del>$1</del>"),a=a.replace(/( {2,}|\\)\n/g,"<br>\n"),a.replace(/(\d+)/g,(e,t)=>s[Number(t)])}const E=/^( {0,3})(#{1,6}\s|>|```|~~~|([-*+]|\d{1,9}[.)])\s|(\*[ \t]*){3,}$|(-[ \t]*){3,}$|(_[ \t]*){3,}$)/,_=/^( {0,3})([-*+]|\d{1,9}[.)])(\s+|$)/,M=/^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;function O(e,t,n){const s=[];let r=0;const a=e=>j(e,t,n);for(;r<e.length;){const i=e[r];if(!i.trim()){r++;continue}const o=/^ {0,3}(`{3,}|~{3,})\s*([^\s`]*)/.exec(i);if(o){const t=[];for(r++;r<e.length&&!new RegExp(`^ {0,3}${"`"===o[1][0]?"`":"~"}{${o[1].length},}\\s*$`).test(e[r]);)t.push(e[r++]);r++;const a=o[2]?` class="language-${x(o[2])}"`:"";s.push(`<pre><code${a}>${x(n(t.join("\n")))}</code></pre>`);continue}const c=/^ {0,3}(#{1,6})\s+(.*?)\s*#*\s*$/.exec(i);if(c){s.push(`<h${c[1].length}>${a(c[2])}</h${c[1].length}>`),r++;continue}if(/^ {0,3}((\*[ \t]*){3,}|(-[ \t]*){3,}|(_[ \t]*){3,})$/.test(i)){s.push("<hr>"),r++;continue}if(/^ {0,3}>/.test(i)){const a=[];for(;r<e.length&&e[r].trim()&&(/^ {0,3}>/.test(e[r])||a.length);)a.push(e[r].replace(/^ {0,3}> ?/,"")),r++;s.push(`<blockquote>\n${O(a,t,n)}\n</blockquote>`);continue}const l=_.exec(i);if(l){const a=/\d/.test(l[2]),i=a?parseInt(l[2],10):1,o=[];let c=!1;for(;r<e.length;){const t=_.exec(e[r]);if(!t||/\d/.test(t[2])!==a)break;const n=t[0].length,s=[e[r].slice(n)];for(r++;r<e.length;){const t=e[r];if(!t.trim()){const t=e[r+1];if(void 0!==t&&t.startsWith(" ".repeat(Math.min(n,4)))&&t.trim()){s.push(""),c=!0,r++;continue}void 0!==t&&_.test(t)&&(c=!0);break}if(t.startsWith(" ".repeat(Math.min(n,4))))s.push(t.slice(Math.min(n,4)));else{if(_.test(t)||E.test(t))break;s.push(t)}r++}for(o.push(s);r<e.length&&!e[r].trim()&&void 0!==e[r+1]&&_.test(e[r+1]);)r++}const u=a?"ol":"ul",d=o.map(e=>{let s=O(e,t,n);return c||(s=s.replace(/^<p>([\s\S]*?)<\/p>/,"$1").replace(/\n<p>([\s\S]*?)<\/p>/g,"\n$1")),`<li>${s}</li>`});s.push(`<${u}${a&&1!==i?` start="${i}"`:""}>\n${d.join("\n")}\n</${u}>`);continue}if(i.includes("|")&&void 0!==e[r+1]&&M.test(e[r+1])&&e[r+1].includes("-")){const t=e=>e.trim().replace(/^\|/,"").replace(/\|$/,"").split(/(?<!\\)\|/).map(e=>e.trim()),n=t(e[r+1]).map(e=>e.startsWith(":")&&e.endsWith(":")?"center":e.endsWith(":")?"right":e.startsWith(":")?"left":""),o=(e,t,s)=>`<${e}${n[s]?` style="text-align:${n[s]}"`:""}>${a(t)}</${e}>`,c=t(i);r+=2;const l=[];for(;r<e.length&&e[r].trim()&&e[r].includes("|");)l.push(t(e[r++]));s.push(`<table>\n<thead><tr>${c.map((e,t)=>o("th",e,t)).join("")}</tr></thead>\n<tbody>\n${l.map(e=>`<tr>${c.map((t,n)=>o("td",e[n]??"",n)).join("")}</tr>`).join("\n")}\n</tbody>\n</table>`);continue}if(/^( {4}|\t)/.test(i)){const t=[];for(;r<e.length&&(/^( {4}|\t)/.test(e[r])||!e[r].trim());)t.push(e[r++].replace(/^( {4}|\t)/,""));for(;t.length&&!t[t.length-1].trim();)t.pop();s.push(`<pre><code>${x(n(t.join("\n")))}</code></pre>`);continue}const u=[];for(;r<e.length&&e[r].trim()&&(!u.length||!E.test(e[r])||/^ {0,3}[-=]+\s*$/.test(e[r]));){if(u.length&&/^ {0,3}(=+|-+)\s*$/.test(e[r])){const t="="===e[r].trim()[0]?1:2;s.push(`<h${t}>${a(u.join("\n"))}</h${t}>`),u.length=0,r++;break}u.push(e[r].replace(/^ {0,3}/,"")),r++}u.length&&s.push(`<p>${a(u.join("\n"))}</p>`)}return s.join("\n")}function A(e,t={},n=e=>e){return O(String(e).replace(/\r\n?/g,"\n").split("\n"),t,n)}function N({text:e,math:t,drawn:n},s){return(s.render?s.render(e):A(e,s,e=>e.replace(g,(e,n)=>t[Number(n)].raw))).replace(new RegExp("<p>\\s*(\\d+)\\s*</p>","g"),(e,s)=>t[Number(s)].display&&n[Number(s)]?`<div class="mv-md-block">${n[Number(s)]}</div>`:e).replace(g,(e,t)=>n[Number(t)]).replace(/<p>\s*<\/p>\n?/g,"")}function L(e,t={}){return N(k(e,t,t.variables||null),t)}const W=new WeakMap,q=new Set;function C(){for(const e of q)e.connected()&&!e.element.isConnected&&e.stop(!0)}function R(t,n,o={}){const c=void 0===o.variables?s:o.variables||null;C();const l=W.get(t);l&&l.stop(l.store!==c);let u=k(n,o,c,t);if(t.innerHTML=N(u,o),!c)return W.delete(t),t;c.own(t,u.defs);let d=t.isConnected;const p=n=>{const s=n.target,r=s&&s.dataset&&s.dataset.mvVar;if(void 0!==r&&t.contains(s)){if("text"===s.type)try{e(s.value,{blocks:o.blocks,scope:{}}),s.removeAttribute("aria-invalid")}catch{return void s.setAttribute("aria-invalid","true")}c.set(r,s.value)}},h=s=>{const l=s.target.closest?.("[data-node]"),d=l&&l.closest("[data-mv-md]");if(!d||!t.contains(d))return;const p=Number(d.dataset.mvMd),h=u.trees[p];if(!h)return;const $=Number(l.dataset.node),g=m(h,$)||[];let v=null;for(let e=g.length;e>=0&&!v;e--){const t=f(h,g.slice(0,e));t&&void 0!==t.from&&(v={node:t,depth:e})}const b=v&&c.definitions().get(v.node.from);if(b&&b.input&&"textbox"===b.input.kind){s.preventDefault();const n=b.name,u=t.ownerDocument.querySelector(`[data-mv-var="${CSS.escape(n)}"]`),d=o.editable&&o.editable.toText;if(!d)return void(u&&(u.focus(),u.select()));const p=t=>e(t),m=f(h,g);return void r(l,d(i(m,p)),{label:`Edit ${n}`,parse:t=>e(t,{blocks:o.blocks}),commit:e=>{const t=a(i(v.node,p),g.slice(v.depth),e);c.set(n,d(t))}})}const x=b&&u.defs.get(b.name),y=x&&x.key===b.key&&void 0!==x.start?{...u.math[p],start:x.start,end:x.end,raw:n.slice(x.start,x.end)}:u.math[p];t.dispatchEvent(new CustomEvent("inlinemath:select",{bubbles:!0,detail:{index:p,start:y.start,end:y.end,raw:y.raw,source:y.source,id:$,path:g}}))};t.addEventListener("input",p),t.addEventListener("change",p),t.addEventListener("dblclick",h);const $=c.subscribe(e=>{e!==t&&(t.isConnected?(d=!0,C(),(()=>{const e=k(n,o,c,t);e.drawn.forEach((n,s)=>{if(n===u.drawn[s]||e.inputs.has(s))return;const r=t.querySelector(`[data-mv-md="${s}"]`);r&&(r.outerHTML=n)});for(const n of t.querySelectorAll("[data-mv-var], [data-mv-out]")){const t=e.defs.get(n.dataset.mvVar??n.dataset.mvOut);if(!t)continue;const s=c.textOf(t);void 0!==n.dataset.mvOut?n.textContent=s:n.value!==s&&n!==n.ownerDocument?.activeElement&&(n.value=s)}u=e})()):d&&g(!0))});function g(e){$(),t.removeEventListener("input",p),t.removeEventListener("change",p),t.removeEventListener("dblclick",h),q.delete(v),W.delete(t),e&&c.own(t,null)}const v={element:t,store:c,stop:g,connected:()=>d};return q.add(v),W.set(t,v),t}export{v as extractMath,L as markdownToHTML,A as renderMarkdown,R as renderMarkdownInto,s as variables};
package/inlinemath.css CHANGED
@@ -98,19 +98,42 @@ span.mv-delim { width: 0.16em; border: 0 solid currentColor; box-sizing: border-
98
98
  /* ---- piecewise ------------------------------------------------------------- */
99
99
  .mv-cases { display: inline-grid; grid-template-columns: auto auto; column-gap: 1em; row-gap: 0.25em; align-items: baseline; text-align: left; margin: 0 0.1em; }
100
100
  .mv-case-value, .mv-case-cond { display: block; }
101
+ .mv-matrix { display: inline-grid; column-gap: 0.8em; row-gap: 0.3em; align-items: baseline; justify-items: center; margin: 0 0.15em; }
102
+ .mv-matrix-row { display: contents; }
103
+ .mv-matrix-cell { display: block; }
101
104
 
102
105
  /* ---- decorations: \boxed \cancel \overline \vec … --------------------------- */
103
106
  .mv-boxed { display: inline-block; border: 0.06em solid currentColor; padding: 0.16em 0.2em; }
104
107
  .mv-over { display: inline-block; border-top: 0.06em solid currentColor; padding-top: 0.08em; }
105
108
  .mv-underline { display: inline-block; border-bottom: 0.06em solid currentColor; padding-bottom: 0.06em; }
106
- .mv-cancel { display: inline-block; background: linear-gradient(to top right, transparent calc(50% - 0.04em), currentColor calc(50% - 0.03em), currentColor calc(50% + 0.03em), transparent calc(50% + 0.04em)); }
109
+ .mv-cancel { display: inline-block; position: relative; }
110
+ .mv-cancel-line { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
107
111
  .mv-accent { display: inline-block; position: relative; padding-top: 0.3em; }
108
112
  .mv-accent-mark { position: absolute; top: 0; left: 0; right: 0; text-align: center; font-size: 0.7em; line-height: 0.6; }
109
113
 
110
114
  /* ---- interaction -------------------------------------------------------------- */
111
115
  .mv-hl { background: var(--mv-hl, rgba(255, 200, 0, 0.35)); box-shadow: 0 0 0 0.08em var(--mv-hl, rgba(255, 200, 0, 0.35)); }
116
+ /* Windows high contrast (and other forced colours) drops backgrounds and shadows: outline a highlight instead */
117
+ @media (forced-colors: active) {
118
+ .mv-hl { outline: 0.08em solid Highlight; outline-offset: 0.04em; }
119
+ }
112
120
  .mv-clickable .mv-n { cursor: pointer; }
121
+ .mv-editable .mv-n:hover { outline: 1px dashed rgba(0, 0, 0, 0.25); }
122
+ /* the box editing a part in place (view.editable, or a Markdown textbox variable) */
123
+ .mv-edit { transform: translateY(-50%); z-index: 1000; box-sizing: content-box; padding: 0.1em 0.35em; font-family: inherit;
124
+ border: 1px solid #1a5fb4; border-radius: 0.25em; background: #fff; color: #111; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); }
125
+ .mv-edit[aria-invalid="true"] { border-color: #c33; outline: 2px solid #c33; }
126
+ /* the live region a view announces steps through ({ announce: true }): read, never seen */
127
+ .mv-sr-only, .mv-sr-math { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
113
128
  .mv-clickable .mv-n:hover { outline: 1px dashed rgba(0, 0, 0, 0.25); }
129
+ /* the keyboard: the expression is one tab stop, and the part the arrow keys are on is ringed */
130
+ .mv[tabindex]:focus-visible { outline: 0.1em solid #1a5fb4; outline-offset: 0.12em; }
131
+ .mv[tabindex]:focus .mv-focus { outline: 0.1em solid #1a5fb4; outline-offset: 0.06em; border-radius: 0.1em; }
132
+ /* while a part is ringed, the whole expression's ring steps back so the part is the one that stands out */
133
+ .mv[tabindex]:focus-visible:has(.mv-focus) { outline: 1px dashed #1a5fb4; }
134
+ @media (forced-colors: active) {
135
+ .mv[tabindex]:focus-visible, .mv[tabindex]:focus .mv-focus { outline-color: Highlight; }
136
+ }
114
137
 
115
138
  /* ---- steps (stepsHTML from '@mathzetta/inlinemath/cas') ---------------------------------------- */
116
139
  .mv-steps { margin: 0; padding-left: 1.6em; }
@@ -125,3 +148,11 @@ span.mv-delim { width: 0.16em; border: 0 solid currentColor; box-sizing: border-
125
148
  .mv-md-display { display: block; text-align: center; margin: 0.6em 0; overflow-x: auto; }
126
149
  .mv-md-block { margin: 0.8em 0; }
127
150
  .mv-md-error code { background: rgba(200, 60, 0, 0.08); color: #a33; border-bottom: 1px dotted #a33; }
151
+ /* saved variables: $A := 2+2$ draws A ≔ 2 + 2; $A := [[…]]$ an input that sets A */
152
+ .mv-md-input-def { display: inline-flex; align-items: center; gap: 0.3em; }
153
+ .mv-md-assign { margin: 0 0.1em; }
154
+ .mv-md-input { font: inherit; font-size: 0.9em; }
155
+ input.mv-md-textbox { font-family: inherit; padding: 0.05em 0.3em; field-sizing: content; min-width: 3ch; }
156
+ input.mv-md-textbox[aria-invalid="true"] { outline: 2px solid #c33; outline-offset: 1px; }
157
+ input.mv-md-slider { vertical-align: middle; }
158
+ .mv-md-output { font-variant-numeric: tabular-nums; min-width: 2ch; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathzetta/inlinemath",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Draw calculus-level maths as HTML and CSS from anything MathZetta reads, with every part addressable.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -47,12 +47,12 @@
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@mathzetta/cas": "^0.1.0",
50
- "mathzetta": "^1.0.0"
50
+ "mathzetta": "^1.1.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@rollup/plugin-terser": "^0.4.4",
54
54
  "@mathzetta/cas": "^0.1.0",
55
- "mathzetta": "^1.0.0",
55
+ "mathzetta": "^1.1.0",
56
56
  "rollup": "^4.65.0"
57
57
  },
58
58
  "license": "MIT",
package/src/cas.js CHANGED
@@ -9,9 +9,10 @@
9
9
  * toHTML('{{solve(x^2 - 5x + 6 = 0, x)}}', { blocks: casBlocks() }) // x = 2 or x = 3
10
10
  * stepsHTML(mathCAS().diff('x^2 sin x', 'x', { steps: 'brief' }).steps)
11
11
  *
12
- * Block functions: simplify, expand, factor, cancel, diff(f, x[, n]), integrate(f, x[, a, b]),
13
- * solve(equation, x), approx(expr[, digits]); d/dx … and ∫ … without limits go to the CAS
14
- * too. Everything else is MathZetta's calculator (calculateBlock), as without this.
12
+ * Block functions: simplify, expand, factor, cancel, diff(f, x[, n]) (or der, derivative,
13
+ * differentiate), integrate(f, x[, a, b]) (or integral), solve(equation, x), approx(expr[, digits]);
14
+ * d/dx … and ∫ … without limits go to the CAS too. In Markdown, saved variables ($_f := x^3$)
15
+ * stand for their expressions: {{diff(f, x)}}. Everything else is MathZetta's calculator (calculateBlock), as without this.
15
16
  *
16
17
  * Needs @mathzetta/cas (a peer dependency, like mathzetta).
17
18
  */
@@ -24,6 +25,8 @@ import { calculateBlock } from './calculator.js';
24
25
  import { toText } from 'mathzetta/text';
25
26
  import { toHTML } from './html.js';
26
27
 
28
+ const EXPRESSIONS = Symbol.for('inlinemath.expressions'); // as variables.js
29
+
27
30
  const shared = new Map();
28
31
 
29
32
  /** How the complex functions are written for MathZetta: Re z, Im z, z̄ (an overline), arg z. */
@@ -69,13 +72,17 @@ export function mathCAS(options = {}) {
69
72
  }
70
73
 
71
74
  const CALLS = new Set(['simplify', 'expand', 'factor', 'cancel', 'diff', 'integrate', 'solve', 'approx']);
75
+ // other names people write for them
76
+ const ALIASES = { integral: 'integrate', differentiate: 'diff', approximate: 'approx', simp: 'simplify' };
72
77
 
73
78
  /**
74
79
  * A `{{…}}` handler: pass it as toHTML(input, { blocks: casBlocks() }).
75
- * @param {object} [options] { cas } a CAS of your own (mathCAS({ rules })), { steps } kept on the handler's `last`
80
+ * @param {object} [options] { cas } a CAS of your own (mathCAS({ rules })); { steps } how much working each call
81
+ * keeps (the CAS's steps option: true, 'brief', false), read from the handler's `last` after a block
76
82
  */
77
83
  export function casBlocks(options = {}) {
78
84
  const cas = options.cas ?? mathCAS();
85
+ const how = options.steps === undefined ? {} : { steps: options.steps };
79
86
  const handler = (text, scope) => {
80
87
  let tokens;
81
88
  try {
@@ -83,8 +90,11 @@ export function casBlocks(options = {}) {
83
90
  } catch {
84
91
  return calculateBlock(text, scope);
85
92
  }
93
+ // saved variables ($_f := x^3$ in Markdown) stand for their expressions: {{diff(f, x)}}
94
+ const saved = scope && scope[EXPRESSIONS];
95
+ if (saved) tokens = tokens.flatMap((t) => (Object.hasOwn(saved, t) ? saved[t] : [t]));
86
96
  const head = tokens[tokens.length - 1];
87
- const name = head?.startsWith('\\:') ? head.slice(2) : null;
97
+ const name = head?.startsWith('\\:') ? (ALIASES[head.slice(2)] ?? head.slice(2)) : null;
88
98
  const symbolic = name && CALLS.has(name);
89
99
  // d/dx …, ∫ … dx and ∫ from a to b … dx (MathZetta writes the limits as int's 3rd and 4th operands)
90
100
  const calculus = head === 'der' || head === 'int';
@@ -95,16 +105,16 @@ export function casBlocks(options = {}) {
95
105
  let r;
96
106
  try {
97
107
  switch (symbolic ? name : head === 'der' ? 'derivative' : 'antiderivative') {
98
- case 'simplify': r = cas.simplify(args[0]); break;
99
- case 'expand': r = cas.expand(args[0]); break;
100
- case 'factor': r = cas.factor(args[0]); break;
101
- case 'cancel': r = cas.cancel(args[0]); break;
102
- case 'diff': r = cas.diff(args[0], word(args[1]), { order: args[2] ? Number(args[2].s) : 1 }); break;
103
- case 'integrate': r = cas.integrate(args[0], word(args[1]), args[3] ? { from: args[2].s, to: args[3].s } : {}); break;
104
- case 'solve': r = cas.solve(args[0], word(args[1])); break;
105
- case 'approx': r = cas.approximate(args[0], { digits: args[1] ? Number(args[1].s) : 15 }); break;
106
- case 'derivative': r = cas.diff(node.args[0], node.args[1].s); break;
107
- default: r = cas.integrate(node); break;
108
+ case 'simplify': r = cas.simplify(args[0], how); break;
109
+ case 'expand': r = cas.expand(args[0], how); break;
110
+ case 'factor': r = cas.factor(args[0], how); break;
111
+ case 'cancel': r = cas.cancel(args[0], how); break;
112
+ case 'diff': r = cas.diff(args[0], word(args[1]), { ...how, order: args[2] ? Number(args[2].s) : 1 }); break;
113
+ case 'integrate': r = cas.integrate(args[0], word(args[1]), { ...how, ...(args[3] ? { from: args[2].s, to: args[3].s } : {}) }); break;
114
+ case 'solve': r = cas.solve(args[0], word(args[1]), how); break;
115
+ case 'approx': r = cas.approximate(args[0], { ...how, digits: args[1] ? Number(args[1].s) : 15 }); break;
116
+ case 'derivative': r = cas.diff(node.args[0], node.args[1].s, how); break;
117
+ default: r = cas.integrate(node, undefined, how); break;
108
118
  }
109
119
  } catch (error) {
110
120
  // no answer: leave it as written, as calculateBlock does
package/src/edit.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Editing a part of a drawn expression in place: a text box opens over the part, holding the
3
+ * part as text; Enter puts what was typed back into the expression, Escape (or leaving it with
4
+ * something that doesn't read) changes nothing. Used by View.editable() and by the Markdown
5
+ * pages' textbox variables.
6
+ *
7
+ * The part is written as text by MathZetta's toText, which the caller passes in (import it
8
+ * from 'mathzetta/text'): Inline Math's core imports only 'mathzetta', 'mathzetta/speech' and
9
+ * 'mathzetta/calculate', so a buildless page's import map stays the same.
10
+ */
11
+
12
+ import { bare } from './variables.js';
13
+
14
+ /** A copy of `tree` (ids dropped) with the node at `path` replaced by `part`. */
15
+ export function replaceAt(tree, path, part) {
16
+ const visit = (n, k) => (k === path.length
17
+ ? part
18
+ : { ...n, args: n.args.map((a, j) => (j === path[k] ? visit(a, k + 1) : a)) });
19
+ return bare(visit(tree, 0));
20
+ }
21
+
22
+ /**
23
+ * Open a text box over `element` holding `text`.
24
+ * @param {Element} element the drawn part
25
+ * @param {string} text what the box starts with
26
+ * @param {object} how
27
+ * @param {(text: string) => object} how.parse the typed text → a tree (throws if it doesn't read)
28
+ * @param {(tree: object, text: string) => void} how.commit called with the new part
29
+ * @param {() => void} [how.cancel] called when nothing changed
30
+ * @param {string} [how.label] the box's accessible name
31
+ * @returns {HTMLInputElement|null}
32
+ */
33
+ export function openEditor(element, text, { parse, commit, cancel, label = 'Edit' }) {
34
+ const doc = element.ownerDocument;
35
+ if (!doc || !doc.body) return null;
36
+ const view = doc.defaultView;
37
+ const r = element.getBoundingClientRect();
38
+ const input = doc.createElement('input');
39
+ input.type = 'text';
40
+ input.className = 'mv-edit';
41
+ input.value = text;
42
+ input.spellcheck = false;
43
+ input.autocomplete = 'off';
44
+ input.setAttribute('aria-label', label);
45
+ const fit = () => { input.style.width = `${Math.max(4, input.value.length + 2)}ch`; };
46
+ const font = view.getComputedStyle(element);
47
+ Object.assign(input.style, {
48
+ position: 'absolute',
49
+ left: `${r.left + view.scrollX}px`,
50
+ top: `${r.top + view.scrollY + r.height / 2}px`,
51
+ minWidth: `${Math.max(r.width, 32)}px`,
52
+ fontSize: font.fontSize,
53
+ });
54
+ fit();
55
+ doc.body.appendChild(input);
56
+ view.getSelection?.()?.removeAllRanges(); // the double-click selected a word of the page
57
+ input.focus();
58
+ input.select();
59
+
60
+ let done = false;
61
+ const close = () => {
62
+ done = true;
63
+ input.remove();
64
+ };
65
+ const finish = (keep) => {
66
+ if (done) return true;
67
+ if (!keep || input.value === text) {
68
+ close();
69
+ if (cancel) cancel();
70
+ return true;
71
+ }
72
+ let tree;
73
+ try {
74
+ tree = parse(input.value);
75
+ } catch (error) {
76
+ input.setAttribute('aria-invalid', 'true');
77
+ input.title = error.message;
78
+ return false;
79
+ }
80
+ close();
81
+ commit(tree, input.value);
82
+ return true;
83
+ };
84
+ input.addEventListener('keydown', (event) => {
85
+ event.stopPropagation(); // the view's arrow keys are not for the box
86
+ if (event.key === 'Enter') { event.preventDefault(); finish(true); }
87
+ if (event.key === 'Escape') { event.preventDefault(); finish(false); }
88
+ });
89
+ input.addEventListener('input', () => {
90
+ fit();
91
+ input.removeAttribute('aria-invalid');
92
+ input.removeAttribute('title');
93
+ });
94
+ input.addEventListener('blur', () => { if (!finish(true)) finish(false); });
95
+ return input;
96
+ }
package/src/glyphs.js CHANGED
@@ -18,6 +18,8 @@ export const PATHS = {
18
18
  '}': { fill: 'M0,0 C0.55,0 0.5,0.05 0.5,0.2 L0.5,0.38 C0.5,0.46 0.65,0.49 0.95,0.5 C0.65,0.51 0.5,0.54 0.5,0.62 L0.5,0.8 C0.5,0.95 0.55,1 0,1 L0,0.985 C0.32,0.985 0.36,0.95 0.36,0.8 L0.36,0.62 C0.36,0.53 0.45,0.505 0.7,0.5 C0.45,0.495 0.36,0.47 0.36,0.38 L0.36,0.2 C0.36,0.05 0.32,0.015 0,0.015 Z' },
19
19
  // Radical: short tick, long down-stroke, long up-stroke to the top right.
20
20
  'sqrt': { stroke: 'M0,0.62 L0.18,0.55 L0.45,0.97 L0.98,0.015' },
21
+ // \cancel: one stroke, bottom left to top right, stretched over what it crosses out.
22
+ 'cancel': { stroke: 'M0,1 L1,0' },
21
23
  // Integral: an S slanted to the right with small hooks at each end.
22
24
  'int': { stroke: 'M0.95,0.09 C0.9,0.01 0.72,0 0.66,0.12 C0.6,0.25 0.42,0.75 0.34,0.88 C0.28,0.99 0.1,0.99 0.05,0.91' },
23
25
  'sum': { fill: 'M0.04,0 L0.94,0 L0.98,0.24 L0.94,0.24 C0.9,0.1 0.8,0.07 0.62,0.07 L0.24,0.07 L0.58,0.5 L0.2,0.93 L0.64,0.93 C0.82,0.93 0.92,0.9 0.95,0.76 L0.99,0.76 L0.94,1 L0.02,1 L0.47,0.47 Z' },
package/src/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // '@mathzetta/inlinemath' — drawing, the live view, selection. See types.d.ts.
2
- export type { Input, Node, Target, LayoutOptions, DrawOptions, ClickInfo } from './types.js';
3
- export { toHTML, treeOf, layout, DEFAULTS, View, render, select, signature, nodeAt, pathOf, numberTree, PATHS } from './types.js';
2
+ export type { Input, Node, Target, LayoutOptions, DrawOptions, ViewOptions, ClickInfo } from './types.js';
3
+ export { toHTML, treeOf, layout, DEFAULTS, View, render, shouldAnimate, select, signature, nodeAt, pathOf, numberTree, PATHS } from './types.js';
package/src/index.js CHANGED
@@ -12,6 +12,6 @@
12
12
 
13
13
  export { toHTML, treeOf } from './html.js';
14
14
  export { layout, DEFAULTS } from './layout.js';
15
- export { View, render } from './view.js';
15
+ export { View, render, shouldAnimate } from './view.js';
16
16
  export { select, signature, nodeAt, pathOf, numberTree } from './select.js';
17
17
  export { PATHS } from './glyphs.js';
package/src/layout.js CHANGED
@@ -21,7 +21,7 @@
21
21
  import {
22
22
  GREEK, needsBrackets, unaryNeedsBrackets, isMinusOneTimes, isImplicitProduct, isVariable,
23
23
  firstLeaf, customName, derivativeOrder, bracketedSides, bracketOp, firstVariable,
24
- isCustomFunction, precedenceOf, toSpeech,
24
+ isCustomFunction, precedenceOf, toAudioSpeech, MATRICES, matrixRows,
25
25
  } from './mathzetta.js';
26
26
  import { svg } from './glyphs.js';
27
27
 
@@ -42,6 +42,7 @@ const OP_CLASS = {
42
42
  '&': 'and', '|': 'or', '\\not': 'not', '\\xor': 'xor', '\\nand': 'nand', '\\nor': 'nor', '?:': 'cases',
43
43
  '()': 'tuple', '[]': 'list', '{}': 'set', '(]': 'interval', '[)': 'interval',
44
44
  '\\sumidx': 'sum', '\\prodidx': 'prod', 'mod': 'mod', 'pmod': 'pmod',
45
+ pmatrix: 'matrix', bmatrix: 'matrix', vmatrix: 'matrix',
45
46
  };
46
47
 
47
48
  const RELATIONS = {
@@ -54,7 +55,7 @@ const BINARY = { '±': '±', '∪': '∪', '∩': '∩', 'mod': 'mod' };
54
55
  const TRIG = ['sin', 'cos', 'tan', 'csc', 'sec', 'cot', 'sinh', 'cosh', 'tanh'];
55
56
  const INVERSE = { arcsin: 'sin', arccos: 'cos', arctan: 'tan', arcsinh: 'sinh', arccosh: 'cosh', arctanh: 'tanh' };
56
57
  /** Functions written with a name and, when the argument is simple, no brackets: sin x, ln x. */
57
- const NAMED = [...TRIG, ...Object.keys(INVERSE), 'ln'];
58
+ const NAMED = [...TRIG, ...Object.keys(INVERSE), 'ln', 'det'];
58
59
  /** Functions always written with brackets: max(a, b). */
59
60
  const CALLS = { atan2: 'atan2', max: 'max', min: 'min', gcd: 'gcd', mean: 'mean', round: 'round', fixed: 'fixed', expm1: 'expm1', random: 'random' };
60
61
 
@@ -74,7 +75,8 @@ export const DEFAULTS = {
74
75
  derivativeBrackets: '[', // d/dx [x² + 1], or '(' for d/dx (x² + 1)
75
76
  scriptFractions: 'slash',// x^{1/2} written x^{1/2} with a slash; 'stack' stacks it
76
77
  cases: true, // c ? a : b drawn as a piecewise brace
77
- label: true, // accessible name of the whole expression: true = the spoken form, a string, (tree) => string, or false
78
+ label: true, // accessible name of the whole expression: true = MathZetta's toAudioSpeech (the grouping in the words), a string, (tree) => string, or false
79
+ mathml: null, // MathZetta's toMathML (import it from 'mathzetta/mathml'): also write the expression as visually hidden MathML, which screen readers can step through and show in math braille; it replaces the label
78
80
  attributes: null, // (node) => ({ ... }) extra attributes for a node's element
79
81
  style: null, // (node) => 'css' inline style for a node's element
80
82
  };
@@ -102,18 +104,34 @@ export function layout(tree, options = {}) {
102
104
  const r = new Layout({ ...DEFAULTS, ...options });
103
105
  const b = r.render(tree, { level: 0 });
104
106
  const cls = `${r.p}${r.o.display ? ` ${r.p}-display` : ''}`;
105
- // Screen readers get the expression read as a teacher would; the spans and glyphs inside are not read piecemeal.
106
- const label = accessibleName(tree, r.o.label);
107
- const body = label === null ? b.html : b.html.replace(/^<span /, '<span aria-hidden="true" ');
107
+ // Screen readers get the expression as MathML they can walk (mathml: true), or read as a teacher would (the
108
+ // label); the spans and glyphs of the drawing are never read piecemeal.
109
+ const mathml = typeof r.o.mathml === 'function' ? assistiveMathML(r.o.mathml, tree, r.p, r.o.display) : '';
110
+ const label = mathml ? null : accessibleName(tree, r.o.label);
111
+ const body = label === null && !mathml ? b.html : b.html.replace(/^<span /, '<span aria-hidden="true" ');
108
112
  const attrs = label === null ? '' : ` role="math" aria-label="${escape(label)}"`;
109
- return { html: `<span class="${cls}"${attrs}>${body}</span>`, h: b.h, d: b.d };
113
+ return { html: `<span class="${cls}"${attrs}>${body}${mathml}</span>`, h: b.h, d: b.d };
114
+ }
115
+
116
+ /**
117
+ * The expression as MathML for assistive technology: no ids (two drawings on a page must not share them), hidden
118
+ * from sight. The page passes MathZetta's toMathML, so Inline Math itself imports only what every page maps.
119
+ */
120
+ function assistiveMathML(toMathML, tree, prefix, display) {
121
+ try {
122
+ return String(toMathML(tree)).replace(/ id="mz-\d+"/g, '')
123
+ .replace(/^<math>/, `<math class="${prefix}-sr-math"${display ? ' display="block"' : ''}>`);
124
+ } catch {
125
+ return ''; // nothing MathZetta can write as MathML: the label stands in
126
+ }
110
127
  }
111
128
 
112
129
  function accessibleName(tree, label) {
113
130
  if (label === false || label === null) return null;
114
131
  if (typeof label === 'string') return label;
115
132
  try {
116
- return typeof label === 'function' ? label(tree) : toSpeech(tree);
133
+ // a screen reader is speech with no drawing in view: the grouping has to be in the words
134
+ return typeof label === 'function' ? label(tree) : toAudioSpeech(tree);
117
135
  } catch {
118
136
  return null;
119
137
  }
@@ -199,6 +217,8 @@ class Layout {
199
217
  if (NAMED.includes(op) || op === 'log') return wrap(this.named(node, ctx));
200
218
  if (CALLS[op]) return wrap(row(this.word(CALLS[op]), this.fence(this.list(args, ctx))));
201
219
  if (isCustomFunction(op)) return wrap(this.custom(node, ctx));
220
+ const rows = matrixRows(node); // pmatrix, bmatrix, vmatrix, and [[1, 2], [3, 4]]
221
+ if (rows) return wrap(this.matrix(node, rows, ctx));
202
222
  if (GROUPS[op]) return wrap(this.fence(this.list(args, ctx), ...GROUPS[op]));
203
223
  if (RELATIONS[op] && args.length === 2) return wrap(row(side(a, 'left'), this.rel(RELATIONS[op]), side(b, 'right')));
204
224
  if (BINARY[op] && args.length === 2) return wrap(row(side(a, 'left'), this.bin(BINARY[op]), side(b, 'right')));
@@ -325,7 +345,8 @@ class Layout {
325
345
  decorate(name, b) {
326
346
  switch (name) {
327
347
  case 'boxed': return box(this.el('boxed', b.html), b.h + 0.22, b.d + 0.22);
328
- case 'cancel': return box(this.el('cancel', b.html), b.h, b.d);
348
+ // a drawn stroke, not a background: Windows high contrast drops backgrounds, and a cancelled term would look kept
349
+ case 'cancel': return box(this.el('cancel', b.html + svg('cancel', `${this.p}-cancel-line`)), b.h, b.d);
329
350
  case 'underline': return box(this.el('underline', b.html), b.h, b.d + 0.12);
330
351
  case 'overline': case 'bar': return box(this.el('over', b.html), b.h + 0.14, b.d);
331
352
  default: return box(this.el('accent', this.el('accent-mark', ACCENTS[name]) + b.html), b.h + 0.3, b.d);
@@ -557,6 +578,28 @@ class Layout {
557
578
  return row(op, body);
558
579
  }
559
580
 
581
+ /**
582
+ * A matrix: a grid of entries, centred on the maths axis, in brackets as tall as it is. Each
583
+ * row that is a list in the tree is an element of its own (display: contents), so a row can
584
+ * be pointed at like any part.
585
+ */
586
+ matrix(node, rows, ctx) {
587
+ const ROWGAP = 0.3;
588
+ const cells = rows.map((r) => r.map((c) => this.render(c, ctx)));
589
+ const heights = cells.map((r) => ({ h: Math.max(LH, ...r.map((b) => b.h)), d: Math.max(LD, ...r.map((b) => b.d)) }));
590
+ const total = heights.reduce((s, x) => s + x.h + x.d, 0) + ROWGAP * (rows.length - 1);
591
+ const lift = AXIS + total / 2 - heights[0].h; // the first row's baseline
592
+ const html = cells.map((r, i) => {
593
+ const inner = r.map((b) => this.el('matrix-cell', b.html)).join('');
594
+ const tree = node.args[i];
595
+ return tree && tree.type === 'op' && tree.op === '[]' ? this.wrap(tree, ['op', 'op-list', 'matrix-row'], box(inner)).html : inner;
596
+ }).join('');
597
+ const columns = rows[0].length;
598
+ const grid = box(this.el('matrix', html, `grid-template-columns:repeat(${columns},auto);vertical-align:${em(lift)}`), AXIS + total / 2, total / 2 - AXIS);
599
+ const [open, close] = MATRICES[node.op] ?? ['[', ']'];
600
+ return this.fence(grid, open, close);
601
+ }
602
+
560
603
  /** c ? a : b → a brace with "a if c", "b otherwise". Nested ternaries add rows. */
561
604
  cases(node, ctx) {
562
605
  const rows = [];
@@ -565,7 +608,8 @@ class Layout {
565
608
  rows.push([this.render(n.args[1], ctx), row(this.word('if'), this.text('space', ''), this.render(n.args[0], ctx))]);
566
609
  n = n.args[2];
567
610
  }
568
- rows.push([this.render(n, ctx), this.word('otherwise')]);
611
+ // a case nobody wrote (`unchanged`: no else, no otherwise row) is not drawn
612
+ if (!(n.type === 'symbol' && n.value === 'unchanged')) rows.push([this.render(n, ctx), this.word('otherwise')]);
569
613
  const ROWGAP = 0.25;
570
614
  const heights = rows.map(([v, c]) => ({ h: Math.max(v.h, c.h), d: Math.max(v.d, c.d) }));
571
615
  const total = heights.reduce((s, x) => s + x.h + x.d, 0) + ROWGAP * (rows.length - 1);
@@ -583,7 +627,7 @@ function isSimpleArgument(node, inScript = false) {
583
627
  if (node.type === 'number') return !node.value.startsWith('-') || inScript;
584
628
  if (node.type === 'symbol') return true;
585
629
  if (node.type !== 'op') return false;
586
- if (node.op === 'abs') return !inScript;
630
+ if (node.op === 'abs' || matrixRows(node)) return !inScript; // |x| and a matrix bring their own brackets
587
631
  if (inScript && (node.op === 'neg' || node.op === '^')) return isSimpleArgument(node.args[0], true) && (node.op === 'neg' || isSimpleArgument(node.args[1], true));
588
632
  if (node.op === '*' && !isMinusOneTimes(node)) {
589
633
  return node.args.every((c) => (c.type === 'number' && !c.value.startsWith('-')) || isShortSymbol(c) || (c.op === '*' && isSimpleArgument(c, inScript)));