Skip to content

Cache TUI transcript block heights to fix long-transcript scroll lag#13592

Merged
harryalbert merged 4 commits into
masterfrom
factory/tui-transcript-scroll-height-cache
Jul 11, 2026
Merged

Cache TUI transcript block heights to fix long-transcript scroll lag#13592
harryalbert merged 4 commits into
masterfrom
factory/tui-transcript-scroll-height-cache

Conversation

@warp-dev-github-integration

@warp-dev-github-integration warp-dev-github-integration Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes scrolling/viewport lag in the warp-tui transcript view with long transcripts containing many AI blocks (collapsible thinking items, agent shell commands, etc.).

Root cause (viewporting inefficiency, not scroll handling). On every frame (each scroll tick and idle repaint), TuiBlockListViewportSource::visible_items re-measured every agent block in a viewport±20-row overhang band by fully rebuilding and laying out each block's element tree via TuiAIBlock::desired_height — with no height cache keyed by width. The block list already stored last_laid_out_height, but the viewport threw that cache away for the measurement decision. As transcripts grow (bigger/more thinking + shell blocks), the fixed per-frame band cost balloons and scrolling feels laggy. This mirrors the GUI blocklist prior art, except the GUI re-lays-out retained elements (cheap when unchanged) while the TUI rebuilt immediate-mode trees from scratch every frame.

Fix. Gate the overhang re-measure on staleness. A non-dirty band block is re-measured only when its cached height cannot be trusted:

  • its layout width changed since it was last measured (reflow), or it was never measured at a known width; or
  • it is still streaming (its height can grow without a per-update invalidation — e.g. an expanded, still-running shell command).

At a stable width with nothing streaming, the cached last_laid_out_height is reused, so height write-back short-circuits and the extra model.lock() acquisitions are skipped. Off-band blocks keep their cached height until they scroll into the band, matching the existing GUI overhang behavior.

To persist the width across frames (the viewport source is reconstructed every render), RichContentItem gains a measured_width, recorded by the row-based (TUI) height-update path; the GUI pixel path leaves it None and is unaffected.

Linked Issue

  • The linked issue is labeled ready-to-spec or ready-to-implement.
  • Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or UI changes).

Investigation + approval thread: https://warpdev.slack.com/archives/C0BCE7AELJ2/p1783722795932879?thread_ts=1783722795.932879&cid=C0BCE7AELJ2

Testing

Validation (all with CARGO_BUILD_JOBS=1):

  • cargo nextest run -p warp_tui — 132 passed (incl. new tests below)
  • cargo nextest run -p warpui_core — 310 passed
  • cargo nextest run -p warp block-list / rich-content tests — 85 passed
  • ./script/format --check — clean
  • cargo clippy --workspace --exclude warp_completer --all-targets --tests -- -D warnings — clean

New / updated tests (in crates/warp_tui/src/tui_block_list_viewport_source_tests.rs):

  • tui_transcript_scroll_reuses_cached_heights_at_stable_width (new regression guard): a non-dirty, non-streaming block seeded with a wrong height at a stable width is not re-measured (no resize emitted), but is re-measured on a width change and when marked dirty. This fails before the fix (the old code re-measured every frame and corrected the seeded height) and passes after.
  • tui_agent_streaming_block_remeasured_at_stable_width (new): a streaming block is still re-measured at a stable width, so live height growth (e.g. an expanded running shell command) stays reflected.
  • tui_agent_overhang_remeasures_visible_non_dirty_height updated to the new contract: re-measures on first measure (no recorded width), and a subsequent same-width frame does not re-measure.

Performance evidence: a headless benchmark over 30 agent blocks showed steady-state frames drop from ~1790µs/frame to the ~56µs/frame cached-reuse baseline (~32× less redundant work per frame in a debug build).

UI verification note: the authenticated warp-tui transcript view cannot be driven non-interactively in the cloud runner (interactive device-auth only), so no computer_use screenshot is included. The deterministic regression test plus the headless benchmark are the gates; a human/CI check on a real device is: open a long transcript with many thinking/shell blocks and confirm smooth wheel/drag scrolling with correct heights (including while a command streams with its output expanded).

  • I have manually tested my changes locally with ./script/run

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Conversation: https://staging.warp.dev/conversation/e38f6ef0-8090-4128-b2d9-078b2aa2e539
Run: https://oz.staging.warp.dev/runs/019f4e2e-143c-74ca-92b9-568681f403af

This PR was generated with Oz.

@cla-bot cla-bot Bot added the cla-signed label Jul 10, 2026
@oz-for-oss

oz-for-oss Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@warp-dev-github-integration[bot]

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR adds width-aware caching for TUI rich-content block heights to avoid remeasuring stable transcript agent blocks on every viewport pass.

Concerns

  • ⚠️ Stable-width completed agent blocks can still change height from local TUI interactions such as expanding/collapsing thinking, shell-command, or file-edit sections, but those click handlers notify without marking the rich content dirty. The new cache gate can therefore preserve stale heights after interaction.
  • ⚠️ This is a user-facing TUI behavior/performance change, but the PR description does not include acceptable TUI visual evidence such as a terminal transcript, render_to_lines/TuiBuffer::to_lines snapshot diff, or ./script/run-tui capture demonstrating the transcript behavior end to end. Please attach one before merge.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread crates/warp_tui/src/tui_block_list_viewport_source.rs Outdated
@warp-dev-github-integration

Copy link
Copy Markdown
Contributor Author

Addressed both review concerns in b68fde9.

1. Stale height on collapse/expand (important): toggles of finished thinking/shell/file-edit sections now invalidate the block's cached height (shared layout_invalidator marks rich content dirty), so no stale/clipped heights after interaction. Covered by a new regression test.

2. TUI visual evidence (headless TuiBuffer::to_lines snapshot): the new test renders a finished, non-streaming thinking block at stable width before/after clicking the header:

collapsed_lines = ["Thought for 2 seconds ▸"]
expanded_lines  = ["Thought for 2 seconds ▾",
                   "    reasoning line one",
                   "    reasoning line two",
                   "    reasoning line three"]
content_height:  collapsed = 2  ->  expanded = 5   (re-measured on the next viewport pass after the toggle)

Validation: warp_tui 133 passed, warpui_core 310 passed, ./script/format --check clean, full-workspace clippy -D warnings clean (all CARGO_BUILD_JOBS=1).

Note on the earlier red Windows check: that failure (test_handle_non_pty_read_event_while_batching, shared-session networking, 1/8476, Windows-only, unrelated to this diff) was on the superseded commit — the full suite is re-running on b68fde9.

@warp-dev-github-integration

Copy link
Copy Markdown
Contributor Author

/oz-review

@harryalbert harryalbert force-pushed the factory/tui-transcript-scroll-height-cache branch from b68fde9 to 2325f75 Compare July 11, 2026 04:05

Copy link
Copy Markdown
Contributor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@harryalbert harryalbert enabled auto-merge (squash) July 11, 2026 04:28
@harryalbert harryalbert merged commit a01df38 into master Jul 11, 2026
27 checks passed
@harryalbert harryalbert deleted the factory/tui-transcript-scroll-height-cache branch July 11, 2026 04:40
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.

2 participants