// jobs.js — Aktivität (v3): Live-System-Metriken + Hintergrund-Jobs mit Log. // Exportiert track(id), damit andere Panels einen Job auto-aufklappen. import { $, esc, fmtBytes } from "../core/ui.js"; const tracked = new Set(); let JOBS = []; let SYS = null; export function track(id) { tracked.add(id); renderJobs(); } const hist = { cpu: [], ram: [], gpu: [] }; const MAX_HIST = 60; function statusBadge(s) { return s === "done" ? 'fertig' : s === "failed" ? 'fehler' : 'läuft…'; } function dotClass(s) { return s === "done" ? "on" : s === "failed" ? "" : "load"; } function tile(label, value, sub) { return `
${label}
${value}
${sub}
`; } function meter(label, pct) { const p = Math.max(0, Math.min(100, pct || 0)); const cls = p >= 90 ? "bad" : p >= 75 ? "warn" : ""; return `
${label}${Math.round(p)} %
`; } function spark(arr, varName) { return '
' + arr.map(v => `
`).join("") + "
"; } function gpuPct() { const g = SYS?.gpu; if (g && (g.vram.total + g.gtt.total) > 0) return ((g.vram.used + g.gtt.used) / (g.vram.total + g.gtt.total)) * 100; return 0; } function renderSys() { if (!SYS) return; hist.cpu.push(SYS.cpu.percent); hist.ram.push(SYS.ram.percent); hist.gpu.push(gpuPct()); for (const k of ["cpu", "ram", "gpu"]) if (hist[k].length > MAX_HIST) hist[k].shift(); const k = $("#act-kpis"); if (k) k.innerHTML = tile("Prozessor (CPU)", `${Math.round(SYS.cpu.percent)} %`, SYS.cpu.temp != null ? `${Math.round(SYS.cpu.temp)}° CPU-Temp` : "Auslastung") + tile("Arbeitsspeicher", `${Math.round(SYS.ram.percent)} %`, `${fmtBytes(SYS.ram.used)} / ${fmtBytes(SYS.ram.total)}`) + tile("Grafikspeicher", `${Math.round(gpuPct())} %`, SYS.gpu_temp != null ? `${Math.round(SYS.gpu_temp)}° GPU-Temp` : "VRAM + GTT"); const g = SYS.gpu; const gpuStr = g && (g.vram.total + g.gtt.total) > 0 ? `${fmtBytes(g.vram.used + g.gtt.used)} / ${fmtBytes(g.vram.total + g.gtt.total)}` : "–"; const s = $("#act-sys"); if (s) s.innerHTML = `

System-Metriken (Bosgame)

Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
Arbeitsspeicher (RAM)${fmtBytes(SYS.ram.used)} / ${fmtBytes(SYS.ram.total)}
Grafikspeicher (VRAM + GTT)${gpuStr}
Speicherplatz (Disk)${Math.round(SYS.disk.percent)} % belegt
Temperatur (GPU / CPU)${SYS.gpu_temp != null ? Math.round(SYS.gpu_temp) + "°" : "–"} / ${SYS.cpu.temp != null ? Math.round(SYS.cpu.temp) + "°" : "–"}
CPU-Verlauf
${spark(hist.cpu, "--accent")}
RAM-Verlauf
${spark(hist.ram, "--purple")}
VRAM-Verlauf
${spark(hist.gpu, "--on")}
`; } function mount() { $("#act-head").innerHTML = `

Aktivität

Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
`; $("#v-activity").innerHTML = `

Hintergrund-Aufgaben

Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
Gerade nichts los.
Alles ruhig — keine laufenden Aufgaben.
`; $("#v-activity").addEventListener("click", e => { const h = e.target.closest(".job-h"); if (!h) return; const id = h.getAttribute("data-id"); tracked.has(id) ? tracked.delete(id) : tracked.add(id); renderJobs(); }); if (SYS) renderSys(); } function renderJobs() { const c = $("#jobs"); if (!c) return; $("#jobs-empty").style.display = JOBS.length ? "none" : "flex"; const failed = JOBS.filter(j => j.state === "failed").length; $("#job-count").textContent = JOBS.length ? (failed ? failed + " Fehler" : JOBS.length + " gesamt") : ""; c.innerHTML = JOBS.map(j => { const log = tracked.has(j.id) ? `
${esc((j.log || []).join("\n"))}
` : ""; return `
${esc(j.label)}${statusBadge(j.state)}
${log}
`; }).join(""); } function onJobs(jobs) { JOBS = jobs || []; renderJobs(); } function onSystem(sys) { SYS = sys; if ($("#act-sys")) renderSys(); } export default { id: "jobs", mount, onJobs, onSystem };