mirror of
https://github.com/edu4rdshl/Strata.git
synced 2026-07-17 23:24:46 +00:00
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.
This commit is contained in:
parent
3fff367364
commit
43a68e7970
5 changed files with 16 additions and 83 deletions
|
|
@ -58,7 +58,7 @@ export default class StrataExtension extends Extension {
|
||||||
_pendingSignalId = null;
|
_pendingSignalId = null;
|
||||||
|
|
||||||
/** @type {boolean} Re-entrancy guard for signal processing */
|
/** @type {boolean} Re-entrancy guard for signal processing */
|
||||||
#busy = false;
|
_busy = false;
|
||||||
|
|
||||||
/** @type {number | null} Keyboard shortcut binding ID */
|
/** @type {number | null} Keyboard shortcut binding ID */
|
||||||
_shortcutId = null;
|
_shortcutId = null;
|
||||||
|
|
@ -90,22 +90,11 @@ export default class StrataExtension extends Extension {
|
||||||
// inert until the panel toggles the `.strata-theme-light` class (panel.js).
|
// inert until the panel toggles the `.strata-theme-light` class (panel.js).
|
||||||
this._loadThemeStylesheet();
|
this._loadThemeStylesheet();
|
||||||
|
|
||||||
// 1. Top-bar indicator icon.
|
|
||||||
this._addIndicator();
|
this._addIndicator();
|
||||||
|
|
||||||
// 2. Spawn the Rust daemon.
|
|
||||||
this._spawnDaemon();
|
this._spawnDaemon();
|
||||||
|
|
||||||
// 3. Connect D-Bus proxy (async - doesn't block if daemon isn't ready yet).
|
|
||||||
this._connectProxy();
|
this._connectProxy();
|
||||||
|
|
||||||
// 4. Track focused window (lightweight - no clipboard I/O).
|
|
||||||
this._connectFocusTracking();
|
this._connectFocusTracking();
|
||||||
|
|
||||||
// 5. Monitor clipboard via Meta.Selection (GNOME-native, no Wayland protocol needed).
|
|
||||||
this._connectClipboardMonitor();
|
this._connectClipboardMonitor();
|
||||||
|
|
||||||
// 6. Register keyboard shortcut.
|
|
||||||
this._registerShortcut();
|
this._registerShortcut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,9 +250,7 @@ export default class StrataExtension extends Extension {
|
||||||
];
|
];
|
||||||
for (const want of PREFERRED)
|
for (const want of PREFERRED)
|
||||||
if (mimes.includes(want)) return want;
|
if (mimes.includes(want)) return want;
|
||||||
// Allowlist only: see comment in pick_mime (daemon). Reading unknown
|
// Allowlist only: see pick_mime in the daemon. Unknown mime types are skipped.
|
||||||
// mime types could pull a 1 GB blob into Shell memory before we can
|
|
||||||
// size-check it.
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,10 +275,8 @@ export default class StrataExtension extends Extension {
|
||||||
if (this._shuttingDown) return;
|
if (this._shuttingDown) return;
|
||||||
try {
|
try {
|
||||||
_conn.call_finish(result);
|
_conn.call_finish(result);
|
||||||
// Name already owned - daemon managed externally (systemd etc).
|
// Name already owned (e.g. systemd user service) - don't spawn a second instance.
|
||||||
console.log('[Strata] daemon already running, skipping spawn');
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// Name not owned - spawn it ourselves.
|
|
||||||
this._doSpawnDaemon();
|
this._doSpawnDaemon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -351,10 +336,7 @@ export default class StrataExtension extends Extension {
|
||||||
const lifetimeMs = (GLib.get_monotonic_time() / 1000) - this._daemonSpawnTime;
|
const lifetimeMs = (GLib.get_monotonic_time() / 1000) - this._daemonSpawnTime;
|
||||||
this._daemon = null;
|
this._daemon = null;
|
||||||
|
|
||||||
if (this._shuttingDown) {
|
if (this._shuttingDown) return;
|
||||||
console.log(`[Strata] daemon exited cleanly during shutdown (status=${exit})`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset attempt counter if the daemon ran long enough to be considered healthy.
|
// Reset attempt counter if the daemon ran long enough to be considered healthy.
|
||||||
if (lifetimeMs >= 5000) {
|
if (lifetimeMs >= 5000) {
|
||||||
|
|
@ -380,7 +362,6 @@ export default class StrataExtension extends Extension {
|
||||||
if (this._shuttingDown) return;
|
if (this._shuttingDown) return;
|
||||||
// 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));
|
||||||
console.log(`[Strata] respawning daemon in ${backoffMs}ms`);
|
|
||||||
this._daemonRestartTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, backoffMs, () => {
|
this._daemonRestartTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, backoffMs, () => {
|
||||||
this._daemonRestartTimerId = null;
|
this._daemonRestartTimerId = null;
|
||||||
this._spawnDaemon();
|
this._spawnDaemon();
|
||||||
|
|
@ -549,8 +530,8 @@ export default class StrataExtension extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _processItemAdded(params) {
|
async _processItemAdded(params) {
|
||||||
if (this.#busy) return;
|
if (this._busy) return;
|
||||||
this.#busy = true;
|
this._busy = true;
|
||||||
try {
|
try {
|
||||||
const [id, mimeType, preview] = params.deepUnpack();
|
const [id, mimeType, preview] = params.deepUnpack();
|
||||||
|
|
||||||
|
|
@ -570,7 +551,7 @@ export default class StrataExtension extends Extension {
|
||||||
return GLib.SOURCE_REMOVE;
|
return GLib.SOURCE_REMOVE;
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
this.#busy = false;
|
this._busy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,17 +595,9 @@ export default class StrataExtension extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Load light.css into the global St theme context ONCE. It is scoped under
|
/** Load light.css once. It is scoped under `.strata-theme-light` and stays
|
||||||
* `.strata-theme-light`, so it stays inert until the panel adds that class
|
* inert until the panel adds that class. We do not subscribe to the theme
|
||||||
* (dark/light switching is the panel's class toggle, independent of this).
|
* context's 'changed' signal because load_stylesheet itself emits it. */
|
||||||
*
|
|
||||||
* 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() {
|
_loadThemeStylesheet() {
|
||||||
try {
|
try {
|
||||||
const themeContext = St.ThemeContext.get_for_stage(global.stage);
|
const themeContext = St.ThemeContext.get_for_stage(global.stage);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Strata",
|
"name": "Strata",
|
||||||
"description": "A fast, stutter-free clipboard manager. All I/O runs in a Rust daemon - GNOME Shell is never blocked.",
|
"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": 8,
|
"version": 8,
|
||||||
"shell-version": ["45", "46", "47", "48", "49", "50"],
|
"shell-version": ["45", "46", "47", "48", "49", "50"],
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
/**
|
/* prefs.js - Strata preferences window. */
|
||||||
* prefs.js - Strata preferences window (GNOME 45+ / Adw).
|
|
||||||
*
|
|
||||||
* Pages:
|
|
||||||
* General: max-history SpinRow, keyboard-shortcut ShortcutRow
|
|
||||||
* Privacy: excluded-apps ExpanderRow + StringList (one entry per line)
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Adw from 'gi://Adw';
|
import Adw from 'gi://Adw';
|
||||||
import Gtk from 'gi://Gtk';
|
import Gtk from 'gi://Gtk';
|
||||||
|
|
@ -22,17 +16,12 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
window.add(this._buildPrivacyPage(settings));
|
window.add(this._buildPrivacyPage(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// General page
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_buildGeneralPage(settings) {
|
_buildGeneralPage(settings) {
|
||||||
const page = new Adw.PreferencesPage({
|
const page = new Adw.PreferencesPage({
|
||||||
title: 'General',
|
title: 'General',
|
||||||
icon_name: 'preferences-system-symbolic',
|
icon_name: 'preferences-system-symbolic',
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── History ──────────────────────────────────────────────────────────
|
|
||||||
const historyGroup = new Adw.PreferencesGroup({ title: 'History' });
|
const historyGroup = new Adw.PreferencesGroup({ title: 'History' });
|
||||||
|
|
||||||
const maxHistoryRow = new Adw.SpinRow({
|
const maxHistoryRow = new Adw.SpinRow({
|
||||||
|
|
@ -89,7 +78,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
|
|
||||||
page.add(historyGroup);
|
page.add(historyGroup);
|
||||||
|
|
||||||
// ── Appearance ───────────────────────────────────────────────────────
|
|
||||||
const appearanceGroup = new Adw.PreferencesGroup({ title: 'Appearance' });
|
const appearanceGroup = new Adw.PreferencesGroup({ title: 'Appearance' });
|
||||||
|
|
||||||
const themes = [
|
const themes = [
|
||||||
|
|
@ -180,7 +168,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
|
|
||||||
page.add(appearanceGroup);
|
page.add(appearanceGroup);
|
||||||
|
|
||||||
// ── Keyboard ─────────────────────────────────────────────────────────
|
|
||||||
const kbGroup = new Adw.PreferencesGroup({ title: 'Keyboard' });
|
const kbGroup = new Adw.PreferencesGroup({ title: 'Keyboard' });
|
||||||
|
|
||||||
const shortcutRow = new Adw.ActionRow({
|
const shortcutRow = new Adw.ActionRow({
|
||||||
|
|
@ -208,10 +195,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Privacy page
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_buildPrivacyPage(settings) {
|
_buildPrivacyPage(settings) {
|
||||||
const page = new Adw.PreferencesPage({
|
const page = new Adw.PreferencesPage({
|
||||||
title: 'Privacy',
|
title: 'Privacy',
|
||||||
|
|
@ -223,7 +206,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
description: 'Items copied while these apps have focus will not be stored in history. Enter a partial app name (case-insensitive).',
|
description: 'Items copied while these apps have focus will not be stored in history. Enter a partial app name (case-insensitive).',
|
||||||
});
|
});
|
||||||
|
|
||||||
// We use a StringList model bound to excluded-apps.
|
|
||||||
const model = new Gtk.StringList();
|
const model = new Gtk.StringList();
|
||||||
const currentApps = settings.get_strv('excluded-apps');
|
const currentApps = settings.get_strv('excluded-apps');
|
||||||
for (const app of currentApps)
|
for (const app of currentApps)
|
||||||
|
|
@ -239,7 +221,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
settings.set_strv('excluded-apps', apps);
|
settings.set_strv('excluded-apps', apps);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Each item in the list: an EditableLabel + Remove button.
|
|
||||||
const listBox = new Gtk.ListBox({
|
const listBox = new Gtk.ListBox({
|
||||||
selection_mode: Gtk.SelectionMode.NONE,
|
selection_mode: Gtk.SelectionMode.NONE,
|
||||||
css_classes: ['boxed-list'],
|
css_classes: ['boxed-list'],
|
||||||
|
|
@ -280,7 +261,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
listBox.append(row);
|
listBox.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add-new row.
|
|
||||||
const addRow = new Adw.ActionRow({ activatable: false });
|
const addRow = new Adw.ActionRow({ activatable: false });
|
||||||
const addEntry = new Gtk.Entry({
|
const addEntry = new Gtk.Entry({
|
||||||
placeholder_text: 'App name…',
|
placeholder_text: 'App name…',
|
||||||
|
|
@ -314,10 +294,6 @@ export default class StrataPreferences extends ExtensionPreferences {
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Keyboard shortcut dialog
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_showShortcutDialog(parent, settings) {
|
_showShortcutDialog(parent, settings) {
|
||||||
const dialog = new Adw.MessageDialog({
|
const dialog = new Adw.MessageDialog({
|
||||||
heading: 'Set Keyboard Shortcut',
|
heading: 'Set Keyboard Shortcut',
|
||||||
|
|
|
||||||
|
|
@ -48,13 +48,8 @@ export const ClipboardItem = GObject.registerClass({
|
||||||
|
|
||||||
this._id = id;
|
this._id = id;
|
||||||
this._mimeType = mimeType;
|
this._mimeType = mimeType;
|
||||||
/** D-Bus proxy used for lazy thumbnail fetch. */
|
|
||||||
this._proxy = opts.proxy ?? null;
|
this._proxy = opts.proxy ?? null;
|
||||||
/** Optional shared LRU cache (Map<id, string filePath>) - populated on first fetch. */
|
|
||||||
this._thumbCache = opts.thumbCache ?? null;
|
this._thumbCache = opts.thumbCache ?? null;
|
||||||
/** Stored on the actor so panel.js can filter by it. */
|
|
||||||
this.actor = this;
|
|
||||||
this._strataPreview = mimeType.startsWith('image/') ? '' : preview;
|
|
||||||
|
|
||||||
const row = new St.BoxLayout({
|
const row = new St.BoxLayout({
|
||||||
style_class: 'strata-item-row',
|
style_class: 'strata-item-row',
|
||||||
|
|
@ -112,10 +107,6 @@ export const ClipboardItem = GObject.registerClass({
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build an image thumbnail container.
|
|
||||||
* - If already in shared cache, apply immediately.
|
|
||||||
* - Else fetch via GetThumbnailRemote, write to ~/.cache/strata/thumbnails/{id}.png,
|
|
||||||
* then apply. The decode happens off-thread (CSS background-image loader). */
|
|
||||||
_buildThumbnail(id) {
|
_buildThumbnail(id) {
|
||||||
const container = new St.Widget({
|
const container = new St.Widget({
|
||||||
width: THUMB_SIZE,
|
width: THUMB_SIZE,
|
||||||
|
|
@ -135,18 +126,15 @@ export const ClipboardItem = GObject.registerClass({
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fast path: already cached this session.
|
|
||||||
if (this._thumbCache?.has(id)) {
|
if (this._thumbCache?.has(id)) {
|
||||||
applyStyle();
|
applyStyle();
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
// Second fast path: file exists on disk from a previous session.
|
|
||||||
if (GLib.file_test(cachePath, GLib.FileTest.EXISTS)) {
|
if (GLib.file_test(cachePath, GLib.FileTest.EXISTS)) {
|
||||||
this._thumbCache?.set(id, cachePath);
|
this._thumbCache?.set(id, cachePath);
|
||||||
applyStyle();
|
applyStyle();
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
// Slow path: ask the daemon for the bytes, then write file async.
|
|
||||||
GLib.mkdir_with_parents(cacheDir, 0o755);
|
GLib.mkdir_with_parents(cacheDir, 0o755);
|
||||||
if (!this._proxy) {
|
if (!this._proxy) {
|
||||||
this._fallbackIcon(container);
|
this._fallbackIcon(container);
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ export class StrataPanel {
|
||||||
|
|
||||||
const widget = this._makeItemWidget(id, mimeType, preview);
|
const widget = this._makeItemWidget(id, mimeType, preview);
|
||||||
this._widgets.set(id, widget);
|
this._widgets.set(id, widget);
|
||||||
this._itemList.insert_child_at_index(widget.actor, 0);
|
this._itemList.insert_child_at_index(widget, 0);
|
||||||
this._setActiveWidget(widget);
|
this._setActiveWidget(widget);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[Strata] prependItem failed for id=${id} mime=${mimeType}:`, e);
|
console.error(`[Strata] prependItem failed for id=${id} mime=${mimeType}:`, e);
|
||||||
|
|
@ -425,7 +425,7 @@ export class StrataPanel {
|
||||||
this._items.push({ id, mimeType, preview });
|
this._items.push({ id, mimeType, preview });
|
||||||
const widget = this._makeItemWidget(id, mimeType, preview);
|
const widget = this._makeItemWidget(id, mimeType, preview);
|
||||||
this._widgets.set(id, widget);
|
this._widgets.set(id, widget);
|
||||||
this._itemList.add_child(widget.actor);
|
this._itemList.add_child(widget);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[Strata] _appendItemFromMeta failed for id=${meta?.id}:`, e);
|
console.error(`[Strata] _appendItemFromMeta failed for id=${meta?.id}:`, e);
|
||||||
}
|
}
|
||||||
|
|
@ -475,12 +475,8 @@ export class StrataPanel {
|
||||||
for (let j = i; j < e; j++)
|
for (let j = i; j < e; j++)
|
||||||
this._appendItemFromMeta(this._searchResults[j]);
|
this._appendItemFromMeta(this._searchResults[j]);
|
||||||
};
|
};
|
||||||
// The very first chunk of a fresh render (start === 0, right after
|
// First chunk runs synchronously so the list never paints empty
|
||||||
// _clearListDom) is rendered synchronously, in the same frame as
|
// between clear and first append. Later chunks yield to idle.
|
||||||
// the clear, so the list never paints empty in between - that
|
|
||||||
// empty frame is the "blink" seen while typing a search. Later
|
|
||||||
// chunks (and scroll-driven pages) yield to idle to keep frames
|
|
||||||
// light.
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
renderChunk();
|
renderChunk();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue