Skip to content
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"date-fns": "^3.6.0",
"electron-updater": "^6.7.3",
"fastify": "^5.7.4",
"fuse.js": "^7.4.2",
"idb-keyval": "^6.2.2",
"lucide-react": "^0.562.0",
"mdast-util-to-hast": "^13.2.1",
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions src/main/http/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const logger = createLogger('HTTP:search');
export function registerSearchRoutes(app: FastifyInstance, services: HttpServices): void {
app.get<{
Params: { projectId: string };
Querystring: { q?: string; maxResults?: string };
Querystring: { q?: string; maxResults?: string; fuzzy?: string };
}>('/api/projects/:projectId/search', async (request) => {
const query = request.query.q ?? '';

Expand All @@ -45,7 +45,8 @@ export function registerSearchRoutes(app: FastifyInstance, services: HttpService
const result = await services.projectScanner.searchSessions(
validatedProject.value!,
validatedQuery.value!,
maxResults
maxResults,
request.query.fuzzy === '1' || request.query.fuzzy === 'true'
);
return result;
} catch (error) {
Expand All @@ -55,7 +56,7 @@ export function registerSearchRoutes(app: FastifyInstance, services: HttpService
});

app.get<{
Querystring: { q?: string; maxResults?: string };
Querystring: { q?: string; maxResults?: string; fuzzy?: string };
}>('/api/search', async (request) => {
const query = request.query.q ?? '';

Expand All @@ -73,7 +74,8 @@ export function registerSearchRoutes(app: FastifyInstance, services: HttpService

const result = await services.projectScanner.searchAllProjects(
validatedQuery.value!,
maxResults
maxResults,
request.query.fuzzy === '1' || request.query.fuzzy === 'true'
);
return result;
} catch (error) {
Expand Down
15 changes: 11 additions & 4 deletions src/main/ipc/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ async function handleSearchSessions(
_event: IpcMainInvokeEvent,
projectId: string,
query: string,
maxResults?: number
maxResults?: number,
fuzzy?: boolean
): Promise<SearchSessionsResult> {
try {
const validatedProject = validateProjectId(projectId);
Expand All @@ -89,7 +90,8 @@ async function handleSearchSessions(
const result = await projectScanner.searchSessions(
validatedProject.value!,
validatedQuery.value!,
safeMaxResults
safeMaxResults,
fuzzy === true
);
return result;
} catch (error) {
Expand All @@ -105,7 +107,8 @@ async function handleSearchSessions(
async function handleSearchAllProjects(
_event: IpcMainInvokeEvent,
query: string,
maxResults?: number
maxResults?: number,
fuzzy?: boolean
): Promise<SearchSessionsResult> {
try {
const validatedQuery = validateSearchQuery(query);
Expand All @@ -116,7 +119,11 @@ async function handleSearchAllProjects(

const { projectScanner } = registry.getActive();
const safeMaxResults = coerceSearchMaxResults(maxResults, 50);
const result = await projectScanner.searchAllProjects(validatedQuery.value!, safeMaxResults);
const result = await projectScanner.searchAllProjects(
validatedQuery.value!,
safeMaxResults,
fuzzy === true
);
return result;
} catch (error) {
logger.error('Error in search-all-projects:', error);
Expand Down
26 changes: 17 additions & 9 deletions src/main/services/discovery/ProjectScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,9 +1096,10 @@ export class ProjectScanner {
async searchSessions(
projectId: string,
query: string,
maxResults: number = 50
maxResults: number = 50,
fuzzy: boolean = false
): Promise<SearchSessionsResult> {
return this.sessionSearcher.searchSessions(projectId, query, maxResults);
return this.sessionSearcher.searchSessions(projectId, query, maxResults, fuzzy);
}

/**
Expand All @@ -1108,7 +1109,11 @@ export class ProjectScanner {
* @param query - Search query string
* @param maxResults - Maximum number of results to return (default 50)
*/
async searchAllProjects(query: string, maxResults: number = 50): Promise<SearchSessionsResult> {
async searchAllProjects(
query: string,
maxResults: number = 50,
fuzzy: boolean = false
): Promise<SearchSessionsResult> {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const startedAt = Date.now();
try {
if (!query || query.trim().length === 0) {
Expand Down Expand Up @@ -1138,7 +1143,9 @@ export class ProjectScanner {
for (let i = 0; i < projects.length; i += searchBatchSize) {
const batch = projects.slice(i, i + searchBatchSize);
const batchResults = await Promise.allSettled(
batch.map((project) => this.sessionSearcher.searchSessions(project.id, query, maxResults))
batch.map((project) =>
this.sessionSearcher.searchSessions(project.id, query, maxResults, fuzzy)
)
);

for (const result of batchResults) {
Expand All @@ -1147,9 +1154,7 @@ export class ProjectScanner {
}
}

// Check if we have enough results already
const totalMatches = allResults.reduce((sum, r) => sum + r.totalMatches, 0);
if (totalMatches >= maxResults) {
if (!fuzzy && allResults.reduce((sum, r) => sum + r.totalMatches, 0) >= maxResults) {
break;
}
}
Expand All @@ -1158,8 +1163,11 @@ export class ProjectScanner {
const mergedResults = allResults.flatMap((r) => r.results);
const totalSessionsSearched = allResults.reduce((sum, r) => sum + r.sessionsSearched, 0);

// Sort by timestamp (most recent first) and limit to maxResults
mergedResults.sort((a, b) => b.timestamp - a.timestamp);
mergedResults.sort((a, b) =>
fuzzy
? (a.matchScore ?? 1) - (b.matchScore ?? 1) || b.timestamp - a.timestamp
: b.timestamp - a.timestamp
);
const limitedResults = mergedResults.slice(0, maxResults);

logger.debug(
Expand Down
Loading