Fix 6 client-server mode bugs (display-message, mouse, popup, paste, right-click)#118
Fix 6 client-server mode bugs (display-message, mouse, popup, paste, right-click)#118ToxMox wants to merge 5 commits into
Conversation
1. display-message: Set app.status_message so messages appear on the status bar (tmux parity). The rendering infrastructure existed but the DisplayMessage handler never populated the field. 2. Mouse client tracking: Add client_id to all mouse/scroll CtrlReqs. On mouse events, update latest_client_id and recompute effective window size before processing coordinates. Fixes status bar clicks breaking when multiple clients have different terminal sizes. 3. Popup race condition: Add 50ms delay after PTY spawn before entering PopupMode, giving the reader thread time to populate the vt100 parser. Prevents empty/invisible popups for fast commands. 4. Paste coalescing: Increase paste chunk size from 512 to 4096 bytes. Add 50ms coalescing window for bracket paste events in embedded mode to merge rapid successive paste fragments from VS Code's ConPTY.
Three fixes for paste fragmentation: 1. Ctrl+V paste: Don't flush immediately on paste_confirmed (Ctrl+V Release). ConPTY is still injecting chars. Promote to stage2 and let growth detection wait for delivery to finish. 2. Right-click paste: After sending clipboard content, suppress paste_pend for 2s to discard duplicate key events that VS Code injects through ConPTY. 3. Non-ASCII paste: Allow large (>=20 char) buffers with non-ASCII into stage2 — they're pastes, not IME. Only apply IME exclusion for small (3-19 char) buffers. Also adds bracket paste detection state machine and coalescing to client-server mode (ported from embedded mode in app.rs).
When highlighting text and right-clicking to copy, VS Code's "smart copy/paste" also injects clipboard content as key events through ConPTY. Suppress paste_pend for 2s after the copy-on-right-click path to discard those duplicate key events.
ConPTY strips bracket paste markers when VTI is disabled, so the state machine never fires in client-server mode. It was added before the real paste fix (paste_pend improvements) was identified. Removing it fixes ESC key being swallowed by the detector's 5ms buffering.
- Normalize \r\n to \n in clipboard reads (Windows clipboard preserves \r, causing double-spaced pastes) - Trim trailing newlines from clipboard paste to avoid blank lines - During paste suppression window, discard text key events (Char, Enter, Tab) that VS Code injects as ConPTY echoes — prevents carriage returns leaking into prompt after highlight+right-click copy
|
Apologies for the extra commits after opening — I was still testing edge cases when I submitted. The last two commits fix:
Should be stable now — tested right-click paste, Ctrl+V paste, highlight+right-click copy, ESC, and normal typing. |
|
Hey @ToxMox, thanks for the incredibly thorough analysis! I went through each of the 6 bugs you identified and can confirm every single one is real — great detective work. I've independently fixed all 6 in commit ffcf3d3: 1. display-message — Added a 2. Mouse multi-client — Added 3. Popup race — Added a 50ms 4. Paste fragmentation — Added growth detection to the stage2 timeout: if 5. Right-click copy suppression — After a right-click copy, text key events are now suppressed for 2 seconds via 6. Clipboard CRLF — I'm closing this PR since the fixes are already on master, but I really appreciate you taking the time to identify and document all of these — the root cause analysis in your PR description was spot-on. Cheers! |
… right-click, CRLF) Fix 6 bugs in client-server mode reported in #118: 1. display-message: handler now sets app.status_message when not using -p flag, so messages actually appear on the status bar. 2. Mouse multi-client tracking: all 10 mouse/scroll CtrlReq variants now carry client_id. Server updates latest_client_id on mouse events so coordinate processing uses the clicking client's dimensions. 3. Popup race condition: added 50ms delay after PTY spawn in display-popup so the reader thread can populate the vt100 parser before the first frame is serialized to clients. Prevents blank popups with fast commands. 4. Paste fragmentation: added growth detection to stage2 timeout. If the paste_pend buffer grew since the last check, the timeout is extended instead of splitting the paste. Large non-ASCII buffers (>=20 chars) now enter stage2 instead of being flushed as IME input. 5. Right-click copy paste suppression: after right-click copy, text key events are suppressed for 2s to prevent VS Code ConPTY from injecting duplicate clipboard content as key events. 6. Clipboard CRLF normalization: read_from_system_clipboard() now normalizes Windows CRLF line endings to LF, preventing double-spaced paste output. Also includes the parse_command_line \\\\ escape fix from #123.
Fix 6 bugs in client-server mode reported by @ToxMox in #118: 1. display-message: handler now sets app.status_message when not using -p flag, so messages actually appear on the status bar. 2. Mouse multi-client tracking: all 10 mouse/scroll CtrlReq variants now carry client_id. Server updates latest_client_id on mouse events so coordinate processing uses the clicking client's dimensions. 3. Popup race condition: added 50ms delay after PTY spawn in display-popup so the reader thread can populate the vt100 parser before the first frame is serialized to clients. Prevents blank popups with fast commands. 4. Paste fragmentation: added growth detection to stage2 timeout. If the paste_pend buffer grew since the last check, the timeout is extended instead of splitting the paste. Large non-ASCII buffers (>=20 chars) now enter stage2 instead of being flushed as IME input. 5. Right-click copy paste suppression: after right-click copy, text key events are suppressed for 2s to prevent VS Code ConPTY from injecting duplicate clipboard content as key events. 6. Clipboard CRLF normalization: read_from_system_clipboard() now normalizes Windows CRLF line endings to LF, preventing double-spaced paste output. Also fixes parse_command_line backslash escape reported by @schgoo in #123. The parser now recognises \\ as an escape sequence inside double quotes (producing a single \), preventing the closing quote from being consumed when the client sends send-text "\\". Co-investigated-by: @ToxMox (#118) Co-investigated-by: @schgoo (#123)
Brings 17 upstream commits into ohboy-builds: - fix: warm claim race condition (psmux#136) - fix: client_prefix flag, window_zoomed_flag (psmux#125, psmux#126) - fix: Shift+Enter/Ctrl+Enter modifiers, paste normalization (psmux#131, psmux#132) - fix: backslash escape, 6 client-server bugs (psmux#118, psmux#123) - fix: -f global option (psmux#119) - fix: set-hook replace/remove, zoomed navigation wrap (psmux#133, psmux#134) - fix: TERM env var mapping, hyphenated option leak (psmux#137) - feat: bell/activity/silence monitoring, allow-rename, update-environment - feat: vim-style bind-key C-hjkl pane navigation (psmux#130) - feat: XDG plugin path support (psmux#135) - feat: auto-generate changelog in release workflow - refactor: move Rust tests to tests-rs/ directory - refactor: rich test dashboard Also applies ohboy-builds stashed changes: - Remove claude-code-fix-tty (Claude Code now auto-detects $TMUX) - Migrate env::set_var to safe crate::util::set_env wrappers (Rust 1.83) - Selection clamp to pane boundaries in copy mode - Fix clippy warnings in forked crates (vt100-psmux, portable-pty-psmux)
Fix 6 bugs in client-server mode reported by @ToxMox in psmux#118: 1. display-message: handler now sets app.status_message when not using -p flag, so messages actually appear on the status bar. 2. Mouse multi-client tracking: all 10 mouse/scroll CtrlReq variants now carry client_id. Server updates latest_client_id on mouse events so coordinate processing uses the clicking client's dimensions. 3. Popup race condition: added 50ms delay after PTY spawn in display-popup so the reader thread can populate the vt100 parser before the first frame is serialized to clients. Prevents blank popups with fast commands. 4. Paste fragmentation: added growth detection to stage2 timeout. If the paste_pend buffer grew since the last check, the timeout is extended instead of splitting the paste. Large non-ASCII buffers (>=20 chars) now enter stage2 instead of being flushed as IME input. 5. Right-click copy paste suppression: after right-click copy, text key events are suppressed for 2s to prevent VS Code ConPTY from injecting duplicate clipboard content as key events. 6. Clipboard CRLF normalization: read_from_system_clipboard() now normalizes Windows CRLF line endings to LF, preventing double-spaced paste output. Also fixes parse_command_line backslash escape reported by @schgoo in psmux#123. The parser now recognises \\ as an escape sequence inside double quotes (producing a single \), preventing the closing quote from being consumed when the client sends send-text "\\". Co-investigated-by: @ToxMox (psmux#118) Co-investigated-by: @schgoo (psmux#123)
I ran into these issues while using psmux in client-server mode from VS Code today and ended up forking and fixing them for myself. Figured they might help others out too. Thanks for the awesome work on psmux!
Context
When psmux runs in client-server mode (
psmux attachfrom VS Code terminal or Windows Terminal), several features silently break because the client and server have separate input/rendering paths. These fixes address the most impactful issues for multi-client workflows and VS Code terminal usage.Fixes
display-message
The rendering infrastructure existed (server serializes
status_messageto JSON, client renders it on the status bar withmessage_style) but theDisplayMessagehandler only expanded the format string and returned it — it never setapp.status_message. One-line fix.Mouse multi-client tracking
With multiple clients attached at different terminal sizes, mouse clicks on the status bar only worked in whichever client last resized.
latest_client_idwas only updated onClientAttachandClientSize— not on mouse events. Addedclient_idto all 10 mouse/scrollCtrlReqvariants. The server now recomputes effective window size from the clicking client's dimensions before processing coordinates.Popup race condition
display-popupwith fast commands (e.g.echo test) produced blank popups. The PTY reader thread hadn't processed any output before the first frame was serialized to the client. Added a 50ms delay after PTY spawn to let the reader thread populate the vt100 parser.Paste fragmentation (VS Code terminal)
Large pastes in VS Code's terminal arrived as multiple
[Pasted text #N]chunks instead of one. Root cause chain:KEY_EVENTrecordspaste_pendburst detector entered stage2 but timed out at 300ms mid-paste (ConPTY delivers text over ~1 second)Ctrl+V Releasetriggered an immediate flush before ConPTY finished injecting charactersFixes:
Ctrl+V Releaseno longer flushes immediately — promotes to stage2 for growth detection so ConPTY can finish delivery; right-click clipboard paste suppresses duplicate ConPTY key events for 2s (discards text keys during the suppression window to prevent leaked Enter/Tab); large non-ASCII buffers (>=20 chars) enter stage2 instead of being treated as IME input; added growth detection to stage2 timeout so ongoing pastes aren't split.Right-click copy triggers unwanted paste
In VS Code with "smart copy/paste" right-click behavior, highlighting text and right-clicking would copy the selection AND paste the clipboard content. VS Code injects clipboard content as key events after the copy action. Uses the same 2s suppression window with full text-key filtering.
Clipboard line ending normalization
Right-click paste produced double-spaced text because the Windows clipboard preserves
\r\nline endings. Now normalizes\r\nto\nand trims trailing newlines from clipboard reads.Test plan
display-message "hello"from command prompt shows in status bardisplay-popup -E "echo hello"renders content (not blank)