Cut the 0.8.0 release (dependency upgrades + single-instance and
error-handling hardening): daemon Cargo.toml/Cargo.lock to 0.8.0, extension
metadata.json version to 8, and add the [0.8.0] CHANGELOG entry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Dependency major upgrades (zbus 4->5, rusqlite 0.31->0.40, dirs 5->6) and the
migration + hardening that came with the review:
- zbus 5: SignalContext -> SignalEmitter, signal_context() -> signal_emitter().
- Single-instance D-Bus name: request with DoNotQueue instead of
ReplaceExisting|AllowReplacement. If the name is already owned the daemon now
exits instead of running without owning it (zbus 5 returns InQueue as Ok) or
stealing the name and orphaning the running instance (zbus does not terminate
a replaced owner). The extension already skips spawning when the name is
owned, so replacement was never needed.
- Stop swallowing real SQLite errors: upsert_item's dedup SELECT and
get_thumbnail/get_raw_item now use .optional().context()? so a missing row is
Ok(None) and a genuine error propagates instead of masquerading as not-found.
- clippy pedantic/nursery cleanup: uninlined format args, redundant mime clone,
pass ObjectId/Connection by reference where not consumed, match->if/is_ok,
doc backticks; #[allow] with rationale for the intentional lints
(significant_drop_tightening on the single-writer lock, unused_async on zbus
interface methods).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cut the 0.7.0 release (D-Bus rename + theme-recursion fix): daemon
Cargo.toml to 0.7.0, refresh Cargo.lock (gif/half/log patch bumps + version),
extension metadata.json version to 7, and add the [0.7.0] CHANGELOG entry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two changes for 0.7.0:
BREAKING (D-Bus): the service is renamed from org.gnome.Strata to
dev.edu4rdshl.Strata. The org.gnome.* namespace is reserved for official
GNOME software; Strata is third-party, so it now uses the reverse-DNS of its
own domain (matching the strata@edu4rdshl.dev UUID). Bus name, object path
(/dev/edu4rdshl/Strata), and interface (dev.edu4rdshl.Strata.Manager) all
change. Daemon and extension are updated together; only external busctl
scripts / non-GNOME front-ends that hard-coded the old name need updating.
The GSettings schema (org.gnome.shell.extensions.strata) is unchanged - that
namespace is correct for GNOME Shell extension settings.
Fix recursion: the theme-context 'changed' handler reloaded light.css, but
load_stylesheet itself emits 'changed', so it fed back into itself and hit
"too much recursion" - flooding the journal and spinning the CPU on screen
unlock (which restyles widgets and fires 'changed'). The subscription is
removed; light.css is loaded once. Dark/light switching is unaffected (it is
the panel's class toggle, not this load). A GNOME Shell theme switch no
longer auto-re-applies the sheet, which is recoverable by re-enabling.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cut the 0.6.0 release (lazy full-history search + image-format and audit
fixes): daemon Cargo.toml/Cargo.lock to 0.6.0, extension metadata.json
version to 6, and add the [0.6.0] CHANGELOG entry.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit fixes for the daemon:
- Decode gif/webp/bmp/tiff/ico (enable the pure-Rust image features). These
mimes were accepted by pick_mime but the image crate only had png/jpeg, so
thumbnail decode failed and the item was silently dropped. Drop avif (needs
heavy C deps) and svg (not a raster format the image crate decodes) from the
allowlist so unsupported types are never selected.
- ItemAdded signal preview now uses db::PREVIEW_CHARS (200), matching the
history/search queries, so a just-copied row and the same row after a reload
carry identical text.
- Add a deterministic ", rowid DESC" tiebreaker to the three created_at DESC
orderings (history, search, prune) so items sharing a millisecond have a
stable order and prune can't evict the wrong one at the boundary.
- SetConfig now prunes immediately (and emits ItemDeleted) when max_history is
lowered, instead of waiting for the next copy.
- Request the bus name with AllowReplacement (plus ReplaceExisting) so a
reload's new instance can cleanly take the name; fix the misleading comment.
- Wayland monitor clears its pending-offers map on Selection{None} so it can't
grow across copy-then-clear cycles (wlroots path).
- Document that SubmitItem is a trusted-caller method (the password-manager
hint is enforced client-side; forwarding it is a future contract change).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cut the 0.5.0 release (light/dark theme support): daemon
Cargo.toml/Cargo.lock to 0.5.0 (no code change, kept in lockstep with the
tag), extension metadata.json version to 5, and promote the CHANGELOG
[Unreleased] entry to [0.5.0] - 2026-05-26.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cut the 0.4.0 release: daemon Cargo.toml/Cargo.lock to 0.4.0, extension
metadata.json version to 4, and promote the CHANGELOG [Unreleased] entry
to [0.4.0] - 2026-05-26.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The monitor.rs comments claimed Mutter implements ext-data-control-v1 on
GNOME 47+, contradicting ARCHITECTURE.md. Mutter exposes neither
ext-data-control-v1 nor zwlr-data-control-v1, so the monitor never binds
on GNOME and ingest comes from the extension via Meta.Selection +
SubmitItem; the monitor only runs on wlroots. Correct the comments
(module doc, Ext probe, bail! message) and the main.rs spawn comment.
Also reconcile ARCHITECTURE.md with the code: supervisor backoff
(1/2/4/8/16 s, 5 s reset, log-only give-up), SIGTERM at 1.5 s, prune as a
separate post-insert call, multi-thread tokio default, the 200 px
load-more threshold, and the SQL-side preview truncation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GetHistory and SearchHistory selected the full content_text column for
every row, so a page of large text items serialized up to megabytes of
JSON over D-Bus and parsed it on the GJS main loop, only to render a
~140-char snippet. The client-side cut never bounded the wire cost.
Truncate to PREVIEW_CHARS (200) with substr() in SQL on both list paths.
Full content is still served on demand by GetItemContent for paste-back.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The column is defined in the initial CREATE TABLE statement, so the
ALTER TABLE migration was redundant from day one -- it always failed
silently via .ok(). No existing user databases lack this column.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The D-Bus interface previously returned content as a base64-encoded
string (type 's'), requiring the daemon to encode and GJS to decode
on every paste. D-Bus natively supports byte arrays (type 'ay'),
eliminating both encode and decode steps entirely.
Changes:
- dbus_service.rs: return (String, Vec<u8>); drop base64 calls
- dbus.js: content_b64 arg changed from type 's' to type 'ay'
- panel.js: remove GLib.base64_decode(); pass ay bytes directly
- Cargo.toml/Cargo.lock: remove base64 dependency (no longer used)
For a 1 MB text paste this avoids allocating a 1.33 MB intermediate
string and decoding it on the compositor main thread.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The daemon stored the focused app ID but never read it. App exclusion
runs entirely in the extension before SubmitItem is called, so the
D-Bus round-trip on every window focus change was pure overhead.
Removed from: extension.js call site, dbus.js XML interface,
dbus_service.rs method + struct field, main.rs Arc allocation.
Focus tracking in _connectFocusTracking is kept since _currentFocusedApp
is still used for the JS-side exclusion check.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous fallback to PathBuf::from(".") silently placed the
database in whatever the current working directory was, making it
impossible to find later. Since the daemon cannot function without a
persistent database, failing fast with an actionable message is the
correct behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
write_to_clipboard took ownership of WriteRequest but immediately
cloned both fields into locals, then cloned bytes again for the text
path -- three heap allocations on a potentially large byte buffer.
Destructure the owned request directly and move the fields. The text
path still needs one clone (bytes goes into two MimeSources), but the
extra upfront clones are gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
get_raw_item is used by GetItemContent (paste-back) and SetClipboard.
Neither caller needs the thumbnail -- they only need the original
payload. Excluding thumbnail_blob from the SELECT avoids loading up to
~40 KB of PNG data per paste operation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously prune() ran two identical NOT IN (SELECT ... ORDER BY
created_at DESC LIMIT n) subqueries: one to collect IDs for signals,
one to delete. SQLite executed the ORDER BY pass twice.
Now we collect the IDs first with a single subquery, then delete by
those exact IDs using params_from_iter. The ORDER BY scan runs once
regardless of how many items are pruned.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
make_thumbnail was computing a base64 string of the thumbnail on every
image clipboard capture. The caller only ever used the raw bytes and
immediately discarded the String. Remove the base64 encode, simplify
the return type to Result<Vec<u8>>, and drop the now-unused
'use base64::Engine' import from main.rs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>