fix(tui): treat ESC+Return with native Shift held as Shift+Enter

Some Apple Terminal setups send ESC+CR for Shift+Enter, which parsed as
alt+enter and queued a follow-up. The native Shift fallback now covers
ESC+CR as well as plain CR.

fixes #11559
This commit is contained in:
Petr Houška 2026-10-29 20:10:08 +01:00
parent e7a0df27c0
commit 86dc686cf2
5 changed files with 21 additions and 3 deletions

View file

@ -15,6 +15,7 @@
### Fixed
- Fixed `Shift+Enter` queueing a follow-up instead of inserting a newline in Apple Terminal setups that send `ESC` + Return. pi now checks the local Shift key state for that sequence, as it already did for plain Return; `Alt+Enter` without Shift still queues a follow-up ([#11559](https://github.com/earendil-works/pi/issues/11559))
- Fixed the working status (OSC 7501) disappearing in Rex and iTerm2 during runs in regular TUI mode. Rendered messages wrote OSC 133 prompt markers to the terminal, which the terminal treats as a new shell prompt; regular mode no longer writes them ([#11519](https://github.com/earendil-works/pi/issues/11519))
- Fixed compaction, branch summaries, and bug-report summaries in SDK sessions passing the stored OAuth token back to the model runtime as an explicit API key. Provider auth that treats explicit keys differently from stored OAuth now sees the OAuth login, as normal prompts do ([#11520](https://github.com/earendil-works/pi/issues/11520))
- Fixed a redundant system prompt update (tools, rules) when an extension set the same active tools in a different order, for example a reversed `pi.setActiveTools()` list. Tools that stay active now keep their order and newly activated tools are appended ([#11495](https://github.com/earendil-works/pi/issues/11495))

View file

@ -40,7 +40,7 @@ This is an iTerm2-wide setting and can also change native trackpad scrolling. Th
## Apple Terminal
Pi enables enhanced key reporting when available. If Terminal.app still sends plain Return for `Shift+Enter`, Pi uses a local macOS modifier fallback and treats it as `Shift+Enter`.
Pi enables enhanced key reporting when available. If Terminal.app still sends plain Return or `ESC` + Return for `Shift+Enter`, Pi uses a local macOS modifier fallback and treats it as `Shift+Enter`. `ESC` + Return without Shift held remains `Alt+Enter`.
The fallback works only when Pi runs on the same Mac as Terminal.app. It cannot inspect the local modifier state when Pi runs on another machine over SSH.
@ -170,6 +170,8 @@ Windows Terminal uses Pi's Windows and WSL shortcut defaults. See [Keybindings](
### Forward Shift+Enter
When Pi runs natively on Windows, it checks the local Shift key state and treats plain Return or `ESC` + Return as `Shift+Enter` while Shift is held. This fallback does not work in WSL or over SSH. If `Shift+Enter` still submits, forward it explicitly.
Open Windows Terminal's `settings.json` with `Ctrl+Shift+,` or **Settings > Open JSON file**. Add this object to its `actions` array:
```json

View file

@ -10,6 +10,7 @@
### Fixed
- Fixed `Shift+Enter` queueing a follow-up in Apple Terminal setups that send `ESC` + Return. When local native Shift detection is available (Apple Terminal, native Windows), `ProcessTerminal` now treats `ESC` + Return with Shift held as `Shift+Enter`; without Shift it remains `Alt+Enter` ([#11559](https://github.com/earendil-works/pi/issues/11559))
- Fixed regular-mode `TUI` writing OSC 133 prompt markers from rendered lines to the terminal. Terminals treat `133;A` as a new shell prompt and drop the OSC 7501 program status, so the working indicator disappeared during runs. Fullscreen still uses the markers for prompt navigation and already stripped them ([#11519](https://github.com/earendil-works/pi/issues/11519))
- Fixed fullscreen double-click selection splitting paths and tokens at `~`, `.`, `\`, and `+`. Word characters now match iTerm2's defaults ([#10980](https://github.com/earendil-works/pi/issues/10980))
- Fixed `fuzzyMatch()` ranking a query scattered over an earlier part of the text above a contiguous match later in it. It now also scores each contiguous occurrence of the query and keeps the best alignment ([#11220](https://github.com/earendil-works/pi/issues/11220), [#11296](https://github.com/earendil-works/pi/pull/11296))

View file

@ -60,12 +60,19 @@ export function refreshTerminalDimensions(): void {
}
}
// Some Apple Terminal setups send ESC+CR (Alt+Enter) for Shift+Enter (#11559).
function isNativeShiftEnterCandidate(data: string): boolean {
return data === "\r" || data === "\x1b\r";
}
export function normalizeNativeShiftEnterInput(
data: string,
shouldDetectNativeShiftEnter: boolean,
isShiftPressed: boolean,
): string {
if (shouldDetectNativeShiftEnter && data === "\r" && isShiftPressed) return NATIVE_SHIFT_ENTER_SEQUENCE;
if (shouldDetectNativeShiftEnter && isNativeShiftEnterCandidate(data) && isShiftPressed) {
return NATIVE_SHIFT_ENTER_SEQUENCE;
}
return data;
}
@ -397,7 +404,7 @@ export class ProcessTerminal implements Terminal {
private forwardInputSequence(sequence: string): void {
if (!this.inputHandler) return;
const shouldDetectNativeShiftEnter =
sequence === "\r" && (isAppleTerminalSession() || process.platform === "win32");
isNativeShiftEnterCandidate(sequence) && (isAppleTerminalSession() || process.platform === "win32");
const input = normalizeNativeShiftEnterInput(
sequence,
shouldDetectNativeShiftEnter,

View file

@ -36,6 +36,13 @@ describe("normalizeNativeShiftEnterInput", () => {
assert.equal(normalizeNativeShiftEnterInput("\r", true, true), "\x1b[13;2u");
});
it("rewrites ESC+CR to CSI-u Shift+Enter when Shift is pressed", () => {
// #11559
assert.equal(normalizeNativeShiftEnterInput("\x1b\r", true, true), "\x1b[13;2u");
assert.equal(normalizeNativeShiftEnterInput("\x1b\r", true, false), "\x1b\r");
assert.equal(normalizeNativeShiftEnterInput("\x1b\r", false, true), "\x1b\r");
});
it("leaves Return unchanged when native Shift detection is disabled", () => {
assert.equal(normalizeNativeShiftEnterInput("\r", false, true), "\r");
});