@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.
- package/LICENSE +21 -0
- package/README.md +326 -2
- package/dist/cas.external.js +2 -0
- package/dist/cas.js +2 -0
- package/dist/chunks/calculate-Cz87Lt0N.js +2 -0
- package/dist/chunks/draw-Bev17BJJ.js +2 -0
- package/dist/chunks/edit-C65xXzo8.js +2 -0
- package/dist/chunks/edit-M68aRA5o.js +2 -0
- package/dist/chunks/html-BimAqn7x.js +2 -0
- package/dist/chunks/html-CupluJSh.js +2 -0
- package/dist/chunks/lite-3Fb-OHbt.js +2 -0
- package/dist/fonts/GUST-FONT-LICENSE.txt +30 -0
- package/dist/fonts/fonts.css +17 -0
- package/dist/fonts/lmroman12-italic.woff2 +0 -0
- package/dist/fonts/lmroman12-regular.woff2 +0 -0
- package/dist/inlinemath-cas.external.global.min.js +2 -0
- package/dist/inlinemath-cas.global.min.js +2 -0
- package/dist/inlinemath-lite-calc.global.min.js +2 -0
- package/dist/inlinemath-lite.external.global.min.js +2 -0
- package/dist/inlinemath-lite.global.min.js +2 -0
- package/dist/inlinemath.css +158 -0
- package/dist/inlinemath.external.global.min.js +2 -0
- package/dist/inlinemath.external.js +2 -0
- package/dist/inlinemath.global.min.js +2 -0
- package/dist/inlinemath.js +2 -0
- package/dist/lite-calc.js +2 -0
- package/dist/lite.external.js +2 -0
- package/dist/lite.js +2 -0
- package/dist/markdown.external.js +2 -0
- package/dist/markdown.js +2 -0
- package/fonts/GUST-FONT-LICENSE.txt +30 -0
- package/fonts/fonts.css +17 -0
- package/fonts/lmroman12-italic.woff2 +0 -0
- package/fonts/lmroman12-regular.woff2 +0 -0
- package/inlinemath.css +158 -0
- package/package.json +95 -4
- package/src/calculator.js +6 -0
- package/src/cas.d.ts +3 -0
- package/src/cas.js +149 -0
- package/src/draw.js +72 -0
- package/src/edit.js +96 -0
- package/src/glyphs.js +40 -0
- package/src/html.js +32 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +17 -0
- package/src/layout.js +649 -0
- package/src/lite-calc.d.ts +3 -0
- package/src/lite-calc.js +15 -0
- package/src/lite.d.ts +3 -0
- package/src/lite.js +18 -0
- package/src/markdown.d.ts +3 -0
- package/src/markdown.js +676 -0
- package/src/mathzetta.js +18 -0
- package/src/select.js +108 -0
- package/src/types.d.ts +204 -0
- package/src/variables.js +228 -0
- 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
|
-
#
|
|
1
|
+
# Inline Math
|
|
2
2
|
|
|
3
|
-
|
|
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.
|