Skip to content

fix: deliver pasted text as a bracketed tmux paste (v0.4.4)#155

Merged
gbasin merged 1 commit into
masterfrom
fix/text-paste-autosend
Jul 1, 2026
Merged

fix: deliver pasted text as a bracketed tmux paste (v0.4.4)#155
gbasin merged 1 commit into
masterfrom
fix/text-paste-autosend

Conversation

@gbasin

@gbasin gbasin commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Problem

Pasting multi-line (or trailing-newline) text into Claude Code's no-flicker/fullscreen renderer auto-submitted instead of staying editable.

  • Desktop: the client used xterm's terminal.paste(), which only wraps text in bracketed-paste markers when the browser xterm's own bracketedPasteMode is on — set only if it parsed a ESC[?2004h. agentboard attaches to an already-running app, so the browser never sees the startup ?2004h; pastes went unbracketed and the first \r submitted. (Classic renderer masks this; no-flicker doesn't.)
  • Mobile: the paste button sent raw clipboard text with \n, which PipePaneTerminalProxy.write() splits into literal Enter keys → line-by-line submit.

Fix

New terminal-paste client message. The client sends raw pasted text (guarded on the live attached-session ref so a mid-paste session switch can't paste into the wrong session); the server delivers it via tmux set-buffer + paste-buffer -d -p, which:

  • brackets only when the real pane requested bracketed paste (tmux tracks ?2004h per-pane, independent of the browser's view) — the correct gate;
  • converts \n\r (client normalizes CRLF/CR→LF first);
  • delivers the whole payload as one atomic paste (no per-line Enter).

Paste no longer triggers the Enter/"working" status heuristic (it isn't a submit). Image-path paste (from #154) is unchanged. Implemented for all three proxies (pty/pipe-pane/ssh) via a shared command builder in TerminalProxyBase.

Verification

  • Empirically validated paste-buffer -p mechanics against real tmux (bracket gate on/off, \n\r, no line-splitting).
  • Unit tests for every hop: client hook sends terminal-paste; server routes to proxy.paste(); proxies emit the exact tmux commands; CRLF normalization + buffer-name sanitization. bun run test929 pass / 0 fail, typecheck + lint clean.
  • End-to-end against an already-running no-flicker app (isolated instance): multi-line paste is held for editing, edits apply, and an explicit Enter submits it.

@gbasin gbasin force-pushed the fix/text-paste-autosend branch from 9d94bc4 to 889ecb9 Compare July 1, 2026 22:06
Pasting multi-line (or trailing-newline) text into Claude Code's
no-flicker/fullscreen renderer auto-submitted instead of staying editable.

Root cause: the client relied on xterm's terminal.paste(), which only wraps
text in bracketed-paste markers when the browser xterm's own bracketedPasteMode
is on. When agentboard attaches to an already-running app, the browser never
sees the startup ESC[?2004h, so pastes went unbracketed and the first CR
submitted. The mobile paste button sent raw text with newlines, which
PipePaneTerminalProxy split into literal Enter keys.

Fix: add a terminal-paste ClientMessage. The client sends raw pasted text
(guarded on the live attached-session ref so a mid-paste session switch can't
paste into the wrong session, and it exits tmux copy-mode first so the paste
isn't swallowed by the copy-mode key table). The server delivers it via tmux
`load-buffer -` (stdin) + `paste-buffer -d -p`:

- paste-buffer -p brackets only when the real pane requested bracketed paste
  (tmux tracks ESC[?2004h per-pane, independent of the browser's view), and
  converts newlines to CR, delivering the payload as one atomic paste;
- load-buffer via stdin (not set-buffer -- <data>) avoids the OS single-argv
  size limit, so large pastes don't fail and fall back to the newline-splitting
  write() path;
- a unique per-paste buffer name keeps two rapid pastes from racing on the
  shared buffer (matters for the SSH proxy's fire-and-forget async delivery);
- on failure we never fall back to write() — a raw multi-line delivery is the
  auto-submit bug — and the drop is logged (terminal_paste_failed, warn).

Paste no longer triggers the Enter/"working" status heuristic. Image-path paste
is unchanged. Implemented for pty/pipe-pane/ssh via shared base helpers, with
unit coverage for all three proxies and a Playwright e2e spec (bracketed-paste
REPL fixture) proving a multi-line paste is held for editing and an explicit
Enter submits it. The e2e webServer pins AGENTBOARD_STATIC_DIR to the repo
build so runs from inside a live agentboard session can't serve the installed
package's stale bundle.

Known limitation: on a remote (SSH) session, paste takes a tmux side-channel
while typed input goes to the interactive pty, so a paste immediately followed
by Enter can, on a slow link, submit before the paste lands.

Verified end-to-end against an already-running no-flicker app: multi-line,
160 KB, rapid double, and copy-mode pastes are all held for editing (not
submitted); an explicit Enter submits.
@gbasin gbasin force-pushed the fix/text-paste-autosend branch from 889ecb9 to 1a7d72b Compare July 1, 2026 22:27
@gbasin gbasin merged commit fde1916 into master Jul 1, 2026
5 checks passed
@gbasin gbasin deleted the fix/text-paste-autosend branch July 1, 2026 22:29
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