feat(terminal): make the Kitty keyboard protocol configurable - #180
Draft
Ayman Bagabas (aymanbagabas) wants to merge 1 commit into
Draft
feat(terminal): make the Kitty keyboard protocol configurable#180Ayman Bagabas (aymanbagabas) wants to merge 1 commit into
Ayman Bagabas (aymanbagabas) wants to merge 1 commit into
Conversation
Add `kitty_keyboard` to `Profile`, defaulting to on. Turning it off makes a session behave like a terminal that never implemented the protocol: the child's mode pushes are ignored, `keyboard_mode` stays empty, and `key press` keeps sending legacy encodings. That is the fallback path a TUI takes on the many terminals without the protocol, and it was not testable before. Gate it natively where the backend offers a switch. alacritty has `Config::kitty_keyboard`, which suppresses the push, pop, set, and report paths, so a child querying with `CSI ? u` gets no reply at all. rio and ghostty take no such configuration, so they are gated where the mode is read and their query replies still go out; the comments say so. Also implement `Emulator::keyboard_mode` for ghostty, which was never wired up and silently reported no flags, so every key reached a ghostty session legacy-encoded no matter what the child had negotiated. libghostty-vt exposes the state as `Terminal::kitty_keyboard_flags`. xterm.js reports no flags because its bundle contains no implementation of the protocol, which is a limit of the emulator rather than of the mapping onto it, so it is declared as a conformance divergence. Support arrives with `vtExtensions.kittyKeyboard` when the vendored bundle reaches 6.1.0 (#179). The two new conformance cases pin the contract for every backend that claims the protocol, so a backend that under-reports fails a named test instead of quietly falling back to legacy keys. Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
This was referenced Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
kitty_keyboardprofile setting (default on) and fixes a gap where the ghostty backend silently reported no Kitty keyboard flags at all.Why
Two separate problems, one subject.
There was no way to test the legacy path. A TUI that negotiates the Kitty protocol still has to work on the many terminals that lack it, and that fallback was unreachable from tui-test: every backend that implemented the protocol always had it on.
ghostty never reported any flags.
Emulator::keyboard_modehas a default returningKeyboardMode::empty(), andGhosttyEmunever overrode it. So every key reaching a ghostty session was legacy-encoded no matter what the child had negotiated:key presswould send\x1b[Ato a program that had asked forCSI uevent reporting. libghostty-vt exposed the state asTerminal::kitty_keyboard_flags()the whole time; it was simply never called.What
Profile::kitty_keyboard, defaulttrue, plumbed throughtui-test.toml, the JS binding (kittyKeyboard), and the Python binding (kitty_keyboard).Gating is native wherever the backend offers a switch:
CSI ? uqueryConfig::kitty_keyboardTerminaltakes no configurationalacritty's switch suppresses the push, pop, set and report paths, so a disabled session is indistinguishable from a terminal that never implemented the protocol. rio and ghostty have no equivalent, so they are gated where the mode is read and their query replies still go out. The comments at each site say exactly this rather than implying parity.
xterm.js
Reports no flags because the vendored bundle contains no implementation. Verified, not inferred:
@xterm/headless6.0.0 has novtExtensions, and no handler forCSI > u,CSI = u, orCSI < u. That is a limit of the emulator rather than of the mapping onto it, which is whatDivergencesis for, so it is declared there asno_kitty_keyboard.Support arrives upstream in the 6.1.0 beta line via
vtExtensions.kittyKeyboard. Tracked in #179 with the steps to take once the vendored bundle gets there; the divergence flag is the one line to delete.Tests
Two new conformance cases, so the contract is pinned for every backend rather than for the one that happened to be implemented first:
conformance_kitty_keyboard_modes_are_pushed_and_poppedre-checks push, stack, and pop, with each of the five flags mapping to its own bit. Skipped for backends declaringno_kitty_keyboard.conformance_kitty_keyboard_can_be_turned_offchecks that a disabled profile reports no flags whatever the child pushes. Runs on all backends including xtermjs, since "off" is a claim every backend can honor.A backend that under-reports now fails a named test instead of quietly falling back to legacy keys. This case is what would have caught the ghostty gap.
Plus
a_profile_can_turn_the_kitty_keyboard_protocol_offfor config parsing, and a default assertion inan_empty_config_yields_the_defaults.cargo test --workspace --features tui-test-rs/ghostty,tui-test-rs/rio,tui-test-rs/xtermjsgives 423 passed, 0 failed.Not in this PR
Routing key encoding to a backend's own encoder, and the related discovery that
keys.rsmodels no DECCKM at all. Separate PR.