Skip to content

fix(terminal): anchor input prompt to viewport bottom during alt-screen streaming (#9365)#10752

Draft
lonexreb wants to merge 1 commit into
warpdotdev:masterfrom
lonexreb:fix/9365-claude-prompt-mid-output
Draft

fix(terminal): anchor input prompt to viewport bottom during alt-screen streaming (#9365)#10752
lonexreb wants to merge 1 commit into
warpdotdev:masterfrom
lonexreb:fix/9365-claude-prompt-mid-output

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

Closes #9365

Summary

When Claude Code (and other CLI coding agents) stream long output inside Warp's alt-screen mode, Warp's input prompt occasionally renders in the middle of the streaming output rather than at the viewport bottom; lines above the prompt get clipped and remain truncated in the scrollback. This PR adds a synchronous size refresh on the input-visibility transitions that occur during alt-screen, so the PTY winsize and the alt-screen grid converge before the next paint.

Filed as draft because the bug is a streaming/layout race that I could not reproduce in a local Warp build (Metal toolchain not available in this environment — see Manual testing note below). The change is conservative and additive, but live verification against a real Claude Code session is required before promoting out of draft.

Manual testing note

This worktree runs on macOS with Command Line Tools only (no full Xcode toolchain). cargo check -p warp_terminal fails at compiling Metal shaders (xcrun: error: unable to find utility "metal"), which is the project-known constraint for building the desktop app outside a developer machine with Xcode installed. The change is small, file-local, and uses an existing internal pattern (refresh_size), and the new unit test exercises both branches of the helper. A teammate with the Metal toolchain should run the full Claude Code repro from the issue (heavy-streaming refactor + type-check) to confirm the visual symptom is resolved.

Root cause

The user input box (and the rich CLI-agent input panel) can become visible or hidden mid-stream when the agent grabs control, gets tagged in/out, or when the user opens/closes the CLI-agent rich input. In alt-screen mode, the output area is wrapped in TerminalSizeElement which fires resize_tx from after_layout, so the PTY eventually receives SIGWINCH with the new row count.

However, a heavy-streaming alt-screen app such as Claude Code can paint into rows that the alt-screen grid still holds from the previous (larger) layout before processing SIGWINCH. When the next Warp paint occurs, those stale rows appear above Warp's input prompt while new content is drawn below it.

This race is the same one the existing resize_alt_screen_redundantly helper solves for the TerminalModeSwapped (alt-screen entry/exit) path. The same belt-and-suspenders refresh wasn't applied to the four input-visibility-flip paths:

  • tag_agent_inapp/src/terminal/view.rs:7319
  • tag_agent_outapp/src/terminal/view.rs:7339
  • open_cli_agent_rich_inputapp/src/terminal/view/use_agent_footer/mod.rs:1003
  • close_cli_agent_rich_input_implapp/src/terminal/view/use_agent_footer/mod.rs:584

Change

  • New helper TerminalView::refresh_size_if_alt_screen_active runs a Refresh-style SizeUpdate only when model.is_alt_screen_active().
  • Called from the four state-transition sites above, immediately before ctx.notify().
  • TerminalView::refresh_size visibility loosened from private to pub(in crate::terminal) so the sibling view/use_agent_footer/mod.rs impl TerminalView block can invoke it.

Test plan

  • Unit test: refresh_size_if_alt_screen_active_only_refreshes_in_alt_screen covers both branches (blocklist mode = no-op, alt-screen mode = refresh runs and pane size is preserved).
  • Manual repro on a machine with the Metal toolchain: start Claude Code in alt-screen, give it a heavy-streaming task ("refactor this route handler and run type-check"), watch for the input prompt rendering mid-output during streaming. Verify prompt stays anchored to the viewport bottom and no rows are clipped.
  • Manual sanity: open and close the CLI-agent rich input while a long-running shell command is running (no alt-screen); confirm no behavior change.
  • Manual sanity: tag the Warp agent in and out during a long-running command in blocklist mode; confirm no behavior change.

🤖 Generated with Claude Code

…en streaming (warpdotdev#9365)

When Claude Code (and other CLI agents) run inside Warp's alt-screen mode,
the Warp input prompt can render in the middle of the streaming output
rather than at the bottom of the viewport. Lines above the prompt get
visually clipped and remain truncated in the scrollback.

## Root cause

When the input box visibility flips while alt-screen is active, the
rendered alt-screen viewport changes size. The output area is wrapped in
`TerminalSizeElement` which fires `resize_tx` asynchronously from
`after_layout`, so the PTY eventually receives SIGWINCH with the new
row count.

However, heavy-streaming alt-screen apps such as Claude Code can paint
into rows that exist in the alt-screen grid from the previous (larger)
layout before processing SIGWINCH. The result: rows beyond the new
visible viewport are still in the grid, and when the next paint occurs
they appear "above" Warp's input prompt while new content is drawn
below it.

This race is analogous to the one solved by
`resize_alt_screen_redundantly` for the alt-screen entry/exit case
(`TerminalModeSwapped`). The same belt-and-suspenders refresh wasn't
applied to the four input-visibility-flip paths:
- `tag_agent_in` (app/src/terminal/view.rs:7319)
- `tag_agent_out` (app/src/terminal/view.rs:7339)
- `open_cli_agent_rich_input`
  (app/src/terminal/view/use_agent_footer/mod.rs:1003)
- `close_cli_agent_rich_input_impl`
  (app/src/terminal/view/use_agent_footer/mod.rs:584)

## Fix

Introduce `TerminalView::refresh_size_if_alt_screen_active` and call it
from the four state-transition sites above. The helper synchronously
runs a `Refresh`-style `SizeUpdate` so the model size, PTY winsize, and
alt-screen grid converge before the next paint.

The change is additive: when the terminal is in blocklist mode, the
helper is a no-op. When alt-screen is inactive, no extra work is done.

## Tests

Added `refresh_size_if_alt_screen_active_only_refreshes_in_alt_screen`
in `app/src/terminal/view_tests.rs` covering both branches of the
helper:
- blocklist mode: no-op, pane size unchanged
- alt-screen mode: refresh executes without panic, pane size preserved
@cla-bot cla-bot Bot added the cla-signed label May 12, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Input prompt box renders mid-output and truncates content during long-running TUI sessions (Claude Code)

1 participant