feat: pure tag-ranked art library search
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
export function searchArt(manifest, query) {
|
||||
const terms = String(query ?? '').toLowerCase().split(/\s+/).filter(Boolean);
|
||||
if (terms.length === 0) return [...manifest];
|
||||
|
||||
const scored = manifest.map((asset, index) => {
|
||||
const hay = [asset.label, ...(asset.tags || [])].join(' ').toLowerCase();
|
||||
const score = terms.reduce((s, term) => s + (hay.includes(term) ? 1 : 0), 0);
|
||||
return { asset, index, score };
|
||||
});
|
||||
|
||||
return scored
|
||||
.filter((r) => r.score > 0)
|
||||
.sort((a, b) => b.score - a.score || a.index - b.index)
|
||||
.map((r) => r.asset);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { searchArt } from '../../src/art/search.js';
|
||||
|
||||
const M = [
|
||||
{ id: 'a', src: 'a.png', label: 'Detective in alley', tags: ['detective', 'alley', 'rain'] },
|
||||
{ id: 'b', src: 'b.png', label: 'Neon street', tags: ['street', 'neon', 'rain'] },
|
||||
{ id: 'c', src: 'c.png', label: 'Worn portrait', tags: ['portrait', 'face'] },
|
||||
];
|
||||
|
||||
describe('searchArt', () => {
|
||||
it('returns the whole manifest for a blank query, in order', () => {
|
||||
expect(searchArt(M, '').map((x) => x.id)).toEqual(['a', 'b', 'c']);
|
||||
expect(searchArt(M, ' ').map((x) => x.id)).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('ranks by number of matched terms', () => {
|
||||
// "rain alley" -> a matches both (2), b matches rain (1), c matches none
|
||||
expect(searchArt(M, 'rain alley').map((x) => x.id)).toEqual(['a', 'b']);
|
||||
});
|
||||
|
||||
it('matches substrings in tags or label, case-insensitive', () => {
|
||||
expect(searchArt(M, 'DETECT').map((x) => x.id)).toEqual(['a']);
|
||||
expect(searchArt(M, 'portrait').map((x) => x.id)).toEqual(['c']);
|
||||
});
|
||||
|
||||
it('breaks rank ties by original manifest order', () => {
|
||||
// "rain" -> a and b both match once; a comes first in manifest
|
||||
expect(searchArt(M, 'rain').map((x) => x.id)).toEqual(['a', 'b']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user