mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): require advertised X11 clipboard images
closes #9786
This commit is contained in:
parent
b45597504e
commit
8676a0dcd8
3 changed files with 41 additions and 20 deletions
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
- Fixed X11 clipboard text being misidentified as an image when the clipboard owner accepts unadvertised image targets ([#9786](https://github.com/earendil-works/pi/issues/9786)).
|
||||
- Prevented managed git packages from automatically installing Pi peer dependencies and added warnings for extension packages that list host-provided modules in `dependencies` ([#9863](https://github.com/earendil-works/pi/issues/9863)).
|
||||
|
||||
## [0.87.1] - 2026-09-22
|
||||
|
|
|
|||
|
|
@ -174,27 +174,20 @@ async function readClipboardImageViaXclip(): Promise<ClipboardImage | null | und
|
|||
timeoutMs: DEFAULT_LIST_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
let candidateTypes: string[] = [];
|
||||
if (targets !== undefined) {
|
||||
candidateTypes = targets
|
||||
.toString("utf-8")
|
||||
.split(/\r?\n/)
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
if (targets === undefined) return undefined;
|
||||
|
||||
const candidateTypes = targets
|
||||
.toString("utf-8")
|
||||
.split(/\r?\n/)
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean);
|
||||
const preferred = selectPreferredImageMimeType(candidateTypes);
|
||||
if (targets !== undefined && !preferred) return null;
|
||||
const tryTypes = new Set(preferred ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : SUPPORTED_IMAGE_MIME_TYPES);
|
||||
if (!preferred) return null;
|
||||
|
||||
for (const mimeType of tryTypes) {
|
||||
const data = await runClipboardCommand("xclip", ["-selection", "clipboard", "-t", mimeType, "-o"]);
|
||||
if (data !== undefined && data.length > 0) {
|
||||
return { bytes: data, mimeType: baseMimeType(mimeType) };
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
const data = await runClipboardCommand("xclip", ["-selection", "clipboard", "-t", preferred, "-o"]);
|
||||
if (data === undefined) return undefined;
|
||||
if (data.length === 0) return null;
|
||||
return { bytes: data, mimeType: baseMimeType(preferred) };
|
||||
}
|
||||
|
||||
async function readClipboardImageViaNativeClipboard(): Promise<ClipboardImage | null | undefined> {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,33 @@ describe("readClipboardImage", () => {
|
|||
});
|
||||
}
|
||||
|
||||
test("X11 does not probe image types when TARGETS fails", async () => {
|
||||
// Regression test for #9786.
|
||||
mocks.getImage.mockResolvedValue(null);
|
||||
mocks.command.mockImplementation(async (_command, args) =>
|
||||
args.includes("TARGETS") ? undefined : Buffer.from("hello"),
|
||||
);
|
||||
expect(await readClipboardImage({ platform: "linux", env: { DISPLAY: ":0" } })).toBeNull();
|
||||
expect(mocks.command).toHaveBeenCalledExactlyOnceWith(
|
||||
"xclip",
|
||||
["-selection", "clipboard", "-t", "TARGETS", "-o"],
|
||||
{ timeoutMs: 1000 },
|
||||
);
|
||||
expect(mocks.getImage).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
test("X11 does not probe unadvertised image types", async () => {
|
||||
mocks.getImage.mockResolvedValue(null);
|
||||
mocks.command.mockImplementation(async (_command, args) => {
|
||||
if (args.includes("TARGETS")) return Buffer.from("image/png\n");
|
||||
if (args.includes("image/png")) return undefined;
|
||||
return Buffer.from("hello");
|
||||
});
|
||||
expect(await readClipboardImage({ platform: "linux", env: { DISPLAY: ":0" } })).toBeNull();
|
||||
expect(mocks.command.mock.calls.map(([, args]) => args[3])).toEqual(["TARGETS", "image/png"]);
|
||||
expect(mocks.getImage).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
test.each([png, null, new Uint8Array()])("native X11 result %j stops fallback", async (bytes) => {
|
||||
mocks.getImage.mockResolvedValue(bytes);
|
||||
expect(await readClipboardImage({ platform: "linux", env: { DISPLAY: ":0" } })).toEqual(
|
||||
|
|
@ -51,7 +78,7 @@ describe("readClipboardImage", () => {
|
|||
);
|
||||
expect(mocks.getNativeClipboard).toHaveBeenCalledExactlyOnceWith();
|
||||
expect(mocks.getImage).toHaveBeenCalledOnce();
|
||||
expect(mocks.command.mock.calls.map(([name]) => name)).toEqual(Array<string>(5).fill("xclip"));
|
||||
expect(mocks.command.mock.calls.map(([name]) => name)).toEqual(["xclip"]);
|
||||
});
|
||||
test.each(["missing module", "unavailable display"])("Wayland: falls back to X11 after %s", async (failure) => {
|
||||
if (failure === "missing module") mocks.getNativeClipboard.mockReturnValue(undefined);
|
||||
|
|
@ -121,7 +148,7 @@ describe("readClipboardImage", () => {
|
|||
error,
|
||||
);
|
||||
expect(mocks.command.mock.calls.map(([name]) => name)).toEqual(
|
||||
platform === "linux" ? ["wl-paste", ...Array<string>(5).fill("xclip")] : [],
|
||||
platform === "linux" ? ["wl-paste", "xclip"] : [],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue