import { useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Wifi, WifiOff, Loader, CheckCircle, AlertCircle, Lock, LockOpen } from "lucide-react"; import { configSchema, loadConfig, saveConfig, checkConnection, type Config, type HealthStatus, } from "@/lib/config"; interface SettingsFormProps { onSaved?: () => void; } const statusConfig = { ok: { icon: CheckCircle, color: "#34d399", label: "Connected" }, "auth-required": { icon: AlertCircle, color: "#f59e0b", label: "Auth required" }, unreachable: { icon: WifiOff, color: "#f87171", label: "Unreachable" }, checking: { icon: Loader, color: "#818cf8", label: "Checking..." }, }; export function SettingsForm({ onSaved }: SettingsFormProps) { const existing = loadConfig(); const [baseUrl, setBaseUrl] = useState(existing?.baseUrl ?? "http://localhost:8000"); const [token, setToken] = useState(existing?.token ?? ""); const [errors, setErrors] = useState>>({}); const [saved, setSaved] = useState(false); const [health, setHealth] = useState<{ status: HealthStatus; message: string } | null>(null); const [checking, setChecking] = useState(false); async function handleTest() { setChecking(true); setHealth({ status: "checking", message: "Connecting..." }); const result = await checkConnection(baseUrl, token || undefined); setHealth(result); setChecking(false); // Auto-show token field if auth is required if (result.status === "auth-required" && !token) { document.getElementById("honcho-token")?.focus(); } } function handleSubmit(e: React.SyntheticEvent) { e.preventDefault(); const result = configSchema.safeParse({ baseUrl, token }); if (!result.success) { const fieldErrors: typeof errors = {}; for (const issue of result.error.issues) { const key = issue.path[0] as keyof Config; fieldErrors[key] = issue.message; } setErrors(fieldErrors); return; } setErrors({}); saveConfig(result.data); setSaved(true); setTimeout(() => { setSaved(false); onSaved?.(); }, 600); } const StatusIcon = health ? statusConfig[health.status].icon : null; return (
{/* Base URL */}
{ setBaseUrl(e.target.value); setHealth(null); }} placeholder="http://localhost:8000" className="flex-1 px-3 py-2 text-sm font-mono rounded-xl outline-none transition-all" style={{ background: "var(--surface)", border: "1px solid var(--border-2)", color: "var(--text-1)", }} onFocus={(e) => { e.target.style.borderColor = "var(--accent)"; }} onBlur={(e) => { e.target.style.borderColor = "var(--border-2)"; }} />
{errors.baseUrl && (

{errors.baseUrl}

)}

URL of your self-hosted Honcho instance

{/* Health status */} {health && (
{StatusIcon && ( )}

{statusConfig[health.status].label}

{health.message}

)}
{/* Token */}