fix: deliver pasted text as a bracketed tmux paste (v0.4.4)#155
Merged
Conversation
9d94bc4 to
889ecb9
Compare
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.
889ecb9 to
1a7d72b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Pasting multi-line (or trailing-newline) text into Claude Code's no-flicker/fullscreen renderer auto-submitted instead of staying editable.
terminal.paste(), which only wraps text in bracketed-paste markers when the browser xterm's ownbracketedPasteModeis on — set only if it parsed aESC[?2004h. agentboard attaches to an already-running app, so the browser never sees the startup?2004h; pastes went unbracketed and the first\rsubmitted. (Classic renderer masks this; no-flicker doesn't.)\n, whichPipePaneTerminalProxy.write()splits into literalEnterkeys → line-by-line submit.Fix
New
terminal-pasteclient 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 viatmux set-buffer+paste-buffer -d -p, which:?2004hper-pane, independent of the browser's view) — the correct gate;\n→\r(client normalizes CRLF/CR→LF first);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
paste-buffer -pmechanics against real tmux (bracket gate on/off,\n→\r, no line-splitting).terminal-paste; server routes toproxy.paste(); proxies emit the exact tmux commands; CRLF normalization + buffer-name sanitization.bun run test→ 929 pass / 0 fail, typecheck + lint clean.