Skip to content

perf: defer off-screen transcript render + fix dropped Ctrl+Down keystroke#584

Merged
SuuBro merged 3 commits into
masterfrom
feat/defer-offscreen-render
May 14, 2026
Merged

perf: defer off-screen transcript render + fix dropped Ctrl+Down keystroke#584
SuuBro merged 3 commits into
masterfrom
feat/defer-offscreen-render

Conversation

@SuuBro
Copy link
Copy Markdown
Owner

@SuuBro SuuBro commented May 14, 2026

Summary

Two surgical, user-facing changes uncovered by a perf investigation (full instrumentation + harness + cross-commit report are parked on `goal/profile-si-320532b0` as a follow-up):

Opt-A — defer off-screen transcript render (perf win)

Sidebar navigation into long sessions was synchronously rendering the entire transcript before first paint, even though only ~3 messages are in viewport after auto-scroll-to-bottom. Now: the last 8 messages render eagerly; older messages render a height-preserving placeholder and swap in via `IntersectionObserver` + `requestIdleCallback` as the user scrolls. Native `Ctrl+F` / `Cmd+F` / `F3` is captured at the keydown level to force-resolve all placeholders before the browser's find UI runs.

Behind a perf flag `deferOffscreenRender` (default-ON). Set `localStorage.bobbitPerfFlags=-deferOffscreenRender` to disable.

Measured impact on a realistic-large transcript fixture (n=5 replicates per arm, every ON sample beats every OFF sample on the headline spans — ranges don't overlap):

Span OFF p95 (med [min–max]) ON p95 (med [min–max]) Δ
`paint.first` 113.5 [111.5–126.7] 76.5 [68.3–80.0] −37 ms / −33%
`nav.session.ready` 330.4 [289.0–383.4] 235.4 [209.9–263.9] −95 ms / −29%
rapid Ctrl+↓ keystroke p95 320.3 [288.2–377.3] 217.9 [212.5–267.6] −102 ms / −32%
rapid Ctrl+↓ inter-keystroke gap p50 109.2 (over snappy) 41.7 (well under) −67 ms

User-facing meaning: rapid Ctrl+↓ walks through the sidebar now feel smooth — the main thread frees up between keystrokes well within the 100 ms snappy budget.

Opt-E — dropped Ctrl+↓ keystroke fix (correctness)

Bug: `getActiveNavId()` in `src/app/sidebar-nav.ts` discarded `state.keyboardNavActiveId` when the URL hadn't yet caught up to the override. During a ~200 ms session attach, 3–4 of 6 rapid Ctrl+↓ presses were re-routed to the same row.

Fix: trust the override unconditionally — the existing hashchange listener already clears it on stale URLs. Single-site change, pinned by `tests/rapid-keystroke-nav.spec.ts`.

Files

11 files, +946 / -7. Strictly the two features + their pinning tests + a minimal perf-flags helper. No server changes, no docs, no instrumentation overhead, no harness.

Verification

  • `npm run check` green.
  • `npm run test:unit` — 1880 pass / 0 fail; new `defer-offscreen-render` 7/7 + `rapid-keystroke-nav` 4/4.
  • `tests/e2e/ui/transcript-fidelity.spec.ts` green for all burst sizes (the existing snapshot-comparison test was updated to force-resolve placeholders before reading DOM — the canonical correctness check for Opt-A).

Follow-up

The branch `goal/profile-si-320532b0` (parked, PR #579) contains the supporting infrastructure that enabled this win: perf-trace primitive, manual harness, cross-commit report, real-session JSONL profile, `HOW-TO-REPEAT.md`. Worth landing later as a separate PR for future perf work; not in scope here.

🤖 Generated with Bobbit

…trl+↓ keystroke

Two user-facing perf wins from goal/profile-si-320532b0, cherry-picked
clean onto master without the Phase 1 instrumentation harness.

Opt-A — defer off-screen transcript render (default-ON behind perf flag)
========================================================================
`<message-list>` now wraps each off-screen transcript item in a
`<deferred-block>` placeholder. The bottom 8 items render eagerly (the
user is scroll-to-bottom on session activation, so the tail is what
first paint shows). Older items render a cheap height-preserving div;
an IntersectionObserver (rootMargin 500px) resolves them on approach
via requestIdleCallback. A Ctrl+F / Cmd+F / F3 escape hatch
force-resolves every placeholder so native browser-find sees the full
transcript.

A/B on the realistic-large fixture (n=5):
  paint.first p95          -37ms (-33%)
  nav.session.ready p95    -95ms (-29%)
  rapidnav.keystroke p95  -102ms (-32%)
  rapidnav.gap p50         -67ms

Disable via `localStorage.bobbitPerfFlags=-deferOffscreenRender`.

Opt-E — fix dropped Ctrl+↓ keystroke during slow attach
========================================================
`getActiveNavId` previously gated the keyboard-nav override on
`window.location.hash === expected`. Session navigation is async
(dynamic import + connectToSession), so a rapid second keystroke
would fire before the first nav's setHashRoute had committed, and
re-derive active id from the stale URL — looping back to the same
row and dropping the keystroke. The override is now trusted
unconditionally; the existing hashchange listener clears it on
stale URLs.

Co-authored-by: bobbit-ai <bobbit@bobbit.ai>
@SuuBro SuuBro force-pushed the feat/defer-offscreen-render branch from 10761f5 to a9764e2 Compare May 14, 2026 11:15
@SuuBro SuuBro merged commit 1c2af77 into master May 14, 2026
@SuuBro SuuBro deleted the feat/defer-offscreen-render branch May 21, 2026 13:46
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.

1 participant