Skip to content

Render alt-screen apps in the TUI and forward input to the PTY#13626

Open
warp-dev-github-integration[bot] wants to merge 2 commits into
masterfrom
factory/tui-alt-screen
Open

Render alt-screen apps in the TUI and forward input to the PTY#13626
warp-dev-github-integration[bot] wants to merge 2 commits into
masterfrom
factory/tui-alt-screen

Conversation

@warp-dev-github-integration

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

Copy link
Copy Markdown
Contributor

Description

When a PTY app enters the alternate screen (vim, htop, less, top, …) inside the Warp TUI (crates/warp_tui), the pane went blank and swallowed input. The terminal model already tracked the alt screen (TerminalModel::is_alt_screen_active, a populated alt_screen() grid) and warp_terminal already had the key/mouse escape-sequence encoders — but the TUI never rendered the alt-screen grid or routed keys to it.

This PR makes the TUI render the alt-screen app full-area and forward keyboard input to the PTY, mirroring the GUI's app/src/terminal/alt_screen/alt_screen_element.rs:

  • New AltScreenElement (crates/warp_tui/src/alt_screen_view.rs) paints the alt-screen grid (reusing the block cell renderer via terminal_block::render_grid_handler) and positions the terminal cursor.
  • Keystrokes are forwarded via a new TuiTerminalSessionAction::ForwardToPtyPtyIntent::WriteAgentInput: special/control keys use the shared ToEscapeSequence encoder; printable keys forward their UTF‑8 bytes; named control keys (escape, enter, tab, backspace) map to their C0 bytes so e.g. Escape leaves an editor's insert mode.
  • While the alt screen is active, focus moves off the input editor so keys reach the PTY instead of the input keymap; ctrl-c stays as the exit escape hatch, and input focus is restored on exit.
  • TuiTerminalSessionView::render returns the alt-screen element while is_alt_screen_active(); redraws ride the existing wakeup stream.

Scope: this is slice 1 (render + keyboard). Mouse forwarding and resize polish are intentional follow-ups (noted in code) — this is a meaty, multi-subsystem area, so a TUI owner's eyes are welcome.

Testing

Live-verified with the tui-verify-change flow (warp-tui-dev under tmux, CARGO_BUILD_JOBS=1):

  • Before: running vim left the pane blank (matches the report).
  • After: vim renders full-area; G jumps to the bottom (200,1 Bot); insert mode types text; Escape leaves insert mode; :q! quits and the normal TUI is restored (input box + footer).

Added warp_tui unit tests for the key→bytes fallback (alt_screen_view::tests): printable → UTF‑8 bytes, named control keys → C0 bytes (escape 0x1b, enter \r, tab \t, backspace 0x7f), and unmapped keys → nothing. cargo nextest run -p warp_tui (alt-screen tests) and cargo clippy -p warp_tui --all-targets -- -D warnings pass; cargo fmt clean.

  • I have manually tested my changes locally

Screenshots / Videos

Captured tmux capture-pane frame of vim rendered inside the TUI (posted in the Slack thread).

Agent Mode

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

Conversation: https://staging.warp.dev/conversation/9e5f8b04-805e-4fc7-a44c-9006ea7eaa73
Run: https://oz.staging.warp.dev/runs/019f58b4-1630-7a22-ac5e-1197961b8dfd
This PR was generated with Oz.

When a PTY app enters the alternate screen (vim, htop, less, top, …) in
the Warp TUI, the pane went blank and swallowed input: the model tracked
the alt screen but the TUI never rendered it or routed keys to it.

Render the alt-screen grid full-area and forward keystrokes to the PTY:
- Add `AltScreenElement`, which paints the alt-screen grid (reusing the
  block cell renderer) and positions the terminal cursor.
- Forward keystrokes as escape sequences (shared terminal encoder) or
  raw/printable bytes via a new `ForwardToPty` action ->
  `PtyIntent::WriteAgentInput`; named control keys (escape, enter, tab,
  backspace) map to their C0 bytes so e.g. Escape leaves insert mode.
- While the alt screen is active, move focus off the input editor so keys
  reach the PTY rather than the input keymap; ctrl-c stays as the exit
  escape hatch, and input focus is restored on exit.

Slice 1 covers rendering + keyboard. Mouse forwarding and resize polish
are tracked as follow-ups.

Co-Authored-By: Warp <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Jul 13, 2026
@oz-for-oss

oz-for-oss Bot commented Jul 13, 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 a TUI alt-screen renderer and routes keyboard input to the PTY while full-screen terminal apps are active.

Concerns

  • Ctrl+letter key combinations can be mis-forwarded as printable letters in the TUI fallback path. The shared legacy encoder only emits C0 bytes for a small Ctrl+number/space set unless keyboard protocol is active, while the TUI key conversion supplies chars for Ctrl+letter presses; the fallback therefore sends letters like a or d instead of control bytes like 0x01 or 0x04.
  • This is a user-facing TUI behavior change, but the attached PR description does not include the required TUI visual evidence. Please attach the tmux capture-pane transcript/snapshot, a render_to_lines/TuiBuffer::to_lines snapshot diff, or a ./script/run-tui capture directly to the PR.

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/alt_screen_view.rs Outdated
}
.to_escape_sequence(model.deref())
};
let bytes = escape_sequence.or_else(|| fallback_key_bytes(&keystroke.key, chars));

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.

⚠️ [IMPORTANT] fallback_key_bytes receives non-empty chars for Ctrl+letter TUI key events after the legacy encoder returns None, so combinations like Ctrl+A/Ctrl+D get forwarded as printable letters. Handle Ctrl+A–Z as C0 bytes, or suppress printable fallback when Ctrl is held, before falling back to chars.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3562bb4. Added ctrl_letter_c0, applied before the printable chars fallback, so Ctrl+A..Ctrl+Z forward as C0 bytes (0x01..0x1A) instead of the letter. Ctrl+C still returns early to the TUI exit escape hatch. Covered by a unit test and live-verified: Ctrl+D scrolls vim half a page (ruler 1,1 Top → 17,1 6%).

- Ctrl+A..Ctrl+Z now forward as C0 control bytes (0x01..0x1A) instead of
  the printable letter the TUI conversion leaves in `chars`, so control
  shortcuts work in full-screen apps. The shared legacy encoder only
  emits C0 bytes for a small Ctrl+number/space set without the kitty
  keyboard protocol; a new `ctrl_letter_c0` covers Ctrl+A..Z. Ctrl+C
  stays as the TUI exit escape hatch.
- Move the inline `#[cfg(test)] mod tests` to a sibling
  `alt_screen_view_tests.rs` per the repo convention, fixing the
  "Check for inline Rust test modules" CI check.

Co-Authored-By: Warp <agent@warp.dev>
@warp-dev-github-integration

Copy link
Copy Markdown
Contributor Author

TUI visual evidence (attached per review)

Verified with the tui-verify-change flow: warp-tui-dev running under tmux, driven with tmux send-keys and read back with tmux capture-pane.

Before this PR: running a full-screen app (e.g. vim) left the pane blank and swallowed input.

After: vim renders full-area and is interactive. tmux capture-pane of vim /tmp/altscreen_demo.txt inside the Warp TUI:

Alt screen line content for verification. The quick brown fox jumps over the lazy dog.
Alt screen line content for verification. The quick brown fox jumps over the lazy dog.
Alt screen line content for verification. The quick brown fox jumps over the lazy dog.
Alt screen line content for verification. The quick brown fox jumps over the lazy dog.
... (full screen of file content) ...
"/tmp/altscreen_demo.txt" 200L, 17400B                                    1,1           Top

Interaction checks (all read back from capture-pane):

  • G → ruler moves to 200,1 Bot; gg back to 1,1 Top.
  • Insert mode (iEsc) inserts text; Escape leaves insert mode; :q! quits and the normal TUI is restored (input box + footer).
  • Ctrl+D (the review's Ctrl+letter concern) scrolls half a page: ruler 1,1 Top17,1 6%, confirming the combo is forwarded as the C0 byte 0x04, not the letter d.

Also covered by warp_tui unit tests in alt_screen_view_tests.rs (Ctrl+A..Z → 0x01..0x1A, printable → UTF‑8, named control keys → C0).

@warp-dev-github-integration

Copy link
Copy Markdown
Contributor Author

/oz-review

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