// overview.js — Dashboard: Quick Actions, Modelle & Recent Jobs import { api } from "../core/api.js"; import { $, esc, icon, toast } 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"]); function counts() { const models = S?.models || []; return { total: models.length, running: models.filter(m => RUNNING.has(m.state)).length, }; } function renderHero() { $("#hero").innerHTML = `
Dashboard

Mission Control

Steuerzentrale für deinen lokalen llama-swap-Stack. Hier verwaltest du Modelle, Downloads und Server-Wartung.

`; } async function triggerAction(action) { if (action === "restart_llama") { toast("Neustart ausgelöst..."); try { await api("/api/service/llama-swap/restart", { method: "POST" }); toast("llama-swap wird neugestartet."); } catch(e) { toast("Fehler: " + e.message, true); } } else if (action === "update_mc") { toast("Update gestartet! Siehe Aktivitäten."); try { await api("/api/update", { method: "POST" }); document.querySelector('.nav-item[data-view="activity"]').click(); } catch(e) { toast("Fehler: " + e.message, true); } } } // Global hook für onclick window.triggerAction = triggerAction; function renderQuickActions() { let actionsHtml = ""; if (SYS) { const ram_percent = SYS.ram.percent || 0; // Wenn RAM über 90% ist, zeige Warnung. // Wir nehmen 90 für Produktion, aber für den Test könnte es angepasst werden. if (ram_percent >= 90) { actionsHtml += `

⚠️ Arbeitsspeicher kritisch (${ram_percent.toFixed(0)}%)

Der RAM/VRAM ist fast voll. Dies kann zu Systeminstabilität führen.

`; } // Simulierter Update-Check (Idealerweise vom Backend, hier als permanenter Button wenn man manuell checken will, // oder wir blenden ihn ein wenn ein lokales flag gesetzt ist. Wir zeigen ihn hier als Feature-Highlight) // Da wir aktuell keinen echten Git-Check im Backend haben, zeigen wir einen "Update Prüfen" Button in den QuickActions. } // 3 Standard Kacheln (Cookbook, Server-Status, Aktivität/Guides) $("#ov-quick").innerHTML = actionsHtml + ` `; } function modelRow(m) { const on = RUNNING.has(m.state); const dot = m.state === "loading" || m.state === "starting" ? "load" : (on || m.state === "bereit") ? "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 `{ }`; if (c === "Bild") return `👁`; return ""; }).join(""); } const filename = m.meta?.filename ? `
${esc(m.meta.filename)}
` : ''; return `
${esc(m.name)}${caps}
${filename}
${state}
`; } function renderModels() { const models = S?.models || []; $("#ov-models").innerHTML = `

Aktuelle Modelle im Stack

${models.length || ""}
${models.length ? `
${models.map(modelRow).join("")}
` : `
Keine Modelle konfiguriert
Hol dir unter „Cookbook“ eins von HuggingFace.
`}`; } function renderRecentJobs() { const latest = J.slice(0, 4); const statusBadge = (s) => { if (s === "done") return 'fertig'; if (s === "failed") return 'fehler'; return 'lädt…'; }; $("#ov-recent-jobs").innerHTML = `

Letzte Aktivitäten

Alle ansehen →
${latest.length ? `
${latest.map(j => `
${esc(j.label)}
${statusBadge(j.state)}
`).join("")}
` : `
Keine Aktivitäten
Alles läuft ruhig.
` } `; } function renderAll() { renderHero(); renderQuickActions(); renderModels(); renderRecentJobs(); } function mount() { renderAll(); } function onStatus(s) { S = s; renderModels(); } function onJobs(jobs) { J = jobs || []; renderRecentJobs(); } function onSystem(sys) { SYS = sys; } export default { id: "overview", mount, onStatus, onJobs, onSystem };