mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-04 11:48:43 +00:00
102 lines
2.5 KiB
TypeScript
102 lines
2.5 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
|
|
import { api } from '../../../utils/api';
|
|
import type { LLMProvider } from '../../../types/app';
|
|
|
|
export type SessionMessageMatch = {
|
|
sessionId: string;
|
|
label: string;
|
|
snippet: string;
|
|
provider: LLMProvider;
|
|
};
|
|
|
|
type ProjectResult = {
|
|
projectId: string | null;
|
|
projectName: string;
|
|
sessions: Array<{
|
|
sessionId: string;
|
|
provider: LLMProvider;
|
|
sessionSummary: string;
|
|
matches: Array<{ snippet: string }>;
|
|
}>;
|
|
};
|
|
|
|
const MIN_QUERY = 2;
|
|
const DEBOUNCE_MS = 250;
|
|
|
|
export function useSessionMessageSearch(
|
|
projectId: string | undefined,
|
|
query: string,
|
|
enabled: boolean,
|
|
) {
|
|
const [items, setItems] = useState<SessionMessageMatch[]>([]);
|
|
const seqRef = useRef(0);
|
|
const esRef = useRef<EventSource | null>(null);
|
|
|
|
useEffect(() => {
|
|
const trimmed = query.trim();
|
|
if (!enabled || !projectId || trimmed.length < MIN_QUERY) {
|
|
setItems([]);
|
|
esRef.current?.close();
|
|
esRef.current = null;
|
|
return;
|
|
}
|
|
|
|
esRef.current?.close();
|
|
esRef.current = null;
|
|
seqRef.current++;
|
|
|
|
const handle = setTimeout(() => {
|
|
const seq = ++seqRef.current;
|
|
const url = api.searchConversationsUrl(trimmed);
|
|
const es = new EventSource(url);
|
|
esRef.current = es;
|
|
const accumulated: SessionMessageMatch[] = [];
|
|
|
|
es.addEventListener('result', (evt) => {
|
|
if (seq !== seqRef.current) {
|
|
es.close();
|
|
return;
|
|
}
|
|
try {
|
|
const data = JSON.parse((evt as MessageEvent).data) as { projectResult: ProjectResult };
|
|
const pr = data.projectResult;
|
|
if (pr.projectId !== projectId) return;
|
|
for (const s of pr.sessions) {
|
|
accumulated.push({
|
|
sessionId: s.sessionId,
|
|
label: s.sessionSummary || s.sessionId,
|
|
snippet: s.matches[0]?.snippet ?? '',
|
|
provider: s.provider,
|
|
});
|
|
}
|
|
setItems([...accumulated]);
|
|
} catch {
|
|
// ignore malformed
|
|
}
|
|
});
|
|
|
|
const finish = () => {
|
|
if (seq !== seqRef.current) return;
|
|
es.close();
|
|
esRef.current = null;
|
|
};
|
|
es.addEventListener('done', finish);
|
|
es.addEventListener('error', finish);
|
|
}, DEBOUNCE_MS);
|
|
|
|
return () => {
|
|
clearTimeout(handle);
|
|
};
|
|
}, [projectId, query, enabled]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
esRef.current?.close();
|
|
esRef.current = null;
|
|
};
|
|
}, []);
|
|
|
|
return items;
|
|
}
|