diff --git a/src/server/clipboard.test.ts b/src/server/clipboard.test.ts index 3454776..1eac1e1 100644 --- a/src/server/clipboard.test.ts +++ b/src/server/clipboard.test.ts @@ -91,11 +91,28 @@ describe("Clipboard helper", () => { expect(text).toBe("windows clipboard text") expect(spawn).toHaveBeenCalledWith( "powershell.exe", - expect.arrayContaining(["Get-Clipboard -Raw"]), - expect.any(Object), + expect.arrayContaining([ + expect.stringMatching( + /\[Console\]::OutputEncoding\s*=\s*\[System\.Text\.Encoding\]::UTF8;\s*Get-Clipboard -Raw/, + ), + ]), + expect.objectContaining({ windowsHide: true }), ) }) + it("should keep a character split across stdout chunks", async () => { + vi.mocked(os.platform).mockReturnValue("win32") + const proc = createMockProcess({}) + vi.mocked(spawn).mockReturnValue(proc) + const rocket = Buffer.from("🚀") + + const pending = getSystemClipboard() + proc.stdout.emit("data", rocket.subarray(0, 2)) + proc.stdout.emit("data", rocket.subarray(2)) + + await expect(pending).resolves.toBe("🚀") + }) + it("should read clipboard on linux using fallback when first command fails", async () => { vi.mocked(os.platform).mockReturnValue("linux") const origEnv = process.env.XDG_SESSION_TYPE diff --git a/src/server/clipboard.ts b/src/server/clipboard.ts index e033c53..ed3e8b1 100644 --- a/src/server/clipboard.ts +++ b/src/server/clipboard.ts @@ -34,17 +34,20 @@ function spawnWithStdin( */ function spawnCaptureOutput(cmd: string, args: string[]): Promise { return new Promise((resolve, reject) => { - const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "ignore"] }) - let output = "" + const child = spawn(cmd, args, { + stdio: ["ignore", "pipe", "ignore"], + windowsHide: true, + }) + const chunks: Buffer[] = [] child.stdout.on("data", (chunk: Buffer) => { - output += chunk.toString("utf-8") + chunks.push(chunk) }) child.on("error", reject) child.on("close", (code) => { - if (code === 0) resolve(output) + if (code === 0) resolve(Buffer.concat(chunks).toString("utf-8")) else reject(new Error(`${cmd} exited with code ${code}`)) }) }) @@ -64,7 +67,7 @@ export async function getSystemClipboard(): Promise { const text = await spawnCaptureOutput("powershell.exe", [ "-NoProfile", "-Command", - "Get-Clipboard -Raw", + "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Clipboard -Raw", ]) return text.replace(/\r?\n$/, "") }