Skip to content

Add image clipboard support (ported from ankushvangari's fork)#3

Open
MiMoHo wants to merge 4 commits into
haad:masterfrom
MiMoHo:image-support
Open

Add image clipboard support (ported from ankushvangari's fork)#3
MiMoHo wants to merge 4 commits into
haad:masterfrom
MiMoHo:image-support

Conversation

@MiMoHo

@MiMoHo MiMoHo commented Jul 3, 2026

Copy link
Copy Markdown

Summary

This ports image clipboard support from @ankushvangari's downstream fork (ankushvangari/Flycut) into this repo, so the feature lands here instead of fragmenting the fork landscape further.

  • Captures, stores, displays, and pastes back images (TIFF/PNG/GIF) alongside text clippings
  • Images stored deduplicated (SHA-256) as files in ~/Library/Application Support/Flycut/Images/
  • Bezel shows image thumbnails; save-to-file (S key) exports images; preferences gain a "Capture images" toggle with a 10 MB size cap
  • Animated GIFs survive capture and paste (offered as file reference so Slack/Discord & co. keep the animation)

Provenance & licensing

The two feature commits are cherry-picked with original authorship preserved from ankushvangari/Flycut, which is a direct descendant of this repo (its history branches off your current master at d944e30). Both repos carry the identical MIT license.txt, so merging is license-clean. I added a credit line for Ankush Vangari to acknowledgements.txt.

Security review & hardening

I had the ported code security-reviewed before opening this PR:

  • ✅ SHA-256 dedup (no collision-based substitution risk), allowlisted file extensions (no path injection via crafted pasteboard type strings), storage in the user-private Application Support dir (no /tmp, no symlink surface), balanced retain/release in the new MRC code
  • ⚠️ One finding fixed on top (last commit): the GIF-preservation feature issues an automatic HTTPS request to the first <img src> URL found in copied HTML — with it enabled by default, copying attacker-authored web content would silently trigger a network request to an arbitrary host (tracking/SSRF primitive) from an app that otherwise makes zero network connections. This PR defaults downloadAnimatedGIFs to NO so it is an explicit opt-in. A follow-up could additionally block loopback/private-range hosts when the option is on.
  • Known limitation (pre-existing in the ported code, non-blocking): if a GIF clipping is evicted from history while its file reference is still on the pasteboard, the backing file is deleted out from under the receiving app.

Testing

The ported commits are the exact code ankushvangari's CI has built and shipped (their v3.x releases). Cherry-picks applied cleanly onto d944e30; all files pass clang -fsyntax-only against the macOS SDK. I could not run a full xcodebuild locally (no full Xcode on this machine), so a CI build/smoke test on your side is appreciated.

🤖 Generated with Claude Code

Ankush Vangari and others added 4 commits July 3, 2026 04:09
Flycut now captures, stores, displays, and pastes back images from the
clipboard alongside text clippings. Images are stored as TIFF files on
disk at ~/Library/Application Support/Flycut/Images/ using SHA-256
content hashing for deduplication. When both text and image are present
on the clipboard, both are captured as separate entries.

- FlycutImageStore: new singleton for file-based image storage
- FlycutClipping: extended with imageHash, imageSize, isImageClipping
- BezelWindow: added NSImageView for image thumbnail display
- pollPB: detects NSPasteboardTypeTIFF/PNG alongside text
- Paste-back works for images in bezel, menu, and search window
- Save-to-file (S key) exports images as .tiff
- Search matches image clippings by source app name
- Preferences: "Capture images" toggle, 10MB max size default
- Image files cleaned up when clippings are evicted from history

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chromium browsers flatten animated GIFs to a still PNG on "Copy Image"
but embed the source GIF URL in the clipboard HTML. Detect that URL and
download the original animated GIF instead of capturing the still.

- Format-aware image store: files keyed by content hash + real extension
  (.gif/.png/.tiff); a resolver finds whatever extension a hash was stored
  under, so existing .tiff clippings keep working.
- Carry the pasteboard UTI (e.g. com.compuserve.gif) on image clippings
  through capture, persistence (reusing the saved "Type"), and paste-back.
- pollPB prefers a real GIF on the pasteboard; otherwise it reads the
  <img src> URL from the clipboard HTML and downloads the GIF (https only,
  .gif path, size cap, 5s timeout, animated-only; skips on failure).
- Paste GIF clippings back as a file-URL only, so apps like Slack/Discord
  upload the actual animated file instead of flattening raw image data to a
  single frame.
- Bezel preview animates GIFs (imagePreview.animates = YES).
- New "Download animated GIFs from web" preference (default on).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The GIF-preservation feature fetches the first <img src> URL found in
copied HTML over the network, triggered automatically on copy. With it
enabled by default, copying attacker-authored web content silently
causes Flycut to issue an HTTPS request to an arbitrary host - a
tracking/SSRF primitive, and a surprise for an app that otherwise makes
zero network connections. Default the preference to NO so users must
knowingly enable it.

Co-Authored-By: Claude Fable 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.

1 participant