docs/plans/2026-05-27-slate-v2-pagination-virtualization-perf.md
Objective:
Optimize Slate v2 pagination virtualization so /examples/pagination remains
responsive at large page and split-table scale, with browser trace evidence
showing bounded mounted DOM/page surfaces and materially improved typing and
selection latency for the 1000-row stress case.
Goal plan: docs/plans/2026-05-27-slate-v2-pagination-virtualization-perf.md
Template: docs/plans/templates/major-task.md
Primary template: docs/plans/templates/major-task.md
Applied packs:
Major source:
http://localhost:3100/examples/paginationMajor lane:
/Users/zbeyens/git/plate-2/.tmp/slate-v2/packages/slate-layout,
/Users/zbeyens/git/plate-2/.tmp/slate-v2/packages/slate-react,
/Users/zbeyens/git/plate-2/.tmp/slate-v2/site/examples/ts/pagination.tsx,
/Users/zbeyens/git/plate-2/.tmp/slate-v2/playwright/integration/examples/pagination.test.ts,
this plan.Completion threshold:
/examples/pagination exposes a stress path capable of producing a
near-1000-page pagination document.node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-slate-v2-pagination-virtualization-perf.md
passes.Verification surface:
PagedEditable, useVirtualizedRootPlan, and pagination
table rendering ownership.http://localhost:3100/examples/pagination./Users/zbeyens/git/plate-2/.tmp/slate-v2:
bun --filter slate-layout test, bun --filter slate-react test:vitest
or focused equivalent, bun typecheck:site, focused pagination Playwright,
and bun check when the slice is stable.Constraints:
node tooling/scripts/completion-check.mjs.Boundaries:
ok go plus the browser trace
already collected for /examples/pagination.http://localhost:3100/examples/pagination.Blocked condition:
Major state:
Current verdict:
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 completion threshold
above is satisfied, final evidence is recorded, and
node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-slate-v2-pagination-virtualization-perf.md
passes.Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
major-task loaded | yes | .agents/skills/major-task/SKILL.md read before implementation. |
| Active goal checked or created | yes | get_goal returned null; create_goal created active goal 019e6aa0-8ace-7e73-b0e9-166d6fbc4a30. |
| Source of truth read before analysis | yes | User asked to execute the traced pagination perf plan; prior trace was collected against the live route before implementation. |
| Major lane selected | yes | Benchmark / performance plus package architecture. |
| Decision criteria stated | yes | Criteria recorded in Major source and Completion threshold. |
| Existing repo patterns / prior decisions checked | partial | Read PagedEditable, useVirtualizedRootPlan, pagination example/test, and huge-document virtualized example/test. |
| Helper stack selected | yes | autogoal, major-task, task, prior debug/performance trace. |
| External research decision recorded | yes | N/A: local repo evidence is sufficient for this runtime bottleneck. |
| Implementation expectation recorded | yes | Implementation is in scope. |
| Workspace authority selected | yes | Code and proof live under /Users/zbeyens/git/plate-2/.tmp/slate-v2; plan lives in /Users/zbeyens/git/plate-2. |
| Branch / PR expectation decided | yes | No commit or PR requested. |
| Browser pack selected | yes | Browser proof is required because the bottleneck is route/runtime behavior. |
| Browser route / app surface identified | yes | http://localhost:3100/examples/pagination. |
| Browser tool decision recorded | yes | Browser plugin tool is unavailable in callable tools; use Playwright against the live local route as fallback. |
| Console/network caveat policy recorded | yes | Browser proof must capture page errors/console failures when running final traces. |
| Package/API pack selected | yes | Runtime packages under .tmp/slate-v2/packages/* are touched. |
| Public surface or package boundary identified | yes | Internal runtime/layout behavior; avoid public API unless needed. |
| Release artifact path selected | yes | Added .changeset/paged-editable-page-window.md and .changeset/virtualized-paginated-editing.md in .tmp/slate-v2. |
changeset skill loaded when .changeset is required | yes | Read .agents/skills/changeset/SKILL.md; used one package per file, patch bumps. |
| Barrel/export impact decision recorded | yes | No package barrel/export layout changed; pnpm brl N/A. |
Work Checklist:
.changeset, registry changelog, or explicit no-artifact reason..changeset work loads changeset and follows its package/version/prose rules.docs/components/changelog.mdx instead of adding a package changeset.main.Completion Gates:
| Gate | Applies | Required action | Evidence |
|---|---|---|---|
| Named verification threshold | complete | Run the repo audit, benchmark, review, prototype, or artifact check named in this plan | Browser trace and focused tests recorded below. |
| Current-state source audit | complete | Map current owner, boundaries, constraints, and affected surfaces | PagedEditable, useVirtualizedRootPlan, DOM repair, node-ref maps, and pagination example audited. |
| Decision criteria closure | complete | Mark each criterion satisfied, narrowed, rejected, or blocked with evidence | All criteria satisfied; evidence below. |
| Options / tradeoffs / rejection record | complete | Record viable options, chosen recommendation, and why alternatives lose | See Decisions and tradeoffs. |
| Review / pressure pass | complete | Run selected reviewer/lens or record N/A with reason | Self-review focused on native selection correctness, stale DOM path maps, and release artifact classification. |
| Review findings closure | complete | Fix or explicitly reject accepted/actionable findings and record closure proof | Fixed row-500 repeated typing selection jump; added regression proof. |
| External-source audit | complete | Cite official/local clone/external sources when used, or record N/A | N/A: no external sources used; local source and browser traces settled the issue. |
| Implementation gates | complete | If code changed, close primary-template and touched-surface gates; otherwise N/A | Package, browser, changeset, lint, and typecheck gates closed. |
| Final handoff contract | complete | Record recommendation, evidence, caveats, residual risk, and next owner | See Final handoff contract. |
| Final lint | complete | Run pnpm lint:fix or scoped equivalent when files changed | pnpm lint:fix from .tmp/slate-v2: no fixes on final run. |
| Goal plan complete | yes | Run node .agents/rules/autogoal/scripts/check-complete.mjs docs/plans/2026-05-27-slate-v2-pagination-virtualization-perf.md | Ready to run after this update. |
| Browser interaction proof | complete | Exercise the target route/interaction with the approved browser tool or record blocker | Playwright against http://localhost:3100/examples/pagination, 14 Chromium tests passed. |
| Browser console/network check | complete | Record console/network state or why it is not applicable | Console observed during trace; only React DevTools/HMR and NO_COLOR warnings, no route runtime errors. |
| Browser final proof artifact | complete | Record screenshot/trace/route proof or exact caveat | Exact DOM/latency trace recorded in Verification evidence; no screenshot needed for perf proof. |
| Public API / package boundary proof | complete | Source-audit public API, exports, and package boundary impact | Runtime behavior changed; no exported file/barrel layout changed. |
| Release artifact classification | complete | Record whether the change is published package behavior/API/types/config/runtime, registry-only, or no published user-visible delta | Published package runtime behavior for slate-layout and slate-react. |
| Published package changeset | complete | If published package users see a delta, load changeset, add/update one .changeset/*.md per package, and prove no forbidden minor on @platejs/slate, @platejs/core, or platejs | Added two patch changesets; no forbidden core package minor. |
| Registry changelog | complete | If the change is registry-only under apps/www/src/registry/**, update docs/components/changelog.mdx and do not add a package changeset | N/A: not registry-only work. |
| No release artifact | complete | If no artifact is needed, record the exact reason: internal-only, docs-only, agent-only, test-only, or no user-visible delta from main | N/A: release artifacts added. |
| Package typecheck/build/test | complete | Run owning package checks or record N/A with reason | slate-layout tests, slate-react focused vitest, package typecheck, site typecheck passed. |
| Barrel/export generation | complete | Run pnpm brl when exports or exported file layout changed, otherwise N/A | N/A: no exports or exported file layout changed. |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | User request, skills, templates, and route trace read. | current-state map |
| Current-state map | complete | Trace and source audit recorded in Findings. | implementation |
| Options and recommendation | complete | Targeted page surface plus split-node windowing chosen. | review |
| Review / pressure pass | complete | Repeated typing exposed a selection/scroll regression; fixed before closeout. | verification |
| Implementation or plan artifact | complete | Package/runtime/example/test/changeset updates complete. | verification |
| Verification | complete | Focused package checks and pagination Chromium suite passed. | closeout |
| Closeout | complete | Final evidence recorded. | final response |
Findings:
huge-document virtualized with 10k blocks: 270 DOM nodes, 8
mounted blocks, median typing 8.3ms.pagination virtualized top-of-doc with 1000 rows x 120px:
888 DOM nodes, table unmounted, intro typing median 66ms, 148 page surfaces
still mounted.PagedEditable creates virtualized page items for Editable,
but still flat-maps every page surface itself.Decisions and tradeoffs:
Implementation notes:
slate-layout: PagedEditable now virtualizes rendered page surfaces when
domStrategy is virtualized and exposes fragment/unit paths on page mount
items so split-node selection can resolve to the owning page.slate-react: virtualized root planning maps deep selection paths to page
items before falling back to top-level ownership, and top-level fallback uses
page item lookup in page-virtualized mode.slate-react: text-input caret repair does not scroll stale DOM selection
inside page-virtualized surfaces, and path-element lookup rejects stale map
entries whose data-slate-path no longer matches the requested path.site/examples/ts/pagination.tsx: the split table renders only row ranges
visible in current page fragments and uses contentBoundary placeholders for
hidden child ranges. Row height stress now reaches near-1000-page documents.playwright/integration/examples/pagination.test.ts: added Chromium proof for
a 1000-row / ~1000-page split table, bounded mounted rows/cells/page surfaces,
and repeated typing in row 500..tmp/slate-v2/.changeset/paged-editable-page-window.md and
.tmp/slate-v2/.changeset/virtualized-paginated-editing.md.Review fixes:
x, y, z all stay at [47,499,1,0].Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
Static Playwright server build hit pre-existing keyboard-input-strategy.ts Next build type error | 1 | Use fresh/live dev server proof plus source-first typechecks for touched packages | Valid pagination proof ran against PLAYWRIGHT_BASE_URL=http://localhost:3100; package/site typechecks passed. |
Accidental bun playwright test ... invoked the script with an extra test positional and began the broader suite | 1 | Use ./node_modules/.bin/playwright directly | Killed the accidental run; ignored unrelated editable-void failure from that wrong command. |
Verification evidence:
/examples/pagination virtualized top-of-doc still mounted 148 page
surfaces./Users/zbeyens/git/plate-2/.tmp/slate-v2,
PLAYWRIGHT_BASE_URL=http://localhost:3100, route /examples/pagination:
x/y/z:
76.2ms / 66.5ms / 74.5ms. Selection stayed [47,499,1,0].x/y/z:
85.5ms / 70.8ms / 78.0ms. Selection stayed [47,499,1,0]./Users/zbeyens/git/plate-2/.tmp/slate-v2:
bun --filter slate-react test:vitest -- test/dom-strategy-page-virtualization.test.tsx test/slate-element-node-ref.test.tsx: pass, 2 files / 6 tests.bun --filter slate-layout test: pass, 35 tests.pnpm turbo typecheck --filter=./packages/slate-layout --filter=./packages/slate-react: pass, 2 packages.bun typecheck:site: pass.pnpm lint:fix: pass, no fixes on final run.PLAYWRIGHT_BASE_URL=http://localhost:3100 ./node_modules/.bin/playwright test playwright/integration/examples/pagination.test.ts --project=chromium: pass, 14 tests.Final handoff contract:
http://localhost:3100/examples/pagination.Timeline:
Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Closeout complete |
| Where am I going? | Final response |
| What is the goal? | Make paginated virtualized rendering fast and bounded for 1000-row / near-1000-page stress cases without breaking editing. |
| What have I learned? | Top-level virtualization alone fails split nodes; page/split unit metadata plus scoped DOM repair is required. |
| What have I done? | See Implementation notes, Verification evidence, and Timeline. |
Open risks:
PLAYWRIGHT_BASE_URL is still blocked by an
existing keyboard-input-strategy.ts Next build type error outside this
pagination diff. Source-first package/site typechecks passed, and browser
proof used the live dev server.