import { useEffect, useState } from "react"; import { ArrowRight, KeyRound, MessageSquareText, ShieldCheck, User, UserPlus, Zap } from "lucide-react"; import { api, setToken } from "../lib/api.ts"; import type { User as UserType } from "../lib/types.ts"; import { Button, FlowerMark, Input, Spinner } from "./ui.tsx"; import { RecoverDialog, RegisterDialog } from "./dialogs.tsx"; import { AccessRequestModal } from "./ReviewPage.tsx"; const PERKS = [ { icon: ShieldCheck, text: "Private by design — no cookies, no tracking" }, { icon: Zap, text: "Your own models, streamed in real time" }, ]; export function Login({ onLogin }: { onLogin: (user: UserType) => void }) { const [username, setUsername] = useState("admin"); const [key, setKey] = useState(""); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); const [recoverOpen, setRecoverOpen] = useState(false); const [recoveryOn, setRecoveryOn] = useState(false); const [recoveryFrom, setRecoveryFrom] = useState(undefined); const [registerOpen, setRegisterOpen] = useState(false); const [registrationOn, setRegistrationOn] = useState(false); const [requestOpen, setRequestOpen] = useState(false); useEffect(() => { api.methods().then((m) => { setRecoveryOn(m.recovery); setRecoveryFrom(m.from); setRegistrationOn(m.registration); }).catch(() => {}); }, []); async function submit(e: React.FormEvent) { e.preventDefault(); if (busy) return; setError(""); setBusy(true); try { const res = await api.login(username.trim().toLowerCase(), key); setToken(res.token); onLogin(res.user); } catch (err) { setError(err instanceof Error ? err.message : "Login failed"); } finally { setBusy(false); } } return (

Welcome back

Sign in to KisAssistant to continue

setUsername(e.target.value)} autoComplete="username" placeholder="Username" aria-label="Username" required />
setKey(e.target.value)} autoComplete="current-password" placeholder="Access key" aria-label="Access key" required />
{error && (

{error}

)}
New here?
setRegisterOpen(false)} onDone={(u, k) => { setUsername(u); setKey(k); }} /> {/* Wishlist path: when open registration is off, visitors can still reserve a username and plead their case; an admin triages it. */} {!registrationOn && ( )} setRequestOpen(false)} /> {recoveryOn && ( )} setRecoverOpen(false)} from={recoveryFrom} />
    {PERKS.map((p) => (
  • {p.text}
  • ))}

Admin key lives in the server .env; user keys are issued by the admin.

); }