File size: 3,488 Bytes
b06ee4d | 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 | import { useEffect, useState } from "react";
import { LandingPage } from "./components/LandingPage";
import { ChatApp } from "./components/ChatApp";
import { useLLM } from "./hooks/useLLM";
import { Loader2 } from "lucide-react";
import "katex/dist/katex.min.css";
function App() {
const { status, loadModel, selectedModel, setSelectedModel, loadedModelId } =
useLLM();
const [hasStarted, setHasStarted] = useState(false);
const [showChat, setShowChat] = useState(false);
const isReady =
status.state === "ready" && loadedModelId === selectedModel.id;
const isLoading = hasStarted && !isReady && status.state !== "error";
const handleStart = () => {
setHasStarted(true);
loadModel();
};
const handleGoHome = () => {
setShowChat(false);
setTimeout(() => setHasStarted(false), 700);
};
useEffect(() => {
if (isReady && hasStarted) {
setShowChat(true);
}
}, [isReady, hasStarted]);
return (
<div className="relative h-screen w-screen bg-[#0A3235]">
{/* Landing page — hidden once loading starts */}
<div
className={`absolute inset-0 z-10 transition-opacity duration-700 ${
hasStarted ? "opacity-0 pointer-events-none" : "opacity-100"
}`}
>
<LandingPage
onStart={handleStart}
isLoading={isLoading}
showChat={showChat}
selectedModel={selectedModel}
onSelectModel={setSelectedModel}
loadedModelId={loadedModelId}
/>
</div>
{/* Chat page — fades in when ready */}
<div
className={`absolute inset-0 z-10 transition-opacity duration-700 ${
showChat ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
>
{hasStarted && <ChatApp onGoHome={handleGoHome} />}
</div>
{/* Loading overlay — sits on top, fades from loading screen directly to chat */}
<div
className={`absolute inset-0 z-30 flex flex-col items-center justify-center transition-opacity duration-700 bg-[#0A3235] ${
isLoading ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
>
<div
className={`flex w-full max-w-md flex-col items-center px-6 transition-all duration-700 ${isLoading ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
>
<img
src="/ai2.svg"
alt="AI2"
className="mb-8 h-9 w-auto"
draggable={false}
/>
<Loader2 className="h-10 w-10 animate-spin text-[#F0529C]" />
<p className="mt-4 text-sm tracking-wide text-[#FAF2E9b3]">
{status.state === "loading"
? (status.message ?? "Loading model…")
: status.state === "error"
? "Error"
: "Initializing…"}
</p>
<div className="mt-4 h-1.5 w-full rounded-full bg-[#FAF2E91a] overflow-hidden">
<div
className="h-full rounded-full bg-[linear-gradient(90deg,#105257_0%,#F0529C_60%,#B11BE8_100%)] transition-[width] ease-out"
style={{
width: `${status.state === "ready" ? 100 : status.state === "loading" && status.progress != null ? status.progress : 0}%`,
}}
/>
</div>
{status.state === "error" && (
<p className="mt-3 text-sm text-red-400">{status.error}</p>
)}
</div>
</div>
</div>
);
}
export default App;
|