@barocss/math-editor 0.4.0 → 0.5.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/API-SESSION.md +10 -0
- package/API-WEB-COMPONENT.md +2 -0
- package/CHANGELOG.md +61 -0
- package/EDITING-SCENARIOS.md +174 -0
- package/IMPLEMENTATION.md +95 -0
- package/LATEX-GUIDE.md +22 -0
- package/LATEX-MODEL.md +12 -0
- package/LATEX-SCOPE.md +10 -1
- package/README.md +73 -19
- package/RELEASING.md +81 -10
- package/RENDERING-TESTS.md +79 -0
- package/ROADMAP.md +132 -5
- package/VALIDATION.md +331 -0
- package/dist/context-tools.d.ts +21 -0
- package/dist/context-tools.js +37 -0
- package/dist/dom/context-keyboard.d.ts +2 -0
- package/dist/dom/context-keyboard.js +22 -0
- package/dist/dom/menu-position.d.ts +3 -0
- package/dist/dom/menu-position.js +45 -1
- package/dist/dom.d.ts +2 -0
- package/dist/dom.js +187 -38
- package/dist/latex.js +11 -0
- package/dist/locales/en.js +13 -1
- package/dist/locales/en.json +13 -1
- package/dist/locales/ko.js +13 -1
- package/dist/locales/ko.json +13 -1
- package/dist/math-editor.d.ts +3 -1
- package/dist/math-editor.js +394 -280
- package/dist/math-layout.d.ts +12 -0
- package/dist/math-layout.js +59 -0
- package/dist/math-spacing.d.ts +13 -0
- package/dist/math-spacing.js +87 -0
- package/dist/model.d.ts +4 -0
- package/dist/model.js +49 -4
- package/dist/root-transform.d.ts +19 -0
- package/dist/root-transform.js +69 -0
- package/dist/suggestions.d.ts +9 -0
- package/dist/suggestions.js +53 -0
- package/dist/web-component.js +9 -1
- package/package.json +11 -3
- package/src/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/src/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/src/fonts/README.md +18 -0
- package/src/shapes/README.md +21 -0
- package/src/shapes/parenthesis-bottom.svg +1 -0
- package/src/shapes/parenthesis-top.svg +1 -0
- package/src/shapes/parenthesis.svg +1 -0
- package/src/shapes/radical.svg +1 -0
- package/src/style.css +695 -44
package/VALIDATION.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# Validation report
|
|
2
2
|
|
|
3
|
+
## Inline fence transformation suggestions — workspace, 2026-09-09
|
|
4
|
+
|
|
5
|
+
Core: **584 tests in 36 files passed**. Core type checking and the main site/docs
|
|
6
|
+
build passed. `fence-suggestions-check.js` passed in Chromium for rich React and
|
|
7
|
+
all nine host demos, including menu reopening, keyboard acceptance, cancellation,
|
|
8
|
+
retained caret, continued typing and separate Undo entries. Editor.js is a block
|
|
9
|
+
host; this run does not claim an inline placement for that adapter.
|
|
10
|
+
|
|
11
|
+
`fence-context-check.js` passed all eight bracket choices through the optional
|
|
12
|
+
footer in the same ten editor views. `fence-inline-options-check.js` passed in a
|
|
13
|
+
native inline iframe with toolbar and footer disabled, including nearest nested
|
|
14
|
+
fence targeting, ko/en switching, unchanged document on locale changes, retained
|
|
15
|
+
input focus/caret, continued typing and teardown.
|
|
16
|
+
|
|
17
|
+
Existing root conversion suggestions were rerun in React and Quill and passed.
|
|
18
|
+
`fence-rendering-check.js` verifies the exported source after changing a bracket
|
|
19
|
+
around a fraction. The editor and KaTeX screenshots were visually inspected:
|
|
20
|
+
`output/playwright/fence-transform-editor.png` and
|
|
21
|
+
`output/playwright/fence-transform-katex.png`. This uses the existing delimiter
|
|
22
|
+
renderer and does not claim pixel-identical layout.
|
|
23
|
+
|
|
24
|
+
See EDIT-029 through EDIT-032 for acceptance criteria. This is local Chromium
|
|
25
|
+
workspace evidence, not a release, real OS IME or cross-browser certification.
|
|
26
|
+
|
|
27
|
+
## Contextual radical tools — workspace, 2026-09-09
|
|
28
|
+
|
|
29
|
+
Core: **570 tests passed in 35 files**. Type checking and the main documentation/demo build passed.
|
|
30
|
+
|
|
31
|
+
`context-tools-check.js` passed in local Chromium for rich React and all nine
|
|
32
|
+
host examples: Tiptap, ProseMirror, Lexical, Editor.js, TinyMCE, CKEditor, Quill,
|
|
33
|
+
Slate and Gutenberg. It covers query-independent discovery, retained tools after
|
|
34
|
+
suggestion dismissal, F6/Escape focus, conversion, selected index editing, a
|
|
35
|
+
non-square-index explanation, radicand retention, Undo, leaving a radical and
|
|
36
|
+
hiding tools during model range selection.
|
|
37
|
+
|
|
38
|
+
`context-tools-options-check.js` passed for nearest nested targeting, native DOM
|
|
39
|
+
inside an iframe, owner-document keyboard focus, dynamic `contextTools`, en/ko
|
|
40
|
+
labels, synthetic composition guards, outside blur, teardown, and dynamic Web
|
|
41
|
+
Component `context-tools` attributes. Synthetic events do not certify OS IME.
|
|
42
|
+
|
|
43
|
+
The footer was visually inspected separately from the suggestion popup. Evidence
|
|
44
|
+
is in `output/playwright/context-tools-footer.png` and the two browser fixtures.
|
|
45
|
+
EDIT-021 through EDIT-028 record acceptance criteria and remaining checks: native
|
|
46
|
+
Tab order, document equality across locale changes, individual framework-wrapper
|
|
47
|
+
option smoke tests, and broader browsers/platforms. The new footer currently
|
|
48
|
+
covers square/indexed roots. Other structure families remain roadmap work.
|
|
49
|
+
These are workspace-source checks; no package or site was published.
|
|
50
|
+
|
|
51
|
+
## Fractional scripts and empty-slot deletion — workspace, 2026-09-09
|
|
52
|
+
|
|
53
|
+
Core: 568 tests passed in 35 files; type checking and formatting passed. The
|
|
54
|
+
main documentation/demo build passed. `empty-slot-deletion-check.js` passed
|
|
55
|
+
16 real-key checks across React/native DOM: Delete and Backspace from empty
|
|
56
|
+
root, fraction, exponent and root-index slots, retained content, focus and Undo.
|
|
57
|
+
Grid cells and populated rows are protected by the added core tests.
|
|
58
|
+
|
|
59
|
+
`rendering-regression-check.js` passed 12 Chromium comparisons: three formulas,
|
|
60
|
+
two base sizes and two renderers. Fraction terms matched KaTeX font sizes;
|
|
61
|
+
vertical offset differences were at most 0.109em against a 0.25em threshold.
|
|
62
|
+
Horizontal gap, active-input size and focus assertions passed. Matching-size
|
|
63
|
+
editor/KaTeX screenshots were visually inspected for the indexed-root power
|
|
64
|
+
and simple fractional power. Full pixel identity is not claimed.
|
|
65
|
+
|
|
66
|
+
Local evidence: `output/playwright/math-rendering-regression-evidence.json`
|
|
67
|
+
records metrics and source SHA-256 values. Screenshot pairs use the paths in
|
|
68
|
+
[Rendering checks](RENDERING-TESTS.md). This is workspace-source evidence, not
|
|
69
|
+
packed-release, all-host, cross-browser or full notation certification.
|
|
70
|
+
|
|
71
|
+
See [Editing scenarios](EDITING-SCENARIOS.md) for stable scenario IDs, acceptance criteria, coverage gaps and per-run reporting.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## Radical conversion suggestions — workspace, 2026-09-09
|
|
75
|
+
|
|
76
|
+
Core tests: 558 passed in 34 files; type checking passed. Ten added cases cover
|
|
77
|
+
nested radicands, retained node IDs, selected index `2`, safe reverse conversion,
|
|
78
|
+
non-square and structured indices, nearest-root targeting, stale targets, empty
|
|
79
|
+
slots, localization, literal text and Undo/Redo.
|
|
80
|
+
|
|
81
|
+
`root-transform-check.js` passed in Chromium for rich React and Quill's native
|
|
82
|
+
DOM field. It checks pointer and arrow/Enter acceptance, selected index editing,
|
|
83
|
+
radicand preservation, reverse conversion, and rejection of index `3` conversion.
|
|
84
|
+
The generated LaTeX and KaTeX preview match; editor/KaTeX radical screenshots
|
|
85
|
+
were inspected. This does not certify every browser or every host adapter.
|
|
86
|
+
|
|
87
|
+
## Selection suggestion navigation — workspace, 2026-09-09
|
|
88
|
+
|
|
89
|
+
`apps/math-integrations/tests/selection-suggestions-check.js` passed in Chromium
|
|
90
|
+
for rich React and all nine host integrations. The check covers Shift+arrow
|
|
91
|
+
ranges, Up/Down navigation, the Alt+Down alias, Enter wrapping the selected
|
|
92
|
+
characters, retained input focus after wrapping, and native mouse-drag selection.
|
|
93
|
+
No page errors were reported. Core tests: 548 passed; core type check passed.
|
|
94
|
+
`selection-collapse-check.js` also passed for React horizontal caret recovery,
|
|
95
|
+
fraction boundaries and resumed typing. `tinymce-inline-check.js` passed for
|
|
96
|
+
inline and classic iframe hosts, including vertical caret recovery after Escape
|
|
97
|
+
dismisses the wrapping menu, Apply/Cancel, serialization and Undo/Redo.
|
|
98
|
+
|
|
99
|
+
A visible wrapping menu now owns vertical arrows. Left/Right still collapses
|
|
100
|
+
a model selection to its ordered edge. This replaces the Alt-only menu
|
|
101
|
+
navigation policy in the historical selection-collapse record below.
|
|
102
|
+
|
|
3
103
|
## Styling and themes — workspace, 2026-09-09
|
|
4
104
|
|
|
5
105
|
Release browser regression: all 385 existing Chromium cases passed across the
|
|
@@ -367,3 +467,234 @@ Core unit regression: **548 tests**. Shared integrations: **52 tests**. Core, in
|
|
|
367
467
|
- Quill composition and typing regression fixtures pass, including read-only recovery, host history and matrix-cell entry.
|
|
368
468
|
|
|
369
469
|
This is local Chromium evidence. OS IME, Safari/Firefox, installed WordPress and cross-application system clipboard checks were not repeated. No new mathematical grammar or numerical behavior is introduced. Release artifacts prepared before these changes require regeneration; nothing was published by this work.
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
## EDIT-019 continuous editing — workspace, 2026-09-10
|
|
473
|
+
|
|
474
|
+
- Run: `2026-09-09T17-47-53-597Z`; source fingerprint remained stable.
|
|
475
|
+
- Evidence: [report](../../output/playwright/editing-scenarios/2026-09-09T17-47-53-597Z/REPORT.md), [checkpoints and source hashes](../../output/playwright/editing-scenarios/2026-09-09T17-47-53-597Z/report.json).
|
|
476
|
+
- Command: `pnpm --filter @barocss/math-editor test:editing`.
|
|
477
|
+
- Environment: Chromium 152 / macOS, English locale, workspace source.
|
|
478
|
+
- **React standalone block: PASS, 28 checkpoints. Quill in-place block: PASS, 29 checkpoints.**
|
|
479
|
+
- Real key input creates `fraction(indexedRoot(3, ab), 2) + matrix(1, 2; 3, 4)`;
|
|
480
|
+
an explicit tree assertion checks content and nesting. The last cell becomes
|
|
481
|
+
`456789` in a second uninterrupted typing chain.
|
|
482
|
+
- Both surfaces pass two five-step Undo/Redo chains, retained typing focus,
|
|
483
|
+
empty-root Delete/Undo/Redo and unchanged retained content. Each first history
|
|
484
|
+
step is compared with its recorded structural checkpoint.
|
|
485
|
+
- Quill draft operations leave host data unchanged. Apply is one host Undo step;
|
|
486
|
+
Redo restores it. Save, page reload, Restore and reopen preserve the Delta and
|
|
487
|
+
formula structure. The restored matrix remains editable; Cancel preserves the
|
|
488
|
+
saved document. React has no host persistence lifecycle, so that part is N/A.
|
|
489
|
+
- The runner fails on either scenario failure or a changed source fingerprint.
|
|
490
|
+
It stores the executed fixture, failure screenshots, diagnostics and final data.
|
|
491
|
+
- No runtime library changes were required. Earlier fixture failures exposed OS
|
|
492
|
+
`End` assumptions and selection of the wrong saved block; the final fixture
|
|
493
|
+
verifies caret offsets with arrows and reopens the uniquely identified formula.
|
|
494
|
+
- Not covered: other hosts, Quill inline mode, real OS IME/clipboard,
|
|
495
|
+
Firefox/WebKit, touch, accessibility and performance budgets. No release-wide
|
|
496
|
+
pass or new rendering pass is claimed.
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
## EDIT-019 Quill inline extension — workspace, 2026-09-10
|
|
500
|
+
|
|
501
|
+
- Run: `2026-09-09T22-30-59-017Z`; source fingerprint remained stable.
|
|
502
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T22-30-59-017Z/REPORT.md) and
|
|
503
|
+
[checkpoints](../../output/playwright/editing-scenarios/2026-09-09T22-30-59-017Z/report.json).
|
|
504
|
+
- **React block: PASS (28 checkpoints); Quill block: PASS (29); Quill inline: PASS (30).**
|
|
505
|
+
- The unchanged continuous editing chain runs in all three targets. Renderer and
|
|
506
|
+
mode are recorded separately, including artifact names and the failure summary.
|
|
507
|
+
- Quill inline commits with Enter from the outer boundary. No extra formula line
|
|
508
|
+
or host paragraph appears; prose is unchanged by math editing.
|
|
509
|
+
- Apply/host Undo/Redo, Save/reload/Restore, re-edit and Cancel preserve the
|
|
510
|
+
inline formula. A later Right-arrow exit returns focus to Quill; typing `q`
|
|
511
|
+
inserts exactly one character immediately after that formula. Host Undo
|
|
512
|
+
restores the committed Delta, including formula data.
|
|
513
|
+
- Runtime library code was unchanged. The runner and scenario documentation were
|
|
514
|
+
extended; no npm publication or new rendering audit was required.
|
|
515
|
+
- Other hosts, standalone inline adapters, OS IME/clipboard, Firefox/WebKit,
|
|
516
|
+
touch, accessibility and performance remain outside this run.
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
## EDIT-019 Tiptap and ProseMirror — workspace, 2026-09-10
|
|
520
|
+
|
|
521
|
+
- Run: `2026-09-09T22-39-51-097Z`, stable source fingerprint.
|
|
522
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T22-39-51-097Z/REPORT.md) and
|
|
523
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T22-39-51-097Z/report.json).
|
|
524
|
+
- **7 targets PASS / 205 checkpoints:** React block (28), Quill block/inline
|
|
525
|
+
(29/30), Tiptap block/inline (29/30), ProseMirror block/inline (29/30).
|
|
526
|
+
- Chromium 152 on macOS, English UI, workspace source. The extended runner hashes
|
|
527
|
+
both newly covered plugin sources and package metadata.
|
|
528
|
+
- Each target passes the existing continuous formula chain. Integrated hosts
|
|
529
|
+
additionally check Apply/host Undo/Redo, Save/reload/Restore, re-edit/Cancel,
|
|
530
|
+
retained formula data and unchanged prose/container structure. Inline targets
|
|
531
|
+
check Enter commit, right-boundary exit, exact prose position and host Undo.
|
|
532
|
+
- Initial integrated run `2026-09-09T22-36-38-459Z`: six PASS, Tiptap inline FAIL.
|
|
533
|
+
A quick prose edit after Restore shared the restored document's history event;
|
|
534
|
+
Undo removed both. The sample now brackets `setContent` with `closeHistory`
|
|
535
|
+
transactions, keeping the replacement and subsequent typing separate.
|
|
536
|
+
- Corrected run `2026-09-09T22-38-26-544Z`: seven PASS. The final run above repeats
|
|
537
|
+
all seven targets with the final runner documentation and source hashes.
|
|
538
|
+
- Integration-demo TypeScript and production build pass. Existing large-chunk
|
|
539
|
+
build warnings remain. Test source formatting passes. Core/plugin runtime
|
|
540
|
+
code was unchanged; the fix belongs to the Tiptap demo's restoration callback.
|
|
541
|
+
- Remaining: Lexical, Editor.js, TinyMCE, CKEditor, Slate and Gutenberg; native OS
|
|
542
|
+
IME/clipboard, other browser engines, touch, accessibility and performance.
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
## EDIT-019 Lexical — workspace, 2026-09-10
|
|
546
|
+
|
|
547
|
+
- Run: `2026-09-09T23-08-15-353Z`, stable source fingerprint.
|
|
548
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T23-08-15-353Z/REPORT.md) and
|
|
549
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T23-08-15-353Z/report.json).
|
|
550
|
+
- **9 targets PASS / 264 checkpoints:** React block (28), Quill block/inline
|
|
551
|
+
(29/30), Tiptap block/inline (29/30), ProseMirror block/inline (29/30),
|
|
552
|
+
Lexical block/inline (29/30).
|
|
553
|
+
- Chromium 152 on macOS, English UI, workspace source. Fingerprints now include
|
|
554
|
+
the Lexical plugin source and package metadata.
|
|
555
|
+
- Lexical passes the full nested editing and two history chains, Apply as one
|
|
556
|
+
host Undo event, Redo, Save/reload/Restore, re-edit and Cancel. Inline also
|
|
557
|
+
passes Enter commit without a new paragraph, right-boundary exit, focus return,
|
|
558
|
+
typing immediately after the formula and host Undo.
|
|
559
|
+
- Host checks read Lexical serialized state and retain container boundaries and
|
|
560
|
+
formula atoms. Drafts must not change saved host state. Restoration must match
|
|
561
|
+
the complete saved JSON; math rendering alone is not the persistence check.
|
|
562
|
+
- The initial run `2026-09-09T23-06-35-494Z` reached Save/Restore in both Lexical
|
|
563
|
+
modes but used an incorrect locator for reopening. Lexical uses a mounted
|
|
564
|
+
`.bme-lexical-node` widget; `data-barocss-math` belongs to its HTML export.
|
|
565
|
+
The fixture now locates its live widget. No product code change was needed.
|
|
566
|
+
- Runner formatting passes. This change only extends tests and documentation;
|
|
567
|
+
no new build, package publication or rendering audit was required.
|
|
568
|
+
- Next: Editor.js, TinyMCE, CKEditor, Slate and Gutenberg. Native OS IME/clipboard,
|
|
569
|
+
other browser engines, touch, accessibility and performance remain outside this run.
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
## EDIT-019 Editor.js — workspace, 2026-09-10
|
|
573
|
+
|
|
574
|
+
- Run: `2026-09-09T23-25-26-845Z`, stable source fingerprint.
|
|
575
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T23-25-26-845Z/REPORT.md) and
|
|
576
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T23-25-26-845Z/report.json).
|
|
577
|
+
- **10 targets PASS / 293 checkpoints:** React block (28), Quill/Tiptap/
|
|
578
|
+
ProseMirror/Lexical block and inline (29/30 each), Editor.js block (29).
|
|
579
|
+
- Chromium 152 on macOS, English UI, workspace source. Fingerprints include
|
|
580
|
+
Editor.js plugin source and package metadata.
|
|
581
|
+
- Editor.js passes nested fraction/root/matrix editing, suggestion selection,
|
|
582
|
+
two uninterrupted formula Undo/Redo chains, empty-root deletion/restoration,
|
|
583
|
+
Apply, Save/reload/Restore, re-edit and Cancel. Draft checks confirm saved host
|
|
584
|
+
data stays unchanged until Apply; prose blocks and block order remain intact.
|
|
585
|
+
- Async `save()` comparisons exclude only the generated `time` field. Block IDs,
|
|
586
|
+
saved model data, paragraph data and version remain in persistence assertions.
|
|
587
|
+
- **Editor.js host Undo/Redo is not covered:** the demo has no document history
|
|
588
|
+
integration. The runner records this limitation in `hostHistory`; it retains
|
|
589
|
+
the mandatory host Undo/Redo assertions for other configured hosts. Editor.js
|
|
590
|
+
inline mode is outside the block tool's contract.
|
|
591
|
+
- Test-source formatting passes. Only the runner, fixture and documentation
|
|
592
|
+
changed; no runtime fix, build, publication or new rendering audit was needed.
|
|
593
|
+
- Next: TinyMCE, CKEditor, Slate and Gutenberg. Native OS IME/clipboard, other
|
|
594
|
+
browser engines, touch, accessibility and performance remain separate work.
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
## EDIT-019 TinyMCE — workspace, 2026-09-10
|
|
598
|
+
|
|
599
|
+
- Run: `2026-09-09T23-39-57-339Z`, stable source fingerprint.
|
|
600
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T23-39-57-339Z/REPORT.md) and
|
|
601
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T23-39-57-339Z/report.json).
|
|
602
|
+
- **12 targets PASS / 352 checkpoints:** React block (28), Quill/Tiptap/
|
|
603
|
+
ProseMirror/Lexical/TinyMCE block and inline (29/30 each), Editor.js block (29).
|
|
604
|
+
- Chromium 152 on macOS, English UI, workspace source. TinyMCE 8.9.0 uses
|
|
605
|
+
`inline: true` for its host. Both math modes are covered; classic iframe mode
|
|
606
|
+
is not covered by this run. Fingerprints include TinyMCE source and metadata.
|
|
607
|
+
- TinyMCE passes the sustained fraction/root/matrix chain, formula history,
|
|
608
|
+
draft isolation, one host Undo/Redo for Apply, Save/reload/Restore, re-edit and
|
|
609
|
+
Cancel. Inline also passes Enter commit, right-boundary exit, focus return,
|
|
610
|
+
typing immediately after the formula and host Undo.
|
|
611
|
+
- The fixture applies and reopens an empty atom before the chain because TinyMCE
|
|
612
|
+
inserts on Apply. This exposed a real bug: the empty preview renderer returned
|
|
613
|
+
no visible content, leaving no click target. The adapter now shows the existing
|
|
614
|
+
localized empty-formula message and skips the renderer for empty LaTeX.
|
|
615
|
+
Serialized content stays empty; the test checks the label does not leak into HTML.
|
|
616
|
+
- HTML comparisons retain the complete serialized document. A detached parser
|
|
617
|
+
reads prose and element boundaries for caret checks. Live locators exclude
|
|
618
|
+
TinyMCE's `.mce-offscreen-selection` clone inside the editable body.
|
|
619
|
+
- Diagnostic run `2026-09-09T23-38-20-112Z` confirmed that a reported prose Undo
|
|
620
|
+
failure was in the fixture: saved and actual HTML were identical, but building
|
|
621
|
+
the expected prose result had mutated the saved units array. The fixture now
|
|
622
|
+
copies that array. No timing wait or product history change is used to pass it.
|
|
623
|
+
- TinyMCE package TypeScript, two unit tests, build and changed-source formatting
|
|
624
|
+
pass. Package-local build also exposed a metafile path check using the wrong
|
|
625
|
+
working directory. The build now sets esbuild's working directory to the repo
|
|
626
|
+
root; the normal package build command succeeds with its dependency checks intact.
|
|
627
|
+
- Patch changeset: `.changeset/tiny-math-empty-preview.md`. No package was published.
|
|
628
|
+
- Remaining: CKEditor, Slate, Gutenberg, TinyMCE iframe mode, native OS
|
|
629
|
+
IME/clipboard and other browser/input environments. Editor.js document history
|
|
630
|
+
remains unverified because its demo has no host history integration.
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
## EDIT-019 CKEditor — workspace, 2026-09-10
|
|
634
|
+
|
|
635
|
+
- Run: `2026-09-09T23-44-59-263Z`, stable source fingerprint.
|
|
636
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T23-44-59-263Z/REPORT.md) and
|
|
637
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T23-44-59-263Z/report.json).
|
|
638
|
+
- **14 targets PASS / 411 checkpoints:** React block (28), Quill/Tiptap/
|
|
639
|
+
ProseMirror/Lexical/TinyMCE/CKEditor block and inline (29/30 each),
|
|
640
|
+
Editor.js block (29).
|
|
641
|
+
- Chromium 152 on macOS, English UI, workspace source. CKEditor 48.5.0 uses the
|
|
642
|
+
local ClassicEditor demo. Fingerprints include CKEditor plugin source and
|
|
643
|
+
package metadata. This run does not cover other CKEditor builds or collaboration.
|
|
644
|
+
- CKEditor passes the sustained fraction/root/matrix chain, keyboard suggestion
|
|
645
|
+
selection, two formula Undo/Redo chains, empty-root deletion/restoration,
|
|
646
|
+
unchanged host data during drafts, Apply as one host Undo event and host Redo.
|
|
647
|
+
- Save/reload/Restore retains the complete serialized HTML and editable math
|
|
648
|
+
structure. Re-edit and Cancel leave that HTML unchanged. Inline also passes
|
|
649
|
+
Enter commit without another paragraph, Right-arrow exit, focus return,
|
|
650
|
+
prose input immediately after the formula and host Undo.
|
|
651
|
+
- The fixture reuses the detached HTML parser for prose/container checks and
|
|
652
|
+
copies parsed units when constructing expectations. Full saved-state equality
|
|
653
|
+
remains required for history and persistence checks.
|
|
654
|
+
- Test-source formatting passes. Only tests and documentation changed in this
|
|
655
|
+
extension; no runtime fix, additional build or package publication was needed.
|
|
656
|
+
- Next: Slate and Gutenberg. TinyMCE iframe hosts, other CKEditor builds,
|
|
657
|
+
native OS IME/clipboard and other browser/input environments remain separate
|
|
658
|
+
targets. Editor.js document history is still outside its demo's configuration.
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
## EDIT-019 all integration demos — workspace, 2026-09-10
|
|
662
|
+
|
|
663
|
+
- Run: `2026-09-09T23-55-26-302Z`, stable source fingerprint.
|
|
664
|
+
- [Report](../../output/playwright/editing-scenarios/2026-09-09T23-55-26-302Z/REPORT.md) and
|
|
665
|
+
[checkpoints and hashes](../../output/playwright/editing-scenarios/2026-09-09T23-55-26-302Z/report.json).
|
|
666
|
+
- **17 targets PASS / 499 checkpoints**, covering all nine integration demos
|
|
667
|
+
plus standalone React block. Chromium 152 on macOS, English UI, workspace source.
|
|
668
|
+
|
|
669
|
+
| Editor | Tested math modes | Checkpoints | Host Undo/Redo |
|
|
670
|
+
| --- | --- | ---: | --- |
|
|
671
|
+
| React standalone | Block | 28 | Not applicable |
|
|
672
|
+
| Quill | Block + inline | 59 | PASS |
|
|
673
|
+
| Tiptap | Block + inline | 59 | PASS |
|
|
674
|
+
| ProseMirror | Block + inline | 59 | PASS |
|
|
675
|
+
| Lexical | Block + inline | 59 | PASS |
|
|
676
|
+
| Editor.js | Block | 29 | Not configured; unverified |
|
|
677
|
+
| TinyMCE | Block + inline, inline host | 59 | PASS |
|
|
678
|
+
| CKEditor | Block + inline, ClassicEditor | 59 | PASS |
|
|
679
|
+
| Slate | Block + inline | 59 | PASS |
|
|
680
|
+
| Gutenberg | Block, standalone provider | 29 | PASS |
|
|
681
|
+
|
|
682
|
+
- Slate passes the sustained nested editing chain, two formula Undo/Redo chains,
|
|
683
|
+
Apply as one host history event, Save/reload/Restore, re-edit and Cancel.
|
|
684
|
+
Inline also passes Enter commit, Right-arrow exit, host focus, exact prose
|
|
685
|
+
insertion position and host Undo. Comparisons retain the saved descendant
|
|
686
|
+
array and formula objects; prose checks also preserve paragraph boundaries.
|
|
687
|
+
- Gutenberg passes the same block chain and provider host Undo/Redo. Full saved
|
|
688
|
+
HTML equality includes WordPress comments and encoded math data. A separate
|
|
689
|
+
detached DOM comparison checks prose and block order; comments are not prose.
|
|
690
|
+
- Both new hosts keep saved document data unchanged while editing a draft.
|
|
691
|
+
Restore reproduces the full saved state and reopens the same editable model.
|
|
692
|
+
- Fingerprints now include Slate and Gutenberg source/package metadata, as well
|
|
693
|
+
as the previously covered hosts, core, shared helpers, demos and test runner.
|
|
694
|
+
- Formatting passes. This extension changed only tests and documentation; no
|
|
695
|
+
runtime fix, new build or publication was needed.
|
|
696
|
+
- This run covers local demo configurations. It does not replace the separate
|
|
697
|
+
WordPress admin/plugin ZIP checks. TinyMCE iframe, other CKEditor builds,
|
|
698
|
+
collaboration, native OS IME/clipboard, Firefox/WebKit, touch, accessibility
|
|
699
|
+
and performance remain separate targets. Editor.js/Gutenberg are block tools;
|
|
700
|
+
no inline mode is claimed for them.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type MathState, type MathStructure } from './model.js';
|
|
2
|
+
/** The closest supported wrapper owns the footer, even when roots and fences are nested. */
|
|
3
|
+
export declare function structureEditingContext(state: MathState): {
|
|
4
|
+
fence: MathStructure;
|
|
5
|
+
root?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
root: {
|
|
8
|
+
id: string;
|
|
9
|
+
kind: "root" | "indexedRoot";
|
|
10
|
+
target: "root" | "indexedRoot";
|
|
11
|
+
canTransform: boolean;
|
|
12
|
+
indexCaret: {
|
|
13
|
+
id: string;
|
|
14
|
+
start: number;
|
|
15
|
+
end: number;
|
|
16
|
+
} | undefined;
|
|
17
|
+
} | undefined;
|
|
18
|
+
fence?: undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
/** Reject an action if focus has moved to a different wrapper since it was displayed. */
|
|
21
|
+
export declare function changeContextFence(state: MathState, id: string, kind: string): MathState;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { documentRows, isLiteralText, } from './model.js';
|
|
2
|
+
import { fences } from './fences.js';
|
|
3
|
+
import { rootEditingContext } from './root-transform.js';
|
|
4
|
+
import { setMathPresentation } from './presentation.js';
|
|
5
|
+
/** The closest supported wrapper owns the footer, even when roots and fences are nested. */
|
|
6
|
+
export function structureEditingContext(state) {
|
|
7
|
+
if (isLiteralText(state.document, state.caret.id))
|
|
8
|
+
return;
|
|
9
|
+
const visit = (row, parent) => {
|
|
10
|
+
for (const node of row.children) {
|
|
11
|
+
if (node.type === 'text') {
|
|
12
|
+
if (node.id === state.caret.id)
|
|
13
|
+
return parent;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const target = fences(node) || ['root', 'indexedRoot'].includes(node.type) ? node : parent;
|
|
17
|
+
for (const slot of node.slots) {
|
|
18
|
+
const found = visit(slot, target);
|
|
19
|
+
if (found)
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const node = documentRows(state.document)
|
|
26
|
+
.map((row) => visit(row))
|
|
27
|
+
.find(Boolean);
|
|
28
|
+
if (!node)
|
|
29
|
+
return;
|
|
30
|
+
return fences(node) ? { fence: node } : { root: rootEditingContext(state) };
|
|
31
|
+
}
|
|
32
|
+
/** Reject an action if focus has moved to a different wrapper since it was displayed. */
|
|
33
|
+
export function changeContextFence(state, id, kind) {
|
|
34
|
+
return structureEditingContext(state)?.fence?.id === id
|
|
35
|
+
? setMathPresentation(state, id, { fence: kind })
|
|
36
|
+
: state;
|
|
37
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Keep focus in the same document (including iframe hosts). Arrow keys only browse; Enter applies. */
|
|
2
|
+
export function focusContextAction(host, key) {
|
|
3
|
+
const buttons = Array.from(host.querySelectorAll('button:not(:disabled)'));
|
|
4
|
+
if (!buttons.length)
|
|
5
|
+
return false;
|
|
6
|
+
const index = buttons.indexOf(host.ownerDocument.activeElement);
|
|
7
|
+
let next;
|
|
8
|
+
if (key === 'F6')
|
|
9
|
+
next = buttons.find((button) => button.getAttribute('aria-pressed') === 'true') ?? buttons[0];
|
|
10
|
+
else if (key === 'ArrowRight')
|
|
11
|
+
next = buttons[(index + 1) % buttons.length];
|
|
12
|
+
else if (key === 'ArrowLeft')
|
|
13
|
+
next = buttons[(index - 1 + buttons.length) % buttons.length];
|
|
14
|
+
else if (key === 'Home')
|
|
15
|
+
next = buttons[0];
|
|
16
|
+
else if (key === 'End')
|
|
17
|
+
next = buttons[buttons.length - 1];
|
|
18
|
+
if (!next)
|
|
19
|
+
return false;
|
|
20
|
+
next.focus({ preventScroll: true });
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
@@ -4,6 +4,9 @@ interface MenuRect {
|
|
|
4
4
|
right: number;
|
|
5
5
|
bottom: number;
|
|
6
6
|
}
|
|
7
|
+
/** Follow layout and visual viewport changes, including ancestor transforms that
|
|
8
|
+
* do not produce window resize or element ResizeObserver notifications. */
|
|
9
|
+
export declare function followMathMenuPosition(anchor: HTMLElement, position: () => void, ignoreScrollWithin?: HTMLElement): () => void;
|
|
7
10
|
/** Choose a scrollable vertical gap without covering host controls in the same column. */
|
|
8
11
|
export declare function mathMenuPlacement(bounds: MenuRect, anchor: MenuRect, width: number, desiredHeight: number, preferredHeight: number, avoid?: readonly MenuRect[]): {
|
|
9
12
|
left: number;
|
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
/** Follow layout and visual viewport changes, including ancestor transforms that
|
|
2
|
+
* do not produce window resize or element ResizeObserver notifications. */
|
|
3
|
+
export function followMathMenuPosition(anchor, position, ignoreScrollWithin) {
|
|
4
|
+
const win = anchor.ownerDocument.defaultView;
|
|
5
|
+
let frame = 0;
|
|
6
|
+
const schedule = () => {
|
|
7
|
+
if (!frame)
|
|
8
|
+
frame = win.requestAnimationFrame(() => {
|
|
9
|
+
frame = 0;
|
|
10
|
+
position();
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
const scroll = (event) => {
|
|
14
|
+
if (event.target &&
|
|
15
|
+
'nodeType' in event.target &&
|
|
16
|
+
ignoreScrollWithin?.contains(event.target))
|
|
17
|
+
return;
|
|
18
|
+
schedule();
|
|
19
|
+
};
|
|
20
|
+
const changes = new MutationObserver(schedule);
|
|
21
|
+
for (let element = anchor; element; element = element.parentElement)
|
|
22
|
+
changes.observe(element, { attributes: true, attributeFilter: ['style', 'class'] });
|
|
23
|
+
const resize = typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(schedule);
|
|
24
|
+
resize?.observe(anchor);
|
|
25
|
+
win.addEventListener('resize', schedule);
|
|
26
|
+
win.addEventListener('scroll', scroll, true);
|
|
27
|
+
win.visualViewport?.addEventListener('resize', schedule);
|
|
28
|
+
win.visualViewport?.addEventListener('scroll', schedule);
|
|
29
|
+
return () => {
|
|
30
|
+
changes.disconnect();
|
|
31
|
+
resize?.disconnect();
|
|
32
|
+
win.cancelAnimationFrame(frame);
|
|
33
|
+
win.removeEventListener('resize', schedule);
|
|
34
|
+
win.removeEventListener('scroll', scroll, true);
|
|
35
|
+
win.visualViewport?.removeEventListener('resize', schedule);
|
|
36
|
+
win.visualViewport?.removeEventListener('scroll', schedule);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
1
39
|
/** Fixed menus escape ordinary scrolling text areas. Transforms and containment
|
|
2
40
|
* establish a local containing block, so overflow at that block and above still clips. */
|
|
3
41
|
function establishesFixedContainingBlock(style) {
|
|
@@ -68,7 +106,13 @@ export function mathMenuPlacement(bounds, anchor, width, desiredHeight, preferre
|
|
|
68
106
|
/** Position a fixed portal in viewport coordinates, including transformed modal hosts. */
|
|
69
107
|
export function positionMathMenu(menu, anchor, maxHeight, avoidElements = []) {
|
|
70
108
|
const win = menu.ownerDocument.defaultView;
|
|
71
|
-
const
|
|
109
|
+
const viewport = win.visualViewport;
|
|
110
|
+
const bounds = {
|
|
111
|
+
left: (viewport?.offsetLeft ?? 0) + 8,
|
|
112
|
+
top: (viewport?.offsetTop ?? 0) + 8,
|
|
113
|
+
right: (viewport?.offsetLeft ?? 0) + (viewport?.width ?? win.innerWidth) - 8,
|
|
114
|
+
bottom: (viewport?.offsetTop ?? 0) + (viewport?.height ?? win.innerHeight) - 8,
|
|
115
|
+
};
|
|
72
116
|
let contained = false;
|
|
73
117
|
for (let parent = menu.parentElement; parent; parent = parent.parentElement) {
|
|
74
118
|
const style = win.getComputedStyle(parent);
|
package/dist/dom.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface DOMMathEditorOptions {
|
|
|
17
17
|
mode?: MathEditorMode;
|
|
18
18
|
toolbar?: boolean | readonly StructureKind[];
|
|
19
19
|
toolbarMaxItems?: number;
|
|
20
|
+
/** Show contextual structure actions below the editing surface. Defaults to true. */
|
|
21
|
+
contextTools?: boolean;
|
|
20
22
|
showLineNumbers?: boolean;
|
|
21
23
|
onChange?: (document: MathDocument, latex: string) => void;
|
|
22
24
|
/** Final DOM/caret notification, including focus and selection-only updates. */
|