@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
### Fixed
|
### 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 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 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)).
|
- 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const psQuotedWinPath = winPath.replaceAll("'", "''");
|
||||||
const psScript = [
|
const psScript = [
|
||||||
"Add-Type -AssemblyName System.Windows.Forms",
|
"Add-Type -AssemblyName System.Windows.Forms",
|
||||||
"Add-Type -AssemblyName System.Drawing",
|
"Add-Type -AssemblyName System.Drawing",
|
||||||
"$path = $env:PI_WSL_CLIPBOARD_IMAGE_PATH",
|
`$path = '${psQuotedWinPath}'`,
|
||||||
"$img = [System.Windows.Forms.Clipboard]::GetImage()",
|
"$img = [System.Windows.Forms.Clipboard]::GetImage()",
|
||||||
"if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }",
|
"if ($img) { $img.Save($path, [System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'ok' } else { Write-Output 'empty' }",
|
||||||
].join("; ");
|
].join("; ");
|
||||||
|
|
||||||
const result = runCommand("powershell.exe", ["-NoProfile", "-Command", psScript], {
|
const result = runCommand("powershell.exe", ["-NoProfile", "-Command", psScript], {
|
||||||
timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,
|
timeoutMs: DEFAULT_POWERSHELL_TIMEOUT_MS,
|
||||||
env: { ...process.env, PI_WSL_CLIPBOARD_IMAGE_PATH: winPath },
|
|
||||||
});
|
});
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { SpawnSyncReturns } from "child_process";
|
import type { SpawnSyncReturns } from "child_process";
|
||||||
|
import { writeFileSync } from "fs";
|
||||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
const mocks = vi.hoisted(() => {
|
const mocks = vi.hoisted(() => {
|
||||||
@@ -107,6 +108,43 @@ describe("readClipboardImage", () => {
|
|||||||
expect(Array.from(result?.bytes ?? [])).toEqual([9, 8]);
|
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 () => {
|
test("Non-Wayland: uses clipboard", async () => {
|
||||||
mocks.spawnSync.mockImplementation(() => {
|
mocks.spawnSync.mockImplementation(() => {
|
||||||
throw new Error("spawnSync should not be called for non-Wayland sessions");
|
throw new Error("spawnSync should not be called for non-Wayland sessions");
|
||||||
|
|||||||
Reference in New Issue
Block a user