Skip to content

TUI: image clipboard paste silently ignored on Kitty/Ghostty (regression of #4077) #25806

@djmittens

Description

@djmittens

Summary

Ctrl+Shift+V in the opencode TUI silently ignores image/* MIME content on Wayland with Kitty / Ghostty (and likely WezTerm + others that don't pipe binary clipboard bytes through their paste action). This is a regression that's been re-reported repeatedly — most prominently in #4077 (closed by stalebot, not fixed; ~12 confirming reports across Linux/macOS/Windows). Filing a fresh issue with root-cause analysis + a concrete fix path since the previous one died from inactivity.

Reproduction

  1. Run opencode TUI inside Kitty (or Ghostty), no tmux required.
  2. Copy a screenshot to the clipboard via any Wayland-native tool:
    • grimblast copy area
    • hyprshot -s -m output -m active --clipboard
    • GNOME Screenshot
  3. In the opencode prompt, press Ctrl+Shift+V.

Expected: image saved as attachment, like opencode 1.0.x did and like alacritty currently does.
Actual: nothing happens. No toast, no error, no attachment.

Text paste works correctly in the same flow — only image MIME types are dropped.

Why it works in Alacritty and not Kitty

Alacritty's paste_from_clipboard action pipes whatever the system clipboard contains as raw bytes into the TTY (including PNG bytes on image clipboards). opencode receives binary in the bracketed paste sequence, detects PNG signature, attaches as image.

Kitty's paste_from_clipboard action is text-only by design (by maintainer's stated philosophy — terminals are text streams, applications should query MIME-typed clipboards via OSC 52 / OSC 5522 or remote control). Image-only clipboards produce zero terminal input on Ctrl+Shift+V. opencode never sees the bytes.

Ghostty + WezTerm follow the same model as Kitty. So this affects ~all modern non-Alacritty terminals.

Proof (binary inspection)

The wl-paste/xclip image-clipboard code path is already present in both opencode 1.4.3 and 1.14.28 binaries:

```js
// from /usr/bin/opencode (1.14.28), function VL():
if (D === "linux") {
let J = await EQ(["wl-paste", "-t", "image/png"], { nothrow: true });
if (J.stdout.byteLength > 0)
return { data: Buffer.from(J.stdout).toString("base64"), mime: "image/png" };
let H = await EQ(["xclip", "-selection", "clipboard", "-t", "image/png", "-o"], ...);
if (H.stdout.byteLength > 0)
return { data: ..., mime: "image/png" };
}
```

But `strace -fe trace=execve` on a running opencode TUI during a Ctrl+Shift+V paste with image on clipboard captures zero `wl-paste` invocations. So this function exists but isn't reached on the keyboard paste path. The trigger seems to fire only on a different code path (drag-drop?), leaving the keyboard paste flow relying on whatever bytes the terminal happens to type — which is empty for image clipboards in Kitty.

Proposed fix

When opencode receives a paste event (bracketed paste begin) AND the terminal is Kitty / Ghostty / WezTerm (detect via `KITTY_WINDOW_ID`, `TERM_PROGRAM`, etc.), opencode should:

  1. Proactively call the existing `VL()` (or equivalent) clipboard-image-read function regardless of what bytes arrived in the bracketed paste.
  2. If that returns image data → attach as image, suppress the (empty) bracketed-paste content.
  3. If it returns text or nothing → fall through to current bracketed-paste handling.

Even simpler: invoke `VL()` on every paste event before checking bracketed content. If the user pasted text it'll return text data (or fall through cheaply); if image, it'll return image. Cost: one wl-paste subprocess per paste, ~2ms.

For better efficiency on Kitty specifically: use kitty's OSC 5522 clipboard protocol to query `image/png` MIME directly via terminal escape sequence — no subprocess, fully in-band. This is also the path that works through SSH cleanly.

Workaround for users hitting this in the meantime

The community-converged workaround is a script bound to Ctrl+Shift+V in kitty that detects image clipboards, dumps to /tmp, and pastes the path via `kitty @ send-text`. Reference: https://micahstubbs.net/2026/Jan/15/kitty-image-paste-claude-code/

This is fine but it's a separate moving piece per user — solving it in opencode means everyone benefits without touching their terminal config.

Environment

Related

Happy to test patches.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions