fix: rename D-Bus name to dev.edu4rdshl.Strata; drop recursive theme reload

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>
This commit is contained in:
Eduard Tolosa 2026-05-26 19:20:12 -05:00
parent 3f32c19fd0
commit b41c9662cb
8 changed files with 40 additions and 44 deletions

View file

@ -72,7 +72,7 @@ ssh fedoradev 'glib-compile-schemas ~/.local/share/gnome-shell/extensions/strata
## D-Bus interface summary
Service `org.gnome.Strata`, object `/org/gnome/Strata`, interface `org.gnome.Strata.Manager`.
Service `dev.edu4rdshl.Strata`, object `/dev/edu4rdshl/Strata`, interface `dev.edu4rdshl.Strata.Manager`.
Methods: `GetHistory(offset u32, limit u32) -> json s`,
`SearchHistory(query s, limit u32) -> json s`,

View file

@ -24,7 +24,7 @@ together and the trade-offs at each layer.
| | ItemAdded / ItemDeleted / HistoryCleared |
+--------+-------------------------------------------------------------------------+
|
| session D-Bus (org.gnome.Strata)
| session D-Bus (dev.edu4rdshl.Strata)
v
+----------------------- strata-daemon (Rust process) --------------------------+
| |
@ -68,7 +68,7 @@ hot ingest path (it fires `SubmitItem` and returns immediately).
### Startup
The extension listens for `notify::g-name-owner` on the D-Bus proxy. When
the daemon's `org.gnome.Strata` name becomes owned, the panel triggers
the daemon's `dev.edu4rdshl.Strata` name becomes owned, the panel triggers
its initial fetch. No polling, no fixed delays.
## Ingest path
@ -313,7 +313,7 @@ and it is small and isolated.
| App to history | Password leak | `x-kde-passwordManagerHint` opt-out |
| User to search | SQL injection | rusqlite `params![]`, FTS5 input bound as value |
| Daemon to FS | Path traversal via id | Ids are server-generated UUID v4 |
| Daemon to extension | Signal spoofing | D-Bus enforces single owner of `org.gnome.Strata` |
| Daemon to extension | Signal spoofing | D-Bus enforces single owner of `dev.edu4rdshl.Strata` |
| Stored item to paste | Command execution | No spawn, no launch_uri, no markup parsing |
## Non-GNOME front-end

View file

@ -12,7 +12,7 @@ Strata is **two components**, and you need **both** for it to work:
| Component | Language | Role |
|---|---|---|
| [`strata-daemon/`](strata-daemon/) | Rust + tokio + zbus | Storage (SQLite + FTS5), dedup, thumbnails, D-Bus service `org.gnome.Strata` |
| [`strata-daemon/`](strata-daemon/) | Rust + tokio + zbus | Storage (SQLite + FTS5), dedup, thumbnails, D-Bus service `dev.edu4rdshl.Strata` |
| [`strata@edu4rdshl.dev/`](strata@edu4rdshl.dev/) | GJS (GNOME Shell extension) | Top-bar panel, search UI, paste-back, clipboard ingest |
The extension auto-connects to the daemon on enable. If the daemon is managed

View file

@ -24,9 +24,9 @@ manually if you're using a non-GNOME front-end.
## D-Bus interface
- **Bus name:** `org.gnome.Strata`
- **Object path:** `/org/gnome/Strata`
- **Interface:** `org.gnome.Strata.Manager`
- **Bus name:** `dev.edu4rdshl.Strata`
- **Object path:** `/dev/edu4rdshl/Strata`
- **Interface:** `dev.edu4rdshl.Strata.Manager`
| Member | Signature | Notes |
|---|---|---|
@ -51,15 +51,15 @@ manually if you're using a non-GNOME front-end.
./target/release/strata-daemon
# In another terminal:
busctl --user call org.gnome.Strata /org/gnome/Strata \
org.gnome.Strata.Manager GetHistory uu 0 10
busctl --user call dev.edu4rdshl.Strata /dev/edu4rdshl/Strata \
dev.edu4rdshl.Strata.Manager GetHistory uu 0 10
# Submit text:
busctl --user call org.gnome.Strata /org/gnome/Strata \
org.gnome.Strata.Manager SubmitItem say "text/plain;charset=utf-8" 5 104 101 108 108 111
busctl --user call dev.edu4rdshl.Strata /dev/edu4rdshl/Strata \
dev.edu4rdshl.Strata.Manager SubmitItem say "text/plain;charset=utf-8" 5 104 101 108 108 111
# Listen for new items:
busctl --user monitor org.gnome.Strata
busctl --user monitor dev.edu4rdshl.Strata
```
## Supported clipboard payloads

View file

@ -57,7 +57,7 @@ pub struct StrataManager {
pub shutdown_tx: tokio::sync::mpsc::UnboundedSender<()>,
}
#[interface(name = "org.gnome.Strata.Manager")]
#[interface(name = "dev.edu4rdshl.Strata.Manager")]
impl StrataManager {
/// Called by the GJS extension when it detects a clipboard change via Meta.Selection.
/// `content` is the raw bytes of the clipboard payload (D-Bus `ay`).

View file

@ -49,7 +49,7 @@ async fn main() -> Result<()> {
};
let dbus_conn = zbus::connection::Builder::session()?
.serve_at("/org/gnome/Strata", manager)?
.serve_at("/dev/edu4rdshl/Strata", manager)?
.build()
.await
.context("Building D-Bus connection")?;
@ -61,14 +61,14 @@ async fn main() -> Result<()> {
// would be refused.
dbus_conn
.request_name_with_flags(
"org.gnome.Strata",
"dev.edu4rdshl.Strata",
zbus::fdo::RequestNameFlags::ReplaceExisting
| zbus::fdo::RequestNameFlags::AllowReplacement,
)
.await
.context("Acquiring D-Bus name org.gnome.Strata")?;
.context("Acquiring D-Bus name dev.edu4rdshl.Strata")?;
tracing::info!("D-Bus service registered as org.gnome.Strata");
tracing::info!("D-Bus service registered as dev.edu4rdshl.Strata");
// -----------------------------------------------------------------------
// Clipboard monitor (Wayland protocols - optional, soft-fail on GNOME).
@ -312,7 +312,7 @@ async fn process_bytes(
if is_new {
let iface = conn
.object_server()
.interface::<_, StrataManager>("/org/gnome/Strata")
.interface::<_, StrataManager>("/dev/edu4rdshl/Strata")
.await
.context("Getting StrataManager interface ref")?;

View file

@ -10,7 +10,7 @@ import Gio from 'gi://Gio';
const STRATA_IFACE_XML = `
<node>
<interface name="org.gnome.Strata.Manager">
<interface name="dev.edu4rdshl.Strata.Manager">
<method name="GetHistory">
<arg type="u" direction="in" name="offset"/>
@ -75,5 +75,5 @@ const STRATA_IFACE_XML = `
export const StrataProxy = Gio.DBusProxy.makeProxyWrapper(STRATA_IFACE_XML);
export const BUS_NAME = 'org.gnome.Strata';
export const OBJECT_PATH = '/org/gnome/Strata';
export const BUS_NAME = 'dev.edu4rdshl.Strata';
export const OBJECT_PATH = '/dev/edu4rdshl/Strata';

View file

@ -440,7 +440,7 @@ export default class StrataExtension extends Extension {
_connectSignals() {
this._itemAddedId = Gio.DBus.session.signal_subscribe(
BUS_NAME,
'org.gnome.Strata.Manager',
'dev.edu4rdshl.Strata.Manager',
'ItemAdded',
OBJECT_PATH,
null,
@ -450,7 +450,7 @@ export default class StrataExtension extends Extension {
this._itemDeletedId = Gio.DBus.session.signal_subscribe(
BUS_NAME,
'org.gnome.Strata.Manager',
'dev.edu4rdshl.Strata.Manager',
'ItemDeleted',
OBJECT_PATH,
null,
@ -473,7 +473,7 @@ export default class StrataExtension extends Extension {
this._historyClearedId = Gio.DBus.session.signal_subscribe(
BUS_NAME,
'org.gnome.Strata.Manager',
'dev.edu4rdshl.Strata.Manager',
'HistoryCleared',
OBJECT_PATH,
null,
@ -600,22 +600,23 @@ export default class StrataExtension extends Extension {
}
/** Load light.css into the global St theme context. Scoped under
* `.strata-theme-light`, so it does nothing until the panel adds that
* class. Reloaded on theme-context changes (a Shell-theme switch drops
* dynamically loaded stylesheets). */
/** Load light.css into the global St theme context ONCE. It is scoped under
* `.strata-theme-light`, so it stays inert until the panel adds that class
* (dark/light switching is the panel's class toggle, independent of this).
*
* We deliberately do NOT subscribe to the theme context's 'changed'
* signal: load_stylesheet itself emits 'changed', so reloading on it feeds
* back into itself and hits "too much recursion" (it fired on screen
* unlock, which restyles widgets). The only thing a one-time load gives up
* is re-applying after a GNOME Shell *theme* switch (which replaces the
* St.Theme and drops this sheet) - a rare action, recoverable by toggling
* the extension. That trade is worth never touching the signal. */
_loadThemeStylesheet() {
try {
const themeContext = St.ThemeContext.get_for_stage(global.stage);
this._lightCssFile = this.dir.get_child('light.css');
themeContext.get_theme().load_stylesheet(this._lightCssFile);
this._stThemeChangedId = themeContext.connect('changed', () => {
try {
themeContext.get_theme().load_stylesheet(this._lightCssFile);
} catch (e) {
console.error('[Strata] light.css reload failed:', e);
}
});
this._stTheme = themeContext.get_theme();
this._stTheme.load_stylesheet(this._lightCssFile);
} catch (e) {
console.error('[Strata] Failed to load light.css:', e);
}
@ -623,16 +624,11 @@ export default class StrataExtension extends Extension {
_unloadThemeStylesheet() {
try {
const themeContext = St.ThemeContext.get_for_stage(global.stage);
if (this._stThemeChangedId) {
themeContext.disconnect(this._stThemeChangedId);
this._stThemeChangedId = null;
}
if (this._lightCssFile)
themeContext.get_theme().unload_stylesheet(this._lightCssFile);
this._stTheme?.unload_stylesheet(this._lightCssFile);
} catch (e) {
console.error('[Strata] Failed to unload light.css:', e);
}
this._stTheme = null;
this._lightCssFile = null;
}
}