// 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 `
`;
}
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 };