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:
+23
-12
@@ -83,8 +83,11 @@ function render() {
|
||||
// Zahlen auf (CPU, RAM, Temperatur bzw. belegt/frei/gesamt).
|
||||
const sysmon = { timer: null, last: null };
|
||||
|
||||
const TURBINE_BLADES = [0, 72, 144, 216, 288].map(a =>
|
||||
`<path transform="rotate(${a} 12 12)" d="M12 12 C10.1 8.7 9.9 5.2 12 2.3 C14.1 5.2 13.9 8.7 12 12 Z"/>`).join('');
|
||||
// Ventilator: vier gebogene Flügel im feststehenden Gehäusering. Nur die
|
||||
// Flügelgruppe dreht sich, der Ring bleibt stehen — sonst sähe man die
|
||||
// Drehung am runden Gehäuse gar nicht.
|
||||
const FAN_BLADES = [0, 90, 180, 270].map(a =>
|
||||
`<path transform="rotate(${a} 12 12)" d="M12 12 C11.4 8.6 11.4 5.4 12.7 3.1 C15.6 4.2 18.2 6.6 18.9 9.4 C17.2 11.2 14.6 12 12 12 Z"/>`).join('');
|
||||
|
||||
function sysmonMarkup() {
|
||||
return `
|
||||
@@ -96,16 +99,20 @@ function sysmonMarkup() {
|
||||
</svg>
|
||||
<span class="sys-val mono" data-disk-pct>–</span>
|
||||
<div class="sys-pop">
|
||||
<div class="sys-pop-h">Speicherplatz</div>
|
||||
<div class="sys-row"><span>Belegt</span><b data-disk-used>–</b></div>
|
||||
<div class="sys-row"><span>Frei</span><b data-disk-free>–</b></div>
|
||||
<div class="sys-row"><span>Gesamt</span><b data-disk-total>–</b></div>
|
||||
<div class="sys-pop-h">Speicher für Aufnahmen</div>
|
||||
<div class="sys-row"><span>Aufnahmen</span><b data-disk-used>–</b></div>
|
||||
<div class="sys-bar"><i data-disk-bar style="width:0%"></i></div>
|
||||
<div class="sys-row"><span>Noch frei</span><b data-disk-free>–</b></div>
|
||||
<div class="sys-row"><span>Gesamt für Aufnahmen</span><b data-disk-total>–</b></div>
|
||||
<div class="sys-note">100 % = freier Platz + alle vorhandenen Aufnahmen<br>(also der Platz eines frisch aufgesetzten Systems)</div>
|
||||
<div class="sys-row sys-sub"><span>Partition gesamt</span><b data-disk-part>–</b></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sys-chip" id="sysload" tabindex="0" role="group" aria-label="Systemlast">
|
||||
<svg class="turbine" viewBox="0 0 24 24" aria-hidden="true" data-turbine>
|
||||
<g class="tb-blades">${TURBINE_BLADES}</g>
|
||||
<circle class="tb-hub" cx="12" cy="12" r="2.2"/>
|
||||
<svg class="fan" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle class="fan-case" cx="12" cy="12" r="10.4"/>
|
||||
<g class="fan-blades" data-turbine>${FAN_BLADES}</g>
|
||||
<circle class="fan-hub" cx="12" cy="12" r="2.1"/>
|
||||
</svg>
|
||||
<div class="sys-pop">
|
||||
<div class="sys-pop-h">Systemlast</div>
|
||||
@@ -142,15 +149,19 @@ function paintSysmon(s) {
|
||||
}
|
||||
|
||||
if (s.disk) {
|
||||
// Prozent = Aufnahmen / (freier Platz + Aufnahmen). Das System selbst zählt
|
||||
// bewusst nicht mit: gezeigt wird, wie viel vom Aufnahme-Vorrat weg ist.
|
||||
const p = Math.round(s.disk.usedPct);
|
||||
disk.className = 'sys-chip ' + sysLevel(p);
|
||||
disk.title = `Speicher ${p} % belegt`;
|
||||
disk.title = `Aufnahmen belegen ${p} % des verfügbaren Platzes`;
|
||||
set('[data-disk-pct]', p + '%');
|
||||
const ring = document.querySelector('[data-disk-ring]');
|
||||
if (ring) ring.setAttribute('stroke-dasharray', `${Math.max(0, Math.min(100, s.disk.usedPct)).toFixed(1)} 100`);
|
||||
set('[data-disk-used]', sysGb(s.disk.usedBytes));
|
||||
const db = document.querySelector('[data-disk-bar]'); if (db) db.style.width = Math.min(100, s.disk.usedPct) + '%';
|
||||
set('[data-disk-used]', `${sysGb(s.disk.recBytes)}${s.disk.recFiles ? ` (${s.disk.recFiles} Dateien)` : ''}`);
|
||||
set('[data-disk-free]', sysGb(s.disk.freeBytes));
|
||||
set('[data-disk-total]', sysGb(s.disk.totalBytes));
|
||||
set('[data-disk-total]', sysGb(s.disk.budgetBytes));
|
||||
set('[data-disk-part]', `${sysGb(s.disk.totalBytes)} — ${Math.round(s.disk.partPct)} % belegt`);
|
||||
}
|
||||
|
||||
const cpu = s.cpu ? s.cpu.pct : 0;
|
||||
|
||||
+12
-7
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user