diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-07-11 21:49:53 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-07-11 21:49:53 +0800 |
| commit | 0e4bdc7ed815ef389d9ca95fc4865f46701b1aaf (patch) | |
| tree | 09c5e1609a4e3526416ae6bd294fb003d58ec70a /utils/AppSearch.qml | |
| parent | bf44811dced54246317c51183b2c77b551fafb79 (diff) | |
activewindow
Diffstat (limited to 'utils/AppSearch.qml')
| -rw-r--r-- | utils/AppSearch.qml | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/utils/AppSearch.qml b/utils/AppSearch.qml new file mode 100644 index 0000000..6d1ad15 --- /dev/null +++ b/utils/AppSearch.qml @@ -0,0 +1,118 @@ +pragma Singleton + +import "fuzzysort.js" as Fuzzy +import "levendist.js" as Levendist +import Quickshell +import Quickshell.Io +import qs.config + +/** + * - Eases fuzzy searching for applications by name + * - Guesses icon name for window class name + */ +Singleton { + id: root + property bool sloppySearch: General.sloppySearch ?? false + property real scoreThreshold: 0.2 + 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", + "brave-browser": "brave-desktop" + }) + 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" + } + ] + + readonly property list<DesktopEntry> 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 + })) + + function fuzzyQuery(search: string): var { // Idk why list<DesktopEntry> doesn't work + if (root.sloppySearch) { + const results = list.map(obj => ({ + entry: obj, + score: Levendist.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 iconExists(iconName) { + if (!iconName || iconName.length == 0) + return false; + return (Quickshell.iconPath(iconName, true).length > 0) && !iconName.includes("image-missing"); + } + + 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 fuzzy 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; + } +} |
