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 4558914cb7e..27678b7a9c4 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -796,6 +796,35 @@ export function Prompt(props: PromptProps) { } // If no image, let the default paste behavior continue } + // Handle copy (Ctrl+Shift+C) - copy selected text to clipboard + if (keybind.match("input_copy", e)) { + if (input.hasSelection()) { + const selectedText = input.getSelectedText() + if (selectedText) { + await Clipboard.copy(selectedText).catch((error) => { + console.error(`Failed to copy selected text to clipboard: ${error}`) + }) + e.preventDefault() + } + } + return + } + // Handle cut (Ctrl+Shift+X) - copy selected text and delete it + if (keybind.match("input_cut", e)) { + if (input.hasSelection()) { + const selectedText = input.getSelectedText() + if (selectedText) { + await Clipboard.copy(selectedText).catch((error) => { + console.error(`Failed to copy selected text to clipboard: ${error}`) + }) + // Delete the selected text using the public deleteChar() method + // which internally calls deleteSelectedText() when there's a selection + input.deleteChar() + e.preventDefault() + } + } + return + } if (keybind.match("input_clear", e) && store.prompt.input !== "") { input.clear() input.extmarks.clear() diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index ead3a0149b4..204b0c922fe 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -665,6 +665,8 @@ export namespace Config { variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"), input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"), input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"), + input_copy: z.string().optional().default("ctrl+insert").describe("Copy selected text from input"), + input_cut: z.string().optional().default("shift+delete").describe("Cut selected text from input"), input_submit: z.string().optional().default("return").describe("Submit input"), input_newline: z .string() diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 9cb7222aa5f..702d52afa52 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1081,6 +1081,14 @@ export type KeybindsConfig = { * Paste from clipboard */ input_paste?: string + /** + * Copy selected text from input + */ + input_copy?: string + /** + * Cut selected text from input + */ + input_cut?: string /** * Submit input */ diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 1bc7bd5c2c8..c7f0db5018a 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -8305,6 +8305,16 @@ "default": "ctrl+v", "type": "string" }, + "input_copy": { + "description": "Copy selected text from input", + "default": "ctrl+insert", + "type": "string" + }, + "input_cut": { + "description": "Cut selected text from input", + "default": "shift+delete", + "type": "string" + }, "input_submit": { "description": "Submit input", "default": "return",