diff --git a/strata@edu4rdshl.dev/extension.js b/strata@edu4rdshl.dev/extension.js index 6dcc240..d115c12 100644 --- a/strata@edu4rdshl.dev/extension.js +++ b/strata@edu4rdshl.dev/extension.js @@ -15,9 +15,8 @@ import { StrataProxy, BUS_NAME, OBJECT_PATH } from './dbus.js'; import { StrataPanel } from './ui/panel.js'; function logError(label, err) { - if (err !== undefined) { - try { Gio.DBusError.strip_remote_error(err); } catch (_) {} - } + if (err instanceof GLib.Error) + Gio.DBusError.strip_remote_error(err); const tail = err !== undefined ? `: ${err?.message ?? err}` : ''; console.error(`[Strata] ${label}${tail}`); } @@ -110,7 +109,6 @@ export default class StrataExtension extends Extension { GLib.Source.remove(this._daemonRestartTimerId); this._daemonRestartTimerId = null; } - // Clean up in reverse order. this._unregisterShortcut(); this._disconnectClipboardMonitor(); this._disconnectFocusTracking(); @@ -235,7 +233,6 @@ export default class StrataExtension extends Extension { /** Pick the best MIME type to store from the offered list (mirrors Rust pick_mime). */ _pickMime(mimes) { const PREFERRED = [ - // Raster images (size-capped at MAX_IMAGE). 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp', 'image/bmp', 'image/tiff', 'image/x-icon', // Plain text (UTF-8 preferred, then locale, then X11 legacy aliases). @@ -281,7 +278,7 @@ export default class StrataExtension extends Extension { try { _conn.call_finish(result); // Name already owned (e.g. systemd user service) - don't spawn a second instance. - } catch (_) { + } catch { this._doSpawnDaemon(); } } @@ -324,7 +321,7 @@ export default class StrataExtension extends Extension { urgency: MessageTray.Urgency.HIGH, }); source.addNotification(notification); - } catch (_) { + } catch { // Message tray may not be available (e.g. during early startup) - already logged above. } } @@ -373,12 +370,12 @@ export default class StrataExtension extends Extension { try { // Graceful shutdown via D-Bus first. this._proxy?.ShutdownRemote(() => {}); - } catch (_) {} + } 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 (_) {} // SIGTERM + try { daemonToStop.send_signal(15); } catch { /* already exited */ } // SIGTERM return GLib.SOURCE_REMOVE; }); } @@ -440,7 +437,7 @@ export default class StrataExtension extends Extension { const cachePath = `${GLib.get_user_cache_dir()}/strata/thumbnails/${id}.png`; GLib.unlink(cachePath); - } catch (_) {} + } catch { /* no cached thumbnail for this id */ } this._addIdleSource(() => this._panel?.removeItem(id)); }); @@ -454,7 +451,7 @@ export default class StrataExtension extends Extension { 'standard::name', Gio.FileQueryInfoFlags.NONE, null); let info; while ((info = en.next_file(null))) { - try { d.get_child(info.get_name()).delete(null); } catch (_) {} + try { d.get_child(info.get_name()).delete(null); } catch { /* best-effort cleanup */ } } en.close(null); } diff --git a/strata@edu4rdshl.dev/prefs.js b/strata@edu4rdshl.dev/prefs.js index fccf52b..6acb5a8 100644 --- a/strata@edu4rdshl.dev/prefs.js +++ b/strata@edu4rdshl.dev/prefs.js @@ -3,7 +3,6 @@ import Adw from 'gi://Adw'; import Gtk from 'gi://Gtk'; import Gdk from 'gi://Gdk'; -import GLib from 'gi://GLib'; import Gio from 'gi://Gio'; import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; @@ -211,7 +210,6 @@ export default class StrataPreferences extends ExtensionPreferences { for (const app of currentApps) model.append(app); - /** Sync the StringList back to GSettings. */ const saveModel = () => { const apps = []; for (let i = 0; i < model.get_n_items(); i++) { diff --git a/strata@edu4rdshl.dev/ui/clipboardItem.js b/strata@edu4rdshl.dev/ui/clipboardItem.js index 554d34e..9b0fcec 100644 --- a/strata@edu4rdshl.dev/ui/clipboardItem.js +++ b/strata@edu4rdshl.dev/ui/clipboardItem.js @@ -22,24 +22,23 @@ function iconForMime(mimeType) { return 'edit-copy-symbolic'; } -/** Detect if a string looks like a URL. */ function isUrl(text) { return /^https?:\/\/.+/i.test(text.trim()); } -/** Detect if a string looks like a CSS/hex color. */ function isColor(text) { return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(text.trim()); } export const ClipboardItem = GObject.registerClass({ + GTypeName: 'StrataClipboardItem', Signals: { 'activate': {}, 'delete': {}, }, -}, class ClipboardItem extends St.Button { - _init(id, mimeType, preview, opts = {}) { - super._init({ +}, class extends St.Button { + constructor(id, mimeType, preview, opts = {}) { + super({ style_class: 'strata-item', x_expand: true, can_focus: true, @@ -56,13 +55,9 @@ export const ClipboardItem = GObject.registerClass({ x_expand: true, }); - // Left: icon or image thumbnail row.add_child(this._buildLeading(mimeType, preview)); - - // Center: text preview row.add_child(this._buildContent(mimeType, preview)); - // Right: delete button const deleteBtn = new St.Button({ style_class: 'strata-item-delete', icon_name: 'edit-delete-symbolic', @@ -122,7 +117,7 @@ export const ClipboardItem = GObject.registerClass({ const applyStyle = () => { try { container.style = `background-image: url("${fileUri}"); background-size: cover; background-repeat: no-repeat;`; - } catch (_) { /* container was destroyed mid-flight */ } + } catch { /* container was destroyed mid-flight */ } }; try { @@ -187,7 +182,7 @@ export const ClipboardItem = GObject.registerClass({ x_align: Clutter.ActorAlign.CENTER, }); container.add_child(icon); - } catch (_) {} + } catch { /* container destroyed before the icon was added */ } } _buildColorSwatch(hex) { @@ -208,8 +203,8 @@ export const ClipboardItem = GObject.registerClass({ style_class: 'strata-item-content', }); - let mainText = ''; - let subText = ''; + let mainText; + let subText = ''; if (mimeType.startsWith('image/')) { // Generic label - the thumbnail identifies the image, and the @@ -219,8 +214,8 @@ export const ClipboardItem = GObject.registerClass({ } else if (isUrl(preview)) { mainText = preview.trim(); try { - subText = new URL(preview.trim()).hostname; - } catch (_) {} + subText = GLib.Uri.parse(preview.trim(), GLib.UriFlags.NONE).get_host() ?? ''; + } catch { /* not a parseable URI */ } } else if (isColor(preview)) { mainText = preview.trim().toUpperCase(); subText = 'Color'; diff --git a/strata@edu4rdshl.dev/ui/panel.js b/strata@edu4rdshl.dev/ui/panel.js index 52681d8..21ffd86 100644 --- a/strata@edu4rdshl.dev/ui/panel.js +++ b/strata@edu4rdshl.dev/ui/panel.js @@ -14,9 +14,8 @@ const SEARCH_DEBOUNCE_MS = 150; const LOAD_MORE_THRESHOLD = 200; function logError(label, err) { - if (err !== undefined) { - try { Gio.DBusError.strip_remote_error(err); } catch (_) {} - } + if (err instanceof GLib.Error) + Gio.DBusError.strip_remote_error(err); const tail = err !== undefined ? `: ${err?.message ?? err}` : ''; console.error(`[Strata] ${label}${tail}`); } @@ -83,7 +82,6 @@ export class StrataPanel { return this._interfaceSettings.get_string('color-scheme') !== 'prefer-dark'; } - /** Toggle the light-theme class on the panel root box. */ _applyTheme() { if (!this._box) return; if (this._effectiveIsLight()) @@ -103,7 +101,6 @@ export class StrataPanel { _buildUI() { - // Overlay container - sits above all windows. this._overlay = new St.Widget({ layout_manager: new Clutter.FixedLayout(), visible: false, @@ -190,7 +187,6 @@ export class StrataPanel { this._searchEntry.get_clutter_text().connect('text-changed', () => { this._scheduleSearch(this._searchEntry.get_text()); }); - // Down arrow from search box moves focus to the first item. this._searchEntry.get_clutter_text().connect('key-press-event', (_actor, event) => { if (event.get_key_symbol() === Clutter.KEY_Down) { this._focusItem(0); @@ -212,11 +208,9 @@ export class StrataPanel { }); this._scrollView.set_child(this._itemList); - // Load more items when scrolled near the bottom. const vadj = this._scrollView.get_vadjustment(); if (vadj) { vadj.connect('notify::value', () => this._maybeLoadMore()); - // Also re-check when the list grows (a new page just appended). vadj.connect('notify::upper', () => this._maybeLoadMore()); } @@ -271,7 +265,6 @@ export class StrataPanel { close() { if (!this._visible) return; this._visible = false; - // Clear hover state since the panel is closing. this._hoveredWidget?.remove_style_class_name('strata-item-hovered'); this._hoveredWidget = null; if (this._grab) { @@ -293,7 +286,6 @@ export class StrataPanel { this._widgets.delete(id); } - // While searching, only render if the new item matches the active query. if (this._searchQuery && !this._matchesSearch(preview, mimeType)) { return; } @@ -337,7 +329,6 @@ export class StrataPanel { if (wasHovered) this._hoveredWidget = null; widget.destroy(); this._widgets.delete(id); - // Re-assign active to the new first visible item. if (wasActive) { const first = this._getVisibleItems()[0]; if (first) this._setActiveWidget(first); @@ -395,14 +386,15 @@ export class StrataPanel { const BATCH = 20; for (let i = 0; i < items.length; i += BATCH) { if (!this._overlay) return false; - await new Promise(resolve => + // eslint-disable-next-line no-await-in-loop -- batches deliberately yield to the main loop + await new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { const end = Math.min(i + BATCH, items.length); for (let j = i; j < end; j++) this._appendItemFromMeta(items[j]); resolve(); return GLib.SOURCE_REMOVE; - }) - ); + }); + }); } this._loadedOffset = offset + items.length; this._hasMore = items.length >= limit; @@ -445,7 +437,6 @@ export class StrataPanel { // snapshot (no re-query, so a scroll cannot race the search). this._renderSearchPage(this._searchEpoch); } else { - // Browse mode: pull the next page from the daemon. if (!this._hasMore) return; this._loadingMore = true; this._loadHistory(this._loadedOffset, this._pageSize) @@ -482,12 +473,14 @@ export class StrataPanel { if (i === 0) { renderChunk(); } else { - await new Promise(resolve => + // eslint-disable-next-line no-await-in-loop -- batches deliberately yield to the main loop + await new Promise(resolve => { GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { renderChunk(); resolve(); return GLib.SOURCE_REMOVE; - })); + }); + }); } } this._searchRendered = end; @@ -543,14 +536,12 @@ export class StrataPanel { return widget; } - /** Focus the item at visible-list index `idx` (clamped). */ _focusItem(idx) { const items = this._getVisibleItems(); if (items.length === 0) return; global.stage.set_key_focus(items[Math.max(0, Math.min(idx, items.length - 1))]); } - /** All currently-visible item actors in order. */ _getVisibleItems() { return this._itemList.get_children().filter(c => c.visible); } @@ -717,13 +708,11 @@ export class StrataPanel { } _moveItemToTop(id, widget) { - // Move data model entry to front. const idx = this._items.findIndex(i => i.id === id); if (idx > 0) { const [entry] = this._items.splice(idx, 1); this._items.unshift(entry); } - // Move actor to position 0 in the list. this._itemList.set_child_at_index(widget, 0); } @@ -750,7 +739,6 @@ export class StrataPanel { const MARGIN = 16; // px gap from screen edge let x, y; - // Horizontal if (position.endsWith('left')) x = monitor.x + MARGIN; else if (position.endsWith('right'))