From bdf482cefce24c534a13d7bf647dc69b67b6fbfc Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 6 Mar 2026 16:02:17 +0100 Subject: [PATCH] fix(tui): add modifyOtherKeys fallback for modified enter keys in tmux When Kitty keyboard protocol is not available (e.g. inside tmux), fall back to xterm modifyOtherKeys mode 2 so that Shift+Enter, Ctrl+Enter, and other modified keys are distinguishable from plain Enter. tmux users need to add to ~/.tmux.conf: set -g extended-keys on set -g extended-keys-format csi-u closes #1872 --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/README.md | 2 +- packages/coding-agent/docs/tmux.md | 39 ++++++++++++++++++++++++++++++ packages/tui/CHANGELOG.md | 4 +++ packages/tui/src/terminal.ts | 20 +++++++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 packages/coding-agent/docs/tmux.md diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 632c7c3b..072f414b 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added `claude-sonnet-4-6` model for the `google-antigravity` provider ([#1859](https://github.com/badlogic/pi-mono/issues/1859)). +- Added [tmux setup documentation](docs/tmux.md) for modified enter key support ([#1872](https://github.com/badlogic/pi-mono/issues/1872)) ### Fixed diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index 268a636e..c01af209 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -68,7 +68,7 @@ pi Then just talk to pi. By default, pi gives the model four tools: `read`, `write`, `edit`, and `bash`. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [pi packages](#pi-packages). -**Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md) +**Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md) --- diff --git a/packages/coding-agent/docs/tmux.md b/packages/coding-agent/docs/tmux.md new file mode 100644 index 00000000..804ab827 --- /dev/null +++ b/packages/coding-agent/docs/tmux.md @@ -0,0 +1,39 @@ +# tmux Setup + +Pi works inside tmux, but tmux strips modifier information from certain keys by default. Without configuration, `Shift+Enter` and `Ctrl+Enter` are indistinguishable from plain `Enter`. + +## Required Configuration + +Add to `~/.tmux.conf`: + +```tmux +set -g extended-keys on +set -g extended-keys-format csi-u +``` + +Then restart tmux (not just reload): + +```bash +tmux kill-server +tmux +``` + +This tells tmux to forward modified key sequences in CSI-u format when an application requests extended key reporting. Pi requests this automatically when Kitty keyboard protocol is not available. + +## What This Fixes + +Without this config, tmux collapses modified enter keys to plain `\r`: + +| Key | Without config | With config | +|-----|---------------|-------------| +| Enter | `\r` | `\r` | +| Shift+Enter | `\r` | `\x1b[13;2u` | +| Ctrl+Enter | `\r` | `\x1b[13;5u` | +| Alt/Option+Enter | `\x1b\r` | `\x1b[13;3u` | + +This affects the default keybindings (`Enter` to submit, `Shift+Enter` for newline) and any custom keybindings using modified enter keys. + +## Requirements + +- tmux 3.2 or later (run `tmux -V` to check) +- A terminal emulator that supports extended keys (Ghostty, Kitty, iTerm2, WezTerm, Windows Terminal) diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index ea77cb8b..88f6cfac 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added xterm modifyOtherKeys mode 2 fallback when Kitty keyboard protocol is not available, enabling modified enter keys (Shift+Enter, Ctrl+Enter) inside tmux ([#1872](https://github.com/badlogic/pi-mono/issues/1872)) + ## [0.56.2] - 2026-03-05 ### Added diff --git a/packages/tui/src/terminal.ts b/packages/tui/src/terminal.ts index 5ebb4967..da9e4ce1 100644 --- a/packages/tui/src/terminal.ts +++ b/packages/tui/src/terminal.ts @@ -57,6 +57,7 @@ export class ProcessTerminal implements Terminal { private inputHandler?: (data: string) => void; private resizeHandler?: () => void; private _kittyProtocolActive = false; + private _modifyOtherKeysActive = false; private stdinBuffer?: StdinBuffer; private stdinDataHandler?: (data: string) => void; private writeLogPath = process.env.PI_TUI_WRITE_LOG || ""; @@ -158,6 +159,11 @@ export class ProcessTerminal implements Terminal { * Sends CSI ? u to query current flags. If terminal responds with CSI ? u, * it supports the protocol and we enable it with CSI > 1 u. * + * If no Kitty response arrives shortly after startup, fall back to enabling + * xterm modifyOtherKeys mode 2. This is needed for tmux, which can forward + * modified enter keys as CSI-u when extended-keys is enabled, but may not + * answer the Kitty protocol query. + * * The response is detected in setupStdinBuffer's data handler, which properly * handles the case where the response arrives split across multiple stdin events. */ @@ -165,6 +171,12 @@ export class ProcessTerminal implements Terminal { this.setupStdinBuffer(); process.stdin.on("data", this.stdinDataHandler!); process.stdout.write("\x1b[?u"); + setTimeout(() => { + if (!this._kittyProtocolActive && !this._modifyOtherKeysActive) { + process.stdout.write("\x1b[>4;2m"); + this._modifyOtherKeysActive = true; + } + }, 150); } /** @@ -204,6 +216,10 @@ export class ProcessTerminal implements Terminal { this._kittyProtocolActive = false; setKittyProtocolActive(false); } + if (this._modifyOtherKeysActive) { + process.stdout.write("\x1b[>4;0m"); + this._modifyOtherKeysActive = false; + } const previousHandler = this.inputHandler; this.inputHandler = undefined; @@ -240,6 +256,10 @@ export class ProcessTerminal implements Terminal { this._kittyProtocolActive = false; setKittyProtocolActive(false); } + if (this._modifyOtherKeysActive) { + process.stdout.write("\x1b[>4;0m"); + this._modifyOtherKeysActive = false; + } // Clean up StdinBuffer if (this.stdinBuffer) {