fix: address EGO review 72215 feedback

- Use GNOME's stylesheet-dark.css/stylesheet-light.css for theming
  instead of loading light.css into the theme context by hand; the panel
  now follows the shell style variant
- Track signals with connectObject/disconnectObject
- Clear the daemon-restart timeout before scheduling a new one
- Stop the daemon synchronously in disable(), no deferred kill timer
- Drop the unused theme setting and its prefs combo
- Bump the metadata version to 11 for resubmission
This commit is contained in:
Eduard Tolosa 2026-07-02 13:43:52 -05:00
parent b6fe86f418
commit 6c4fa04221
9 changed files with 162 additions and 327 deletions

View file

@ -39,7 +39,6 @@ pack: schemas
--extra-source=ui \ --extra-source=ui \
--extra-source=dbus.js \ --extra-source=dbus.js \
--extra-source=util.js \ --extra-source=util.js \
--extra-source=light.css \
--force --force
@echo "Packed: $(EXTENSION_UUID).shell-extension.zip" @echo "Packed: $(EXTENSION_UUID).shell-extension.zip"

View file

@ -22,7 +22,6 @@ export default class StrataExtension extends Extension {
_daemonRestartAttempts = 0; _daemonRestartAttempts = 0;
_shuttingDown = false; _shuttingDown = false;
_daemonRestartTimerId = null; _daemonRestartTimerId = null;
_daemonKillTimerId = null;
/** True while a GetNameOwner check before spawning is in flight, so an /** True while a GetNameOwner check before spawning is in flight, so an
* overlapping respawn attempt can't spawn a second daemon. */ * overlapping respawn attempt can't spawn a second daemon. */
_spawnPending = false; _spawnPending = false;
@ -36,18 +35,12 @@ export default class StrataExtension extends Extension {
_itemDeletedId = null; _itemDeletedId = null;
_historyClearedId = null; _historyClearedId = null;
/** @type {number | null} focused-window signal connection ID */
_focusSignalId = null;
/** @type {string} WM class of the currently focused app (lower-cased) */ /** @type {string} WM class of the currently focused app (lower-cased) */
_currentFocusedApp = ''; _currentFocusedApp = '';
/** @type {object | null} PanelMenu.Button indicator */ /** @type {object | null} PanelMenu.Button indicator */
_indicator = null; _indicator = null;
/** @type {number | null} Meta.Selection owner-changed signal ID */
_selectionChangedId = null;
/** @type {number | null} Debounce timer for clipboard reads */ /** @type {number | null} Debounce timer for clipboard reads */
_clipboardDebounceId = null; _clipboardDebounceId = null;
@ -64,29 +57,16 @@ export default class StrataExtension extends Extension {
enable() { enable() {
this._shuttingDown = false; this._shuttingDown = false;
this._daemonRestartAttempts = 0; this._daemonRestartAttempts = 0;
// Cancel any pending force-kill timer left over from a previous disable
// (enable→disable→enable within 1.5s) so it can't outlive this cycle.
if (this._daemonKillTimerId) {
GLib.Source.remove(this._daemonKillTimerId);
this._daemonKillTimerId = null;
}
this._settings = this.getSettings(); this._settings = this.getSettings();
this._excludedApps = this._settings.get_strv('excluded-apps'); this._excludedApps = this._settings.get_strv('excluded-apps');
this._excludedAppsChangedId = this._settings.connect('changed::excluded-apps', () => {
this._excludedApps = this._settings.get_strv('excluded-apps');
});
this._readSizeLimits(); this._readSizeLimits();
this._configChangedIds = [ this._settings.connectObject(
this._settings.connect('changed::max-history', () => { this._readSizeLimits(); this._pushConfig(); }), 'changed::excluded-apps', () => { this._excludedApps = this._settings.get_strv('excluded-apps'); },
this._settings.connect('changed::max-text-mb', () => { this._readSizeLimits(); this._pushConfig(); }), 'changed::max-history', () => { this._readSizeLimits(); this._pushConfig(); },
this._settings.connect('changed::max-image-mb', () => { this._readSizeLimits(); this._pushConfig(); }), 'changed::max-text-mb', () => { this._readSizeLimits(); this._pushConfig(); },
]; 'changed::max-image-mb', () => { this._readSizeLimits(); this._pushConfig(); },
this);
// Load the light theme overrides into the Shell theme context. They stay
// inert until the panel toggles the `.strata-theme-light` class (panel.js).
this._loadThemeStylesheet();
this._addIndicator(); this._addIndicator();
this._spawnDaemon(); this._spawnDaemon();
@ -108,27 +88,13 @@ export default class StrataExtension extends Extension {
this._disconnectFocusTracking(); this._disconnectFocusTracking();
this._disconnectSignals(); this._disconnectSignals();
this._clearIdleSources(); this._clearIdleSources();
if (this._proxyOwnerId && this._proxy) { this._proxy?.disconnectObject(this);
this._proxy.disconnect(this._proxyOwnerId); this._settings?.disconnectObject(this);
this._proxyOwnerId = 0;
}
if (this._configChangedIds && this._settings) {
for (const id of this._configChangedIds) this._settings.disconnect(id);
this._configChangedIds = null;
}
if (this._excludedAppsChangedId && this._settings) {
this._settings.disconnect(this._excludedAppsChangedId);
this._excludedAppsChangedId = null;
}
this._panel?.destroy(); this._panel?.destroy();
this._panel = null; this._panel = null;
if (this._indicatorClickId && this._indicator) { this._indicator?.disconnectObject(this);
this._indicator.disconnect(this._indicatorClickId);
this._indicatorClickId = null;
}
this._indicator?.destroy(); this._indicator?.destroy();
this._indicator = null; this._indicator = null;
this._unloadThemeStylesheet();
this._stopDaemon(); this._stopDaemon();
this._proxy = null; this._proxy = null;
this._settings = null; this._settings = null;
@ -143,20 +109,19 @@ export default class StrataExtension extends Extension {
style_class: 'system-status-icon', style_class: 'system-status-icon',
}); });
this._indicator.add_child(icon); this._indicator.add_child(icon);
this._indicatorClickId = this._indicator.connect('button-press-event', () => { this._indicator.connectObject('button-press-event', () => {
this._panel?.toggle(); this._panel?.toggle();
return false; // EVENT_PROPAGATE return false; // EVENT_PROPAGATE
}); }, this);
Main.panel.addToStatusArea('strata', this._indicator); Main.panel.addToStatusArea('strata', this._indicator);
} }
_connectClipboardMonitor() { _connectClipboardMonitor() {
const selection = global.display.get_selection(); global.display.get_selection().connectObject('owner-changed', (_sel, type) => {
this._selectionChangedId = selection.connect('owner-changed', (_sel, type) => {
if (type !== Meta.SelectionType.SELECTION_CLIPBOARD) return; if (type !== Meta.SelectionType.SELECTION_CLIPBOARD) return;
this._scheduleClipboardRead(); this._scheduleClipboardRead();
}); }, this);
} }
_disconnectClipboardMonitor() { _disconnectClipboardMonitor() {
@ -167,10 +132,7 @@ export default class StrataExtension extends Extension {
this._clipboardDebounceId = null; this._clipboardDebounceId = null;
} }
this._clipboardTransferPending = false; this._clipboardTransferPending = false;
if (this._selectionChangedId !== null) { global.display.get_selection().disconnectObject(this);
global.display.get_selection().disconnect(this._selectionChangedId);
this._selectionChangedId = null;
}
} }
/** Debounce entry point - coalesces rapid clipboard changes (e.g. from /** Debounce entry point - coalesces rapid clipboard changes (e.g. from
@ -347,6 +309,10 @@ export default class StrataExtension extends Extension {
_scheduleDaemonRestart() { _scheduleDaemonRestart() {
if (this._shuttingDown) return; if (this._shuttingDown) return;
if (this._daemonRestartTimerId !== null) {
GLib.Source.remove(this._daemonRestartTimerId);
this._daemonRestartTimerId = null;
}
// Exponential backoff: 1s, 2s, 4s, 8s, 16s // Exponential backoff: 1s, 2s, 4s, 8s, 16s
const backoffMs = 1000 * Math.pow(2, Math.max(0, this._daemonRestartAttempts - 1)); const backoffMs = 1000 * Math.pow(2, Math.max(0, this._daemonRestartAttempts - 1));
this._daemonRestartTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, backoffMs, () => { this._daemonRestartTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, backoffMs, () => {
@ -358,20 +324,9 @@ export default class StrataExtension extends Extension {
_stopDaemon() { _stopDaemon() {
if (!this._daemon) return; if (!this._daemon) return;
// Capture reference immediately so the timer doesn't kill a newly-spawned daemon. const daemon = this._daemon;
const daemonToStop = this._daemon;
this._daemon = null; this._daemon = null;
try { try { daemon.send_signal(15); } catch { /* already exited */ } // SIGTERM
// Graceful shutdown via D-Bus first.
this._proxy?.ShutdownRemote(() => {});
} catch { /* proxy may already be gone */ }
// Give it 1.5s then force-terminate. Track the source so a re-enable
// within that window can cancel it (see enable()).
this._daemonKillTimerId = GLib.timeout_add(GLib.PRIORITY_LOW, 1500, () => {
this._daemonKillTimerId = null;
try { daemonToStop.send_signal(15); } catch { /* already exited */ } // SIGTERM
return GLib.SOURCE_REMOVE;
});
} }
@ -393,8 +348,8 @@ export default class StrataExtension extends Extension {
// again on every owner transition so a respawned // again on every owner transition so a respawned
// daemon picks up the latest values. // daemon picks up the latest values.
this._pushConfig(); this._pushConfig();
this._proxyOwnerId = proxy.connect('notify::g-name-owner', proxy.connectObject('notify::g-name-owner',
() => { if (proxy.g_name_owner) this._pushConfig(); }); () => { if (proxy.g_name_owner) this._pushConfig(); }, this);
} }
); );
} catch (e) { } catch (e) {
@ -511,17 +466,14 @@ export default class StrataExtension extends Extension {
_connectFocusTracking() { _connectFocusTracking() {
this._focusSignalId = global.display.connect('notify::focus-window', () => { global.display.connectObject('notify::focus-window', () => {
const win = global.display.focus_window; const win = global.display.focus_window;
this._currentFocusedApp = (win?.get_wm_class() ?? '').toLowerCase(); this._currentFocusedApp = (win?.get_wm_class() ?? '').toLowerCase();
}); }, this);
} }
_disconnectFocusTracking() { _disconnectFocusTracking() {
if (this._focusSignalId !== null) { global.display.disconnectObject(this);
global.display.disconnect(this._focusSignalId);
this._focusSignalId = null;
}
} }
@ -542,29 +494,4 @@ export default class StrataExtension extends Extension {
_unregisterShortcut() { _unregisterShortcut() {
Main.wm.removeKeybinding('keyboard-shortcut'); Main.wm.removeKeybinding('keyboard-shortcut');
} }
/** Load light.css once. It is scoped under `.strata-theme-light` and stays
* inert until the panel adds that class. We do not subscribe to the theme
* context's 'changed' signal because load_stylesheet itself emits it. */
_loadThemeStylesheet() {
try {
const themeContext = St.ThemeContext.get_for_stage(global.stage);
this._lightCssFile = this.dir.get_child('light.css');
this._stTheme = themeContext.get_theme();
this._stTheme.load_stylesheet(this._lightCssFile);
} catch (e) {
logError('Failed to load light.css', e);
}
}
_unloadThemeStylesheet() {
try {
this._stTheme?.unload_stylesheet(this._lightCssFile);
} catch (e) {
logError('Failed to unload light.css', e);
}
this._stTheme = null;
this._lightCssFile = null;
}
} }

View file

@ -1,143 +0,0 @@
/* Strata light theme.
*
* Loaded into the Shell theme context by extension.js and activated by toggling
* the `.strata-theme-light` class on the panel root box (see ui/panel.js).
* Every rule is scoped under `.strata-theme-light` so it is strictly more
* specific than its counterpart in stylesheet.css and wins deterministically,
* regardless of stylesheet load order. The dark theme (stylesheet.css) is left
* untouched: with no class present, none of these rules apply.
*
* Colors follow GNOME Adwaita (blue #3584e4, link #1a5fb4, amber #b5830a,
* red #e01b24) and are picked for contrast on a light panel.
*
* Performance rule (same as stylesheet.css): NO `transition: all`, NO
* Clutter/CSS animations. Only background-color transitions on hover.
*/
/* ── Panel container ────────────────────────────────────────────────────── */
.strata-panel.strata-theme-light {
background-color: rgba(250, 250, 250, 0.98);
border-color: rgba(0, 0, 0, 0.14);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.28);
}
/* ── Header ─────────────────────────────────────────────────────────────── */
.strata-theme-light .strata-title {
color: rgba(0, 0, 0, 0.87);
}
.strata-theme-light .strata-clear-btn {
border-color: rgba(0, 0, 0, 0.18);
color: rgba(0, 0, 0, 0.55);
}
.strata-theme-light .strata-clear-btn:hover {
background-color: rgba(0, 0, 0, 0.07);
color: rgba(0, 0, 0, 0.85);
}
/* ── Search box ─────────────────────────────────────────────────────────── */
.strata-theme-light .strata-search {
background-color: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.14);
color: rgba(0, 0, 0, 0.87);
caret-color: rgba(0, 0, 0, 0.8);
}
.strata-theme-light .strata-search:focus {
border-color: rgba(53, 132, 228, 0.8);
background-color: rgba(0, 0, 0, 0.035);
}
/* Placeholder ("Search...") text - legible dim gray on the light panel. */
.strata-theme-light .strata-search-hint {
color: rgba(0, 0, 0, 0.55);
}
/* ── Clipboard item: hover / focus (Adwaita blue) ───────────────────────── */
.strata-theme-light .strata-item:hover,
.strata-theme-light .strata-item-hovered {
background-color: rgba(53, 132, 228, 0.14);
border-color: rgba(53, 132, 228, 0.45);
}
.strata-theme-light .strata-item:focus,
.strata-theme-light .strata-item-focused {
background-color: rgba(53, 132, 228, 0.16);
border-color: rgba(53, 132, 228, 0.65);
}
.strata-theme-light .strata-item:hover:focus,
.strata-theme-light .strata-item-focused.strata-item-hovered {
background-color: rgba(53, 132, 228, 0.24);
border-color: rgba(53, 132, 228, 0.78);
}
.strata-theme-light .strata-item:focus .strata-item-text,
.strata-theme-light .strata-item-focused .strata-item-text {
color: rgba(0, 0, 0, 0.95);
}
/* ── Active item (warm amber, darkened for light bg) ────────────────────── */
.strata-theme-light .strata-item-active {
background-color: rgba(181, 131, 10, 0.16);
border-color: rgba(181, 131, 10, 0.50);
}
.strata-theme-light .strata-item-active .strata-item-text {
color: rgba(120, 82, 0, 1.0);
}
.strata-theme-light .strata-item-active.strata-item-hovered {
background-color: rgba(181, 131, 10, 0.24);
}
.strata-theme-light .strata-item-active.strata-item-focused,
.strata-theme-light .strata-item-active:focus {
background-color: rgba(53, 132, 228, 0.20);
border-color: rgba(53, 132, 228, 0.65);
}
.strata-theme-light .strata-item-active.strata-item-focused .strata-item-text,
.strata-theme-light .strata-item-active:focus .strata-item-text {
color: rgba(0, 0, 0, 0.95);
}
.strata-theme-light .strata-item:active {
background-color: rgba(0, 0, 0, 0.10);
}
/* ── Item icon / thumbnail ──────────────────────────────────────────────── */
.strata-theme-light .strata-item-icon {
color: rgba(0, 0, 0, 0.55);
}
.strata-theme-light .strata-item-thumb {
border-color: rgba(0, 0, 0, 0.15);
}
/* ── Text content ───────────────────────────────────────────────────────── */
.strata-theme-light .strata-item-text {
color: rgba(0, 0, 0, 0.85);
}
.strata-theme-light .strata-item-url {
color: rgba(26, 95, 180, 1.0);
}
.strata-theme-light .strata-item-subtext {
color: rgba(0, 0, 0, 0.55);
}
/* ── Delete button (Adwaita red) ────────────────────────────────────────── */
.strata-theme-light .strata-item:hover .strata-item-delete,
.strata-theme-light .strata-item:focus .strata-item-delete,
.strata-theme-light .strata-item-focused .strata-item-delete,
.strata-theme-light .strata-item-hovered .strata-item-delete {
color: rgba(224, 27, 36, 0.75);
}
.strata-theme-light .strata-item-delete:hover {
background-color: rgba(224, 27, 36, 0.13);
color: rgba(224, 27, 36, 1.0);
}

View file

@ -2,7 +2,7 @@
"name": "Strata", "name": "Strata",
"description": "A fast clipboard manager. Storage, search and image decoding run in a separate Rust daemon.", "description": "A fast clipboard manager. Storage, search and image decoding run in a separate Rust daemon.",
"uuid": "strata@edu4rdshl.dev", "uuid": "strata@edu4rdshl.dev",
"version": 10, "version": 11,
"shell-version": ["50"], "shell-version": ["50"],
"settings-schema": "org.gnome.shell.extensions.strata", "settings-schema": "org.gnome.shell.extensions.strata",
"url": "https://github.com/Edu4rdSHL/Strata" "url": "https://github.com/Edu4rdSHL/Strata"

View file

@ -79,29 +79,6 @@ export default class StrataPreferences extends ExtensionPreferences {
const appearanceGroup = new Adw.PreferencesGroup({ title: 'Appearance' }); const appearanceGroup = new Adw.PreferencesGroup({ title: 'Appearance' });
const themes = [
{ id: 'auto', label: 'Automatic' },
{ id: 'light', label: 'Light' },
{ id: 'dark', label: 'Dark' },
];
const themeRow = new Adw.ComboRow({
title: 'Theme',
subtitle: 'Automatic follows the system light/dark preference',
model: Gtk.StringList.new(themes.map(t => t.label)),
});
const currentTheme = settings.get_string('theme');
const currentThemeIdx = themes.findIndex(t => t.id === currentTheme);
themeRow.selected = currentThemeIdx >= 0 ? currentThemeIdx : 0;
themeRow.connect('notify::selected', () => {
settings.set_string('theme', themes[themeRow.selected].id);
});
settings.connect('changed::theme', () => {
const idx = themes.findIndex(t => t.id === settings.get_string('theme'));
if (idx >= 0 && themeRow.selected !== idx)
themeRow.selected = idx;
});
appearanceGroup.add(themeRow);
const panelWidthRow = new Adw.SpinRow({ const panelWidthRow = new Adw.SpinRow({
title: 'Panel width', title: 'Panel width',
subtitle: 'Width of the clipboard panel in pixels', subtitle: 'Width of the clipboard panel in pixels',

View file

@ -52,17 +52,6 @@
<description>When enabled, clicking or pressing Enter on a history item moves it to position 1 in the list.</description> <description>When enabled, clicking or pressing Enter on a history item moves it to position 1 in the list.</description>
</key> </key>
<key name="theme" type="s">
<choices>
<choice value="auto"/>
<choice value="light"/>
<choice value="dark"/>
</choices>
<default>'auto'</default>
<summary>Panel color theme</summary>
<description>Auto follows the system light/dark color scheme (org.gnome.desktop.interface color-scheme). Light and Dark force a fixed theme.</description>
</key>
<key name="panel-position" type="s"> <key name="panel-position" type="s">
<choices> <choices>
<choice value="top-center"/> <choice value="top-center"/>

View file

@ -1,4 +1,4 @@
/* Strata clipboard manager stylesheet. /* Strata dark theme (the default; loaded when the system color scheme is dark).
* *
* Performance rule: NO `transition: all`, NO Clutter/CSS animations. * Performance rule: NO `transition: all`, NO Clutter/CSS animations.
* Only background-color transitions on hover (GPU-composited, zero JS). * Only background-color transitions on hover (GPU-composited, zero JS).

View file

@ -0,0 +1,129 @@
/* Strata light theme.
*
* GNOME Shell loads this instead of stylesheet-dark.css when the system color
* scheme is light. It imports the dark base and overrides the colors below, so
* the overrides win by load order. Colors follow GNOME Adwaita (blue #3584e4,
* link #1a5fb4, amber, red #e01b24), picked for contrast on a light panel.
*/
@import url("stylesheet-dark.css");
.strata-panel {
background-color: rgba(250, 250, 250, 0.98);
border-color: rgba(0, 0, 0, 0.14);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.28);
}
.strata-title {
color: rgba(0, 0, 0, 0.87);
}
.strata-clear-btn {
border-color: rgba(0, 0, 0, 0.18);
color: rgba(0, 0, 0, 0.55);
}
.strata-clear-btn:hover {
background-color: rgba(0, 0, 0, 0.07);
color: rgba(0, 0, 0, 0.85);
}
.strata-search {
background-color: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.14);
color: rgba(0, 0, 0, 0.87);
caret-color: rgba(0, 0, 0, 0.8);
}
.strata-search:focus {
border-color: rgba(53, 132, 228, 0.8);
background-color: rgba(0, 0, 0, 0.035);
}
/* Placeholder ("Search...") text - legible dim gray on the light panel. */
.strata-search-hint {
color: rgba(0, 0, 0, 0.55);
}
.strata-item:hover,
.strata-item-hovered {
background-color: rgba(53, 132, 228, 0.14);
border-color: rgba(53, 132, 228, 0.45);
}
.strata-item:focus,
.strata-item-focused {
background-color: rgba(53, 132, 228, 0.16);
border-color: rgba(53, 132, 228, 0.65);
}
.strata-item:hover:focus,
.strata-item-focused.strata-item-hovered {
background-color: rgba(53, 132, 228, 0.24);
border-color: rgba(53, 132, 228, 0.78);
}
.strata-item:focus .strata-item-text,
.strata-item-focused .strata-item-text {
color: rgba(0, 0, 0, 0.95);
}
.strata-item-active {
background-color: rgba(181, 131, 10, 0.16);
border-color: rgba(181, 131, 10, 0.50);
}
.strata-item-active .strata-item-text {
color: rgba(120, 82, 0, 1.0);
}
.strata-item-active.strata-item-hovered {
background-color: rgba(181, 131, 10, 0.24);
}
.strata-item-active.strata-item-focused,
.strata-item-active:focus {
background-color: rgba(53, 132, 228, 0.20);
border-color: rgba(53, 132, 228, 0.65);
}
.strata-item-active.strata-item-focused .strata-item-text,
.strata-item-active:focus .strata-item-text {
color: rgba(0, 0, 0, 0.95);
}
.strata-item:active {
background-color: rgba(0, 0, 0, 0.10);
}
.strata-item-icon {
color: rgba(0, 0, 0, 0.55);
}
.strata-item-thumb {
border-color: rgba(0, 0, 0, 0.15);
}
.strata-item-text {
color: rgba(0, 0, 0, 0.85);
}
.strata-item-url {
color: rgba(26, 95, 180, 1.0);
}
.strata-item-subtext {
color: rgba(0, 0, 0, 0.55);
}
.strata-item:hover .strata-item-delete,
.strata-item:focus .strata-item-delete,
.strata-item-focused .strata-item-delete,
.strata-item-hovered .strata-item-delete {
color: rgba(224, 27, 36, 0.75);
}
.strata-item-delete:hover {
background-color: rgba(224, 27, 36, 0.13);
color: rgba(224, 27, 36, 1.0);
}

View file

@ -1,7 +1,6 @@
/* panel.js - Strata clipboard popup panel. */ /* panel.js - Strata clipboard popup panel. */
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import St from 'gi://St'; import St from 'gi://St';
import Clutter from 'gi://Clutter'; import Clutter from 'gi://Clutter';
import Meta from 'gi://Meta'; import Meta from 'gi://Meta';
@ -19,8 +18,8 @@ export class StrataPanel {
this._proxy = proxy; this._proxy = proxy;
this._settings = settings; this._settings = settings;
this._pageSize = settings.get_int('page-size'); this._pageSize = settings.get_int('page-size');
this._pageSizeChangedId = settings.connect('changed::page-size', settings.connectObject('changed::page-size',
() => { this._pageSize = settings.get_int('page-size'); }); () => { this._pageSize = settings.get_int('page-size'); }, this);
/** @type {{ id: string, mimeType: string, preview: string }[]} */ /** @type {{ id: string, mimeType: string, preview: string }[]} */
this._items = []; this._items = [];
/** @type {Map<string, ClipboardItem>} id → widget */ /** @type {Map<string, ClipboardItem>} id → widget */
@ -46,16 +45,6 @@ export class StrataPanel {
this._buildUI(); this._buildUI();
// Theme: apply light/dark to the panel box and react to changes.
// The light.css overrides (loaded by extension.js) only take effect
// while the `.strata-theme-light` class is present on `_box`.
this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
this._applyTheme();
this._themeChangedId = this._settings.connect('changed::theme',
() => this._applyTheme());
this._colorSchemeChangedId = this._interfaceSettings.connect('changed::color-scheme',
() => this._applyTheme());
// Trigger the initial load as soon as the daemon owns its bus name. // Trigger the initial load as soon as the daemon owns its bus name.
// At extension boot the daemon may still be starting (its D-Bus name // At extension boot the daemon may still be starting (its D-Bus name
// not yet owned); the proxy fires notify::g-name-owner when that // not yet owned); the proxy fires notify::g-name-owner when that
@ -63,25 +52,8 @@ export class StrataPanel {
// and only then issue GetHistory. If the daemon is already up, the // and only then issue GetHistory. If the daemon is already up, the
// owner is already set and we load immediately. // owner is already set and we load immediately.
this._tryInitialLoad(); this._tryInitialLoad();
this._nameOwnerId = this._proxy.connect('notify::g-name-owner', this._proxy.connectObject('notify::g-name-owner',
() => this._tryInitialLoad()); () => this._tryInitialLoad(), this);
}
/** True when the effective theme is light. `auto` follows the system
* color-scheme (anything other than prefer-dark counts as light). */
_effectiveIsLight() {
const mode = this._settings.get_string('theme');
if (mode === 'light') return true;
if (mode === 'dark') return false;
return this._interfaceSettings.get_string('color-scheme') !== 'prefer-dark';
}
_applyTheme() {
if (!this._box) return;
if (this._effectiveIsLight())
this._box.add_style_class_name('strata-theme-light');
else
this._box.remove_style_class_name('strata-theme-light');
} }
_tryInitialLoad() { _tryInitialLoad() {
@ -342,23 +314,8 @@ export class StrataPanel {
} }
destroy() { destroy() {
if (this._pageSizeChangedId) { this._settings.disconnectObject(this);
this._settings.disconnect(this._pageSizeChangedId); this._proxy.disconnectObject(this);
this._pageSizeChangedId = 0;
}
if (this._themeChangedId) {
this._settings.disconnect(this._themeChangedId);
this._themeChangedId = 0;
}
if (this._colorSchemeChangedId && this._interfaceSettings) {
this._interfaceSettings.disconnect(this._colorSchemeChangedId);
this._colorSchemeChangedId = 0;
}
this._interfaceSettings = null;
if (this._nameOwnerId) {
this._proxy.disconnect(this._nameOwnerId);
this._nameOwnerId = 0;
}
if (this._searchDebounceId) { if (this._searchDebounceId) {
GLib.Source.remove(this._searchDebounceId); GLib.Source.remove(this._searchDebounceId);
this._searchDebounceId = null; this._searchDebounceId = null;