Skip to content

Support the Kitty keyboard protocol (flag 1, disambiguate)#2067

Draft
Carreau wants to merge 3 commits intoprompt-toolkit:mainfrom
Carreau:kitty-protocol
Draft

Support the Kitty keyboard protocol (flag 1, disambiguate)#2067
Carreau wants to merge 3 commits intoprompt-toolkit:mainfrom
Carreau:kitty-protocol

Conversation

@Carreau
Copy link
Copy Markdown
Contributor

@Carreau Carreau commented Apr 16, 2026

Push the Kitty keyboard protocol on startup and pop it on exit so
supporting terminals deliver modified keys — Ctrl-Enter, Shift-Enter,
Ctrl-Shift-Enter, Alt-letter, and full modifier coverage on the
navigation block — as distinct CSI u sequences instead of being
collapsed into their unmodified equivalents. Terminals that don't
implement the protocol silently ignore the push, so there is no
regression for them.

Detection mirrors the existing CPR machinery: the renderer emits
CSI ? u, a new binding under key_binding/bindings/ consumes the
response and flips Renderer.kitty_support from UNKNOWN to SUPPORTED,
letting callers branch on capability if they want.

The bulk of the code lives in new files:

  • input/kitty_keyboard.py — CSI u decoder, functional-key table,
    query-response parser
  • output/kitty_keyboard.py — push/pop context manager with
    reference-counted depth, sequence constants
  • key_binding/bindings/kitty_keyboard.py — response-consuming binding
  • docs/pages/advanced_topics/kitty_keyboard_protocol.rst — maintainer
    notes on wire format, capability detection, and known sharp edges

Touch-points in existing code are small and mirror the CPR pattern:
one import + one regex + one dispatch branch in vt100_parser.py, a
push/query/pop trio in renderer.py, one binding registration in
key_binding/defaults.py, four new enum values in keys.py.

prompt_toolkit does not push xterm's modifyOtherKeys. Users whose
terminal or tmux has it enabled independently still get the existing
CSI 27 Enter fallback in ansi_escape_sequences.py, which folds all
three modifier+Enter variants to plain Keys.ControlM so the form at
least submits rather than silently doing nothing.

New Keys: ControlEnter, ControlShiftEnter, ShiftEnter,
KittyKeyboardResponse. Bindings registered as c-enter / s-enter /
c-s-enter fire on Kitty-capable terminals; on non-Kitty terminals
they don't fire (plain Enter fires instead, as before).

@Carreau
Copy link
Copy Markdown
Contributor Author

Carreau commented Apr 16, 2026

Alternate to #2066, that push it (much further) using kitty, draft for now, I need to check some things, and trim it down as it only support part of the kitty protocol; I need to make sure of a few things (whether it's set or push/pop; and how it behaves in some case) and re-read everything

Push the Kitty keyboard protocol on startup and pop it on exit so
supporting terminals deliver modified keys — Ctrl-Enter, Shift-Enter,
Ctrl-Shift-Enter, Alt-letter, and full modifier coverage on the
navigation block — as distinct CSI u sequences instead of being
collapsed into their unmodified equivalents. Terminals that don't
implement the protocol silently ignore the push, so there is no
regression for them.

Detection mirrors the existing CPR machinery: the renderer emits
`CSI ? u`, a new binding under key_binding/bindings/ consumes the
response and flips `Renderer.kitty_support` from UNKNOWN to SUPPORTED,
letting callers branch on capability if they want.

The bulk of the code lives in new files:
- input/kitty_keyboard.py — CSI u decoder, functional-key table,
  query-response parser
- output/kitty_keyboard.py — push/pop context manager with
  reference-counted depth, sequence constants
- key_binding/bindings/kitty_keyboard.py — response-consuming binding
- docs/pages/advanced_topics/kitty_keyboard_protocol.rst — maintainer
  notes on wire format, capability detection, and known sharp edges

Touch-points in existing code are small and mirror the CPR pattern:
one import + one regex + one dispatch branch in vt100_parser.py, a
push/query/pop trio in renderer.py, one binding registration in
key_binding/defaults.py, four new enum values in keys.py.

prompt_toolkit does not push xterm's modifyOtherKeys. Users whose
terminal or tmux has it enabled independently still get the existing
`CSI 27` Enter fallback in ansi_escape_sequences.py, which folds all
three modifier+Enter variants to plain Keys.ControlM so the form at
least submits rather than silently doing nothing.

New Keys: ControlEnter, ControlShiftEnter, ShiftEnter,
KittyKeyboardResponse. Bindings registered as `c-enter` / `s-enter` /
`c-s-enter` fire on Kitty-capable terminals; on non-Kitty terminals
they don't fire (plain Enter fires instead, as before).
@joouha
Copy link
Copy Markdown
Contributor

joouha commented Apr 19, 2026

Hi Carreau, Jonathan,

I have also implemented support for CSI-u / kitty keyboard protocol in prompt_toolkit for euporie, but I took a slightly different approach to Claude's implementation here, which might be of interest.

I generated new entries for prompt_toolkit.keys:Keys and prompt_toolkit.input.ansi_escape_sequences:ANSI_SEQUENCES using this script, adding kitty and CSI-u keys and sequences. This way, the vt100 input parser can be used as is, without the need to add special handling for kitty style input sequences (they are just treated like any other key-press). Having a greatly expanded list of recognised keys does not appear to have a noticeable performance impact.

I also added enable/disable_extened_keys methods to the Output class (only implemented for Vt10Output). Rather than keeping track of the kitty keyboard state, I simply push and pop from the stack before and after each render cycle (so more akin to how mouse support is implemented).

The apptk package I have linked to above is a modshim overlay package for prompt_toolkit, which contains many extensions and new features. I'd be happy to split out the keyboard input changes (or anything else) into an alternative to this PR if that would be useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants