Files
cc-switch/src/hooks/useSessionSearch.ts
T
Jason 308314baac fix(session-manager): improve session search accuracy and Chinese support
- Pre-filter sessions by provider before indexing to prevent result
  truncation when FlexSearch limit cuts across providers
- Switch tokenizer from "forward" to "full" for Chinese substring matching
- Preserve FlexSearch relevance ranking when search query is present
2026-04-10 00:14:50 +08:00

73 lines
1.8 KiB
TypeScript

import { useCallback, useMemo } from "react";
import FlexSearch from "flexsearch";
import type { SessionMeta } from "@/types";
interface UseSessionSearchOptions {
sessions: SessionMeta[];
providerFilter: string;
}
interface UseSessionSearchResult {
search: (query: string) => SessionMeta[];
}
/**
* 使用 FlexSearch 实现会话全文搜索
* 索引会话元数据(标题、摘要、项目目录等)
*/
export function useSessionSearch({
sessions,
providerFilter,
}: UseSessionSearchOptions): UseSessionSearchResult {
const filteredByProvider = useMemo(() => {
if (providerFilter === "all") return sessions;
return sessions.filter((s) => s.providerId === providerFilter);
}, [sessions, providerFilter]);
const index = useMemo(() => {
const nextIndex = new FlexSearch.Index({
tokenize: "full",
resolution: 9,
});
filteredByProvider.forEach((session, idx) => {
const metaContent = [
session.sessionId,
session.title,
session.summary,
session.projectDir,
session.sourcePath,
]
.filter(Boolean)
.join(" ");
nextIndex.add(idx, metaContent);
});
return nextIndex;
}, [filteredByProvider]);
const search = useCallback(
(query: string): SessionMeta[] => {
const needle = query.trim();
if (!needle) {
return [...filteredByProvider].sort((a, b) => {
const aTs = a.lastActiveAt ?? a.createdAt ?? 0;
const bTs = b.lastActiveAt ?? b.createdAt ?? 0;
return bTs - aTs;
});
}
const results = index.search(needle, {
limit: filteredByProvider.length,
}) as number[];
return results.map((idx) => filteredByProvider[idx]);
},
[index, filteredByProvider],
);
return { search };
}