pragma Singleton import Quickshell import Quickshell.Io import "../scripts/levendist.js" as Levendist import "../scripts/fuzzysort.js" as Fuzzy import qs.Config Singleton { id: root readonly property list list: Array.from(DesktopEntries.applications.values).sort((a, b) => a.name.localeCompare(b.name)) readonly property var preppedNames: list.map(a => ({ name: Fuzzy.prepare(`${a.name} `), entry: a })) property var regexSubstitutions: [ { "regex": /^steam_app_(\d+)$/, "replace": "steam_icon_$1" }, { "regex": /Minecraft.*/, "replace": "minecraft" }, { "regex": /.*polkit.*/, "replace": "system-lock-screen" }, { "regex": /gcr.prompter/, "replace": "system-lock-screen" } ] property real scoreThreshold: 0.2 property bool sloppySearch: Config.options?.search.sloppy ?? false property var substitutions: ({ "code-url-handler": "visual-studio-code", "Code": "visual-studio-code", "gnome-tweaks": "org.gnome.tweaks", "pavucontrol-qt": "pavucontrol", "wps": "wps-office2019-kprometheus", "wpsoffice": "wps-office2019-kprometheus", "footclient": "foot", "zen": "zen-browser" }) signal reload function computeScore(...args) { return Levendist.computeScore(...args); } function computeTextMatchScore(...args) { return Levendist.computeTextMatchScore(...args); } function fuzzyQuery(search: string): var { // Idk why list doesn't work if (root.sloppySearch) { const results = list.map(obj => ({ entry: obj, score: computeScore(obj.name.toLowerCase(), search.toLowerCase()) })).filter(item => item.score > root.scoreThreshold).sort((a, b) => b.score - a.score); return results.map(item => item.entry); } return Fuzzy.go(search, preppedNames, { all: true, key: "name" }).map(r => { return r.obj.entry; }); } function guessIcon(str) { if (!str || str.length == 0) return "image-missing"; // Normal substitutions if (substitutions[str]) return substitutions[str]; // Regex substitutions for (let i = 0; i < regexSubstitutions.length; i++) { const substitution = regexSubstitutions[i]; const replacedName = str.replace(substitution.regex, substitution.replace); if (replacedName != str) return replacedName; } // If it gets detected normally, no need to guess if (iconExists(str)) return str; let guessStr = str; // Guess: Take only app name of reverse domain name notation guessStr = str.split('.').slice(-1)[0].toLowerCase(); if (iconExists(guessStr)) return guessStr; // Guess: normalize to kebab case guessStr = str.toLowerCase().replace(/\s+/g, "-"); if (iconExists(guessStr)) return guessStr; // Guess: First fuzze desktop entry match const searchResults = root.fuzzyQuery(str); if (searchResults.length > 0) { const firstEntry = searchResults[0]; guessStr = firstEntry.icon; if (iconExists(guessStr)) return guessStr; } // Give up return str; } function iconExists(iconName) { if (!iconName || iconName.length == 0) return false; return (Quickshell.iconPath(iconName, true).length > 0) && !iconName.includes("image-missing"); } }