diff --git a/quickshell/Modals/DankLauncherV2/Controller.qml b/quickshell/Modals/DankLauncherV2/Controller.qml index 97e208d5..a8137f5b 100644 --- a/quickshell/Modals/DankLauncherV2/Controller.qml +++ b/quickshell/Modals/DankLauncherV2/Controller.qml @@ -44,16 +44,14 @@ Item { signal searchQueryRequested(string query) onActiveChanged: { - if (active) { - if (clipboardSearchEnabledInAll()) - ClipboardService.ensureLauncherHistory(); - } else { + if (!active) { SessionData.addLauncherHistory(searchQuery); sections = []; flatModel = []; selectedItem = null; _clearModeCache(); + ClipboardService.invalidateLauncherSearchCache(); } } @@ -88,11 +86,17 @@ Item { Connections { target: ClipboardService - function onInternalEntriesChanged() { + function onLauncherSearchReady(query) { if (!active || !clipboardSearchEnabledInAll()) return; - if (searchMode === "all" && searchQuery.length >= 2) - performSearch(); + if (searchMode !== "all") + return; + const trimmed = (searchQuery || "").trim(); + if (trimmed.length < 2 && query.length > 0) + return; + if (query !== trimmed) + return; + searchDebounce.restart(); } } @@ -403,9 +407,6 @@ Item { searchQuery = query; searchDebounce.restart(); - if (searchMode === "all" && clipboardSearchEnabledInAll() && query.length >= 2) - ClipboardService.ensureLauncherHistory(); - var filesInAll = searchMode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll); if (searchMode !== "plugins" && (searchMode === "files" || query.startsWith("/") || filesInAll) && query.length > 0) { fileSearchDebounce.restart(); @@ -424,8 +425,6 @@ Item { searchMode = mode; modeChanged(mode); performSearch(); - if (mode === "all" && clipboardSearchEnabledInAll() && searchQuery.length >= 2) - ClipboardService.ensureLauncherHistory(); var filesInAll = mode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll) && searchQuery.length > 0; if (mode === "files" || filesInAll) { fileSearchDebounce.restart(); @@ -1209,7 +1208,6 @@ Item { } if (clipboardSearchEnabledInAll()) { - ClipboardService.ensureLauncherHistory(); var clipboardItems = AppSearchService.getBuiltInLauncherItems("dms_clipboard_search", query); var clipboardLimit = Math.min(clipboardItems.length, 8); for (var j = 0; j < clipboardLimit; j++) { diff --git a/quickshell/Services/AppSearchService.qml b/quickshell/Services/AppSearchService.qml index 5a22f6fd..062c1f8e 100644 --- a/quickshell/Services/AppSearchService.qml +++ b/quickshell/Services/AppSearchService.qml @@ -296,9 +296,8 @@ Singleton { function getBuiltInLauncherItems(pluginId, query) { if (pluginId === "dms_clipboard_search") { - ClipboardService.ensureLauncherHistory(); const trimmed = (query || "").toString().trim(); - const entries = trimmed.length === 0 ? ClipboardService.getRecentLauncherEntries(20) : ClipboardService.getLauncherEntries(trimmed, 20, 1); + const entries = ClipboardService.getCachedLauncherSearchEntries(trimmed, 20); return entries.map(entry => ({ type: "clipboard", data: entry @@ -308,8 +307,7 @@ Singleton { if (pluginId !== "dms_settings_search") return []; - SettingsSearchService.search(query); - const results = SettingsSearchService.results; + const results = SettingsSearchService.searchForLauncher(query); const items = []; for (let i = 0; i < results.length; i++) { const r = results[i]; diff --git a/quickshell/Services/ClipboardService.qml b/quickshell/Services/ClipboardService.qml index 46ddd2df..3a45ebce 100644 --- a/quickshell/Services/ClipboardService.qml +++ b/quickshell/Services/ClipboardService.qml @@ -27,9 +27,14 @@ Singleton { property bool keyboardNavigationActive: false property int refCount: 0 property real _launcherLastRefresh: 0 + property bool _launcherCacheValid: false + property string _launcherCachedQuery: "" + property var _launcherCachedEntries: [] + property int _launcherSearchSeq: 0 signal historyCopied signal historyCleared + signal launcherSearchReady(string query) Process { id: wtypeProcess @@ -103,6 +108,63 @@ Singleton { } } + function requestLauncherSearch(query, limit) { + if (!clipboardAvailable) { + return; + } + + const trimmed = (query || "").toString().trim(); + const maxItems = limit > 0 ? limit : 20; + if (_launcherCacheValid && _launcherCachedQuery === trimmed) { + return; + } + + _launcherSearchSeq++; + const seq = _launcherSearchSeq; + DMSService.sendRequest("clipboard.search", { + "query": trimmed, + "limit": maxItems + }, function (response) { + if (seq !== _launcherSearchSeq) { + return; + } + if (response.error) { + log.warn("Launcher clipboard search failed:", response.error); + _launcherCacheValid = true; + _launcherCachedQuery = trimmed; + _launcherCachedEntries = []; + launcherSearchReady(trimmed); + return; + } + const result = response.result || {}; + _launcherCacheValid = true; + _launcherCachedQuery = trimmed; + _launcherCachedEntries = result.entries || []; + launcherSearchReady(trimmed); + }); + } + + function getCachedLauncherSearchEntries(query, limit) { + if (!clipboardAvailable) { + return []; + } + + const trimmed = (query || "").toString().trim(); + const maxItems = limit > 0 ? limit : 20; + if (!_launcherCacheValid || _launcherCachedQuery !== trimmed) { + requestLauncherSearch(trimmed, maxItems); + return []; + } + return _launcherCachedEntries.slice(0, maxItems); + } + + function invalidateLauncherSearchCache() { + _launcherCacheValid = false; + _launcherCachedQuery = ""; + _launcherCachedEntries = []; + _launcherSearchSeq++; + } + function getLauncherEntries(query, limit, minLength) { if (!clipboardAvailable) { return []; diff --git a/quickshell/Services/SettingsSearchService.qml b/quickshell/Services/SettingsSearchService.qml index 232cff4c..6566d539 100644 --- a/quickshell/Services/SettingsSearchService.qml +++ b/quickshell/Services/SettingsSearchService.qml @@ -159,17 +159,15 @@ Singleton { _translatedCache = cache; } - function search(text) { - query = text; - if (!text) { - results = []; - return; - } + function _searchEntries(text, maxResults) { + if (!text) + return []; var queryLower = text.toLowerCase().trim(); var queryWords = queryLower.split(/\s+/).filter(w => w.length > 0); var scored = []; var cache = _translatedCache; + var limit = maxResults > 0 ? maxResults : 15; for (var i = 0; i < cache.length; i++) { var entry = cache[i]; @@ -234,7 +232,20 @@ Singleton { } scored.sort((a, b) => b.score - a.score); - results = scored.slice(0, 15).map(s => s.item); + return scored.slice(0, limit).map(s => s.item); + } + + function searchForLauncher(text) { + return _searchEntries(text, 15); + } + + function search(text) { + query = text; + if (!text) { + results = []; + return; + } + results = _searchEntries(text, 15); } function clear() {