@mathzetta/inlinemath 0.0.0-stage → 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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +326 -2
  3. package/dist/cas.external.js +2 -0
  4. package/dist/cas.js +2 -0
  5. package/dist/chunks/calculate-Cz87Lt0N.js +2 -0
  6. package/dist/chunks/draw-Bev17BJJ.js +2 -0
  7. package/dist/chunks/edit-C65xXzo8.js +2 -0
  8. package/dist/chunks/edit-M68aRA5o.js +2 -0
  9. package/dist/chunks/html-BimAqn7x.js +2 -0
  10. package/dist/chunks/html-CupluJSh.js +2 -0
  11. package/dist/chunks/lite-3Fb-OHbt.js +2 -0
  12. package/dist/fonts/GUST-FONT-LICENSE.txt +30 -0
  13. package/dist/fonts/fonts.css +17 -0
  14. package/dist/fonts/lmroman12-italic.woff2 +0 -0
  15. package/dist/fonts/lmroman12-regular.woff2 +0 -0
  16. package/dist/inlinemath-cas.external.global.min.js +2 -0
  17. package/dist/inlinemath-cas.global.min.js +2 -0
  18. package/dist/inlinemath-lite-calc.global.min.js +2 -0
  19. package/dist/inlinemath-lite.external.global.min.js +2 -0
  20. package/dist/inlinemath-lite.global.min.js +2 -0
  21. package/dist/inlinemath.css +158 -0
  22. package/dist/inlinemath.external.global.min.js +2 -0
  23. package/dist/inlinemath.external.js +2 -0
  24. package/dist/inlinemath.global.min.js +2 -0
  25. package/dist/inlinemath.js +2 -0
  26. package/dist/lite-calc.js +2 -0
  27. package/dist/lite.external.js +2 -0
  28. package/dist/lite.js +2 -0
  29. package/dist/markdown.external.js +2 -0
  30. package/dist/markdown.js +2 -0
  31. package/fonts/GUST-FONT-LICENSE.txt +30 -0
  32. package/fonts/fonts.css +17 -0
  33. package/fonts/lmroman12-italic.woff2 +0 -0
  34. package/fonts/lmroman12-regular.woff2 +0 -0
  35. package/inlinemath.css +158 -0
  36. package/package.json +95 -4
  37. package/src/calculator.js +6 -0
  38. package/src/cas.d.ts +3 -0
  39. package/src/cas.js +149 -0
  40. package/src/draw.js +72 -0
  41. package/src/edit.js +96 -0
  42. package/src/glyphs.js +40 -0
  43. package/src/html.js +32 -0
  44. package/src/index.d.ts +3 -0
  45. package/src/index.js +17 -0
  46. package/src/layout.js +649 -0
  47. package/src/lite-calc.d.ts +3 -0
  48. package/src/lite-calc.js +15 -0
  49. package/src/lite.d.ts +3 -0
  50. package/src/lite.js +18 -0
  51. package/src/markdown.d.ts +3 -0
  52. package/src/markdown.js +676 -0
  53. package/src/mathzetta.js +18 -0
  54. package/src/select.js +108 -0
  55. package/src/types.d.ts +204 -0
  56. package/src/variables.js +228 -0
  57. package/src/view.js +382 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Robert Wilcox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,327 @@
1
- # Temporary Holding Version
1
+ # Inline Math
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Draws calculus-level maths as plain HTML and CSS (the `@mathzetta/inlinemath` package, at inlinemath.com). The input is anything
4
+ [MathZetta](https://github.com/triplelog/mathzetta/tree/main/packages/mathzetta) reads (`sin^2 x`, `\frac{d}{dx}(x^2)`, `x**2/2`, `√x`).
5
+ Every part of the drawing can be found, coloured, clicked and animated.
6
+
7
+ Inline Math is not a LaTeX engine. It covers what a Calculus course writes:
8
+ - fractions, exponents and subscripts, radicals, brackets that grow;
9
+ - `d/dx` and `dy/dx`, integrals with limits, `lim` with its subscript, `∑`/`∏`;
10
+ - absolute value, floor and ceiling, sets and intervals;
11
+ - piecewise functions, `\boxed`, `\cancel`, `\vec`, `\hat`, `\overline`.
12
+
13
+ It needs nothing besides MathZetta: no KaTeX, no fonts (Latin Modern is
14
+ optional), and no build step.
15
+
16
+ ```bash
17
+ npm install @mathzetta/inlinemath mathzetta # mathzetta 1.x is a peer; add @mathzetta/cas for @mathzetta/inlinemath/cas
18
+ ```
19
+
20
+ Inline Math is ES modules (`import`). TypeScript declarations come with it. `require('@mathzetta/inlinemath')`
21
+ works where Node can require ES modules (Node 20.19+ and 22.12+); in the browser, use the bundles
22
+ in `dist/` (below).
23
+
24
+ ```js
25
+ import { toHTML, render } from '@mathzetta/inlinemath';
26
+
27
+ toHTML('\\int_0^1 x^2\\,dx'); // HTML string; works in Node too
28
+
29
+ const view = render(div, 'x^2 + 2x'); // a live view
30
+ view.highlight('2x'); // name a part by writing it
31
+ view.color('x^2', 'crimson');
32
+ view.on('click', ({ node, id, path }) => …); // also from the keyboard: Tab to it, arrows walk the parts, Enter picks one
33
+ view.set('2x + x^2', { animate: true }); // the next step: matching parts glide over (instant under reduced motion; 'always' to insist)
34
+ render(div, 'x^2', { announce: true }); // read each new step to a screen reader (a polite live region after the div)
35
+ view.announce('Step 2: expand'); // your own words, through the same region
36
+ view.segments(); // [{ text: 'x', id: 3 }, { text: 'squared', id: 5 }, …]
37
+ view.editable({ toText, onEdit: ({ text }) => … }); // double-click a part (or F2 on it) to edit it in place
38
+ ```
39
+
40
+ **Editing a part.** `view.editable({ toText })` (`import { toText } from 'mathzetta/text'`; in the
41
+ `<script>` build it is `InlineMath.toText`) opens a text box over a part when it is double-clicked,
42
+ or when F2 is pressed on the part the keyboard is on, holding that part as text. Enter puts what
43
+ was typed in its place (the rest of the expression glides over), Escape leaves it as it was, and
44
+ text that doesn't read keeps the box open, marked. `onEdit` gets the new expression as a tree and
45
+ as text, the edited part's path and what was typed; `view.edit(target)` opens the box from code.
46
+
47
+ **Keyboard and screen readers.** Every drawing has an accessible name (`role="math"`, its spoken form for a listener
48
+ who can't see it). Once a view listens for `'click'`, the drawing is one tab stop: the arrow keys walk its parts (Down
49
+ into a part, Up out, Left/Right between neighbours, Home or Escape back to the whole), each part is spoken through a
50
+ polite live region and ringed, and Enter or Space calls the click listener for the current part (`event` is then the
51
+ KeyboardEvent). `view.focusPart(node)` moves there from code.
52
+
53
+ Inline Math needs its stylesheet on the page (the fonts are optional):
54
+
55
+ ```js
56
+ import '@mathzetta/inlinemath/inlinemath.css'; // with a bundler
57
+ import '@mathzetta/inlinemath/fonts/fonts.css'; // optional: Latin Modern, the TeX look
58
+ ```
59
+
60
+ ```html
61
+ <!-- or from the CDN -->
62
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/inlinemath.css">
63
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/fonts/fonts.css"> <!-- optional -->
64
+ ```
65
+
66
+ ### Lite: drawing only
67
+
68
+ `@mathzetta/inlinemath/lite` is `toHTML()` and nothing live: the same layout, glyphs, colours, highlights and
69
+ spoken labels for screen readers, without the live view (`render`, step animation, events), Markdown
70
+ or the CAS — about a third smaller. `{{…}}` blocks are worked out only if MathZetta's calculator is
71
+ loaded too (`import 'mathzetta/calculate'`, or `@mathzetta/inlinemath/lite-calc`, which brings it); otherwise
72
+ they are drawn as written.
73
+
74
+ ```js
75
+ import { toHTML } from '@mathzetta/inlinemath/lite';
76
+ el.innerHTML = toHTML('\\frac{d}{dx} x^2 = 2x', { colors: { '2x': 'crimson' } });
77
+ ```
78
+
79
+ ## Try it
80
+
81
+ ```bash
82
+ npm test # 253 checks; every input in mathzetta/test/cases/calculus.tsv
83
+ ```
84
+
85
+ [inlinemath.com](https://inlinemath.com) has the live editor, steps and the gallery; its
86
+ [Markdown](https://inlinemath.com/markdown.html) and [CAS](https://inlinemath.com/cas.html) pages are playgrounds.
87
+
88
+ ## Files
89
+
90
+ | File | What |
91
+ | --- | --- |
92
+ | `src/mathzetta.js`, `src/calculator.js` | The **only** imports from MathZetta (a peer dependency; in this repository, the workspace's `packages/mathzetta`): the core and speech, and the calculator, which lite leaves out. |
93
+ | `src/draw.js`, `src/html.js`, `src/lite.js` | `toHTML` without the calculator (shared), the full build's `toHTML` (with it), and the lite entry. |
94
+ | `src/layout.js` | Turns a tree into HTML. All notation choices live here. |
95
+ | `src/glyphs.js` | SVG paths for `( ) { } √ ∫ ∑ ∏`. Change a shape here. |
96
+ | `src/select.js` | Picks parts by id, expression, path or test. Also `numberTree`. |
97
+ | `src/view.js` | The live view: highlight, colour, events, speech segments, step animation. |
98
+ | `src/html.js` | `toHTML`, `treeOf`, and the `colors` / `highlight` options. |
99
+ | `inlinemath.css` | All the styling. The numbers marked *metric* must match the constants in layout.js. |
100
+
101
+ ## How it lays maths out
102
+
103
+ - **Estimated, not measured.** Each piece knows roughly how far it reaches above and below the baseline, in em.
104
+ - From that, `layout.js` writes `vertical-align` values that put fraction bars on the maths axis, lift exponents above tall bases, and size `∫` to its integrand.
105
+ - The same string comes out in Node, in a browser or in a video frame, with no layout pass after rendering.
106
+ - **Self-stretching delimiters.** Brackets, bars, floors and braces are drawn at `height: 100%` of what they enclose (SVG or CSS borders), so they need no estimate at all.
107
+ - This is the best idea from inline-math's imcss renderer. Inline Math fixes its aspect-ratio bug: shapes use `preserveAspectRatio="none"` with a fixed width.
108
+ - **Stable ids.** Every node's element has `data-node` = MathZetta's postfix index. MathML ids and `toSpeechSegments()` ids use the same number, so a spoken phrase can light up the part it describes.
109
+ - Decoration (signs, brackets, bars) has no id. A click on it belongs to the node that drew it.
110
+
111
+ ## Options
112
+
113
+ These go to `toHTML(input, options)` and `render(el, input, options)`.
114
+
115
+ | Option | Default | |
116
+ | --- | --- | --- |
117
+ | `display` | `true` | Limits above and below `∑`. `false` gives running-text style (smaller fractions, limits as scripts). |
118
+ | `fnParens` | `'auto'` | `sin x`, `sin 2x`, `ln|x|`, but `sin(x + 1)`. `'always'` gives `sin(x)`. |
119
+ | `trigPowers` | `true` | `sin(x)^2` → sin²x |
120
+ | `inverseTrig` | `'arc'` | `'power'` gives sin⁻¹x |
121
+ | `derivativeBrackets` | `'['` | `'('` for d/dx (x² + 1) |
122
+ | `scriptFractions` | `'slash'` | x^{3/2} written with a slash. `'stack'` stacks it. |
123
+ | `cases` | `true` | `c ? a : b` drawn as a piecewise brace |
124
+ | `colors` | | `{ 'x^2': 'red', 5: 'blue' }` (number keys are ids) or `[[target, colour]]` |
125
+ | `highlight` | | target(s) that get the `mv-hl` class |
126
+ | `attributes`, `style` | | `(node) => ({…})` / `(node) => 'css'` for each node's element |
127
+ | `mathml` | | MathZetta's `toMathML` (`import { toMathML } from 'mathzetta/mathml'`): also write the expression as visually hidden MathML (no ids), which screen readers can step through part by part and show in math braille; the drawing stays hidden from them and the label is dropped (MathML carries the meaning). A keyboard-enabled view keeps its label on the focusable element |
128
+ | `label` | `true` | the accessible name of the expression (`role="math" aria-label`): `true` = its spoken form for a listener who can't see it (MathZetta's `toAudioSpeech`: "the square root of the quantity x plus 1, end root, plus 2"), or a string, `(tree) => string`, or `false` |
129
+ | `prefix` | `'mv'` | class prefix |
130
+
131
+ A *target* can be any of these:
132
+ - an id, e.g. `4`;
133
+ - an expression, e.g. `'x^2'`, which matches every part that is exactly that subtree;
134
+ - `{ path: [0, 1] }`;
135
+ - a function `(node) => bool`;
136
+ - an array of any of the above.
137
+
138
+ ## A static build: `npm run build`
139
+
140
+ ```bash
141
+ npm install # at the repository root: links mathzetta and MathZetta CAS, installs rollup
142
+ npm run build # dist/ (a few seconds)
143
+ ```
144
+
145
+ - **`dist/`** — Inline Math with everything inside, no import map or `node_modules` needed:
146
+ - `inlinemath.js`, `markdown.js`, `cas.js` — ES modules (shared code in `dist/chunks/`);
147
+ - `inlinemath.global.min.js` (136 KB, 46 KB gzipped) — a classic `<script>`: `window.InlineMath`, with Inline Math and the Markdown;
148
+ - `inlinemath-cas.global.min.js` (270 KB, 87 KB gzipped) — the same plus the CAS (`InlineMath.mathCAS`, `casBlocks`, `stepsHTML`);
149
+ - `inlinemath.css` and `fonts/` — put both on the page.
150
+
151
+ ```html
152
+ <!-- dist/ copied beside the page as inlinemath/ -->
153
+ <link rel="stylesheet" href="inlinemath/fonts/fonts.css">
154
+ <link rel="stylesheet" href="inlinemath/inlinemath.css">
155
+ <script src="inlinemath/inlinemath.global.min.js"></script>
156
+ <script>
157
+ el.innerHTML = InlineMath.markdownToHTML('The area is $\\pi r^2$; for $r = 3$ it is ${{pi r^2}}$.');
158
+ </script>
159
+ <!-- or as a module from the same copy: import { toHTML } from './inlinemath/inlinemath.js'; -->
160
+ ```
161
+
162
+ Both folders are build output (git-ignored); run the build again after changing `src/`.
163
+
164
+ ## From a CDN
165
+
166
+ Every npm release's `dist/` is on jsDelivr (and unpkg); the bundles carry MathZetta inside, so a
167
+ page needs nothing else:
168
+
169
+ ```html
170
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/fonts/fonts.css">
171
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/inlinemath.css">
172
+
173
+ <!-- a script tag: window.InlineMath -->
174
+ <script src="https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/inlinemath.global.min.js"></script>
175
+ <script>document.getElementById('out').innerHTML = InlineMath.toHTML('\\frac{d}{dx} x^2');</script>
176
+
177
+ <!-- or modules: inlinemath.js, markdown.js, cas.js (the CAS one holds MathZetta CAS too) -->
178
+ <script type="module">
179
+ import { toHTML } from 'https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/inlinemath.js';
180
+ import { markdownToHTML } from 'https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/markdown.js';
181
+ </script>
182
+ ```
183
+
184
+ `@0.2` follows the latest 0.2.x; `@0.2.0` pins a release.
185
+
186
+ **The stylesheet is required** — the drawing is HTML laid out by `inlinemath.css` (6.9 KB, 2.1 KB
187
+ gzipped): without it fractions, radicals and brackets don't line up. The classic builds
188
+ (`*.global.min.js`) carry it and add it to the page themselves (`<style id="inlinemath-css">`; a
189
+ `<link>` to `dist/inlinemath.css` is still fine, it is only added when absent); the ES modules
190
+ don't, so a page using them links `dist/inlinemath.css`. **The fonts are optional**: `fonts/fonts.css`
191
+ declares Latin Modern Roman (the TeX look) in two files, regular 45 KB and italic 47 KB (woff2), each
192
+ downloaded only when text uses it; without the link the maths is drawn in the system serif.
193
+
194
+ Every build comes **with MathZetta inside, or without it** (`*.external.*`), with or without the
195
+ CAS (MathZetta CAS). Sizes, gzipped:
196
+
197
+ | | ES module | classic `<script>` (`window.InlineMath`) |
198
+ |---|---|---|
199
+ | Lite (drawing only), MathZetta inside | `lite.js` 32 KB | `inlinemath-lite.global.min.js` 33 KB |
200
+ | Lite with the calculator (`{{…}}` worked out) | `lite-calc.js` 41 KB | `inlinemath-lite-calc.global.min.js` 43 KB |
201
+ | Lite without MathZetta | `lite.external.js` 7 KB | `inlinemath-lite.external.global.min.js` 9 KB |
202
+ | Inline Math (+ Markdown in the classic build), MathZetta inside | `inlinemath.js` 42 KB, `markdown.js` | `inlinemath.global.min.js` 46 KB |
203
+ | …without MathZetta | `inlinemath.external.js` **8 KB**, `markdown.external.js` | `inlinemath.external.global.min.js` 13 KB |
204
+ | with the CAS, MathZetta inside | `cas.js` 82 KB | `inlinemath-cas.global.min.js` 87 KB |
205
+ | with the CAS, without MathZetta | `cas.external.js` 40 KB | `inlinemath-cas.external.global.min.js` 52 KB |
206
+
207
+ Without MathZetta, the page loads it once itself: a classic page puts a MathZetta `<script>` first —
208
+ `mathzetta.min.js`, or the `maths-display` flavour, since speech, text and calculate are used (for
209
+ lite, `display`, or `maths-display` to work out `{{…}}`) — and
210
+ the external classic build reads `window.MathZetta`; a module page maps the names with an import
211
+ map (every name can point at the one MathZetta bundle). The fonts are 94 KB more, only if linked.
212
+
213
+ ```html
214
+ <script type="importmap">
215
+ { "imports": { "mathzetta": "https://cdn.jsdelivr.net/npm/mathzetta@1/build/mathzetta.esm.min.js",
216
+ "mathzetta/speech": "https://cdn.jsdelivr.net/npm/mathzetta@1/build/mathzetta.esm.min.js",
217
+ "mathzetta/calculate": "https://cdn.jsdelivr.net/npm/mathzetta@1/build/mathzetta.esm.min.js" } }
218
+ </script>
219
+ <script type="module">
220
+ import { toHTML } from 'https://cdn.jsdelivr.net/npm/@mathzetta/inlinemath@0.2/dist/inlinemath.external.js';
221
+ </script>
222
+ ```
223
+
224
+ Publishing is `npm publish` (`prepublishOnly` runs the tests and the build).
225
+
226
+ ## Markdown with maths: `@mathzetta/inlinemath/markdown`
227
+
228
+ ```js
229
+ import { markdownToHTML } from '@mathzetta/inlinemath/markdown';
230
+ el.innerHTML = markdownToHTML(text); // needs inlinemath.css
231
+ el.innerHTML = markdownToHTML(text, { blocks: casBlocks() }); // {{solve(…)}}, {{diff(…)}} too
232
+ ```
233
+
234
+ - **Maths:** `$…$` inline, `$$…$$` or a ` ```math ` fence on its own (centred), and
235
+ `\(…\)` / `\[…\]`. MathZetta reads it, so LaTeX, plain text (`$sqrt(x)/2$`) and
236
+ code all work.
237
+ - **Working out:** `{{…}}` inside maths is worked out (the calculator, or the CAS with
238
+ `blocks`). A definition like `$r = 3$` is remembered, exactly, for the `{{…}}` after it
239
+ (`define: false` turns that off; `scope: { r: 3 }` gives values).
240
+ - **Saved variables:** `$A := 2+2$` saves A (drawn A ≔ 2 + 2), and `$_A := 2+2$` saves it
241
+ without drawing anything. After that, A in any maths is drawn as its expression and `{{A}}`
242
+ is its value, so `$A = {{A}}$` draws 2 + 2 = 4 and `$3A$` draws 3(2 + 2). A name is a letter
243
+ (Greek too), optionally with a subscript (`A_1`); a variable may use the ones before it
244
+ (`$_B := 2A$`), and saving it again replaces it from there on. A plain `$r = 3$` still only
245
+ remembers the number: r keeps being drawn as r.
246
+ - **Matrices:** `\begin{pmatrix}…`, `bmatrix`, `vmatrix` (and `[[1, 2], [3, 4]]`) are drawn as
247
+ a grid in brackets as tall as it, and worked out exactly: `{{A^2}}`, `{{A^{-1}}}`, `{{A^T}}`,
248
+ `{{det(A)}}`, `{{A [5, 6]}}`, `{{linsolve(A, b)}}`. A saved variable can hold one:
249
+ `$_M := [[2, 1], [1, 3]]$` then `$M^2 = {{M^2}}$`.
250
+ - **With the CAS:** `{ blocks: casBlocks() }` works out `{{der(x^2, x)}}` (also `diff`,
251
+ `derivative`, `d/dx …`), `{{integral(x^2, x)}}`, `{{solve(…, x)}}`, `{{factor(…)}}` …, and a
252
+ saved variable stands for its expression there: after `$_f := x^3 + 2x$`, `{{der(f, x)}}` is
253
+ 3x² + 2.
254
+ - **Inputs:** `$A := [[2+2]]$` is a textbox that sets A, `$f := [[x^2 | sin(x) | 2x]]$` a
255
+ dropdown, `$n := [[1..10]]$` a slider (`[[0..1 step 0.05 = 0.5]]`: a step and a starting
256
+ value); with `$_…$` only the input is shown. `renderMarkdownInto(el, text)` makes them live:
257
+ every part of the page drawn with it shares the page's store (`variables`), so a variable
258
+ saved in one part is used in the others, and changing an input redraws everything that uses
259
+ it. `variables.set('A', 'x^2')` sets one from code, `variables.get('A')` reads it;
260
+ `{ variables: createVariables() }` gives a part a store of its own, `{ variables: false }` none.
261
+ `markdownToHTML` is static: inputs drawn at their starting values.
262
+ - **Editing by double-click:** in `renderMarkdownInto`, a part drawn from a textbox variable is
263
+ edited where it is drawn with `{ editable: { toText } }` (the whole variable is written back
264
+ into its textbox, so everything using it redraws); without `toText` the textbox is focused.
265
+ Double-clicking any other part fires `inlinemath:select` on the element with where that maths
266
+ is in the Markdown (`detail.start`, `detail.end`, `detail.raw`; for a part drawn from a
267
+ variable saved in the same text, where it was saved), so an editor beside it can select it.
268
+ `extractMath` gives every piece's `start` and `end` too.
269
+ - **Dollars:** a `$` is maths only when it looks like it (Pandoc's rule): "$5 and $10"
270
+ stays text, and `\$` is a dollar sign. Code spans and fences are left alone.
271
+ - **Bad maths:** maths that doesn't read is shown as written, marked, with the reason as
272
+ its tooltip.
273
+ - **The Markdown:** a small built-in renderer — headings, emphasis, links, images, lists,
274
+ quotes, code, tables, rules. Raw HTML is shown as text unless `html: true`, and
275
+ `javascript:` links are dropped. `render: (md) => html` uses another renderer
276
+ (marked, markdown-it); the maths is taken out first and put back into its output.
277
+ - **Options:** `math` passes options to every drawing (`fnParens`, `colors` …);
278
+ `delimiters` picks which marks count.
279
+ - **Playground:** [inlinemath.com/markdown.html](https://inlinemath.com/markdown.html), an editor with a live preview.
280
+
281
+ ## The CAS: `@mathzetta/inlinemath/cas`
282
+
283
+ MathZetta CAS (exact simplify, factor, solve, d/dx, ∫ …; @mathzetta/cas's docs/cas.md)
284
+ in drawn maths:
285
+
286
+ ```js
287
+ import { toHTML } from '@mathzetta/inlinemath';
288
+ import { casBlocks, stepsHTML, mathCAS } from '@mathzetta/inlinemath/cas';
289
+
290
+ toHTML("f'(x) = {{diff(x^3 sin(x), x)}}", { blocks: casBlocks() })
291
+ toHTML('{{solve(x^2 - 5x + 6 = 0, x)}}', { blocks: casBlocks() }) // x = 2 ∨ x = 3
292
+ el.innerHTML = stepsHTML(mathCAS().solve('sqrt(x + 1) = x - 1', 'x', { steps: 'brief' }).steps)
293
+ ```
294
+
295
+ Block functions: `simplify`, `expand`, `factor`, `cancel`, `diff(f, x[, n])`,
296
+ `integrate(f, x[, a, b])`, `solve(eq, x)`, `approx(x[, digits])`; `d/dx …` and
297
+ `∫ … dx` go to the CAS too; every other block is the calculator, as before.
298
+ `stepsHTML` draws each step with the rewritten part highlighted and its
299
+ explanation. `mathCAS({ rules, names })` uses rule files of your own;
300
+ `mathCAS({ complex: true })` makes i the imaginary unit (and reads Re, Im, conj,
301
+ arg; the conjugate is drawn with an overline). Pages map
302
+ `@mathzetta/cas/engine` and `@mathzetta/cas` in their import map ([inlinemath.com/cas.html](https://inlinemath.com/cas.html) is a
303
+ playground: any input, any operation, detail level, and a live rule editor).
304
+
305
+ ## What still needs doing
306
+
307
+ **Notation MathZetta doesn't read yet** (Inline Math can only draw what parses):
308
+ - **Evaluation bars:** `[F(x)]_0^1` reads as a power of a subscript, and `\left. F \right|_0^1` is rejected.
309
+ - **Partial derivatives:** `\partial f / \partial x` becomes products.
310
+ - **Derivative orders:** `f^{(4)}` becomes a power.
311
+ - **Lists of answers:** `x = 2, x = -3` is rejected.
312
+ - **Accents:** `\dot x` becomes a product.
313
+
314
+ **Inline Math features worth adding next:**
315
+ 1. **Exit animation.** Parts that disappear in a step should fade out where they were (ghost copies). Today they vanish, and new signs and bars just appear.
316
+ 2. **Step-diff helper.** Given `step.before`, `step.after` and `step.path`, return what changed, then highlight → animate → un-highlight in one call. That would be the video's main move.
317
+ 3. **Narration sync.** Something like `view.follow(segments, timings)`: drive highlights from a text-to-speech engine's word timings (`words: [{start, end, i, j}]`).
318
+ 4. **Copy as LaTeX or text** from a selection.
319
+ 5. **Line breaking and alignment.** Multi-line derivations with the `=` signs lined up (an `align` helper), and breaking long sums.
320
+ 6. **A DOM refine pass, optional.** Measure once after fonts load, to nudge fraction bars and script heights where the em estimates are off with unusual fonts.
321
+ 7. **Spacing table.** Move the per-operator spacing (bin, rel, thin) into one table so a house style is one edit.
322
+
323
+ ## Known rough edges
324
+
325
+ - **Fonts.** The placement estimates are tuned for Latin Modern and Times-like serifs. Fonts with unusual ascent and descent will sit a little off.
326
+ - **Accents.** `\vec` and `\hat` use font glyphs and are only approximately centred.
327
+ - **Unbalanced fractions.** Taller-than-usual numerators on fractions are placed from estimates. Very deep nesting can drift by a few hundredths of an em.