mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-02-17 13:52:14 +01:00
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details.
100 lines
3.4 KiB
TypeScript
100 lines
3.4 KiB
TypeScript
import { ToolRegistryEntry } from "../data/toolsTaxonomy";
|
|
import { scoreMatch, minScoreForQuery, normalizeForSearch } from "./fuzzySearch";
|
|
|
|
export interface RankedToolItem {
|
|
item: [string, ToolRegistryEntry];
|
|
matchedText?: string;
|
|
}
|
|
|
|
export function filterToolRegistryByQuery(
|
|
toolRegistry: Record<string, ToolRegistryEntry>,
|
|
query: string
|
|
): RankedToolItem[] {
|
|
const entries = Object.entries(toolRegistry);
|
|
if (!query.trim()) {
|
|
return entries.map(([id, tool]) => ({ item: [id, tool] as [string, ToolRegistryEntry] }));
|
|
}
|
|
|
|
const nq = normalizeForSearch(query);
|
|
const threshold = minScoreForQuery(query);
|
|
|
|
const exactName: Array<{ id: string; tool: ToolRegistryEntry; pos: number }> = [];
|
|
const exactSyn: Array<{ id: string; tool: ToolRegistryEntry; text: string; pos: number }> = [];
|
|
const fuzzyName: Array<{ id: string; tool: ToolRegistryEntry; score: number; text: string }> = [];
|
|
const fuzzySyn: Array<{ id: string; tool: ToolRegistryEntry; score: number; text: string }> = [];
|
|
|
|
for (const [id, tool] of entries) {
|
|
const nameNorm = normalizeForSearch(tool.name || '');
|
|
const pos = nameNorm.indexOf(nq);
|
|
if (pos !== -1) {
|
|
exactName.push({ id, tool, pos });
|
|
continue;
|
|
}
|
|
|
|
const syns = Array.isArray(tool.synonyms) ? tool.synonyms : [];
|
|
let matchedExactSyn: { text: string; pos: number } | null = null;
|
|
for (const s of syns) {
|
|
const sn = normalizeForSearch(s);
|
|
const sp = sn.indexOf(nq);
|
|
if (sp !== -1) {
|
|
matchedExactSyn = { text: s, pos: sp };
|
|
break;
|
|
}
|
|
}
|
|
if (matchedExactSyn) {
|
|
exactSyn.push({ id, tool, text: matchedExactSyn.text, pos: matchedExactSyn.pos });
|
|
continue;
|
|
}
|
|
|
|
// Fuzzy name
|
|
const nameScore = scoreMatch(query, tool.name || '');
|
|
if (nameScore >= threshold) {
|
|
fuzzyName.push({ id, tool, score: nameScore, text: tool.name || '' });
|
|
}
|
|
|
|
// Fuzzy synonyms (we'll consider these only if fuzzy name results are weak)
|
|
let bestSynScore = 0;
|
|
let bestSynText = '';
|
|
for (const s of syns) {
|
|
const synScore = scoreMatch(query, s);
|
|
if (synScore > bestSynScore) {
|
|
bestSynScore = synScore;
|
|
bestSynText = s;
|
|
}
|
|
if (bestSynScore >= 95) break;
|
|
}
|
|
if (bestSynScore >= threshold) {
|
|
fuzzySyn.push({ id, tool, score: bestSynScore, text: bestSynText });
|
|
}
|
|
}
|
|
|
|
// Sort within buckets
|
|
exactName.sort((a, b) => a.pos - b.pos || (a.tool.name || '').length - (b.tool.name || '').length);
|
|
exactSyn.sort((a, b) => a.pos - b.pos || a.text.length - b.text.length);
|
|
fuzzyName.sort((a, b) => b.score - a.score);
|
|
fuzzySyn.sort((a, b) => b.score - a.score);
|
|
|
|
// Concatenate buckets with de-duplication by tool id
|
|
const seen = new Set<string>();
|
|
const ordered: RankedToolItem[] = [];
|
|
|
|
const push = (id: string, tool: ToolRegistryEntry, matchedText?: string) => {
|
|
if (seen.has(id)) return;
|
|
seen.add(id);
|
|
ordered.push({ item: [id, tool], matchedText });
|
|
};
|
|
|
|
for (const { id, tool } of exactName) push(id, tool, tool.name);
|
|
for (const { id, tool, text } of exactSyn) push(id, tool, text);
|
|
for (const { id, tool, text } of fuzzyName) push(id, tool, text);
|
|
for (const { id, tool, text } of fuzzySyn) push(id, tool, text);
|
|
|
|
if (ordered.length > 0) return ordered;
|
|
|
|
// Fallback: return everything unchanged
|
|
return entries.map(([id, tool]) => ({ item: [id, tool] as [string, ToolRegistryEntry] }));
|
|
}
|
|
|
|
|
|
|