From d2c43915145bad613a07b085535f0f3347e21c53 Mon Sep 17 00:00:00 2001 From: pcortellezzi Date: Fri, 2 Jan 2026 17:49:04 -0300 Subject: [PATCH] feat: Persistent Plugins & Async Updates (#1231) - PluginService: maintain persistent instances for Launcher plugins - AppSearchService: reuse persistent instances for queries - Added requestLauncherUpdate signal for async UI refreshes --- quickshell/Modules/AppDrawer/AppLauncher.qml | 4 ++ quickshell/Services/AppSearchService.qml | 70 +++++++++++++++----- quickshell/Services/PluginService.qml | 25 ++++--- 3 files changed, 71 insertions(+), 28 deletions(-) diff --git a/quickshell/Modules/AppDrawer/AppLauncher.qml b/quickshell/Modules/AppDrawer/AppLauncher.qml index 0cd13b55..189cef96 100644 --- a/quickshell/Modules/AppDrawer/AppLauncher.qml +++ b/quickshell/Modules/AppDrawer/AppLauncher.qml @@ -51,6 +51,10 @@ Item { function onPluginLoaded() { updateCategories() } function onPluginUnloaded() { updateCategories() } function onPluginListUpdated() { updateCategories() } + function onRequestLauncherUpdate(pluginId) { + // Only update if we are actually looking at this plugin or in All category + updateFilteredModel() + } } Connections { diff --git a/quickshell/Services/AppSearchService.qml b/quickshell/Services/AppSearchService.qml index 93ee53aa..7dbafd8e 100644 --- a/quickshell/Services/AppSearchService.qml +++ b/quickshell/Services/AppSearchService.qml @@ -411,26 +411,43 @@ Singleton { return [] } - const component = PluginService.pluginLauncherComponents[pluginId] - if (!component) + let instance = PluginService.pluginInstances[pluginId] + let isPersistent = true + + if (!instance) { + const component = PluginService.pluginLauncherComponents[pluginId] + if (!component) + return [] + + try { + instance = component.createObject(root, { + "pluginService": PluginService + }) + isPersistent = false + } catch (e) { + console.warn("AppSearchService: Error creating temporary plugin instance", pluginId, ":", e) + return [] + } + } + + if (!instance) return [] try { - const instance = component.createObject(root, { - "pluginService": PluginService - }) - - if (instance && typeof instance.getItems === "function") { + if (typeof instance.getItems === "function") { const items = instance.getItems(query || "") - instance.destroy() + if (!isPersistent) + instance.destroy() return items || [] } - if (instance) { + if (!isPersistent) { instance.destroy() } } catch (e) { console.warn("AppSearchService: Error getting items from plugin", pluginId, ":", e) + if (!isPersistent) + instance.destroy() } return [] @@ -440,26 +457,43 @@ Singleton { if (typeof PluginService === "undefined") return false - const component = PluginService.pluginLauncherComponents[pluginId] - if (!component) + let instance = PluginService.pluginInstances[pluginId] + let isPersistent = true + + if (!instance) { + const component = PluginService.pluginLauncherComponents[pluginId] + if (!component) + return false + + try { + instance = component.createObject(root, { + "pluginService": PluginService + }) + isPersistent = false + } catch (e) { + console.warn("AppSearchService: Error creating temporary plugin instance for execution", pluginId, ":", e) + return false + } + } + + if (!instance) return false try { - const instance = component.createObject(root, { - "pluginService": PluginService - }) - - if (instance && typeof instance.executeItem === "function") { + if (typeof instance.executeItem === "function") { instance.executeItem(item) - instance.destroy() + if (!isPersistent) + instance.destroy() return true } - if (instance) { + if (!isPersistent) { instance.destroy() } } catch (e) { console.warn("AppSearchService: Error executing item from plugin", pluginId, ":", e) + if (!isPersistent) + instance.destroy() } return false diff --git a/quickshell/Services/PluginService.qml b/quickshell/Services/PluginService.qml index 8a181772..24666090 100644 --- a/quickshell/Services/PluginService.qml +++ b/quickshell/Services/PluginService.qml @@ -39,6 +39,7 @@ Singleton { signal pluginDataChanged(string pluginId) signal pluginListUpdated signal globalVarChanged(string pluginId, string varName) + signal requestLauncherUpdate(string pluginId) Timer { id: resyncDebounce @@ -286,12 +287,14 @@ Singleton { return false; } - if (isDaemon) { + // MODIFICATION: Treat Launchers as persistent instances like Daemons + if (isDaemon || isLauncher) { const instance = comp.createObject(root, { - "pluginId": pluginId + "pluginId": pluginId, + "pluginService": root // Inject PluginService }); if (!instance) { - console.error("PluginService: failed to instantiate daemon:", pluginId, comp.errorString()); + console.error("PluginService: failed to instantiate plugin:", pluginId, comp.errorString()); pluginLoadFailed(pluginId, comp.errorString()); return false; } @@ -299,13 +302,15 @@ Singleton { newInstances[pluginId] = instance; pluginInstances = newInstances; - const newDaemons = Object.assign({}, pluginDaemonComponents); - newDaemons[pluginId] = comp; - pluginDaemonComponents = newDaemons; - } else if (isLauncher) { - const newLaunchers = Object.assign({}, pluginLauncherComponents); - newLaunchers[pluginId] = comp; - pluginLauncherComponents = newLaunchers; + if (isDaemon) { + const newDaemons = Object.assign({}, pluginDaemonComponents); + newDaemons[pluginId] = comp; + pluginDaemonComponents = newDaemons; + } else { + const newLaunchers = Object.assign({}, pluginLauncherComponents); + newLaunchers[pluginId] = comp; + pluginLauncherComponents = newLaunchers; + } } else if (isDesktop) { const newDesktop = Object.assign({}, pluginDesktopComponents); newDesktop[pluginId] = comp;