Skip to content

fix(mobile): shell Paste inserts a multi-line block as one bracketed paste - #600

Merged
Ark0N merged 2 commits into
Ark0N:masterfrom
timkjr:fix/mobile-paste-bracketed
Oct 13, 2026
Merged

Ark0N merged 2 commits into
Ark0N:masterfrom
timkjr:fix/mobile-paste-bracketed

Conversation

@timkjr

@timkjr timkjr commented Oct 10, 2026 •

Copy link
Copy Markdown

On a phone, the shell accessory bar's Paste overlay sent its text raw and then an Enter (app.sendInput(text) + a delayed \r). Every newline in a multi-line paste reached the shell as Enter, so each line ran as soon as it arrived, followed by one more Enter.

  • Paste now sends one write wrapped in ESC[200~ … ESC[201~ with newlines as CR, through the same path Compose already uses (its bracketed write is extracted into _sendBracketedPaste, unchanged for Compose). Paste never presses Enter: the block lands at the prompt for the user to review and run. This applies to a single-line paste too: a pasted one-liner now waits for Enter instead of running.
  • In a shell that never enabled bracketed paste (dash, bash before 5.1), tmux strips the markers, so the embedded CRs still act as Enter there: every line but the last runs as before, and the last one waits. No marker bytes reach the shell.
  • A paste over the input-frame budget is refused with a toast and keeps its text for trimming (the raw path could exceed the WS frame limit, and its delayed Enter still went out after a refusal).
  • The paste, and an image picked from the same overlay, go to the session the overlay was opened for, not whichever tab is active at Send.

The overlay is only reached for shell sessions (the shell bar's Paste button, and Compose redirecting a shell), so agent sessions are unaffected.

Testing: new cases in test/mobile-prompt-composer.test.ts; typecheck, lint, frontend syntax, format check and the full suite green on this branch. Checked by hand on a phone against my own deployment: a multi-line paste lands at the prompt as one block and runs only on Enter. Checked against tmux 3.5a with a client writing the frame into bash, zsh, dash and cat -v: bash and zsh take the block as one edit, dash and cat show no marker bytes.

timkjr and others added 2 commits October 10, 2026 12:49
…paste

The shell accessory bar's Paste overlay sent its text raw and then an
Enter (`app.sendInput(text)` + a delayed `\r`), so every newline in a
multi-line paste reached the shell as Enter and each line ran on its own
the moment it arrived, followed by one more Enter.

Send it the way a terminal paste does instead: one write wrapped in
ESC[200~ ... ESC[201~ with newlines as CR, through the same path Compose
already uses (its bracketed write is extracted into
`_sendBracketedPaste`, unchanged for Compose). Paste never presses Enter:
the block lands at the prompt for the user to review and run. The
overlay is only reached for shell sessions (the shell bar's Paste button,
and Compose redirecting a shell), so agent sessions are unaffected.

Also: a paste over the input-frame budget is refused with a toast and
keeps its text for trimming (the raw path could exceed the WS frame
limit), and the paste goes to the session the overlay was opened for,
not whichever tab is active at Send.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pened for

The Paste dialog's image branch called _uploadAndInsertImages() without a
session, so it fell back to the active session at pick time: after a tab
switch with the dialog open, the image went to the other tab, while the
text Send already used the session captured at open.

Also corrects the comments around the shared bracketed write: the size
refusal now lives in _sendBracketedPaste(), the budget covers the shell
Paste too, and a shell without bracketed paste (dash, bash before 5.1)
gets the markers stripped by tmux, so every line but the last still runs
there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@timkjr

timkjr commented Oct 12, 2026

Copy link
Copy Markdown
Author

One follow-up commit on top, from a second pass over this PR:

  • The image button in the same Paste dialog still uploaded to whichever tab was active when the image was picked. It now uses the session the dialog was opened for, like the text Send. There's a new test for it.
  • Comments corrected: the size refusal now lives in _sendBracketedPaste, and the "not run" note says it holds only for shells with bracketed paste. In dash or bash before 5.1, tmux strips the markers, so every line but the last still runs, as before.

The description now also says that a one-line paste waits for Enter.

@Ark0N
Ark0N merged commit b7c18ab into Ark0N:master Oct 13, 2026
2 checks passed
Ark0N pushed a commit that referenced this pull request Oct 13, 2026
…600 merge-time fix)

_sendBracketedPaste wrapped the text as is, so clipboard text carrying the
end marker (a pastejacking page can write one) closed the bracket early and
the rest was typed as keys, its CR running a command before review. The
paste markers inside the text are now dropped first; this also hardens the
Compose path, which shares the helper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Ark0N

Ark0N commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Thanks @timkjr, shipped in 1.41.1. One fix at merge: a pasted ESC[201~ can no longer end the bracketed paste early and run the rest of the block without Enter.

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.

2 participants