All notable changes to FlashPaste are listed here. The format follows Keep a Changelog 1.1.0 and the project follows Semantic Versioning.
Release-tag policy: every vX.Y commit on main must be tagged and have a matching GitHub release before the turn ends — see AGENTS.md for the full rule.
- Double-paste under XWayland kitty, round two. The kitty
ctrl+vinterception is retired: under XWayland,kitten @ lsreportsis_focused=falsefor every OS window and emptyforeground_processes, sokitty-paste-router.sh's tmux detection could never succeed. Every paste took the slow fallthrough (a backgroundlaunchthat lands seconds late under load), and the late second fire fell outside the daemon's 2.5 s dedup window — observed as identical 162-byte pastes 4 s apart.config/keybindings.canonicalnow requires kittyctrl+vto beUNBOUND(new canonical keyword, enforced byflashpaste-keybindings-check.sh); tmux'sbind -n C-vis the single Ctrl+V handler (~5 ms, pane id always from the pressing client). In non-tmux kitty windows the raw0x16reaches Claude Code, which reads the clipboard itself via thewl-pasteshim; plain shells use kitty's nativectrl+shift+v.
flashpaste-keybindings-check.sh:UNBOUNDkeyword inkeybindings.canonicalasserts a key must NOT be bound on a surface (previously only positive "must contain" rules existed).kitty-paste-router.shnow traces to the shared clipboard-pipeline log (paste-routertag: invoked / suppressed / fallthrough / pane-resolved / done / image-fallback), closing the observability gap that made this regression invisible — the router was the only paste path that wrote no logs.
1.34 - 2026-05-21
- CI hygiene rolled into a tagged release so the
v1.34tag and theLintworkflow on the same commit agree. v1.33's release workflow shipped the .deb successfully, but the Lint workflow on that exact tag failed on pre-existing issues:docs/overlay-references.mdMD032 (17 lists missing surrounding blank lines),openspec/changes/flashpaste-overlayd-phase1-2026-05-20/CHANGE.mdMD022 / MD025 / MD012 / MD058 (proposal H1 nested inside an H2, doubled blank line, three tables without surrounding blanks),openspec/plans/flashpaste-overlayd-phase1-2026-05-20/tasks.mdMD055 (table rows without leading / trailing pipes),README.md:7MD001 (h3 directly under h1, skipping h2 — promoted to h2),tests/click-through.md:14MD032 (list directly under a colon-terminated paragraph), and two dead links inCHANGELOG.mdpointing at the never-taggedv1.32(now pointing at the existingv1.33/v1.27references). No behaviour change vs v1.33.
1.33 - 2026-05-21
bin/flashpaste-capture-clip— new helper that reads an image from the system clipboard and writes it to~/Pictures/Screenshots/flashpaste-clip-latest.pngso the daemon's inotify watcher can stage it. Bridges the long-standing "browser Copy Image" gap (ROADMAP.md:32, now ticked) where Firefox / Chrome put image bytes on the Wayland clipboard without writing a file, so the daemon's fast path never saw them and Claude Code'swl-paste -t image/pngcame back with 0 bytes (mutter blocks surfaceless reads, and Firefox under Wayland often doesn't mirror images to the X11 selection either). Stable filename — the file is overwritten on every capture, no clutter accumulates in the screenshots dir.
bin/paste_image.shimage branch now callsflashpaste-capture-clipbefore sending\026to kitty, then waits 60 ms for the daemon to claim the staged bytes. Cost is one extra wl-paste + xclip probe per image paste (~30–80 ms); the helper validates the PNG / JPEG / WebP magic so a malformed read can't clobber the daemon's stage with text bytes (the same xclip-lies-about-MIME trap the wl-paste shim already defends against atbin/wl-paste:99-104).rs/flashpaste-common/src/compress.rstightens the attachment defaults:DEFAULT_MAX_BYTES4 MB → 3.5 MB andDEFAULT_MAX_DIM2400 px → 1568 px. Claude's API caps single attachments at 5 MB base64 (~3.75 MB raw) and multi-image conversations at 2000×2000 px; the new defaults sit under both with headroom for HTTP framing and the JSON-RPC wrapper. Browser-copied images that come in larger now downscale cleanly before they hit the agent surface.
flashpaste-overlayd, a Wayland screen-annotation daemon for agent-driven highlights, pointers, labels, and fade-out TTLs.- Three MCP overlay tools in
flashpaste-mcp:highlight_region,point_at, andclear_annotations. - GNOME-compatible overlay fallback path for environments without layer-shell support.
- Multi-monitor overlay support so annotations map to the visible output geometry instead of assuming a single screen.
flashpaste-doctornow runs 17 core checks, up from the previous 13, including overlay daemon, socket, compositor, and draw-path probes.
- Compositor support matrix: wlroots compositors with layer-shell get the native layer-surface path; GNOME/Mutter uses the fallback path; other compositors should run
flashpaste-overlayd --probebefore relying on MCP annotations. - GNOME fallback caveat: annotations are still programmatic and click-through by design, but Mutter does not expose the same layer-shell semantics as wlroots, so placement and stacking should be treated as best-effort until the probe and quickstart workflow pass on the target desktop.
- CI hygiene rolled into a tagged release so the
v1.Xtag and theLintworkflow on the same commit agree. v1.25's release workflow shipped the .deb successfully, but the Lint workflow on that exact tag failed on three pre-existing issues fixed in commit8d280efonmainbut never re-tagged:bin/flashpaste-logs.sh:118SC1007 (GRN= RED= WHT=rewritten asGRN='' RED='' WHT=''),bin/flashpaste-logs.sh:206/236/238SC2178/SC2128 (the localprev=""inkitty_pollershadoweddeclare -A prev=()inclaude_state_poller— renamed the local tolast), andCHANGELOG.md:92MD049 (_…_italics on the v1.22 revert note converted to*…*). No behaviour change vs v1.25.
rs/flashpasted/src/ipc.rstext-vs-image intent decision inhandle_paste. The singlelatest_selectionslot still holds at most one variant (mirrors how real clipboards work), but the dispatcher now consults live X11 TARGETS too: if the daemon has a fresh staged image AND the X11 CLIPBOARD has been taken over by an external app advertising text-only targets (browser, terminal selection, IDE copy, …), the user's text is scraped, staged, and dispatched as text instead of forcing the image through. The staged image stays in memory so a subsequent paste with no text-overlay still serves it.rs/flashpasted/src/paste.rsdispatch_text_paste: tmuxload-buffer+paste-buffer -t <pane>text path. No clipboard claim, no kitty IPC, no unbind/rebind dance — pure tmux pty injection so the text lands in any pane regardless of which terminal hosts the tmux client. Replaces the "punt to bash" path for the text case.rs/flashpasted/src/tmux.rssend_ctrl_v_to_pane(pane):tmux send-keys -t pane -l \x16injects the literal Ctrl-V byte directly into the named pane's pty, bypassing kitty's "active window only" filter. Fixes the user-reported "I could paste image only into one Claude Code chat — the rest doesn't get my img."
bin/wl-pasteshim now refuses to lie.xclip -selection clipboard -t image/png -oon a text-only clipboard silently returns the text bytes instead of failing (xclip falls back to whatever's in the selection when the requested MIME isn't advertised). The shim was forwarding that text-as-image lie up to Claude Code, which would report "no image found" while pasting raw text into the chat. New behaviour: when a MIME-typed target is requested, the shim probes TARGETS first; if the requested mime isn't on offer, exit 1 with no stdout — matching what a healthywl-paste -t image/pngdoes on a missing MIME.rs/flashpasted/src/ipc.rsremoved theclipboard_holds_user_textpunt-to-bash short-circuit at the top ofhandle_paste. It was firing on every tmux highlight (which auto-copies via@clipto xclip), forcing Claude pastes to deliver highlighted log junk instead of the user's screenshot. The new intent decision (above) handles the same case more precisely — it only honours external text when X11 is actually owned by another app, not when xclip is briefly text-typed by our own pipe.
bin/clipboard-set.sh,bin/flashpaste-logs.sh,bin/flashpaste-screenshot-preload.sh,rs/flashpasted/src/{inotify_watch,wayland}.rs: in-flight tweaks bundled with the release. Notable: clipboard-set.sh gateswl-copybehindFLASHPASTE_USE_WL_COPY=1and reaps stalewl-brokenflags; flashpaste-logs.sh adds--clip/--kittypoller streams with the wl-paste call gated behind--clip-waylandto keep the dock quiet on Mutter.
wait_for_pane_idleinrs/flashpasted/src/tmux.rs(and theclaude_is_busy/line_has_token_counterhelpers + theFLASHPASTE_PANE_IDLE_TIMEOUT_MSenv knob inpaste.rs). The v1.23 idea was to detect Claude generating via the live↓ N tokensindicator and hold the dispatch until idle; empirically the detector matched any scrollback line containing<digit> tokens(chat history, release notes, "Saved 200 tokens", etc.), so it timed out on every press into a Claude pane and added the full timeout (5 s default after the v1.23 30 s → 5 s tweak) as pure latency. Confirmed in journalctl:ms_idle_wait=5097,ms_idle_wait=5145back-to-back onpane=%41. Dispatches now fire immediately; if the TUI drops the byte the user retries, which is far cheaper than 5 s of guaranteed hang.
- The
paste_in_flight+pending_pastededup instate.rs/ipc.rsstays. With the wait gone its window shrinks from "up to 30 s" to "~10–20 ms" (just the dispatch itself) but it's still useful for absorbing a rapid double-click on the right-click → Paste menu so Claude sees one\026instead of two.
- The in-flight dedup is now pane-aware.
state.rsadds apending_pane: Mutex<Option<String>>that records the most recent absorbed pane id; the replay dispatch reads it and targets the saved pane instead of always replaying to the pane the initial dispatch was running on. Watcher had caught the cross-pane bug as "absorbed-press pane=%38 → replay pane=%41 (wrong pane)." ipc.rsdemotesBroken pipe (os error 32)on the IPC accept path fromWARNtoDEBUG. That's the trigger's 150 ms read timeout closing the socket before we finish writing the queued-paste reply — expected behaviour, not a bug, but it was polluting the WARN stream.
bin/flashpaste-logs— unified live viewer across the three streams the pipeline writes to (daemon journal, trigger log, clipboard-pipeline log). Colorized, prefixed, grep / since / -n / no-follow flags.install.shsymlinks it without the.shsuffix to match the muscle memory offlashpaste-trigger/flashpaste-doctor.
rs/flashpasted/main.rsbounds the tokio runtime drop withshutdown_timeout(500ms). Without it, the blocking selection-owner threads kept the runtime alive forever, leaving systemd indeactivating (stop-sigterm)until the 90 sTimeoutStopSecSIGKILL — during which the socket file existed but the listener was already torn down, soflashpaste-triggergot ECONNREFUSED and the user saw paste as "broken after every restart."rs/flashpastedpaste pipeline now cancels copy-mode before sending\026(a wheel-scrolled pane silently swallowed the byte) and waits up to 30 s for the Claude Code TUI to finish generating (detected by the live↓ N tokensindicator) so pastes during generation no longer drop on the floor.rs/flashpasted/ipc.rsadds an in-flight dispatch guard. While one dispatch is waiting for Claude to become idle, additional paste presses are deduped instead of queueing — previously 4 queued presses fired four\026bytes back-to-back the instant Claude unblocked.rs/flashpastedkitty dispatch matchesstate:activeinstead ofstate:focused— survives the focus steal from screenshot tools, right-click menu rendering, and other transient focus changes.rs/flashpastedlatchesWAYLAND_WEDGEDonce the compositor proves it speaks noext-data-control/wlr-data-control(Mutter on GNOME 46): subsequent re-asserts skip the doomedcopy_multitask entirely instead of spawn-blocking on every paste.rs/flashpastedstaged-image TTL bumped 2 min → 30 min so the AFK-then-paste case (screenshot, switch away, come back) doesn't silently demote Tier 3 → bash.rs/flashpastedCtrl-V rebind now matches the documentedflashpaste-trigger || tmux-paste-dispatch.shfallback. Previously the daemon rebound to bash-only after the first paste, silently demoting the rest of the tmux session to Tier 1.bin/clipboard-set.shgates thewl-copypath behindFLASHPASTE_USE_WL_COPY=1. On Mutter the wl-copy fork costs ~4–5 extra execs per paste AND surfaces phantom "Unknown" dock icons, all without acting as a durable selection owner (no data-control protocol available). xclip remains the durable owner.
- shellcheck SC2163 in
bin/clipboard-set.sh:40andbin/get-clipboard-text.sh:50(export "$kv"→export "${kv?}"). - shellcheck SC2209 in
bin/flashpaste-trace.sh:162(AWK_BIN=awk→AWK_BIN='awk'). - markdownlint: bulk MD022 / MD031 / MD032 blank-line fixes via
markdownlint-cli2 --fixacrossAGENTS.md,CHANGELOG.md,CONTRIBUTING.md,docs/adr/*,docs/*. MD040 fence-language tags added to 12 plain-text fences.docs/glossary.mdentries promoted h3 → h2 (flat list, no intermediate h2). README "TL;DR for AI assistants" blockquote heading converted to bold (was h3 skipping h2)..markdownlint.jsonsetsMD025.front_matter_title=""so YAML frontmattertitle:no longer clashes with the body h1, and disablesMD060(table-pipe spacing) which the CI action's bundled markdownlint version does not enforce.
examples/tmux.conf.snippet: v1.22 dropped the-Oflag from the right-click menu binding on the theory that-Ofroze the TUI pane until Escape. That repro was on tmux <3.4 and does not hold on 3.6a; removing-Oinstead caused the menu to auto-dismiss the instant the user moved the mouse toward an entry. v1.23 restores-Oso the menu stays open until item-click, click-outside, or Escape.
examples/tmux.conf.snippet: dropped the-Oflag from the right-click menu binding so the menu auto-dismisses on click-outside / mouse-release-outside. With-Othe menu held the pane in modal-grab until Escape, which read as the pane being "frozen" after a right-click — most visible in TUIs that grab keystrokes (Claude Code chat input). Reverted in v1.23 — the freeze repro was on tmux <3.4 and the removal made the menu unusable on 3.6a.
1.19 - 2026-05-19
- Killed the "wl-clipboard" dock flicker on copy (replaces the v1.13
NoDisplayworkaround with the root-cause fix in the daemon path)
1.18 - 2026-05-19
flashpaste-mcpserver exposing clipboard / screenshot / paste-into-pane tools to LLM agents over MCP stdioflashpasteagent skill for Claude CodeAGENTS.mdrelease-policy enforcementAGENTS-release-check.shaudit script
1.17 - 2026-05-19
- Tier 3 path enabled by default in the example snippets — Ctrl+V now uses
flashpaste-triggerwith automatic fallback totmux-paste-dispatch.shwhen the daemon socket is absent
1.16 - 2026-05-19
- Rust daemon (
flashpasted) — long-lived clipboard owner with inotify-driven screenshot pre-stage - Sub-15 ms Tier 3 paste path via
flashpaste-trigger(1-byte unix-socket ping to the daemon) - Trigger falls back to the bash dispatcher when the daemon is absent
1.15 - 2026-05-19
- Debian packaging —
make debproducesdist/flashpaste_*_all.deb - GitHub Actions release workflow (
.github/workflows/release.yml) auto-builds the.deband publishes a GitHub release on everyv*tag push
- Aggressively kill phantom dock icons (refines v1.13)
The v1.10–v1.14 tags predate the
.github/workflows/release.ymlworkflow. Per AGENTS.md, we do not retroactively tag them by default — their build environment may not reproduce.
NoDisplay=true.desktopfiles forwl-paste/wl-copyto suppress Ubuntu Dock phantom-icon flashes
- Kitty
Ctrl+Vauto-routes between text and image paste
- Parallel
flashpaste-doctorprobes (13 checks) - Upstream credits surfaced in README
- One-line
bootstrap.shinstaller - Optional structured logging
- Screenshot watcher (
flashpaste-screenshot-watcher.path+.service)
Initial commit: sub-120 ms bash hot path for image-paste into GNOME Wayland TUIs.