mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): read Termux clipboard on Android platform
Termux reports process.platform as android, so the Linux-only branch skipped termux-clipboard-get and Ctrl+V pasted nothing. Also show the Termux:API hint when a Termux copy fails. fixes #10391
This commit is contained in:
parent
adae824645
commit
592fb57b70
3 changed files with 41 additions and 10 deletions
|
|
@ -24,6 +24,7 @@
|
|||
- Fixed codemode output items running together, so models could not tell where one `text()` or `console.log()` output ended and the next began. With several text items, each now starts with a `==> text N/M <==` line, and `console` calls follow the other output in one `<console_output>` block with one line per call
|
||||
- Fixed `/mcp` waiting for all servers to connect before opening; the manager now updates live and remains usable while enabling, reconnecting, or disabling servers ([#10562](https://github.com/earendil-works/pi/issues/10562))
|
||||
- Fixed images being dropped as "could not be resized" when running under `node --watch` on Node 24.19+ and 26.x, where Node posts its own messages on the image resize worker channel ([#10527](https://github.com/earendil-works/pi/issues/10527))
|
||||
- Fixed clipboard paste doing nothing in Termux, and failed copies there omitting the Termux:API install hint ([#10391](https://github.com/earendil-works/pi/issues/10391))
|
||||
|
||||
## [1.0.4] - 2026-10-05
|
||||
|
||||
|
|
|
|||
|
|
@ -52,17 +52,18 @@ async function copyViaWindowsClipboard(text: string): Promise<boolean> {
|
|||
|
||||
/** Read plain text from the system clipboard. */
|
||||
export async function readClipboardText(): Promise<string | null> {
|
||||
const commands: [string, string[]][] = [];
|
||||
// Termux reports platform "android", not "linux" (#10391).
|
||||
if (process.env.TERMUX_VERSION) commands.push(["termux-clipboard-get", []]);
|
||||
if (platform() === "linux") {
|
||||
const commands: [string, string[]][] = [];
|
||||
if (process.env.TERMUX_VERSION) commands.push(["termux-clipboard-get", []]);
|
||||
if (process.env.WAYLAND_DISPLAY) commands.push(["wl-paste", ["--no-newline", "--type", "text"]]);
|
||||
if (process.env.DISPLAY) {
|
||||
commands.push(["xclip", ["-selection", "clipboard", "-out"]], ["xsel", ["--clipboard", "--output"]]);
|
||||
}
|
||||
for (const [command, args] of commands) {
|
||||
const bytes = await runClipboardCommand(command, args, { timeoutMs: 5000 });
|
||||
if (bytes !== undefined) return bytes.toString("utf8") || null;
|
||||
}
|
||||
}
|
||||
for (const [command, args] of commands) {
|
||||
const bytes = await runClipboardCommand(command, args, { timeoutMs: 5000 });
|
||||
if (bytes !== undefined) return bytes.toString("utf8") || null;
|
||||
}
|
||||
try {
|
||||
return (await getNativeClipboard()?.getText()) || null;
|
||||
|
|
@ -129,10 +130,10 @@ export async function copyToClipboard(text: string): Promise<void> {
|
|||
}
|
||||
if (copied) return;
|
||||
if (oversized) throw new Error("Clipboard unavailable: text exceeds the OSC 52 size limit");
|
||||
if (env.TERMUX_VERSION) {
|
||||
throw new Error("Clipboard unavailable: install the Termux:API app and `termux-api` package");
|
||||
}
|
||||
if (p === "linux") {
|
||||
if (env.TERMUX_VERSION) {
|
||||
throw new Error("Clipboard unavailable: install the Termux:API app and `termux-api` package");
|
||||
}
|
||||
if (env.WAYLAND_DISPLAY) {
|
||||
throw new Error("Clipboard unavailable: install `wl-clipboard` (`wl-copy`) or check Wayland access");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ describe("readClipboardText", () => {
|
|||
["WAYLAND_DISPLAY", "wl-paste", ["--no-newline", "--type", "text"], ["wl-paste"]],
|
||||
["DISPLAY", "xclip", ["-selection", "clipboard", "-out"], ["xclip"]],
|
||||
["DISPLAY", "xsel", ["--clipboard", "--output"], ["xclip", "xsel"]],
|
||||
["TERMUX_VERSION", "termux-clipboard-get", [], ["termux-clipboard-get"]],
|
||||
] as const) {
|
||||
test.each(["clipboard text", ""])(`${command} result %j stops fallback`, async (text) => {
|
||||
// Regression test for #7248: empty Wayland content must not fall through to stale X11.
|
||||
|
|
@ -100,6 +99,15 @@ describe("readClipboardText", () => {
|
|||
expect(mocks.getNativeClipboard).toHaveBeenCalledExactlyOnceWith();
|
||||
expect(mocks.command.mock.calls.map(([name]) => name)).toEqual(["wl-paste", "xclip", "xsel"]);
|
||||
});
|
||||
test.each(["clipboard text", ""])("Termux reads termux-clipboard-get result %j on Android", async (text) => {
|
||||
// Regression test for #10391: Termux reports platform "android".
|
||||
mocks.platform.mockReturnValue("android");
|
||||
vi.stubEnv("TERMUX_VERSION", "0.119");
|
||||
mocks.command.mockResolvedValue(Buffer.from(text));
|
||||
await expect(readClipboardText()).resolves.toBe(text || null);
|
||||
expect(mocks.command).toHaveBeenCalledExactlyOnceWith("termux-clipboard-get", [], { timeoutMs: 5000 });
|
||||
expect(mocks.getNativeClipboard).not.toHaveBeenCalled();
|
||||
});
|
||||
test("falls back to X11 tools when wl-paste is unavailable", async () => {
|
||||
mocks.platform.mockReturnValue("linux");
|
||||
vi.stubEnv("WAYLAND_DISPLAY", "wayland-0");
|
||||
|
|
@ -257,6 +265,27 @@ describe("copyToClipboard", () => {
|
|||
);
|
||||
expect(mocks.command.mock.calls.map(([name]) => name)).toEqual(["wl-copy", "xclip", "xsel"]);
|
||||
});
|
||||
test("Termux on Android writes through termux-clipboard-set", async () => {
|
||||
mocks.platform.mockReturnValue("android");
|
||||
vi.stubEnv("TERMUX_VERSION", "0.119");
|
||||
mocks.getNativeClipboard.mockReturnValue(undefined);
|
||||
await copyToClipboard("hello");
|
||||
expect(mocks.command).toHaveBeenCalledExactlyOnceWith("termux-clipboard-set", [], {
|
||||
input: "hello",
|
||||
timeoutMs: 5000,
|
||||
});
|
||||
expect(osc52Writes).toHaveLength(0);
|
||||
});
|
||||
test("reports the Termux:API requirement on Android", async () => {
|
||||
// Regression test for #10391: Termux reports platform "android".
|
||||
mocks.platform.mockReturnValue("android");
|
||||
vi.stubEnv("TERMUX_VERSION", "0.119");
|
||||
mocks.getNativeClipboard.mockReturnValue(undefined);
|
||||
mocks.command.mockResolvedValue(undefined);
|
||||
await expect(copyToClipboard("hello")).rejects.toThrow(
|
||||
"Clipboard unavailable: install the Termux:API app and `termux-api` package",
|
||||
);
|
||||
});
|
||||
test("uses OSC 52 when native and command writes fail in a remote session", async () => {
|
||||
vi.stubEnv("SSH_CONNECTION", "client server");
|
||||
mocks.clipboard.setText.mockRejectedValue(new Error("native failed"));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue