Download frontend/src/components/layout/SettingsModal.jsx from Rixhot/ZARAI: direct link, hf CLI and curl.
- Browser
- Download file 9.77 kB
-
https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/components/layout/SettingsModal.jsx
- Command line
-
hf download hf://spaces/Rixhot/ZARAI/frontend/src/components/layout/SettingsModal.jsx
-
curl -L -o SettingsModal.jsx https://huggingface.co/spaces/Rixhot/ZARAI/resolve/main/frontend/src/components/layout/SettingsModal.jsx
9.77 kB
| /** | |
| * JARVIS β Settings Modal | |
| * Runtime configuration: model status, memory stats, preferences. | |
| * Opens via keyboard shortcut Ctrl+, or settings button. | |
| */ | |
| import React, { useState, useEffect } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { | |
| X, Brain, Eye, Mic, Database, Zap, Server, | |
| RefreshCw, Trash2, CheckCircle, XCircle, AlertCircle, | |
| } from 'lucide-react' | |
| import clsx from 'clsx' | |
| import toast from 'react-hot-toast' | |
| import { healthAPI } from '../../services/api' | |
| import useAppStore from '../../stores/appStore' | |
| export default function SettingsModal({ open, onClose }) { | |
| const [health, setHealth] = useState(null) | |
| const [loading, setLoading] = useState(false) | |
| const { sessions, messages } = useAppStore() | |
| const fetchHealth = async () => { | |
| setLoading(true) | |
| try { | |
| const data = await healthAPI.check() | |
| setHealth(data) | |
| } catch { | |
| setHealth({ status: 'offline' }) | |
| } finally { | |
| setLoading(false) | |
| } | |
| } | |
| useEffect(() => { | |
| if (open) fetchHealth() | |
| }, [open]) | |
| const totalMessages = Object.values(messages).reduce( | |
| (sum, msgs) => sum + (msgs?.length || 0), 0 | |
| ) | |
| const models = [ | |
| { key: 'llm', label: 'Language Model', icon: Brain, desc: 'TinyLlama / Mistral' }, | |
| { key: 'blip', label: 'BLIP Vision', icon: Eye, desc: 'Image captioning' }, | |
| { key: 'clip', label: 'CLIP Vision', icon: Eye, desc: 'Image-text similarity' }, | |
| { key: 'whisper', label: 'Whisper STT', icon: Mic, desc: 'Speech-to-text' }, | |
| ] | |
| return ( | |
| <AnimatePresence> | |
| {open && ( | |
| <> | |
| {/* Backdrop */} | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| exit={{ opacity: 0 }} | |
| className="fixed inset-0 bg-black/60 backdrop-blur-sm z-40" | |
| onClick={onClose} | |
| /> | |
| {/* Modal */} | |
| <motion.div | |
| initial={{ opacity: 0, scale: 0.95, y: 16 }} | |
| animate={{ opacity: 1, scale: 1, y: 0 }} | |
| exit={{ opacity: 0, scale: 0.95, y: 16 }} | |
| transition={{ type: 'spring', stiffness: 300, damping: 25 }} | |
| className="fixed inset-0 flex items-center justify-center z-50 p-4" | |
| > | |
| <div className="w-full max-w-md bg-jarvis-surface border border-jarvis-border | |
| rounded-2xl shadow-2xl overflow-hidden"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between px-5 py-4 | |
| border-b border-jarvis-border"> | |
| <div> | |
| <h2 className="font-display text-sm font-bold text-jarvis-accent tracking-widest"> | |
| SYSTEM STATUS | |
| </h2> | |
| <p className="text-[10px] text-jarvis-dim font-mono mt-0.5"> | |
| JARVIS Multi-Modal AI Assistant v1.0 | |
| </p> | |
| </div> | |
| <button | |
| onClick={onClose} | |
| className="p-1.5 rounded-lg text-jarvis-dim hover:text-jarvis-text | |
| hover:bg-jarvis-panel transition-colors" | |
| > | |
| <X size={15} /> | |
| </button> | |
| </div> | |
| <div className="p-5 space-y-5 max-h-[70vh] overflow-y-auto"> | |
| {/* Backend status */} | |
| <section> | |
| <SectionLabel icon={Server} label="Backend" /> | |
| <div className="flex items-center gap-2 mt-2"> | |
| <StatusDot ok={health?.status === 'online'} /> | |
| <span className="text-sm font-mono text-jarvis-text"> | |
| {health?.status === 'online' ? 'FastAPI Online' : 'Connectingβ¦'} | |
| </span> | |
| <button | |
| onClick={fetchHealth} | |
| disabled={loading} | |
| className="ml-auto text-jarvis-dim hover:text-jarvis-accent | |
| transition-colors p-1" | |
| > | |
| <RefreshCw size={12} className={loading ? 'animate-spin' : ''} /> | |
| </button> | |
| </div> | |
| </section> | |
| {/* AI Models */} | |
| <section> | |
| <SectionLabel icon={Brain} label="AI Models" /> | |
| <div className="mt-2 space-y-2"> | |
| {models.map(({ key, label, icon: Icon, desc }) => { | |
| const status = health?.models?.[key] | |
| return ( | |
| <div key={key} | |
| className="flex items-center gap-3 px-3 py-2.5 | |
| bg-jarvis-panel rounded-lg border border-jarvis-border"> | |
| <Icon size={14} className="text-jarvis-dim flex-shrink-0" /> | |
| <div className="flex-1 min-w-0"> | |
| <p className="text-xs font-medium text-jarvis-text">{label}</p> | |
| <p className="text-[10px] text-jarvis-dim font-mono">{desc}</p> | |
| </div> | |
| <ModelStatus status={status} /> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| </section> | |
| {/* Memory stats */} | |
| <section> | |
| <SectionLabel icon={Database} label="Memory & Storage" /> | |
| <div className="mt-2 grid grid-cols-2 gap-2"> | |
| {[ | |
| { label: 'Sessions', value: sessions.length }, | |
| { label: 'Messages', value: totalMessages }, | |
| { | |
| label: 'FAISS Vectors', | |
| value: health?.memory?.total_vectors ?? 'β', | |
| }, | |
| { | |
| label: 'Memory Store', | |
| value: health?.memory?.initialized ? 'Ready' : 'Not ready', | |
| }, | |
| ].map(({ label, value }) => ( | |
| <div key={label} | |
| className="bg-jarvis-panel rounded-lg border border-jarvis-border | |
| px-3 py-2"> | |
| <p className="text-[10px] text-jarvis-dim font-mono uppercase tracking-wider"> | |
| {label} | |
| </p> | |
| <p className="text-lg font-bold text-jarvis-accent font-mono mt-0.5"> | |
| {value} | |
| </p> | |
| </div> | |
| ))} | |
| </div> | |
| </section> | |
| {/* API endpoints */} | |
| <section> | |
| <SectionLabel icon={Zap} label="API Endpoints" /> | |
| <div className="mt-2 space-y-1 font-mono text-[11px]"> | |
| {[ | |
| { method: 'POST', path: '/api/chat/message', desc: 'Chat' }, | |
| { method: 'POST', path: '/api/image/analyze', desc: 'Vision' }, | |
| { method: 'POST', path: '/api/voice/interact', desc: 'Voice' }, | |
| { method: 'POST', path: '/api/tools/execute', desc: 'Tools' }, | |
| { method: 'WS', path: '/api/ws/chat/{id}', desc: 'Streaming' }, | |
| ].map(({ method, path, desc }) => ( | |
| <div key={path} | |
| className="flex items-center gap-2 px-2 py-1 rounded"> | |
| <span className={clsx( | |
| 'text-[9px] font-bold px-1.5 py-0.5 rounded', | |
| method === 'POST' ? 'bg-jarvis-accent/20 text-jarvis-accent' : | |
| method === 'WS' ? 'bg-jarvis-success/20 text-jarvis-success' : | |
| 'bg-jarvis-muted/20 text-jarvis-muted', | |
| )}> | |
| {method} | |
| </span> | |
| <span className="text-jarvis-muted">{path}</span> | |
| <span className="ml-auto text-jarvis-dim">{desc}</span> | |
| </div> | |
| ))} | |
| </div> | |
| </section> | |
| {/* Docs link */} | |
| <div className="text-center pt-1"> | |
| <a | |
| href="/api/docs" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-xs text-jarvis-accent hover:underline font-mono" | |
| > | |
| Open Interactive API Docs β | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </motion.div> | |
| </> | |
| )} | |
| </AnimatePresence> | |
| ) | |
| } | |
| // ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function SectionLabel({ icon: Icon, label }) { | |
| return ( | |
| <div className="flex items-center gap-2"> | |
| <Icon size={11} className="text-jarvis-accent" /> | |
| <span className="text-[10px] font-mono uppercase tracking-widest text-jarvis-dim"> | |
| {label} | |
| </span> | |
| </div> | |
| ) | |
| } | |
| function StatusDot({ ok }) { | |
| return ( | |
| <div className={clsx( | |
| 'w-2 h-2 rounded-full', | |
| ok ? 'bg-jarvis-success animate-pulse-slow' : 'bg-jarvis-dim', | |
| )} /> | |
| ) | |
| } | |
| function ModelStatus({ status }) { | |
| if (status === true) return <CheckCircle size={13} className="text-jarvis-success" /> | |
| if (status === false) return <XCircle size={13} className="text-jarvis-error" /> | |
| return <AlertCircle size={13} className="text-jarvis-dim" /> | |
| } | |