This commit is contained in:
2026-02-14 00:09:06 -08:00
parent a447641f1d
commit 9a8e127ccc

View File

@@ -28,6 +28,7 @@ import { cn } from "@/lib/utils";
type Provider = "openai" | "anthropic" | "xai"; type Provider = "openai" | "anthropic" | "xai";
type AuthMode = "open" | "token"; type AuthMode = "open" | "token";
type SidebarSelection = { kind: "chat" | "search"; id: string }; type SidebarSelection = { kind: "chat" | "search"; id: string };
type DraftSelectionKind = "chat" | "search";
type SidebarItem = SidebarSelection & { type SidebarItem = SidebarSelection & {
title: string; title: string;
updatedAt: string; updatedAt: string;
@@ -132,6 +133,7 @@ export default function App() {
const [selectedItem, setSelectedItem] = useState<SidebarSelection | null>(null); const [selectedItem, setSelectedItem] = useState<SidebarSelection | null>(null);
const [selectedChat, setSelectedChat] = useState<ChatDetail | null>(null); const [selectedChat, setSelectedChat] = useState<ChatDetail | null>(null);
const [selectedSearch, setSelectedSearch] = useState<SearchDetail | null>(null); const [selectedSearch, setSelectedSearch] = useState<SearchDetail | null>(null);
const [draftKind, setDraftKind] = useState<DraftSelectionKind | null>(null);
const [isLoadingCollections, setIsLoadingCollections] = useState(false); const [isLoadingCollections, setIsLoadingCollections] = useState(false);
const [isLoadingSelection, setIsLoadingSelection] = useState(false); const [isLoadingSelection, setIsLoadingSelection] = useState(false);
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
@@ -163,6 +165,7 @@ export default function App() {
setSelectedItem(null); setSelectedItem(null);
setSelectedChat(null); setSelectedChat(null);
setSelectedSearch(null); setSelectedSearch(null);
setDraftKind(null);
}; };
const refreshCollections = async (preferredSelection?: SidebarSelection) => { const refreshCollections = async (preferredSelection?: SidebarSelection) => {
@@ -277,9 +280,9 @@ export default function App() {
}, [isAuthenticated, selectedKey]); }, [isAuthenticated, selectedKey]);
useEffect(() => { useEffect(() => {
if (selectedItem?.kind === "search") return; if (draftKind === "search" || selectedItem?.kind === "search") return;
transcriptEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); transcriptEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
}, [selectedChat?.messages.length, isSending, selectedItem?.kind]); }, [draftKind, selectedChat?.messages.length, isSending, selectedItem?.kind]);
const messages = selectedChat?.messages ?? []; const messages = selectedChat?.messages ?? [];
@@ -294,6 +297,8 @@ export default function App() {
}, [searches, selectedItem]); }, [searches, selectedItem]);
const selectedTitle = useMemo(() => { const selectedTitle = useMemo(() => {
if (draftKind === "chat") return "New chat";
if (draftKind === "search") return "New search";
if (!selectedItem) return "Sybil"; if (!selectedItem) return "Sybil";
if (selectedItem.kind === "chat") { if (selectedItem.kind === "chat") {
if (selectedChat) return getChatTitle(selectedChat, selectedChat.messages); if (selectedChat) return getChatTitle(selectedChat, selectedChat.messages);
@@ -303,69 +308,34 @@ export default function App() {
if (selectedSearch) return getSearchTitle(selectedSearch); if (selectedSearch) return getSearchTitle(selectedSearch);
if (selectedSearchSummary) return getSearchTitle(selectedSearchSummary); if (selectedSearchSummary) return getSearchTitle(selectedSearchSummary);
return "New search"; return "New search";
}, [selectedChat, selectedChatSummary, selectedItem, selectedSearch, selectedSearchSummary]); }, [draftKind, selectedChat, selectedChatSummary, selectedItem, selectedSearch, selectedSearchSummary]);
const isSearchMode = selectedItem?.kind === "search"; const isSearchMode = draftKind ? draftKind === "search" : selectedItem?.kind === "search";
const isSearchRunning = isSending && selectedItem?.kind === "search"; const isSearchRunning = isSending && selectedItem?.kind === "search";
const handleCreateChat = async () => { const handleCreateChat = () => {
setError(null); setError(null);
try { setDraftKind("chat");
const chat = await createChat(); setSelectedItem(null);
setSelectedItem({ kind: "chat", id: chat.id }); setSelectedChat(null);
setSelectedChat({ setSelectedSearch(null);
id: chat.id,
title: chat.title,
createdAt: chat.createdAt,
updatedAt: chat.updatedAt,
messages: [],
});
setSelectedSearch(null);
await refreshCollections({ kind: "chat", id: chat.id });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes("bearer token")) {
handleAuthFailure(message);
} else {
setError(message);
}
}
}; };
const handleCreateSearch = async () => { const handleCreateSearch = () => {
setError(null); setError(null);
try { setDraftKind("search");
const search = await createSearch(); setSelectedItem(null);
setSelectedItem({ kind: "search", id: search.id }); setSelectedChat(null);
setSelectedSearch({ setSelectedSearch(null);
id: search.id,
title: search.title,
query: search.query,
createdAt: search.createdAt,
updatedAt: search.updatedAt,
requestId: null,
latencyMs: null,
error: null,
results: [],
});
setSelectedChat(null);
await refreshCollections({ kind: "search", id: search.id });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (message.includes("bearer token")) {
handleAuthFailure(message);
} else {
setError(message);
}
}
}; };
const handleSendChat = async (content: string) => { const handleSendChat = async (content: string) => {
let chatId = selectedItem?.kind === "chat" ? selectedItem.id : null; let chatId = draftKind === "chat" ? null : selectedItem?.kind === "chat" ? selectedItem.id : null;
if (!chatId) { if (!chatId) {
const chat = await createChat(); const chat = await createChat();
chatId = chat.id; chatId = chat.id;
setDraftKind(null);
setSelectedItem({ kind: "chat", id: chatId }); setSelectedItem({ kind: "chat", id: chatId });
setSelectedChat({ setSelectedChat({
id: chat.id, id: chat.id,
@@ -433,11 +403,15 @@ export default function App() {
}; };
const handleSendSearch = async (query: string) => { const handleSendSearch = async (query: string) => {
let searchId = selectedItem?.kind === "search" ? selectedItem.id : null; let searchId = draftKind === "search" ? null : selectedItem?.kind === "search" ? selectedItem.id : null;
if (!searchId) { if (!searchId) {
const search = await createSearch(); const search = await createSearch({
query,
title: query.slice(0, 80),
});
searchId = search.id; searchId = search.id;
setDraftKind(null);
setSelectedItem({ kind: "search", id: searchId }); setSelectedItem({ kind: "search", id: searchId });
} }
@@ -542,6 +516,7 @@ export default function App() {
setSelectedItem(null); setSelectedItem(null);
setSelectedChat(null); setSelectedChat(null);
setSelectedSearch(null); setSelectedSearch(null);
setDraftKind(null);
setComposer(""); setComposer("");
setError(null); setError(null);
}; };
@@ -638,7 +613,10 @@ export default function App() {
"mb-1 w-full rounded-lg px-3 py-2 text-left transition", "mb-1 w-full rounded-lg px-3 py-2 text-left transition",
active ? "bg-slate-700 text-slate-50" : "text-slate-200 hover:bg-slate-800" active ? "bg-slate-700 text-slate-50" : "text-slate-200 hover:bg-slate-800"
)} )}
onClick={() => setSelectedItem({ kind: item.kind, id: item.id })} onClick={() => {
setDraftKind(null);
setSelectedItem({ kind: item.kind, id: item.id });
}}
type="button" type="button"
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">