diff --git a/html/js/office-render.js b/html/js/office-render.js
index a8d690f..f5016c5 100644
--- a/html/js/office-render.js
+++ b/html/js/office-render.js
@@ -379,17 +379,34 @@ function renderKpiMonitor(frame, kpis) {
return g;
}
- const rowH = (frame.h - 30) / tiles.length;
- tiles.forEach((tile, index) => {
- const rowY = frame.y + 20 + index * rowH;
+ // Die Zeilen stehen direkt untereinander, statt die Monitorhoehe gleichmaessig unter den
+ // Kacheln aufzuteilen: Zaehler-Kacheln wie "Agenten"/"Online" (Issue #39) und "Hooks"
+ // (Issue #38) haben history: [] und damit keine Sparkline - verteilt man die Hoehe trotzdem,
+ // klaffen zwischen den Werten grosse leere Sparkline-Kaesten. Ein Verlauf bekommt seinen
+ // Platz deshalb nur, wenn es ihn wirklich gibt.
+ const LABEL_HEIGHT = 20;
+ // Passt zur Punkteberechnung in buildKpiSparklinePoints() (Default-Hoehe 32) - so fuellt die
+ // Linie den Kasten genau aus.
+ const SPARKLINE_HEIGHT = 32;
+ const ROW_GAP = 4;
+
+ let rowTop = frame.y + 20;
+ tiles.forEach((tile) => {
g.appendChild(
text(`${tile.label}: ${tile.currentValue ?? "–"} ${tile.unit}`.trim(), {
x: frame.x + 16,
- y: rowY + 14,
+ y: rowTop + 14,
class: "kpi-tile-label",
})
);
- g.appendChild(renderSparkline(tile.sparklinePoints, frame.w - 32, rowH - 26, frame.x + 16, rowY + 20));
+ rowTop += LABEL_HEIGHT;
+
+ if (tile.sparklinePoints) {
+ g.appendChild(renderSparkline(tile.sparklinePoints, frame.w - 32, SPARKLINE_HEIGHT, frame.x + 16, rowTop));
+ rowTop += SPARKLINE_HEIGHT;
+ }
+
+ rowTop += ROW_GAP;
});
return g;