diff --git a/strata@edu4rdshl.dev/extension.js b/strata@edu4rdshl.dev/extension.js index cdba089..5d9af8a 100644 --- a/strata@edu4rdshl.dev/extension.js +++ b/strata@edu4rdshl.dev/extension.js @@ -59,10 +59,10 @@ export default class StrataExtension extends Extension { this._daemonRestartAttempts = 0; this._settings = this.getSettings(); - this._excludedApps = this._settings.get_strv('excluded-apps'); + this._excludedApps = this._readExcluded(); this._readSizeLimits(); this._settings.connectObject( - 'changed::excluded-apps', () => { this._excludedApps = this._settings.get_strv('excluded-apps'); }, + 'changed::excluded-apps', () => { this._excludedApps = this._readExcluded(); }, 'changed::max-history', () => { this._readSizeLimits(); this._pushConfig(); }, 'changed::max-text-mb', () => { this._readSizeLimits(); this._pushConfig(); }, 'changed::max-image-mb', () => { this._readSizeLimits(); this._pushConfig(); }, @@ -392,23 +392,50 @@ export default class StrataExtension extends Extension { this._historyClearedId = this._proxy.connectSignal('HistoryCleared', () => { - try { - const dir = `${GLib.get_user_cache_dir()}/strata/thumbnails`; - const d = Gio.File.new_for_path(dir); - if (d.query_exists(null)) { - const en = d.enumerate_children( - 'standard::name', Gio.FileQueryInfoFlags.NONE, null); - let info; - while ((info = en.next_file(null))) { - try { d.get_child(info.get_name()).delete(null); } catch { /* best-effort cleanup */ } - } - en.close(null); - } - } catch (e) { logError('cache clear failed', e); } + this._clearThumbnailCache(); this._addIdleSource(() => this._panel?.clearItems()); }); } + /** Delete every cached thumbnail without blocking the compositor: + * enumerate and unlink asynchronously in batches. */ + _clearThumbnailCache() { + const dir = Gio.File.new_for_path( + `${GLib.get_user_cache_dir()}/strata/thumbnails`); + dir.enumerate_children_async( + 'standard::name', Gio.FileQueryInfoFlags.NONE, + GLib.PRIORITY_DEFAULT, null, + (d, res) => { + let en; + try { + en = d.enumerate_children_finish(res); + } catch { + return; // dir missing or unreadable + } + const step = () => { + en.next_files_async(32, GLib.PRIORITY_DEFAULT, null, (e, r) => { + let infos; + try { + infos = e.next_files_finish(r); + } catch { + infos = []; + } + if (infos.length === 0) { + e.close_async(GLib.PRIORITY_DEFAULT, null, () => {}); + return; + } + for (const info of infos) { + e.get_child(info).delete_async( + GLib.PRIORITY_DEFAULT, null, + (f, rr) => { try { f.delete_finish(rr); } catch { /* best-effort */ } }); + } + step(); + }); + }; + step(); + }); + } + _disconnectSignals() { if (this._itemAddedId && this._proxy) { this._proxy.disconnectSignal(this._itemAddedId); @@ -459,9 +486,13 @@ export default class StrataExtension extends Extension { }); } + _readExcluded() { + return this._settings.get_strv('excluded-apps').map(s => s.toLowerCase()); + } + _isExcluded(appClass) { if (!appClass) return false; - return this._excludedApps.some(ex => appClass.includes(ex.toLowerCase())); + return this._excludedApps.some(ex => appClass.includes(ex)); } diff --git a/strata@edu4rdshl.dev/ui/clipboardItem.js b/strata@edu4rdshl.dev/ui/clipboardItem.js index 76a8787..56e6639 100644 --- a/strata@edu4rdshl.dev/ui/clipboardItem.js +++ b/strata@edu4rdshl.dev/ui/clipboardItem.js @@ -57,8 +57,11 @@ export const ClipboardItem = GObject.registerClass({ x_expand: true, }); - row.add_child(this._buildLeading(mimeType, preview)); - row.add_child(this._buildContent(mimeType, preview)); + const isImage = mimeType.startsWith('image/'); + const urlLike = !isImage && isUrl(preview ?? ''); + const colorLike = !isImage && !urlLike && isColor(preview ?? ''); + row.add_child(this._buildLeading(mimeType, preview, isImage, colorLike)); + row.add_child(this._buildContent(preview, isImage, urlLike, colorLike)); const deleteBtn = new St.Button({ style_class: 'strata-item-delete', @@ -88,11 +91,11 @@ export const ClipboardItem = GObject.registerClass({ }); } - _buildLeading(mimeType, preview) { - if (mimeType.startsWith('image/')) { + _buildLeading(mimeType, preview, isImage, colorLike) { + if (isImage) { return this._buildThumbnail(this._id); } - if (isColor(preview)) { + if (colorLike) { return this._buildColorSwatch(preview); } const icon = new St.Icon({ @@ -197,7 +200,7 @@ export const ClipboardItem = GObject.registerClass({ }); } - _buildContent(mimeType, preview) { + _buildContent(preview, isImage, urlLike, colorLike) { preview = preview ?? ''; const box = new St.BoxLayout({ vertical: true, @@ -208,17 +211,17 @@ export const ClipboardItem = GObject.registerClass({ let mainText; let subText = ''; - if (mimeType.startsWith('image/')) { + if (isImage) { // Generic label - the thumbnail identifies the image, and the // on-clipboard format (often PNG even for a copied GIF/WebP) is an // implementation detail that misleads more than it informs. mainText = 'Image'; - } else if (isUrl(preview)) { + } else if (urlLike) { mainText = preview.trim(); try { subText = GLib.Uri.parse(preview.trim(), GLib.UriFlags.NONE).get_host() ?? ''; } catch { /* not a parseable URI */ } - } else if (isColor(preview)) { + } else if (colorLike) { mainText = preview.trim().toUpperCase(); subText = 'Color'; } else { @@ -230,7 +233,7 @@ export const ClipboardItem = GObject.registerClass({ const labelMain = new St.Label({ text: mainText || '(empty)', - style_class: `strata-item-text${isUrl(preview) ? ' strata-item-url' : ''}`, + style_class: `strata-item-text${urlLike ? ' strata-item-url' : ''}`, x_expand: true, y_align: Clutter.ActorAlign.CENTER, }); diff --git a/strata@edu4rdshl.dev/ui/panel.js b/strata@edu4rdshl.dev/ui/panel.js index aa137ff..cc99f3e 100644 --- a/strata@edu4rdshl.dev/ui/panel.js +++ b/strata@edu4rdshl.dev/ui/panel.js @@ -278,6 +278,7 @@ export class StrataPanel { removeItem(id) { this._items = this._items.filter(i => i.id !== id); + this._thumbCache.delete(id); // Keep the search snapshot in sync so a deleted/pruned item sitting in // the not-yet-rendered tail can't be re-created as a phantom row when // scrolling appends the next page. Adjust _searchRendered if the removed @@ -310,6 +311,7 @@ export class StrataPanel { this._hoveredWidget = null; this._activeWidget = null; this._widgets.clear(); + this._thumbCache.clear(); this._itemList.destroy_all_children(); }