Commit graph

12 commits

Author SHA1 Message Date
6796a44802 refactor: drop dead fields and stale comments 2026-06-25 18:19:18 -05:00
306cedfd68 fix: retry the initial history load when the first fetch fails 2026-06-25 18:17:42 -05:00
0e9c83f2d8 extension: address shexli warnings before EGO submission
Funnel console.error through a logError helper per file so the static
analyzer sees one logging site instead of 11+13. The helper also strips the
GDBus.Error prefix, which removes the per-site strip_remote_error call.

Track the indicator's button-press signal ID and disconnect it explicitly in
disable, even though indicator.destroy() would clean it up too.

Track GLib.idle_add source IDs in a Set and flush pending ones in disable,
so deferred panel mutations cannot run on a torn-down extension.
2026-06-25 16:44:41 -05:00
8943136384 extension: align D-Bus client with the GJS guide
Route ItemAdded, ItemDeleted and HistoryCleared through proxy.connectSignal
instead of the low-level Gio.DBus.session.signal_subscribe. The wrapper is
already constructed and pre-unpacks signal args.

Strip the GDBus.Error: remote prefix from D-Bus errors before logging so the
journal shows the actual message.
2026-06-25 16:09:42 -05:00
43a68e7970 extension: cleanup before any future EGO submission
Remove narration breadcrumb comments, the unused _strataPreview field, the
self-referencing this.actor alias, and three non-error console.log calls.
Rename #busy to _busy for consistency with the rest of the class. Tighten a
couple of over-long docstrings.
2026-06-25 15:33:13 -05:00
107de1c3ff fix: extension supervisor + panel hardening
- 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>
2026-05-26 21:33:04 -05:00
46ce549b2f feat: lazy full-history search, rendered blink-free
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>
2026-05-26 17:29:18 -05:00
945ad61bf7 feat: light/dark theme support
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>
2026-05-26 14:49:08 -05:00
f9826c63a8 perf: return raw bytes from GetItemContent instead of base64
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>
2026-05-25 04:00:06 -05:00
575e216658 perf: replace O(n) hover loop with Clutter hit-test in motion-event
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>
2026-05-25 03:51:50 -05:00
64d5bbfdc3 fix: avoid double-destroy in clearItems()
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>
2026-05-25 03:43:09 -05:00
350d647953 Initial commit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 03:02:34 -05:00