// overview.js — Dashboard-Kopf: Hero + Mini-Stats, KPI-Reihe, Stack-Gesundheit, // kompakte Modell-Liste ("Session Router"). Speist sich aus /api/status + /api/jobs. import { $, icon, esc } from "../core/ui.js"; let S = null; // letzter Status let J = []; // letzte Job-Liste const RUNNING = new Set(["running", "ready", "loading", "starting"]); function counts() { const models = S?.models || []; return { total: models.length, running: models.filter(m => RUNNING.has(m.state)).length, jobsRun: J.filter(j => j.state === "running" || j.state === "queued").length, jobsErr: J.filter(j => j.state === "failed").length, swap: !!S?.swap_ok, }; } function mini(label, val, tone = "") { const v = tone ? `${val}` : val; return `
${label}
${v}
`; } function renderHero() { const c = counts(); $("#hero").innerHTML = `
Übersicht

Mission Control

Steuerzentrale für deinen lokalen llama-swap-Stack — Modelle, Downloads, Wartung und Schnelltest an einem Ort.

${mini("Modelle", c.total)} ${mini("Aktiv", c.running, "on")} ${mini("Jobs", c.jobsRun)} ${mini("Fehler", c.jobsErr, c.jobsErr ? "bad" : "")}
`; } function kpi(cls, title, ic, value, sub) { return `
${title}${icon(ic)}
${value}
${sub}
`; } function renderKpis() { const c = counts(); $("#kpis").innerHTML = kpi(c.swap ? "green" : "red", "llama-swap", "swap", c.swap ? "Online" : "Offline", "Transport-Status") + kpi("blue", "Modelle", "monitor", `${c.running}/${c.total}`, "aktiv / gesamt") + kpi("purple", "Jobs", "layers", c.jobsRun, "laufend") + kpi(c.jobsErr ? "red" : "muted", "Fehler", "alert", c.jobsErr, "in der Aktivität") + kpi("muted", "System-Last", "gauge", "n/a", "bald · Live-Auslastung"); } function kvRow(k, v, cls = "") { return `
${k}${v}
`; } function renderHealth() { const c = counts(); $("#health").innerHTML = `

Stack-Gesundheit

${c.swap ? "Connected" : "Offline"}
${kvRow("llama-swap", c.swap ? "Connected" : "Offline", c.swap ? "ok" : "bad")} ${kvRow("Modelle (gesamt)", c.total)} ${kvRow("Aktiv", c.running, c.running ? "ok" : "")} ${kvRow("Jobs (laufend)", c.jobsRun)} ${kvRow("Fehler", c.jobsErr, c.jobsErr ? "bad" : "")} ${kvRow("Auslastung (RAM/GPU/Disk)", "folgt", "na")}
`; } function modelRow(m) { const on = RUNNING.has(m.state); const dot = m.state === "loading" || m.state === "starting" ? "load" : on ? "on" : ""; const state = on ? (m.state === "loading" ? "lädt…" : "geladen") : "bereit"; return `
${esc(m.name)}
TTL ${m.ttl ?? "—"}${typeof m.ttl === "number" ? "s" : ""}
${m.port ?? "auto"}
${state}
`; } function renderModels() { const models = S?.models || []; $("#ov-models").innerHTML = `

Modelle

${models.length || ""}
${models.length ? `
${models.map(modelRow).join("")}
` : `
Keine Modelle konfiguriert
Hol dir unter „Modelle" eins von HuggingFace.
`}`; } function renderAll() { renderHero(); renderKpis(); renderHealth(); renderModels(); } function mount() { renderAll(); } function onStatus(s) { S = s; renderAll(); } function onJobs(jobs) { J = jobs || []; renderHero(); renderKpis(); renderHealth(); } export default { id: "overview", mount, onStatus, onJobs };