Spaces:
Running
Running
File size: 4,142 Bytes
b2c1c67 48954a5 b2c1c67 48954a5 b2c1c67 48954a5 b2c1c67 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | import { useCallback, useEffect, useState } from "react";
import AuthPage from "./components/AuthPage.jsx";
import Sidebar from "./components/Sidebar.jsx";
import ChatView from "./components/ChatView.jsx";
import AnalyticsView from "./components/AnalyticsView.jsx";
import DocumentsPanel from "./components/DocumentsPanel.jsx";
import DemoBanner from "./components/DemoBanner.jsx";
import { api, getTokens, setTokens, setUnauthorizedHandler } from "./lib/api.js";
export default function App() {
const [user, setUser] = useState(null);
const [booting, setBooting] = useState(true);
const [appInfo, setAppInfo] = useState(null);
const [sessions, setSessions] = useState([]);
const [activeSessionId, setActiveSessionId] = useState(null);
const [view, setView] = useState("chat");
const [docsOpen, setDocsOpen] = useState(false);
const logout = useCallback(() => {
setTokens(null);
setUser(null);
setSessions([]);
setActiveSessionId(null);
setView("chat");
}, []);
useEffect(() => {
setUnauthorizedHandler(() => logout());
(async () => {
try {
const info = await fetch("/api/info").then((r) => r.json());
setAppInfo(info);
} catch {}
if (getTokens()) {
try {
setUser(await api("/api/auth/me"));
} catch {}
}
setBooting(false);
})();
}, [logout]);
const refreshSessions = useCallback(async () => {
const list = await api("/api/sessions");
setSessions(list);
return list;
}, []);
useEffect(() => {
if (user) refreshSessions().catch(() => {});
}, [user, refreshSessions]);
const newChat = useCallback(async () => {
const session = await api("/api/sessions", { method: "POST", body: {} });
setSessions((prev) => [session, ...prev]);
setActiveSessionId(session.id);
setView("chat");
return session;
}, []);
const deleteSession = useCallback(
async (id) => {
await api(`/api/sessions/${id}`, { method: "DELETE" });
setSessions((prev) => prev.filter((s) => s.id !== id));
if (activeSessionId === id) setActiveSessionId(null);
},
[activeSessionId]
);
const renameSession = useCallback(async (id, title) => {
const updated = await api(`/api/sessions/${id}`, {
method: "PATCH",
body: { title },
});
setSessions((prev) => prev.map((s) => (s.id === id ? updated : s)));
}, []);
const patchSessionLocal = useCallback((id, patch) => {
setSessions((prev) =>
prev.map((s) => (s.id === id ? { ...s, ...patch } : s))
);
}, []);
if (booting) {
return (
<div className="app-shell" style={{ alignItems: "center", justifyContent: "center" }}>
<div className="spinner" />
</div>
);
}
if (!user) {
return <AuthPage onAuthed={setUser} appInfo={appInfo} />;
}
return (
<div className="app-shell">
<Sidebar
user={user}
sessions={sessions}
activeSessionId={activeSessionId}
view={view}
onSelectSession={(id) => {
setActiveSessionId(id);
setView("chat");
}}
onNewChat={() => newChat().catch(() => {})}
onDeleteSession={deleteSession}
onRenameSession={renameSession}
onOpenDocs={() => setDocsOpen(true)}
onShowAnalytics={() => setView("analytics")}
onLogout={logout}
/>
<main className="main-column">
{appInfo?.demo_mode && <DemoBanner appInfo={appInfo} />}
{view === "analytics" ? (
<AnalyticsView />
) : (
<ChatView
appInfo={appInfo}
sessionId={activeSessionId}
session={sessions.find((s) => s.id === activeSessionId) || null}
ensureSession={async () => {
if (activeSessionId) return activeSessionId;
const session = await newChat();
return session.id;
}}
onSessionMeta={patchSessionLocal}
onOpenDocs={() => setDocsOpen(true)}
/>
)}
</main>
{docsOpen && <DocumentsPanel onClose={() => setDocsOpen(false)} />}
</div>
);
}
|