feat(gui): Systemanzeige in der Kopfzeile (Speicherring + Turbine)

Zwei Symbole zwischen Bluetooth-Tab und Hell/Dunkel-Schalter: ein Ring mit
der Plattenbelegung in Prozent und eine Turbine, deren Drehzahl der CPU-Last
folgt. Beide klappen bei Mauskontakt eine Karte mit Zahlen auf (belegt/frei/
gesamt bzw. CPU, RAM, Temperatur).

Neues Modul sysstat.js liest alles aus /proc bzw. fs.statfsSync, damit die
GUI im Sekundentakt fragen kann, ohne df/top-Prozesse zu starten. CPU-% aus
zwei /proc/stat-Messpunkten mit eigenem 2-s-Ticker, RAM ueber MemAvailable,
Temperatur als waermste SoC-/Core-Zone.

Doku: Kapitel 07 + Changelog.
This commit is contained in:
Claude
2026-08-17 21:52:21 +02:00
parent 0f530b575a
commit 47288c0479
6 changed files with 334 additions and 1 deletions
+28 -1
View File
@@ -74,13 +74,40 @@ tune2fs -O ^orphan_file,^metadata_csum_seed /dev/mmcblk0p1 && e2fsck -fy /dev/mm
| Pfad | Inhalt |
|---|---|
| `/opt/callrec/*.js` | server / recorder / llm / diar / spk / db / bt |
| `/opt/callrec/*.js` | server / recorder / llm / diar / spk / db / bt / sysstat |
| `/opt/callrec/web/` | Vite-Frontend (`npx vite build` nach Änderungen an `src/`) |
| `/opt/callrec/aime-*.sh` | Setup-/Bridge-/Cap-Skripte |
| `/opt/callrec/milestones/m1-20260709/` | Meilenstein-1-Snapshot inkl. MANIFEST.md (Rollback) |
| `/opt/callrec/*.bak-*` | Datei-Backups je Änderung (z. B. `.bak-structsum`, `.bak-anchor`) |
| `/etc/sudoers.d/callrec-cpucap` | NOPASSWD für `aime-cpucap.sh` (Thermal-Cap) |
## Systemanzeige in der Kopfzeile (seit 2026-08-17)
Zwischen dem Tab „Bluetooth“ und dem Hell/Dunkel-Schalter stehen zwei Symbole, die den Zustand des
Boards ohne SSH zeigen:
| Symbol | Zeigt an | Klappkarte bei Mauskontakt |
|---|---|---|
| Ring + Prozentzahl | Belegung der Partition mit den Aufnahmen (`/`, eMMC) | Belegt / Frei / Gesamt in GB |
| Turbine | CPU-Last — je höher, desto schneller die Drehung | CPU % (+ Kernzahl), RAM % mit GB, Temperatur mit Zone |
- Datenquelle: `GET /api/sysstat` (`app/sysstat.js`). Alles aus `/proc` bzw. `fs.statfsSync` — **kein**
`df`/`top`-Aufruf, damit die GUI im Sekundentakt fragen darf, ohne Prozesse zu starten.
- CPU-% aus der Differenz zweier `/proc/stat`-Messpunkte. `sysstat.js` misst dafür **selbst** alle 2 s
(`setInterval(...).unref()`); ohne diesen eigenen Ticker wäre die erste Antwort nach jedem Start 0 %.
- RAM rechnet mit `MemAvailable`, nicht `MemFree` — sonst stünde das Board wegen Cache dauernd bei 95 %.
- Temperatur = wärmste der SoC-/Core-/Center-/GPU-/NPU-Zonen; `iwlwifi_1` (WLAN-Karte) fliegt raus, weil
ihr Wert nichts über die Rechenlast sagt.
- Frontend: `sysmonMarkup()` / `paintSysmon()` / `tickSysmon()` in `web/src/main.js`, Poll alle 5 s, im
Hintergrund-Tab (`document.hidden`) ausgesetzt. Ein einziger Ticker für alle `render()`-Durchläufe;
nach jedem `render()` werden die zuletzt geholten Werte sofort zurückgemalt, weil `render()` die
Kopfzeile komplett neu schreibt.
- Drehzahl der Turbine läuft in **Stufen** (`spinSeconds()`: 7 s bei Leerlauf … 0,35 s ab 90 %). Eine
stufenlos gesetzte `animation-duration` würde die laufende Drehung bei jedem Poll neu ansetzen — das
ruckt sichtbar. `prefers-reduced-motion` stoppt die Animation ganz.
- Farbe: grün < 75 %, gelb ab 75 %, rot ab 90 %; bei der Turbine zählt der schlimmste Wert aus CPU, RAM
und Temperatur (Temperatur skaliert 45 °C → 0 %, 90 °C → 100 %).
## Deploy-Ablauf (von Windows aus)
```mermaid
+33
View File
@@ -3,6 +3,39 @@
Chronik der Entwicklungsschritte. **Regel: jede zukünftige Änderung bekommt hier einen Eintrag** (Datum,
Was, Warum, betroffene Dateien/Backups).
## 2026-08-17 — Systemanzeige in der Kopfzeile: Speicherring + Turbine
**Wunsch:** zwischen dem Bluetooth-Tab und dem Hell/Dunkel-Schalter eine Anzeige, die den belegten
Speicherplatz in Prozent zeigt, dazu ein zweites Symbol, das bei Mauskontakt CPU-Last, RAM und
Temperatur aufklappt — als Turbine, die sich mit steigender Last schneller dreht.
**Umsetzung:**
- Neues Modul `sysstat.js` mit `status()`: Platte über `fs.statfsSync` (gemessen wird die Partition mit
den Aufnahmen, Prozentwert wie `df` inklusive root-Reserve), RAM aus `/proc/meminfo`
(`MemAvailable`, nicht `MemFree`), CPU-% aus der Differenz zweier `/proc/stat`-Messpunkte, Temperatur
als wärmste SoC-/Core-/Center-/GPU-/NPU-Zone (WLAN-Zone `iwlwifi_1` ausgeschlossen).
Eigener 2-s-Ticker (`unref()`), sonst wäre die erste CPU-Antwort nach jedem Start 0 %.
- `GET /api/sysstat` in `server.js` — ohne `wrap()`/Warteschlange, weil es reine `/proc`-Lesevorgänge
sind und die Abfrage keinen Prozess startet.
- GUI (`web/src/main.js`): `sysmonMarkup()` zeichnet beide Chips, `paintSysmon()` schreibt die Werte ins
DOM, `tickSysmon()` pollt alle 5 s (im Hintergrund-Tab ausgesetzt). Ein einziger Ticker für alle
`render()`-Läufe; direkt nach `render()` werden die letzten Werte zurückgemalt, weil `render()` die
Kopfzeile komplett neu schreibt und die Chips sonst bis zum nächsten Poll leer stünden.
- Turbine: fünf Flügel als SVG-Pfade, Drehdauer per CSS-Variable `--spin`. Die Dauer läuft bewusst in
**Stufen** (7 s Leerlauf … 0,35 s ab 90 %) — eine stufenlos nachgeführte `animation-duration` setzt
die laufende Drehung bei jedem Poll neu an und ruckt sichtbar. `prefers-reduced-motion` stoppt sie.
- Speicherring: `<circle r="15.9155">` (Umfang exakt 100) ⇒ `stroke-dasharray` = Prozentwert.
- Ampel: grün < 75 %, gelb ab 75 %, rot ab 90 %; bei der Turbine zählt der schlimmste Wert aus CPU, RAM
und Temperatur (45 °C → 0 %, 90 °C → 100 %).
- Karten öffnen rein über CSS (`.sys-chip:hover .sys-pop`, zusätzlich `:focus-within` für Tastatur) —
kein Handler, der einen `render()`-Durchlauf überleben müsste.
**Dateien:** neu `app/sysstat.js`; geändert `app/server.js` (`.bak-sysmon`), `app/web/src/main.js`
(`.bak-sysmon`), `app/web/src/style.css` (`.bak-sysmon`). Vite-Build + `systemctl restart callrec`
(vorher geprüft: keine Analyse `pending`). Verifiziert: `/api/sysstat` liefert 52,4 % Platte
(`df` sagt 53 %), 46 °C, 8 Kerne. Doku: Kapitel 07.
## 2026-08-17 — Kontext zum Gespräch: ein Freitextfeld nach dem Stop
**Wunsch:** nach dem Beenden einer Aufnahme die Möglichkeit haben, Kontext zum gerade geführten