Skip to content

feat(terminal): adopt Kitty keyboard protocol for proper modifier key handling #139

Description

@Fldicoahkiin

Problem

Currently, xterm.js treats Shift+Enter identically to Enter (both send \r). Users expect Shift+Enter to insert a newline for multiline command editing — consistent with Claude Code, Warp, Kitty, Ghostty, and other modern terminals.

Current Workaround

Intercept Shift+Enter at the JS level (keybindings.ts) and send \n instead of \r, combined with a zsh ^J rebind in default_init.sh. This works but only handles one key combo and requires shell-side configuration.

Ideal Solution: Kitty Keyboard Protocol

xterm.js v6.1.0-beta.197 already includes a full Kitty keyboard protocol implementation (KittyKeyboard.ts, KeyboardService.ts). Enabling it would:

  • Distinguish all modifier+key combinations (Shift+Enter, Ctrl+Enter, etc.)
  • Align with Kitty, Ghostty, WezTerm, iTerm2, and other modern terminals
  • Eliminate the need for JS-level key interception hacks

What we tried

const term = new XTerm({ vtExtensions: { kittyKeyboard: true } });
printf '\x1b[>1u'  # Enable DISAMBIGUATE_ESCAPE_CODES flag

What went wrong

With flag 1, xterm.js encodes Ctrl+C as \x1b[99;5u instead of the legacy \x03. Per the Kitty spec, flag 1 should only re-encode ambiguous keys. Ctrl+C has an unambiguous legacy encoding and should remain \x03. This causes zsh to output raw 9;5u characters.

What needs to happen

  1. Investigate xterm.js behavior — Determine if this is a bug in xterm.js's Kitty implementation or if shell-side bindkeys are needed for all Ctrl+letter keys
  2. Shell integration — Add zsh/bash/fish bindings for common CSI u sequences
  3. Testing — Verify Ctrl+C/Z/D/L and other essential keys still work
  4. Gradual rollout — Consider making Kitty protocol opt-in via settings initially

Industry Reference

Terminal Shift+Enter approach
Kitty/Ghostty Kitty protocol (native)
VS Code F12 proxy sequence (PowerShell only)
Warp Block editor (UI layer)
Hyper/Tabby/ttyd Not handled
2code (current) JS intercept + zsh ^J rebind

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions