"use client"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { api } from "@/lib/api"; import { LiveProvider, useLive } from "@/lib/events"; import { fmtGB } from "@/lib/format"; import { Dot } from "./ui"; const NAV = [ { href: "/", label: "Dashboard", icon: "◫" }, { href: "/models", label: "Models", icon: "▤" }, { href: "/playground", label: "Playground", icon: "▷" }, { href: "/downloads", label: "Downloads", icon: "⇩" }, { href: "/harvester", label: "Harvester", icon: "✦" }, { href: "/keys", label: "API Keys", icon: "⚿" }, { href: "/system", label: "System", icon: "◉" }, { href: "/settings", label: "Settings", icon: "⚙" }, { href: "/docs", label: "API Docs", icon: "❯" }, ]; type AuthState = { checked: boolean; authenticated: boolean; needsSetup: boolean; email?: string }; export function Shell({ children }: { children: React.ReactNode }) { const path = usePathname(); const router = useRouter(); const [auth, setAuth] = useState({ checked: false, authenticated: false, needsSetup: false }); const isLogin = path === "/login"; useEffect(() => { let alive = true; api.get<{ needs_setup: boolean; authenticated: boolean; principal: { name: string } | null }>("/api/auth/status") .then((s) => { if (!alive) return; setAuth({ checked: true, authenticated: s.authenticated, needsSetup: s.needs_setup, email: s.principal?.name }); if (!s.authenticated && !isLogin) router.replace(`/login?next=${encodeURIComponent(path)}`); if (s.authenticated && isLogin) router.replace("/"); }) .catch(() => alive && setAuth({ checked: true, authenticated: false, needsSetup: false })); return () => { alive = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [path]); if (isLogin) return {children}; if (!auth.checked || !auth.authenticated) { return
Loading…
; } return (
{children}
); } function Sidebar({ path, email }: { path: string; email?: string }) { const live = useLive(); const router = useRouter(); const cur = live.manager?.loaded.find((w) => w.status === "ready"); const loading = live.manager && Object.values(live.manager.progress).length > 0; return ( ); } function MobileNav({ path }: { path: string }) { const items = NAV.slice(0, 5); return ( ); } function MoreMenu({ path }: { path: string }) { const [open, setOpen] = useState(false); const rest = NAV.slice(5); const active = rest.some((n) => path.startsWith(n.href)); return ( <> {open && (
setOpen(false)}>
e.stopPropagation()}> {rest.map((n) => ( setOpen(false)} className="flex items-center gap-3 px-3 py-3 text-sm rounded-lg hover:bg-surface-2"> {n.icon}{n.label} ))}
)} ); }