Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/omnibar-tier-dominant-ranking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inkeep/open-knowledge": patch
---

Rank the Cmd+K omnibar name-first. An exact filename match now leads the results even when many files share that basename and have stronger body-text scores — the file you typed is no longer buried below same-named siblings or pushed past the fetch limit. The omnibar still searches content, but a strong content match only reorders within a name-match tier rather than outranking the name itself; the deliberate "by meaning" search keeps content-relevance ranking. A query that matches many folders or name-only files (`evidence`, `index`) no longer fills the whole list with one kind — folders and files are bounded so content pages fill the rest.
35 changes: 35 additions & 0 deletions packages/app/src/components/CommandPalette.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,38 @@ describe('CommandPalette DOM behavior', () => {
expect(commandDialogProps.at(-1)?.placement).toBeUndefined();
});
});

describe('NavigationItem path subtitle', () => {
beforeEach(() => {
cleanup();
});

test('a file result row renders its path so same-named siblings are distinguishable', async () => {
const { NavigationItem } = await import('./CommandPalette');
const fileA = {
kind: 'file' as const,
path: 'reports/q3/data.csv',
name: 'data.csv',
title: 'data.csv',
score: 1,
};
const fileB = {
kind: 'file' as const,
path: 'exports/legacy/data.csv',
name: 'data.csv',
title: 'data.csv',
score: 1,
};
render(
<>
<NavigationItem entry={fileA as never} query="data.csv" onSelect={() => {}} />
<NavigationItem entry={fileB as never} query="data.csv" onSelect={() => {}} />
</>,
);

const rowA = screen.getByTestId('command-palette-nav-file-reports/q3/data.csv');
const rowB = screen.getByTestId('command-palette-nav-file-exports/legacy/data.csv');
expect(rowA.textContent).toContain('reports/q3/data.csv');
expect(rowB.textContent).toContain('exports/legacy/data.csv');
});
});
2 changes: 1 addition & 1 deletion packages/app/src/components/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function resolveCreateInitialDir(
return defaultInitialDir(activeDocName);
}

function NavigationItem({
export function NavigationItem({
entry,
query = '',
onSelect,
Expand Down
6 changes: 4 additions & 2 deletions packages/app/src/components/command-palette-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ describe('fetchWorkspaceSearchEntries', () => {
expect(requestBody).toEqual({
query: 'homepage',
intent: 'full_text',
ranking: 'navigation',
scopes: ['page', 'folder', 'content', 'file'],
limit: 30,
limit: 50,
source: 'omnibar',
});
expect(entries).toEqual([
Expand Down Expand Up @@ -297,8 +298,9 @@ describe('fetchWorkspaceSearchEntries', () => {
expect(requestBody).toEqual({
query: 'auth retries',
intent: 'full_text',
ranking: 'relevance',
scopes: ['page', 'folder', 'content', 'file'],
limit: 30,
limit: 50,
source: 'omnibar',
semantic: true,
});
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/components/command-palette-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface WorkspaceEntrySearchCorpus {

export const EMPTY_QUERY_NAV_LIMIT = 20;
const MATCH_QUERY_NAV_LIMIT = 50;
const API_SEARCH_LIMIT = 30;
const API_SEARCH_LIMIT = 50;
export const SEMANTIC_RESULT_LIMIT = 30;

let cachedEntriesFingerprint = '';
Expand Down Expand Up @@ -249,6 +249,7 @@ export async function fetchWorkspaceSearchEntries(
body: JSON.stringify({
query: normalizedQuery,
intent: 'full_text',
ranking: options.semantic ? 'relevance' : 'navigation',
scopes: ['page', 'folder', 'content', 'file'],
limit: options.limit ?? API_SEARCH_LIMIT,
source: 'omnibar',
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ export {
type WorkspaceSearchIntent,
type WorkspaceSearchKind,
type WorkspaceSearchOptions,
type WorkspaceSearchRanking,
type WorkspaceSearchResult,
type WorkspaceSearchScope,
type WorkspaceSemanticInput,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/schemas/api/tags-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export const SearchRequestSchema = z
.object({
query: z.string().optional(),
intent: z.enum(['autocomplete', 'full_text', 'omnibar']).optional(),
ranking: z.enum(['navigation', 'relevance']).optional(),
scopes: z.array(z.enum(['page', 'folder', 'content', 'file'])).optional(),
scope: z.string().optional(),
limit: z.number().int().nonnegative().optional(),
Expand Down
36 changes: 18 additions & 18 deletions packages/core/src/search/workspace-search.baseline.fixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@
"expected": [
{
"id": "page:auth/login",
"score": 1707.1890049060949,
"score": 700001.25,
"signals": {
"lexical": 700,
"fullText": 49.734450245304735,
Expand All @@ -215,7 +215,7 @@
},
{
"id": "page:misc/login-history",
"score": 1479.3204629742795,
"score": 600001.47759861,
"signals": {
"lexical": 600,
"fullText": 42.40352314871397,
Expand All @@ -232,7 +232,7 @@
"expected": [
{
"id": "page:auth/login",
"score": 1890.912389055217,
"score": 700001.25,
"signals": {
"lexical": 700,
"fullText": 58.92061945276085,
Expand All @@ -241,7 +241,7 @@
},
{
"id": "page:misc/login-history",
"score": 1626.389211640448,
"score": 600001.4694744988,
"signals": {
"lexical": 600,
"fullText": 49.756960582022394,
Expand Down Expand Up @@ -302,34 +302,34 @@
"expected": [
{
"id": "folder:auth",
"score": 1573.1702123532718,
"score": 700001.6623001031,
"signals": {
"lexical": 700,
"fullText": 41.783510617663595,
"recency": 37.5
}
},
{
"id": "page:guides/authentication-overview",
"score": 1566.0036368361461,
"signals": {
"lexical": 600,
"fullText": 45.80018184180731,
"recency": 50
}
},
{
"id": "page:auth",
"score": 1554.4202123532718,
"score": 700001.2873001031,
"signals": {
"lexical": 700,
"fullText": 41.783510617663595,
"recency": 18.75
}
},
{
"id": "page:guides/authentication-overview",
"score": 600002,
"signals": {
"lexical": 600,
"fullText": 45.80018184180731,
"recency": 50
}
},
{
"id": "page:auth/session-token-refresh",
"score": 713.485200031778,
"score": 550000.6511841159,
"signals": {
"lexical": 550,
"fullText": 6.9242600015889,
Expand All @@ -338,7 +338,7 @@
},
{
"id": "page:auth/login",
"score": 700.985200031778,
"score": 550000.4011841159,
"signals": {
"lexical": 550,
"fullText": 6.9242600015889,
Expand Down Expand Up @@ -495,7 +495,7 @@
"expected": [
{
"id": "folder:recipes",
"score": 1808.6874410044754,
"score": 700001,
"signals": {
"lexical": 700,
"fullText": 55.43437205022377,
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/search/workspace-search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ describe('searchWorkspaceDocuments', () => {
const results = searchWorkspaceDocuments(documents, 'arch', { intent: 'omnibar' });

expect(results.map((result) => result.document.path)).toEqual([
'architecture',
'architecture/overview',
'architecture',
]);
});

Expand Down Expand Up @@ -197,19 +197,19 @@ describe('canonical-kind ranking — markdown outranks a same-stem file (D5)', (
expect(ranked[0]?.document.path).toBe('foo');
});

test('a markdown page outranks a same-bracket file even when the file is newer (kind demotion dominates recency)', () => {
test('a markdown page outranks a same-stem file at equal recency (kind is the within-tier tiebreaker)', () => {
const page = createWorkspaceSearchDocument({
kind: 'page',
path: 'config',
title: 'config',
modifiedTs: 10,
});
const newerFile = createWorkspaceSearchDocument({
const file = createWorkspaceSearchDocument({
kind: 'file',
path: 'sub/config',
modifiedTs: 100,
modifiedTs: 10,
});
const ranked = searchWorkspaceDocuments([page, newerFile], 'config', { intent: 'omnibar' });
const ranked = searchWorkspaceDocuments([page, file], 'config', { intent: 'omnibar' });
const pageRank = ranked.findIndex((r) => r.document.path === 'config');
const fileRank = ranked.findIndex((r) => r.document.path === 'sub/config');
expect(pageRank).toBeGreaterThanOrEqual(0);
Expand Down
Loading