Recognize Esc and other control keys in kitty keyboard mode
Some checks are pending
ci / test (macos-latest) (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run

Enabling the kitty keyboard protocol for Shift+Enter made terminals
report Esc as CSI 27 u, which the CSI-u parser dropped as KeyUnknown,
so Esc stopped aborting the agent. Map kitty control codepoints
(Esc=27, Tab=9, Backspace=127/8) back to their dedicated keys.
This commit is contained in:
patriceckhart 2026-06-16 07:46:29 +02:00
parent ab7fb37046
commit 1cc654ebbf
2 changed files with 32 additions and 0 deletions

View file

@ -342,9 +342,22 @@ func keyFromModifiedCode(code, mod int) (Key, bool) {
shift := bits&1 != 0
alt := bits&2 != 0
ctrl := bits&4 != 0
// Kitty keyboard protocol (CSI ... u) reports control keys as their
// codepoints: Esc=27, Enter=13, Tab=9, Backspace=127. Without the
// enhanced-mode handling these arrive as raw bytes; with it enabled
// they come through here, so map them back to their dedicated keys.
switch code {
case 13:
return Key{Kind: KeyEnter, Shift: shift, Alt: alt, Ctrl: ctrl}, true
case 27:
return Key{Kind: KeyEsc, Shift: shift, Alt: alt, Ctrl: ctrl}, true
case 9:
if shift {
return Key{Kind: KeyShiftTab, Alt: alt, Ctrl: ctrl}, true
}
return Key{Kind: KeyTab, Shift: shift, Alt: alt, Ctrl: ctrl}, true
case 127, 8:
return Key{Kind: KeyBackspace, Shift: shift, Alt: alt, Ctrl: ctrl}, true
}
if ctrl {
switch code {

View file

@ -30,6 +30,25 @@ func TestReaderParsesModifyOtherKeysCtrlC(t *testing.T) {
}
}
func TestReaderParsesCSIUEsc(t *testing.T) {
k := readKey(t, "\x1b[27u")
if k.Kind != KeyEsc {
t.Fatalf("Read kind=%v, want esc", k.Kind)
}
}
func TestReaderParsesCSIUTabAndBackspace(t *testing.T) {
if k := readKey(t, "\x1b[9u"); k.Kind != KeyTab {
t.Fatalf("Read kind=%v, want tab", k.Kind)
}
if k := readKey(t, "\x1b[9;2u"); k.Kind != KeyShiftTab {
t.Fatalf("Read kind=%v, want shift-tab", k.Kind)
}
if k := readKey(t, "\x1b[127u"); k.Kind != KeyBackspace {
t.Fatalf("Read kind=%v, want backspace", k.Kind)
}
}
func TestReaderParsesSGRMouseWheel(t *testing.T) {
cases := []struct {
seq string