What
Pasting terminal output that contains ANSI escapes puts the escape bodies into the prompt as literal text, and an unterminated bracketed paste leaves the editor unable to accept any input — Ctrl+C included.
Why
pi-tui 1.4.0, components/editor.ts:
handlePaste() filters pasted text per character:
let filteredText = cleanText
.split("")
.filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
.join("");ESC (0x1b) is dropped and the rest of the sequence is kept, so \x1b[38;2;111;63;32m is inserted as the visible text [38;2;111;63;32m, and \x1b[<35;153;17M as [<35;153;17M. A pasted screenful of coloured/mouse-reporting output turns into thousands of characters of that. The comment a few lines above already documents this same class of leak for tmux csi-u re-encoding.
handleInput() then handles isInPaste before every keybinding and returns:
if (this.isInPaste) {
this.pasteBuffer += data;
const endIndex = this.pasteBuffer.indexOf("\x1b[201~");
if (endIndex !== -1) { ... }
return;
}
...
if (kb.matches(data, "tui.input.copy")) { // Ctrl+C, unreachable while isInPaste
return;
}If \x1b[201~ never arrives — another process on the same tty reads it first, the paste is cut short, or a split read loses it — every later keystroke, Ctrl+C included, is appended to pasteBuffer and dropped. The session cannot be typed into again; only a new paste (its own terminator closes the stale one) or killing the process recovers.
Repro
- Start
piin a pane whose tty is shared with another process that can read stdin — e.g. asudo/SUDO_ASKPASSfallback doingread -rs … < /dev/tty, or a tool that hands the terminal to a child. - Paste terminal output containing ANSI (truecolor SGR, SGR mouse reports).
- The escape bodies appear as literal text in the prompt. If the paste terminator was consumed by the other reader, the editor stops accepting input permanently and Ctrl+C does nothing.
Observed on macOS in a multiplexer pane (pi 1.4.0), where a password prompt owned the tty while the paste was in flight.
Suggested fix
- Strip whole escape sequences (there is already a
stripTerminalSequenceshelper) before the per-character filter, so a pasted ANSI stream degrades to plain text instead of escape corpses. - Bound
isInPaste: clear it on an idle timeout or on a lone ESC, so one missing terminator cannot wedge the editor.
Activity
0No activity yet.