Skip to content

Esc should leave tmux scroll (copy) mode in a pod #21

Description

@jarvis08

Problem

Scrolling the wheel in a pod puts tmux into copy-mode — the scroll mode. Getting
out of it is unreliable and invisible:

  • Esc works on some hosts and not others. OmniAgent never configures
    mode-keys (electron/pty.ts:53, src-tauri/src/pty.rs:92), so tmux infers it
    from $EDITOR / $VISUAL when the server starts. Where that resolves to
    emacs, Escape cancels copy-mode; where it resolves to vi, Escape only
    clears the selection and you have to press q. The behaviour therefore differs
    per server, and per whatever started the tmux server there — which is exactly
    the kind of machine-dependent difference a fleet tool should not expose.
  • There is no indication you are in scroll mode. tmux's own status bar is
    turned off (set-option status off) because the pod header already shows
    connection state, so nothing on screen says "your keys are copy-mode commands
    right now". Typing into a pod in this state silently does the wrong thing.

Today's escape hatches are q, or scrolling all the way back to the bottom
(the default wheel binding uses copy-mode -e, which exits at the bottom).
Neither is discoverable, and Esc is what everyone reaches for first.

Proposal

Make Esc leave scroll mode in every pod, on every host, regardless of what the
remote tmux server inferred.

Two ways to do it, with different scoping trade-offs — needs a decision:

  • (a) Bind Esc explicitly in both copy-mode tables, added to the session
    bootstrap chain in tmuxCommand() (electron/pty.ts:53) and its Rust twin
    (src-tauri/src/pty.rs:92 and :114):

    bind-key -T copy-mode    Escape send-keys -X cancel \;
    bind-key -T copy-mode-vi Escape send-keys -X cancel \;
    

    Precise, and keeps vi copy-mode keys for users who want them. Caveat:
    bind-key is server-global, so this also changes sessions the user created
    by hand on that host — the same concern Named per-pod tmux sessions on remote hosts #6 raised about not disturbing manual
    sessions. The bootstrap already sets server-global options
    (set-option -sg escape-time 0), so this is not a new class of side effect,
    but it should be a deliberate choice.

  • (b) Pin the mode on the pod's own sessionmode-keys is a window
    option, so set-option -w mode-keys emacs can scope it to this session's
    windows and leave the rest of the host alone. Simpler and side-effect free, but
    it takes vi copy-mode keys away from vi users inside pods. (Verify the -w
    scoping against the tmux versions in use.)

Recommendation: (a), so Esc is fixed without changing which copy-mode dialect a
user gets.

Optional, related — because the status bar is off, consider showing a
SCROLL badge in the pod header while #{pane_in_mode} is true, next to where
NEEDS INPUT appears (src/components/TerminalPod.tsx:216). That addresses the
second half of the problem — not knowing you are in the mode at all. Could be
split into its own issue if it needs polling to detect.

Notes

  • escape-time is already 0 on the session (electron/pty.ts:61), so a lone Esc
    forwards immediately and the binding will feel instant. That option exists for
    a related reason — see the comment there about ⌥⌫ arriving as Esc + Backspace
    — so any change here should be checked against that behaviour.
  • Full-screen apps in the pane (vim, less, an agent TUI) are not in copy-mode and
    are unaffected.
  • Left-button drag selection is handled browser-side, not by tmux
    (src/components/TerminalPod.tsx:325), so it is unaffected either way. The
    wheel is what enters copy-mode.

Acceptance criteria

  • Pressing Esc leaves scroll mode in a pod, on local and remote hosts alike.
  • Behaviour is identical whether the host's tmux resolved mode-keys to vi or
    emacs.
  • q keeps working, and scrolling to the bottom still exits.
  • Esc handling inside full-screen apps in the pane is unchanged.
  • ⌥⌫ in the shell still deletes a word (the escape-time 0 behaviour is not
    regressed).
  • Both the Electron and Tauri bootstrap strings are updated — they are
    duplicated and must not drift.
  • The chosen scoping (server-global binding vs per-session option) is
    documented in the code comment, since it affects sessions the user created
    manually.

Out of scope

Related

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions