fix(tui): enable Kitty keyboard protocol so Shift+Enter inserts a newline#13782
Conversation
…line
The headless TUI never opted into the Kitty keyboard protocol
("progressive keyboard enhancement"). In terminals that require the
opt-in (e.g. Ghostty), Enter and Shift+Enter both arrive as a bare CR,
so the input couldn't distinguish "submit" from "insert newline" and
Shift+Enter submitted instead of adding a line. It worked in the Warp
terminal only because that path sends LF (decoded as Ctrl+J, which an
existing binding catches).
Push `PushKeyboardEnhancementFlags(DISAMBIGUATE_ESCAPE_CODES)` when
entering the alternate screen and `PopKeyboardEnhancementFlags` when
leaving, mirroring what Codex/Claude Code do. The shift-enter->InsertNewline
binding and the SHIFT-modifier conversion already exist, so no keymap or
feature-flag change is needed. Unsupported terminals ignore the sequence.
Adds a regression test asserting the enter/leave lifecycle emits the
push (CSI >1u) and pop (CSI <1u) sequences.
CHANGELOG-BUG-FIX: Fixed Shift+Enter not inserting a newline in the Warp TUI when run in terminals using the Kitty keyboard protocol (e.g. Ghostty).
Co-Authored-By: Oz <oz-agent@warp.dev>
|
@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 Powered by Oz |
There was a problem hiding this comment.
Overview
This PR enables crossterm's Kitty keyboard protocol enhancement flag when entering the headless TUI alternate screen and pops it on exit, with a regression test covering the emitted push/pop escape sequences. I did not find code-level correctness or security issues in the changed lines, and there is no spec context to validate against.
Concerns
- This is a user-facing TUI input behavior change, but the PR does not include acceptable TUI verification evidence such as a terminal transcript, a render snapshot diff, or a
./script/run-tuicapture demonstrating Shift+Enter inserting a newline end to end. The byte-level escape-sequence test is useful automated coverage, but it does not replace the repository-required evidence for behavioral TUI changes; the stated headless-runner limitation is not an exemption.
Verdict
Found: 0 critical, 1 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
crossterm hard-codes `is_ansi_code_supported() -> false` for Push/PopKeyboardEnhancementFlags on Windows, forcing the legacy console API path which returns `Unsupported`. Putting them inside the main `execute!` made `enter_terminal_screen`/`leave_terminal_screen` return Err on Windows (breaking the Windows unit tests, and TUI startup). Issue the push/pop as best-effort (`let _ = execute!(...)`) so a failure never aborts terminal setup/restore, mirroring how Codex handles it. Gate the byte-level assertions in the regression test to non-Windows, where the ANSI sequences are actually emitted; the enter/leave calls are still asserted to succeed on every platform. Co-Authored-By: Oz <oz-agent@warp.dev>
Adds a runtime-level test that pushes a Shift+Enter crossterm KeyEvent (the distinct event the Kitty protocol enables) through event conversion and the keymap responder chain, asserting it dispatches its bound action. This is the exact path the TUI input's shift-enter -> insert-newline binding relies on, giving durable behavioral coverage beyond the byte-level push/pop sequence test. Co-Authored-By: Oz <oz-agent@warp.dev>
`test_handle_pty_read_event_while_not_batching` waits on the real ~50ms PTY batch timer via `assert_eventually!`. Its own comment documents this as a Windows timing flake; the prior 200ms budget still timed out on loaded Windows CI runners (coarse ~15.6ms timer granularity + a loaded single-threaded executor), which was the only failing check on this PR. Raise both waits to a 1s budget. `assert_eventually!` returns as soon as the condition holds, so this keeps the real batch-timer path under test without slowing the passing case, and eliminates the flake. Unrelated to this PR's TUI change otherwise — surfaced by, not caused by, its CI runs. Co-Authored-By: Oz <oz-agent@warp.dev>
…dows CI)" This reverts commit 0d52a7f.
Make the enter_terminal_screen comment more concise and state explicitly that the Kitty keyboard-protocol opt-in only affects the TUI's own host terminal (the GUI never enters raw mode / the alt screen and never runs this). Also add a blank line after the alternate-screen setup. Co-Authored-By: Oz <oz-agent@warp.dev>
Description
In Warp's headless TUI (
crates/warp_tui,./script/run-tui), pressing Shift+Enter did not insert a newline in the input when running inside terminals that require the Kitty keyboard protocol ("progressive keyboard enhancement") — e.g. Ghostty. It submitted instead. It worked in the Warp terminal and for Codex/Claude Code in Ghostty.Root cause: the TUI terminal setup in
crates/warpui_core/src/runtime/mod.rs(enter_terminal_screen/leave_terminal_screen) never pushed/popped the keyboard enhancement flags. Without that opt-in, protocol terminals send a bare CR (0x0d) for both Enter and Shift+Enter, so crossterm deliversKeyCode::Enterwith no SHIFT modifier for both and the keymap runsenter→Submit instead ofshift-enter→InsertNewline. It worked in the Warp terminal only because that path sends LF (0x0a), which crossterm decodes in raw mode as Ctrl+J — caught by the existingctrl-j→InsertNewline binding.Fix: push
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)when entering the alternate screen andPopKeyboardEnhancementFlagswhen leaving — the same opt-in Codex/Claude Code use. Theshift-enter→InsertNewline binding and the SHIFT-modifier conversion already exist, so no keymap or feature-flag change is needed. Terminals that don't support the protocol silently ignore the sequence, and the existingRawModeGuardalready runs the pop on drop/unwind.Testing
terminal_screen_lifecycle_toggles_keyboard_enhancementincrates/warpui_core/src/runtime/mod_tests.rs— asserts entering the TUI emits the push sequence (CSI>1u) and leaving emits the pop sequence (CSI<1u). Fails before the fix, passes after.cargo test -p warpui_core --features tui runtime::tests::terminal_screen_lifecycle— passes../script/format— clean;cargo clippy -p warpui_core --features tui --all-targets --tests -- -D warnings— clean.UI verification limitation
This fix is user-visible, but its observable effect only manifests in a Kitty-protocol terminal (Ghostty/kitty/foot/WezTerm). Ghostty is a GUI terminal emulator and this fix targets the headless
warp_tui, neither of which can be driven in the headless cloud runner used to author this PR, so I could not perform a livecomputer_useverification of Shift+Enter in Ghostty. The byte-level regression test is the primary automated guard. Please manually verify in Ghostty: run./script/run-tui, type text, press Shift+Enter → a newline is inserted and the message is not submitted; plain Enter still submits; Ctrl+J / Alt+Enter still insert newlines; Ctrl+C still exits and Esc still closes menus. Also confirm no regression in the Warp terminal (Shift+Enter still inserts a newline).Agent Mode
Conversation: https://staging.warp.dev/conversation/72206894-95ac-4379-b316-9542ef8efd73
Run: https://oz.staging.warp.dev/runs/019f6706-0179-7a2d-900a-9619b27361f0
This PR was generated with Oz.