File size: 7,193 Bytes
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import { useState } from "react";
import { BrainCircuit, FileSearch, Gauge, Github, Sparkles, Wrench } from "lucide-react";
import { api, setTokens } from "../lib/api.js";

export default function AuthPage({ onAuthed, appInfo }) {
  const [mode, setMode] = useState("login");
  const [email, setEmail] = useState("");
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [busy, setBusy] = useState(false);
  const [demoBusy, setDemoBusy] = useState(false);
  const isDemo = Boolean(appInfo?.demo_mode);

  async function startDemo() {
    setError("");
    setDemoBusy(true);
    try {
      const response = await fetch("/api/auth/demo", { method: "POST" });
      const data = await response.json();
      if (!response.ok) {
        throw new Error(
          typeof data.detail === "string"
            ? data.detail
            : "Could not start the demo. Please try again."
        );
      }
      setTokens(data);
      onAuthed(await api("/api/auth/me"));
    } catch (err) {
      setError(err.message);
    } finally {
      setDemoBusy(false);
    }
  }

  async function submit(event) {
    event.preventDefault();
    setError("");
    setBusy(true);
    try {
      const path = mode === "login" ? "/api/auth/login" : "/api/auth/register";
      const body =
        mode === "login" ? { email, password } : { email, username, password };
      const response = await fetch(path, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const data = await response.json();
      if (!response.ok) {
        throw new Error(
          typeof data.detail === "string"
            ? data.detail
            : "Please check your details and try again."
        );
      }
      setTokens(data);
      onAuthed(await api("/api/auth/me"));
    } catch (err) {
      setError(err.message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <div className="auth-page">
      <div className="auth-hero">
        <div className="brand" style={{ padding: 0 }}>
          <div className="brand-logo">
            <BrainCircuit size={18} />
          </div>
          Synapse
        </div>
        <h1>
          Your agentic AI assistant,
          <br />
          with real superpowers.
        </h1>
        <p>
          Streaming chat backed by an autonomous tool-calling agent, hybrid
          retrieval over your documents, and full cost observability.
        </p>
        <div className="hero-feature">
          <div className="bullet"><Wrench size={16} /></div>
          <div>
            <b>Agentic tool use</b>
            <span>Web search, weather, calculator and document retrieval, chosen autonomously.</span>
          </div>
        </div>
        <div className="hero-feature">
          <div className="bullet"><FileSearch size={16} /></div>
          <div>
            <b>Hybrid RAG with citations</b>
            <span>BM25 + dense vectors fused with RRF, reranked, and cited inline.</span>
          </div>
        </div>
        <div className="hero-feature">
          <div className="bullet"><Gauge size={16} /></div>
          <div>
            <b>Built-in observability</b>
            <span>Token, cost and latency analytics for every conversation.</span>
          </div>
        </div>
      </div>

      <div className="auth-form-side">
        <form className="auth-card fade-in" onSubmit={submit}>
          {isDemo ? (
            <>
              <h2>Try the live demo</h2>
              <p className="sub">
                One click, no signup. You get a private sandbox preloaded with a
                sample report so document retrieval works immediately.
              </p>
              {error && <div className="auth-error">{error}</div>}
              <button
                type="button"
                className="btn primary demo-cta"
                onClick={startDemo}
                disabled={demoBusy}
              >
                <Sparkles size={17} />
                {demoBusy ? "Preparing your sandbox..." : "Launch demo"}
              </button>
              <ul className="demo-points">
                <li>Ask about the weather or search the web, the agent picks its own tools</li>
                <li>Ask about the preloaded report to see hybrid RAG with citations</li>
                <li>Upload your own PDF, or open Analytics for live token and cost metrics</li>
              </ul>
              {appInfo?.repo_url && (
                <a className="repo-link" href={appInfo.repo_url} target="_blank" rel="noreferrer">
                  <Github size={15} /> View the source on GitHub
                </a>
              )}
              <div className="auth-divider"><span>or use an account</span></div>
            </>
          ) : (
            <>
              <h2>{mode === "login" ? "Welcome back" : "Create your account"}</h2>
              <p className="sub">
                {mode === "login"
                  ? "Sign in to continue your conversations."
                  : "A minute from now you will be chatting with Synapse."}
              </p>
              {error && <div className="auth-error">{error}</div>}
            </>
          )}
          {mode === "register" && (
            <div className="field">
              <label htmlFor="username">Name</label>
              <input
                id="username"
                value={username}
                onChange={(e) => setUsername(e.target.value)}
                placeholder="What should we call you?"
                required
                minLength={2}
              />
            </div>
          )}
          <div className="field">
            <label htmlFor="email">Email</label>
            <input
              id="email"
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="you@example.com"
              required
            />
          </div>
          <div className="field">
            <label htmlFor="password">Password</label>
            <input
              id="password"
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              placeholder={mode === "register" ? "At least 8 characters" : "Your password"}
              required
              minLength={8}
            />
          </div>
          <button className="btn primary" style={{ width: "100%" }} disabled={busy}>
            {busy ? "Please wait..." : mode === "login" ? "Sign in" : "Create account"}
          </button>
          <div className="auth-switch">
            {mode === "login" ? "New to Synapse?" : "Already have an account?"}{" "}
            <button
              type="button"
              onClick={() => {
                setMode(mode === "login" ? "register" : "login");
                setError("");
              }}
            >
              {mode === "login" ? "Create an account" : "Sign in"}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}