Skip to content

fix(tui): isolate tab scroll state - #42456

Merged
kitlangton merged 4 commits into
v2from
tab-scroll-state
Aug 14, 2026
Merged

kitlangton merged 4 commits into
v2from
tab-scroll-state

Conversation

@kitlangton

@kitlangton kitlangton commented Aug 14, 2026 •

Copy link
Copy Markdown
Contributor

What

Keep each session tab at its own transcript reading position when the tab_scroll experiment is enabled.

Before / After

Before

  1. Scroll upward in tab A.
  2. Switch to tab B.
  3. Tab A's cleanup could save its position under B because the route had already advanced.
  4. The saved absolute scrollTop also became stale when older transcript rows mounted above the visible tail.
  5. Switching back could show another tab's position or unrelated transcript content.

After

  1. Each mounted session captures its own session ID.
  2. Scrolling records that tab's distance from the transcript bottom while the renderable is live.
  3. Switching back mounts older rows, then restores the same bottom-relative offset.
  4. Tabs retain independent reading positions as transcript history mounts or grows.

How

  • packages/tui/src/routes/session/index.tsx captures the mounted session ID instead of reading the advanced route during cleanup.
  • Scroll state is updated during transcript scrolling, before OpenTUI destroys the scrollbox.
  • Restoration uses distance from the bottom rather than absolute scrollTop, so prepending lazily mounted history does not change the remembered location.
  • packages/tui/src/context/session-tabs.tsx stores per-session bottom offsets and clears them when the experiment is disabled or a tab closes.

Scope

  • Applies only when the existing tab_scroll experiment is enabled.
  • Scroll state remains in-memory for open tabs; this does not persist positions across TUI restarts.

Testing

  • cd packages/tui && bun run test (694 passed, 5 skipped)
  • cd packages/tui && bun typecheck
  • Push hook: workspace typecheck, 35 packages passed
  • bunx prettier --check src/routes/session/index.tsx src/context/session-tabs.tsx
  • Live PTY verification against the elected V2 server: enabled tab_scroll, scrolled one session several pages upward, switched to another tab and back, and confirmed the original transcript region and Latest ↓ state were restored without affecting the other tab.

Performance

Profiled warm tab switches in a real PTY against the elected V2 server using one warmup and repeated alternating switches. Timings ran from tab selection to the post-layout restored transcript.

Scenario Mounted rows Median settle
Bottom-pinned baseline 40 75.7 ms
Previous full restoration 444 286.6 ms
Bounded anchor restoration 75 86.0 ms
Deep historical restoration 114 101.9 ms

Bounded restoration is about 70% faster in the measured scrolled-up case and stays close to the bottom-pinned path. The visible transcript was captured before and after deep restoration and matched exactly apart from the devtools focus marker.

Row reduction averaged about 1.1 ms, so caching reduced rows was rejected as the next optimization: transcript render/layout, not row derivation, dominated this workload. Temporary profiling instrumentation was removed before commit.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant