Skip to content

Fix Windows standalone paste: hidden helper spawns + native clipboard#205

Merged
nedtwigg merged 2 commits into
mainfrom
win-copy-paste
Jul 4, 2026
Merged

Fix Windows standalone paste: hidden helper spawns + native clipboard#205
nedtwigg merged 2 commits into
mainfrom
win-copy-paste

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Jul 3, 2026

Copy link
Copy Markdown
Member

Problem

On Windows standalone, a Ctrl+V paste locked the GUI and spawned console windows that flickered and vanished. VS Code and macOS standalone were fine.

Root cause: every clipboard read shelled out to powershell.exe via the Node sidecar. The sidecar runs as a windowless GUI child, so each console subprocess got a fresh console window allocated. doPaste fires two reads (file paths + text) on every paste, so several windows flashed and stole focus at once. VS Code (vscode.env.clipboard) and macOS (pbpaste/osascript) never spawn a console, which is why only Windows standalone broke.

Fix (two commits)

1. Spawn all sidecar helper CLIs with windowsHide — pass windowsHide: true (libuv CREATE_NO_WINDOW) on every child_process call in the sidecar so no console window is ever allocated. Wrapped execFileP in clipboard-ops.js so all native reads inherit it, added it to the writeViaStdin spawn, and covered the remaining POSIX lsof/ps calls in pty-core.js (the Windows reg/powershell/netstat calls already set it). Harmless no-op off Windows.

2. Read the clipboard natively in Rust on Windows — even hidden, each paste round-tripped Rust → sidecar → powershell.exe, and PowerShell's cold start made Ctrl+V feel sluggish. New clipboard_win module reads the Win32 clipboard directly: CF_UNICODETEXT for text, CF_HDROP/DragQueryFileW for file paths, CF_DIB wrapped in a BITMAPFILEHEADER → temp .bmp for images. A paste is now a few in-process API calls with no subprocess at all. The three read_clipboard_* Tauri commands use this on Windows and keep the sidecar path on macOS/Linux, where pbpaste/xclip never pop a console window and already work well.

Scope note

macOS/Linux are deliberately left on the sidecar — they don't have the console-window problem. Clipboard writes (copy) still go through navigator.clipboard in the webview; the sidecar clip write (only used by the agent-browser edit channel) is now flicker-free from commit 1.

Tests

  • Rust unit tests cover the DIB→BMP file-header math (BI_RGB, full palette, BI_BITFIELDS, truncated input); cargo check/cargo test are warning-free.
  • Sidecar JS tests updated for the windowsHide option, including a regression guard that the clipboard-write spawn passes it. (Pre-existing resolveSpawnConfig path-separator test failures on Windows are unrelated to this change.)

🤖 Generated with Claude Code

nedtwigg and others added 2 commits July 3, 2026 09:17
…icker

On Windows standalone, Ctrl+V locked the GUI and spawned console windows
that flickered and vanished. The sidecar runs as a windowless GUI child,
so shelling out to a console CLI without CREATE_NO_WINDOW allocates a fresh
console window per process. A single paste fans out to 2-3 PowerShell reads
(file paths + text, then image) via clipboard-ops, so several windows flash
and steal focus at once. VS Code (vscode.env.clipboard) and macOS
(pbpaste/osascript) never spawn a console, which is why only Windows
standalone broke.

Pass windowsHide: true (libuv CREATE_NO_WINDOW) on every child_process call
in the sidecar: wrap execFileP in clipboard-ops so all native reads inherit
it, add it to the writeViaStdin spawn, and cover the remaining POSIX lsof/ps
calls in pty-core (the Windows reg/powershell/netstat calls already set it).
Harmless no-op off Windows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ecar

Follow-up to the windowsHide fix: even hidden, each paste round-tripped
Rust -> Node sidecar -> powershell.exe (Get-Clipboard), and PowerShell's
cold start made Ctrl+V feel sluggish since doPaste issues two reads (file
paths + text) on every paste.

Add a Windows-only clipboard_win module that reads the Win32 clipboard
directly: CF_UNICODETEXT for text, CF_HDROP (DragQueryFileW) for file
paths, and CF_DIB wrapped in a BITMAPFILEHEADER -> temp .bmp for images.
A paste is now a few in-process API calls with no subprocess at all.

The three read_clipboard_* Tauri commands call this natively on Windows
and keep the sidecar path on macOS/Linux, where pbpaste/xclip never pop a
console window and already work well. Unit-tests cover the DIB->BMP pixel
offset math (BI_RGB, palette, BI_BITFIELDS, truncated input).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7300a88
Status: ✅  Deploy successful!
Preview URL: https://8ab652a3.mouseterm.pages.dev
Branch Preview URL: https://win-copy-paste.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change reads well and the fix is sound: the two-pronged approach (hide every sidecar console spawn, then bypass the sidecar entirely on Windows) targets the root cause precisely, and the windowsHide audit across pty-core.js is complete — every remaining synchronous PowerShell/reg/netstat/lsof/ps spawn already carries the flag. The DIB→BMP header math in bmp_file_header is the subtle part and the unit tests cover the cases that matter (BI_RGB 32bpp, implicit 8bpp palette, BI_BITFIELDS masks, truncated input); the offset arithmetic checks out for BITMAPINFOHEADER and V4/V5 alike.

One observation, not blocking: the entire clipboard_win module is #[cfg(windows)], and PR CI (ci.yml) runs only on ubuntu-latestrelease.yml is the sole job that builds the Windows target and it fires only on push: tags. So on a pull request, nothing compiles or runs this module: not the byte-offset header math, not its four unit tests, not even a type-check. A compile error or a regression in the trickiest logic in the PR would surface only at release time.

bmp_file_header (and its tests submodule) is pure — it operates on byte slices with no Win32 dependency. Lifting it into a small platform-neutral module (or gating just the Win32-touching functions rather than the whole file) would let the Ubuntu PR run compile and exercise that logic on every push, which is where the real risk in this change lives. Your call on whether it's worth the restructure.

@nedtwigg nedtwigg merged commit 98a9d8c into main Jul 4, 2026
6 checks passed
@nedtwigg nedtwigg deleted the win-copy-paste branch July 4, 2026 05:01
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