docs/plans/2026-04-22-slate-v2-editable-event-operation-coverage-plan.md
Build high-confidence browser editing coverage for every meaningful event and
operation lane used by the v2 Editable runtime.
The coverage must prevent the current class of regressions:
Backspace is the first tracer bullet. It is not the whole lane.
No, the current proof is not "absolute best" yet.
The architecture direction is right:
Editableslate-browserpackages/slate-react/src/editable/*But the proof is still too thin around event/operation coverage.
The refactor made ownership cleaner. It did not magically prove every editing path. The next step is a full event/operation test grid that asserts browser state, not just model state.
slateEditableslate-browser model + DOM assertionsdecorate back as the primary overlay APIEditor.getSnapshot() as the urgent render/read patheditable.tsxEditableDOMRoot still coordinates native beforeinputCurrent owner files in .tmp/slate-v2/packages/slate-react/src/editable/:
browser-handle.tsclipboard-input-strategy.tscomposition-state.tsdom-repair-queue.tsinput-router.tskeyboard-input-strategy.tsmodel-input-strategy.tsnative-input-strategy.tsselection-reconciler.tsEditableDOMRoot should remain a coordinator, not a policy dump.
Current accepted coordinator:
beforeinput orchestration remains in EditableDOMRoot because it
coordinates many already-extracted owners. Extracting it as one huge helper
would make the boundary worse.Any user-facing editing row must assert every relevant layer.
For text/editing operations:
For clipboard operations:
For composition/IME:
For focus/selection:
Legacy Slate is evidence, not a cage.
Before calling this lane complete, build a parity ledger from:
../slate/packages/slate-react/**../slate/site/examples/**../slate/playwright/** if present.tmp/slate-v2/playwright/integration/examples/**.tmp/slate-v2/packages/slate-react/test/**Every legacy browser-editing behavior must be classified:
recovered: v2 has an equivalent or stronger browser-visible rowreplaced: v2 proves the same user contract through the final API/runtimehard-cut: legacy behavior is intentionally not supported, with rationalecompat-only: supported only through explicit compat surfacedeferred: accepted with exact owner, risk, and future gateNo hidden skip debt:
Required artifact:
Legacy No-Regression Ledger section to this plan as rows are
classifiedMinimum columns:
| Legacy behavior | Final v2 behavior | Status | Proof command | Owner | Rationale |
|---|
Skip inventory:
.tmp/slate-v2/playwright/integration/examples/**: no skipped rows found
with rg -n "test\\.skip|\\.skip\\(|skip\\(".../slate/playwright/integration/examples/**: one skipped row,
inlines.test.ts arrow-key read-only inline navigation.playwright/integration/examples/inlines.test.ts /
arrow keys skip over read-only inline.| Legacy behavior | Final v2 behavior | Status | Proof command | Owner | Rationale |
|---|---|---|---|---|---|
| Editable void structure, duplication, and embedded input editing | editable-voids keeps legacy rows and adds outer-selection restoration, nested editor input, and selectionchange-noise rows | recovered | bun test:integration-local | slate-react browser event policy | v2 proves the original behavior plus the higher-risk focus/selection paths that legacy did not cover. |
| Check-list checkbox toggling | check-lists toggles checkbox and preserves editor selection/follow-up insertion | recovered | bun test:integration-local | slate-react internal interactive target policy | v2 keeps the checkbox behavior and proves the non-editable target does not corrupt selection. |
| Read-only inline arrow navigation | Active inlines row for arrow keys around read-only inline | recovered | bun test:integration-local | slate-react selection/navigation | Legacy skipped this row; v2 runs it. |
| Inline link cut | inlines cut row deletes selected inline link text and keeps desktop caret follow-up; mobile proves deletion only | recovered / mobile transport narrowed | bun test:integration-local | clipboard transport / mobile automation | Desktop projects prove caret follow-up; mobile cannot use forbidden clipboard reads or reliable role follow-up after cut, so it proves deletion. |
| Richtext render and typing | richtext covers render, browser insertion, visual caret, movement, delete/backspace, undo, paste-over-selection, and route remount | recovered | bun test:integration-local | slate-react input/selection/history | v2 exceeds legacy's render/type/undo coverage with browser-visible selection and caret assertions. |
| Plaintext typing | plaintext inserts typed text | recovered | bun test:integration-local | browser input | Legacy behavior remains active. |
| Paste HTML bold/code | paste-html keeps bold/code rows and adds selected-content rich paste/follow-up proof; mobile uses semantic insertion where clipboard write is denied | recovered / mobile transport narrowed | bun test:integration-local | clipboard transport | Desktop proves clipboard path; mobile proves model/visible rich insertion because navigator.clipboard.write* is denied. |
| Shadow DOM editor render/edit/line break | shadow-dom covers nested shadow rendering, editing, and new-line typing | recovered | bun test:integration-local | slate-react DOM bridge | Legacy behavior remains active and green across projects. |
| Markdown shortcuts quote/list/heading | markdown-shortcuts keeps quote, list, and heading rows | recovered | bun test:integration-local | app shortcut example | Legacy behavior remains active. |
| Mentions render/list/insert | mentions keeps render/list/insert rows | recovered | bun test:integration-local | inline void/app example | Legacy behavior remains active after earlier mention/full-selection fixes. |
| Images render/delete invalid URL/selected image | images covers image render, invalid prompt rejection, and selected image deletion | recovered | bun test:integration-local | void element selection/deletion | v2 keeps legacy image behavior and adds invalid prompt proof. |
| Tables render | tables table tag row remains active | recovered | bun test:integration-local | table rendering | Legacy behavior remains active. |
| Huge document chunking example | v2 huge document renders without child-count chunking | replaced | bun test:integration-local; REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local | semantic islands / large-document runtime | Legacy chunking behavior is intentionally replaced by semantic islands, corridor, and occlusion. |
| Code highlighting visual token rendering | v2 uses semantic token projection assertions | replaced | bun test:integration-local | projection-source overlays | Final API teaches projection sources instead of legacy decorate as primary overlay story. |
| Search highlighting | v2 active row highlights searched text | recovered | bun test:integration-local | projection/decorated text | Legacy behavior remains active. |
| Hovering toolbar | v2 keeps appear/disappear rows | recovered | bun test:integration-local | selection/floating UI | Legacy behavior remains active. |
Styling via style and className | v2 keeps both styling rows | recovered | bun test:integration-local | public Editable props | Legacy behavior remains active. |
| Iframe editor | v2 iframe editor remains editable | recovered | bun test:integration-local | DOM bridge | Legacy behavior remains active. |
| Read-only editor | v2 read-only row remains non-editable | recovered | bun test:integration-local | read-only policy | Legacy behavior remains active. |
| Forced layout deletion persistence | v2 forced-layout rows remain active | recovered | bun test:integration-local | example rendering | Legacy behavior remains active. |
| Placeholder rendering and editor height | v2 placeholder rows remain active | recovered | bun test:integration-local | placeholder rendering | Legacy behavior remains active. |
| Markdown preview | v2 markdown preview row remains active | recovered | bun test:integration-local | example rendering | Legacy behavior remains active. |
| Embeds | v2 embeds row remains active | recovered | bun test:integration-local | void/embed rendering | Legacy behavior remains active. |
Classification summary:
The event matrix below is not enough unless these cross-cutting shapes are covered too.
Rows:
Assertions:
Rows:
Assertions:
Rows:
onBeforeInput handled returns trueonBeforeInput prevents defaultonInput handled returns trueonKeyDown handled returns trueonPaste handled returns trueonCopy / onCut handled returns truetrueAssertions:
Rows:
<input> / <textarea> inside editorAssertions:
Rows:
Assertions:
Rows:
Assertions:
Every operation class reachable from Editable needs at least one browser row
or explicit classification:
set_selectioninsert_textremove_textsplit_nodemerge_nodeinsert_noderemove_nodeset_nodeFor each row, record:
Any product change that touches input routing, selection reconciliation, DOM-owned text, large-document activation, or render subscriptions must rerun at least one performance/locality guardrail.
Fast guardrails:
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
Do not let correctness work silently undo the huge-doc runtime posture.
beforeinputRows:
insertTextinsertReplacementTextinsertFromCompositioninsertCompositionTextdeleteCompositionTextinsertFromPasteinsertFromDropinsertFromYankinsertLineBreakinsertParagraphdeleteContentBackwarddeleteContentForwarddeleteWordBackwarddeleteWordForwarddeleteSoftLineBackwarddeleteSoftLineForwarddeleteHardLineBackwarddeleteHardLineForwarddeleteEntireSoftLinedeleteByCutdeleteByDragdeleteByCompositionhistoryUndohistoryRedoFor each supported row:
Primary files:
richtext.test.tshighlighted-text.test.tslarge-document-runtime.test.tsshadow-dom.test.tsPrimary owners:
model-input-strategy.tsnative-input-strategy.tsselection-reconciler.tsdom-repair-queue.tsonInput / Native inputRows:
input after prevented beforeinputAssertions:
Primary owner:
model-input-strategy.tsdom-repair-queue.tsRows:
beforeinput is unavailableAssertions:
Primary owner:
keyboard-input-strategy.tsRows:
Assertions:
Primary owner:
clipboard-input-strategy.tsRows:
Assertions:
Primary owner:
clipboard-input-strategy.tsinput-router.tsRows:
insertFromComposition resets composition stateAssertions:
Primary owner:
composition-state.tsRows:
Assertions:
Primary owner:
selection-reconciler.tsRows:
beforeinput and inputAssertions:
Primary owner:
input-router.tsselection-reconciler.tsRows:
Assertions:
Primary owner:
dom-repair-queue.tsUse vertical slices. Do not write the full matrix first.
Add one RED row:
test('keeps caret editable after browser Backspace at selected text end', ...)
Must assert:
Command:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace at selected text end"
Status: closed.
Actions:
keeps caret editable after browser Backspace at selected text end to
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsOZDOMRepairQueue now exposes repairCaretAfterModelOperation(...)preferModelSelectionForInputRefdata-slate-path when weak-map identity is
staleChanged files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.ts.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/hooks/use-slate-node-ref.tsxEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace at selected text end"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|inserts text through browser input|visual caret|undo|types at the browser-selected end"
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "delete|directly synced|IME|paste"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
Results:
1 passed9 passed14 passed1 pass, 15 pass, 6 passDecision:
Mirror Phase 1 with native Delete.
Command:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Delete at selected"
Status: closed.
Actions:
keeps caret editable after browser Delete before trailing punctuation
to .tmp/slate-v2/playwright/integration/examples/richtext.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Delete before trailing punctuation"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|inserts text through browser input|visual caret|undo|types at the browser-selected end"
Results:
1 passed10 passedDecision:
Native Backspace/Delete over expanded selection.
Must prove collapsed selection at deletion start and follow-up typing.
Status: closed.
Actions:
keeps caret editable after browser Backspace deletes selected range
to .tmp/slate-v2/playwright/integration/examples/richtext.test.tskeeps caret editable after browser Delete deletes selected range to
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace deletes selected range"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Delete deletes selected range"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|inserts text through browser input|visual caret|undo|types at the browser-selected end"
Results:
1 passed1 passed12 passedDecision:
Use highlighted-text.test.ts.
Rows:
Status: closed.
Actions:
keeps caret editable after Backspace inside decorated text to
.tmp/slate-v2/playwright/integration/examples/highlighted-text.test.tskeeps caret editable after Delete inside decorated text to
.tmp/slate-v2/playwright/integration/examples/highlighted-text.test.tskeeps caret editable after deleting a decorated selected range to
.tmp/slate-v2/playwright/integration/examples/highlighted-text.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/highlighted-text.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --grep "Backspace inside decorated"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --grep "Delete inside decorated"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --grep "decorated selected range"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
1 passed1 passed1 passed7 passedDecision:
Already improved:
Add remaining rows:
Status: partially closed.
Actions:
keeps caret editable after plain text paste over selected range to
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsslate-browserDOMRepairQueue and schedules caret
repair after Editor.replace(...) / ReactEditor.insertData(...)Changed files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "plain text paste over selected range"
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "paste|Backspace|Delete|visual caret|undo|browser-selected end"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
Results:
1 passed20 passed1 pass, 15 pass, 6 passDecision:
Status: partially closed.
Actions:
keeps caret editable after rich HTML paste over selected content to
.tmp/slate-v2/playwright/integration/examples/paste-html.test.tsslate-browserChanged files:
.tmp/slate-v2/playwright/integration/examples/paste-html.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --grep "rich HTML paste over selected"
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium
Results:
1 passed3 passedDecision:
Status: partially closed.
Actions:
keeps caret editable after cutting inline link text to
.tmp/slate-v2/playwright/integration/examples/inlines.test.tsControlOrMeta+XChanged files:
.tmp/slate-v2/playwright/integration/examples/inlines.test.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.tsEvidence:
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --grep "cutting inline link"
bunx playwright test ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "cut|copy|paste|Backspace|Delete"
Results:
1 passed10 passedDecision:
Verdict: keep course.
Harsh take: clipboard mutators are much healthier now, and they found real selection bugs. There are still narrower gaps, but the next highest-value matrix family is composition.
Why:
Risks:
Next move:
Do-not-do list:
Verdict: keep course.
Harsh take: Backspace/Delete coverage is already proving its worth; it found real DOM caret repair gaps in delete and paste paths. Keep moving through the matrix vertically.
Why:
Risks:
Earliest gates:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromiumbunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "paste"bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1Next move:
Do-not-do list:
Add rows for:
Status: deferred.
Probe:
richtext row for IME composition replacing an expanded
selectioneditor.ime.compose(...)transport: 'synthetic' | 'native' option to
slate-browser IME helpers for future targeted proofResult:
Classification:
Decision:
slate-browser IME helper fidelity:
Changed files:
.tmp/slate-v2/packages/slate-browser/src/playwright/ime.ts.tmp/slate-v2/packages/slate-browser/src/playwright/index.tsRejected tactic:
null selection even though text commitsAdditional probe:
slate-browser synthetic composition path in
Chromium with transport: 'synthetic'Decision:
slate-browser IME helper fidelityAdd rows for movement/extension hotkeys only after delete rows are stable.
Rows:
Assert model selection and DOM selection.
Status: partially closed.
Actions:
keeps selection synchronized after browser ArrowLeft and ArrowRight
to .tmp/slate-v2/playwright/integration/examples/richtext.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "ArrowLeft and ArrowRight"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
1 passed1 pass, 15 pass, 6 passslate-browser, slate-dom, and slate-react:
passedDecision:
Alt+ArrowLeft / Alt+ArrowRightControl+ArrowLeft /
Control+ArrowRightAlt+Shift+ArrowDown shortcutVerdict: keep course.
Harsh take: the high-risk text editing and clipboard rows are paying off. They found real cursor/selection gaps in delete, paste, and inline cut. Keep moving through event families, but do not force unautomatable native shortcuts.
Why:
Risks:
Recent broad gate:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/paste-html.test.ts --project=chromium --grep "Backspace|Delete|paste|cut|copy|ArrowLeft|ArrowRight|visual caret|undo|browser-selected end"
Result:
20 passedNext move:
Do-not-do list:
Rows:
Status: partially closed.
Actions:
selects the current block on browser triple click to
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "triple click"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
1 passed1 passslate-browser, slate-dom, and slate-react:
passedDecision:
Status: partially closed.
Actions:
selects void content by browser click without mutating content to
.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.tsChanged files:
.tmp/slate-v2/playwright/integration/examples/large-document-runtime.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "selects void"
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "void|inline|table"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
1 passed4 passed1 passslate-browser, slate-dom, and slate-react:
passedDecision:
Status: closed for the current browser matrix.
Actions:
slate-react event ownership for interactive internal controls by
adding editable/target-policy.tsChanged files:
.tmp/slate-v2/packages/slate-react/src/editable/target-policy.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/input-router.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/playwright/integration/examples/editable-voids.test.ts.tmp/slate-v2/playwright/integration/examples/check-lists.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --grep "restores outer"
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=webkit --grep "restores outer"
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/check-lists.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "restores outer|keeps nested editor input|keeps selection"
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
bun run bench:react:rerender-breadth:local
REACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:local
Results:
12 passed across Chromium, Firefox,
WebKit, and mobile1 + 15 + 6 passedDecision:
Next move:
Rows:
Status: closed for focused lifecycle rows.
Actions:
ignores selectionchange noise from input inside editable void
to .tmp/slate-v2/playwright/integration/examples/editable-voids.test.tsdoes not duplicate native input handling after route remount
to .tmp/slate-v2/playwright/integration/examples/richtext.test.ts/examples/plaintext as the browser-visible
unmount/remount proofChanged files:
.tmp/slate-v2/playwright/integration/examples/editable-voids.test.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --grep "selectionchange noise"
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "selectionchange noise"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "route remount"
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "route remount"
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
4 passed across Chromium, Firefox, WebKit, and
mobile4 passed across Chromium, Firefox, WebKit, and mobileDecision:
Next move:
After Chromium is green:
Every failure gets classified:
No blanket project skips.
Status: Chromium cluster green.
Actions:
Evidence:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/check-lists.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium
Results:
59 passedDecision:
Next move:
Status: green.
Actions:
applyEditableCut
repair the restored model-owned collapsed selection after deleting the
fragmentcut event payloadChanged files:
.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/playwright/integration/examples/highlighted-text.test.ts.tmp/slate-v2/playwright/integration/examples/inlines.test.ts.tmp/slate-v2/playwright/integration/examples/paste-html.test.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=firefox --grep "cuts decorated"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/richtext.test.ts --project=mobile --grep "paste over selected|rich HTML paste"
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=mobile --grep "cutting inline"
bun test:integration-local
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
Results:
21 passed356 passedAccepted transport classifications:
navigator.clipboard is denied by the
Playwright mobile project; mobile clipboard rows prove model/visible behavior
through semantic insertion or deletion instead of forbidden clipboard APIsDecision:
Next move:
For packages/slate-react/** product changes:
bun test ./packages/slate-react/test/dom-text-sync-contract.ts --bail 1
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bun test ./packages/slate-react/test/projections-and-selection-contract.tsx --bail 1
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-dom --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-dom --filter=./packages/slate-react --force
For browser rows:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "Backspace|Delete|visual caret|undo"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "delete|directly synced|IME|paste"
Final browser cluster:
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium
Release-quality gate:
bun test:integration-local
bun test:integration-local can close browser editing only if every remaining
skip/failure is explicitly classified.
Do we have the absolute best Slate/slate-react architecture?
No, not yet.
We have the right direction and a much cleaner runtime shape. But an editor is not "best" until event/operation coverage proves the weird browser paths: delete, composition, selection drift, clipboard transport, focus traps, shadow DOM, mobile, and platform differences.
Would I do it differently from scratch?
Yes:
slate-browser model+DOM+caret assertions firstEditableDOMRoot as a coordinator onlyDo we have the absolute best core for React-perfect perf?
No.
Core is good enough for the current huge-doc React lane, but not theoretical perfection. Remaining core work:
Would I hard-cut more legacy features?
Yes.
Hard cuts to keep:
decorateEditable path as public runtimeeditor.apply / onChange as taught extension modelHard cuts still worth doing:
decorate bridge to explicit compat, not primary exportscreateSlateDecorationSource adapter-onlyThis coverage lane is done only when:
Editable operation class is covered or explicitly
classifiedbun test:integration-local is green or every remaining row is explicitly
accepted/deferred with rationaleDo not stop at one Backspace fix.
Do not stop at model-only proof.
Do not stop at Chromium-only if the claim is framework-grade browser editing.
The final deliverable is not "more tests".
The final deliverable is a browser-editing contract for v2 Editable:
EditableDOMRoot remains a coordinator, not a policy dumpdone only after all of the above