style: conform the extension to the GJS ESLint config

- clipboardItem: convert _init() to constructor()/super() and give the class
  an explicit GTypeName (no-restricted-syntax, no-shadow).
- clipboardItem: replace new URL() with GLib.Uri.parse().get_host(). GJS has
  no global URL, so the old call always threw and the URL hostname subtitle
  never rendered; this makes it work.
- Use optional catch bindings (catch {}); logError guards with
  instanceof GLib.Error instead of a try/catch.
- prefs: drop an unused GLib import.
- panel: block-body promise executors, and a no-await-in-loop disable on the
  batched renders (the per-batch yield is intentional).
- Drop the remaining restate-the-code comments.
This commit is contained in:
Eduard Tolosa 2026-06-25 19:43:47 -05:00
parent 376c6442f9
commit bb792cee95
4 changed files with 28 additions and 50 deletions

View file

@ -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);
}

View file

@ -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++) {

View file

@ -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,7 +203,7 @@ export const ClipboardItem = GObject.registerClass({
style_class: 'strata-item-content',
});
let mainText = '';
let mainText;
let subText = '';
if (mimeType.startsWith('image/')) {
@ -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';

View file

@ -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'))