87 lines
3.0 KiB
JavaScript
87 lines
3.0 KiB
JavaScript
// overview.js — Dashboard-Kopf: Hero + kompakte Modell-Liste.
|
|
|
|
import { $, 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,
|
|
};
|
|
}
|
|
|
|
function mini(label, val, tone = "") {
|
|
const v = tone ? `<b style="${tone === "bad" ? "color:var(--err)" : ""}">${val}</b>` : val;
|
|
return `<div class="mini"><div class="l">${label}</div><div class="v">${v}</div></div>`;
|
|
}
|
|
|
|
function renderHero() {
|
|
const c = counts();
|
|
$("#hero").innerHTML = `<div class="hero">
|
|
<div>
|
|
<div class="eyebrow">Übersicht</div>
|
|
<h1>Mission Control</h1>
|
|
<p>Steuerzentrale für deinen lokalen llama-swap-Stack — Modelle, Downloads,
|
|
Wartung und Schnelltest an einem Ort.</p>
|
|
</div>
|
|
<div class="hero-stats">
|
|
${mini("Modelle", c.total)}
|
|
${mini("Aktiv", c.running, "on")}
|
|
${mini("Jobs", c.jobsRun)}
|
|
${mini("Fehler", c.jobsErr, c.jobsErr ? "bad" : "")}
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
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";
|
|
|
|
let caps = "";
|
|
if (m.meta && m.meta.caps) {
|
|
caps = m.meta.caps.map(c => {
|
|
if (c === "Code") return `<span title="Code" style="color:var(--blue);font-size:0.9em;margin-left:6px">{ }</span>`;
|
|
if (c === "Bild") return `<span title="Vision" style="color:var(--purple);font-size:0.9em;margin-left:6px">👁</span>`;
|
|
return "";
|
|
}).join("");
|
|
}
|
|
|
|
return `<div class="li">
|
|
<span class="li-dot ${dot}"></span>
|
|
<div class="li-main">
|
|
<div class="li-id" style="font-weight:500">${esc(m.name)}${caps}</div>
|
|
<div class="li-sub">TTL ${m.ttl ?? "—"}${typeof m.ttl === "number" ? "s" : ""}</div>
|
|
</div>
|
|
<div class="li-right">
|
|
<div class="li-meta">${m.port ?? "auto"}</div>
|
|
<div class="li-time">${state}</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
function renderModels() {
|
|
const models = S?.models || [];
|
|
$("#ov-models").innerHTML = `
|
|
<div class="card-h"><h3>Aktuelle Modelle im Stack</h3><span class="meta">${models.length || ""}</span></div>
|
|
${models.length
|
|
? `<div class="list">${models.map(modelRow).join("")}</div>`
|
|
: `<div class="empty-c"><div class="e-t">Keine Modelle konfiguriert</div>
|
|
<div class="e-s">Hol dir unter „Cookbook“ eins von HuggingFace.</div></div>`}`;
|
|
}
|
|
|
|
function renderAll() { renderHero(); renderModels(); }
|
|
|
|
function mount() { renderAll(); }
|
|
function onStatus(s) { S = s; renderAll(); }
|
|
function onJobs(jobs) { J = jobs || []; renderHero(); }
|
|
|
|
export default { id: "overview", mount, onStatus, onJobs };
|