diff --git a/changelog.md b/changelog.md index eb7838c..cf2e2be 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-10-29 - 1.5.1 - fix(scriptindex) +Improve script search: use ObjectSorter with weighted results prioritizing slug and name + +- Replaced smartfuzzy.FuzzyMatcher with smartfuzzy.ObjectSorter for multi-field fuzzy searching +- Search now runs across slug, name, and description fields +- Added weighting so slug matches are prioritized, name matches receive a medium boost +- Search returns ordered script objects based on adjusted score + ## 2025-10-29 - 1.5.0 - feat(scripts) Add fuzzy search and type filtering for community scripts; improve scripts CLI output and cache handling diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 32508d6..88f4d2e 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/moxytool', - version: '1.5.0', + version: '1.5.1', description: 'Proxmox administration tool for vGPU setup, VM management, and cluster configuration' } diff --git a/ts/moxytool.classes.scriptindex.ts b/ts/moxytool.classes.scriptindex.ts index 8f6c822..d5f8d12 100644 --- a/ts/moxytool.classes.scriptindex.ts +++ b/ts/moxytool.classes.scriptindex.ts @@ -272,29 +272,35 @@ export class ScriptIndex { scripts = scripts.filter((script) => script.type === typeFilter); } - // Use smartfuzzy for ranking - const fuzzyMatcher = new plugins.smartfuzzy.FuzzyMatcher(); + // Use ObjectSorter for fuzzy searching across multiple fields + const sorter = new plugins.smartfuzzy.ObjectSorter(scripts); - const scoredResults = scripts.map((script) => { - // Calculate match scores for each field - const nameScore = script.name ? fuzzyMatcher.match(query, script.name) : 0; - const slugScore = script.slug ? fuzzyMatcher.match(query, script.slug) : 0; - const descScore = script.description ? fuzzyMatcher.match(query, script.description) : 0; + // Search across slug, name, and description + const results = sorter.sort(query, ['slug', 'name', 'description']); - // Prioritize: slug > name > description - const totalScore = slugScore * 3 + nameScore * 2 + descScore; + // Post-process to weight results by which field matched + const weightedResults = results.map((result) => { + let weight = 1; + + // Boost score if match was in slug (highest priority) + if (result.matches?.some((m) => m.key === 'slug')) { + weight = 3; + } + // Boost if match was in name (medium priority) + else if (result.matches?.some((m) => m.key === 'name')) { + weight = 2; + } return { - script, - score: totalScore, + ...result, + adjustedScore: (result.score || 0) / weight, }; }); - // Filter out non-matches and sort by score - return scoredResults - .filter((result) => result.score > 0) - .sort((a, b) => b.score - a.score) - .map((result) => result.script); + // Sort by adjusted score and return just the script objects + return weightedResults + .sort((a, b) => (a.adjustedScore || 0) - (b.adjustedScore || 0)) + .map((result) => result.item); } /**