Skip to content

Raw '\x1b' key comparisons bypass the keymap and break on kitty-protocol terminals #4

Description

@GeekCmore

Background

pi-tui negotiates the kitty keyboard protocol at startup (falling back to modifyOtherKeys). On supporting terminals the Escape key no longer arrives as a bare \x1b byte — it arrives as CSI-u \x1b[27u. pi-tui's matchesKey(data, 'escape') normalizes all three encodings (legacy, kitty CSI-u, modifyOtherKeys), and ctx.mayflyKeymap.matches(data, ACTION_CANCEL) delegates to it.

Any panel that instead compares raw bytes — data === '\x1b' — silently stops reacting to Escape on those terminals, while plain-letter shortcuts (e.g. q) keep working, which makes the failure look bizarre to the user. Headless PTY tests do not reproduce it because a PTY never negotiates kitty, so Esc stays in the legacy encoding there.

This exact bug shipped in the /btw view (fixed in #3 by routing Escape/arrows through mayflyKeymap).

Architecture observation

Key registration is single-sourced (core/keymap.ts + the INTERACTION_KEY_ACTIONS batch in interaction/keys.ts). Key dispatch is not: every panel hand-writes its own handleInput, and nothing enforces going through the keymap. A raw '\x1b' literal imports nothing, so the "only core imports pi-tui" rule cannot catch it.

Known offenders (raw data === '\x1b' outside core)

  • packages/mayfly/src/interaction/attach-view.ts:278 — /agents attach view: Esc never closes on kitty terminals (q still works). Its arrow-key constants (lines 40-43) have the same exposure.
  • packages/mayfly/src/interaction/canonical-panel.ts:122 — the adapter's no-focus-target fallback: onUnhandledEscape never fires for kitty Esc. (Focused compiler surfaces are fine — ui-compiler.ts:1883 already uses matchesKey(Key.escape).)
  • packages/mayfly/src/interaction/plan-review-panel.ts:106 — Esc during revision editing: the compiler exits its own edit state but the panel's editing flag stays set, leaving the two out of sync.

Related gap: page-up/page-down/home/end are not registered as keymap actions at all, so every consumer raw-compares \x1b[5~ etc. (select-list.ts:128-131, frontend-panel.ts, attach-view.ts, ui-compiler.ts:1985-1988). Today kitty happens to keep those encodings identical, but nothing guarantees that.

Suggested direction

  1. Route the three offenders through mayflyKeymap (ACTION_CANCEL, ACTION_MOVE_UP/DOWN), mirroring fix(mayfly): rebuild /btw as an editor-slot BtwView #3.
  2. Register page-up/page-down/home/end as interaction actions and migrate the raw comparers.
  3. Consider an oxlint rule banning \x1b literals in interaction/ and transcript/ so new panels cannot bypass the keymap.
  4. Longer term, if more hand-rolled buffer panels appear, consider a shared panel shell that owns the standard Esc/q/arrows dispatch.

Reproduction

On a kitty-protocol terminal (kitty, WezTerm, Ghostty, …): open /agents → attach to a subagent → press Esc. Nothing happens; q (empty buffer) closes it.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions