Repository navigation
fix(clipboard): paste CleanShot JPEG screenshots stored under the macOS PNG type - #29685
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to
|
330383e to
ecd7a61
Compare
…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.
ecd7a61 to
701b193
Compare
…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>
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-*.pngtemp 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-urlpluspublic.pngto the pasteboard, and thepublic.pngpayload is JPEG bytes (ffd8ffe0...). Chromium's macOS clipboard returns those bytes as-is, and Electron'sclipboard.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 returnednull, so the terminal skipped the paste.The new
readClipboardNativeImagereader callsclipboard.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 rawpublic.pngbytes, 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 withnativeImage.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:
image/pngorimage/jpegviareadBuffer: the real macOS clipboard returns zero bytes for the MIME names. Only the native UTI name retrieves the payload.availableFormats(): Chromium advertises onlytext/uri-listfor an image-plus-file clipboard, so enumeration cannot see the image.Related: #27346 added native PNG/TIFF detection for screenshots that sit beside a file URL. That makes
hasImagesay 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 throughavailableFormats(), 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_1decodes to a bracketed paste ofprobe-text). The CleanShot JPEG paste delivers zero bytes.After:
RECEIVED_2decodes to a bracketed paste of/var/folders/.../T/orca-paste-<id>.png, the saved 64x64 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.pngbytes (SHA-256ecd50460...) fail throughreadImage()and decode throughcreateFromBuffer.Manual steps for a reviewer on a Mac with CleanShot:
Automated:
src/main/window/clipboard-macos-image-paste.test.tsadds 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.tscovers the shared JPEG dimension reader now requiring0xffbefore 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.tsruns 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 withnullfrom save, passes here. It does not touch the system clipboard and skips off macOS.pnpm tc, fullpnpm 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.
Review
Agent skill upstream boundary
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.platformcheck. 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
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)