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 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<SidebarSelection | null>(null);
const [selectedChat, setSelectedChat] = useState<ChatDetail | null>(null);
const [selectedSearch, setSelectedSearch] = useState<SearchDetail | null>(null);
const [draftKind, setDraftKind] = useState<DraftSelectionKind | null>(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"
>
<div className="flex items-center gap-2">