Feinschliff Phase 2: Dashboard Redesign, RAM Check, Accordions
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// overview.js — Dashboard-Kopf: Hero + kompakte Modell-Liste.
|
||||
// overview.js — Dashboard: Quick Actions, Modelle & Recent Jobs
|
||||
|
||||
import { $, esc } from "../core/ui.js";
|
||||
import { $, esc, icon } from "../core/ui.js";
|
||||
|
||||
let S = null; // letzter Status
|
||||
let J = []; // letzte Job-Liste
|
||||
let SYS = null;
|
||||
|
||||
const RUNNING = new Set(["running", "ready", "loading", "starting"]);
|
||||
|
||||
@@ -12,34 +13,48 @@ function counts() {
|
||||
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>
|
||||
<div class="eyebrow">Dashboard</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" : "")}
|
||||
<p>Steuerzentrale für deinen lokalen llama-swap-Stack. Hier verwaltest du Modelle, Downloads und Server-Wartung.</p>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderQuickActions() {
|
||||
// 3 Kacheln (Cookbook, Server-Status, Aktivität/Guides)
|
||||
$("#ov-quick").innerHTML = `
|
||||
<div class="card" style="cursor:pointer; display:flex; flex-direction:column; gap:12px; transition:border-color 0.2s" onclick="document.querySelector('.nav-item[data-view=\\'cookbook\\']').click()" onmouseover="this.style.borderColor='var(--act)'" onmouseout="this.style.borderColor='var(--line)'">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<h3 style="margin:0; font-size:16px;">Modell finden</h3>
|
||||
<span style="color:var(--act)">${icon("book")}</span>
|
||||
</div>
|
||||
<p style="margin:0; font-size:13px; color:var(--mut);">Durchsuche HuggingFace nach neuen Modellen im Cookbook.</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="cursor:pointer; display:flex; flex-direction:column; gap:12px; transition:border-color 0.2s" onclick="document.querySelector('.nav-item[data-view=\\'activity\\']').click()" onmouseover="this.style.borderColor='var(--act)'" onmouseout="this.style.borderColor='var(--line)'">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<h3 style="margin:0; font-size:16px;">Live Metriken</h3>
|
||||
<span style="color:var(--act)">${icon("pulse")}</span>
|
||||
</div>
|
||||
<p style="margin:0; font-size:13px; color:var(--mut);">${SYS ? `System läuft (RAM: ${SYS.ram.percent.toFixed(0)}%, CPU: ${SYS.cpu.percent.toFixed(0)}%)` : 'Lade Metriken...'}</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="cursor:pointer; display:flex; flex-direction:column; gap:12px; transition:border-color 0.2s" onclick="document.querySelector('.nav-item[data-view=\\'server\\']').click()" onmouseover="this.style.borderColor='var(--act)'" onmouseout="this.style.borderColor='var(--line)'">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<h3 style="margin:0; font-size:16px;">Wartung</h3>
|
||||
<span style="color:var(--act)">${icon("server")}</span>
|
||||
</div>
|
||||
<p style="margin:0; font-size:13px; color:var(--mut);">Server neustarten, VRAM leeren oder Engine aktualisieren.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function modelRow(m) {
|
||||
const on = RUNNING.has(m.state);
|
||||
const dot = m.state === "loading" || m.state === "starting" ? "load" : on ? "on" : "";
|
||||
@@ -53,15 +68,16 @@ function modelRow(m) {
|
||||
return "";
|
||||
}).join("");
|
||||
}
|
||||
|
||||
const filename = m.meta?.filename ? `<div class="li-sub" style="font-family:var(--mono); color:var(--mut);">${esc(m.meta.filename)}</div>` : '';
|
||||
|
||||
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>
|
||||
${filename}
|
||||
</div>
|
||||
<div class="li-right">
|
||||
<div class="li-meta">${m.port ?? "auto"}</div>
|
||||
<div class="li-time">${state}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -77,10 +93,43 @@ function renderModels() {
|
||||
<div class="e-s">Hol dir unter „Cookbook“ eins von HuggingFace.</div></div>`}`;
|
||||
}
|
||||
|
||||
function renderAll() { renderHero(); renderModels(); }
|
||||
function renderRecentJobs() {
|
||||
const latest = J.slice(0, 4);
|
||||
|
||||
const statusBadge = (s) => {
|
||||
if (s === "done") return '<span class="badge b-run" style="font-size:10px">fertig</span>';
|
||||
if (s === "failed") return '<span class="badge b-err" style="font-size:10px">fehler</span>';
|
||||
return '<span class="badge b-load" style="font-size:10px">lädt…</span>';
|
||||
};
|
||||
|
||||
$("#ov-recent-jobs").innerHTML = `
|
||||
<div class="card-h"><h3>Letzte Aktivitäten</h3><span class="meta" style="cursor:pointer" onclick="document.querySelector('.nav-item[data-view=\\'activity\\']').click()">Alle ansehen →</span></div>
|
||||
${latest.length
|
||||
? `<div class="list">
|
||||
${latest.map(j => `
|
||||
<div class="li" style="padding:10px 4px">
|
||||
<div class="li-main">
|
||||
<div class="li-id" style="font-size:12.5px">${esc(j.label)}</div>
|
||||
</div>
|
||||
<div class="li-right">${statusBadge(j.state)}</div>
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>`
|
||||
: `<div class="empty-c"><div class="e-t">Keine Aktivitäten</div><div class="e-s">Alles läuft ruhig.</div></div>`
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function renderAll() {
|
||||
renderHero();
|
||||
renderQuickActions();
|
||||
renderModels();
|
||||
renderRecentJobs();
|
||||
}
|
||||
|
||||
function mount() { renderAll(); }
|
||||
function onStatus(s) { S = s; renderAll(); }
|
||||
function onJobs(jobs) { J = jobs || []; renderHero(); }
|
||||
function onStatus(s) { S = s; renderModels(); }
|
||||
function onJobs(jobs) { J = jobs || []; renderRecentJobs(); }
|
||||
function onSystem(sys) { SYS = sys; renderQuickActions(); }
|
||||
|
||||
export default { id: "overview", mount, onStatus, onJobs };
|
||||
export default { id: "overview", mount, onStatus, onJobs, onSystem };
|
||||
|
||||
Reference in New Issue
Block a user