fix(command-palette): match file results against the full relative path

Cmd+P scored server file hits by basename only, so a query like
"solo-is-a" returned nothing for solo-is-a-team-size/index.md.
Score by relativePath and widen the server candidate limit to 40
so client-side reranking has enough to work with.
This commit is contained in:
Bohdan Triapitsyn
2026-08-24 14:19:25 +03:00
parent 97edd033f9
commit 279fa8b691
3 changed files with 28 additions and 3 deletions
@@ -352,7 +352,7 @@ export const CommandPalette: React.FC = () => {
return;
}
let cancelled = false;
void searchFiles(currentRoot, trimmedQuery, 10, { type: 'file' })
void searchFiles(currentRoot, trimmedQuery, 40, { type: 'file' })
.then((results) => {
if (cancelled) return;
setFileResults(
@@ -16,4 +16,27 @@ describe('commandPaletteFilesState', () => {
expect(scoreCommandPaletteFiles(fileResults, 'alpha', freshKey, staleKey)).toEqual([]);
expect(scoreCommandPaletteFiles(fileResults, 'alpha', freshKey, freshKey)).toHaveLength(1);
});
test('matches directory segments of the relative path, not just the basename', () => {
const fileResults = [
{ name: 'index.md', path: '/kb/solo-is-a-team-size/index.md', relativePath: 'solo-is-a-team-size/index.md' },
{ name: 'index.md', path: '/kb/software-developer/index.md', relativePath: 'software-developer/index.md' },
];
const key = buildCommandPaletteFileSearchKey('/kb', 'solo-is-a');
const scored = scoreCommandPaletteFiles(fileResults, 'solo-is-a', key, key);
expect(scored).toHaveLength(1);
expect(scored[0].item.relativePath).toBe('solo-is-a-team-size/index.md');
});
test('ranks prefix path matches above later substring matches', () => {
const fileResults = [
{ name: 'index.md', path: '/kb/notes/solo/index.md', relativePath: 'notes/solo/index.md' },
{ name: 'index.md', path: '/kb/solo-is-a-team-size/index.md', relativePath: 'solo-is-a-team-size/index.md' },
];
const key = buildCommandPaletteFileSearchKey('/kb', 'solo');
const scored = scoreCommandPaletteFiles(fileResults, 'solo', key, key);
expect(scored[0].item.relativePath).toBe('solo-is-a-team-size/index.md');
});
});
@@ -11,7 +11,7 @@ export const buildCommandPaletteFileSearchKey = (
return JSON.stringify([currentRoot, trimmedQuery]);
};
export const scoreCommandPaletteFiles = <T extends { name: string }>(
export const scoreCommandPaletteFiles = <T extends { name: string; relativePath: string }>(
fileResults: T[],
trimmedQuery: string,
fileSearchKey: string,
@@ -21,7 +21,9 @@ export const scoreCommandPaletteFiles = <T extends { name: string }>(
return [];
}
return scoreByFuzzyQuery(fileResults, trimmedQuery, (file) => file.name, {
// Score against the full relative path: queries like "solo-is-a" must match
// solo-is-a-team-size/index.md even though the basename is just index.md.
return scoreByFuzzyQuery(fileResults, trimmedQuery, (file) => file.relativePath || file.name, {
limit: 10,
threshold: 0.4,
});