Skip to content

fix(clipboard): paste CleanShot JPEG screenshots stored under the macOS PNG type - #29685

Merged
AmoabaKelvin merged 3 commits into
stablyai:mainfrom
AmoabaKelvin:pasting-images-with-cmd-v
Oct 16, 2026
Merged

AmoabaKelvin merged 3 commits into
stablyai:mainfrom
AmoabaKelvin:pasting-images-with-cmd-v

Conversation

@AmoabaKelvin

@AmoabaKelvin AmoabaKelvin commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​168 $\color{#cf222e}{\Huge{\mathbf{−}}}$​7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​161
Prod 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​35 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​33

ELI5

CleanShot, when set to save JPEGs, puts a JPEG on the clipboard but labels it as PNG. Orca asked Electron for "the PNG" and got back nothing, so Cmd+V into Claude Code did nothing. Orca now looks at what the bytes actually are when the normal read comes back empty, so the screenshot gets attached.

What Changed

Before: on macOS, copy a CleanShot screenshot with CleanShot's file format set to JPEG, focus a Claude Code terminal, press Cmd+V. Nothing is typed into the terminal, no orca-paste-*.png temp file is written, and the composer shows no image preview. Ordinary PNG screenshots paste fine.

After: the same paste writes the temp PNG, types its path into the terminal, Claude attaches it, and the composer preview matches.

Mechanism: CleanShot 5.0.1 writes public.file-url plus public.png to the pasteboard, and the public.png payload is JPEG bytes (ffd8ffe0...). Chromium's macOS clipboard returns those bytes as-is, and Electron's clipboard.readImage() hands them to its PNG-only decoder, which returns an empty image. Orca's save handler treated that empty image as "no image on the clipboard" and returned null, so the terminal skipped the paste.

The new readClipboardNativeImage reader calls clipboard.readImage() first and returns it when non-empty or when not on macOS. Only when it comes back empty on macOS does it read the raw public.png bytes, check them against the existing source-byte limit, reject oversized declared dimensions from the encoded header before any decode (the same pixel-bomb guard the Windows copied-file fallback uses), and decode them with nativeImage.createFromBuffer, which sniffs the real format. Both the save IPC and the thumbnail IPC use this one reader. The source gate (so a copied image file alone is still not treated as a clipboard image), the dimension and output-size limits, the Windows copied-file fallback, and local/SSH/paired-runtime routing are unchanged. The thumbnail handler swallows a decode or size rejection and returns no preview; the save call still reports it.

Why

JPEG is already supported. The failure is that the clipboard type and the bytes disagree, and the decoder trusts the type. Alternatives considered:

  • Reading image/png or image/jpeg via readBuffer: the real macOS clipboard returns zero bytes for the MIME names. Only the native UTI name retrieves the payload.
  • Checking availableFormats(): Chromium advertises only text/uri-list for an image-plus-file clipboard, so enumeration cannot see the image.
  • Loading the referenced file from the file URL: that would accept any copied image file as a pasted image, which Keep picked skills and pending uploads when native chat composers close #26538 deliberately stopped.
  • Changing terminal key handling: cannot help when no file was ever saved.

Related: #27346 added native PNG/TIFF detection for screenshots that sit beside a file URL. That makes hasImage say yes for this clipboard, but does not repair the decode. #26898 is an open proposal for the same symptom; its helper reads MIME-named buffers and filters through availableFormats(), which return empty on the real CleanShot pasteboard.

Linked Issue

Fixes #26739

Visual Proof

Hidden Orca dev build, Cmd+V dispatched into a terminal running a script that echoes each PTY paste as base64. A text paste is sent first as a control, then the real CleanShot JPEG capture.

Before: only the text control arrives (RECEIVED_1 decodes to a bracketed paste of probe-text). The CleanShot JPEG paste delivers zero bytes.

before-cleanshot-jpeg-cmd-v.png

After: RECEIVED_2 decodes to a bracketed paste of /var/folders/.../T/orca-paste-<id>.png, the saved 64x64 PNG.

after-cleanshot-jpeg-cmd-v.png

Testing

Reproduced on macOS 27.0.1 (arm64) with CleanShot 5.0.1 set to JPEG, Electron 43.7.5 and 43.7.8, and Claude Code 2.1.301. The same raw public.png bytes (SHA-256 ecd50460...) fail through readImage() and decode through createFromBuffer.

Manual steps for a reviewer on a Mac with CleanShot:

  • Set CleanShot's file format to JPEG, capture a region, copy it.
  • In an Orca terminal running Claude Code, press Cmd+V. On main nothing happens. On this branch the image attaches.

Automated:

  • src/main/window/clipboard-macos-image-paste.test.ts adds the JPEG-under-PNG recovery, malformed raw bytes, oversized raw bytes before decode, oversized declared JPEG dimensions before decode, oversized dimensions and output with recovery, and sender auth before any clipboard read. These fail on main.
  • src/shared/raster-image-dimensions.test.ts covers the shared JPEG dimension reader now requiring 0xff before a marker. Stray bytes after the start-of-image marker used to be parsed as a frame header and reported bogus dimensions, which the new guard relies on.
  • tests/e2e/clipboard-macos-image-paste.spec.ts runs a hidden Electron app with the measured clipboard API responses stubbed and the real JPEG encoder and decoder, then checks the saved PNG signature and size, the matching thumbnail, the malformed-data control, and the copied-file source gate. Fails on main with null from save, passes here. It does not touch the system clipboard and skips off macOS.
  • 13 focused clipboard suites (136 tests), pnpm tc, full pnpm lint, the changed-file quality gate, and React Doctor all pass on this commit.

Not tested live: Windows and Linux GUI, and a live SSH or paired-server target. Those paths are untouched and covered by their existing routing suites.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

The shared JPEG dimension reader change also affects the existing preview-limit and base64-preview callers, which only become stricter about where a marker may start. macOS-only fallback behind a process.platform check. No wire changes, no new settings, no new processes. Target routing after the decode is unchanged, so SSH and paired-runtime saves behave as before.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 421cb872-740a-4cee-b745-d12f8c127206
📥 Commits

Reviewing files that changed from the base of the PR and between ecd7a61e75bb4d2a5a2568b28004f5e59c0ef6ae and 701b193.

📒 Files selected for processing: 4
  • src/main/window/clipboard-macos-image-paste.test.ts
  • src/main/window/clipboard-native-image.ts
  • src/shared/raster-image-dimensions.test.ts
  • src/shared/raster-image-dimensions.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

The change adds readClipboardNativeImage() to recover nonempty macOS public.png data when clipboard.readImage() returns an empty image. The thumbnail and image-save handlers now use this function. Thumbnail reads return null when image reading or thumbnail creation fails. Tests cover successful JPEG recovery, malformed and oversized images, unauthorized senders, thumbnail previews, and saved files.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 701b1

No concrete issue currently blocks merging. The new clipboard fallback addresses the reported macOS paste case while retaining the described size and authorization checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Issue #26739 requires macOS image paste to save a clipboard image and return its temp-file path for Claude Code. readClipboardNativeImage() recovers non-PNG bytes from public.png after an empty `c…
Out of Scope Changes check Passed The changes stay within issue #26739. The production changes add macOS clipboard recovery, connect it to the existing save and thumbnail handlers, and tighten shared JPEG dimension parsing needed for …
Title check Passed The title clearly and concisely describes the main change: fixing macOS CleanShot JPEG screenshots stored under the PNG clipboard type.
Description check Passed The description is complete and follows the repository template. It explains the user impact, implementation, rationale, linked issue, visual proof, testing, platform considerations, and checklist sta…
Full details: Linked Issues check

Explanation

Issue #26739 requires macOS image paste to save a clipboard image and return its temp-file path for Claude Code. readClipboardNativeImage() recovers non-PNG bytes from public.png after an empty clipboard.readImage() result. clipboard:saveImageAsTempFile uses this reader and preserves the existing PNG save flow. The thumbnail handler uses the same reader. Focused tests cover JPEG recovery, PNG output, thumbnail data, malformed data, size limits, and sender authorization.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue #26739. The production changes add macOS clipboard recovery, connect it to the existing save and thumbnail handlers, and tighten shared JPEG dimension parsing needed for the recovery limit. The added unit and end-to-end tests verify this behavior. Existing source gating, routing, Windows fallback, and output limits remain in the affected flow. No unrelated product behavior is shown.

Full details: Description check

Explanation

The description is complete and follows the repository template. It explains the user impact, implementation, rationale, linked issue, visual proof, testing, platform considerations, and checklist status.

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AmoabaKelvin
AmoabaKelvin force-pushed the pasting-images-with-cmd-v branch from 330383e to ecd7a61 Compare October 16, 2026 17:29
…OS PNG type

CleanShot set to JPEG writes JPEG bytes under public.png. Electron's
readImage decodes that type as PNG only and returns an empty image, so
the save handler returned null and Cmd+V typed nothing into Claude Code.

When readImage is empty on macOS, read the raw public.png bytes and
decode them with nativeImage.createFromBuffer, keeping the existing
source gate, byte and dimension limits, and target routing. The save
and thumbnail IPC handlers share the one reader.
Stray bytes after SOI were parsed as a frame header and reported bogus dimensions.
…raw clipboard bytes

Matches the Windows copied-file fallback so a small header cannot make the
main process allocate a pixel bomb before the size check.
@AmoabaKelvin
AmoabaKelvin force-pushed the pasting-images-with-cmd-v branch from ecd7a61 to 701b193 Compare October 16, 2026 19:38
@AmoabaKelvin
AmoabaKelvin merged commit fe41a9a into stablyai:main Oct 16, 2026
41 checks passed
@AmoabaKelvin
AmoabaKelvin deleted the pasting-images-with-cmd-v branch October 16, 2026 21:44
marinhooodev added a commit to marinhooodev/orca that referenced this pull request Oct 16, 2026
…reader

Explorer image paste called clipboard.readImage() directly, so a CleanShot
screenshot stored as JPEG bytes under macOS public.png pasted nothing.
Use readClipboardNativeImage, which chat paste adopted in stablyai#29685.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

[Bug]: macOS: Cmd+V with an image on the clipboard inserts nothing in a Claude Code terminal (no orca-paste temp file created)

1 participant