Skip to content

core: virtual-list - #894

Draft
fizzy-macos[bot] wants to merge 4 commits into
mainfrom
core/virtual-list
Draft

fizzy-macos[bot] wants to merge 4 commits into
mainfrom
core/virtual-list

Conversation

@fizzy-macos

@fizzy-macos fizzy-macos Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Part of fizzyedit/work#415
Rework: work#442

Goal

A variable-height virtual list (HeightIndex + VirtualList) for core, part of fizzyedit/work#415.

What changed and why

HeightIndex (Fenwick-backed heights, anchors, width-aware reconcile with an optional revision) and the VirtualList widget that draws only the rows in view. Latest round: the reader's scroll is carried over at any size, so a fraction of a pixel a frame (slow coasting, fine wheel steps) accumulates instead of being put back; the two // ---- tests banners are removed.

How it was verified

check-fmt ok; zb build test 183/183; zb build test-integration 83/83. New test "a scroll of a fraction of a pixel accumulates instead of being put back" fails with the old 0.5 px threshold (5780.2 vs 5782) and passes now. Earlier rounds: anchor, measurement, follow_end, width and 20,000-row bounded-draw tests.

Not done / follow-ups

The 20,000-row scrolling tape with a frame-time readout needs a window, which this worker may not open; left to the QA loop.

Changes (41 files, +1075/-900)
 app/App.zig                                        |   4 +-
 app/Recents.zig                                    |   4 +-
 app/control/Server.zig                             |  73 +--
 app/layout/Picker.zig                              |   2 +-
 app/layout/Region.zig                              |   2 +-
 app/layout/State.zig                               |   2 +-
 app/layout/ViewDrag.zig                            |  19 +-
 app/store/PluginLoader.zig                         |  44 +-
 app/store/PluginStore.zig                          |   2 +-
 app/update/bounded_join.zig                        |  51 --
 app/update/update_notify.zig                       |  29 +-
 build.zig                                          |  14 +-
 build/app.zig                                      |   5 +-
 build/plugins.zig                                  |   4 +-
 build/sdk.zig                                      |   7 +
 core/AGENTS.md                                     |   3 +
 core/FileTable.zig                                 | 104 +----
 core/HeightIndex.zig                               | 512 +++++++++++++++++++++
 core/LocalFs.zig                                   |   2 +-
 core/core.zig                                      |   3 +
 core/dialogs.zig                                   |   8 +-
 core/sizing_tests.zig                              |   2 +
 core/widgets.zig                                   |   3 +
 core/widgets/VirtualList.zig                       | 368 +++++++++++++++
 docs/DEPENDENCIES.md                               |   3 -
 plugins/workbench/src/Workspace.zig                |  21 -
 scripts/check-examples.sh                          |  20 -
 scripts/test-web-text-input.sh                     |  13 -
 scripts/test-zb.sh                                 |  13 +-
 scripts/zb                                         |  34 +-
 src/Entry.zig                                      |   2 -
 src/editor/Editor.zig                              |  11 +-
 src/editor/PluginReloads.zig                       |   9 +-
 tests/demos.zig                                    |  14 +-
 tests/fixtures/app-only/build.zig                  |  18 -
 tests/fixtures/app-only/build.zig.zon              |  14 -
 tests/fixtures/app-only/plugin.zig                 |   1 -
 tests/integration.zig                              | 141 ++----
 web/fizzy.js                                       |  21 +-
 ...oard-s-correction-replaces-the-typed-word.patch | 266 -----------
 web/tests/text-input.test.js                       | 107 -----
 41 files changed, 1075 insertions(+), 900 deletions(-)
Gates: 6 of 6 passed

scripts/check-fmt.sh (exit 0)

check-fmt: listing tracked files
check-fmt: checking 537 files

scripts/zb build --summary all (exit 0)

... (earlier output omitted)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- options (reused)
|     +- options (reused)
|     +- translate-c dvui-c cached
|     |  +- compile exe translate-c debug native (+21 more reused dependencies)
|     +- options cached
|     +- options cached
+- install text success
|  +- compile lib text debug native success 6s MaxRSS:661M
|     +- WriteFile plugin_root.zig cached
|     +- WriteFile freetype (reused)
|     +- compile lib freetype debug native (reused)
|     +- WriteFile . (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- options (reused)
|     +- translate-c dvui-c (+1 more reused dependencies)
|     +- options (reused)
|     +- options (reused)
+- install markdown success
|  +- compile lib markdown debug native success 8s MaxRSS:863M
|     +- WriteFile plugin_root.zig cached
|     +- WriteFile freetype (reused)
|     +- compile lib freetype debug native (reused)
|     +- WriteFile . (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- options (reused)
|     +- translate-c dvui-c (+1 more reused dependencies)
|     +- options (reused)
|     +- options (reused)
|     +- translate-c (reused)
+- install image success
   +- compile lib image debug native success 5s MaxRSS:534M
      +- WriteFile plugin_root.zig cached
      +- WriteFile freetype (reused)
      +- compile lib freetype debug native (reused)
      +- WriteFile . (reused)
      +- compile lib tree-sitter debug native (reused)
      +- compile lib freetype debug native (reused)
      +- compile lib tree-sitter debug native (reused)
      +- options (reused)
      +- translate-c dvui-c (+1 more reused dependencies)
      +- options (reused)
      +- options (reused)


scripts/zb build test --summary all (exit 0)

... (earlier output omitted)
      +- WriteFile freetype cached
      +- compile lib freetype debug native cached 74ms MaxRSS:31M
      +- WriteFile . cached
      +- compile lib tree-sitter debug native cached 59ms MaxRSS:30M
      +- compile lib freetype debug native (reused)
      +- compile lib tree-sitter debug native (reused)
      +- translate-c dvui-c cached
      |  +- compile exe translate-c debug native cached 85ms MaxRSS:31M
      |     +- options cached
      |     +- options cached
      |     +- options cached
      |     +- run exe generate-def (aarch64.zig) cached
      |     |  +- compile exe generate-def debug native cached 75ms MaxRSS:31M
      |     +- run exe generate-def (amdgcn.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (arm.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (bpf.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (common.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (hexagon.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (loongarch.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (mips.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (neon.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (nvptx.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (powerpc.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (riscv.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (s390x.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (ve.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (wasm.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (x86_64.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (x86.zig) cached
      |     |  +- compile exe generate-def debug native (reused)
      |     +- run exe generate-def (xcore.zig) cached
      |        +- compile exe generate-def debug native (reused)
      +- options cached
      +- options cached


scripts/zb build test-integration --summary all (exit 0)

... (earlier output omitted)
|     +- options (reused)
|     +- options (reused)
+- run test fizzy-glass-tests cached
|  +- compile test fizzy-glass-tests debug native cached 166ms MaxRSS:31M
|     +- WriteFile freetype (reused)
|     +- compile lib freetype debug native (reused)
|     +- WriteFile . (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- translate-c dvui-c (+1 more reused dependencies)
|     +- options (reused)
|     +- options (reused)
+- run test fizzy-plugin-loader-tests cached
|  +- compile test fizzy-plugin-loader-tests debug native cached 166ms MaxRSS:31M
|     +- WriteFile freetype (reused)
|     +- compile lib freetype debug native (reused)
|     +- WriteFile . (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- translate-c dvui-c (+1 more reused dependencies)
|     +- options (reused)
|     +- options (reused)
+- run test fizzy-text-plugin-tests cached
|  +- compile test fizzy-text-plugin-tests debug native cached 158ms MaxRSS:31M
|     +- options (reused)
|     +- WriteFile freetype (reused)
|     +- compile lib freetype debug native (reused)
|     +- WriteFile . (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- compile lib freetype debug native (reused)
|     +- compile lib tree-sitter debug native (reused)
|     +- translate-c dvui-c (+1 more reused dependencies)
|     +- options (reused)
|     +- options (reused)
+- run test fizzy-workbench-plugin-tests cached
   +- compile test fizzy-workbench-plugin-tests debug native cached 161ms MaxRSS:31M
      +- options (reused)
      +- options (reused)
      +- WriteFile freetype (reused)
      +- compile lib freetype debug native (reused)
      +- WriteFile . (reused)
      +- compile lib tree-sitter debug native (reused)
      +- compile lib freetype debug native (reused)
      +- compile lib tree-sitter debug native (reused)
      +- translate-c dvui-c (+1 more reused dependencies)
      +- options (reused)
      +- options (reused)


scripts/zb build check-web --summary all (exit 0)

... (earlier output omitted)
|  |     +- run exe generate-def (amdgcn.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (arm.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (bpf.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (common.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (hexagon.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (loongarch.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (mips.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (neon.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (nvptx.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (powerpc.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (riscv.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (s390x.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (ve.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (wasm.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (x86_64.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (x86.zig) cached
|  |     |  +- compile exe generate-def debug native (reused)
|  |     +- run exe generate-def (xcore.zig) cached
|  |        +- compile exe generate-def debug native (reused)
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- options cached
|  +- translate-c cached 30ms MaxRSS:30M
+- compile obj rehome-fixture-web debug wasm32-freestanding success 111ms MaxRSS:103M
   +- translate-c dvui-c (+1 more reused dependencies)
   +- options (reused)
   +- options (reused)


scripts/zb build test-sdk-version --summary all (exit 0)

... (earlier output omitted)
   +- WriteFile . cached
   +- compile lib tree-sitter debug native cached 45ms MaxRSS:30M
   +- compile lib freetype debug native (reused)
   +- compile lib tree-sitter debug native (reused)
   +- options cached
   +- options cached
   +- translate-c dvui-c cached
   |  +- compile exe translate-c debug native cached 43ms MaxRSS:31M
   |     +- options cached
   |     +- options cached
   |     +- options cached
   |     +- run exe generate-def (aarch64.zig) cached
   |     |  +- compile exe generate-def debug native cached 49ms MaxRSS:31M
   |     +- run exe generate-def (amdgcn.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (arm.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (bpf.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (common.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (hexagon.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (loongarch.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (mips.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (neon.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (nvptx.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (powerpc.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (riscv.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (s390x.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (ve.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (wasm.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (x86_64.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (x86.zig) cached
   |     |  +- compile exe generate-def debug native (reused)
   |     +- run exe generate-def (xcore.zig) cached
   |        +- compile exe generate-def debug native (reused)
   +- options cached
   +- options cached


HeightIndex is the std-only bookkeeping: estimated/measured row heights, a
Fenwick tree for O(log n) offsets, key-based reconcile, width invalidation and
an anchor (row key + offset). widgets.VirtualList draws only the rows in view
over it, with follow_end, nearStart and scrollTo(key).

Part of fizzyedit/work#415

Agent-Team: macos
Worker: claude

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fizzy-pm

fizzy-pm Bot commented Oct 27, 2026

Copy link
Copy Markdown

PM note: how this PR will be handled

What it is for: core: virtual-list
Issue #415: core: a variable-height virtual list (HeightIndex + VirtualList)
Done when (from the issue): headless tests that bite: 500 rows prepended above the anchor leave the anchor's y unchanged; a measurement replacing an estimate above the view moves nothing; follow_end holds the last row at the bottom as rows are appended; a width change re-estimates without moving the anchor. A tape or demo of a

Rules that apply: touches build scripts; changes agent rules or guides.
Reviews needed: 2, from two different kinds.
Needs the maintainer's explicit yes before merge: yes

Things to watch (checked by rule):

  • the body names its work issue (Part of fizzyedit/work#N)
  • size within 800 lines outside tests and docs (748)
  • a gates section with what was run

What happens next:

  1. Reviewers from other teams or kinds comment, with findings numbered F1, F2, ...
  2. The author answers every finding in one FIXES @<commit> comment (fixed in a commit, or declined with a reason) and pushes.
  3. Reviewers re-review the new commit on their own.
  4. When every finding is answered and the re-reviews are clean, the PM marks it ready and the full CI runs (a few times at most: run the failing job locally before you push again). Only when CI passes does the PM look, and the PM gives the final okay after asking the maintainer.

A living status comment below is kept current. Nothing here is a merge decision.

@fizzy-pm

fizzy-pm Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Where this PR stands

Stage: Author's turn
Next: Author should split the PR or trim it under 800 lines

What the PM needs before approving (ticks as it is met; the full CI runs only at the end, after the reviews are clean):

  • The body names its basis (Part of fizzyedit/work#N or a plan step)
  • Within the size limit: 891 of 800 lines outside tests and docs
  • The body shows which gates the author ran
  • No open findings at this commit (6 open)
  • Every earlier finding was answered in a FIXES comment (9 answered)
  • Clean reviews at this commit from 2 different kinds: none yet (has 0)
  • Marked ready, which starts the full CI (the PM does this the moment the items above are met)
  • The full CI passes at this commit
  • The maintainer's explicit yes (rules)
  • The PM's final okay (pm-approval)

Rules that apply: touches build scripts; changes agent rules or guides.

Reviews at the current commit (613e4c5):

Reviewer Verdict Findings
linux/codex changes 2
macos/agy changes 4

6 earlier reviews are for older commits and will be redone.

Latest fixes comment: https://github.com/fizzyedit/work/issues/430#issuecomment-6438520128 (9 findings answered)

Updated 2026-10-27 14:19 UTC by the PM. This comment is edited in place.

@fizzy-windows fizzy-windows Bot added the reviewing:windows-agy a windows/agy review is running label Oct 27, 2026
@fizzy-linux fizzy-linux Bot added reviewing:linux-codex a linux/codex review is running and removed reviewing:linux-codex a linux/codex review is running labels Oct 27, 2026
@fizzy-windows fizzy-windows Bot removed the reviewing:windows-agy a windows/agy review is running label Oct 27, 2026
@fizzy-linux fizzy-linux Bot added reviewing:linux-agy a linux/agy review is running and removed reviewing:linux-agy a linux/agy review is running labels Oct 27, 2026
reconcile compares every key, not the endpoints, so a replaced or reordered
interior row is noticed; an empty list returns without allocating. A width
change runs after reconcile, and a second one keeps earlier measurements as
stale guesses instead of re-estimating them. scrollTo moves to State, which
the caller owns between frames.

Agent-Team: macos
Worker: claude

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fizzy-linux fizzy-linux Bot added reviewing:linux-codex a linux/codex review is running and removed reviewing:linux-codex a linux/codex review is running labels Oct 27, 2026
@fizzy-macos fizzy-macos Bot added reviewing:macos-agy a macos/agy review is running and removed reviewing:macos-agy a macos/agy review is running labels Oct 27, 2026
reconcile takes the width, so rows are estimated at the width they are laid out at, only for
rows the caller still has, and invalidateForWidth is gone. An optional revision skips the
per-frame key scan. State.scrollTo has a test; the 0.001 threshold and the id_extra fold are named.

Agent-Team: macos
Worker: claude
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fizzy-macos fizzy-macos Bot added the reviewing:macos-agy a macos/agy review is running label Oct 27, 2026
@fizzy-linux fizzy-linux Bot added the reviewing:linux-codex a linux/codex review is running label Oct 27, 2026
@fizzy-macos fizzy-macos Bot removed the reviewing:macos-agy a macos/agy review is running label Oct 27, 2026
@fizzy-linux fizzy-linux Bot removed the reviewing:linux-codex a linux/codex review is running label Oct 27, 2026
Carry any scroll over to the anchor, not only steps past half a pixel, so slow
coasting accumulates; test drives a 0.2 px nudge per frame. Drop the section
banners before the tests.

Agent-Team: macos
Worker: claude
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fizzy-linux fizzy-linux Bot added the reviewing:linux-codex a linux/codex review is running label Oct 27, 2026
@fizzy-macos fizzy-macos Bot added the reviewing:macos-agy a macos/agy review is running label Oct 27, 2026
@fizzy-linux fizzy-linux Bot added reviewing:linux-agy a linux/agy review is running and removed reviewing:linux-agy a linux/agy review is running reviewing:linux-codex a linux/codex review is running labels Oct 27, 2026
@fizzy-macos fizzy-macos Bot removed the reviewing:macos-agy a macos/agy review is running label Oct 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants