(function () {
var root = document.getElementById("overview");
if (!root) return;
var containerUrl = root.dataset.containerUrl;
var cpuScale = root.dataset.cpuScale; var memScale = root.dataset.memScale;
var memCap = Number(root.dataset.memCap);
var MiB = 1048576;
var GiB = 1073741824;
var CPU_FLOOR = 0.1; var MEM_FLOOR = 16 * MiB; var TRAIL = 24; var DECAY = 0.84; var GAP = 0.012; var GROUP_GAP = 0.06;
var css = getComputedStyle(document.documentElement);
var COLOR = {
bar: css.getPropertyValue("--accent").trim(),
grid: css.getPropertyValue("--border").trim(),
label: css.getPropertyValue("--muted").trim(),
halo: css.getPropertyValue("--panel").trim(),
hover: css.getPropertyValue("--fg").trim(),
err: css.getPropertyValue("--err").trim(),
};
var list = []; var hist = {}; var layout = []; var lastTs = 0;
function clamp01(v) { return v < 0 ? 0 : v > 1 ? 1 : v; }
function scaled(mode, frac, logFrac) {
if (mode === "log") return clamp01(logFrac);
if (mode === "sqrt") return clamp01(Math.sqrt(frac));
return clamp01(frac);
}
function scaleCpu(v) {
var lo = Math.log10(CPU_FLOOR);
var logFrac = (Math.log10(Math.max(v, CPU_FLOOR)) - lo) / (Math.log10(100) - lo);
return scaled(cpuScale, v / 100, logFrac);
}
function scaleMem(v) {
var lo = Math.log2(MEM_FLOOR);
var logFrac = (Math.log2(Math.max(v, MEM_FLOOR)) - lo) / (Math.log2(memCap) - lo);
return scaled(memScale, v / memCap, logFrac);
}
function cpuTicks() {
if (cpuScale === "log") return [1, 10, 50];
if (cpuScale === "sqrt") return [5, 25, 60];
return [25, 50, 75];
}
function memTicks() {
if (memScale !== "log") return [memCap / 4, memCap / 2, (memCap * 3) / 4];
var out = [];
for (var v = 128 * MiB; v < memCap * 0.7 && out.length < 3; v *= 8) out.push(v);
return out;
}
function fmtCpu(v) { return (v >= 10 ? v.toFixed(0) : v.toFixed(1)) + "%"; }
function fmtMem(v) {
if (v >= GiB) return (v / GiB).toFixed(v >= 10 * GiB ? 0 : 1) + "G";
return (v / MiB).toFixed(0) + "M";
}
function makeDisc(key, scale, ticks, metric, fmt) {
var canvas = document.getElementById("overview-" + key);
var readout = document.getElementById("overview-readout-" + key);
var disc = {
canvas: canvas,
ctx: canvas.getContext("2d"),
scale: scale,
ticks: ticks,
metric: metric, fmt: fmt,
readout: readout,
size: 0,
hover: -1,
};
function resize() {
var w = canvas.parentElement.clientWidth;
var size = Math.max(200, Math.min(300, w));
if (size === disc.size) return;
disc.size = size;
var dpr = window.devicePixelRatio || 1;
canvas.width = size * dpr;
canvas.height = size * dpr;
canvas.style.width = size + "px";
canvas.style.height = size + "px";
disc.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
draw(disc);
}
new ResizeObserver(resize).observe(canvas.parentElement);
resize();
canvas.addEventListener("mousemove", function (e) {
var r = canvas.getBoundingClientRect();
var half = disc.size / 2;
var dx = e.clientX - r.left - half;
var dy = e.clientY - r.top - half;
var idx = -1;
if (Math.sqrt(dx * dx + dy * dy) <= half - 6) {
idx = hitSector(Math.atan2(dy, dx));
}
canvas.style.cursor = idx >= 0 ? "pointer" : "default";
if (idx !== disc.hover) {
disc.hover = idx;
draw(disc);
updateReadout(disc);
}
});
canvas.addEventListener("mouseleave", function () {
if (disc.hover === -1) return;
disc.hover = -1;
draw(disc);
updateReadout(disc);
});
canvas.addEventListener("click", function () {
if (disc.hover >= 0) window.location.href = containerUrl + list[disc.hover].id;
});
return disc;
}
function updateReadout(disc) {
var c = list[disc.hover];
if (!c) {
disc.readout.textContent = "";
return;
}
var v = latest(c.id, disc.metric);
var name = c.stack ? c.stack + "/" + c.name : c.name;
var text = c.down ? "down" : v == null ? "–" : disc.fmt(v);
disc.readout.textContent = name + " · " + text;
}
function latest(id, metric) {
var h = hist[id];
return h && h.length ? h[h.length - 1][metric] : null;
}
function computeLayout() {
var n = list.length;
layout = [];
if (!n) return;
var boundaries = 0;
var i;
for (i = 1; i < n; i++) {
if (list[i].stack !== list[i - 1].stack) boundaries++;
}
var width = (2 * Math.PI - boundaries * GROUP_GAP - (n - boundaries) * GAP) / n;
var a = -Math.PI / 2;
for (i = 0; i < n; i++) {
if (i > 0) a += list[i].stack !== list[i - 1].stack ? GROUP_GAP : GAP;
layout.push({ a0: a, a1: a + width });
a += width;
}
}
function hitSector(angle) {
var a = angle < -Math.PI / 2 ? angle + 2 * Math.PI : angle;
for (var i = 0; i < layout.length; i++) {
if (a >= layout[i].a0 && a <= layout[i].a1) return i;
}
return -1;
}
function sectorPath(ctx, cx, cy, r0, r1, a0, a1) {
ctx.beginPath();
ctx.moveTo(cx + r0 * Math.cos(a0), cy + r0 * Math.sin(a0));
ctx.arc(cx, cy, r1, a0, a1);
ctx.arc(cx, cy, r0, a1, a0, true);
ctx.closePath();
}
function draw(disc) {
var ctx = disc.ctx;
var size = disc.size;
if (!size) return;
var cx = size / 2;
var cy = size / 2;
var rim = size / 2 - 14; var r0 = 6; ctx.clearRect(0, 0, size, size);
var ticks = disc.ticks();
ctx.lineWidth = 1;
ctx.strokeStyle = COLOR.grid;
var i;
for (i = 0; i < ticks.length; i++) {
ctx.beginPath();
ctx.arc(cx, cy, r0 + disc.scale(ticks[i]) * (rim - r0), 0, 2 * Math.PI);
ctx.stroke();
}
ctx.beginPath();
ctx.arc(cx, cy, rim, 0, 2 * Math.PI);
ctx.stroke();
for (i = 0; i < list.length; i++) {
var sec = layout[i];
if (list[i].down) {
sectorPath(ctx, cx, cy, r0, rim, sec.a0, sec.a1);
ctx.fillStyle = COLOR.halo;
ctx.fill();
ctx.strokeStyle = COLOR.err;
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.lineWidth = 1;
continue;
}
ctx.fillStyle = COLOR.bar;
var h = hist[list[i].id] || [];
for (var k = 0; k < h.length; k++) {
var alpha = Math.pow(DECAY, h.length - 1 - k);
if (alpha < 0.03) continue;
var v = h[k][disc.metric];
if (v == null) continue;
var r = r0 + disc.scale(v) * (rim - r0);
if (r <= r0 + 0.5) continue;
ctx.globalAlpha = alpha;
sectorPath(ctx, cx, cy, r0, r, sec.a0, sec.a1);
ctx.fill();
}
}
ctx.globalAlpha = 1;
if (disc.hover >= 0 && layout[disc.hover]) {
var hs = layout[disc.hover];
ctx.strokeStyle = COLOR.hover;
ctx.lineWidth = 1.5;
sectorPath(ctx, cx, cy, r0, rim, hs.a0, hs.a1);
ctx.stroke();
ctx.lineWidth = 1;
}
ctx.font = "10px system-ui, sans-serif";
ctx.textAlign = "center";
for (i = 0; i < ticks.length; i++) {
var ry = cy - (r0 + disc.scale(ticks[i]) * (rim - r0)) - 2;
var label = disc.metric === "cpu" ? ticks[i] + "%" : fmtMem(ticks[i]);
ctx.strokeStyle = COLOR.halo;
ctx.lineWidth = 3;
ctx.strokeText(label, cx, ry);
ctx.fillStyle = COLOR.label;
ctx.fillText(label, cx, ry);
ctx.lineWidth = 1;
}
}
var cpuDisc = makeDisc("cpu", scaleCpu, cpuTicks, "cpu", fmtCpu);
var memDisc = makeDisc("mem", scaleMem, memTicks, "mem", function (v) {
return fmtMem(v) + "iB";
});
function apply(payload) {
if (!payload || !payload.containers || payload.ts_ms === lastTs) return;
lastTs = payload.ts_ms;
list = payload.containers;
var seen = {};
for (var i = 0; i < list.length; i++) {
var c = list[i];
seen[c.id] = true;
var h = hist[c.id] || (hist[c.id] = []);
h.push({ cpu: c.cpu, mem: c.mem });
if (h.length > TRAIL) h.shift();
}
for (var id in hist) {
if (!seen[id]) delete hist[id];
}
computeLayout();
draw(cpuDisc);
draw(memDisc);
updateReadout(cpuDisc);
updateReadout(memDisc);
}
document.addEventListener("dockdoe:overview", function (e) { apply(e.detail); });
var seedEl = document.getElementById("overview-seed");
if (seedEl) {
try {
apply(JSON.parse(seedEl.textContent));
} catch (err) {
}
}
})();