From 56c89650024cfa581ea462eccbd76b76fb33c569 Mon Sep 17 00:00:00 2001 From: bornmw Date: Fri, 29 May 2026 23:55:58 -0400 Subject: [PATCH] feat(opencode): add linux_clipboard_selection config for primary buffer support (fixes #29963) --- .../src/cli/cmd/tui/config/tui-schema.ts | 1 + .../src/cli/cmd/tui/util/clipboard.ts | 43 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts index 3cdfe8770aa4..8b3490a79478 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts @@ -85,4 +85,5 @@ export const TuiInfo = Schema.Struct({ scroll_acceleration: Schema.optional(ScrollAcceleration), diff_style: Schema.optional(DiffStyle), mouse: Schema.optional(Schema.Boolean).annotate({ description: "Enable or disable mouse capture (default: true)" }), + linux_clipboard_selection: Schema.optional(Schema.Literals(["clipboard", "primary", "both"])).annotate({ description: "Linux clipboard selection: 'clipboard' (Ctrl+C), 'primary' (middle-click), or 'both' (default: 'both')" }), }) diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts index be3cec14c6a2..8b396c27467e 100644 --- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts +++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts @@ -8,6 +8,7 @@ import { ChildProcess } from "effect/unstable/process" import { AppProcess } from "@opencode-ai/core/process" import * as Filesystem from "../../../../util/filesystem" import * as Process from "../../../../util/process" +import { get } from "../config/tui" const writeWithStdin = (cmd: string[], text: string): Promise => Effect.runPromise( @@ -135,16 +136,52 @@ const getCopyMethod = lazy(async () => { } if (os === "linux") { + const config = await get() + const selection = config.linux_clipboard_selection ?? "both" + if (process.env["WAYLAND_DISPLAY"] && which("wl-copy")) { - console.log("clipboard: using wl-copy") + console.log(`clipboard: using wl-copy (${selection})`) + if (selection === "primary") { + return (text: string) => writeWithStdin(["wl-copy", "-p"], text) + } + if (selection === "both") { + return async (text: string) => { + await Promise.allSettled([ + writeWithStdin(["wl-copy", "-p"], text), + writeWithStdin(["wl-copy"], text), + ]) + } + } return (text: string) => writeWithStdin(["wl-copy"], text) } if (which("xclip")) { - console.log("clipboard: using xclip") + console.log(`clipboard: using xclip (${selection})`) + if (selection === "primary") { + return (text: string) => writeWithStdin(["xclip", "-selection", "primary"], text) + } + if (selection === "both") { + return async (text: string) => { + await Promise.allSettled([ + writeWithStdin(["xclip", "-selection", "primary"], text), + writeWithStdin(["xclip", "-selection", "clipboard"], text), + ]) + } + } return (text: string) => writeWithStdin(["xclip", "-selection", "clipboard"], text) } if (which("xsel")) { - console.log("clipboard: using xsel") + console.log(`clipboard: using xsel (${selection})`) + if (selection === "primary") { + return (text: string) => writeWithStdin(["xsel", "--primary", "--input"], text) + } + if (selection === "both") { + return async (text: string) => { + await Promise.allSettled([ + writeWithStdin(["xsel", "--primary", "--input"], text), + writeWithStdin(["xsel", "--clipboard", "--input"], text), + ]) + } + } return (text: string) => writeWithStdin(["xsel", "--clipboard", "--input"], text) } }