Initial commit for Start9 packaging
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Shared exercise search utilities — fuzzy matching + abbreviation expansion.
|
||||
* Used by both ExercisePicker (log workout) and ExercisesClient (exercise library).
|
||||
*/
|
||||
|
||||
// Common gym abbreviations
|
||||
const ABBREVIATIONS: Record<string, string> = {
|
||||
kb: "kettlebell",
|
||||
db: "dumbbell",
|
||||
bb: "barbell",
|
||||
ghd: "glute ham developer",
|
||||
rdl: "romanian deadlift",
|
||||
ohp: "overhead press",
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand a search query into multiple variants using known abbreviations.
|
||||
* e.g. "kb swing" → ["kb swing", "kettlebell swing"]
|
||||
*/
|
||||
export function expandAbbreviations(query: string): string[] {
|
||||
const lower = query.toLowerCase().trim();
|
||||
const variants: string[] = [lower];
|
||||
|
||||
// Check if the whole query is an abbreviation
|
||||
if (ABBREVIATIONS[lower]) {
|
||||
variants.push(ABBREVIATIONS[lower]);
|
||||
}
|
||||
|
||||
// Check if the query starts with an abbreviation followed by a space
|
||||
for (const [abbr, full] of Object.entries(ABBREVIATIONS)) {
|
||||
if (lower.startsWith(abbr + " ")) {
|
||||
variants.push(full + lower.slice(abbr.length));
|
||||
}
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Score how well a query matches a target string.
|
||||
* Lower = better match. Returns -1 for no match.
|
||||
*
|
||||
* Priority: exact match (0) > starts with (1) > word starts with (2) > substring (3) > fuzzy chars (4+)
|
||||
*/
|
||||
export function fuzzyScore(query: string, target: string): number {
|
||||
const q = query.toLowerCase();
|
||||
const t = target.toLowerCase();
|
||||
|
||||
if (t === q) return 0;
|
||||
if (t.startsWith(q)) return 1;
|
||||
|
||||
const words = t.split(/\s+/);
|
||||
if (words.some((w) => w.startsWith(q))) return 2;
|
||||
if (t.includes(q)) return 3;
|
||||
|
||||
// Fuzzy character match
|
||||
let qi = 0;
|
||||
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
||||
if (t[ti] === q[qi]) qi++;
|
||||
}
|
||||
|
||||
return qi === q.length ? 4 + (t.length - q.length) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search exercises using fuzzy matching + abbreviation expansion.
|
||||
* Returns scored exercises sorted by best match, or -1 for no match.
|
||||
*/
|
||||
export function scoreExercise(query: string, exerciseName: string): number {
|
||||
const variants = expandAbbreviations(query);
|
||||
const scores = variants
|
||||
.map((q) => fuzzyScore(q, exerciseName))
|
||||
.filter((s) => s >= 0);
|
||||
|
||||
return scores.length > 0 ? Math.min(...scores) : -1;
|
||||
}
|
||||
Reference in New Issue
Block a user