summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml
diff options
context:
space:
mode:
authorAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
committerAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
commit70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch)
treeab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml
parent5d94c0a7d44a2255b81815a52a7056a94a39842d (diff)
downloadRaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz
RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml1894
1 files changed, 1894 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml
new file mode 100644
index 0000000..67cb606
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/quickshell/Modals/DankLauncherV2/Controller.qml
@@ -0,0 +1,1894 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import Quickshell
+import Quickshell.Io
+import qs.Common
+import qs.Services
+import "Scorer.js" as Scorer
+import "ControllerUtils.js" as Utils
+import "NavigationHelpers.js" as Nav
+import "ItemTransformers.js" as Transform
+
+Item {
+ id: root
+
+ property string searchQuery: ""
+ property string searchMode: "all"
+ property string previousSearchMode: "all"
+ property bool autoSwitchedToFiles: false
+ property bool isFileSearching: false
+ property var sections: []
+ property var flatModel: []
+ property int selectedFlatIndex: 0
+ property var selectedItem: null
+ property bool isSearching: false
+ property string activePluginId: ""
+ property var collapsedSections: ({})
+ property bool keyboardNavigationActive: false
+ property bool active: false
+ property var _modeSectionsCache: ({})
+ property bool _queryDrivenSearch: false
+ property bool _diskCacheConsumed: false
+ property var sectionViewModes: ({})
+ property var pluginViewPreferences: ({})
+ property int gridColumns: SettingsData.appLauncherGridColumns
+ property int viewModeVersion: 0
+ property string viewModeContext: "spotlight"
+
+ signal itemExecuted
+ signal searchCompleted
+ signal modeChanged(string mode)
+ signal queryChanged(string query)
+ signal viewModeChanged(string sectionId, string mode)
+ signal searchQueryRequested(string query)
+
+ onActiveChanged: {
+ if (!active) {
+ SessionData.addLauncherHistory(searchQuery);
+
+ sections = [];
+ flatModel = [];
+ selectedItem = null;
+ _clearModeCache();
+ }
+ }
+
+ onSearchModeChanged: {
+ if (searchMode === "apps") {
+ _loadAppCategories();
+ } else {
+ appCategory = "";
+ appCategories = [];
+ }
+ }
+
+ Connections {
+ target: SettingsData
+ function onSortAppsAlphabeticallyChanged() {
+ AppSearchService.invalidateLauncherCache();
+ _clearModeCache();
+ }
+ }
+
+ Connections {
+ target: AppSearchService
+ function onCacheVersionChanged() {
+ if (!active)
+ return;
+ _clearModeCache();
+ if (searchMode === "apps") {
+ _loadAppCategories();
+ performSearch();
+ } else if (!searchQuery && searchMode === "all") {
+ performSearch();
+ }
+ }
+ }
+
+ Connections {
+ target: PluginService
+ function onRequestLauncherUpdate(pluginId) {
+ if (!active)
+ return;
+ if (activePluginId === pluginId) {
+ if (activePluginCategories.length <= 1)
+ loadPluginCategories(pluginId);
+ performSearch();
+ return;
+ }
+ if (searchQuery)
+ performSearch();
+ }
+ }
+
+ Process {
+ id: wtypeProcess
+ command: ["wtype", "-M", "ctrl", "-P", "v", "-p", "v", "-m", "ctrl"]
+ running: false
+ }
+
+ Process {
+ id: copyProcess
+ running: false
+ onExited: pasteTimer.start()
+ }
+
+ Timer {
+ id: pasteTimer
+ interval: 200
+ repeat: false
+ onTriggered: wtypeProcess.running = true
+ }
+
+ function pasteSelected() {
+ if (!selectedItem)
+ return;
+ if (!SessionService.wtypeAvailable) {
+ ToastService.showError("wtype not available - install wtype for paste support");
+ return;
+ }
+
+ const pluginId = selectedItem.pluginId;
+ if (!pluginId)
+ return;
+ const pasteArgs = AppSearchService.getPluginPasteArgs(pluginId, selectedItem.data);
+ if (!pasteArgs)
+ return;
+ copyProcess.command = pasteArgs;
+ copyProcess.running = true;
+ itemExecuted();
+ }
+
+ readonly property var sectionDefinitions: [
+ {
+ id: "favorites",
+ title: I18n.tr("Pinned"),
+ icon: "push_pin",
+ priority: 1,
+ defaultViewMode: "list"
+ },
+ {
+ id: "apps",
+ title: I18n.tr("Applications"),
+ icon: "apps",
+ priority: 2,
+ defaultViewMode: "list"
+ },
+ {
+ id: "browse_plugins",
+ title: I18n.tr("Browse"),
+ icon: "category",
+ priority: 2.5,
+ defaultViewMode: "grid"
+ },
+ {
+ id: "files",
+ title: I18n.tr("Files"),
+ icon: "folder",
+ priority: 4,
+ defaultViewMode: "list"
+ },
+ {
+ id: "fallback",
+ title: I18n.tr("Commands"),
+ icon: "terminal",
+ priority: 5,
+ defaultViewMode: "list"
+ }
+ ]
+
+ property int historyIndex: -1
+ property string typingBackup: ""
+
+ function navigateHistory(direction) {
+ let history = SessionData.launcherQueryHistory;
+ if (history.length === 0)
+ return;
+
+ if (historyIndex === -1)
+ typingBackup = searchQuery;
+
+ let nextIndex = historyIndex + (direction === "up" ? 1 : -1);
+ if (nextIndex >= history.length)
+ nextIndex = history.length - 1;
+ if (nextIndex < -1)
+ nextIndex = -1;
+
+ if (nextIndex === historyIndex)
+ return;
+ historyIndex = nextIndex;
+
+ let targetText = (historyIndex === -1) ? typingBackup : history[historyIndex];
+
+ setSearchQuery(targetText);
+ searchQueryRequested(targetText);
+ }
+
+ property string fileSearchType: "all"
+ property string fileSearchExt: ""
+ property string fileSearchFolder: ""
+ property string fileSearchSort: "score"
+
+ property string pluginFilter: ""
+ property string activePluginName: ""
+ property var activePluginCategories: []
+ property string activePluginCategory: ""
+ property string appCategory: ""
+ property var appCategories: []
+
+ function getSectionViewMode(sectionId) {
+ if (sectionId === "browse_plugins")
+ return "list";
+ if (pluginViewPreferences[sectionId]?.enforced)
+ return pluginViewPreferences[sectionId].mode;
+ if (sectionViewModes[sectionId])
+ return sectionViewModes[sectionId];
+
+ var savedModes = viewModeContext === "appDrawer" ? (SettingsData.appDrawerSectionViewModes || {}) : (SettingsData.spotlightSectionViewModes || {});
+ if (savedModes[sectionId])
+ return savedModes[sectionId];
+
+ for (var i = 0; i < sectionDefinitions.length; i++) {
+ if (sectionDefinitions[i].id === sectionId)
+ return sectionDefinitions[i].defaultViewMode || "list";
+ }
+
+ if (pluginViewPreferences[sectionId]?.mode)
+ return pluginViewPreferences[sectionId].mode;
+
+ return "list";
+ }
+
+ function setSectionViewMode(sectionId, mode) {
+ if (sectionId === "browse_plugins")
+ return;
+ if (pluginViewPreferences[sectionId]?.enforced)
+ return;
+ sectionViewModes = Object.assign({}, sectionViewModes, {
+ [sectionId]: mode
+ });
+ viewModeVersion++;
+ if (viewModeContext === "appDrawer") {
+ var savedModes = Object.assign({}, SettingsData.appDrawerSectionViewModes || {}, {
+ [sectionId]: mode
+ });
+ SettingsData.appDrawerSectionViewModes = savedModes;
+ } else {
+ var savedModes = Object.assign({}, SettingsData.spotlightSectionViewModes || {}, {
+ [sectionId]: mode
+ });
+ SettingsData.spotlightSectionViewModes = savedModes;
+ }
+ viewModeChanged(sectionId, mode);
+ }
+
+ function canChangeSectionViewMode(sectionId) {
+ if (sectionId === "browse_plugins")
+ return false;
+ return !pluginViewPreferences[sectionId]?.enforced;
+ }
+
+ function canCollapseSection(sectionId) {
+ return searchMode === "all";
+ }
+
+ function setPluginViewPreference(pluginId, mode, enforced) {
+ var prefs = Object.assign({}, pluginViewPreferences);
+ prefs[pluginId] = {
+ mode: mode,
+ enforced: enforced || false
+ };
+ pluginViewPreferences = prefs;
+ }
+
+ function applyActivePluginViewPreference(pluginId, isBuiltIn) {
+ var sectionId = "plugin_" + pluginId;
+ var pref = null;
+ if (isBuiltIn) {
+ var builtIn = AppSearchService.builtInPlugins[pluginId];
+ if (builtIn && builtIn.viewMode) {
+ pref = {
+ mode: builtIn.viewMode,
+ enforced: builtIn.viewModeEnforced === true
+ };
+ }
+ } else {
+ pref = PluginService.getPluginViewPreference(pluginId);
+ }
+
+ if (pref && pref.mode) {
+ setPluginViewPreference(sectionId, pref.mode, pref.enforced);
+ } else {
+ var prefs = Object.assign({}, pluginViewPreferences);
+ delete prefs[sectionId];
+ pluginViewPreferences = prefs;
+ }
+ }
+
+ function clearActivePluginViewPreference() {
+ var prefs = {};
+ for (var key in pluginViewPreferences) {
+ if (!key.startsWith("plugin_")) {
+ prefs[key] = pluginViewPreferences[key];
+ }
+ }
+ pluginViewPreferences = prefs;
+ }
+
+ property int _searchVersion: 0
+ property bool _pluginPhasePending: false
+ property bool _pluginPhaseForceFirst: false
+ property var _phase1Items: []
+
+ Timer {
+ id: searchDebounce
+ interval: 60
+ onTriggered: root.performSearch()
+ }
+
+ Timer {
+ id: pluginPhaseTimer
+ interval: 1
+ onTriggered: root._performPluginPhase()
+ }
+
+ Timer {
+ id: fileSearchDebounce
+ interval: 200
+ onTriggered: root.performFileSearch()
+ }
+
+ function getOrTransformApp(app) {
+ return AppSearchService.getOrTransformApp(app, transformApp);
+ }
+
+ function setSearchQuery(query) {
+ _searchVersion++;
+ _queryDrivenSearch = true;
+ _pluginPhasePending = false;
+ _phase1Items = [];
+ pluginPhaseTimer.stop();
+ searchQuery = query;
+ searchDebounce.restart();
+
+ var filesInAll = searchMode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll);
+ if (searchMode !== "plugins" && (searchMode === "files" || query.startsWith("/") || filesInAll) && query.length > 0) {
+ fileSearchDebounce.restart();
+ }
+ }
+
+ function setMode(mode, isAutoSwitch) {
+ if (searchMode === mode)
+ return;
+ if (isAutoSwitch) {
+ previousSearchMode = searchMode;
+ autoSwitchedToFiles = true;
+ } else {
+ autoSwitchedToFiles = false;
+ }
+ searchMode = mode;
+ modeChanged(mode);
+ performSearch();
+ var filesInAll = mode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll) && searchQuery.length > 0;
+ if (mode === "files" || filesInAll) {
+ fileSearchDebounce.restart();
+ }
+ }
+
+ function restorePreviousMode() {
+ if (!autoSwitchedToFiles)
+ return;
+ autoSwitchedToFiles = false;
+ searchMode = previousSearchMode;
+ modeChanged(previousSearchMode);
+ performSearch();
+ }
+
+ function cycleMode(reverse = false) {
+ var modes = ["all", "apps", "files", "plugins"];
+ var currentIndex = modes.indexOf(searchMode);
+ if (!reverse)
+ var nextIndex = (currentIndex + 1) % modes.length;
+ else
+ var nextIndex = (currentIndex - 1 + modes.length) % modes.length;
+ setMode(modes[nextIndex]);
+ }
+
+ function reset() {
+ searchQuery = "";
+ searchMode = "all";
+ previousSearchMode = "all";
+ autoSwitchedToFiles = false;
+ isFileSearching = false;
+ fileSearchType = "all";
+ fileSearchExt = "";
+ fileSearchFolder = "";
+ fileSearchSort = "score";
+ sections = [];
+ flatModel = [];
+ selectedFlatIndex = 0;
+ selectedItem = null;
+ isSearching = false;
+ activePluginId = "";
+ activePluginName = "";
+ activePluginCategories = [];
+ activePluginCategory = "";
+ appCategory = "";
+ appCategories = [];
+ pluginFilter = "";
+ collapsedSections = {};
+ _clearModeCache();
+ _queryDrivenSearch = false;
+ _pluginPhasePending = false;
+ _pluginPhaseForceFirst = false;
+ _phase1Items = [];
+ pluginPhaseTimer.stop();
+ }
+
+ function loadPluginCategories(pluginId) {
+ if (!pluginId) {
+ if (activePluginCategories.length > 0) {
+ activePluginCategories = [];
+ activePluginCategory = "";
+ }
+ return;
+ }
+
+ const categories = AppSearchService.getPluginLauncherCategories(pluginId);
+ if (categories.length === activePluginCategories.length) {
+ let same = true;
+ for (let i = 0; i < categories.length; i++) {
+ if (categories[i].id !== activePluginCategories[i]?.id) {
+ same = false;
+ break;
+ }
+ }
+ if (same)
+ return;
+ }
+ activePluginCategories = categories;
+ activePluginCategory = "";
+ AppSearchService.setPluginLauncherCategory(pluginId, "");
+ }
+
+ function setActivePluginCategory(categoryId) {
+ if (activePluginCategory === categoryId)
+ return;
+ activePluginCategory = categoryId;
+ AppSearchService.setPluginLauncherCategory(activePluginId, categoryId);
+ performSearch();
+ }
+
+ function setAppCategory(category) {
+ if (appCategory === category)
+ return;
+ appCategory = category;
+ _queryDrivenSearch = true;
+ _clearModeCache();
+ performSearch();
+ }
+
+ function _loadAppCategories() {
+ appCategories = AppSearchService.getAllCategories();
+ }
+
+ function setFileSearchType(type) {
+ if (fileSearchType === type)
+ return;
+ fileSearchType = type;
+ performFileSearch();
+ }
+
+ function setFileSearchExt(ext) {
+ if (fileSearchExt === ext)
+ return;
+ fileSearchExt = ext;
+ performFileSearch();
+ }
+
+ function setFileSearchFolder(folder) {
+ if (fileSearchFolder === folder)
+ return;
+ fileSearchFolder = folder;
+ performFileSearch();
+ }
+
+ function setFileSearchSort(sort) {
+ if (fileSearchSort === sort)
+ return;
+ fileSearchSort = sort;
+ performFileSearch();
+ }
+
+ function clearPluginFilter() {
+ if (pluginFilter) {
+ pluginFilter = "";
+ performSearch();
+ return true;
+ }
+ return false;
+ }
+
+ function preserveSelectionAfterUpdate(forceFirst) {
+ if (forceFirst)
+ return function () {
+ return getFirstItemIndex();
+ };
+ var previousSelectedId = selectedItem?.id || "";
+ return function (newFlatModel) {
+ if (!previousSelectedId)
+ return getFirstItemIndex();
+ for (var i = 0; i < newFlatModel.length; i++) {
+ if (!newFlatModel[i].isHeader && newFlatModel[i].item?.id === previousSelectedId)
+ return i;
+ }
+ return getFirstItemIndex();
+ };
+ }
+
+ function performSearch() {
+ queryChanged(searchQuery);
+
+ var currentVersion = _searchVersion;
+ isSearching = true;
+ var shouldResetSelection = _queryDrivenSearch;
+ _queryDrivenSearch = false;
+ var restoreSelection = preserveSelectionAfterUpdate(shouldResetSelection);
+
+ var cachedSections = AppSearchService.getCachedDefaultSections();
+ if (!cachedSections && !_diskCacheConsumed && !searchQuery && searchMode === "all" && !pluginFilter) {
+ _diskCacheConsumed = true;
+ var diskSections = _loadDiskCache();
+ if (diskSections) {
+ activePluginId = "";
+ activePluginName = "";
+ activePluginCategories = [];
+ activePluginCategory = "";
+ clearActivePluginViewPreference();
+ for (var i = 0; i < diskSections.length; i++) {
+ if (collapsedSections[diskSections[i].id] !== undefined)
+ diskSections[i].collapsed = collapsedSections[diskSections[i].id];
+ }
+ _applyHighlights(diskSections, "");
+ flatModel = Scorer.flattenSections(diskSections);
+ sections = diskSections;
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+ }
+
+ if (cachedSections && !searchQuery && searchMode === "all" && !pluginFilter) {
+ activePluginId = "";
+ activePluginName = "";
+ activePluginCategories = [];
+ activePluginCategory = "";
+ clearActivePluginViewPreference();
+ var modeCache = _getCachedModeData("all");
+ if (modeCache) {
+ _applyHighlights(modeCache.sections, "");
+ sections = modeCache.sections;
+ flatModel = modeCache.flatModel;
+ } else {
+ var newSections = cachedSections.map(function (s) {
+ var copy = Object.assign({}, s, {
+ items: s.items ? s.items.slice() : []
+ });
+ if (collapsedSections[s.id] !== undefined)
+ copy.collapsed = collapsedSections[s.id];
+ return copy;
+ });
+ _applyHighlights(newSections, "");
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ _setCachedModeData("all", sections, flatModel);
+ }
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ var allItems = [];
+
+ var triggerMatch = detectTrigger(searchQuery);
+ if (triggerMatch.pluginId) {
+ var pluginChanged = activePluginId !== triggerMatch.pluginId;
+ activePluginId = triggerMatch.pluginId;
+ activePluginName = getPluginName(triggerMatch.pluginId, triggerMatch.isBuiltIn);
+ applyActivePluginViewPreference(triggerMatch.pluginId, triggerMatch.isBuiltIn);
+
+ if (pluginChanged && !triggerMatch.isBuiltIn)
+ loadPluginCategories(triggerMatch.pluginId);
+
+ var pluginItems = getPluginItems(triggerMatch.pluginId, triggerMatch.query);
+ for (var k = 0; k < pluginItems.length; k++)
+ allItems.push(pluginItems[k]);
+
+ if (triggerMatch.isBuiltIn) {
+ var builtInItems = AppSearchService.getBuiltInLauncherItems(triggerMatch.pluginId, triggerMatch.query);
+ for (var j = 0; j < builtInItems.length; j++) {
+ allItems.push(transformBuiltInLauncherItem(builtInItems[j], triggerMatch.pluginId));
+ }
+ }
+
+ var dynamicDefs = buildDynamicSectionDefs(allItems);
+ var scoredItems = Scorer.scoreItems(allItems, triggerMatch.query, getFrecencyForItem);
+ var sortAlpha = !triggerMatch.query && SettingsData.sortAppsAlphabetically;
+ var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
+
+ for (var sid in collapsedSections) {
+ for (var i = 0; i < newSections.length; i++) {
+ if (newSections[i].id === sid) {
+ newSections[i].collapsed = collapsedSections[sid];
+ }
+ }
+ }
+
+ _applyHighlights(newSections, triggerMatch.query);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ activePluginId = "";
+ activePluginName = "";
+ activePluginCategories = [];
+ activePluginCategory = "";
+ clearActivePluginViewPreference();
+
+ if (searchMode === "files") {
+ var fileQuery = searchQuery.startsWith("/") ? searchQuery.substring(1).trim() : searchQuery.trim();
+ isFileSearching = fileQuery.length >= 2 && DSearchService.dsearchAvailable;
+ sections = [];
+ flatModel = [];
+ selectedFlatIndex = 0;
+ selectedItem = null;
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ if (searchMode === "apps") {
+ var isCategoryFiltered = appCategory && appCategory !== I18n.tr("All");
+ var cachedSections = AppSearchService.getCachedDefaultSections();
+ if (cachedSections && !searchQuery && !isCategoryFiltered) {
+ var modeCache = _getCachedModeData("apps");
+ if (modeCache) {
+ _applyHighlights(modeCache.sections, "");
+ sections = modeCache.sections;
+ flatModel = modeCache.flatModel;
+ } else {
+ var appSectionIds = ["favorites", "apps"];
+ var newSections = cachedSections.filter(function (s) {
+ return appSectionIds.indexOf(s.id) !== -1;
+ }).map(function (s) {
+ var copy = Object.assign({}, s, {
+ items: s.items ? s.items.slice() : []
+ });
+ if (collapsedSections[s.id] !== undefined)
+ copy.collapsed = collapsedSections[s.id];
+ return copy;
+ });
+ _applyHighlights(newSections, "");
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ _setCachedModeData("apps", sections, flatModel);
+ }
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ if (isCategoryFiltered) {
+ var rawApps = AppSearchService.getAppsInCategory(appCategory);
+ for (var i = 0; i < rawApps.length; i++) {
+ allItems.push(getOrTransformApp(rawApps[i]));
+ }
+ // Also include core apps (DMS Settings etc.) that match this category
+ var allCoreApps = AppSearchService.getCoreApps("");
+ for (var i = 0; i < allCoreApps.length; i++) {
+ var coreAppCats = AppSearchService.getCategoriesForApp(allCoreApps[i]);
+ if (coreAppCats.indexOf(appCategory) !== -1)
+ allItems.push(transformCoreApp(allCoreApps[i]));
+ }
+ } else {
+ var apps = searchApps(searchQuery);
+ for (var i = 0; i < apps.length; i++) {
+ allItems.push(apps[i]);
+ }
+ }
+
+ var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
+ var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically;
+ var newSections = Scorer.groupBySection(scoredItems, sectionDefinitions, sortAlpha, searchQuery ? 50 : 500);
+
+ for (var sid in collapsedSections) {
+ for (var i = 0; i < newSections.length; i++) {
+ if (newSections[i].id === sid) {
+ newSections[i].collapsed = collapsedSections[sid];
+ }
+ }
+ }
+
+ _applyHighlights(newSections, searchQuery);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ if (searchMode === "plugins") {
+ if (!searchQuery && !pluginFilter) {
+ var browseItems = getPluginBrowseItems();
+ for (var k = 0; k < browseItems.length; k++)
+ allItems.push(browseItems[k]);
+ } else if (pluginFilter) {
+ var isBuiltInFilter = !!AppSearchService.builtInPlugins[pluginFilter];
+ applyActivePluginViewPreference(pluginFilter, isBuiltInFilter);
+
+ var filterItems = getPluginItems(pluginFilter, searchQuery);
+ for (var k = 0; k < filterItems.length; k++)
+ allItems.push(filterItems[k]);
+
+ var builtInItems = AppSearchService.getBuiltInLauncherItems(pluginFilter, searchQuery);
+ for (var j = 0; j < builtInItems.length; j++) {
+ allItems.push(transformBuiltInLauncherItem(builtInItems[j], pluginFilter));
+ }
+ } else {
+ var emptyTriggerPlugins = getEmptyTriggerPlugins();
+ for (var i = 0; i < emptyTriggerPlugins.length; i++) {
+ var pluginId = emptyTriggerPlugins[i];
+ var pItems = getPluginItems(pluginId, searchQuery);
+ for (var k = 0; k < pItems.length; k++)
+ allItems.push(pItems[k]);
+ }
+
+ var builtInLauncherPlugins = getBuiltInEmptyTriggerLaunchers();
+ for (var i = 0; i < builtInLauncherPlugins.length; i++) {
+ var pluginId = builtInLauncherPlugins[i];
+ var blItems = AppSearchService.getBuiltInLauncherItems(pluginId, searchQuery);
+ for (var j = 0; j < blItems.length; j++) {
+ allItems.push(transformBuiltInLauncherItem(blItems[j], pluginId));
+ }
+ }
+ }
+
+ var dynamicDefs = buildDynamicSectionDefs(allItems);
+ var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
+ var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically;
+ var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, 500);
+
+ for (var sid in collapsedSections) {
+ for (var i = 0; i < newSections.length; i++) {
+ if (newSections[i].id === sid) {
+ newSections[i].collapsed = collapsedSections[sid];
+ }
+ }
+ }
+
+ _applyHighlights(newSections, searchQuery);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+
+ isSearching = false;
+ searchCompleted();
+ return;
+ }
+
+ var apps = searchApps(searchQuery);
+ for (var i = 0; i < apps.length; i++) {
+ allItems.push(apps[i]);
+ }
+
+ if (searchMode === "all") {
+ if (searchQuery && searchQuery.length >= 2) {
+ _pluginPhasePending = true;
+ _phase1Items = allItems.slice();
+ _pluginPhaseForceFirst = shouldResetSelection;
+ pluginPhaseTimer.restart();
+ isSearching = true;
+ searchCompleted();
+ return;
+ } else if (!searchQuery) {
+ var emptyTriggerOrdered = getEmptyTriggerPluginsOrdered();
+ for (var i = 0; i < emptyTriggerOrdered.length; i++) {
+ var plugin = emptyTriggerOrdered[i];
+ if (plugin.isBuiltIn) {
+ var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
+ for (var j = 0; j < blItems.length; j++)
+ allItems.push(transformBuiltInLauncherItem(blItems[j], plugin.id));
+ } else {
+ var pItems = getPluginItems(plugin.id, searchQuery);
+ for (var j = 0; j < pItems.length; j++)
+ allItems.push(pItems[j]);
+ }
+ }
+
+ var browseItems = getPluginBrowseItems();
+ for (var i = 0; i < browseItems.length; i++)
+ allItems.push(browseItems[i]);
+ }
+ }
+
+ var dynamicDefs = buildDynamicSectionDefs(allItems);
+
+ if (currentVersion !== _searchVersion) {
+ isSearching = false;
+ return;
+ }
+
+ var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
+ var sortAlpha = !searchQuery && SettingsData.sortAppsAlphabetically;
+ var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, sortAlpha, searchQuery ? 50 : 500);
+
+ if (currentVersion !== _searchVersion) {
+ isSearching = false;
+ return;
+ }
+
+ for (var i = 0; i < newSections.length; i++) {
+ var sid = newSections[i].id;
+ if (collapsedSections[sid] !== undefined) {
+ newSections[i].collapsed = collapsedSections[sid];
+ }
+ }
+
+ _applyHighlights(newSections, searchQuery);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+
+ if (!AppSearchService.isCacheValid() && !searchQuery && searchMode === "all" && !pluginFilter) {
+ AppSearchService.setCachedDefaultSections(sections, flatModel);
+ _saveDiskCache(sections);
+ }
+
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+
+ isSearching = _pluginPhasePending;
+ searchCompleted();
+ }
+
+ function _performPluginPhase() {
+ _pluginPhasePending = false;
+ if (!searchQuery || searchQuery.length < 2 || searchMode !== "all")
+ return;
+
+ var currentVersion = _searchVersion;
+ var restoreSelection = preserveSelectionAfterUpdate(_pluginPhaseForceFirst);
+ var allItems = _phase1Items;
+ _phase1Items = [];
+
+ var allPluginsOrdered = getAllVisiblePluginsOrdered();
+ var maxPerPlugin = 10;
+ for (var i = 0; i < allPluginsOrdered.length; i++) {
+ if (currentVersion !== _searchVersion)
+ return;
+ var plugin = allPluginsOrdered[i];
+ if (plugin.isBuiltIn) {
+ var blItems = AppSearchService.getBuiltInLauncherItems(plugin.id, searchQuery);
+ var blLimit = Math.min(blItems.length, maxPerPlugin);
+ for (var j = 0; j < blLimit; j++) {
+ var item = transformBuiltInLauncherItem(blItems[j], plugin.id);
+ item._preScored = 900 - j;
+ allItems.push(item);
+ }
+ } else {
+ var pItems = getPluginItems(plugin.id, searchQuery, maxPerPlugin);
+ for (var j = 0; j < pItems.length; j++) {
+ pItems[j]._preScored = 900 - j;
+ allItems.push(pItems[j]);
+ }
+ }
+ }
+
+ if (currentVersion !== _searchVersion)
+ return;
+
+ var dynamicDefs = buildDynamicSectionDefs(allItems);
+ var scoredItems = Scorer.scoreItems(allItems, searchQuery, getFrecencyForItem);
+ var newSections = Scorer.groupBySection(scoredItems, dynamicDefs, false, 50);
+
+ if (currentVersion !== _searchVersion)
+ return;
+
+ for (var i = 0; i < newSections.length; i++) {
+ var sid = newSections[i].id;
+ if (collapsedSections[sid] !== undefined)
+ newSections[i].collapsed = collapsedSections[sid];
+ }
+
+ _applyHighlights(newSections, searchQuery);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ selectedFlatIndex = restoreSelection(flatModel);
+ updateSelectedItem();
+ isSearching = false;
+ searchCompleted();
+ }
+
+ function performFileSearch() {
+ if (!DSearchService.dsearchAvailable)
+ return;
+ var fileQuery = "";
+ var effectiveType = fileSearchType || "all";
+ var includeFiles = SettingsData.dankLauncherV2IncludeFilesInAll;
+ var includeFolders = SettingsData.dankLauncherV2IncludeFoldersInAll;
+
+ if (searchQuery.startsWith("/")) {
+ fileQuery = searchQuery.substring(1).trim();
+ } else if (searchMode === "files") {
+ fileQuery = searchQuery.trim();
+ } else if (searchMode === "all" && (includeFiles || includeFolders)) {
+ fileQuery = searchQuery.trim();
+ if (includeFiles && !includeFolders)
+ effectiveType = "file";
+ else if (!includeFiles && includeFolders)
+ effectiveType = "dir";
+ else
+ effectiveType = "all";
+ } else {
+ return;
+ }
+
+ if (fileQuery.length < 2) {
+ isFileSearching = false;
+ return;
+ }
+
+ isFileSearching = true;
+
+ var splitBothTypes = searchMode === "all" && includeFiles && includeFolders && DSearchService.supportsTypeFilter;
+ var queryTypes = splitBothTypes ? ["file", "dir"] : [effectiveType];
+ var pending = queryTypes.length;
+ var aggregatedItems = [];
+
+ for (var t = 0; t < queryTypes.length; t++) {
+ var queryType = queryTypes[t];
+ var params = {
+ limit: 20,
+ fuzzy: true,
+ sort: fileSearchSort || "score",
+ desc: true
+ };
+
+ if (DSearchService.supportsTypeFilter) {
+ params.type = (queryType && queryType !== "all") ? queryType : "all";
+ }
+ if (fileSearchExt) {
+ params.ext = fileSearchExt;
+ }
+ if (fileSearchFolder) {
+ params.folder = fileSearchFolder;
+ }
+
+ DSearchService.search(fileQuery, params, function (response) {
+ pending--;
+ if (!response.error) {
+ var hits = response.result?.hits || [];
+ for (var i = 0; i < hits.length; i++) {
+ var hit = hits[i];
+ var docTypes = hit.locations?.doc_type;
+ var isDir = docTypes ? !!docTypes["dir"] : false;
+ aggregatedItems.push(transformFileResult({
+ path: hit.id || "",
+ score: hit.score || 0,
+ is_dir: isDir
+ }));
+ }
+ }
+ if (pending > 0)
+ return;
+
+ isFileSearching = false;
+ _applyFileSearchResults(aggregatedItems, effectiveType);
+ });
+ }
+ }
+
+ function _applyFileSearchResults(fileItems, effectiveType) {
+ var fileSections = [];
+ var showType = effectiveType;
+ var order = SettingsData.launcherPluginOrder || [];
+ var filesOrderIdx = order.indexOf("__files");
+ var foldersOrderIdx = order.indexOf("__folders");
+ var filesPriority = filesOrderIdx !== -1 ? 2.6 + filesOrderIdx * 0.01 : 4;
+ var foldersPriority = foldersOrderIdx !== -1 ? 2.6 + foldersOrderIdx * 0.01 : 4.1;
+
+ if (showType === "all" && DSearchService.supportsTypeFilter) {
+ var onlyFiles = [];
+ var onlyDirs = [];
+ for (var j = 0; j < fileItems.length; j++) {
+ if (fileItems[j].data?.is_dir)
+ onlyDirs.push(fileItems[j]);
+ else
+ onlyFiles.push(fileItems[j]);
+ }
+ if (onlyFiles.length > 0) {
+ fileSections.push({
+ id: "files",
+ title: I18n.tr("Files"),
+ icon: "insert_drive_file",
+ priority: filesPriority,
+ items: onlyFiles,
+ collapsed: collapsedSections["files"] || false,
+ flatStartIndex: 0
+ });
+ }
+ if (onlyDirs.length > 0) {
+ fileSections.push({
+ id: "folders",
+ title: I18n.tr("Folders"),
+ icon: "folder",
+ priority: foldersPriority,
+ items: onlyDirs,
+ collapsed: collapsedSections["folders"] || false,
+ flatStartIndex: 0
+ });
+ }
+ } else {
+ var filesIcon = showType === "dir" ? "folder" : showType === "file" ? "insert_drive_file" : "folder";
+ var filesTitle = showType === "dir" ? I18n.tr("Folders") : I18n.tr("Files");
+ var singlePriority = showType === "dir" ? foldersPriority : filesPriority;
+ if (fileItems.length > 0) {
+ fileSections.push({
+ id: "files",
+ title: filesTitle,
+ icon: filesIcon,
+ priority: singlePriority,
+ items: fileItems,
+ collapsed: collapsedSections["files"] || false,
+ flatStartIndex: 0
+ });
+ }
+ }
+
+ var newSections;
+ if (searchMode === "files") {
+ newSections = fileSections;
+ } else {
+ var existingNonFile = sections.filter(function (s) {
+ return s.id !== "files" && s.id !== "folders";
+ });
+ newSections = existingNonFile.concat(fileSections);
+ }
+ newSections.sort(function (a, b) {
+ return a.priority - b.priority;
+ });
+ _applyHighlights(newSections, searchQuery);
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+ selectedFlatIndex = getFirstItemIndex();
+ updateSelectedItem();
+ }
+
+ function searchApps(query) {
+ var apps = AppSearchService.searchApplications(query);
+ var items = [];
+
+ for (var i = 0; i < apps.length; i++) {
+ items.push(getOrTransformApp(apps[i]));
+ }
+
+ var coreApps = AppSearchService.getCoreApps(query);
+ for (var i = 0; i < coreApps.length; i++) {
+ items.push(transformCoreApp(coreApps[i]));
+ }
+
+ return items;
+ }
+
+ function transformApp(app) {
+ var appId = app.id || app.execString || app.exec || "";
+ var override = SessionData.getAppOverride(appId);
+ return Transform.transformApp(app, override, [], I18n.tr("Launch"));
+ }
+
+ function transformCoreApp(app) {
+ return Transform.transformCoreApp(app, I18n.tr("Open"));
+ }
+
+ function transformBuiltInLauncherItem(item, pluginId) {
+ return Transform.transformBuiltInLauncherItem(item, pluginId, I18n.tr("Open"));
+ }
+
+ function transformFileResult(file) {
+ return Transform.transformFileResult(file, I18n.tr("Open"), I18n.tr("Open folder"), I18n.tr("Copy path"), I18n.tr("Open in terminal"));
+ }
+
+ function detectTrigger(query) {
+ if (!query || query.length === 0)
+ return {
+ pluginId: null,
+ query: query
+ };
+
+ var pluginTriggers = PluginService.getAllPluginTriggers();
+ for (var trigger in pluginTriggers) {
+ if (trigger && query.startsWith(trigger)) {
+ return {
+ pluginId: pluginTriggers[trigger],
+ query: query.substring(trigger.length).trim()
+ };
+ }
+ }
+
+ var builtInTriggers = AppSearchService.getBuiltInLauncherTriggers();
+ for (var trigger in builtInTriggers) {
+ if (trigger && query.startsWith(trigger)) {
+ return {
+ pluginId: builtInTriggers[trigger],
+ query: query.substring(trigger.length).trim(),
+ isBuiltIn: true
+ };
+ }
+ }
+
+ return {
+ pluginId: null,
+ query: query
+ };
+ }
+
+ function getEmptyTriggerPlugins() {
+ var plugins = PluginService.getPluginsWithEmptyTrigger();
+ var visible = plugins.filter(function (pluginId) {
+ return SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ });
+ return sortPluginIdsByOrder(visible);
+ }
+
+ function getAllLauncherPluginIds() {
+ var launchers = PluginService.getLauncherPlugins();
+ return Object.keys(launchers);
+ }
+
+ function getVisibleLauncherPluginIds() {
+ var launchers = PluginService.getLauncherPlugins();
+ var visible = Object.keys(launchers).filter(function (pluginId) {
+ return SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ });
+ return sortPluginIdsByOrder(visible);
+ }
+
+ function getAllBuiltInLauncherIds() {
+ var launchers = AppSearchService.getBuiltInLauncherPlugins();
+ return Object.keys(launchers);
+ }
+
+ function getVisibleBuiltInLauncherIds() {
+ var launchers = AppSearchService.getBuiltInLauncherPlugins();
+ var visible = Object.keys(launchers).filter(function (pluginId) {
+ return SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ });
+ return sortPluginIdsByOrder(visible);
+ }
+
+ function sortPluginIdsByOrder(pluginIds) {
+ return Utils.sortPluginIdsByOrder(pluginIds, SettingsData.launcherPluginOrder || []);
+ }
+
+ function getAllVisiblePluginsOrdered() {
+ var thirdPartyLaunchers = PluginService.getLauncherPlugins() || {};
+ var builtInLaunchers = AppSearchService.getBuiltInLauncherPlugins() || {};
+ var all = [];
+ for (var id in thirdPartyLaunchers) {
+ if (SettingsData.getPluginAllowWithoutTrigger(id))
+ all.push({
+ id: id,
+ isBuiltIn: false
+ });
+ }
+ for (var id in builtInLaunchers) {
+ if (SettingsData.getPluginAllowWithoutTrigger(id))
+ all.push({
+ id: id,
+ isBuiltIn: true
+ });
+ }
+ return Utils.sortPluginsOrdered(all, SettingsData.launcherPluginOrder || []);
+ }
+
+ function getEmptyTriggerPluginsOrdered() {
+ var thirdParty = PluginService.getPluginsWithEmptyTrigger() || [];
+ var builtIn = AppSearchService.getBuiltInLauncherPluginsWithEmptyTrigger() || [];
+ var all = [];
+ for (var i = 0; i < thirdParty.length; i++) {
+ var id = thirdParty[i];
+ if (SettingsData.getPluginAllowWithoutTrigger(id))
+ all.push({
+ id: id,
+ isBuiltIn: false
+ });
+ }
+ for (var i = 0; i < builtIn.length; i++) {
+ var id = builtIn[i];
+ if (SettingsData.getPluginAllowWithoutTrigger(id))
+ all.push({
+ id: id,
+ isBuiltIn: true
+ });
+ }
+ return Utils.sortPluginsOrdered(all, SettingsData.launcherPluginOrder || []);
+ }
+
+ function getPluginBrowseItems() {
+ var items = [];
+ var browseLabel = I18n.tr("Browse");
+ var triggerLabel = I18n.tr("Trigger: %1");
+ var noTriggerLabel = I18n.tr("No trigger");
+
+ var launchers = PluginService.getLauncherPlugins();
+ for (var pluginId in launchers) {
+ var trigger = PluginService.getPluginTrigger(pluginId);
+ var isAllowed = SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ items.push(Transform.createPluginBrowseItem(pluginId, launchers[pluginId], trigger, false, isAllowed, browseLabel, triggerLabel, noTriggerLabel));
+ }
+
+ var builtInLaunchers = AppSearchService.getBuiltInLauncherPlugins();
+ for (var pluginId in builtInLaunchers) {
+ var trigger = AppSearchService.getBuiltInPluginTrigger(pluginId);
+ var isAllowed = SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ items.push(Transform.createPluginBrowseItem(pluginId, builtInLaunchers[pluginId], trigger, true, isAllowed, browseLabel, triggerLabel, noTriggerLabel));
+ }
+
+ return items;
+ }
+
+ function getBuiltInEmptyTriggerLaunchers() {
+ var plugins = AppSearchService.getBuiltInLauncherPluginsWithEmptyTrigger();
+ var visible = plugins.filter(function (pluginId) {
+ return SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ });
+ return sortPluginIdsByOrder(visible);
+ }
+
+ function getPluginItems(pluginId, query, limit) {
+ var items = AppSearchService.getPluginItemsForPlugin(pluginId, query);
+ var count = limit > 0 && limit < items.length ? limit : items.length;
+ var transformed = [];
+
+ for (var i = 0; i < count; i++) {
+ transformed.push(transformPluginItem(items[i], pluginId));
+ }
+
+ return transformed;
+ }
+
+ function getPluginName(pluginId, isBuiltIn) {
+ if (isBuiltIn) {
+ var plugin = AppSearchService.builtInPlugins[pluginId];
+ return plugin ? plugin.name : pluginId;
+ }
+ var launchers = PluginService.getLauncherPlugins();
+ if (launchers[pluginId]) {
+ return launchers[pluginId].name || pluginId;
+ }
+ return pluginId;
+ }
+
+ function getPluginMetadata(pluginId) {
+ var builtIn = AppSearchService.builtInPlugins[pluginId];
+ if (builtIn) {
+ return {
+ name: builtIn.name || pluginId,
+ icon: builtIn.cornerIcon || "extension"
+ };
+ }
+ var launchers = PluginService.getLauncherPlugins();
+ if (launchers[pluginId]) {
+ var rawIcon = launchers[pluginId].icon || "extension";
+ return {
+ name: launchers[pluginId].name || pluginId,
+ icon: Utils.stripIconPrefix(rawIcon)
+ };
+ }
+ return {
+ name: pluginId,
+ icon: "extension"
+ };
+ }
+
+ function buildDynamicSectionDefs(items) {
+ var baseDefs = sectionDefinitions.slice();
+ var pluginSections = {};
+ var order = SettingsData.launcherPluginOrder || [];
+ var orderMap = {};
+ for (var k = 0; k < order.length; k++)
+ orderMap[order[k]] = k;
+ var unorderedPriority = 2.6 + order.length * 0.01;
+
+ for (var i = 0; i < items.length; i++) {
+ var section = items[i].section;
+ if (!section || !section.startsWith("plugin_"))
+ continue;
+ if (pluginSections[section])
+ continue;
+ var pluginId = section.substring(7);
+ var meta = getPluginMetadata(pluginId);
+ var viewPref = getPluginViewPref(pluginId);
+ var orderIdx = orderMap[pluginId];
+ var priority;
+ if (orderIdx !== undefined) {
+ priority = 2.6 + orderIdx * 0.01;
+ } else {
+ priority = unorderedPriority;
+ unorderedPriority += 0.01;
+ }
+
+ pluginSections[section] = {
+ id: section,
+ title: meta.name,
+ icon: meta.icon,
+ priority: priority,
+ defaultViewMode: viewPref.mode || "list"
+ };
+
+ if (viewPref.mode)
+ setPluginViewPreference(section, viewPref.mode, viewPref.enforced);
+ }
+
+ for (var sectionId in pluginSections) {
+ baseDefs.push(pluginSections[sectionId]);
+ }
+
+ baseDefs.sort(function (a, b) {
+ return a.priority - b.priority;
+ });
+ return baseDefs;
+ }
+
+ function getPluginViewPref(pluginId) {
+ var builtIn = AppSearchService.builtInPlugins[pluginId];
+ if (builtIn && builtIn.viewMode) {
+ return {
+ mode: builtIn.viewMode,
+ enforced: builtIn.viewModeEnforced === true
+ };
+ }
+
+ var pref = PluginService.getPluginViewPreference(pluginId);
+ if (pref && pref.mode) {
+ return pref;
+ }
+
+ return {
+ mode: "list",
+ enforced: false
+ };
+ }
+
+ function transformPluginItem(item, pluginId) {
+ return Transform.transformPluginItem(item, pluginId, I18n.tr("Select"));
+ }
+
+ function getFrecencyForItem(item) {
+ if (item.type !== "app")
+ return null;
+
+ var appId = item.id;
+ var usageRanking = AppUsageHistoryData.appUsageRanking || {};
+
+ var idVariants = [appId, appId.replace(".desktop", "")];
+ var usageData = null;
+
+ for (var i = 0; i < idVariants.length; i++) {
+ if (usageRanking[idVariants[i]]) {
+ usageData = usageRanking[idVariants[i]];
+ break;
+ }
+ }
+
+ return {
+ usageCount: usageData?.usageCount || 0
+ };
+ }
+
+ function getFirstItemIndex() {
+ return Nav.getFirstItemIndex(flatModel);
+ }
+
+ function _getCachedModeData(mode) {
+ return _modeSectionsCache[mode] || null;
+ }
+
+ function _setCachedModeData(mode, sectionsData, flatModelData) {
+ var cache = Object.assign({}, _modeSectionsCache);
+ cache[mode] = {
+ sections: sectionsData,
+ flatModel: flatModelData
+ };
+ _modeSectionsCache = cache;
+ }
+
+ function _clearModeCache() {
+ _modeSectionsCache = {};
+ }
+
+ function _saveDiskCache(sectionsData) {
+ var serializable = [];
+ for (var i = 0; i < sectionsData.length; i++) {
+ var s = sectionsData[i];
+ var items = [];
+ var srcItems = s.items || [];
+ for (var j = 0; j < srcItems.length; j++) {
+ var it = srcItems[j];
+ items.push({
+ id: it.id,
+ type: it.type,
+ name: it.name || "",
+ subtitle: it.subtitle || "",
+ icon: it.icon || "",
+ iconType: it.iconType || "image",
+ iconFull: it.iconFull || "",
+ section: it.section || "",
+ isCore: it.isCore || false,
+ isBuiltInLauncher: it.isBuiltInLauncher || false,
+ pluginId: it.pluginId || ""
+ });
+ }
+ serializable.push({
+ id: s.id,
+ title: s.title || "",
+ icon: s.icon || "",
+ priority: s.priority || 0,
+ items: items
+ });
+ }
+ CacheData.saveLauncherCache(serializable);
+ }
+
+ function _actionsFromDesktopEntry(appId) {
+ if (!appId)
+ return [];
+ var entry = DesktopEntries.heuristicLookup(appId);
+ if (!entry || !entry.actions || entry.actions.length === 0)
+ return [];
+ var result = [];
+ for (var i = 0; i < entry.actions.length; i++) {
+ result.push({
+ name: entry.actions[i].name,
+ icon: "play_arrow",
+ actionData: entry.actions[i]
+ });
+ }
+ return result;
+ }
+
+ function _loadDiskCache() {
+ var cached = CacheData.loadLauncherCache();
+ if (!cached || !Array.isArray(cached) || cached.length === 0)
+ return null;
+
+ var sectionsData = [];
+ for (var i = 0; i < cached.length; i++) {
+ var s = cached[i];
+ var items = [];
+ var srcItems = s.items || [];
+ for (var j = 0; j < srcItems.length; j++) {
+ var it = srcItems[j];
+ items.push({
+ id: it.id || "",
+ type: it.type || "app",
+ name: it.name || "",
+ subtitle: it.subtitle || "",
+ icon: it.icon || "",
+ iconType: it.iconType || "image",
+ iconFull: it.iconFull || "",
+ section: it.section || "",
+ isCore: it.isCore || false,
+ isBuiltInLauncher: it.isBuiltInLauncher || false,
+ pluginId: it.pluginId || "",
+ data: {
+ id: it.id
+ },
+ actions: _actionsFromDesktopEntry(it.id),
+ primaryAction: it.type === "app" && !it.isCore ? {
+ name: I18n.tr("Launch"),
+ icon: "open_in_new",
+ action: "launch"
+ } : null,
+ _diskCached: true,
+ _hName: "",
+ _hSub: "",
+ _hRich: false,
+ _preScored: undefined
+ });
+ }
+ sectionsData.push({
+ id: s.id || "",
+ title: s.title || "",
+ icon: s.icon || "",
+ priority: s.priority || 0,
+ items: items,
+ collapsed: false,
+ flatStartIndex: 0
+ });
+ }
+ return sectionsData;
+ }
+
+ function updateSelectedItem() {
+ if (selectedFlatIndex >= 0 && selectedFlatIndex < flatModel.length) {
+ var entry = flatModel[selectedFlatIndex];
+ selectedItem = entry.isHeader ? null : entry.item;
+ } else {
+ selectedItem = null;
+ }
+ }
+
+ function _applyHighlights(sectionsData, query) {
+ if (!query || query.length === 0) {
+ for (var i = 0; i < sectionsData.length; i++) {
+ var items = sectionsData[i].items;
+ for (var j = 0; j < items.length; j++) {
+ var item = items[j];
+ item._hName = item.name || "";
+ item._hSub = item.subtitle || "";
+ item._hRich = false;
+ }
+ }
+ return;
+ }
+
+ var highlightColor = Theme.primary;
+ var nameColor = Theme.surfaceText;
+ var subColor = Theme.surfaceVariantText;
+ var lowerQuery = query.toLowerCase();
+
+ for (var i = 0; i < sectionsData.length; i++) {
+ var items = sectionsData[i].items;
+ for (var j = 0; j < items.length; j++) {
+ var item = items[j];
+ item._hName = _highlightField(item.name || "", lowerQuery, query.length, nameColor, highlightColor);
+ item._hSub = _highlightField(item.subtitle || "", lowerQuery, query.length, subColor, highlightColor);
+ item._hRich = true;
+ }
+ }
+ }
+
+ function _highlightField(text, lowerQuery, queryLen, baseColor, highlightColor) {
+ if (!text)
+ return "";
+ var idx = text.toLowerCase().indexOf(lowerQuery);
+ if (idx === -1)
+ return text;
+ var before = text.substring(0, idx);
+ var match = text.substring(idx, idx + queryLen);
+ var after = text.substring(idx + queryLen);
+ return '<span style="color:' + baseColor + '">' + before + '</span><span style="color:' + highlightColor + '; font-weight:600">' + match + '</span><span style="color:' + baseColor + '">' + after + '</span>';
+ }
+
+ function getCurrentSectionViewMode() {
+ if (selectedFlatIndex < 0 || selectedFlatIndex >= flatModel.length)
+ return "list";
+ var entry = flatModel[selectedFlatIndex];
+ if (!entry || entry.isHeader)
+ return "list";
+ return getSectionViewMode(entry.sectionId);
+ }
+
+ function getGridColumns(sectionId) {
+ return Nav.getGridColumns(getSectionViewMode(sectionId), gridColumns);
+ }
+
+ function _cancelPendingSelectionReset() {
+ _queryDrivenSearch = false;
+ _pluginPhaseForceFirst = false;
+ }
+
+ function selectNext() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculateNextIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectPrevious() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculatePrevIndex(flatModel, selectedFlatIndex, null, null, gridColumns, getSectionViewMode);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectRight() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculateRightIndex(flatModel, selectedFlatIndex, getSectionViewMode);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectLeft() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculateLeftIndex(flatModel, selectedFlatIndex, getSectionViewMode);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectNextSection() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculateNextSectionIndex(flatModel, selectedFlatIndex);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectPreviousSection() {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculatePrevSectionIndex(flatModel, selectedFlatIndex);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectPageDown(visibleItems) {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculatePageDownIndex(flatModel, selectedFlatIndex, visibleItems);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectPageUp(visibleItems) {
+ keyboardNavigationActive = true;
+ _cancelPendingSelectionReset();
+ var newIndex = Nav.calculatePageUpIndex(flatModel, selectedFlatIndex, visibleItems);
+ if (newIndex !== selectedFlatIndex) {
+ selectedFlatIndex = newIndex;
+ updateSelectedItem();
+ }
+ }
+
+ function selectIndex(index) {
+ keyboardNavigationActive = false;
+ if (index >= 0 && index < flatModel.length && !flatModel[index].isHeader) {
+ selectedFlatIndex = index;
+ updateSelectedItem();
+ }
+ }
+
+ function toggleSection(sectionId) {
+ _clearModeCache();
+ var newCollapsed = Object.assign({}, collapsedSections);
+ var currentState = newCollapsed[sectionId];
+
+ if (currentState === undefined) {
+ for (var i = 0; i < sections.length; i++) {
+ if (sections[i].id === sectionId) {
+ currentState = sections[i].collapsed || false;
+ break;
+ }
+ }
+ }
+
+ newCollapsed[sectionId] = !currentState;
+ collapsedSections = newCollapsed;
+
+ var newSections = sections.slice();
+ for (var i = 0; i < newSections.length; i++) {
+ if (newSections[i].id === sectionId) {
+ newSections[i] = Object.assign({}, newSections[i], {
+ collapsed: newCollapsed[sectionId]
+ });
+ }
+ }
+ flatModel = Scorer.flattenSections(newSections);
+ sections = newSections;
+
+ if (selectedFlatIndex >= flatModel.length) {
+ selectedFlatIndex = getFirstItemIndex();
+ }
+ updateSelectedItem();
+ }
+
+ function executeSelected() {
+ if (searchDebounce.running) {
+ searchDebounce.stop();
+ performSearch();
+ }
+ if (!selectedItem)
+ return;
+ executeItem(selectedItem);
+ }
+
+ function executeItem(item) {
+ if (!item)
+ return;
+
+ SessionData.addLauncherHistory(searchQuery);
+
+ if (item.type === "plugin_browse") {
+ var browsePluginId = item.data?.pluginId;
+ if (!browsePluginId)
+ return;
+ var browseTrigger = item.data.isBuiltIn ? AppSearchService.getBuiltInPluginTrigger(browsePluginId) : PluginService.getPluginTrigger(browsePluginId);
+
+ if (browseTrigger && browseTrigger.length > 0) {
+ searchQueryRequested(browseTrigger);
+ } else {
+ setMode("plugins");
+ pluginFilter = browsePluginId;
+ performSearch();
+ }
+ return;
+ }
+
+ switch (item.type) {
+ case "app":
+ if (item.isCore) {
+ AppSearchService.executeCoreApp(item.data);
+ } else if (item.data?.isAction) {
+ launchAppAction(item.data);
+ } else {
+ launchApp(item.data);
+ }
+ break;
+ case "plugin":
+ if (item.isBuiltInLauncher) {
+ AppSearchService.executeBuiltInLauncherItem(item.data);
+ } else {
+ AppSearchService.executePluginItem(item.data, item.pluginId);
+ }
+ break;
+ case "file":
+ openFile(item.data?.path);
+ break;
+ default:
+ return;
+ }
+
+ itemExecuted();
+ }
+
+ function executeAction(item, action) {
+ if (!item || !action)
+ return;
+ switch (action.action) {
+ case "launch":
+ executeItem(item);
+ break;
+ case "open":
+ openFile(item.data.path);
+ break;
+ case "open_folder":
+ openFolder(item.data.path);
+ break;
+ case "copy_path":
+ copyToClipboard(item.data.path);
+ break;
+ case "open_terminal":
+ openTerminal(item.data.path);
+ break;
+ case "copy":
+ copyToClipboard(item.name);
+ break;
+ case "execute":
+ executeItem(item);
+ break;
+ case "launch_dgpu":
+ if (item.type === "app" && item.data) {
+ launchAppWithNvidia(item.data);
+ }
+ break;
+ case "toggle_all_visibility":
+ if (item.type === "plugin_browse" && item.data?.pluginId) {
+ var pluginId = item.data.pluginId;
+ var currentState = SettingsData.getPluginAllowWithoutTrigger(pluginId);
+ SettingsData.setPluginAllowWithoutTrigger(pluginId, !currentState);
+ performSearch();
+ }
+ return;
+ default:
+ if (item.type === "app" && action.actionData) {
+ launchAppAction({
+ parentApp: item.data,
+ actionData: action.actionData
+ });
+ }
+ }
+
+ itemExecuted();
+ }
+
+ function _resolveDesktopEntry(app) {
+ if (!app)
+ return null;
+ if (app.command)
+ return app;
+ var id = app.id || app.execString || app.exec || "";
+ if (!id)
+ return null;
+ return DesktopEntries.heuristicLookup(id);
+ }
+
+ function launchApp(app) {
+ var entry = _resolveDesktopEntry(app);
+ if (!entry)
+ return;
+ SessionService.launchDesktopEntry(entry);
+ AppUsageHistoryData.addAppUsage(entry);
+ }
+
+ function launchAppWithNvidia(app) {
+ var entry = _resolveDesktopEntry(app);
+ if (!entry)
+ return;
+ SessionService.launchDesktopEntry(entry, true);
+ AppUsageHistoryData.addAppUsage(entry);
+ }
+
+ function launchAppAction(actionItem) {
+ if (!actionItem || !actionItem.actionData)
+ return;
+ var entry = _resolveDesktopEntry(actionItem.parentApp);
+ if (!entry)
+ return;
+ SessionService.launchDesktopAction(entry, actionItem.actionData);
+ AppUsageHistoryData.addAppUsage(entry);
+ }
+
+ function openFile(path) {
+ if (!path)
+ return;
+ Qt.openUrlExternally("file://" + path);
+ }
+
+ function openFolder(path) {
+ if (!path)
+ return;
+ var folder = path.substring(0, path.lastIndexOf("/"));
+ Qt.openUrlExternally("file://" + folder);
+ }
+
+ function openTerminal(path) {
+ if (!path)
+ return;
+ var terminal = Quickshell.env("TERMINAL") || "xterm";
+ Quickshell.execDetached({
+ command: [terminal],
+ workingDirectory: path
+ });
+ }
+
+ function copyToClipboard(text) {
+ if (!text)
+ return;
+ Quickshell.execDetached(["dms", "cl", "copy", text]);
+ }
+}