feat(terminal): make the Kitty keyboard protocol configurable - #180
feat(terminal): make the Kitty keyboard protocol configurable#180Ayman Bagabas (aymanbagabas) wants to merge 5 commits 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>
# Conflicts: # README.md # SKILL.md # bindings/js/src/config.ts # bindings/python/src/tui_test/_config.py # crates/tui-test/src/terminal/alacritty.rs # crates/tui-test/src/terminal/conformance.rs # crates/tui-test/src/terminal/ghostty/core.rs # crates/tui-test/src/terminal/ghostty/mod.rs # crates/tui-test/src/terminal/xtermjs.rs
Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
`profile_kitty_keyboard` is a new positional argument between scrollback and colors, and both binding suites pin the native call by position. Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
cpendery (cpendery)
left a comment
There was a problem hiding this comment.
So I understand that we are no respecting the child requesting input to be sent via kitty keyboard; however, we are answering yes to child pty's with rio (& ghostty) that we support kitty keyboard. I think this could cause some weird failures in the future for apps that get back a yes for support but input doesn't come in the expected format.
nit: also can we rename the profile field to be supportKittyKeyboardProtocol or something a little more clear than just kitty_keyboard
Having a standard way of intercepting requests & replying with enable or disabled for features would be useful to extend for more responses as well, for example, we currently capture win32-input-mode (9001) requests and strip them. It would be great to centralize/standardize that logic |
| if raw.get("scrollback") is not None: | ||
| normalized["scrollback"] = raw["scrollback"] | ||
| if raw.get("kitty_keyboard") is not None: | ||
| normalized["kitty_keyboard"] = bool(raw["kitty_keyboard"]) |
There was a problem hiding this comment.
i'm pretty sure that this will convert invalid values to True by default, not sure if we want that behavior
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.