docs/plans/2026-04-22-slate-v2-editing-kernel-hard-cut-rewrite-plan.md
The current slate-react browser editing architecture is still not the final
shape.
It has better files:
EditableInputControllerEditableSelectionControllerEditableMutationControllerEditableCaretEngineDOMRepairQueueBut those are currently mostly ownership labels around legacy timing. They do not yet form one authoritative editing kernel.
The missing architecture is:
browser event
-> target policy
-> input intent
-> selection source
-> ownership decision
-> mutation command
-> transaction/commit
-> DOM text/selection repair
-> trace
Until that chain is centralized, edge cases will keep leaking through whenever browser DOM selection, Slate model selection, app/plugin transforms, and delayed repair disagree.
The correct solution is not to patch every bug. It is to hard-cut the ambiguity that lets those bugs exist.
Keep:
slateEditableHard-cut:
Editable to behave like legacy Slate ReactCurrent split is useful but incomplete:
InputController classifies some events.SelectionController imports/exports selection in some paths.MutationController applies some model-owned mutations/repairs.CaretEngine owns some movement commands.DOMRepairQueue repairs some model-owned DOM drift.But no single owner decides the full lifecycle.
That means:
Native text transport is useful.
Native structural editing is not safe enough.
The browser should provide platform events for:
The browser should not be trusted as the source of truth for:
Those must be editor-owned commands.
The runtime still asks, path by path:
That decision must be explicit and centralized per event.
The kernel should carry:
type SelectionSource =
| "model-owned"
| "dom-current"
| "composition-owned"
| "shell-backed"
| "internal-control"
| "app-owned"
| "unknown";
Every event must leave the kernel with exactly one source of selection truth.
The test suite now has strong rows, but still too many are example assertions.
Example rows prove "this route works today."
They do not prove the state space:
The suite needs generated gauntlets.
The huge-doc perf architecture is good and should be kept.
But correctness owns priority.
The kernel must preserve:
But no performance lane can bypass:
Add a single kernel above the current controllers:
type EditableEditingKernel = {
dispatchBrowserEvent(event: EditableBrowserEvent): EditableKernelResult;
};
The kernel is the only owner of:
Controllers become workers, not decision makers.
Every browser event follows this shape:
1. classify target
2. classify event intent
3. decide ownership
4. import or preserve selection
5. run command
6. collect mutation result
7. schedule DOM repair
8. emit trace
9. return handled/native/deferred result
The kernel must use explicit states:
type EditableKernelState =
| "idle"
| "dom-selection"
| "model-owned"
| "composition"
| "internal-control"
| "shell-backed"
| "clipboard"
| "dragging"
| "repairing";
The kernel must own these event families:
keydownbeforeinputinputselectionchangecompositionstartcompositionupdatecompositionendpastecopycutdragstartdragoverdropfocusblurmousedownclickEvery dispatch returns:
type EditableKernelResult = {
handled: boolean;
nativeAllowed: boolean;
intent: InputIntent | null;
selectionSource: SelectionSource;
command: EditableCommand | null;
repair: EditableRepairRequest | null;
trace: EditableTraceEntry;
};
Owns only:
Does not:
Owns only:
Does not:
Owns only:
Does not:
Owns only:
Does not:
Owns only:
Does not:
Owns proof, not product behavior:
Does not:
Model-owned commands:
Native allowed:
Examples/plugins may request commands.
They should not mutate editor state from raw browser handlers without going through the kernel/command contract.
Markdown shortcuts should become:
registerInputRule({
trigger: "* ",
at: "block-start",
command: { type: "set-block", blockType: "list-item", wrap: "bulleted-list" },
});
Not:
editor.insertText = ...
Transforms.select(...)
Transforms.delete(...)
Transforms.setNodes(...)
Transforms.wrapNodes(...)
DOM-only selection setup is allowed only when the row explicitly proves native DOM transport.
Default setup should use slate-browser semantic helpers that set both:
Do not preserve:
decorate as primary overlay APIEditablebun test:integration-local passing is a floor.
Closure requires:
Recover:
Do not recover:
EditableLegacy was not perfect. It was coherent.
The rewrite must preserve coherence while replacing the runtime.
Steal:
Do not steal:
Steal:
Do not steal:
Steal:
Do not steal:
Steal:
Do not steal:
Goal:
Actions:
test:integration-local as baseline.slate-react:
Transforms.selectTransforms.deselectReactEditor.focusEditor.insertTextEditor.insertBreakEditor.delete*editor.insertText =editor.deleteBackward =onKeyDownonDOMBeforeInputonInputFiles:
.tmp/slate-v2/packages/slate-react/src/editable/**.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/site/examples/ts/**Gates:
bun test:integration-local
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
Exit:
Status: inventory complete, baseline gates in progress.
Actions:
active goal state from the closed controller lane to this
active editing-kernel rewrite lane..tmp/slate-v2/packages/slate-react/src/**.tmp/slate-v2/site/examples/ts/**.tmp/slate-v2/playwright/integration/examples/**Transforms.selectTransforms.deselectReactEditor.focusEditor.insertTextEditor.insertBreakEditor.delete*editor.insertText =editor.deleteBackward =onKeyDownonDOMBeforeInputonInputInventory command:
rg -n "Transforms\\.(select|deselect)|ReactEditor\\.focus|Editor\\.(insertText|insertBreak|delete|deleteBackward|deleteForward|deleteFragment)|editor\\.(insertText|deleteBackward)\\s*=|onKeyDown|onDOMBeforeInput|onInput" packages/slate-react/src site/examples/ts playwright/integration/examples -g "*.ts" -g "*.tsx"
rg -n "preferModelSelectionForInputRef|isUpdatingSelection|IS_FOCUSED|IS_COMPOSING|state\\.activeIntent|selectionSource|repairCaret|syncDOMSelection|selectionchange|beforeinput|composition" packages/slate-react/src/editable packages/slate-react/src/components -g "*.ts" -g "*.tsx"
High-signal inventory findings:
EditableDOMRoot still has broad event orchestration:
components/editable.tsx: 23 direct editing/timing matcheskeyboard-input-strategy.ts still owns structural key fallback:
19 matchesandroid-input-manager.ts is a parallel editing subsystem:
22 direct mutation/selection matchesselection-reconciler.ts and selection-controller.ts split selection truth:
8 direct selection calls, 22 timing/source matches5 direct selection calls, 18 timing/source matchesmutation-controller.ts owns some command helpers but not all mutation
routing:
6 direct mutation/focus matchesclipboard-input-strategy.ts, composition-state.ts,
model-input-strategy.ts, and dom-repair-queue.ts still perform direct
model/selection/repair actions.markdown-shortcuts.tsx: editor.insertText =, editor.deleteBackward =,
and direct Transforms.selectcheck-lists.tsx: editor.deleteBackward =tables.tsx: editor.deleteBackward =inlines.tsx: editor.insertText = and keydown movementmentions.tsx: direct selection and keydown ownershipcode-highlighting.tsx: direct keydown/text insertionCounts by notable file:
packages/slate-react/src/hooks/android-input-manager/android-input-manager.ts: 22
packages/slate-react/src/components/editable.tsx: 23
packages/slate-react/src/editable/keyboard-input-strategy.ts: 19
packages/slate-react/src/editable/selection-reconciler.ts: 8
packages/slate-react/src/editable/model-input-strategy.ts: 7
packages/slate-react/src/editable/mutation-controller.ts: 6
packages/slate-react/src/editable/browser-handle.ts: 6
packages/slate-react/src/editable/selection-controller.ts: 5
packages/slate-react/src/editable/clipboard-input-strategy.ts: 5
site/examples/ts/code-highlighting.tsx: 6
site/examples/ts/mentions.tsx: 5
site/examples/ts/markdown-shortcuts.tsx: 4
site/examples/ts/inlines.tsx: 4
Decision:
EditableEditingKernel API/type anchor
before moving behavior.Next action:
bun test:integration-localbun run bench:react:rerender-breadth:localREACT_HUGE_COMPARE_BLOCKS=5000 REACT_HUGE_COMPARE_ITERATIONS=5 REACT_HUGE_COMPARE_TYPE_OPS=10 bun run bench:react:huge-document:legacy-compare:localStatus: complete.
Evidence:
bun test:integration-local
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:
368 passed000011.82ms vs legacy off/on 269.14ms / 291.05ms0.12ms vs 16.80ms / 0.85ms7.86ms vs 154.91ms / 31.89ms0.92ms vs 176.53ms / 32.85ms2.28ms vs 157.52ms / 40.94ms0.49ms vs 179.91ms / 39.81ms25.48ms vs 170.71ms / 39.24ms23.43ms vs 112.77ms / 123.25ms29.15ms vs 111.32ms / 115.81msDecision:
Next owner:
EditableEditingKernel API in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.Goal:
New file:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.tsDefine:
EditableBrowserEventEditableKernelStateEditableKernelContextEditableKernelResultEditableTraceEntryEditableCommandEditableOwnershipRules:
Gates:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
Exit:
Status: complete.
Actions:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.EditableBrowserEventFamilyEditableKernelStateEditableEventTargetOwnerEditableOwnershipEditableCommandEditableBrowserEventEditableKernelTraceEntryEditableKernelResultEditableKernelContextEditableEditingKernelChanged files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
Results:
Decision:
Next owner:
Goal:
Actions:
Files:
editing-kernel.tsinput-controller.tsselection-controller.tsmutation-controller.tscaret-engine.tspackages/slate-browser/src/playwright/index.tsGates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation and mutation chained"
bun --filter slate-browser test
Exit:
Status: complete for keydown.
Actions:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts:
mapSelectionSourceToKernelState(...)getEditableKernelTrace(...)clearEditableKernelTrace(...)recordEditableKernelTrace(...)EditableDOMRoot.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|word movement|line extension|read-only inline" --workers=4 --retries=0
Results:
24 passedRejected tactic:
slate-react. Rerunning after
package build/typecheck completed passed. Do not parallelize Playwright
against package builds.Decision:
Next owner:
beforeinput, React/native input, clipboard,
composition, focus/blur, mouse, and drag/drop handlers without changing
behavior.Status: complete for root handler entry tracing.
Actions:
recordKernelEventTrace(...) inside EditableDOMRoot.beforeinputinputChanged files:
.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|word movement|line extension|read-only inline" --workers=4 --retries=0
Results:
24 passedRejected tactic:
slate-react while package output is being rebuilt.Remaining Phase 2 gap:
selectionchange and DOM repair execution traces are not yet recorded.Next owner:
selectionchange and DOM repair execution, then expose
kernel traces through slate-browser scenario snapshots/artifacts without
changing runtime behavior.Status: complete.
Actions:
repair to EditableBrowserEventFamily.selectionchangeDOMRepairQueue.repairCaretAfterModelOperation.SlateBrowserHandle.getKernelTrace()kernelTrace to slate-browser EditorSnapshot.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-browser/src/playwright/index.tsEvidence:
bunx turbo build --filter=./packages/slate-browser --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-browser --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --grep "navigation and mutation chained" --workers=1 --retries=0
find test-results -name 'richtext-navigation-mutation-gauntlet.json' -print
rg -n 'kernelTrace|eventFamily|repair|selectionchange|keydown' test-results/**/richtext-navigation-mutation-gauntlet.json
Results:
1 passedkernelTrace entries for:
focusselectionchangemousedownclickkeydownDecision:
Next owner:
editing-kernel.tsGoal:
Actions:
kernel.dispatchKeyDown(...)kernel.dispatchBeforeInput(...)kernel.dispatchInput(...)kernel.dispatchSelectionChange(...)Hard rule:
Gates:
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|Backspace|Delete|visual caret"
Exit:
Status: complete for keydown.
Actions:
EditableKeyDownKernelDecision.prepareEditableKeyDownKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.EditableDOMRoot now consumes that decision and still calls the same
existing keydown worker.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|word movement|line extension|read-only inline" --workers=4 --retries=0
Results:
24 passedDecision:
Next owner:
beforeinput enters through kernel decision prep first,
while preserving current native/model decision behavior.Status: complete.
Actions:
EditableBeforeInputKernelDecision.prepareEditableBeforeInputKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.beforeinput decision prep into the kernel:
getNativeBeforeInputDecision(...)syncSelectionForBeforeInput(...)applyModelOwnedBeforeInputOperation(...)Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|visual caret|paste|can add list|can add a h1|traced slate-browser scenario|navigation and mutation chained" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=webkit --grep "rich HTML paste over selected content" --workers=1 --retries=0
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "rich HTML paste over selected content|pasted bold|pasted code" --workers=4 --retries=0
Results:
75 passed, 1 failed
paste-html rich paste selection assertion1 passed12 passedDecision:
Next owner:
Status: complete.
Actions:
EditableClipboardKernelDecision.prepareEditableClipboardKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.applyEditablePaste(...)applyEditableCopy(...)applyEditableCut(...)applyEditableDrag*applyEditableDrop(...)Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "copy|cut|paste|fragment|shell-backed|rich HTML" --workers=4 --retries=0
Results:
32 passedDecision:
Next owner:
Status: complete.
Actions:
EditableCompositionKernelDecision.prepareEditableCompositionKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.compositionstartcompositionupdatecompositionendapplyEditableCompositionStart(...)applyEditableCompositionUpdate(...)applyEditableCompositionEnd(...)Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "IME|composition" --workers=1 --retries=0
Results:
15 pass15 passedDecision:
Next owner:
Status: complete.
Actions:
EditableFocusMouseKernelDecision.prepareEditableFocusMouseKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.applyEditableFocus(...)applyEditableBlur(...)applyEditableClick(...)applyEditableMouseDown(...)Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/check-lists.test.ts ./playwright/integration/examples/select.test.ts ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "restores outer|keeps nested editor input focused|selectionchange noise|focus on checkbox|triple click|selects the current block" --workers=4 --retries=0
Results:
24 passedDecision:
Next owner:
Status: complete.
Actions:
EditableInputKernelDecision.prepareEditableInputKernel(...) in
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.input and inputCapture decision prep into the kernel:
applyEditableInput(...)domRepairQueue.repairDOMInput(...)richtext Backspace-at-end test setup from DOM-only selection to
semantic+DOM selection. The row's product claim is browser insertion and
Backspace after a stable selection, not DOM-only setup.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/plaintext.test.ts ./playwright/integration/examples/large-document-runtime.test.ts ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "inserts text|visual caret|directly synced|DOM-owned|Backspace|Delete|traced slate-browser scenario|navigation and mutation" --workers=4 --retries=0
Results:
80 passedDecision:
Next owner:
Goal:
Actions:
preferModelSelectionForInputRef where possible.model-owneddom-currentcomposition-ownedshell-backedinternal-controlapp-ownedGates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/shadow-dom.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
Exit:
Status: complete.
Actions:
EditableSelectionSourceTransition.shouldReleaseModelSelection boolean decision with an
explicit transition:
native-selection-movepreferModelSelection: falseselectionSource: dom-currentselectionSourceTransition: null to beforeinput decisions so the
beforeinput path has the same explicit shape without changing behavior.EditableDOMRoot now applies the keydown transition through
setEditableModelSelectionPreference(...).Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|word movement|line extension|read-only inline" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --grep "Backspace|Delete|visual caret|navigation and mutation" --workers=2 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/plaintext.test.ts ./playwright/integration/examples/large-document-runtime.test.ts ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "inserts text|visual caret|directly synced|DOM-owned|Backspace|Delete|traced slate-browser scenario|navigation and mutation" --workers=4 --retries=0
Results:
24 passed12 passed80 passedRejected tactic:
richtext Backspace-at-selected-text-end row was relying on DOM-only
selection setup even though its product claim is browser insertion followed
by Backspace. Under clustered load it became flaky. The row now uses
semantic+DOM setup and still proves native insertion/backspace.Decision:
Next owner:
preferModelSelection?: boolean with an
explicit selection-source transition while keeping boolean compatibility only
as a temporary fallback.Status: complete.
Actions:
EditableSelectionSourceTransition to input-state.ts so both the
kernel and mutation controller can share it.selectionSourceTransition?: EditableSelectionSourceTransition to
repair requests.preferModelSelection?: boolean as temporary compatibility fallback in
EditableRepairRequest.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/input-state.ts.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/caret-engine.ts.tmp/slate-v2/packages/slate-react/src/editable/clipboard-input-strategy.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation|Arrow|word movement|line extension|Backspace|Delete|copy|cut|paste|fragment|shell-backed|directly synced|visual caret" --workers=4 --retries=0
Results:
112 passedDecision:
app-owned as a selection
source but SelectionSource does not yet include it.Next owner:
app-owned to SelectionSource and kernel state mapping, then
use it for app/native-owned internal target transitions where behavior stays
unchanged.Status: complete.
Actions:
app-owned to SelectionSource.app-owned to EditableKernelState.SelectionSource: app-owned to kernel state app-owned.app-owned.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/input-state.ts.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/check-lists.test.ts ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "restores outer|keeps nested editor input focused|selectionchange noise|focus on checkbox|traced slate-browser scenario|navigation and mutation" --workers=4 --retries=0
Results:
24 passedDecision:
preferModelSelectionForInputRef.current remain and need to
be reduced behind selection-controller/kernel helpers.Next owner:
preferModelSelectionForInputRef.current reads in
beforeinput/key paths with explicit selection-controller helper calls where
behavior remains unchanged.Status: complete.
Actions:
isEditableModelSelectionPreferred(...) to
selection-controller.ts.syncEditorSelectionFromDOM(...) to take inputController instead
of a raw preference ref.EditableDOMRoot beforeinput/key paths to use the helper.useEditableSelectionReconciler(...) to receive inputController
and use the helper.preferModelSelectionForInputRef.current reads are
limited to selection-controller.ts.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/selection-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/selection-reconciler.ts.tmp/slate-v2/packages/slate-react/src/editable/input-controller.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsxEvidence:
rg -n "preferModelSelectionForInputRef\\.current" packages/slate-react/src -g "*.ts" -g "*.tsx"
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts ./playwright/integration/examples/check-lists.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation chained|Arrow|word movement|line extension|read-only inline|restores outer|keeps nested editor input focused|selectionchange noise|focus on checkbox" --workers=4 --retries=0
Results:
selection-controller.ts40 passedDecision:
EditableRepairRequest.preferModelSelection.Next owner:
preferModelSelection?: boolean from repair requests and
require selectionSourceTransition for repair-driven selection source
changes.Status: complete.
Actions:
preferModelSelection?: boolean from EditableRepairRequest.applyEditableRepairRequest(...).selectionSourceTransition.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.tsEvidence:
rg -n "preferModelSelection\\?:|preferModelSelection: true|preferModelSelection: false|preferModelSelection" packages/slate-react/src/editable packages/slate-react/src/components -g "*.ts" -g "*.tsx"
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/paste-html.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "navigation and mutation|Arrow|word movement|line extension|Backspace|Delete|copy|cut|paste|fragment|shell-backed|directly synced|visual caret" --workers=4 --retries=0
Results:
preferModelSelection references are:
112 passedDecision:
selection-controller and compatibility beforeinput arguments.Next owner:
Goal:
Model-owned:
Actions:
MutationController.Gates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|undo|navigation and mutation"
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
Exit:
Status: complete.
Actions:
beforeinput as data for
most structural edits, but the command contract is still split across multiple
files.Current owners:
model-input-strategy.ts owns model-backed beforeinput structural edits:
expanded range delete, deleteContent*, deleteWord*, delete*Line*,
insertLineBreak, insertParagraph, paste/drop/yank/replacement/text, and
composition commit.mutation-controller.ts owns low-level helpers for model-owned delete,
expanded delete, line break, data transfer, text input, and history.keyboard-input-strategy.ts still owns structural key fallback behavior when
beforeinput is unavailable, plus the Chrome/WebKit void-boundary delete
workaround and select-all/history hotkeys.android-input-manager.ts is a separate structural edit subsystem that
schedules delete, split, text, paste/drop, and composition operations.browser-handle.ts exposes direct structural commands for tests/proofs:
deleteBackward, deleteForward, deleteFragment, insertBreak, and
insertText.markdown-shortcuts.tsx, tables.tsx, check-lists.tsx,
inlines.tsx, code-highlighting.tsx, mentions.tsx, and toolbar-style
richtext transforms.Decision:
MutationController command
dispatch.Rejected tactics:
Evidence:
sed -n '1,260p' packages/slate-react/src/editable/editing-kernel.ts
sed -n '1,620p' packages/slate-react/src/editable/model-input-strategy.ts
sed -n '1,620p' packages/slate-react/src/editable/keyboard-input-strategy.ts
sed -n '1,280p' packages/slate-react/src/editable/mutation-controller.ts
rg -n "insertBreak|insertSoftBreak|deleteBackward|deleteForward|deleteFragment|deleteContent|deleteBy|deleteWord|deleteLine|inputType|beforeinput|Editor\\.delete|Transforms\\.delete" packages/slate-react/src site/examples/ts packages/slate-browser playwright/integration/examples -g "*.ts" -g "*.tsx"
Next owner:
beforeinput.inputType and fallback keydown hotkeys to
EditableCommandMutationController.applyEditableCommand(...)Status: complete.
Actions:
delete, delete-fragment, delete-both, insert-break, history,
insert-text, and movement trace commands.MutationController.applyEditableCommand(...) as the single dispatcher
for executable model-owned commands.beforeinput, Android, browser handle,
and example/plugin paths.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.ts.tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|undo|navigation and mutation|Arrow|word movement|line extension" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|read-only inline|void|selection|Arrow|keyboard" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "records kernel commands" --workers=4 --retries=0
Results:
48 passed48 passed4 passedDecision:
Rejected tactics:
Next owner:
applyModelOwnedBeforeInputOperation(...) structural
execution through applyEditableCommand(...):
Status: complete.
Actions:
getEditableCommandFromBeforeInputType(...) so beforeinput tracing
and beforeinput execution share the same command mapping.applyEditableCommand(...).applyEditableCommand(...) inside
applyModelOwnedTextInput(...).Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/editable/mutation-controller.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|undo|navigation and mutation|Arrow|word movement|line extension|records kernel commands" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/inlines.test.ts ./playwright/integration/examples/editable-voids.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|read-only inline|void|selection|Arrow|keyboard" --workers=4 --retries=0
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 --project=firefox --project=webkit --project=mobile --grep "plain text paste|rich HTML paste|pasted bold|pasted code|commits IME composition" --workers=4 --retries=0
Results:
52 passed48 passed24 passedDecision:
Rejected tactics:
Editor.* operations.Next owner:
EditableCommandStatus: complete.
Actions:
applyEditableCommand(...).scheduleCommand(...) so scheduled structural operations use
the same command dispatcher as desktop keydown/beforeinput.Changed files:
.tmp/slate-v2/packages/slate-react/src/hooks/android-input-manager/android-input-manager.tsEvidence:
rg -n "Editor\\.insertText|Editor\\.deleteFragment|Editor\\.deleteBackward|Editor\\.deleteForward|Editor\\.insertBreak|Editor\\.insertSoftBreak|ReactEditor\\.insertData" packages/slate-react/src/hooks/android-input-manager/android-input-manager.ts
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
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=mobile --grep "Backspace|Delete|undo|navigation and mutation|Arrow|word movement|line extension|records kernel commands|plain text paste|rich HTML paste|pasted bold|pasted code|commits IME composition" --workers=2 --retries=0
Results:
Editor.* / ReactEditor.insertData scan: no matches22 passedDecision:
Rejected tactics:
Next owner:
insertTextinsertBreakdeleteFragmentdeleteBackwarddeleteForwardselectRange as selection-controller/proof APIStatus: complete.
Actions:
applyEditableCommand(...):
insertTextinsertBreakinsertDatadeleteFragmentdeleteBackwarddeleteForwardselectRange as a selection/proof API, not a mutation command.Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "records kernel commands" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/shadow-dom.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "can add|keeps caret|deleting a decorated|cuts decorated|line break|follow-up typing|directly synced|deletes backward|deletes forward|records kernel commands" --workers=4 --retries=0
Results:
8 passed40 passedDecision:
Rejected tactics:
selectRange through mutation dispatch.Next owner:
Goal:
New concept:
type EditableInputRule = {
id: string;
trigger: string | RegExp;
at: "block-start" | "selection" | "text";
command: EditableCommand;
};
Actions:
editor.insertText = ... override to input
rules.Files:
editing-kernel.tsmutation-controller.tssite/examples/ts/markdown-shortcuts.tsxGates:
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bun test:integration-local
Exit:
Status: complete.
Actions:
Classification:
markdown-shortcuts.tsx: input-rule owner.
editor.insertText = ..., editor.deleteBackward = ...,
direct Transforms.delete, Transforms.setNodes, Transforms.wrapNodes,
and Transforms.unwrapNodes.check-lists.tsx: command owner.
editor.deleteBackward = ... with direct
Transforms.setNodes.tables.tsx: boundary policy owner.
editor.deleteBackward, editor.deleteForward, and
editor.insertBreak monkeypatches that block crossing table-cell
boundaries.inlines.tsx: mixed owner.
onKeyDown directly calls Transforms.move for
offset-based inline movement; editor.insertText/insertData wrap links.CaretEngine; URL insertion/paste
belongs to an input-rule/data-transfer rule.code-highlighting.tsx: mixed owner.
Editor.insertText.mentions.tsx: app interaction command owner.
Transforms.select and
insertMention.richtext.tsx: app command owner.
Decision:
CaretEngine, so forcing them into the first input-rule patch
would recreate the current mess.Rejected tactics:
Evidence:
sed -n '80,200p' site/examples/ts/markdown-shortcuts.tsx
sed -n '1,130p' site/examples/ts/tables.tsx
sed -n '80,135p' site/examples/ts/check-lists.tsx
sed -n '1,170p' site/examples/ts/inlines.tsx
sed -n '1,180p' site/examples/ts/code-highlighting.tsx
sed -n '1,180p' site/examples/ts/mentions.tsx
rg -n "editor\\.(insertText|deleteBackward|deleteForward|insertBreak|apply|onChange)|Transforms\\.(delete|insert|setNodes|wrapNodes|unwrapNodes|select)|Editor\\.(insertText|delete|insertBreak|addMark|removeMark)" site/examples/ts -g "*.tsx"
Next owner:
markdown-shortcuts.tsx editor method monkeypatching with
explicit input-rule/command plumbing:
Status: complete for proof-handle/semantic transport; native fast-list continuation remains a later caret/list-boundary owner.
Actions:
EditableInputRule support to the public Editable prop surface.EditableTextBlocks and exported
EditableInputRule from slate-react.insertText through input rules so tests do
not bypass the same rule contract.markdown-shortcuts.tsx editor.insertText and
editor.deleteBackward monkeypatching.inputRules for text triggersonKeyDowninsertParagraph/insertLineBreak to request caret repair
after structural breaks.Changed files:
.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx.tmp/slate-v2/packages/slate-react/src/editable/browser-handle.ts.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/index.ts.tmp/slate-v2/site/examples/ts/markdown-shortcuts.tsx.tmp/slate-v2/playwright/integration/examples/markdown-shortcuts.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts ./playwright/integration/examples/highlighted-text.test.ts ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|undo|navigation and mutation|Arrow|word movement|line extension|records kernel commands|keeps caret|directly synced|deletes backward|deletes forward" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=webkit --grep "Backspace at selected text end" --workers=1 --retries=0
Results:
12 passed91 passed, 1 WebKit row failed1 passedFailed probes recorded:
onDOMBeforeInput shortcut handler was wrong because it ran outside
the core repair timing and left fast follow-up typing against stale DOM.Decision:
EditableInputRule in the Editable beforeinput
pipeline, not editor method monkeypatching and not app-local DOM handlers.Rejected tactics:
editor.insertText / editor.deleteBackward monkeypatching.Next owner:
check-lists.tsx Backspace-at-start commandStatus: complete.
Actions:
check-lists.tsx editor.deleteBackward monkeypatching.onKeyDown.Changed files:
.tmp/slate-v2/site/examples/ts/check-lists.tsx.tmp/slate-v2/playwright/integration/examples/check-lists.test.tsEvidence:
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/check-lists.test.ts ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
24 passedDecision:
Editable, because app-owned
keydown commands need the same post-command selection discipline as input
rules.Rejected tactics:
editor.deleteBackward monkeypatching.onKeyDown special case.Next owner:
Editable keydown
handlers before converting table/inlines:
Editable applies model-owned selection preference and caret repairStatus: complete.
Actions:
EditableKeyCommandHandler and onKeyCommand as an explicit
app-owned model-command key path.onKeyCommand through applyEditableKeyDown(...) with model-owned
caret repair and selection-source transition.onKeyCommand through EditableTextBlocks.EditableKeyCommandHandler from slate-react.check-lists.tsx from local delayed repair to onKeyCommand.Changed files:
.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/packages/slate-react/src/components/editable-text-blocks.tsx.tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/index.ts.tmp/slate-v2/site/examples/ts/check-lists.tsxEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/check-lists.test.ts ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Backspace|Delete|navigation and mutation|Arrow|records kernel commands" --workers=4 --retries=0
Results:
24 passed40 passedDecision:
onKeyDown remains for UI/menu handling and should not be inferred as a
model mutation path.Rejected tactics:
preventDefault keydown handler as model-owned.Next owner:
onKeyCommand commands:
deleteBackward at cell startdeleteForward at cell endinsertBreak inside tableStatus: complete.
Actions:
tables.tsx editor.deleteBackward, editor.deleteForward, and
editor.insertBreak monkeypatching.onKeyCommand.slate-browser path locators do not map cleanly through table DOM wrappers.Changed files:
.tmp/slate-v2/site/examples/ts/tables.tsx.tmp/slate-v2/playwright/integration/examples/tables.test.tsEvidence:
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/tables.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
16 passedDecision:
CaretEngine, not a text input rule.Rejected tactics:
Next owner:
CaretEngine
ownerStatus: complete for URL typed/pasted command split; inline movement remains
Phase 7 CaretEngine owner.
Actions:
inlines.tsx editor.insertText and editor.insertData
monkeypatching.EditableInputRule for typed URL insertion.onPaste app command for plain-text URL paste.isInline, isElementReadOnly, and isSelectable editor
capability overrides because those define element semantics, not mutation
transport.Changed files:
.tmp/slate-v2/site/examples/ts/inlines.tsx.tmp/slate-v2/playwright/integration/examples/inlines.test.tsEvidence:
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
16 passedDecision:
CaretEngine owner.Rejected tactics:
isInline; they are
not mutation bypasses.Next owner:
onKeyCommand, input rules, app command helpers, or
Phase 7 caret workStatus: complete.
Actions:
Classification:
code-highlighting.tsx
Tab key inserts two spaces with Editor.insertText.onKeyCommand text command, low risk.mentions.tsx
Transforms.select(...) and insertMention(...).isInline, isVoid, markableVoid stay; they define
model behavior, not mutation transport.richtext.tsx
inlines.tsx
CaretEngine owner.markdown-shortcuts.tsx, check-lists.tsx, tables.tsx
Decision:
Rejected tactics:
Next owner:
CaretEngine owns visual movement:
Goal:
Actions:
moveHorizontalmoveWordmoveLineextendLinemoveHomeEndGates:
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Arrow|word movement|line extension|navigation and mutation"
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
Exit:
Status: complete for native desktop markdown list continuation and existing navigation guardrails.
Actions:
repairCaretAfterModelOperation() model-first by removing DOM selection
import before repair.pressSequentially('* ') does not trigger the shortcut transport.Changed files:
.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.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/playwright/integration/examples/markdown-shortcuts.test.tsEvidence:
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bun run lint:fix
bun run lint
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Arrow|word movement|line extension|navigation and mutation" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
16 passed20 passed16 passedFailed probes recorded:
* did not trigger markdown shortcut
beforeinput. The existing semantic list row remains the honest mobile proof.Decision:
Rejected tactics:
Next owner:
CaretEngine API explicit:
moveHorizontal, moveWord, moveLine,
extendLine, and moveHomeEndStatus: active replan.
Harsh take:
EditableEditingKernel is not
yet the only authority. That is why regressions keep appearing in timing
gaps between keydown, beforeinput, selectionchange, app commands, DOM
repair, and browser-native behavior.Current gap:
CaretEngine, DOMRepairQueue, SelectionController,
MutationController, input rules, and app commands exist, but they still
coordinate by convention instead of one audited kernel result.Reference lesson from local editor sources:
Decision:
Editable; recover legacy timing discipline,
not legacy monolith code.Next owner:
EditableKernelMode transitions for idle, dom-selection,
model-owned, composition, app-owned, clipboard, dragging,
internal-control, and shell-backedKernelResult as the single carrier for command, selection source,
ownership, repair, trace, and allowed native behaviorThen:
CaretEngine into explicit movement APIs under the kernel
result contract.Status: complete for trace-level state-machine skeleton and keydown movement result routing.
Actions:
getEditableKernelTransition(...) with first illegal-transition guards:
nativeAllowed requires native ownershipcreateEditableKernelTraceEntry(...) and
createEditableKernelResult(...).applyEditableKeyDown(...) return handled status.createEditableKernelResult(...).Changed files:
.tmp/slate-v2/packages/slate-react/src/editable/editing-kernel.ts.tmp/slate-v2/packages/slate-react/src/editable/keyboard-input-strategy.ts.tmp/slate-v2/packages/slate-react/src/components/editable.tsx.tmp/slate-v2/playwright/integration/examples/richtext.test.tsEvidence:
bun run lint:fix
bun run lint
bunx turbo build --filter=./packages/slate-react --force
bunx turbo typecheck --filter=./packages/slate-react --force
bunx playwright test ./playwright/integration/examples/richtext.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "Arrow|word movement|line extension|navigation and mutation|kernel transitions" --workers=4 --retries=0
bunx playwright test ./playwright/integration/examples/markdown-shortcuts.test.ts ./playwright/integration/examples/inlines.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --workers=4 --retries=0
Results:
24 passed32 passedDecision:
Rejected tactics:
Next owner:
Goal:
Actions:
Gates:
bunx playwright test ./playwright/integration/examples/paste-html.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
bunx playwright test ./playwright/integration/examples/highlighted-text.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "copy|cut"
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile --grep "paste|fragment|shell"
Exit:
Goal:
Actions:
composition state on compositionstart/update.composition-owned.Gates:
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --grep "IME|composition"
bun test ./packages/slate-react/test/large-doc-and-scroll.tsx --bail 1
Exit:
Goal:
Actions:
Gates:
bunx playwright test ./playwright/integration/examples/large-document-runtime.test.ts --project=chromium --project=firefox --project=webkit --project=mobile
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
Exit:
Goal:
Add gauntlet families:
Each generated scenario must assert:
Files:
.tmp/slate-v2/packages/slate-browser/src/playwright/**.tmp/slate-v2/playwright/integration/examples/**Exit:
Goal:
Actions:
createScenarioReductionCandidates(...).Exit:
Goal:
Cut or isolate:
decorate from primary APIrenderChunkEditableeditor.apply / onChange as normal extension pathseditor.insertText = ... example overrides where kernel commands
existExit:
Required gates:
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 --filter slate-browser test
bun run test:slate-browser
bun test:integration-local
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
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
Completion criteria:
Every user-editing scenario must assert:
Do not regress:
Perf gates must stay tied to correctness gates. No perf win can bypass:
slate-reactsrc/editable/editing-kernel.ts
src/editable/input-controller.ts
src/editable/selection-controller.ts
src/editable/mutation-controller.ts
src/editable/caret-engine.ts
src/editable/dom-repair-queue.ts
src/editable/input-router.ts
src/components/editable.tsx
slate-browsersrc/playwright/index.ts
src/playwright/scenario.ts
src/playwright/reducer.ts
src/playwright/capabilities.ts
site/examples/ts/markdown-shortcuts.tsx
site/examples/ts/richtext.tsx
site/examples/ts/large-document-runtime.tsx
Editable monolith.Do the hard-cut kernel rewrite.
The current controller split is a good staging area, but it is not the final
architecture. The next serious lane must make EditableEditingKernel the only
authority over browser editing timing.
Until then, the system will keep producing edge-case regressions because the architecture still allows multiple pieces to believe they own selection and mutation timing.
The product goal remains:
The path to get there is not another local bug fix. It is the kernel.