diff --git a/web/src/App.tsx b/web/src/App.tsx index d534d1b..bd60ce4 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -28,6 +28,7 @@ import { cn } from "@/lib/utils"; type Provider = "openai" | "anthropic" | "xai"; type AuthMode = "open" | "token"; type SidebarSelection = { kind: "chat" | "search"; id: string }; +type DraftSelectionKind = "chat" | "search"; type SidebarItem = SidebarSelection & { title: string; updatedAt: string; @@ -132,6 +133,7 @@ export default function App() { const [selectedItem, setSelectedItem] = useState(null); const [selectedChat, setSelectedChat] = useState(null); const [selectedSearch, setSelectedSearch] = useState(null); + const [draftKind, setDraftKind] = useState(null); const [isLoadingCollections, setIsLoadingCollections] = useState(false); const [isLoadingSelection, setIsLoadingSelection] = useState(false); const [isSending, setIsSending] = useState(false); @@ -163,6 +165,7 @@ export default function App() { setSelectedItem(null); setSelectedChat(null); setSelectedSearch(null); + setDraftKind(null); }; const refreshCollections = async (preferredSelection?: SidebarSelection) => { @@ -277,9 +280,9 @@ export default function App() { }, [isAuthenticated, selectedKey]); useEffect(() => { - if (selectedItem?.kind === "search") return; + if (draftKind === "search" || selectedItem?.kind === "search") return; transcriptEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); - }, [selectedChat?.messages.length, isSending, selectedItem?.kind]); + }, [draftKind, selectedChat?.messages.length, isSending, selectedItem?.kind]); const messages = selectedChat?.messages ?? []; @@ -294,6 +297,8 @@ export default function App() { }, [searches, selectedItem]); const selectedTitle = useMemo(() => { + if (draftKind === "chat") return "New chat"; + if (draftKind === "search") return "New search"; if (!selectedItem) return "Sybil"; if (selectedItem.kind === "chat") { if (selectedChat) return getChatTitle(selectedChat, selectedChat.messages); @@ -303,69 +308,34 @@ export default function App() { if (selectedSearch) return getSearchTitle(selectedSearch); if (selectedSearchSummary) return getSearchTitle(selectedSearchSummary); 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 handleCreateChat = async () => { + const handleCreateChat = () => { setError(null); - try { - const chat = await createChat(); - setSelectedItem({ kind: "chat", id: chat.id }); - setSelectedChat({ - 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); - } - } + setDraftKind("chat"); + setSelectedItem(null); + setSelectedChat(null); + setSelectedSearch(null); }; - const handleCreateSearch = async () => { + const handleCreateSearch = () => { setError(null); - try { - const search = await createSearch(); - setSelectedItem({ kind: "search", id: search.id }); - setSelectedSearch({ - 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); - } - } + setDraftKind("search"); + setSelectedItem(null); + setSelectedChat(null); + setSelectedSearch(null); }; 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) { const chat = await createChat(); chatId = chat.id; + setDraftKind(null); setSelectedItem({ kind: "chat", id: chatId }); setSelectedChat({ id: chat.id, @@ -433,11 +403,15 @@ export default function App() { }; 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) { - const search = await createSearch(); + const search = await createSearch({ + query, + title: query.slice(0, 80), + }); searchId = search.id; + setDraftKind(null); setSelectedItem({ kind: "search", id: searchId }); } @@ -542,6 +516,7 @@ export default function App() { setSelectedItem(null); setSelectedChat(null); setSelectedSearch(null); + setDraftKind(null); setComposer(""); setError(null); }; @@ -638,7 +613,10 @@ export default function App() { "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" )} - onClick={() => setSelectedItem({ kind: item.kind, id: item.id })} + onClick={() => { + setDraftKind(null); + setSelectedItem({ kind: item.kind, id: item.id }); + }} type="button" >