@pie-players/pie-calculator-cortex 0.3.74 → 1.0.0-beta.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/README.md +331 -170
- package/dist/assets/{evaluation-worker-DkU_wGh3.js → evaluation-worker-0xRDVTJx.js} +98934 -88735
- package/dist/chunks/{compute-engine-BZSGMexT.js → compute-engine-Bd6XVE22.js} +90029 -79776
- package/dist/chunks/{runtime-C7H1jS4E.js → runtime-YFqnU6ic.js} +1233 -1240
- package/dist/chunks/{src-BcptuNS5.js → src-D_1goyPT.js} +5886 -3705
- package/dist/index.js +1 -1
- package/dist/src/evaluation-engine.d.ts +0 -2
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
# `@pie-players/pie-calculator-cortex`
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
A basic, scientific and graphing calculator adapter built on MathLive, the
|
|
4
|
+
CortexJS Compute Engine and JSXGraph. It implements the provider contract from
|
|
5
|
+
[`@pie-players/pie-calculator`](../calculator/README.md) and bundles all runtime
|
|
6
|
+
code and assets, so it needs no API key, CDN or network connection. Toolkit hosts
|
|
7
|
+
select it with `provider.id: "calculator-cortex"`
|
|
8
|
+
([Calculator providers](../default-tool-loaders/README.md#calculator-providers));
|
|
9
|
+
hosts that mount a calculator themselves use `CortexCalculatorProvider` directly.
|
|
10
|
+
The [Desmos](../calculator-desmos/README.md) and
|
|
11
|
+
[GeoGebra](../calculator-geogebra/README.md) adapters implement the same contract.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add @pie-players/pie-calculator-cortex
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
7
20
|
|
|
8
21
|
```ts
|
|
9
22
|
import { CortexCalculatorProvider } from "@pie-players/pie-calculator-cortex";
|
|
@@ -20,28 +33,154 @@ const calculator = await provider.createCalculator(
|
|
|
20
33
|
);
|
|
21
34
|
```
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
`initialize()` takes `CortexCalculatorProviderInit`, which carries only
|
|
37
|
+
`onTelemetry` because there is no credential to supply. `createCalculator()`
|
|
38
|
+
initializes the provider on first use. Both fail with `worker-unavailable`
|
|
39
|
+
outside a browser, where module workers are unsupported, and after `destroy()`.
|
|
40
|
+
Evaluation runs in a Web Worker under the configured time limit.
|
|
41
|
+
|
|
42
|
+
The calculator implements every optional `Calculator` method. `getValue()`
|
|
43
|
+
returns the current input as LaTeX, `evaluate(latex)` returns the formatted
|
|
44
|
+
answer with a `.` decimal separator, and the capabilities report history,
|
|
45
|
+
graphing and a `maxPrecision` of 21.
|
|
46
|
+
|
|
47
|
+
## Modes
|
|
48
|
+
|
|
49
|
+
| Mode | Accepted input |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `basic` | Numbers, `+ − × ÷`, unary negation, parentheses, square root and percent. No constants. |
|
|
52
|
+
| `scientific` | Basic plus powers and roots, exponential and logarithms (natural, base 10 and any other base), trigonometric and inverse-trigonometric functions in the configured angle mode, absolute value, factorial, `π`, `e` and scientific notation. |
|
|
53
|
+
| `graphing` | Scientific plus the variable `x`, entered as `f(x)` or `y=f(x)`, for up to six expressions with pan, zoom, reset and keyboard tracing. |
|
|
54
|
+
|
|
55
|
+
Input is checked against the mode's function set after parsing, whichever key or
|
|
56
|
+
keystroke produced it. The factorial is the Gamma continuation off the integers,
|
|
57
|
+
matching Desmos, and a domain error at the negative integers.
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
`createCalculator()` takes `CortexCalculatorProviderConfig`: the provider-neutral
|
|
62
|
+
config with `settings` typed as `CortexCalculatorSettings`. A value outside its
|
|
63
|
+
range rejects `createCalculator()` with `invalid-state`.
|
|
64
|
+
|
|
65
|
+
| Field | Default | Accepted values |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `locale` | `"en-US"` | A BCP 47 tag; see [Localization](#localization). |
|
|
68
|
+
| `theme` | `"auto"` | `"light"`, `"dark"` or `"auto"`; see [Theming](#theming). |
|
|
69
|
+
| `restrictedMode` | `false` | See [Restricted mode](#restricted-mode). |
|
|
70
|
+
| `settings.angleMode` | `"degree"` | `"degree"` or `"radian"` |
|
|
71
|
+
| `settings.calculationPrecision` | `15` | Integer 1–21, in significant digits |
|
|
72
|
+
| `settings.displayPrecision` | `10` | Integer 1–12 |
|
|
73
|
+
| `settings.historyLimit` | `20` | Integer 0–50; `0` keeps no history |
|
|
74
|
+
| `settings.evaluationTimeLimitMs` | `1000` | Integer 100–2000 |
|
|
75
|
+
| `settings.allowedFunctions` | The mode's full set | Function ids from the mode's set |
|
|
76
|
+
| `settings.allowClipboard` | `true` | Boolean |
|
|
77
|
+
| `settings.messages` | Built-in catalog | Partial message overrides; see [Localization](#localization) |
|
|
78
|
+
| `settings.direction` | `"auto"` | `"ltr"`, `"rtl"` or `"auto"` |
|
|
79
|
+
| `settings.graph.viewport` | `-10` to `10` on both axes | `{ xMin, xMax, yMin, yMax }`, finite, each minimum below its maximum |
|
|
80
|
+
| `settings.graph.showAxes` | `true` | Boolean |
|
|
81
|
+
| `settings.graph.showGrid` | `true` | Boolean |
|
|
82
|
+
|
|
83
|
+
The function ids are `square-root`, `power`, `root`, `exponential`,
|
|
84
|
+
`natural-log`, `common-log`, `log-base-n`, `sine`, `cosine`, `tangent`,
|
|
85
|
+
`inverse-sine`, `inverse-cosine`, `inverse-tangent`, `absolute-value` and
|
|
86
|
+
`factorial`. Basic mode's set is `square-root` alone; scientific and graphing
|
|
87
|
+
take all fifteen. `allowedFunctions` only narrows the mode's set: an id the mode
|
|
88
|
+
lacks is dropped, and an unknown id is rejected. The keypad hides the keys of
|
|
89
|
+
removed functions.
|
|
90
|
+
|
|
91
|
+
## Restricted mode
|
|
92
|
+
|
|
93
|
+
`restrictedMode: true` blocks copy, cut, paste and the context menu in the
|
|
94
|
+
expression input, and forces `allowClipboard` to `false`. No setting relaxes it.
|
|
95
|
+
Clipboard blocking follows `restrictedMode` alone; `allowClipboard` is validated
|
|
96
|
+
and has no effect of its own. Clipboard policy beyond the input belongs to the
|
|
97
|
+
host.
|
|
98
|
+
|
|
99
|
+
## Limits
|
|
100
|
+
|
|
101
|
+
| Limit | Value | Error |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| Expression input | 1,024 UTF-16 code units | `expression-too-complex` |
|
|
104
|
+
| Parsed expression | 256 nodes, depth 32 | `expression-too-complex` |
|
|
105
|
+
| Evaluation time | `evaluationTimeLimitMs` | `evaluation-timeout` |
|
|
106
|
+
| Worker start | 20 seconds | `worker-unavailable` |
|
|
107
|
+
| Graph expressions | 6 | `invalid-state` on import |
|
|
108
|
+
| Graph samples | 200–1,200 points per expression, by plot width | — |
|
|
109
|
+
|
|
110
|
+
A timed-out evaluation terminates the worker, and the next calculation starts a
|
|
111
|
+
new one.
|
|
112
|
+
|
|
113
|
+
## Errors
|
|
114
|
+
|
|
115
|
+
Failures throw `CortexCalculatorError` with a `code` and a `recoverable` flag.
|
|
116
|
+
The calculator shows recoverable errors beside the input and keeps the learner's
|
|
117
|
+
edit buffer.
|
|
118
|
+
|
|
119
|
+
| Code | Raised for | Recoverable |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `invalid-expression` | Input that does not parse or has no finite real result, and a calculation superseded by newer input. | Yes |
|
|
122
|
+
| `unsupported-expression` | A command, function or symbol outside the mode's set, or an unsupported calculator type. | Yes |
|
|
123
|
+
| `expression-too-complex` | Input over the size limits. | Yes |
|
|
124
|
+
| `evaluation-timeout` | An evaluation over the time limit. | Yes |
|
|
125
|
+
| `invalid-state` | Invalid settings, a container that is not an element, and a rejected state import. | Yes |
|
|
126
|
+
| `worker-unavailable` | No browser or module-worker support, a worker that fails to start or stops, and a destroyed provider or calculator. | No |
|
|
127
|
+
|
|
128
|
+
## Telemetry
|
|
129
|
+
|
|
130
|
+
`onTelemetry` receives `pie-tool-operation-start`, `pie-tool-operation-success`
|
|
131
|
+
and `pie-tool-operation-error` for each evaluation. The payload carries
|
|
132
|
+
`toolId: "calculator"`, `backend: "cortex"`, `calculatorType`,
|
|
133
|
+
`operation: "evaluate"`, `duration` on completion and `errorType` (the error
|
|
134
|
+
code) on failure. It never carries expressions, results, history, state or graph
|
|
135
|
+
coordinates. A throwing or rejecting callback does not affect the calculator.
|
|
136
|
+
|
|
137
|
+
## State
|
|
138
|
+
|
|
139
|
+
`exportState()` returns the provider-neutral `CalculatorState` with
|
|
140
|
+
`provider: "cortex"`, `value` equal to the current input, `history` capped at
|
|
141
|
+
`historyLimit`, and `providerState` in this schema:
|
|
25
142
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
143
|
+
```ts
|
|
144
|
+
import type { CortexCalculatorStateV1 } from "@pie-players/pie-calculator-cortex";
|
|
145
|
+
|
|
146
|
+
const providerState: CortexCalculatorStateV1 = {
|
|
147
|
+
schema: "pie-calculator-cortex",
|
|
148
|
+
version: 1,
|
|
149
|
+
type: "graphing",
|
|
150
|
+
angleMode: "radian",
|
|
151
|
+
calculationPrecision: 15,
|
|
152
|
+
displayPrecision: 10,
|
|
153
|
+
inputLatex: "\\sin(x)",
|
|
154
|
+
graph: {
|
|
155
|
+
viewport: { xMin: -10, xMax: 10, yMin: -10, yMax: 10 },
|
|
156
|
+
expressions: [
|
|
157
|
+
{ id: "e1", latex: "\\sin(x)", colorIndex: 0, lineStyle: "solid", hidden: false },
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
```
|
|
30
162
|
|
|
31
|
-
|
|
163
|
+
`importState()` is atomic: the calculator is unchanged unless the whole state
|
|
164
|
+
validates. It rejects with `invalid-state`:
|
|
32
165
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
166
|
+
- a `provider` other than `"cortex"`, or a `type` other than the calculator's;
|
|
167
|
+
- a `schema` other than `"pie-calculator-cortex"` or a `version` other than `1`;
|
|
168
|
+
- a `value` that differs from `inputLatex`;
|
|
169
|
+
- a precision outside its range;
|
|
170
|
+
- `graph` on a non-graphing state, or none on a graphing one;
|
|
171
|
+
- more than six expressions, an `id` that is empty, longer than 128 characters
|
|
172
|
+
or repeated, a `colorIndex` outside 0–5, or a `lineStyle` other than `solid`,
|
|
173
|
+
`dashed` or `dotted`;
|
|
174
|
+
- more history entries than `historyLimit`.
|
|
37
175
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
176
|
+
A persisted expression or history entry that fails validation rejects the import
|
|
177
|
+
with that expression's own error code.
|
|
178
|
+
|
|
179
|
+
Import restores the angle mode and keeps the calculator's configured precision.
|
|
180
|
+
Unknown fields are ignored and not re-emitted. Version 1 has no migration path,
|
|
181
|
+
and no state converts to or from another provider. The toolkit's
|
|
182
|
+
`<pie-tool-calculator>` does not persist state, so saving and restoring belong to
|
|
183
|
+
hosts that drive the adapter directly.
|
|
45
184
|
|
|
46
185
|
## Localization
|
|
47
186
|
|
|
@@ -53,11 +192,11 @@ displayed answer, and writing direction.
|
|
|
53
192
|
|
|
54
193
|
One resolver serves the mathfield, the keypad's separator key and the displayed
|
|
55
194
|
answer, so an `nl-NL` calculator whose keypad writes `1,5` answers `1,5`. The
|
|
56
|
-
locale reaches the display only:
|
|
57
|
-
the serialized state stay `.`-separated, so state saved
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
intact.
|
|
195
|
+
locale reaches the display only: the value `evaluate()` returns, the history
|
|
196
|
+
entries a host reads and the serialized state stay `.`-separated, so state saved
|
|
197
|
+
under one locale is not reinterpreted under another. The display swaps the
|
|
198
|
+
separator without reformatting, which keeps `displayPrecision` and an exponential
|
|
199
|
+
answer like `2.432902008e+18` intact.
|
|
61
200
|
|
|
62
201
|
Every package-owned visible string, accessible name, status, and recoverable
|
|
63
202
|
error can be replaced with typed per-instance messages:
|
|
@@ -76,181 +215,203 @@ await provider.createCalculator("basic", container, {
|
|
|
76
215
|
```
|
|
77
216
|
|
|
78
217
|
Unspecified messages fall back to the selected built-in catalog, then English.
|
|
79
|
-
Message templates retain their named placeholders,
|
|
80
|
-
`{lineStyle}`, and `{result}`. `settings.direction` defaults
|
|
81
|
-
derives `ltr` or `rtl` from the locale; a host
|
|
82
|
-
`"rtl"` when its language policy requires it.
|
|
83
|
-
|
|
84
|
-
## Keypad
|
|
85
|
-
|
|
86
|
-
Basic and scientific render a display and a keypad; graphing puts the keypad in its
|
|
87
|
-
expression rail. The keypad is this package's own — real `<button>` elements with
|
|
88
|
-
localized accessible names, one tab stop with arrow-key movement inside it, and PIE
|
|
89
|
-
tokens throughout.
|
|
90
|
-
|
|
91
|
-
It is deliberately not MathLive's virtual keyboard, which is switched off entirely
|
|
92
|
-
(`mathVirtualKeyboardPolicy = "manual"`) rather than merely hidden. MathLive's is a
|
|
93
|
-
viewport-fixed singleton whose keycaps are `div[tabindex="-1"]` with no `role` and
|
|
94
|
-
whose toggle is a `div[role="button"]` with no `tabindex`, so it contains no
|
|
95
|
-
focusable elements at all and cannot be opened or operated by keyboard or switch
|
|
96
|
-
access; under `"auto"` it also auto-shows on any touch-capable device, dropping
|
|
97
|
-
itself across the bottom of the assessment rather than inside the tool panel. Its
|
|
98
|
-
`container` setter throws inside an iframe, which is how assessments are commonly
|
|
99
|
-
delivered.
|
|
100
|
-
|
|
101
|
-
Keys are gated on `settings.allowedFunctions`, so a host that narrows the set gets a
|
|
102
|
-
keypad that cannot offer a key the validator would reject. Basic omits the constants
|
|
103
|
-
outright, matching `validateSymbol`. Scientific and graphing put their function keys
|
|
104
|
-
on a second layer rather than in extra rows: a row costs about 50px of panel height
|
|
105
|
-
at every size that ships, and eight rows in one layer puts the keypad past the
|
|
106
|
-
panel's 480px floor. Four rows is the budget; the graphing layer spends five because
|
|
107
|
-
it carries the graph keys too. The e2e suite switches to every layer and measures it
|
|
108
|
-
at both the size the panel opens at and its resize floor.
|
|
109
|
-
|
|
110
|
-
The commit key is on every layer, in the same corner. On the numeric layer alone it
|
|
111
|
-
was unreachable from the function keys — Enter still committed, but a pointer or
|
|
112
|
-
switch-access user has no Enter.
|
|
113
|
-
|
|
114
|
-
Every key inserts a template with at most one placeholder. A second is
|
|
115
|
-
unreachable: `ArrowRight` leaves a subscript or a fraction rather than crossing to
|
|
116
|
-
the next placeholder, and MathLive binds `moveToNextPlaceholder` to Tab, which this
|
|
117
|
-
keypad spends on being a single tab stop. `nth-root` and `fraction` therefore use
|
|
118
|
-
`#@` to take the expression already typed as their second operand, and `log-base-n`
|
|
119
|
-
fills its base and lets the argument follow the subscript.
|
|
120
|
-
|
|
121
|
-
Layouts live as data in `src/keypad-layouts.ts`. Adding a key needs a message key in
|
|
122
|
-
both catalogs — `as const satisfies CortexCalculatorMessages` makes that a
|
|
123
|
-
compile-time obligation. Where a key's visible label is a word, its accessible name
|
|
124
|
-
must contain that word (WCAG 2.5.3, and what voice control speaks): `keySine` is
|
|
125
|
-
`"sin, sine"`, not `"sine of"`.
|
|
126
|
-
|
|
127
|
-
## Fitting the panel
|
|
128
|
-
|
|
129
|
-
The panel is two surfaces with no card between them: a screen and a console, each
|
|
130
|
-
running to its edges. The screen holds the tape, the live expression and the answer,
|
|
131
|
-
with the angle mode pinned above its scroller so history passes behind it; the
|
|
132
|
-
console is the keypad's recessed plane, carrying the layer tabs and the backspace and
|
|
133
|
-
clear icons above the grid — inline SVG in `currentColor`, because `⌫` is the face a
|
|
134
|
-
backspace button wants and the code point least likely to be in a host's font stack. Nothing sits on bare card, and the type's name is not drawn — the
|
|
135
|
-
tool shell's header already carries it, and a second copy cost 46px of a 500px panel.
|
|
136
|
-
It stays as visually-hidden text for the document outline. `--cortex-tape-inset` and
|
|
137
|
-
the keypad's inline padding are one value, so the mathfield's text and the first key
|
|
138
|
-
column share a left edge.
|
|
139
|
-
|
|
140
|
-
A tool panel is resizable, so the height available is a runtime fact and every fixed
|
|
141
|
-
size in the tool answers to it. `CalculatorView.svelte` measures its own box with a
|
|
142
|
-
`ResizeObserver` and stamps `data-pie-density` — `comfortable` at 400px of content
|
|
143
|
-
and up, `compact` to 320, `tight` below — and the metrics live as tokens that each
|
|
144
|
-
tier re-declares in one place: key and control target sizes, the display's floor,
|
|
145
|
-
the result's type size and the board's floor. A `ResizeObserver` rather than a
|
|
146
|
-
`container-type: size` query, which carries `contain: layout` and would make the
|
|
147
|
-
calculator the containing block for every fixed-position descendant, MathLive's
|
|
148
|
-
popovers among them.
|
|
149
|
-
|
|
150
|
-
Keys hold the 44px of WCAG 2.5.5 at every size a panel opens at, which is what the
|
|
151
|
-
tiers are measured against: basic needs 398px of content and scientific 385px, and
|
|
152
|
-
both open at more. Below that, keys give up height before the keypad gives up rows —
|
|
153
|
-
a row scrolled out of the panel costs a pointer or switch-access learner the key
|
|
154
|
-
entirely — and the smallest tier is 28px, clear of 2.5.8's 24px floor at Level AA.
|
|
155
|
-
|
|
156
|
-
Nothing is ever clipped. The calculator root scrolls its own content as the floor
|
|
157
|
-
case; it cannot be left to the tool shell, because the wrapper pins the calculator to
|
|
158
|
-
`height: 100% !important` inside an `overflow: hidden` box, so the shell's
|
|
159
|
-
`overflow-y: auto` never sees anything to scroll. Above that floor the graphing view
|
|
160
|
-
places the pressure deliberately: stacked, the two panels hold their content and the
|
|
161
|
-
calculator takes one scroll; side by side they scroll in their own columns instead,
|
|
162
|
-
so a readout does not push the plot off the panel. Flex shrinking is what makes this
|
|
163
|
-
load-bearing — an item shrunk below its content paints outside its box rather than
|
|
164
|
-
clipping, which is how keypad rows were drawn over the graph controls.
|
|
218
|
+
Unknown keys are ignored. Message templates retain their named placeholders,
|
|
219
|
+
such as `{index}`, `{lineStyle}`, and `{result}`. `settings.direction` defaults
|
|
220
|
+
to `"auto"`, which derives `ltr` or `rtl` from the locale; a host sets `"ltr"`
|
|
221
|
+
or `"rtl"` when its language policy requires it.
|
|
165
222
|
|
|
166
223
|
## Theming
|
|
167
224
|
|
|
168
225
|
`theme: "light" | "dark" | "auto"` supplies accessible package defaults.
|
|
169
226
|
`"auto"` follows `prefers-color-scheme`.
|
|
170
227
|
|
|
171
|
-
Those defaults are
|
|
228
|
+
Those defaults are fallbacks. Every color resolves as
|
|
172
229
|
`var(--pie-x, var(--cortex-x))`, so a host's tokens reach the tool and the
|
|
173
|
-
package's own values apply only where the host has none.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
`tests/calculator-cortex-style-contract.test.ts`
|
|
179
|
-
returns.
|
|
230
|
+
package's own values apply only where the host has none. `@pie-players/pie-theme`
|
|
231
|
+
publishes ten `[data-color-scheme]` PNP palettes and marks every token used here
|
|
232
|
+
as `required`, so the package never declares a `--pie-*` token on the calculator
|
|
233
|
+
element: a declaration there would override every ancestor, a learner's
|
|
234
|
+
color-scheme accommodation included.
|
|
235
|
+
`tests/calculator-cortex-style-contract.test.ts` enforces it.
|
|
180
236
|
|
|
181
237
|
Consumed: `--pie-text`, `--pie-white`, `--pie-background-dark`, `--pie-border`,
|
|
182
238
|
`--pie-border-gray`, `--pie-blue-grey-300`, `--pie-button-bg`,
|
|
183
|
-
`--pie-button-color`, `--pie-button-hover-bg`, `--pie-button-
|
|
184
|
-
`--pie-button-
|
|
185
|
-
`--pie-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
`--pie-
|
|
192
|
-
themes and in all ten schemes.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
`--pie-font-scale` is **not** consumed, matching the recorded decision in
|
|
239
|
+
`--pie-button-color`, `--pie-button-hover-bg`, `--pie-button-hover-border`,
|
|
240
|
+
`--pie-button-active-bg`, `--pie-button-focus-outline`, `--pie-primary`,
|
|
241
|
+
`--pie-primary-dark`, `--pie-incorrect`, `--pie-incorrect-secondary`,
|
|
242
|
+
`--pie-content-emphasis` and `--pie-font-family`.
|
|
243
|
+
|
|
244
|
+
`--pie-background` is excluded. It is the page token, which a host may point at
|
|
245
|
+
its own backdrop or at a translucent value, and resolving the calculator's fill
|
|
246
|
+
through it would take every contrast guarantee out of this package's hands.
|
|
247
|
+
Surfaces take `--pie-white` for the card and `--pie-background-dark` for the
|
|
248
|
+
recessed keypad plane, both opaque in the base themes and in all ten schemes. The
|
|
249
|
+
card and keypad surfaces are package-private (`--pie-calculator-surface`,
|
|
250
|
+
`--pie-calculator-surface-raised`) and carry no compatibility guarantee.
|
|
251
|
+
|
|
252
|
+
`--pie-font-scale` is not consumed, matching the recorded decision in
|
|
198
253
|
`section-player/tests/content-text-follows-font-scale.test.ts`: the font
|
|
199
254
|
accommodation applies to what the learner reads, and a keypad growing with the
|
|
200
|
-
passage is a layout problem
|
|
255
|
+
passage is a layout problem.
|
|
201
256
|
|
|
202
257
|
Graph colors have package-owned hooks because no canonical series palette
|
|
203
258
|
exists: `--pie-calculator-series-1`, `--pie-calculator-series-2`,
|
|
204
259
|
`--pie-calculator-series-3`, `--pie-calculator-series-4`,
|
|
205
|
-
`--pie-calculator-series-5`, and `--pie-calculator-series-6
|
|
206
|
-
has a solid, dashed, or dotted line
|
|
207
|
-
3:1 contrast against the graph surface
|
|
260
|
+
`--pie-calculator-series-5`, and `--pie-calculator-series-6`, all registered
|
|
261
|
+
`component-public` tokens. Each series also has a solid, dashed, or dotted line
|
|
262
|
+
style; hosts overriding colors must retain 3:1 contrast against the graph surface
|
|
263
|
+
and keep the palette distinguishable.
|
|
264
|
+
|
|
265
|
+
JSXGraph's own axis and grid defaults are light in every theme, so the package
|
|
266
|
+
themes the plot's axes, tick labels and grid from the resolved tokens and
|
|
267
|
+
re-applies them when `theme: "auto"` follows an OS change. The plot is
|
|
268
|
+
`aria-hidden`, out of axe's reach, so `e2e/calculator-cortex.spec.ts` asserts its
|
|
269
|
+
contrast directly: tick labels at 4.5:1 as text and axes at 3:1 as a graphical
|
|
270
|
+
object.
|
|
271
|
+
|
|
272
|
+
## Keypad
|
|
273
|
+
|
|
274
|
+
Basic and scientific render a display and a keypad; graphing puts the keypad in
|
|
275
|
+
its expression rail. The keypad is this package's own: real `<button>` elements
|
|
276
|
+
with localized accessible names, one tab stop with arrow-key movement inside it,
|
|
277
|
+
and PIE tokens throughout.
|
|
278
|
+
|
|
279
|
+
MathLive's virtual keyboard is switched off entirely
|
|
280
|
+
(`mathVirtualKeyboardPolicy = "manual"`). Its keycaps are `div[tabindex="-1"]`
|
|
281
|
+
with no `role` and its toggle is a `div[role="button"]` with no `tabindex`, so it
|
|
282
|
+
cannot be opened or operated by keyboard or switch access. It is a viewport-fixed
|
|
283
|
+
singleton that, under `"auto"`, shows on any touch-capable device across the
|
|
284
|
+
bottom of the assessment, and its `container` setter throws inside an iframe,
|
|
285
|
+
where assessments are commonly delivered.
|
|
286
|
+
|
|
287
|
+
Keys are gated on `settings.allowedFunctions`, so a narrowed set never offers a
|
|
288
|
+
key the validator would reject, and basic omits the constants. Scientific and
|
|
289
|
+
graphing put their function keys on a second layer: a row costs about 50px of
|
|
290
|
+
panel height at every shipped size, and eight rows in one layer puts the keypad
|
|
291
|
+
past the panel's 480px floor. Four rows is the budget; the graphing layer spends
|
|
292
|
+
five because it carries the graph keys too. The e2e suite switches to every layer
|
|
293
|
+
and measures it at both the size the panel opens at and its resize floor. The
|
|
294
|
+
commit key is on every layer, in the same corner, because a pointer or
|
|
295
|
+
switch-access user has no Enter key.
|
|
296
|
+
|
|
297
|
+
Every key inserts a template with at most one placeholder. A second is
|
|
298
|
+
unreachable: `ArrowRight` leaves a subscript or a fraction without crossing to
|
|
299
|
+
the next placeholder, and MathLive binds `moveToNextPlaceholder` to Tab, which
|
|
300
|
+
this keypad spends on being a single tab stop. `nth-root` and `fraction`
|
|
301
|
+
therefore use `#@` to take the expression already typed as their second operand,
|
|
302
|
+
and `log-base-n` fills its base and lets the argument follow the subscript.
|
|
303
|
+
|
|
304
|
+
Layouts live as data in `src/keypad-layouts.ts`. Adding a key needs a message key
|
|
305
|
+
in both catalogs, which `as const satisfies CortexCalculatorMessages` makes a
|
|
306
|
+
compile-time obligation. Where a key's visible label is a word, its accessible
|
|
307
|
+
name contains that word (WCAG 2.5.3, and what voice control speaks): `keySine` is
|
|
308
|
+
`"sin, sine"`.
|
|
309
|
+
|
|
310
|
+
## Panel fit
|
|
311
|
+
|
|
312
|
+
The panel is two surfaces with no card between them, a screen and a console, each
|
|
313
|
+
running to its edges. The screen holds the tape, the live expression and the
|
|
314
|
+
answer, with the angle mode pinned above its scroller so history passes behind
|
|
315
|
+
it. The console is the keypad's recessed plane, carrying the layer tabs and the
|
|
316
|
+
backspace and clear icons above the grid, drawn as inline SVG in `currentColor`
|
|
317
|
+
because `⌫` is the code point least likely to be in a host's font stack. The
|
|
318
|
+
type's name is not drawn, since the tool shell's header already carries it and a
|
|
319
|
+
second copy costs 46px of a 500px panel; it stays as visually hidden text for the
|
|
320
|
+
document outline. `--cortex-tape-inset` and the keypad's inline padding are one
|
|
321
|
+
value, so the mathfield's text and the first key column share a left edge.
|
|
322
|
+
|
|
323
|
+
A tool panel is resizable, so every fixed size answers to the height available
|
|
324
|
+
at runtime. `CalculatorView.svelte` measures its own box with a `ResizeObserver`
|
|
325
|
+
and stamps `data-pie-density`: `comfortable` at 400px of content and up,
|
|
326
|
+
`compact` to 320px, `tight` below. Each tier re-declares the metrics tokens in
|
|
327
|
+
one place: key and control target sizes, the display's floor, the result's type
|
|
328
|
+
size and the board's floor. A `container-type: size` query would carry
|
|
329
|
+
`contain: layout` and make the calculator the containing block for every
|
|
330
|
+
fixed-position descendant, MathLive's popovers among them.
|
|
331
|
+
|
|
332
|
+
Keys hold the 44px of WCAG 2.5.5 at every size a panel opens at, which is what the
|
|
333
|
+
tiers are measured against: basic needs 398px of content and scientific 385px,
|
|
334
|
+
and both open at more. Below that, keys give up height before the keypad gives up
|
|
335
|
+
rows, because a row scrolled out of the panel costs a pointer or switch-access
|
|
336
|
+
learner the key entirely. The smallest tier is 28px, clear of 2.5.8's 24px floor
|
|
337
|
+
at Level AA.
|
|
338
|
+
|
|
339
|
+
Nothing is clipped. The calculator root scrolls its own content as the floor
|
|
340
|
+
case, because the wrapper pins the calculator to `height: 100% !important` inside
|
|
341
|
+
an `overflow: hidden` box, so the shell's `overflow-y: auto` never has anything
|
|
342
|
+
to scroll. Above that floor the graphing view places the pressure: stacked, the
|
|
343
|
+
two panels hold their content and the calculator takes one scroll; side by side,
|
|
344
|
+
each scrolls in its own column, so a readout does not push the plot off the
|
|
345
|
+
panel. The arrangement is load-bearing because a flex item shrunk below its
|
|
346
|
+
content paints outside its box without clipping.
|
|
347
|
+
|
|
348
|
+
## Isolated demos
|
|
208
349
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
350
|
+
Run `bun run --cwd packages/calculator-cortex demo`, then open the basic,
|
|
351
|
+
scientific, or graphing page from the mode navigation. Each page mounts one
|
|
352
|
+
calculator directly through `CortexCalculatorProvider`, without an assessment
|
|
353
|
+
player, toolkit coordinator or tool wrapper.
|
|
354
|
+
|
|
355
|
+
The demo controls switch interface language, theme, and text direction by
|
|
356
|
+
destroying and recreating only that calculator instance. **Panel size** resizes
|
|
357
|
+
the container to the box the tool shell gives the calculator, without recreating
|
|
358
|
+
it. Check every change at both sizes it offers: *Shipped tool panel* is what the
|
|
359
|
+
panel opens at for that type, and *Panel minimum* is its configured resize floor.
|
|
360
|
+
The package's size-dependent rules are container queries on width and density
|
|
361
|
+
tiers on height, which a fluid demo at 1280px reaches neither of.
|
|
216
362
|
|
|
217
|
-
##
|
|
363
|
+
## Test coverage
|
|
218
364
|
|
|
219
365
|
Feature coverage rests on three suites with different jobs.
|
|
220
366
|
|
|
221
|
-
`tests/calculator-cortex-keypad-coverage.test.ts`
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
Implicit multiplication, parenthesised groups and the inverse-trigonometric keys
|
|
225
|
-
were all refused by the expression policy until this test reached them.
|
|
367
|
+
`tests/calculator-cortex-keypad-coverage.test.ts` maintains itself: every shipped
|
|
368
|
+
keypad key must map to an expression proven to validate and evaluate. A key with
|
|
369
|
+
no entry fails, and an entry naming a retired key fails.
|
|
226
370
|
|
|
227
371
|
`tests/calculator-cortex-scenarios.test.ts` pins values, traced from the PRD's
|
|
228
372
|
capability spec: precedence, boundary values, display thresholds, the domain
|
|
229
373
|
edges of every function, and the refusals each mode owes. Its LaTeX entry shapes
|
|
230
374
|
are derived from the public corner-case corpora in `mathquill` and Doenet's
|
|
231
|
-
`math-expressions`, which test their own parsers
|
|
375
|
+
`math-expressions`, which test their own parsers; the shapes carry over and the
|
|
232
376
|
expectations do not.
|
|
233
377
|
|
|
234
378
|
`tests/calculator-cortex-corpus.test.ts` covers volume, and asserts properties
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
379
|
+
because a fixture of individual expectations at corpus size fails in ways nobody
|
|
380
|
+
can act on. The corpus is GSM8K's inline calculator annotations (`<<48/2=24>>`,
|
|
381
|
+
expression/result pairs authored to be executed by a calculator) over
|
|
382
|
+
`0-9 + - * / . ( )` alone, which is exactly basic mode's capability set. Four
|
|
383
|
+
properties hold: every outcome is a declared error code or an answer, never an
|
|
384
|
+
undeclared throw; every answer matches its authored result numerically, since the
|
|
385
|
+
annotations carry their author's currency formatting; capability sets nest, so
|
|
386
|
+
what basic accepts scientific and graphing accept identically; and a displayed
|
|
387
|
+
answer re-entered answers itself. Only the second uses the labels; the rest would
|
|
388
|
+
hold against any corpus.
|
|
245
389
|
|
|
246
390
|
300 entries are committed under `tests/fixtures/`, chosen by a deterministic
|
|
247
|
-
stride so regenerating produces no diff. For the full
|
|
391
|
+
stride so regenerating produces no diff. For the full 10,770:
|
|
248
392
|
|
|
249
393
|
```bash
|
|
250
|
-
bun run test:corpus
|
|
394
|
+
bun run --cwd packages/calculator-cortex test:corpus
|
|
251
395
|
```
|
|
252
396
|
|
|
253
|
-
Playwright covers what
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
397
|
+
Playwright covers what MathLive builds from real keystrokes, which no unit test
|
|
398
|
+
reaches. Those tests assert only the answer: turning `/` into a fraction is
|
|
399
|
+
MathLive's behavior, while the LaTeX it hands to `validateExpression` is this
|
|
400
|
+
package's seam, and the two have disagreed, `2x` and `(4+5)` among them.
|
|
401
|
+
|
|
402
|
+
## Exports
|
|
403
|
+
|
|
404
|
+
| Export | Kind |
|
|
405
|
+
| --- | --- |
|
|
406
|
+
| `CortexCalculatorProvider` | The `CalculatorProvider` implementation (`providerId: "cortex"`). |
|
|
407
|
+
| `CortexCalculatorError`, `CortexCalculatorErrorCode` | Error class and its code union. |
|
|
408
|
+
| `cortexEnglishMessages`, `cortexDutchMessages` | The built-in message catalogs. |
|
|
409
|
+
| `localeDirection(locale)` | The `ltr` or `rtl` direction the package derives from a locale. |
|
|
410
|
+
| `CortexCalculatorProviderInit`, `CortexCalculatorProviderConfig`, `CortexCalculatorSettings`, `CortexGraphSettings`, `CortexGraphViewport`, `CortexAngleMode`, `CortexFunctionId`, `CortexTextDirection` | Configuration types. |
|
|
411
|
+
| `CortexCalculatorMessages`, `CortexCalculatorMessageKey`, `CortexCalculatorMessageOverrides` | Message types. |
|
|
412
|
+
| `CortexCalculatorState`, `CortexCalculatorStateV1`, `CortexGraphState`, `CortexGraphExpressionState`, `CortexGraphLineStyle` | State types. |
|
|
413
|
+
|
|
414
|
+
## License
|
|
415
|
+
|
|
416
|
+
MIT. `LICENSE.md` lists the bundled MathLive, CortexJS Compute Engine and
|
|
417
|
+
JSXGraph with their licenses.
|