Skip to content

Kitty graphics capability probe leaks "Gi=31;OK" into the input field when running inside tmux #3500

Description

@changlianxiya-139

Summary

When running crush inside tmux (outer terminal: kitty), the terminal capability probe leaks the string Gi=31;OK into the editor input field on startup, right after the prompt icon.

Environment

  • crush v0.88.1 (also reproducible on main)
  • Outer terminal: kitty (client termname xterm-kitty)
  • Inside: tmux 3.7b (TERM_PROGRAM=tmux, TERM=xterm-256color)
  • Shell: zsh

What I see

On startup the bottom editor line renders:

Y  Gi=31;OK

where Y is the yolo-mode prompt icon. Gi=31;OK is typed into the textarea as if the user had pressed those keys.

Root cause

  1. Crush sends a kitty graphics probe. QueryCmd in internal/ui/common/capabilities.go writes:

    ansi.KittyGraphics([]byte("AAAA"), "i=31", "s=1", "v=1", "a=q", "t=d", "f=24")
    

    i.e. \x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\. Inside tmux this is wrapped with ansi.TmuxPassthrough (DCS passthrough, allow-passthrough on).

  2. shouldQueryCapabilities returns true for tmux. After PR fix(ui): prevent AAAA probe bleed in terminals without Kitty graphics support #1967 the logic is:

    return (!okTermProg && !okSSHTTY) ||
        (!strings.Contains(termProg, osVendorTypeApple) && !okSSHTTY) ||
        xstrings.ContainsAnyOf(termType, kittyTerminals...)

    With TERM_PROGRAM=tmux, the middle branch is true, so the probe is sent even though tmux is not a kitty-graphics terminal.

  3. The outer terminal responds with an APC sequence. kitty answers:

    \x1b_Gi=31;OK\x1b\
    
  4. tmux splits the APC response into keypresses. tmux's TTY key parser (tty-keys.c) has no entry for \x1b_ (APC). The fallback path does:

    if (*buf == '\033' && len >= 2) {
        key = (u_char)buf[1] | KEYC_META;
        size = 2;
    }

    So \x1b_ becomes Alt+_ (2 bytes consumed), the remaining Gi=31;OK are forwarded one character at a time as ordinary printable keys, and the trailing \x1b\ becomes Alt+.

  5. Crush inserts the printable chars into the textarea. Since they arrive without the ESC/APC framing, ultraviolet treats them as regular keypresses and they land in the input field (Gi=31;OK).

Related

Suggested fix

Skip the capability probe when running inside tmux, since tmux's TTY key parser cannot forward APC/kitty-graphics responses to the pane. E.g. in shouldQueryCapabilities:

func shouldQueryCapabilities(env uv.Environ) bool {
    // tmux cannot forward APC (kitty graphics) responses from the outer
    // terminal; they get split into keypresses ("Gi=31;OK" in the prompt).
    if _, isTmux := env.LookupEnv("TMUX"); isTmux {
        return false
    }
    ...
}

Alternatively, in QueryCmd only send the kitty graphics probe when not inside tmux.

Repro

  1. Inside tmux (outer kitty), run crush (any mode).
  2. Observe Gi=31;OK typed into the editor input field on startup.

Note: the exact leaked string depends on the outer terminal's response payload; with kitty it is the OK response to the a=q query with transmission id 31.

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