Skip to content

fix: bracket multi-line terminal pastes so pasted code isn't treated as Enter#1013

Open
SmolSmolStar wants to merge 1 commit into
web3dev1337:mainfrom
SmolSmolStar:fix/bracketed-paste
Open

fix: bracket multi-line terminal pastes so pasted code isn't treated as Enter#1013
SmolSmolStar wants to merge 1 commit into
web3dev1337:mainfrom
SmolSmolStar:fix/bracketed-paste

Conversation

@SmolSmolStar

Copy link
Copy Markdown
Contributor

Summary

Pasting multi-line code into a terminal (e.g. a SQL block or a snippet from chat) misbehaves: the paste is sent to the PTY as a raw string, so every newline is read as an Enter keypress. The result is code that submits line-by-line or early instead of landing as one clean block. This affects both the worktree terminals and the Commander terminal.

Root cause

The paste handlers send the clipboard text straight through (sendTerminalInput / sendInput) with no bracketed-paste wrapping. Bracketed paste (ESC[200~ESC[201~) is the mechanism that tells a program "this is pasted text — treat the newlines as literal input, don't act on them." Without it, multi-line pastes are indistinguishable from a burst of typed Enters.

Change

  • client/terminal.js (worktree terminals): the three raw-send sites now route through a new pasteTextToTerminal() helper that calls xterm's own terminal.paste(). That normalizes newlines and, only when the running program has bracketed-paste mode enabled (it sent ESC[?2004h), wraps the text in ESC[200~ … ESC[201~. It flows through terminal.onData (which already forwards to sendTerminalInput and marks the session active), and updateInputBuffer ignores ESC-prefixed data so autosuggestions aren't polluted. Falls back to a raw send if the xterm instance is unavailable. Image paste is untouched.
  • client/commander-panel.js (Commander terminal): its paste handler intentionally bypasses xterm.onData (to keep pastes out of slash-command capture), so it brackets inline via a small bracketPastedText() helper that reads terminal.modes.bracketedPasteMode.
  • No regression for plain programs: anything that doesn't enable bracketed-paste mode receives the text unwrapped, exactly as before.

Test plan

  • Extends tests/e2e/commander-paste.spec.js with a multi-line case: with bracketed-paste mode enabled, the POST /api/commander/input body must be ESC[200~line one\rline two ESC[201~. The existing single-line / mode-off case still asserts a raw body (guards against regressions).
  • The underlying mechanism (xterm terminal.paste() bracketing + terminal.modes.bracketedPasteMode reflecting ESC[?2004h) was verified in a headless browser against xterm 5.3.0.
  • Note: I could not execute the e2e suite locally on Windows (the repo's e2e webServer command uses POSIX VAR=value env-prefix syntax that cmd.exe rejects, and the run-e2e-safe.js runner spawns npx which doesn't resolve under Git Bash). The tests are discovered by playwright --list and will run in CI on Linux.

🤖 Generated with Claude Code

…as Enter

Pasting multi-line text into a terminal sent the raw string (newlines included)
straight to the PTY. With no bracketed-paste wrapping, each newline is read as an
Enter keypress, so multi-line code submits line-by-line / early instead of landing
as one block. This hit both the worktree terminals and the Commander terminal.

- client/terminal.js: route paste through a new pasteTextToTerminal() helper that
  uses xterm's terminal.paste(). paste() normalizes newlines and, when the running
  program has bracketed-paste mode enabled (it sent ESC[?2004h), wraps the text in
  ESC[200~ .. ESC[201~ so it's treated as literal pasted input. It flows through
  terminal.onData (already forwarding to sendTerminalInput), and updateInputBuffer
  ignores ESC-prefixed data so autosuggestions aren't polluted. Falls back to a raw
  send if the xterm instance is unavailable. Image paste is unchanged.
- client/commander-panel.js: the Commander paste handler bypasses xterm.onData (to
  keep paste out of slash-command capture), so it brackets inline via a small
  bracketPastedText() helper using terminal.modes.bracketedPasteMode.
- Programs that don't enable bracketed-paste mode still receive the text unwrapped,
  so nothing regresses for them.

Tests: extend tests/e2e/commander-paste.spec.js with a multi-line case asserting the
POST body is bracketed when mode is on (the existing single-line/mode-off case still
asserts a raw body). The bracketing mechanism (xterm.paste + terminal.modes) was
verified in a headless browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant