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;