fix(gui): Ventilator-Icon und Speicheranzeige auf Aufnahme-Vorrat

Das erste Icon las sich als Kleeblatt. Jetzt vier gebogene Pinwheel-Fluegel
in einem feststehenden Gehaeusering; nur die Fluegel drehen. Form vorab ohne
Browser geprueft (lab/preview_fan.py rendert die Bezier-Kandidaten als PNG).

Die Prozentzahl misst nicht mehr die Partition, sondern den Aufnahme-Vorrat:
100 % = freier Platz + alle Audiodateien unter recordings/, belegt = diese
Dateien. System, Modelle und node_modules fallen aus Zaehler und Nenner
heraus; der df-Wert steht als Fusszeile in der Klappkarte.

sysstat.js scannt recordings/ per readdir+stat (kein du-Prozess) und puffert
60 s; status() ist dadurch async, die Route laeuft ueber wrap().

Doku: Kapitel 07 + Changelog.
This commit is contained in:
Claude
2026-08-17 22:03:17 +02:00
parent 47288c0479
commit db50c9616d
7 changed files with 215 additions and 43 deletions
+12 -7
View File
@@ -148,18 +148,20 @@ nav { display: flex; gap: 6px; flex-wrap: wrap; }
.ring-bg { stroke: var(--line); }
.ring-fg { stroke: currentColor; stroke-linecap: round; transition: stroke-dasharray .4s ease; }
/* Turbine: Drehdauer kommt als --spin aus main.js (je mehr Last, desto kürzer) */
.turbine { width: 21px; height: 21px; flex: none; }
.tb-blades { fill: currentColor; transform-origin: 12px 12px; animation: tbspin var(--spin, 7s) linear infinite; }
.tb-hub { fill: var(--surface); stroke: currentColor; stroke-width: 1.4; }
/* Ventilator: nur die Flügel drehen, das Gehäuse steht (sonst wäre an einem
runden Ring keine Drehung zu sehen). Drehdauer kommt als --spin aus main.js. */
.fan { width: 22px; height: 22px; flex: none; }
.fan-case { fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .45; }
.fan-blades { fill: currentColor; transform-origin: 12px 12px; animation: tbspin var(--spin, 7s) linear infinite; }
.fan-hub { fill: var(--surface); stroke: currentColor; stroke-width: 1.5; }
@keyframes tbspin { to { transform: rotate(360deg); } }
.sys-chip.off .tb-blades { animation: none; }
@media (prefers-reduced-motion: reduce) { .tb-blades { animation: none; } }
.sys-chip.off .fan-blades { animation: none; }
@media (prefers-reduced-motion: reduce) { .fan-blades { animation: none; } }
/* Klappkarte bei Mauskontakt (oder Tastaturfokus) */
.sys-pop {
display: none; position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
min-width: 215px; padding: 10px 12px; color: var(--fg);
min-width: 215px; max-width: 300px; padding: 10px 12px; color: var(--fg);
background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
}
@@ -171,6 +173,9 @@ nav { display: flex; gap: 6px; flex-wrap: wrap; }
.sys-row { display: flex; justify-content: space-between; gap: 14px; font-size: 13px; padding: 2px 0; }
.sys-row span { color: var(--muted); }
.sys-row b { font-weight: 600; font-variant-numeric: tabular-nums; }
.sys-note { font-size: 11px; line-height: 1.35; color: var(--muted); margin: 6px 0 4px; }
.sys-row.sys-sub { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 5px; font-size: 12px; }
.sys-row.sys-sub b { font-weight: 500; color: var(--muted); }
.sys-bar { height: 5px; border-radius: 3px; background: var(--inset); overflow: hidden; margin: 3px 0 7px; }
.sys-bar i { display: block; height: 100%; background: var(--accent); transition: width .4s ease; }
.sys-chip.ok .sys-bar i { background: var(--ok); }