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>
- Daemon supervisor: a _spawnPending guard so an in-flight GetNameOwner check
plus a firing backoff timer can't spawn two daemons; wait_async now binds the
subprocess and _onDaemonExited ignores foreign/stale exits, so backoff
accounting can't be corrupted by an overlapping spawn.
- Panel: clearItems() invalidates the search snapshot (_resultsEpoch = -1) so a
scroll after HistoryCleared can't render stale rows; _ensureVisible's idle
callback null-guards the overlay like the other idle callbacks.
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 extension:
- Track and disconnect the changed::excluded-apps GSettings handler in
disable() (it was the one connection whose id was never captured).
- Track the _stopDaemon 1.5s force-kill timer and cancel any leftover on
enable(), so the source can't outlive a disable->enable cycle.
- Guard the exclusion-path delete with _proxy?.DeleteItemAsync.
- Mirror the daemon's image allowlist (drop avif/svg) in _pickMime.
- Show a generic "Image" label for all image rows instead of singling out
PNG; the thumbnail identifies the image and the on-clipboard format (often
PNG even for a copied GIF/WebP) is an implementation detail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Search now covers the entire stored history (bounded by max-history) instead
of an arbitrary 500-result cap, and renders a page at a time as you scroll
rather than building every match at once - so a broad query on a large
history no longer builds thousands of row widgets up front.
The full match set is snapshotted once (lightweight truncated metadata) and
paged from memory, so scrolling never re-queries and cannot race the search.
The first chunk renders synchronously in the same frame as the list clear, so
the list never paints empty between keystrokes (no "blink").
Concurrency hardening for the render path:
- epoch-ownership of the shared loading guard so a fast new query cannot be
blocked by, or have its guard cleared by, a superseded render;
- a results-epoch guard so a scroll during a query's fetch window cannot
render the previous query's stale snapshot;
- removeItem drops the id from the snapshot (adjusting the rendered index) so
a pruned item in the unrendered tail cannot reappear as a phantom row.
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>
Add a Theme preference (Automatic / Light / Dark). Automatic follows the
system org.gnome.desktop.interface color-scheme; the dark theme is
unchanged. Light styling lives in light.css, every rule scoped under a
.strata-theme-light class the panel toggles on its root box, loaded into the
St theme context by the extension. St's CSS engine has no custom properties,
so this specificity-based scoped override is how the palette is switched
without generating a stylesheet at runtime; switching is a single class
toggle off the ingest/render hot paths.
Also in this change:
- fix: the keyboard shortcut now hides the panel when it is already open.
The binding lacked Shell.ActionMode.POPUP, so while the panel's modal grab
was active the second press was swallowed and toggle() never ran.
- fix: the search placeholder ("Search...") is now legible in the light
theme; it previously inherited a light-on-dark Shell color.
- pack: bundle light.css via --extra-source (gnome-extensions pack only
auto-includes stylesheet.css), so packaged installs ship the light theme.
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 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 implementation iterated all visible widgets on every
mouse-move event, calling get_transformed_position/size on each to
find which one was under the cursor. With a full page of items this
fires dozens of times per second on the compositor thread.
Use event.get_source() to let Clutter do its own hit-test (already
computed), then walk up the parent chain to find the owning
ClipboardItem. The walk is O(tree-depth), typically 3-4 steps,
regardless of history size.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each ClipboardItem widget is a direct child of _itemList.
destroy_all_children() already destroys all of them. Calling
widget.destroy() on each one beforehand triggered destruction twice on
the same actor, causing spurious warnings. Drop the forEach loop and
rely solely on destroy_all_children().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The extension uses ESM modules (GNOME 45+). The old imports.* global
does not exist in that context, causing a silent ReferenceError when
the strata-daemon binary is not found -- the user never saw the
notification. Replace with a proper import statement.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>