Skip to content

fix(tui): paste text when the terminal forwards Ctrl+V as a key event - #2

Open
NormalDudeBro wants to merge 6 commits into
bb-deeplearning:codemaxxxingfrom
NormalDudeBro:fix/ctrl-v-paste
Open

fix(tui): paste text when the terminal forwards Ctrl+V as a key event#2
NormalDudeBro wants to merge 6 commits into
bb-deeplearning:codemaxxxingfrom
NormalDudeBro:fix/ctrl-v-paste

Conversation

@NormalDudeBro

@NormalDudeBro NormalDudeBro commented Aug 7, 2026

Copy link
Copy Markdown

Problem

The TUI prompt's Ctrl+V paste silently does nothing on terminals that forward Ctrl+V to the app instead of pasting natively (e.g. Windows Terminal 1.25+ with the kitty keyboard protocol active). Two gaps combined:

  1. The prompt's input_paste keydown handler was image-only - for a text clipboard it read the clipboard, found no image, and fell through, while the terminal-side bracketed paste never fires because the key was forwarded. Text pastes were dropped entirely.
  2. Even that keydown branch could never run on key-forwarding terminals: the prompt.paste command registered the input_paste keybind, and the command dialog's global keypress listener runs before the focused textarea's handler. On a keybind match it calls preventDefault(), which skips the textarea's onKeyDown entirely - so the very terminals this targets never delivered the keypress to the paste code.

Fix

Three changes:

  • Route both paste signals through one shared pipeline: the bracketed-paste onPaste handler and the forwarded-Ctrl+V input_paste keydown branch both call handlePastedText(), so a forwarded Ctrl+V behaves identically to native bracketed paste - CRLF normalization, file/PDF/SVG paths become attachments, large text shows the [Pasted ~N lines] summary, and the layout flush runs.
  • Unbind prompt.paste (drop its keybind: "input_paste"): with the keybind registered, the command dialog's global listener intercepted every forwarded Ctrl+V and preventDefault()ed before the textarea's onKeyDown ran. With it removed, Ctrl+V flows straight to the textarea's paste branch. The command stays reachable via command.trigger("prompt.paste") for the image-only bracketed-paste fallback.
  • Read win32 clipboard text via PowerShell in clipboard.ts (see below).

Double-insert is not possible by construction: terminals that paste natively never deliver the Ctrl+V keydown, so the two paths are mutually exclusive.

Windows clipboard read in the compiled exe (clipboard.ts)

The forwarding fix exposed a second bug that only surfaces in the shipped binary. clipboardy@4.0.0's Windows implementation spawns a helper binary (clipboard_x86_64.exe) via __dirname; the single-file bun --compile bundle embeds that helper at a virtual in-bundle path (B:\~BUN\fallbacks\windows\clipboard_x86_64.exe) that cannot be executed ("The system cannot find the drive specified"). Every clipboard read therefore failed at runtime in the compiled exe and Ctrl+V silently did nothing, while bun run dev worked because the helper exists on disk in node_modules.

clipboard.ts now reads win32 text via PowerShell before the clipboardy fallback - the same mechanism the code already uses for the image probe and the write path (Add-Type -AssemblyName System.Windows.Forms; [Console]::OutputEncoding = UTF8; [Console]::Write([System.Windows.Forms.Clipboard]::GetText())). [Console]::Write returns byte-exact text (verified for multi-line content, unlike Get-Clipboard -Raw, which appends a trailing \r\n); an empty clipboard falls through to the existing clipboardy/prompt-hint fallback. Linux/macOS paths are untouched.

Unchanged behavior

  • Image paste (clipboard images via pasteAttachment) - same as before.
  • Empty clipboard - still falls through to the prompt.paste hint command.
  • Native bracketed-paste terminals (most Unix terminals, Windows Terminal < 1.25) - unaffected; they never emit the Ctrl+V keydown.

Tests

packages/opencode/test/cli/cmd/tui/paste-ctrlv.test.tsx - 6 cases: forwarded Ctrl+V inserts text without double-insert, multi-line text renders, empty clipboard is a no-op, plain keys are unaffected, plus two shadowing regressions - a command claiming the Ctrl+V keybind shadows the textarea paste (listener matched, nothing inserted), and with no command claiming it the forwarded Ctrl+V inserts the clipboard text.

The clipboard read was additionally verified at the binary level: a probe compiled with the real build conditions (single-file, Windows x64) fails on the pre-fix clipboardy path and returns the clipboard text byte-exact with the PowerShell read, and the rebuilt exe pastes Ctrl+V end-to-end. bun run typecheck (all 13 workspace packages) passes.

/connect dialogs (latest commit 80e6610f2)

The provider connection flow used separate shared dialog inputs rather than the main prompt textarea, so the prompt-level Ctrl+V fix did not cover them. On terminals that forward Ctrl+V, paste still did nothing in both places users need it most:

  • the provider search field (DialogSelect)
  • API-key, authorization-code, and provider metadata prompts (DialogPrompt)

Both shared dialogs now recognize the configured input_paste keybind in their existing keyboard listeners, prevent the raw key event from reaching the input, and insert clipboard text through Clipboard.pasteText(). The helper accepts text MIME types only, normalizes CRLF/CR newlines, and reuses the existing Windows PowerShell read path in compiled binaries. Image handling and the main prompt paste pipeline are unchanged.

Additional verification

  • clipboard-paste.test.ts: text insertion/newline normalization plus image, empty, and missing clipboard no-ops.
  • dialog-paste-ctrlv.test.tsx: a kitty-forwarded Ctrl+V reaches a dialog keyboard listener and inserts into its focused input.
  • Targeted TUI paste suite: 9 passed, 0 failed across 3 files.
  • bun typecheck: passed in debug and production checkouts.
  • Windows x64 single-file builds: passed smoke tests in both checkouts.
  • Live compiled-binary check: Ctrl+V pasted anthropic into the /connect provider search and filtered the list correctly.

… input_paste keydown branch only handled images; on key-forwarding terminals (e.g. WT 1.25+ with kitty active) Ctrl+V never emits a bracketed paste, so text was dropped entirely. Insert clipboard text directly with CRLF normalization; terminals that paste natively never deliver the keydown, so no double-insert.
@NormalDudeBro
NormalDudeBro marked this pull request as draft August 7, 2026 03:07
The prompt's input_paste keydown branch (added for key-forwarding terminals
like Windows Terminal 1.25+ with the kitty keyboard protocol active) inserted
raw clipboard text directly, bypassing the bracketed-paste pipeline: file
paths were not turned into attachments, large text skipped the paste summary,
and the layout render flush was missing.

Extract handlePastedText() from the onPaste handler and call it from both
paste signals so behavior is identical regardless of which signal the terminal
delivers. Add testRender-based regression tests covering kitty-forwarded
Ctrl+V: text inserts without double-insert, multi-line text renders, empty
clipboard is a no-op, plain keys are unaffected.
@NormalDudeBro
NormalDudeBro marked this pull request as ready for review August 7, 2026 03:45
The prompt.paste command registered keybind input_paste (ctrl+v), and the command dialog's global keypress listener runs before the focused renderable handlers — on a match it calls preventDefault(), which skips the textarea's onKeyDown entirely. On terminals that forward raw 0x16 (WT 1.25+ with kitty active) Ctrl+V therefore never reached the paste branch. Removing the keybind lets Ctrl+V flow to the textarea; the command stays triggerable via command.trigger('prompt.paste') for the image-only bracketed-paste fallback.

Tests: paste-ctrlv.test.tsx now simulates the command-dialog global listener (useKeyboard) alongside the textarea and asserts both configurations — a command claiming Ctrl+V shadows the paste, and with no command claiming it the forwarded Ctrl+V inserts the clipboard text.
…e compiled exe

The compiled single-file exe bundles clipboardy's Windows implementation,
which spawns its helper binary (clipboard_x86_64.exe) from a virtual
in-bundle path (B:/~BUN/...) that cannot be executed, so every clipboard
read fails at runtime there and Ctrl+V paste silently does nothing. Use
the same PowerShell mechanism as the image probe and the write path;
[Console]::Write avoids PowerShell's output formatting so the returned
text is byte-exact.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant