1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-25 22:12:50 -05:00
Files
DankMaterialShell/quickshell/Modals/DankLauncherV2/Scorer.js

246 lines
6.7 KiB
JavaScript

.pragma library
const Weights = {
exactMatch: 10000,
prefixMatch: 5000,
wordBoundary: 1000,
substring: 500,
fuzzy: 100,
frecency: 2000,
typeBonus: {
app: 1000,
plugin: 900,
file: 800,
action: 600
}
}
function tokenize(text) {
return text.toLowerCase().trim().split(/[\s\-_]+/).filter(function (w) { return w.length > 0 })
}
function hasWordBoundaryMatch(text, query) {
var textWords = tokenize(text)
var queryWords = tokenize(query)
if (queryWords.length === 0) return false
if (queryWords.length > textWords.length) return false
for (var i = 0; i <= textWords.length - queryWords.length; i++) {
var allMatch = true
for (var j = 0; j < queryWords.length; j++) {
if (!textWords[i + j].startsWith(queryWords[j])) {
allMatch = false
break
}
}
if (allMatch) return true
}
return false
}
function levenshteinDistance(s1, s2) {
var len1 = s1.length
var len2 = s2.length
var matrix = []
for (var i = 0; i <= len1; i++) {
matrix[i] = [i]
}
for (var j = 0; j <= len2; j++) {
matrix[0][j] = j
}
for (var i = 1; i <= len1; i++) {
for (var j = 1; j <= len2; j++) {
var cost = s1[i - 1] === s2[j - 1] ? 0 : 1
matrix[i][j] = Math.min(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + cost
)
}
}
return matrix[len1][len2]
}
function fuzzyScore(text, query) {
var maxDistance = query.length === 3 ? 1 : query.length <= 6 ? 2 : 3
var bestScore = 0
if (Math.abs(text.length - query.length) <= maxDistance) {
var distance = levenshteinDistance(text, query)
if (distance <= maxDistance) {
var maxLen = Math.max(text.length, query.length)
bestScore = 1 - (distance / maxLen)
}
}
var words = tokenize(text)
for (var i = 0; i < words.length && bestScore < 0.8; i++) {
if (Math.abs(words[i].length - query.length) > maxDistance) continue
var wordDistance = levenshteinDistance(words[i], query)
if (wordDistance <= maxDistance) {
var wordMaxLen = Math.max(words[i].length, query.length)
var score = 1 - (wordDistance / wordMaxLen)
bestScore = Math.max(bestScore, score)
}
}
return bestScore
}
function getTimeBucketWeight(daysSinceUsed) {
for (var i = 0; i < TimeBuckets.length; i++) {
if (daysSinceUsed <= TimeBuckets[i].maxDays) {
return TimeBuckets[i].weight
}
}
return 10
}
function calculateTextScore(name, query) {
if (name === query) return Weights.exactMatch
if (name.startsWith(query)) return Weights.prefixMatch
if (name.includes(query)) return Weights.substring
if (hasWordBoundaryMatch(name, query)) return Weights.wordBoundary
if (query.length >= 3) {
var fs = fuzzyScore(name, query)
if (fs > 0) return fs * Weights.fuzzy
}
return 0
}
function score(item, query, frecencyData) {
var typeBonus = Weights.typeBonus[item.type] || 0
if (!query || query.length === 0) {
var usageCount = frecencyData ? frecencyData.usageCount : 0
return typeBonus + (usageCount * 100)
}
var name = (item.name || "").toLowerCase()
var q = query.toLowerCase()
var textScore = calculateTextScore(name, q)
if (textScore === 0 && item.subtitle) {
var subtitleScore = calculateTextScore(item.subtitle.toLowerCase(), q)
textScore = subtitleScore * 0.5
}
if (textScore === 0 && item.keywords) {
for (var i = 0; i < item.keywords.length; i++) {
var keywordScore = calculateTextScore(item.keywords[i].toLowerCase(), q)
if (keywordScore > 0) {
textScore = keywordScore * 0.3
break
}
}
}
if (textScore === 0) return 0
var usageBonus = frecencyData ? Math.min(frecencyData.usageCount * 10, Weights.frecency) : 0
return textScore + usageBonus + typeBonus
}
function scoreItems(items, query, getFrecencyFn) {
var scored = []
for (var i = 0; i < items.length; i++) {
var item = items[i]
var frecencyData = getFrecencyFn ? getFrecencyFn(item) : null
var itemScore = score(item, query, frecencyData)
if (itemScore > 0 || !query || query.length === 0) {
scored.push({
item: item,
score: itemScore
})
}
}
scored.sort(function (a, b) {
return b.score - a.score
})
return scored
}
function groupBySection(scoredItems, sectionOrder, sortAlphabetically, maxPerSection) {
var sections = {}
var result = []
var limit = maxPerSection || 50
for (var i = 0; i < sectionOrder.length; i++) {
var sectionId = sectionOrder[i].id
sections[sectionId] = {
id: sectionId,
title: sectionOrder[i].title,
icon: sectionOrder[i].icon,
priority: sectionOrder[i].priority,
items: [],
collapsed: false
}
}
for (var i = 0; i < scoredItems.length; i++) {
var scoredItem = scoredItems[i]
var item = scoredItem.item
var sectionId = item.section || "apps"
if (sections[sectionId] && sections[sectionId].items.length < limit) {
sections[sectionId].items.push(item)
} else if (sections["apps"] && sections["apps"].items.length < limit) {
sections["apps"].items.push(item)
}
}
for (var i = 0; i < sectionOrder.length; i++) {
var section = sections[sectionOrder[i].id]
if (section && section.items.length > 0) {
if (sortAlphabetically && section.id === "apps") {
section.items.sort(function (a, b) {
return (a.name || "").localeCompare(b.name || "")
})
}
result.push(section)
}
}
return result
}
function flattenSections(sections) {
var flat = []
for (var i = 0; i < sections.length; i++) {
var section = sections[i]
flat.push({
isHeader: true,
section: section,
sectionId: section.id,
sectionIndex: i
})
if (!section.collapsed) {
for (var j = 0; j < section.items.length; j++) {
flat.push({
isHeader: false,
item: section.items[j],
sectionId: section.id,
sectionIndex: i,
indexInSection: j
})
}
}
}
return flat
}