perf(gui): virtualize long status contact lists
Status builds a button, subtitle and avatar ring for every author on each repaint. Virtualize lists that exceed one viewport for unfocused pointer/touch use without active accessibility, cache their geometry, and keep the existing eager layout for small lists. Keyboard modality, focus inside the list, and active accessibility retain eager rendering so Tab and assistive technology can reach offscreen contacts. Mouse movement cannot unmount a focused row. My status and Recent stay in one scrolling region with the existing scroll handle. Only components/status/list.rs changes.
Paired synthetic GPUI draws on the same Linux runner, unfocused pointer modality with accessibility inactive, 320×600 viewport, three alternating base/head process pairs per size, seven samples of 20 warmed frames per process. Base e6e3020 plus the same test-only probe and diagnostic row selectors; measured production revision 9c119c2. The current head 8d61230 only extends regression coverage; production code is identical. Both use release opt-level 3, LTO disabled, 16 codegen units. No production dependencies added.
| Contacts | Base median/frame | Head median/frame | Contact elements/frame |
|---|---|---|---|
| 1 | 0.175 ms | 0.184 ms | 1 → 1 |
| 10 | 0.751 ms | 0.511 ms | 10 → 7 |
| 100 | 6.499 ms | 0.530 ms | 100 → 7 |
| 1,000 | 109.583 ms | 0.635 ms | 1,000 → 7 |
At 1,000 contacts, median test-process peak RSS was 94.1 → 22.2 MiB and total test-process CPU was 15.759 → 0.102 s. RSS and process CPU include fixture/startup. These do not measure live heap or the complete connected application. Feed snapshots still clone author data. Web timing is unmeasured; keyboard, focused-list and accessibility rendering deliberately preserve the old cost to retain navigation. The single-contact test median is about 8.5 µs higher; sample ranges overlap and no small-list speedup is claimed.
Rejected: always-virtual rendering added about 20 µs/frame for one contact and reduced Tab reachability from 100 contacts to 7. The small-list and keyboard fallbacks remove those regressions. The virtual empty My status placeholder now has bounded height and single-line ellipsis, preventing overlap at narrow widths and large fonts; the small-list eager placeholder retains its wrapping behavior. Long eager and virtual lists share bounded header geometry to prevent row-position jumps across modalities.
Validation: 593 GUI tests pass, 8 ignored; 9 focused Status tests also pass in release; GUI all-targets/all-features Clippy and workspace formatting pass. Tests cover visible-row bounds, scrolling to the final contact, geometry invalidation, expiry into empty state, 100-contact Tab reachability, and actual placeholder bounds at 120 px width/24 px font in both densities. The earlier placeholder-height thread is resolved. Codex completed final test-only head 8d61230 with no findings. Both review threads are resolved. CI/web/platform checks are pending. Additional tests cover focus and Tab order across modality changes, unchanged selection/scroll, resize with 24 px Compact scaling, grouped and own updates, and exact contact positions across modes. A real left-button press/release also verifies author activation, absent pointer focus and continued virtualization; the pinned Button already prevents default focus, so no duplicate handler was added. Active accessibility uses GPUI's public is_a11y_active guard; an actual screen-reader session was not exercised here.
Reproduce the retained pointer probe with STATUS_AUTHORS=1000 CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 cargo test -p oxidezap-gui --release status_visible_cost -- --ignored --nocapture.
Summary by CodeRabbit
- New Features
- Long status lists now load and scroll more efficiently, while keeping keyboard navigation and accessibility support available. Lists that fit in the visible area continue to display in full.
- The scrolling area includes an overlaid scrollbar, and empty “My status” placeholders use a consistent height with text that fits within the row.