diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index a8f3b01d943e..1d056da9363b 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -590,14 +590,15 @@ export function Prompt(props: PromptProps) { }) } - function setVimRegister(register: VimRegister, notify = false) { + async function setVimRegister(register: VimRegister, notify = false) { if (!useSystemClipboardRegister()) { vimState.setRegister(register) return } clipboardRegister = register if (!register) return - Clipboard.copy(register.text) + + await Clipboard.copy(register.text) .then(() => { if (notify) toast.show({ message: "Copied to clipboard", variant: "info" }) }) @@ -606,8 +607,9 @@ export function Prompt(props: PromptProps) { async function syncVimRegisterFromClipboard() { if (!useSystemClipboardRegister()) return - const content = await Clipboard.read() - if (content?.mime !== "text/plain" || !content.data) { + const content = await Clipboard.read().catch(() => undefined) + if (!content) return + if (content.mime !== "text/plain" || !content.data) { clipboardRegister = null return } diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts index c34563411e50..8e10a14fb02e 100644 --- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts +++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts @@ -104,15 +104,35 @@ export async function read(): Promise { } if (os === "linux") { - const wayland = await Process.run(["wl-paste", "-t", "image/png"], { nothrow: true }) - if (wayland.stdout.byteLength > 0) { - return { data: Buffer.from(wayland.stdout).toString("base64"), mime: "image/png" } + const which = await getWhich() + const hasWlPaste = !!process.env["WAYLAND_DISPLAY"] && which("wl-paste") + + if (hasWlPaste) { + const wayland = await Process.run(["wl-paste", "-t", "image/png"], { nothrow: true }) + if (wayland.stdout.byteLength > 0) { + return { data: Buffer.from(wayland.stdout).toString("base64"), mime: "image/png" } + } } - const x11 = await Process.run(["xclip", "-selection", "clipboard", "-t", "image/png", "-o"], { - nothrow: true, - }) - if (x11.stdout.byteLength > 0) { - return { data: Buffer.from(x11.stdout).toString("base64"), mime: "image/png" } + + if (!hasWlPaste) { + const x11 = await Process.run(["xclip", "-selection", "clipboard", "-t", "image/png", "-o"], { + nothrow: true, + }) + if (x11.stdout.byteLength > 0) { + return { data: Buffer.from(x11.stdout).toString("base64"), mime: "image/png" } + } + } + + // Read text via native tools before falling back to clipboardy + if (hasWlPaste) { + const text = await Process.text(["wl-paste", "--no-newline"], { nothrow: true }) + if (text.text) return { data: text.text, mime: "text/plain" } + } else if (which("xclip")) { + const text = await Process.text(["xclip", "-selection", "clipboard", "-o"], { nothrow: true }) + if (text.text) return { data: text.text, mime: "text/plain" } + } else if (which("xsel")) { + const text = await Process.text(["xsel", "--clipboard", "--output"], { nothrow: true }) + if (text.text) return { data: text.text, mime: "text/plain" } } }