Skip to content

perf(app): keep hidden sidebar rows windowed on resize - #2000

Closed
ymichael wants to merge 1 commit into
mainfrom
bb/pr-2-responsive-sidebar-windowing-thr_85dv9qb78t
Closed

perf(app): keep hidden sidebar rows windowed on resize#2000
ymichael wants to merge 1 commit into
mainfrom
bb/pr-2-responsive-sidebar-windowing-thr_85dv9qb78t

Conversation

@ymichael

@ymichael ymichael commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

What was wrong

During compact drawer breakpoint/orientation transitions, SidebarWindowedItems treated a not-yet-attached ref or a connected scrollport reporting transient zero-height geometry as an unusable viewport and synchronously promoted every placeholder. The pre-paint geometry pass also considered only vertical overlap, so a closed compact drawer translated offscreen could promote all of its rows. IntersectionObserver pruned them again after layout, but WebKit had already rendered and laid out the hidden thread tree. This is a follow-up to #1261.

What changed

  • Keep placeholders while a new scrollport ref attaches and while a connected scrollport is transiently zero-height; detached zero-height previews and environments without IntersectionObserver still render every item.
  • Skip pre-paint promotion when the scrollport is horizontally offscreen or inside a closed sidebar panel.
  • Continue observing connected zero-height scrollports so reopening can promote visible rows once geometry settles.
  • Preserve already-realized and alwaysMountedKeys rows while preventing newly overlapping hidden rows from being promoted.
  • Add focused regressions for null refs, connected and detached zero-height geometry, closed-drawer overlap, retained/always-mounted rows, and visible realization after reopening.

There are no wire, host-daemon protocol, CLI, SDK, or configuration changes; HOST_DAEMON_PROTOCOL_VERSION is unchanged.

How you verified

The new null-ref, connected-zero-height, and closed-drawer tests fail against the baseline implementation and pass with this change.

  • pnpm exec turbo run test --filter=@bb/app -- --run src/components/sidebar/SidebarWindowedItems.test.tsx — 7/7 passed.
  • pnpm exec turbo run test --filter=@bb/app --force — 398 files passed; 3,046 tests passed and 3 skipped.
  • pnpm exec turbo run typecheck --filter=@bb/app — passed.
  • Manual iOS Simulator Safari smoke check confirmed the ordinary wide sidebar still renders and navigates normally.

iOS Simulator Safari profile

I profiled both variants from the same fetched origin/main baseline (2cf68c69b, the tip when capture began), using the same seeded database and scenario. The branch was subsequently rebased through current origin/main (4bf34b590); those later upstream commits do not touch either sidebar-windowing file. The focused tests and app typecheck passed on the same patch after the preceding rebase, and the final rebase was conflict-free; the full app suite and wide-sidebar smoke check had already passed before these upstream-only rebases.

Configuration: iPhone 16 Pro Simulator, iOS 18.4 Safari, Xcode 26.6 on macOS 26.5.1, Vite development build. Fixture: pnpm seed:perf -- --reset --projects 12 --threads 1200 --events 400000 --seed 4242, producing 12 projects, 1,200 threads, 402,148 events, and 145 windowed sidebar groups. Each variant ran 7 identical landscape cycles: compact closed -> open -> close -> 900px wide breakpoint -> 393px compact breakpoint -> reopen -> close, with two animation frames plus 280 ms settling after each action.

Peak realized rows were sampled with a subtree MutationObserver. Main-thread stalls were sampled as requestAnimationFrame gaps over 50 ms, and “blocking” is the accumulated time beyond 50 ms. Style/layout timing is the sum of six forced getComputedStyle/getBoundingClientRect/scrollHeight checkpoints per cycle. Values are median (range), in milliseconds except row counts.

Metric Baseline Patched
Peak realized windowed DOM groups 145 (145–145) 2 (2–2)
Settled realized groups while compact drawer closed 0 (0–0) 0 (0–0)
Realized groups in ordinary wide sidebar 2 (2–2) 2 (2–2)
Longest main-thread frame gap 310 (296–422) 148 (139–167)
Total frame-blocking time beyond 50 ms 456 (434–573) 187 (170–235)
Forced style/layout checkpoints 15 (8–31) 19 (11–24)

The row peak dropped 98.6%, the median worst frame gap dropped 52.3%, and median excess frame-blocking time dropped 59.0%. The small forced style/layout sample was noisy and did not improve; the material gain came from avoiding the transient React realization and its associated long main-thread stall. The remaining profiling caveats are development-build overhead and Simulator rather than physical-device hardware.

Fixes #1261

AGENT GENERATED: by GPT-5

@ymichael
ymichael force-pushed the bb/pr-2-responsive-sidebar-windowing-thr_85dv9qb78t branch 2 times, most recently from 941bb51 to 7f4678a Compare August 20, 2026 18:08
@ymichael
ymichael force-pushed the bb/pr-2-responsive-sidebar-windowing-thr_85dv9qb78t branch from 7f4678a to 0f20a6e Compare August 20, 2026 18:10
@ymichael ymichael closed this Aug 20, 2026
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.

Large active thread lists keep all sidebar rows mounted

1 participant