1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 05:55:37 -05:00

Better app launchers using rescan, and fuzzysort. GPL license.

FuzzySort was ripped from caelestia, which is the reason for GPL change.
This commit is contained in:
bbedward
2025-07-14 11:25:18 -04:00
parent 5c5653a41a
commit 603ac51df0
5 changed files with 1598 additions and 154 deletions

View File

@@ -2,6 +2,7 @@ import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import "../Common/fuzzysort.js" as Fuzzy
pragma Singleton
pragma ComponentBehavior: Bound
@@ -12,61 +13,66 @@ Singleton {
property var applicationsByName: ({})
property var applicationsByExec: ({})
property bool ready: false
property int refreshInterval: 10000
Timer {
id: refreshTimer
interval: root.refreshInterval
repeat: true
running: true
onTriggered: root.refreshApplications()
}
// Pre-prepared fuzzy search data
property var preppedApps: []
Component.onCompleted: {
loadApplications()
}
function refreshApplications() {
loadApplications()
Connections {
target: DesktopEntries
function onApplicationsChanged() {
console.log("AppSearchService: DesktopEntries applicationsChanged signal received")
console.log("AppSearchService: Current applications count before reload:", applications.length)
loadApplications()
}
}
function loadApplications() {
var allApps = Array.from(DesktopEntries.applications.values)
// Debug: Check what properties are available
if (allApps.length > 0) {
var firstApp = allApps[0]
if (firstApp.exec !== undefined) console.log(" exec:", firstApp.exec)
if (firstApp.execString !== undefined) console.log(" execString:", firstApp.execString)
if (firstApp.executable !== undefined) console.log(" executable:", firstApp.executable)
if (firstApp.command !== undefined) console.log(" command:", firstApp.command)
}
applications = allApps
.filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name))
// Build lookup maps
var byName = {}
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
byName[app.name.toLowerCase()] = app
// Trigger rescan on next frame to avoid blocking
Qt.callLater(function() {
var allApps = Array.from(DesktopEntries.applications.values)
// Clean exec string for lookup
var execProp = app.execString || ""
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) {
byExec[cleanExec] = app
applications = allApps
.filter(app => !app.noDisplay)
.sort((a, b) => a.name.localeCompare(b.name))
// Build lookup maps
var byName = {}
var byExec = {}
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
byName[app.name.toLowerCase()] = app
// Clean exec string for lookup
var execProp = app.execString || ""
var cleanExec = execProp ? execProp.replace(/%[fFuU]/g, "").trim() : ""
if (cleanExec) {
byExec[cleanExec] = app
}
}
}
applicationsByName = byName
applicationsByExec = byExec
ready = true
console.log("AppSearchService: Loaded", applications.length, "applications")
applicationsByName = byName
applicationsByExec = byExec
// Prepare fuzzy search data
preppedApps = applications.map(app => ({
name: Fuzzy.prepare(app.name || ""),
comment: Fuzzy.prepare(app.comment || ""),
entry: app
}))
ready = true
console.log("AppSearchService: Loaded", applications.length, "applications")
console.log("AppSearchService: Prepared", preppedApps.length, "apps for fuzzy search")
console.log("AppSearchService: Ready status:", ready)
})
}
function searchApplications(query) {
@@ -74,62 +80,25 @@ Singleton {
return applications
}
var lowerQuery = query.toLowerCase()
var results = []
for (var i = 0; i < applications.length; i++) {
var app = applications[i]
var score = 0
// Check name
var nameLower = app.name.toLowerCase()
if (nameLower === lowerQuery) {
score = 1000
} else if (nameLower.startsWith(lowerQuery)) {
score = 500
} else if (nameLower.includes(lowerQuery)) {
score = 100
}
// Check comment/description
if (app.comment) {
var commentLower = app.comment.toLowerCase()
if (commentLower.includes(lowerQuery)) {
score += 50
}
}
// Check generic name
if (app.genericName) {
var genericLower = app.genericName.toLowerCase()
if (genericLower.includes(lowerQuery)) {
score += 25
}
}
// Check keywords
if (app.keywords && app.keywords.length > 0) {
for (var j = 0; j < app.keywords.length; j++) {
if (app.keywords[j].toLowerCase().includes(lowerQuery)) {
score += 10
break
}
}
}
if (score > 0) {
results.push({
app: app,
score: score
})
}
if (!ready || preppedApps.length === 0) {
return []
}
// Sort by score descending
results.sort((a, b) => b.score - a.score)
// Use fuzzy search with both name and comment fields
var results = Fuzzy.go(query, preppedApps, {
all: false,
keys: ["name", "comment"],
scoreFn: r => {
// Prioritize name matches over comment matches
var nameScore = r[0] ? r[0].score : 0
var commentScore = r[1] ? r[1].score : 0
return nameScore > 0 ? nameScore * 0.9 + commentScore * 0.1 : commentScore * 0.5
},
limit: 50
})
// Return just the apps
return results.map(r => r.app)
// Extract the desktop entries from results
return results.map(r => r.obj.entry)
}
function getAppByName(name) {