UI: Redesign active roles overview to slot-centric list in Dashboard and ModelsView

This commit is contained in:
Hitonabi
2026-06-26 13:38:58 +02:00
parent 066feee3ea
commit d286dbf203
7 changed files with 482 additions and 422 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,8 +7,8 @@
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-rbtAsh2N.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DJE5r1SD.css">
<script type="module" crossorigin src="/assets/index-D6XqaWNR.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-yPX24G-1.css">
</head>
<body>
<div id="root"></div>
+56 -44
View File
@@ -238,7 +238,7 @@ export function DashboardView() {
}
}
const activeModels = models.filter((m) => m.role)
// Active updates check
const activeOsJob = jobs.find(j => j.label.includes("OS-Update") && (j.state === "running" || j.state === "queued"))
@@ -568,58 +568,70 @@ export function DashboardView() {
</div>
</div>
{/* Card 4: Active Model Roles */}
{/* Card 4: Slot-Belegung (Rollen & Modelle) */}
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/15 flex flex-col justify-between">
<div>
<div className="flex items-center gap-2 mb-4">
<Layers className="h-4.5 w-4.5 text-primary" />
<h2 className="text-sm font-semibold tracking-wide uppercase text-foreground">Aktive Rollen</h2>
<h2 className="text-sm font-semibold tracking-wide uppercase text-foreground">Rollen-Belegung</h2>
</div>
<div className="space-y-2 max-h-48 overflow-y-auto pr-1 scrollbar-thin">
{activeModels.length === 0 ? (
<div className="text-xs text-muted-foreground py-6 text-center">Keine Modelle als Rollen zugewiesen.</div>
) : (
activeModels.map((m) => {
const isRunning = running.includes(m.name)
return (
<div
key={m.name}
className={cn(
"flex items-center justify-between p-2.5 rounded-xl border transition-all duration-300",
isRunning
? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5"
: "border-border/40 bg-background/20"
)}
>
<div className="min-w-0 flex-1 mr-2">
<div className="flex items-center gap-2">
<span className={cn("text-[9px] font-semibold uppercase px-1.5 py-0.5 rounded font-mono",
m.role === "fast" ? "bg-cyan-500/15 text-cyan-400" :
m.role === "heavy" ? "bg-amber-500/15 text-amber-400" :
m.role === "coder" ? "bg-violet-500/15 text-violet-400" :
m.role === "vision" ? "bg-pink-500/15 text-pink-400" : "bg-muted text-muted-foreground"
)}>
{m.role}
</span>
{isRunning && (
<span className="flex h-2 w-2 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500"></span>
</span>
)}
</div>
<div className="text-[10px] font-medium mt-1 truncate" title={m.name}>
{m.name}
</div>
</div>
<div className="text-[9px] font-mono text-muted-foreground shrink-0">
{isRunning ? "Warm" : "Bereit"}
{["fast", "heavy", "coder", "reasoning", "vision", "scout"].map((role) => {
const m = models.find((x) => x.role === role)
const isRunning = m ? running.includes(m.name) : false
return (
<div
key={role}
className={cn(
"flex items-center justify-between p-2 rounded-xl border transition-all duration-300",
isRunning
? "border-emerald-500/30 bg-emerald-500/5 shadow-sm shadow-emerald-500/5"
: m
? "border-primary/20 bg-primary/5"
: "border-border/30 bg-background/10 opacity-60"
)}
>
<div className="min-w-0 flex-1 mr-2">
<div className="flex items-center gap-2">
<span className={cn("text-[9px] font-semibold uppercase px-1.5 py-0.5 rounded font-mono border tracking-wider shrink-0",
role === "fast" ? "bg-cyan-500/15 text-cyan-400 border-cyan-500/25" :
role === "heavy" ? "bg-amber-500/15 text-amber-400 border-amber-500/25" :
role === "coder" ? "bg-violet-500/15 text-violet-400 border-violet-500/25" :
role === "reasoning" ? "bg-emerald-500/15 text-emerald-400 border-emerald-500/25" :
role === "vision" ? "bg-pink-500/15 text-pink-400 border-pink-500/25" :
"bg-teal-500/15 text-teal-400 border-teal-500/25"
)}>
{role}
</span>
<span className="text-xs font-semibold truncate font-mono text-foreground">
{m ? m.name.split("/").pop()?.replace(/\.gguf$/i, "") : "nicht zugewiesen"}
</span>
</div>
</div>
)
})
)}
<div className="flex items-center gap-1.5 shrink-0">
{m ? (
isRunning ? (
<span className="flex items-center gap-1 text-[8px] font-semibold text-emerald-400 uppercase tracking-wider font-mono">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500 animate-pulse" /> warm
</span>
) : (
<span className="text-[8px] font-semibold text-muted-foreground uppercase tracking-wider font-mono">
bereit
</span>
)
) : (
<span className="text-[8px] font-semibold text-muted-foreground/60 uppercase tracking-wider font-mono">
</span>
)}
</div>
</div>
)
})}
</div>
</div>
<div className="mt-4 text-[10px] text-muted-foreground/80 border-t border-border/30 pt-3">
+48
View File
@@ -788,6 +788,54 @@ function Cockpit() {
</div>
</div>
{/* ZONE B.5: Slot-Belegung */}
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/10 space-y-3.5">
<div className="text-xs font-bold uppercase tracking-wider text-muted-foreground px-1">
Gateway Steckplatz-Belegung (Slot-Zuweisung)
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
{["fast", "heavy", "coder", "reasoning", "vision", "scout"].map((role) => {
const m = models.find((x) => x.role === role)
const isWarm = m ? running.includes(m.name) : false
return (
<div
key={role}
onClick={() => setActiveRoleForAssign(role)}
className={cn(
"rounded-xl border p-3 flex flex-col justify-between gap-2.5 transition-all cursor-pointer select-none text-left bg-background/25 hover:border-primary/45 hover:bg-background/40 group min-h-[90px]",
isWarm
? "border-emerald-500/40 shadow-sm shadow-emerald-500/5"
: m
? "border-primary/20 bg-primary/5"
: "border-border/30 border-dashed opacity-75"
)}
>
<div className="flex items-center justify-between">
<span className={cn("text-[9px] font-bold uppercase tracking-wider font-mono px-1.5 py-0.5 rounded border",
role === "fast" ? "bg-cyan-500/10 text-cyan-400 border-cyan-500/20" :
role === "heavy" ? "bg-amber-500/10 text-amber-400 border-amber-500/20" :
role === "coder" ? "bg-violet-500/10 text-violet-400 border-violet-500/20" :
role === "reasoning" ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" :
role === "vision" ? "bg-pink-500/10 text-pink-400 border-pink-500/20" :
"bg-teal-500/10 text-teal-400 border-teal-500/20"
)}>{role}</span>
{isWarm && <span className="h-1.5 w-1.5 rounded-full bg-emerald-400 animate-pulse" />}
</div>
<div className="text-[10px] font-mono font-medium truncate text-foreground/90 pr-1 font-space" title={m?.name}>
{m ? m.name.split("/").pop()?.replace(/\.gguf$/i, "") : "nicht zugewiesen"}
</div>
<div className="text-[8px] text-primary/70 group-hover:text-primary font-bold uppercase tracking-wider transition-colors font-space">
Ändern
</div>
</div>
)
})}
</div>
</div>
{/* ZONE C: Library list cards & Upgrade Radar */}
<div className="space-y-4">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 px-1">