@@ -22,6 +22,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed WSL clipboard image paste by passing the PowerShell save path directly instead of through a custom environment variable ([#2469](https://github.com/badlogic/pi-mono/issues/2469)).
|
||||
- Fixed Google Vertex Gemini 3 tool call replay for unsigned tool calls ([#4032](https://github.com/badlogic/pi-mono/issues/4032)).
|
||||
- Fixed blocked `edit` tool results rendering the rejection reason twice after interactive extension confirmation ([#3830](https://github.com/badlogic/pi-mono/issues/3830)).
|
||||
- Fixed extension-triggered thinking level changes refreshing the interactive editor border immediately ([#3888](https://github.com/badlogic/pi-mono/issues/3888)).
|
||||
|
||||
@@ -172,17 +172,17 @@ function readClipboardImageViaPowerShell(): ClipboardImage | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
const psQuotedWinPath = winPath.replaceAll("'", "''");
|
||||
const psScript = [
|
||||
"Add-Type -AssemblyName System.Windows.Forms",
|
||||
"Add-Type -AssemblyName System.Drawing",
|
||||
"$path = $env:PI_WSL_CLIPBOARD_IMAGE_PATH",
|
||||
`$path = '${psQuotedWinPath}'`,
|
||||
"$img = [System.Windows.Forms.Clipboard]::GetImage()",
|
||||
"if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }",
|
||||
].join("; ");
|
||||
|
||||
const result = runCommand("powershell.exe", ["-NoProfile", "-Command", psScript], {
|
||||
timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,
|
||||
env: { ...process.env, PI_WSL_CLIPBOARD_IMAGE_PATH: winPath },
|
||||
});
|
||||
if (!result.ok) {
|
||||
return null;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SpawnSyncReturns } from "child_process";
|
||||
import { writeFileSync } from "fs";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
@@ -107,6 +108,43 @@ describe("readClipboardImage", () => {
|
||||
expect(Array.from(result?.bytes ?? [])).toEqual([9, 8]);
|
||||
});
|
||||
|
||||
test("WSL: passes PowerShell path directly instead of through a custom env var", async () => {
|
||||
mocks.clipboard.hasImage.mockImplementation(() => {
|
||||
throw new Error("clipboard.hasImage should not be called before PowerShell on WSL");
|
||||
});
|
||||
|
||||
let tmpFile: string | undefined;
|
||||
mocks.spawnSync.mockImplementation((command, args, options) => {
|
||||
if (command === "wl-paste" || command === "xclip") {
|
||||
return spawnOk(Buffer.alloc(0));
|
||||
}
|
||||
|
||||
if (command === "wslpath") {
|
||||
tmpFile = args[1];
|
||||
return spawnOk(Buffer.from("C:\\Users\\O'Hare\\clip.png\n", "utf-8"));
|
||||
}
|
||||
|
||||
if (command === "powershell.exe") {
|
||||
const spawnOptions = options as { env?: NodeJS.ProcessEnv };
|
||||
expect(spawnOptions.env?.PI_WSL_CLIPBOARD_IMAGE_PATH).toBeUndefined();
|
||||
expect(args[2]).toContain("$path = 'C:\\Users\\O''Hare\\clip.png'");
|
||||
if (!tmpFile) {
|
||||
throw new Error("wslpath should be called before powershell.exe");
|
||||
}
|
||||
writeFileSync(tmpFile, Buffer.from([4, 5, 6]));
|
||||
return spawnOk(Buffer.from("ok\n", "utf-8"));
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected spawnSync call: ${command} ${args.join(" ")}`);
|
||||
});
|
||||
|
||||
const { readClipboardImage } = await import("../src/utils/clipboard-image.js");
|
||||
const result = await readClipboardImage({ platform: "linux", env: { WSL_DISTRO_NAME: "Ubuntu" } });
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.mimeType).toBe("image/png");
|
||||
expect(Array.from(result?.bytes ?? [])).toEqual([4, 5, 6]);
|
||||
});
|
||||
|
||||
test("Non-Wayland: uses clipboard", async () => {
|
||||
mocks.spawnSync.mockImplementation(() => {
|
||||
throw new Error("spawnSync should not be called for non-Wayland sessions");
|
||||
|
||||
Reference in New Issue
Block a user