@@ -30,15 +30,53 @@ export namespace Clipboard {
3030 }
3131
3232 if ( os === "win32" || release ( ) . includes ( "WSL" ) ) {
33- const script =
34- "Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [System.Convert]::ToBase64String($ms.ToArray()) }"
35- const base64 = await $ `powershell.exe -NonInteractive -NoProfile -command "${ script } "` . nothrow ( ) . text ( )
36- if ( base64 ) {
37- const imageBuffer = Buffer . from ( base64 . trim ( ) , "base64" )
38- if ( imageBuffer . length > 0 ) {
39- return { data : imageBuffer . toString ( "base64" ) , mime : "image/png" }
33+ // Helper: encode PowerShell script as base64 UTF-16LE for -EncodedCommand
34+ // This avoids ALL quoting/escaping issues with -command "..." which breaks
35+ // when clipboard content ends with backslash sequences (e.g., "c:\path\file.png")
36+ const encodePS = ( script : string ) => Buffer . from ( script , "utf16le" ) . toString ( "base64" )
37+
38+ // Try to get image from Windows clipboard via PowerShell
39+ const imgScript = `
40+ Add-Type -AssemblyName System.Windows.Forms
41+ $img = [System.Windows.Forms.Clipboard]::GetImage()
42+ if ($img) {
43+ $ms = New-Object System.IO.MemoryStream
44+ $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
45+ [System.Convert]::ToBase64String($ms.ToArray())
46+ }
47+ ` . trim ( )
48+ const imgEncoded = encodePS ( imgScript )
49+ const imgOut = ( await $ `powershell.exe -NonInteractive -NoProfile -EncodedCommand ${ imgEncoded } ` . nothrow ( ) . text ( ) ) . trim ( )
50+ if ( imgOut ) {
51+ try {
52+ const buf = Buffer . from ( imgOut , "base64" )
53+ // Validate PNG magic bytes to prevent garbage PowerShell output from being treated as image
54+ const isPng = buf . length >= 8 &&
55+ buf [ 0 ] === 0x89 && buf [ 1 ] === 0x50 && buf [ 2 ] === 0x4e && buf [ 3 ] === 0x47 &&
56+ buf [ 4 ] === 0x0d && buf [ 5 ] === 0x0a && buf [ 6 ] === 0x1a && buf [ 7 ] === 0x0a
57+ if ( isPng ) {
58+ return { data : buf . toString ( "base64" ) , mime : "image/png" }
59+ }
60+ } catch {
61+ // Invalid base64, fall through to text
4062 }
4163 }
64+
65+ // Get TEXT from Windows clipboard via PowerShell
66+ // CRITICAL: On WSL2, clipboardy uses Linux clipboard tools (xclip/wl-paste) which
67+ // can't access Windows clipboard. We MUST use PowerShell to read Windows clipboard text.
68+ // Using -EncodedCommand to avoid quoting issues with trailing backslashes in clipboard content.
69+ const textScript = `
70+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
71+ try { Get-Clipboard -Raw } catch { "" }
72+ ` . trim ( )
73+ const textEncoded = encodePS ( textScript )
74+ const text = ( await $ `powershell.exe -NonInteractive -NoProfile -EncodedCommand ${ textEncoded } ` . nothrow ( ) . text ( ) )
75+ . replace ( / \r \n / g, "\n" )
76+ . replace ( / \r / g, "\n" )
77+ if ( text && text . trim ( ) ) {
78+ return { data : text , mime : "text/plain" }
79+ }
4280 }
4381
4482 if ( os === "linux" ) {
0 commit comments