import { useEffect, useState } from "react" import { Check, X, Loader2, Cpu, Palette, User as UserIcon, Info, Users, Plus, Trash2, } from "lucide-react" import { listModels } from "@crema/llm-ui" import { AppShell } from "~/components/layout/app-shell" import { Button } from "~/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card" import { Input } from "~/components/ui/input" import { Textarea } from "~/components/ui/textarea" import { DEFAULT_SETTINGS, DEFAULT_SYSTEM_PROMPT, saveLLMSettings, useLLMSettings, type LLMSettings, } from "~/lib/llm-settings" import { loadActiveAgentId, newAgentId, resetAgents, saveActiveAgentId, saveAgents, useAgents, type Agent, } from "~/lib/agents" import { pageTitle } from "~/lib/page-meta" export const meta = () => pageTitle("Settings") const SECTION_KEY = "crema.settings.section" type SectionId = "llm" | "agents" | "appearance" | "account" | "about" const sections: { id: SectionId label: string icon: React.ComponentType<{ className?: string }> description: string }[] = [ { id: "llm", label: "LLM", icon: Cpu, description: "Model endpoint & budgets" }, { id: "agents", label: "Agents", icon: Users, description: "Personas, roles, sub-prompts", }, { id: "appearance", label: "Appearance", icon: Palette, description: "Theme, font size, surface, background", }, { id: "account", label: "Account", icon: UserIcon, description: "Profile & preferences" }, { id: "about", label: "About", icon: Info, description: "Version & credits" }, ] type TestState = | { kind: "idle" } | { kind: "running" } | { kind: "ok"; count: number } | { kind: "fail"; reason: string } export default function SettingsRoute() { const settings = useLLMSettings() const [draft, setDraft] = useState(settings) const [savedAt, setSavedAt] = useState(null) const [test, setTest] = useState({ kind: "idle" }) useEffect(() => { setDraft(settings) }, [settings]) const runTest = async () => { setTest({ kind: "running" }) const ac = new AbortController() const timeout = setTimeout(() => ac.abort(), 4000) try { const rows = await listModels({ baseURL: draft.baseURL, signal: ac.signal }) setTest({ kind: "ok", count: rows.length }) } catch (e) { setTest({ kind: "fail", reason: e instanceof Error ? e.message : String(e), }) } finally { clearTimeout(timeout) } } const dirty = draft.baseURL !== settings.baseURL || draft.contextTokens !== settings.contextTokens || draft.responseBudget !== settings.responseBudget const save = () => { saveLLMSettings(draft) setSavedAt(Date.now()) } const reset = () => { setDraft(DEFAULT_SETTINGS) } const [section, setSection] = useState(() => { if (typeof window === "undefined") return "llm" const stored = localStorage.getItem(SECTION_KEY) return sections.some((s) => s.id === stored) ? (stored as SectionId) : "llm" }) useEffect(() => { if (typeof window !== "undefined") localStorage.setItem(SECTION_KEY, section) }, [section]) return (
{section === "llm" && ( LLM Configure the local model endpoint and context budgets used by the Assistant. setDraft((d) => ({ ...d, baseURL: e.target.value })) } placeholder="http://localhost:1234/v1" spellCheck={false} autoComplete="off" /> setDraft((d) => ({ ...d, contextTokens: Number(e.target.value) || d.contextTokens, })) } />