perf: defer off-screen transcript render + fix dropped Ctrl+Down keystroke#584
Merged
Conversation
…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>
…ferOffscreenRender docs Co-authored-by: bobbit-ai <bobbit@bobbit.ai>
10761f5 to
a9764e2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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):
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
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