docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md
Objective: Define the Slate v2 architecture plan for real-time virtualized typing: replace the current fixed-delay native text repair patch with a DOM-first native input transaction scheduler, deterministic flush boundaries, and incremental pagination/layout invalidation. Keep implementation details intentionally loose enough for execution mode to auto-iterate, but make the target measurable enough that implementation cannot stop until the browser proof is actually fast.
Goal plan: docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md
Template: docs/plans/templates/slate-plan.md
Primary template: docs/plans/templates/slate-plan.md
Applied packs:
Completion threshold:
/examples/pagination?page_layout=single&strategy=virtualized, default
~1000-page document, fast printable typing must prove:
<=120ms;<=600ms;<=1400 elements and <=10 page surfaces;node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md passes.Verification surface:
check-complete from plate-2..tmp/slate-v2 slate-react input runtime,
DOM repair, native input policy, slate-layout, and pagination example..tmp/slate-v2: focused unit tests for
transaction scheduling/flush boundaries, focused Playwright for virtualized
pagination typing, typecheck, scoped lint, and dirty-local autoreview if
implementation is non-trivial.plate-2; any Slate v2 source/runtime/browser/API
claim must cite and verify the live .tmp/slate-v2 workspace command.Constraints:
Boundaries:
.tmp/slate-v2/packages/slate-react/src/editable/input-router.ts,
.tmp/slate-v2/packages/slate-react/src/editable/runtime-before-input-events.ts,
.tmp/slate-v2/packages/slate-react/src/editable/runtime-input-events.ts,
.tmp/slate-v2/packages/slate-react/src/editable/native-input-strategy.ts,
.tmp/slate-v2/packages/slate-react/src/editable/dom-repair-queue.ts,
.tmp/slate-v2/packages/slate-layout/src/index.ts,
.tmp/slate-v2/packages/slate-layout/src/react.tsx, and
.tmp/slate-v2/site/examples/ts/pagination.tsx.docs/plans/**, docs/research/**,
docs/slate-issues/**, docs/slate-v2/ledgers/**,
docs/slate-v2/references/**.Blocked condition:
Slate Plan lane state:
.tmp/slate-v2 after user
review; implementation may vary internals but must keep the invariants and
keep iterating until the measurable proof gates passCurrent verdict:
DEFERRED_NATIVE_TEXT_INPUT_REPAIR_DELAY_MS = 80 in input-router.ts, while
layout and pagination still rebuild broad projection maps from snapshots.
External runtime evidence points to deterministic update/flush boundaries and
dirty-region reconciliation, not timeout-owned correctness. The ownership
boundary is right, but the scheduler is not yet a principled runtime contract.Completion rule:
update_goal(status: complete) while any required checklist item
remains unchecked. If an item does not apply, check it and add N/A: <reason>.update_goal(status: complete) until every Slate Plan
completion gate below is satisfied and
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Skill analysis before edits | yes | slate-plan explicitly invoked; standard mode selected because user asked for durable plan and measurable auto-iteration goal. |
| Active goal checked or created | yes | get_goal returned none; create_goal created this Slate Plan planning goal. |
| Source of truth read before edits | yes | Latest user request plus live .tmp/slate-v2 source listed in Boundaries. |
docs/solutions checked for non-trivial existing-code work | no | N/A for current-state planning pass; no implementation patch in this activation. |
Live .tmp/slate-v2 grounding needed for current-state claims | yes | Read current input router, beforeinput runtime, native input strategy, DOM repair, layout refresh, paged rendering, and pagination example source. |
Work Checklist:
Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | yes | Define exact unit, browser, benchmark, and closeout proof gates, with numeric stop conditions | Verification matrix below names .tmp/slate-v2 commands and budgets: p95 visible typing <=120ms, 36-char settle <=600ms, DOM <=1400, page surfaces <=10, no fixed debounce correctness. |
| Slate v2 source, runtime, browser, package, public API, or issue-fix claim | yes | Record live .tmp/slate-v2 command/proof or mark as planning-only with reason | Planning-only: no source patch or issue-fix claim. Live source/test surfaces were read and execution commands are listed for later proof. |
| Issue ledger or PR reference changed | yes | Sync the relevant ledger/reference row or record why no sync applies | No sync applies: this plan keeps related issue rows as non-claim/proof-route guardrails and does not add PR close text. |
| Autoreview for uncommitted implementation changes | no | Load .agents/skills/autoreview/SKILL.md; run the helper from the git checkout that owns the implementation diff (.tmp/slate-v2 for Slate v2 patches) until no accepted/actionable findings, or record N/A for planning-only/no local patch | N/A planning-only: no .tmp/slate-v2 implementation diff is introduced by this plan pass. Execution phase must run autoreview from .tmp/slate-v2 if it patches source. |
| Final user-review handoff | yes | Emit final handoff or keep the plan pending with the next pass | Final handoff section is filled below with accepted items, cuts, non-claims, proof gates, and execution owner. |
| Goal plan complete | yes | Run node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md | Passed in /Users/zbeyens/git/plate-2; no Slate behavior proof is claimed by this command. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Current-state read and initial score | complete | live source read; initial score 0.72; current verdict recorded | related issue discovery |
| Related issue discovery | complete | read requirements, clusters, pilot dossier, benchmark map, issue coverage matrix, v2 sync ledger, and open issue ledger rows for input/runtime, selection, virtualization, rerender, and transaction-boundary pressure | issue-ledger pass |
| Issue-ledger pass | complete | existing sync ledger, coverage matrix, open issue ledger, and PR reference already keep the relevant rows as related/proof-route/non-claimed; no ledger/reference edit is needed until this plan promotes or changes an issue claim | intent/boundary pass |
| Intent/boundary and decision brief | complete | decision fixed: internal DOM-first native text transaction scheduler, deterministic flush boundaries, and layout invalidation; adoption story and hard cuts recorded | research refresh |
| Research, ecosystem strategy, live-source refresh | complete | ProseMirror transaction/DOMObserver model, react-prosemirror React flush bridge, Lexical update/read/dirties/reconciler model, Pretext deterministic offscreen layout constraints, Tiptap Pages limitations/table workaround, and live .tmp/slate-v2 source refreshed | pressure passes |
| Performance/DX/migration/regression/simplicity pressure passes | complete | pressure pass chose runtime-owned pending native text transactions, flush-before-boundary as default, dirty top-level layout invalidation with prefix reuse/suffix recomposition, and no new public API | verification plan and objection ledger |
| High-risk deliberate mode | complete | high-risk pre-mortem plus pressure pass covers stale reads, remounts, layout breadth, collaboration/history, and IME/mobile overclaim | verification plan and objection ledger |
| Ecosystem maintainer pass | skipped | no public external ecosystem API claim; external systems are evidence only, not compatibility targets | final closeout |
| Revision pass | complete | pressure pass revised open questions, implementation phases, and proof strategy without touching implementation | issue sync accounting |
| Slate maintainer objection ledger | complete | objection ledger below closes stale reads, scheduler privacy, layout invalidation, history/collab, table/media, browser matrix, and public DX objections with proof gates | closure score and final gates |
| Issue sync accounting | complete | no issue/reference rows need sync because all issue rows remain related/non-claim/proof-route guardrails | closure score and final gates |
| Closure score and final gates | complete | scorecard >= 0.92, no dimension below 0.85, final handoff filled, planning-only verification recorded | accepted-plan execution |
Scorecard:
| Dimension | Weight | Score | Evidence |
|---|---|---|---|
| React 19.2 runtime performance | 0.20 | 0.92 | The plan rejects hook-local timer repair and React state loops, names runtime-owned pending native text transactions, and forces Playwright/bench proof against visible latency, burst settle, DOM count, page-surface count, and profiler counters. |
| Slate-close unopinionated DX | 0.20 | 0.94 | No public debounce prop, no pagination-owned input queue, no Lexical-style public $ API. Proof controls stay in examples/benchmarks; core call sites remain normal Slate hooks/events. |
| Plate and slate-yjs migration backbone | 0.15 | 0.91 | Flush-before-boundary is the default: history, undo, command, remote/collab visibility, and export paths see committed Slate operations with metadata. A merged pending read view is explicitly last resort. |
| Regression-proof testing strategy | 0.20 | 0.93 | Verification pass names exact unit, layout, browser, benchmark, and closeout gates. It also names failure budgets that keep execution iterating until the virtualized typing target is met. |
| Research evidence completeness | 0.15 | 0.92 | Comparator research plus live source/test inspection covers ProseMirror, react-prosemirror, Lexical, Pretext, Tiptap Pages, current input runtime, layout, page virtualization, integration tests, and benchmarks. |
| shadcn-style composability and minimalism | 0.10 | 0.94 | The plan keeps the public API small, rejects product-facing typing knobs, and lets implementation auto-iterate internally against measured contracts. |
Source-backed architecture north star:
input-router.ts:42; coalescing
replaces the last repair by path in input-router.ts:520-534; virtualized
React capture skips duplicate repair in runtime-input-events.ts:159-166;
native policy is decided in runtime-before-input-events.ts:228-243 and
native-input-strategy.ts:7-38; DOM repair applies text/model selection in
dom-repair-queue.ts:183-335; layout refresh still composes full block/page
output in slate-layout/src/index.ts:2468-2545.Public API target:
| Surface | Proposed shape | User-facing DX | Compatibility / migration | Evidence | Verdict |
|---|---|---|---|---|---|
| Native text transaction scheduler | Internal slate-react runtime; no public API until proven necessary | Raw Slate users keep typing normally; apps keep onKeyDown, onInput, onDOMBeforeInput hooks | Replace fixed timeout semantics with deterministic flush policy; no app migration if kept internal | input-router.ts:42, input-router.ts:520-534 | revise |
Internal runtime target:
| Layer | Current owner | Target mechanism | Avoids | Evidence | Verdict |
|---|---|---|---|---|---|
| Native printable input | slate-react beforeinput/native input strategy | Keep DOM-first path when safe; record pending text transaction keyed by root/path/range | Model-owned per-char updates and full selector notify on hot path | runtime-before-input-events.ts:228-243, native-input-strategy.ts:7-38 | keep/revise |
| DOM input repair | slate-react runtime, not hook-local timeout state | Runtime-owned pending native text transaction queue with explicit flushPendingNativeText(reason)-style boundary; idle catch-up is secondary | Dirty fixed debounce, hook-local repair replacement, and stale command reads | input-router.ts:447-534, runtime-kernel-trace.ts:176-232, dom-repair-queue.ts:183-335 | revise hard |
| Pagination/layout | slate-layout and pagination example | Use EditorCommit.dirtyTopLevelRanges / dirtyPaths to recompute from earliest affected block with prefix reuse and suffix recomposition until page-break/height convergence | Full projection/decoration rebuild during typing | EditorCommit has dirtyPaths/dirtyTopLevelRanges in interfaces/editor.ts:1735-1771; layout currently extracts all blocks in slate-layout/src/index.ts:1534-1580 and composes all blocks in :2468-2545 | revise hard |
Hook / component / render DX target:
| Surface | Call-site shape | Composition rule | Performance rule | Evidence | Verdict |
|---|---|---|---|---|---|
| Raw Slate public API | No new public prop for typing delay or pagination batching | Apps keep normal onDOMBeforeInput, onInput, command middleware, and editor updates | Hot path stays internal; no app-side memo/debounce ceremony | runtime-before-input-events.ts:228-243, runtime-input-events.ts:159-177 | keep minimal |
| Pagination example | Proof controls may expose strategy/page count/debug metrics only | Example proves runtime behavior; it does not own text correctness | Control changes must not be required for correctness | pagination.tsx:1708-1803 | proof-only |
| Layout React helpers | Keep component API stable while internals learn invalidation | Consumers receive snapshots/fragments; internals decide affected ranges | Do not widen subscriptions or rebuild projection maps on every text input | slate-layout/src/react.tsx:454-490 | revise internals |
Plate migration-backbone target:
| Pressure | Slate substrate target | Plate adaptation route | Non-goal | Evidence | Verdict |
|---|---|---|---|---|---|
| Plate plugins and commands | Deterministic committed transactions after native DOM draft flush | Plate keeps using command/input-rule/plugin layers above raw Slate commits | Plate should not need to own raw DOM drafts or set debounce options | runtime-before-input-events.ts:228-243, dom-repair-queue.ts:330-345 | keep core-owned |
| Plate pagination/docs | Layout invalidation API and page snapshots remain reusable | Plate can consume page fragments/metrics after execution proves the substrate | Product-specific page UI, table policy, and export fidelity are outside raw Slate | slate-layout/src/index.ts:2468-2545 | revise layout substrate |
slate-yjs migration-backbone target:
| Pressure | Slate substrate target | Collaboration route | Non-goal | Evidence | Verdict |
|---|---|---|---|---|---|
| Local printable DOM drafts | Pending native text is local runtime state until a deterministic flush creates Slate operations | Yjs sees committed text transactions with metadata/history semantics, not timer artifacts | Current @slate/yjs adapter compatibility is not required for this raw-Slate plan | dom-repair-queue.ts:330-345, #1770 issue accounting | keep local until flush |
| Remote updates during pending text | Flush before applying command/selection/history/collab boundaries or define an explicit merged read view | Later execution must prove remote/local ordering with unit contracts before claiming collaboration readiness | Do not expose raw DOM draft state as collaborative document content | issue ledger #1770; current source lacks this contract | research/pressure needed |
Intent / boundary record:
<=120ms p95 visible
keystroke latency, <=600ms 36-character burst model-settle, no dropped text,
correct model/DOM selection, <=1400 DOM elements, <=10 page surfaces, and
no fixed timeout as correctness.slate-react input runtime owns native text transactions, flush
boundaries, model selection catch-up, and command/read coherence.slate-layout owns committed snapshot/page invalidation after
flushed transactions; pagination is the proof surface, not the text-input
owner.Decision brief:
Issue accounting:
| Issue / cluster | Claim category | Exact claim | Why | Proof route | V2 sync ledger | PR line |
|---|---|---|---|---|---|---|
| Mobile, IME, and input semantics cluster | Related / not fixed | The plan routes real-time typing through first-class input/runtime ownership; it does not close mobile or IME issues by planning alone. | Corpus marks this as the top-priority theme with 129 issues and requires DOM/model input suppression that does not desync. | Execution must add exact browser/device proof before any closure claim. | Existing cluster rows stay related. | No auto-close line. |
| DOM selection/focus bridge cluster | Related / not fixed | Deterministic flush boundaries should reduce stale selection, but a real selection bridge still has to exist. | Cluster says cursor-loss, wrong-path, and crash-class selection bugs are major runtime ownership pressure. | Focused browser selection proof plus model/DOM selection equality checks. | Existing rows stay related. | No auto-close line. |
#6022 Android mark-toggle keyboard/cursor jump | Related / not fixed | Native typing scheduler may help this class, but it does not prove Android keyboard visibility. | Existing pilot keeps it cluster-synced and says exact closure requires Android/device proof. | Raw Android Chrome/WebView or equivalent device lane. | Preserve related/no fixed claim. | Keep related text only. |
#4541 editor.selection lags after insertText | Related / not fixed | Commands and imperative reads must not observe stale selection after printable typing. | Dossier describes repeated text insertion leaving editor.selection behind. | Unit contract for flush-before-command plus browser burst/selection proof. | Existing cluster-synced row stays open until exact repro proof. | No auto-close line. |
#790 dynamic rendering | Related proof-route backlog | Page/spread virtualization and mounted-count budgets target this pressure, but planning does not claim it. | Coverage matrix and sync ledger require mount/edit/scroll benchmarks, DOM coverage, and native behavior proof. | Virtualized pagination benchmark: initial mount, fast scroll, click/edit, mounted DOM count, browser native behavior. | Preserve proof-route backlog. | No auto-close line. |
#2051 leaf rerender breadth | Related guardrail | Simple native typing should not broaden leaf/block rerender breadth. | Ledger keeps it as benchmark-gated performance pressure. | Rerender breadth plus p95 typing latency under large document and many-leaf workloads. | Preserve guardrail status. | No auto-close line. |
#5131 selection-driven rerenders | Related guardrail | Scheduler/layout consumers must not make selection changes rerender broad editor surfaces. | Ledger keeps useSlate broad by contract and asks for narrower selector/block-slice locality proof. | Subscription/rerender instrumentation during selection and typing. | Preserve not-claimed/guardrail status. | No auto-close line. |
#5216 Safari selection lag | Related / browser-specific | Selection proof should include Safari before claiming broad parity. | Open ledger treats it as selection-performance pressure. | Safari long-paragraph selection/typing proof, if execution expands beyond Chromium. | Preserve cluster-synced status. | No auto-close line. |
#1770 operation combining/transaction boundaries | Related / not fixed | Pending native text commits must land as coherent history/collab transactions, not raw timing artifacts. | Coverage matrix says existing proof does not provide a general operation-merging utility. | Unit contracts for pending transaction commit metadata, history, undo, and collaboration visibility. | Preserve related status. | No auto-close line. |
Issue-ledger sync status:
#6022, #790,
#2051, #5131, #5216, #4541, and #1770.#6022 pilot already states the
Android/device proof boundary.#790, #2051, #5131, #6022, and #1770
proof boundaries.Ecosystem strategy synthesis:
| System | Source | Mechanism | Avoids | Steal | Reject | Slate target | Verdict |
|---|---|---|---|---|---|---|---|
| ProseMirror | ../prosemirror-state/src/transaction.ts:22-40, :67-92; ../prosemirror-view/src/index.ts:89-90, :153-219, :487-514; ../prosemirror-view/test/view.ts:52-54 | Transaction carries doc, selection, stored marks, and metadata; view owns DOMObserver and explicit DOM flush in tests. | Stale selection/metadata after edits and implicit DOM mutation repair. | Explicit transaction metadata, mapped selection, DOMObserver flush as a testable internal boundary. | Importing the full PM document-view/node-view architecture or public dispatchTransaction shape. | Internal pending native text transactions with explicit metadata and first-class flush test helpers. | steal discipline, not architecture |
| react-prosemirror | ../react-prosemirror/src/hooks/useEditor.ts:57-100; ../react-prosemirror/src/hooks/useEditorEffect.ts:29-44; ../react-prosemirror/src/contexts/EditorContext.ts:8-19 | React adapter wraps dispatch in flushSync by default and disables that inside editor effects to avoid reentrant sync loops. | Letting React scheduling accidentally own editor correctness. | Adapter-level flush switch and effect-safe dispatch policy. | A React-controlled editor state loop as the typing hot path for Slate. | Keep hot path in slate-react runtime; use React only to publish stable committed state and measured snapshots. | useful guardrail |
| Lexical | ../lexical/packages/lexical-website/docs/intro.md:80-106, :110-116; ../lexical/packages/lexical-website/docs/concepts/editor-state.md:122-134, :193-228; ../lexical/packages/lexical-website/docs/concepts/transforms.md:52-93; ../lexical/packages/lexical/src/LexicalUpdates.ts:505-553, :914-1053 | Immutable current state plus pending mutable state; reads flush pending updates; commands run in update context; dirty leaves/elements drive reconciliation; discrete updates force sync commit. | Full-tree recompute, stale reads, and unbounded transform loops. | Read/update boundary semantics, dirty-set invalidation, sync commit escape hatch, and clear dirty-selection validation. | Lexical's node model, transform system, and public $ helper API. | Pending native text queue must define read/flush semantics and dirty affected ranges without changing Slate's public model. | strongest runtime comparator |
| Pretext | ../pretext/AGENTS.md:49-78; ../pretext/pages/demos/markdown-chat.data.ts:142-146; ../pretext/src/layout.test.ts:1226-1298 | Prepare-time text measurement and deterministic line walking after width/fonts are known; hot layout path has no DOM/canvas reads. | Asking DOM for offscreen heights during virtualization. | Offscreen/page height planning and streaming line cursor API as a future layout oracle. | Making Pretext authoritative for active editing caret/composition, or claiming cross-client fidelity while prepare() still relies on canvas/browser font metrics. | Use DOM truth for the active edit corridor; use Pretext-like APIs only for offscreen estimation/page planning and tests. | bounded adoption |
| Tiptap Pages | ../tiptap-docs/src/content/pages/core-concepts/limitations.mdx:11-20; ../tiptap-docs/src/content/pages/guides/table-with-pages.mdx:16-66; ../tiptap-docs/src/content/pages/core-concepts/options.mdx:11-87 | Page layout is product-facing; tables need a Pages-specific table package because table layout/splitting is heavily modified. | Pretending CSS page gaps solve complex block/table splitting. | Clear owner split: page/layout package owns table/media split policy, not the core text runtime. | CSS-float pagination limits, semantic AST splitting as the default answer, or page options as a typing correctness knob. | slate-layout owns split policy/invalidation; slate-react owns text input. Tables/media need provider-owned layout fragments, not arbitrary AST mutation. | cautionary reference |
| Current Slate v2 | .tmp/slate-v2/packages/slate-react/src/editable/input-router.ts:42, :447-534; runtime-input-events.ts:159-177; dom-repair-queue.ts:330-345; slate-layout/src/index.ts:487-488, :2468-2545, :2554-2628; slate-layout/src/react.tsx:454-490; site/examples/ts/pagination.tsx:1708-1803 | Native path exists and virtualized mode can defer repair, but correctness and layout settle still depend on fixed timers and broad snapshot/projection work. | Duplicate React capture repair and app-owned batching. | Existing DOM repair import, native-safety policy, and page fragment projection as starting points. | Keeping 80ms/320-640ms timers as the architecture. | Replace with deterministic flush + dirty block/page invalidation; keep implementation details free to iterate against metrics. | revise |
Legacy regression proof matrix:
| Regression class | Legacy behavior | Slate v2 target | Proof route | Owner | Status |
|---|---|---|---|---|---|
| stale command/read after printable typing | editor.selection or text can lag native DOM insertion (#4541 pressure) | command, toolbar, input-rule, history, and imperative reads flush pending text first by default; merged pending reads are allowed only if proof shows flushing is too expensive | unit tests around insertText + command/read; browser burst asserts final model text/selection equals visible caret | slate-react runtime | pressure accepted |
| duplicate or reordered text under burst typing | fixed repair delay can coalesce to the last repair and rely on timeout ordering | pending transaction queue preserves input order and path/root ownership; replacement-by-path is not enough | 36-char burst browser test plus scheduler unit tests | slate-react runtime | pressure accepted |
| wrong path after virtualization remount/scroll | pending repair targets can become stale when visible pages remount | pending transaction invalidates on root/path/range/node identity mismatch or flushes before remount boundary | fast scroll/edit/remount browser proof | slate-react + virtualized DOM strategy | pressure accepted |
| layout stall during large-doc typing | text-only refresh waits 320-640ms then recomposes broad layout/projection | committed text invalidates from dirty top-level range; reuse prefix blocks/fragments/pages and recompose suffix until layout converges | perf trace with 1000-page route and DOM/page-surface budgets | slate-layout | pressure accepted |
| IME/mobile overclaim | printable ASCII native path does not prove composition/mobile | plan keeps IME/mobile issues related until exact device/browser proof exists | composition unit tests plus raw device/browser lane before issue closure | slate-react runtime | guarded |
Performance / DX / regression pressure review:
| Pressure | Current risk | Accepted constraint | Rejected shortcut | Proof needed next |
|---|---|---|---|---|
| Typing hot path | Hook-local 80ms repair still makes correctness timing-dependent. | Move pending native text into runtime-owned transaction state with explicit flush reasons; timeout can only be idle catch-up. | Lowering or tuning DEFERRED_NATIVE_TEXT_INPUT_REPAIR_DELAY_MS. | scheduler unit tests and browser burst latency/model-settle proof |
| React scheduling | React capture and selector notifications can accidentally become the hot path. | DOM owns visible insert; React publishes committed state after flush. Existing deferred selector and DOM text sync machinery can be reused, but not as public API. | Controlled React editor state loop or app-side memo/debounce ceremony. | render-count/profiler row in browser proof |
| Command/history/collab visibility | Pending DOM text can become invisible state. | Flush-before-boundary is the default rule for command, selection, blur, paste/delete, undo/history, and collab apply/export. | Broad merged pending read view as the first design. | unit tests for every boundary before execution closes |
| Layout breadth | Layout has useful commit dirtiness but currently ignores it and rebuilds the whole block/projection world. | Use dirtyTopLevelRanges and dirtyPaths to start from the earliest affected block, reuse prefix layout, and recompose suffix until page breaks/heights stabilize. | Pagination-local invalidation, whole-doc compose after every text change, or AST splitting to fake pagination. | perf trace plus projection/page-surface counters |
| Table/media pagination | Complex boxes can move page breaks and span pages. | nodeLayout / layout provider owns split policy and fragments; Slate AST stays semantic. | Splitting table nodes in the AST as the default visual pagination fix. | multi-page table fixture proof |
| Public DX | Users should not tune correctness. | No public debounce or batching prop; debug metrics and example controls are proof-only. | typingDebounceMs, pagination-owned text batching, or app-owned flush hooks. | API diff review in verification pass |
Browser stress / parity strategy:
| Surface | Scenario | Browser/device | Command or proof route | Expected signal | Status |
|---|---|---|---|---|---|
| virtualized pagination typing | /examples/pagination?page_layout=single&strategy=virtualized, default ~1000 pages, mid-document paragraph, 36 printable chars | Chromium first; Safari added before broad selection parity claim | focused Playwright perf spec with input latency marks and final model/DOM/caret assertion | p95 visible keystroke <=120ms, settle <=600ms, no dropped/reordered text | pending execution |
| page-level virtualization budget | same route while typing and fast scrolling near table/page breaks | Chromium first | DOM counter and page-surface instrumentation in browser proof | <=1400 DOM elements and <=10 page surfaces | pending execution |
| deterministic flush boundaries | type then command/read/selection move/blur/paste/delete/undo | unit tests plus one browser proof row | scheduler flush count and model text/selection equal DOM after each boundary | no stale command/read state | pending execution |
| table/media layout pressure | multi-page table and media block in pagination fixture | Chromium first | layout fragment snapshot/perf proof | affected page range recomputes; no AST table split required for visual pagination | pending execution |
| IME/mobile non-claim guard | composition and mobile keyboard flows | Safari/Android only if execution claims those issues | separate proof lane before closing #6022 or IME cluster | no issue closure from desktop typing alone | guarded |
Execution verification matrix:
| Gate | Cwd | Command | Must prove | Stop condition |
|---|---|---|---|---|
| Native text scheduler and flush contracts | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun --filter slate-react test:vitest -- input-router-contract.test.tsx runtime-repair-engine-contract.test.tsx model-input-strategy-contract.test.ts native-input-strategy-contract.test.ts editing-kernel-contract.test.ts editing-epoch-kernel-contract.test.ts keyboard-input-strategy-contract.test.ts selection-runtime-contract.test.ts selection-controller-contract.test.ts projected-collab-substrate-contract.test.ts | Pending text preserves order/root/path/range; every command/read/selection/blur/composition/history/collab boundary flushes or explicitly proves a merged pending view. | All rows green; no correctness dependency on a fixed timer. |
| History/collab transaction visibility | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun test ./packages/slate/test/collab-history-runtime-contract.ts ./packages/slate-history/test/history-contract.ts ./packages/slate-history/test/document-state-history-contract.ts | Flushed native text appears as normal Slate operations with history/collab metadata; undo/redo and remote apply do not see hidden DOM draft state. | All rows green after scheduler changes. |
| Layout invalidation | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun --filter ./packages/slate-layout test | Committed text edits use dirtyTopLevelRanges / dirtyPaths, reuse prefix layout, and recompose suffix only until page-break/height convergence. | Layout tests green plus a new affected-range regression row. |
| Page virtualization unit contract | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun --filter slate-react test:vitest -- dom-strategy-page-virtualization.test.tsx dom-strategy-and-scroll.test.tsx render-profiler-contract.test.tsx | Virtualized mode retains page items, maps split-table rows to page items, emits stable metrics, and keeps render profiler counters bounded. | Unit tests green and metrics do not loop on unchanged virtualized state. |
| Pagination browser perf and correctness | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bunx playwright test playwright/integration/examples/pagination.test.ts --project=chromium | Direct virtualized load, staged-to-virtualized switch, 1000-page route, 10-page table, fast scroll, mid-document typing, burst typing, margin selection, and projected paragraph click/edit stay correct. | p95 visible typing <=120ms; 36-char burst settle <=600ms; DOM <=1400; page surfaces <=10; no dropped/reordered chars; model selection equals visible caret. |
| Huge-document regression proof | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | PLAYWRIGHT_RETRIES=0 PLAYWRIGHT_WORKERS=1 bunx playwright test playwright/integration/examples/huge-document.test.ts --project=chromium | Existing staged/virtualized long-editor focus, scroll, typing, and undo rows still pass after scheduler/layout changes. | Chromium row green; no regression in scroll-to-caret behavior. |
| React and browser benchmark proof | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | REACT_ACTIVE_TYPING_BREAKDOWN_BLOCKS=5000 REACT_ACTIVE_TYPING_BREAKDOWN_TYPE_OPS=36 bun bench:react:active-typing:local | Active typing does not broaden React rerender work or require app-side memo/debounce ceremony. | Artifact shows bounded active typing work against current baseline. |
| Huge-document trace proof | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | SLATE_BROWSER_TRACE_BLOCKS=5000 SLATE_BROWSER_TRACE_TYPE_OPS=36 bun bench:react:huge-document:browser-trace:local | Browser trace has no long-task/long-animation-frame regression that contradicts the Playwright perf row. | Artifact remains under accepted benchmark budget or records a blocker. |
| Final fast package gate | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun check | Source changes pass lint, typecheck, and fast package tests. | Green before handoff. |
| Release-quality browser claim gate | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | bun check:full | Release-proof guards plus full integration sweep pass before any production-ready browser claim. | Green only when execution claims release-quality browser behavior. |
| Implementation autoreview | /Users/zbeyens/git/plate-2/.tmp/slate-v2 | Load /Users/zbeyens/git/plate-2/.agents/skills/autoreview/SKILL.md and run its local helper from this checkout. | Non-trivial scheduler/layout implementation has no accepted/actionable review findings. | Clean autoreview or documented accepted exceptions. |
Execution auto-iteration rule:
Execution TDD queue:
| Order | Red test to add or harden | Why | Required result |
|---|---|---|---|
| 1 | Scheduler preserves an ordered burst across same path, different path, remount, and selection movement. | Prevents another "last repair wins" timeout patch. | Burst text imports exactly once, in order, with final caret at DOM caret. |
| 2 | Flush-before-command/read/blur/selection/history/collab boundaries. | Commands and plugins cannot see stale model text. | Boundary observers read committed Slate state, not raw DOM drafts. |
| 3 | Layout affected-range recomposition from dirtyTopLevelRanges. | Model catch-up is useless if layout still recomposes the world. | A text edit before page N reuses prefix pages and recomposes only the needed suffix. |
| 4 | Virtualized pagination burst typing in the 1000-page route. | This is the user-visible perf target. | <=120ms p95 visible typing, <=600ms 36-char settle, no dropped text, <=1400 DOM, <=10 page surfaces. |
| 5 | Multi-page table and media route under fast scroll plus typing. | Avoids table-specific hacks or AST splitting. | Provider-owned layout fragments remain semantic and bounded. |
Verification workspace gate:
| Claim | Workspace | Command | Result | Owner |
|---|---|---|---|---|
| Current source grounding only | .tmp/slate-v2 | `nl -ba <source files> | sed ...` | source read complete; no behavior proof claimed in planning pass |
| External runtime comparator grounding | sibling repos from /Users/zbeyens/git/plate-2 | targeted nl -ba / rg reads in ../prosemirror-*, ../react-prosemirror, ../lexical, ../pretext, and ../tiptap-docs | comparator evidence complete for planning; no external implementation borrowed | slate-plan research pass |
| Pressure source grounding | .tmp/slate-v2 | targeted rg / nl -ba reads for commit dirtiness, layout extraction/projection, DOM repair, kernel trace, node selectors, and page mount plan | pressure pass complete; no behavior proof claimed | slate-plan pressure pass |
| Test and benchmark surface grounding | .tmp/slate-v2 | read slate-react and slate-layout package scripts, pagination/huge-document Playwright specs, input-router/runtime-repair/page-virtualization/render-profiler/layout tests, and benchmark scripts | verification pass complete; execution proof commands named above | slate-plan verification pass |
Autoreview workspace gate:
| Reviewed patch owner | Cwd | Command | Result | Notes |
|---|---|---|---|---|
| Planning-only pass | /Users/zbeyens/git/plate-2 | N/A | No implementation patch to review | Execution must run autoreview from .tmp/slate-v2 after non-trivial source changes. |
Applicable implementation-skill review matrix:
| Lens | Applies | Status | Findings | Plan delta |
|---|---|---|---|---|
| vercel-react-best-practices | yes | complete for planning | React must publish committed state without owning printable typing correctness; no controlled React editor state loop | no public React prop; runtime owns typing |
| performance-oracle | yes | complete for planning | Biggest risk is whole-doc layout/projection after commit, not just native input repair | dirty top-level range plus prefix reuse/suffix recomposition |
| performance | yes | complete for planning | Needs interaction latency, model-settle, mounted DOM, page-surface, and render-count budgets | verification plan must name commands/counters |
| tdd | yes | complete for planning | Behavior requires scheduler unit tests, flush-boundary tests, remount invalidation tests, and browser perf/caret tests | verification plan owns exact test names |
| shadcn | no | skipped for current pass | No UI API change intended; pagination controls are proof surface only | no change |
| react-useeffect | maybe | skipped for planning | No implementation hook/effect code is edited in planning mode. Execution review should apply it only if the scheduler lands in React effects. | no change now |
High-risk deliberate-mode pre-mortem:
| Risk | Trigger | Failure mode | Mitigation | Proof | Status |
|---|---|---|---|---|---|
| stale reads from pending DOM text | command, input rule, toolbar, history, or collaboration reads before flush | user-visible DOM has text that Slate commands/history do not see | flush-before-boundary first; merged pending read view only if proof demands it | unit tests for command/read boundaries and Playwright burst selection | accepted, proof pending |
| wrong ownership after virtualization remount | visible page/window changes while pending text exists | flush writes to stale path or wrong root | pending transaction keyed by root/path/range plus node identity invalidation | scroll/type/remount browser proof | accepted, proof pending |
| layout recomposes too broadly | text-only edit in 1000-page route | typing still stalls because layout/projection rebuilds too much | dirty top-level range, prefix reuse, suffix recomposition, and bounded page surfaces | perf trace + DOM/page count thresholds | accepted, proof pending |
| IME/mobile overclaim | scheduler handles printable ASCII but not composition/mobile | plan accidentally claims #6022/IME closure | non-claim rows plus raw device/browser proof gate | issue ledger and device proof before claim | guarded |
Slate maintainer objection ledger:
| Change | Objection | Tradeoff | Evidence | Migration/docs/proof answer | Verdict |
|---|---|---|---|---|---|
| Runtime-owned pending native text transactions | More hidden runtime state can become hard to reason about. | Internal complexity buys native visible typing and keeps public API clean. | Current fixed repair delay and path replacement are in input-router.ts; DOM repair already imports text through editor.update. | Keep it private, expose only debug/profiler counters, and prove ordered burst plus invalidation tests. | accept with tests |
| Flush-before-boundary default | Flushing at every boundary may be too slow. | Correctness first; merged pending reads are more invasive. | Lexical-style reads flush pending state; Slate commits already carry metadata. | Execute flush-boundary unit/browser tests first; add an internal merged read view only if measured flush cost breaks budgets. | accept |
| Dirty layout invalidation | Page breaks can ripple, so invalidation may still recompose large suffixes. | Prefix reuse plus suffix convergence is honest; fake local pagination is worse. | EditorCommit exposes dirtiness; layout currently extracts/composes broadly. | Proof must show affected-range reuse and browser budgets; if not, introduce a narrow internal layout.invalidate primitive, not an app prop. | accept |
| Page-level virtualization while editing | Remounts can make pending DOM targets stale. | Scheduler must key by root/path/range and node identity, then flush or invalidate before ownership changes. | Existing page virtualization tests map split table rows to page items and share visible page windows. | Browser proof must cover fast scroll, table rows, clicked projected paragraphs, and final selection equality. | accept |
| History/collab visibility | DOM drafts could bypass undo, history, or remote subscribers. | Pending DOM text stays local only until deterministic flush. | History/collab contract tests already exist and issue #1770 is a guardrail. | Add focused rows proving flushed text becomes normal operations with metadata and remote apply does not see hidden draft state. | accept |
| Table/media pagination | Visual splitting can tempt AST mutation. | Layout provider owns visual fragments; Slate AST remains semantic. | Tiptap Pages needed a dedicated table package; current pagination route already has multi-page table proof. | Keep table split policy in layout fragments and prove a 10-page table without semantic table-node splitting. | accept |
| Browser matrix | Chromium perf is not Safari/mobile/IME parity. | Narrow proof is valid if claims stay narrow. | Existing issue accounting keeps #6022, IME, Safari selection, and mobile rows as related/non-claim. | Chromium required for perf; Safari/mobile required only before broad browser, mobile, or IME issue closure. | accept |
| Public DX | Users may ask for knobs when perf varies. | Exposing correctness knobs makes the wrong abstraction sticky. | User feedback explicitly rejected dirty debounce; examples already host stress controls. | Keep controls for proof metrics only; no typingDebounceMs or pagination text-batching prop. | accept |
Hard cuts and rejected alternatives:
| Option / API | Keep / cut / reject | Why | Migration cost | Evidence | Follow-up |
|---|---|---|---|---|---|
| Fixed repair debounce as correctness | cut | Timing guesses fail under fast typing, slow devices, command reads, blur, and remounts. | Internal only if current patch is replaced; no public migration. | input-router.ts:42, input-router.ts:470-473 | Replace with deterministic scheduler contract. |
Public typingDebounceMs / pagination text-batching prop | reject | Pushes core input correctness onto apps and teaches the wrong API. | None; do not add it. | User explicitly called the debounce dirty; public API target stays internal. | Keep proof knobs to metrics/stress only. |
| Pagination-local repair queue | reject | Would make paginated editors semantically different from normal Slate editors. | Avoided by core-owned scheduler. | pagination.tsx:1708-1803 proves pagination already does projection work; it should not own text semantics. | Core runtime owns typing. |
| Synchronous model commit on every character as sole fix | reject as sole answer | It preserves Slate coherence but keeps React/model/layout work on the hot path. | Could remain fallback for unsafe native cases. | native policy already falls back when unsafe in native-input-strategy.ts:23-67. | Keep as fallback, not large-doc fast path. |
| Merged pending read view as default | reject for first execution | It makes every read path conditional and risks leaking DOM drafts into plugins/history/collab. Flush-before-boundary is simpler and closer to Slate. | None unless execution proof shows flush is too expensive. | Lexical reads flush pending updates; current Slate commit metadata already supports deterministic operations. | Keep as last-resort optimization only. |
| Internal DOM-first scheduler | keep | Best split of native typing latency and Slate model coherence. | Internal runtime migration; public call sites unchanged. | runtime-before-input-events.ts:228-243, dom-repair-queue.ts:330-345, runtime-kernel-trace.ts:176-232 | Verification pass must name scheduler/flush tests. |
| Incremental layout invalidation | keep | Without it, committed text still forces page/projection work too broadly. Current commit dirtiness already exposes dirtyTopLevelRanges; layout should consume it. | Internal layout change; page snapshot consumers should stay stable. | interfaces/editor.ts:1735-1771, public-state.ts:3577-3693, slate-layout/src/index.ts:1534-1580, :2468-2545 | Verification pass must name layout perf tests. |
Plan deltas from review:
slate-react owns
DOM-first pending native transactions and deterministic flush; slate-layout
owns committed-snapshot invalidation; apps own product policy; timeout,
public debounce props, and pagination-local text queues are rejected.dirtyTopLevelRanges-driven layout
invalidation with prefix reuse plus suffix recomposition..tmp/slate-v2 unit, browser, benchmark, fast
package, full browser, and autoreview gates. It also closed maintainer
objections and defined the auto-iteration stop rule: keep changing internals
until the same command set proves the numeric budgets or a precise blocker is
recorded.Open questions and decision-changing evidence:
| Question | Why it matters | Evidence needed | Owner | Status |
|---|---|---|---|---|
| Does execution need a merged pending read view, or is flush-before-read enough? | This decides how commands/toolbars/collab observe native DOM drafts. | Unit/browser proof must show whether boundary flushing meets latency budgets. | verification plan | decided default: flush-before-boundary; merged read view only if proof demands it |
| What is the minimal layout invalidation API? | The plan should avoid over-prescribing internals while still forcing affected-range behavior. | Exact execution API can vary, but proof must show dirtyTopLevelRanges/dirtyPaths drive affected-range recomposition. | verification plan | decided direction: earliest dirty top-level block, prefix reuse, suffix recomposition until page-break convergence |
| Does Pretext-style pagination require authority over page breaks before execution? | Authoritative page breaks may affect collab/export determinism. | Existing pageBreak snapshot API can stay future extension point. | verification plan | no for active typing; Pretext-style layout stays offscreen/future export aid |
| Which browser matrix is mandatory for final execution proof? | Chromium alone may miss Safari selection and mobile/IME failures. | Verification pass must name exact browser rows. | verification plan | Chromium required for perf; Safari/mobile required only for explicit selection/mobile/IME issue claims |
Implementation phases with owners:
| Phase | Owner | Scope | Entry criteria | Exit criteria | Verification |
|---|---|---|---|---|---|
| 1. Replace fixed delay with scheduler contract | slate-plan execution mode | internal native text transaction queue and flush policy | user accepts final plan | no arbitrary fixed debounce owns correctness; commands/blur/composition/selection boundary flush deterministically | unit tests for scheduler and flush boundaries |
| 2. Wire model/selection correctness | slate-plan execution mode | DOM repair and model selection catch-up | phase 1 green | visible DOM, model text, selection, undo/history metadata stay coherent | unit tests + Playwright burst/selection |
| 3. Incremental pagination invalidation | slate-plan execution mode | dirty top-level range invalidation, prefix layout reuse, suffix recomposition, and projection/page-mount bounding after committed transactions | phase 2 green and profiler points at layout/projection | typing avoids full projection/layout rebuild unless structure/metrics require it | browser perf benchmark + profiler evidence |
| 4. Stress proof and review | slate-plan execution mode | default 1000-page route and edge cases | phases 1-3 green | measured thresholds pass and autoreview has no accepted/actionable findings | Playwright, typecheck, lint, autoreview |
Fast driver gates:
| Gate | Cwd | Command / artifact | Proves | Status |
|---|---|---|---|---|
| planning artifact check | plate-2 | node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md | final plan/template integrity | complete |
| current source grounding | .tmp/slate-v2 | source reads listed in Boundaries | current state claims | complete |
| Slate v2 behavior check | .tmp/slate-v2 | execution verification matrix above | runtime/browser behavior | named for accepted-plan execution |
Final user-review handoff outline:
typingDebounceMs, no
pagination-local text queue, no default merged pending read view, no table AST
split for visual pagination.#790, #2051, #5131,
#6022, #4541, #5216, and #1770 as related guardrails only. It closes
no issue by planning alone.bun check, and autoreview rows above; bun check:full is required before a
release-quality browser claim..tmp/slate-v2, use the TDD queue,
keep implementation details flexible, and keep iterating until the numeric
budgets pass or a precise blocker is recorded.Final completion gates:
| Gate | Required evidence | Status |
|---|---|---|
| score >= 0.92 and no dimension below 0.85 | scorecard rows cite evidence | complete: weighted score is above 0.92 and no row is below 0.91. |
| all pass rows complete or skipped with evidence | phase/pass table closed | complete: every phase row is complete or skipped with reason. |
| issue/reference sync closed | issue-ledger sync status closed | complete: no sync applies because no issue/PR close claim changed. |
| live source grounding complete | source-backed rows cite current owners | complete: source and test/bench surfaces were read in .tmp/slate-v2. |
| workspace verification recorded | verification workspace gate closed | complete: planning-only proof recorded and execution commands named. |
| autoreview clean or N/A | .agents/skills/autoreview/SKILL.md loaded and clean from the git checkout that owns non-trivial uncommitted implementation changes (.tmp/slate-v2 for Slate v2 patches), or N/A with reason | complete: N/A because this pass changed only the planning artifact. |
| final handoff emitted or lane remains active | final response / next pass recorded | complete: handoff section above is filled and next_pass is none. |
check-complete passes | node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md | complete: [autogoal] complete: docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md. |
Findings:
slate-react, not pagination.input-router.ts:42
hard-codes an 80ms delayed repair, and input-router.ts:520-534 only
coalesces by replacing the last repair for the same path.runtime-input-events.ts:159-166 already avoids duplicate React capture repair
in virtualized mode. Keep that owner split.runtime-before-input-events.ts:228-243 makes native-vs-model policy before
sync/input rules; command/input-rule boundaries must become explicit flush
boundaries.dom-repair-queue.ts:183-335 already knows how to import DOM text into the
model and move selection when the target still owns the DOM caret. The plan
should build on that instead of inventing a pagination-local path.slate-layout/src/index.ts:2468-2545 still extracts all layout blocks and
composes the whole snapshot on refresh. It defers text-only refresh at
index.ts:2625-2627, but that is separate from real incremental invalidation.pagination.tsx:1724-1803; this is acceptable for proof but not
enough for the final real-time architecture.#790, #2051, and #5131 make the perf bar concrete: execution must prove
mounted count, fast scroll/edit behavior, and rerender breadth, not just lower
one local timeout.#6022, #4541, and the mobile/IME cluster make overclaiming dangerous.
This plan can define the runtime route, but exact closure needs device or
repro-specific browser proof.#1770 is a transaction-boundary guardrail: pending native text cannot become
an invisible side channel that bypasses history, undo, or collaboration.DEFERRED_NATIVE_TEXT_INPUT_REPAIR_DELAY_MS = 80, skips React capture
repair when deferred native text repair is active, imports DOM text through
editor.update, and still composes layout/projection from whole snapshots.EditorCommit already exposes dirtyPaths, dirtyTopLevelRanges, and
runtime-id dirtiness, so the long-term layout fix should consume commit
dirtiness instead of adding a pagination-only invalidation channel.extractLayoutBlocks
reads every child and getSlatePageLayoutProjection walks every fragment.
That makes lower text-repair timers irrelevant if committed text still
triggers whole-document layout/projection work.input-router-contract.test.tsx, runtime-repair-engine- contract.test.tsx, dom-strategy-page-virtualization.test.tsx,
render-profiler-contract.test.tsx, page-layout-contract.test.ts,
pagination Playwright rows for virtualized startup, table stress, middle
typing, burst typing, fast scroll, and huge-document scroll/focus tests.Decisions and tradeoffs:
Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| None yet | 0 |
External/browser findings:
Timeline:
.tmp/slate-v2 source..tmp/slate-v2 source evidence recorded; next pass is pressure review.node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-31-real-time-virtualized-typing-architecture.md.Verification evidence:
/Users/zbeyens/git/plate-2/.tmp/slate-v2:
nl -ba packages/slate-react/src/editable/input-router.ts,
runtime-before-input-events.ts, runtime-input-events.ts,
native-input-strategy.ts, dom-repair-queue.ts,
packages/slate-layout/src/index.ts, packages/slate-layout/src/react.tsx,
and site/examples/ts/pagination.tsx./Users/zbeyens/git/plate-2:
nl -ba .tmp/slate-v2/packages/slate-react/src/editable/input-router.ts,
runtime-before-input-events.ts, runtime-input-events.ts,
native-input-strategy.ts, dom-repair-queue.ts,
packages/slate-layout/src/index.ts,
packages/slate-layout/src/react.tsx, and
site/examples/ts/pagination.tsx./Users/zbeyens/git/plate-2: targeted rg and
nl -ba reads in ../prosemirror-state, ../prosemirror-view,
../react-prosemirror, ../lexical, ../pretext, and ../tiptap-docs;
comparator evidence is planning-only and does not claim copied code./Users/zbeyens/git/plate-2: targeted rg and nl -ba
reads in .tmp/slate-v2/packages/slate/src/interfaces/editor.ts,
.tmp/slate-v2/packages/slate/src/core/public-state.ts,
.tmp/slate-v2/packages/slate-layout/src/index.ts,
.tmp/slate-v2/packages/slate-layout/src/page-mount-plan.ts,
.tmp/slate-v2/packages/slate-react/src/editable/model-input-strategy.ts,
.tmp/slate-v2/packages/slate-react/src/editable/runtime-kernel-trace.ts,
.tmp/slate-v2/packages/slate-react/src/hooks/use-node-selector.tsx, and
.tmp/slate-v2/packages/slate-react/src/hooks/use-editor-selector.tsx./Users/zbeyens/git/plate-2: read
.tmp/slate-v2/package.json, packages/slate-react/package.json,
packages/slate-layout/package.json, playwright.config.ts,
playwright/integration/examples/pagination.test.ts,
playwright/integration/examples/huge-document.test.ts,
packages/slate-react/test/input-router-contract.test.tsx,
runtime-repair-engine-contract.test.tsx,
dom-strategy-page-virtualization.test.tsx,
render-profiler-contract.test.tsx,
packages/slate-layout/test/page-layout-contract.test.ts, and browser
benchmark scripts for active typing and huge-document traces.Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Slate Plan ready for user review after the final mechanical check. |
| Where am I going? | Accepted-plan execution in .tmp/slate-v2: write the red tests, replace timeout-owned correctness with the internal scheduler, add layout invalidation, and iterate until the browser budgets pass. |
| What is the goal? | Produce a user-review-ready architecture plan for real-time virtualized typing, then let execution mode auto-iterate against measurable browser budgets. |
| What have I learned? | The best long-term decision is an internal DOM-first transaction scheduler plus deterministic flush and commit-dirtiness-driven layout invalidation; a hook-local debounce is the wrong unit, and a merged pending read view is overkill until proof says otherwise. |
| What have I done? | Created the planning goal/plan, completed current-state read, related issue discovery, issue-ledger sync, intent/decision boundary, focused research/live-source refresh, pressure review, verification matrix, objection ledger, and final handoff. |
Open risks:
layout.invalidate primitive if
dirtyTopLevelRanges is not precise enough in practice.