Fix Shift+Enter for VS Code terminal with Node.js apps (Claude Code)#132
Fix Shift+Enter for VS Code terminal with Node.js apps (Claude Code)#132ToxMox wants to merge 1 commit into
Conversation
Client: augment_enter_shift polls GetAsyncKeyState to detect physical Shift key, remapping crossterm's misreported Alt+Enter back to Shift+Enter (xterm.js sends ESC+CR which ConPTY interprets as Alt). Server: send ESC+CR (\x1b\r) for Shift+Enter to ConPTY input pipe, matching what VS Code's xterm.js sends. The round-trip works because libuv translates the resulting Alt+Enter KEY_EVENT_RECORD back to \x1b\r, which Node.js apps interpret as Shift+Enter. The existing CSI \x1b[13;2~ encoding is dropped by ConPTY (it doesn't recognize code 13 in ~ format). Win32-input-mode doesn't work for Node.js because libuv ignores SHIFT on VK_RETURN.
…ncoding (PR #132) PR #131 - Multi-line paste reverse order in PowerShell: - Root cause: write_paste_chunked sends clipboard text with bare LF to PTY pipe. ConPTY expects CR for Enter; bare LF confuses PSReadLine, causing reversed order. - Fix: Normalize line endings (LF->CR, CRLF->CR) in write_paste_chunked before writing to the PTY pipe. PR #132 - Shift+Enter not working for VS Code terminal / Node.js apps: - Root cause (client): VS Code xterm.js sends ESC+CR for Shift+Enter. ConPTY interprets ESC as Alt prefix, so crossterm misreports Alt+Enter. - Root cause (server): CSI 13;2~ encoding is non-standard (code 13 not in VT function key table) and silently dropped by ConPTY. - Fix (client): augment_enter_shift polls GetAsyncKeyState(VK_SHIFT) to detect physical Shift key and remaps crossterm's misreported Alt+Enter to Shift+Enter. - Fix (server): encode_key_event and send_key_to_active send ESC+CR instead of CSI 13;mod~ for Shift/Alt+Enter on Windows. This matches VS Code xterm.js encoding and round-trips through ConPTY -> libuv correctly. - Ctrl+Enter variants still use CSI encoding (unaffected by ConPTY). Tests: 7 new unit tests covering paste normalization (LF, CRLF, mixed, bracketed) and Shift/Alt+Enter ConPTY encoding. All 68 tests pass.
|
Hey @ToxMox, solid work on this one — both root causes you identified are correct. Client side: VS Code's xterm.js sends \x1b\r\ for Shift+Enter, ConPTY interprets the ESC as an Alt prefix, and crossterm delivers Alt+Enter instead of Shift+Enter. Your \�ugment_enter_shift\ approach using \GetAsyncKeyState(VK_SHIFT)\ is the right call — there's no other way to recover the real modifier once ConPTY has already eaten it. Server side: CSI \x1b[13;2~\ is non-standard (code 13 isn't in the VT function key table), and ConPTY silently drops it. Sending \x1b\r\ (ESC+CR) instead works because the round-trip preserves the ESC through libuv's Alt-prefix handling, so Node.js apps like Claude Code receive it correctly. I independently verified both bugs by writing unit tests — Shift+Enter encoding produced \x1b[13;2~\ (7 bytes) instead of \x1b\r\ (2 bytes), and Alt+Enter had the same issue. After applying the fix (same approach as your PR), all new tests pass along with the full 68-test suite. I also extended \�ncode_key_event\ with the same Windows-conditional logic for the embedded-mode path, and added the #[cfg(windows)]\ guard in \send_key_to_active\ for the S-Enter/M-Enter command dispatch. Fixed in abd5e25. Closing this PR since master already has the fix, but thanks for the thorough analysis — the ConPTY round-trip explanation was particularly helpful. |
|
Awesome! Glad my PRs have been useful. Thanks for the quick implementations! 😎 |
|
@ToxMox Yes they have been. Thank you! |
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)
…PTY encoding (PR psmux#132) PR psmux#131 - Multi-line paste reverse order in PowerShell: - Root cause: write_paste_chunked sends clipboard text with bare LF to PTY pipe. ConPTY expects CR for Enter; bare LF confuses PSReadLine, causing reversed order. - Fix: Normalize line endings (LF->CR, CRLF->CR) in write_paste_chunked before writing to the PTY pipe. PR psmux#132 - Shift+Enter not working for VS Code terminal / Node.js apps: - Root cause (client): VS Code xterm.js sends ESC+CR for Shift+Enter. ConPTY interprets ESC as Alt prefix, so crossterm misreports Alt+Enter. - Root cause (server): CSI 13;2~ encoding is non-standard (code 13 not in VT function key table) and silently dropped by ConPTY. - Fix (client): augment_enter_shift polls GetAsyncKeyState(VK_SHIFT) to detect physical Shift key and remaps crossterm's misreported Alt+Enter to Shift+Enter. - Fix (server): encode_key_event and send_key_to_active send ESC+CR instead of CSI 13;mod~ for Shift/Alt+Enter on Windows. This matches VS Code xterm.js encoding and round-trips through ConPTY -> libuv correctly. - Ctrl+Enter variants still use CSI encoding (unaffected by ConPTY). Tests: 7 new unit tests covering paste normalization (LF, CRLF, mixed, bracketed) and Shift/Alt+Enter ConPTY encoding. All 68 tests pass.
Summary
Shift+Enter doesn't work for Node.js apps (like Claude Code) running inside psmux in VS Code's terminal. The existing CSI
\x1b[13;2~encoding froma761f3eis dropped by ConPTY (it doesn't recognize code 13 in~format), so the keypress never reaches the child process.This PR fixes both the client-side detection and server-side delivery for the VS Code + ConPTY + Node.js case.
Root cause
Two separate issues in the VS Code terminal path:
Client: VS Code's xterm.js sends
\x1b\r(ESC + CR) for Shift+Enter. ConPTY interprets the ESC prefix as Alt, so crossterm reports Alt+Enter instead of Shift+Enter.Server: Node.js apps use libuv's
uv_tty_read_raw, which translatesVK_RETURNto\rregardless of modifier flags. Both the CSI\x1b[13;2~encoding (dropped by ConPTY) and win32-input-modeKEY_EVENT_RECORDwithSHIFT_PRESSED(libuv ignores shift on Enter) fail to deliver the modifier.Fix
Client (
platform.rs):augment_enter_shiftpollsGetAsyncKeyState(VK_SHIFT)to detect the physical Shift key and remaps crossterm's misreported Alt+Enter back to Shift+Enter.Server (
input.rs): Sends\x1b\r(ESC + CR) for Shift+Enter — the same bytes VS Code's xterm.js sends. The round-trip works because libuv does preserve Alt as an ESC prefix:Trade-offs
GetAsyncKeyStateis a heuristic — there's a small race window between keypress and event processing. In practice this is reliable for interactive use.\x1b\rfor Shift+Enter, which ConPTY interprets as Alt+Enter.User setup
For full Shift+Enter support in VS Code, users also need:
VS Code keybindings.json — ensures VS Code sends
\x1b\rfor Shift+Enter in terminal:{ "key": "shift+enter", "command": "workbench.action.terminal.sendSequence", "args": { "text": "\u001b\r" }, "when": "terminalFocus && terminalShellType != 'pwsh'" }PowerShell profile (
$PROFILE) — maps Alt+Enter to AddLine since ConPTY delivers the key as Alt+Enter:Test plan