From a174578673283e2fb2a7177b805c54deaab35d6f Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Thu, 8 Jan 2026 17:44:29 +0300 Subject: [PATCH] feat: add wayland clipboard support --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/src/utils/clipboard.ts | 39 ++++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 9e804db4..4b7e60cf 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -9,6 +9,7 @@ - `setActiveTools()` in ExtensionAPI for dynamic tool management - Built-in renderers used automatically for tool overrides without custom `renderCall`/`renderResult` - `ssh.ts` example: remote tool execution via `--ssh user@host:/path` +- Wayland clipboard support for `/copy` command using wl-copy with xclip/xsel fallback ([#570](https://github.com/badlogic/pi-mono/pull/570) by [@OgulcanCelik](https://github.com/OgulcanCelik)) ### Fixed diff --git a/packages/coding-agent/src/utils/clipboard.ts b/packages/coding-agent/src/utils/clipboard.ts index 5baf954a..33e5bc2c 100644 --- a/packages/coding-agent/src/utils/clipboard.ts +++ b/packages/coding-agent/src/utils/clipboard.ts @@ -1,5 +1,6 @@ -import { execSync } from "child_process"; +import { execSync, spawn } from "child_process"; import { platform } from "os"; +import { isWaylandSession } from "./clipboard-image.js"; export function copyToClipboard(text: string): void { const p = platform(); @@ -11,17 +12,41 @@ export function copyToClipboard(text: string): void { } else if (p === "win32") { execSync("clip", options); } else { - // Linux - try xclip first, fall back to xsel - try { - execSync("xclip -selection clipboard", options); - } catch { - execSync("xsel --clipboard --input", options); + // Linux - try wl-copy for Wayland, fall back to xclip/xsel for X11 + const isWayland = isWaylandSession(); + if (isWayland) { + try { + // Verify wl-copy exists (spawn errors are async and won't be caught) + execSync("which wl-copy", { stdio: "ignore" }); + // wl-copy with execSync hangs due to fork behavior; use spawn instead + const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] }); + proc.stdin.on("error", () => { + // Ignore EPIPE errors if wl-copy exits early + }); + proc.stdin.write(text); + proc.stdin.end(); + proc.unref(); + } catch { + // Fall back to xclip/xsel (works on XWayland) + try { + execSync("xclip -selection clipboard", options); + } catch { + execSync("xsel --clipboard --input", options); + } + } + } else { + try { + execSync("xclip -selection clipboard", options); + } catch { + execSync("xsel --clipboard --input", options); + } } } } catch (error) { const msg = error instanceof Error ? error.message : String(error); if (p === "linux") { - throw new Error(`Failed to copy to clipboard. Install xclip or xsel: ${msg}`); + const tools = isWaylandSession() ? "wl-copy, xclip, or xsel" : "xclip or xsel"; + throw new Error(`Failed to copy to clipboard. Install ${tools}: ${msg}`); } throw new Error(`Failed to copy to clipboard: ${msg}`); }