Ein neues Status-Icon im Titelbalken zeigt den Zustand der lokalen Transkripte und der Serverabfrage; ein Klick oeffnet ein eigenes Fenster mit persistiertem, zeitgestempeltem Ereignis-/Fehlerprotokoll (loeschbar). Ausserdem erkennt die App beim Start Erstinstallation vs. Update (plattformunabhaengig fuer Windows und Linux) und meldet Updates per nativer Benachrichtigung. Dazu: Versionierungs-Policy dokumentiert (package.json vor jedem Release erhoehen, sonst greift weder die Update-Erkennung noch bleiben alte Release-Dateien in release/ erhalten), sowie zwei neue Dokumente fuer Endanwender (BENUTZERHANDBUCH.md) und eine Download-Webseite (PRODUKTSEITE.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
366 lines
15 KiB
Markdown
366 lines
15 KiB
Markdown
# Claude Live Dashboard
|
||
|
||
Ein rahmenloses Always-on-Top-Widget für Windows und Linux, das die Auslastung des
|
||
Claude-Plans live anzeigt: 5-Stunden-Fenster, Wochenverbrauch, Reset-Countdown,
|
||
Tokenverbrauch je Modell und die Hochrechnung auf das Fensterende.
|
||
|
||
Die Daten stammen aus den Transkripten, die Claude Code ohnehin unter
|
||
`~/.claude/projects/**/*.jsonl` schreibt. Es wird nichts installiert, nichts
|
||
mitgeschnitten und standardmäßig keine Netzwerkverbindung aufgebaut.
|
||
|
||
## Start
|
||
|
||
Node.js (ab Version 20) wird benötigt.
|
||
|
||
### Windows
|
||
|
||
Ist Node nicht im `PATH` (etwa bei nvm-windows ohne aktive Version), genügt der
|
||
Versionspfad:
|
||
|
||
```powershell
|
||
$env:PATH = "$env:APPDATA\nvm\v22.22.1;$env:PATH"
|
||
npm install
|
||
npm start
|
||
```
|
||
|
||
Alternativ `start.cmd` doppelklicken.
|
||
|
||
Das Tray-Symbol landet unter Windows zunächst im Überlaufbereich hinter dem
|
||
`^` in der Taskleiste; per Drag lässt es sich dauerhaft sichtbar machen.
|
||
|
||
### Linux (Ubuntu)
|
||
|
||
```bash
|
||
sudo apt install nodejs npm # falls noch nicht vorhanden
|
||
./start.sh
|
||
```
|
||
|
||
`start.sh` installiert fehlende Abhängigkeiten selbst und startet Electron;
|
||
`npm install && npm start` tut dasselbe.
|
||
|
||
Das Tray-Symbol braucht unter GNOME die Erweiterung **AppIndicator** — beim
|
||
Ubuntu-Desktop ist sie vorinstalliert und aktiv. Auf einer Minimalinstallation
|
||
oder unter anderen Oberflächen:
|
||
|
||
```bash
|
||
sudo apt install gnome-shell-extension-appindicator
|
||
```
|
||
|
||
Ohne sie läuft das Widget weiter, nur das Symbol in der oberen Leiste fehlt.
|
||
Anders als unter Windows ist im StatusNotifierItem-Standard nicht festgelegt,
|
||
welcher Klick als Aktivierung zählt — verlässlich ist deshalb das Kontextmenü
|
||
(Rechtsklick) mit „Dashboard anzeigen“ als erstem Eintrag.
|
||
|
||
**Wayland:** Ab Electron 36 wählt Chromium in einer Wayland-Sitzung von sich
|
||
aus Wayland — dort darf sich ein Fenster aber weder selbst positionieren noch
|
||
dauerhaft in den Vordergrund legen, und genau das ist der Kern dieses Widgets.
|
||
Die App startet sich deshalb unter Wayland einmalig selbst mit
|
||
`--ozone-platform=x11` neu und läuft über XWayland. Das geschieht automatisch,
|
||
es ist nichts einzustellen.
|
||
|
||
Der Schalter *muss* auf der Kommandozeile stehen: Ozone legt die Plattform
|
||
fest, bevor das Main-Skript läuft — `app.commandLine.appendSwitch()` und
|
||
`ELECTRON_OZONE_PLATFORM_HINT` kommen beide zu spät. Wer die Automatik
|
||
umgehen will, setzt eine der beiden Größen von Hand; dann greift der Neustart
|
||
nicht mehr:
|
||
|
||
```bash
|
||
ELECTRON_OZONE_PLATFORM_HINT=wayland ./start.sh # nativ Wayland, ohne Positionierung
|
||
```
|
||
|
||
## Bedienung
|
||
|
||
| Aktion | Wirkung |
|
||
|---|---|
|
||
| Kopfzeile ziehen | Fenster verschieben (Position wird gemerkt) |
|
||
| Statuspunkt (links vom Zahnrad) | Fehler-/Ereignis-Log öffnen |
|
||
| ⚙ | Einstellungen öffnen |
|
||
| „Details“ | Modell-Aufschlüsselung, Tempo und Datengrundlage aufklappen |
|
||
| `–` | Ausblenden — Rückkehr über das Tray-Symbol |
|
||
| Tray-Klick | Ein-/Ausblenden |
|
||
| Tray-Menü | Deckkraft, Klick-Durchlässigkeit, Autostart, Einstellungen, Beenden |
|
||
|
||
### Einstellungen
|
||
|
||
Das Zahnrad in der Kopfzeile öffnet einen Dialog für alle Felder der
|
||
`config.json`: Deckkraft, Klick-Durchlässigkeit, Autostart, Serverabfrage,
|
||
Bezugsgrößen, Gewichtung und Wochenfenster.
|
||
|
||
Gespeicherte Änderungen greifen **sofort** — ohne Neustart. Der Dialog liest
|
||
und schreibt dieselbe Datei, die sich auch von Hand bearbeiten lässt:
|
||
Kommentarfelder (`_hinweis` und Verwandte) und unbekannte Einträge bleiben
|
||
dabei unangetastet, weil die vorhandene Datei nur ergänzt und nicht ersetzt
|
||
wird. `Strg`+`S` speichert, `Esc` schließt.
|
||
|
||
Autostart ist kein Feld der Datei, sondern ein Systemzustand — er wird deshalb
|
||
sofort beim Umschalten übernommen, unabhängig vom Speichern.
|
||
|
||
„Klicks durchlassen“ macht das Widget für Mausereignisse transparent — es liegt
|
||
dann über der Arbeit, ohne sie zu behindern. Zurückschalten geht über das
|
||
Tray-Menü.
|
||
|
||
### Endpunkt-Status & Ereignis-Log
|
||
|
||
Der Punkt links vom Zahnrad zeigt den Zustand der beiden Datenquellen —
|
||
Symbol *und* Farbe, nie Farbe allein:
|
||
|
||
| Zustand | Bedeutung |
|
||
|---|---|
|
||
| ● grün | Beide Quellen funktionieren |
|
||
| ◆ gelb | Serverabfrage gestört (App rechnet lokal weiter) |
|
||
| ◆ rot | Lokale Transkripte nicht lesbar — die Kerndatenquelle fehlt |
|
||
|
||
Ein Klick öffnet ein eigenes Fenster mit dem chronologischen, zeitgestempelten
|
||
Protokoll aller Störungen (Lesefehler, Dateiüberwachung, Serverabfrage) sowie
|
||
App-Ereignissen wie Erstinstallation und Updates. Das Protokoll übersteht
|
||
Neustarts und das Speichern der Einstellungen — es liegt in `state.json` — und
|
||
lässt sich über „Log löschen“ im selben Fenster zurücksetzen.
|
||
|
||
## Was angezeigt wird
|
||
|
||
**Laufende Sitzung** — Auslastung des aktuellen 5-Stunden-Fensters, dazu
|
||
verbrauchte Tokens und Tempo.
|
||
|
||
**Wochenkontingent** — das übergeordnete Limit über alle Modelle, mit exaktem
|
||
Reset-Zeitpunkt.
|
||
|
||
**Nutzungsguthaben** — der Budget-Zustand jenseits des Plan-Kontingents
|
||
(verbraucht von Monatslimit). Erscheint nur, wenn der Server ihn meldet.
|
||
|
||
Darunter die eigentliche Kernaussage: *wie lange das Kontingent bei diesem
|
||
Tempo noch reicht* — gerechnet in **aktiver Arbeitszeit**, nicht in
|
||
Kalenderzeit. Eine Hochrechnung der Burn-Rate auf fünf Kalendertage wäre
|
||
Unsinn, weil niemand durchgehend arbeitet.
|
||
|
||
## Woher die Werte kommen
|
||
|
||
Standardmäßig fragt das Dashboard denselben Endpunkt ab, den auch `/usage`
|
||
nutzt (`GET /api/oauth/usage` bei `api.anthropic.com`). Damit sind die
|
||
Prozentwerte, Schweregrade und Reset-Zeitpunkte **exakt** — sekundengenau und
|
||
ohne Schätzung.
|
||
|
||
Das Zugriffstoken wird aus `~/.claude/.credentials.json` gelesen, nie
|
||
gespeichert, nie protokolliert und nur an diesen Host gesendet. Es gibt bewusst
|
||
keinen eigenen Refresh-Flow: Läuft das Token ab, pausiert die Abfrage, bis
|
||
Claude Code es erneuert hat. Nach drei Fehlversuchen schaltet sie sich für die
|
||
Sitzung ab.
|
||
|
||
Abschalten über `config.json`:
|
||
|
||
```json
|
||
{ "oauth": { "enabled": false } }
|
||
```
|
||
|
||
**Wichtig:** Der Endpunkt ist nicht dokumentiert und kann durch ein Update
|
||
brechen. Deshalb rechnet das Dashboard *immer* auch lokal weiter — fällt die
|
||
Abfrage aus, läuft es ohne Unterbrechung auf den eigenen Zahlen weiter, nur
|
||
eben mit Näherungswerten. Die Anzeige sagt jederzeit, welcher Fall vorliegt:
|
||
|
||
| Anzeige | Bedeutung |
|
||
|---|---|
|
||
| **exakt (Server)** | Wert vom Server, entspricht `/usage` — nur mit Stufe 2 |
|
||
| **eigener Wert** | `limits` aus `config.json` |
|
||
| **kalibriert** | aus mindestens 5 eigenen abgeschlossenen Fenstern abgeleitet |
|
||
| **geschätzt** | noch zu wenige Messungen, grober Anhaltspunkt |
|
||
|
||
Solange die Serverabfrage läuft, kalibriert sich das Dashboard **selbst**: Der
|
||
Server nennt den exakten Anteil, lokal ist der gewichtete Verbrauch im selben
|
||
Fenster bekannt — daraus folgt die Bezugsgröße unmittelbar. Auch das
|
||
Wochenfenster wird aus dem gemeldeten Reset-Zeitpunkt abgeleitet, nicht
|
||
geraten.
|
||
|
||
Die folgenden Abschnitte betreffen nur den Fall, dass die Abfrage **nicht**
|
||
verfügbar ist.
|
||
|
||
### Kalibrieren (Rückfallebene)
|
||
|
||
Ohne Serverabfrage nimmt das Dashboard das höchste je beobachtete
|
||
Fenstervolumen als untere Schranke des Limits. Das ist grob. Besser: den
|
||
Prozentwert in **Einstellungen ▸ Nutzung** ablesen und unmittelbar danach:
|
||
|
||
```powershell
|
||
npm run calibrate -- --week 14 # nur rechnen
|
||
npm run calibrate -- --week 14 --write # in config.json übernehmen
|
||
npm run calibrate -- --block 37 --write # 5-Stunden-Fenster
|
||
```
|
||
|
||
Das Werkzeug teilt den aktuellen Verbrauch durch den angezeigten Anteil und
|
||
erhält so die Bezugsgröße exakt. Danach das Dashboard neu starten.
|
||
|
||
Steht in der Nutzungsanzeige ein fester Reset-Zeitpunkt („Zurücksetzung Mo.,
|
||
21:59“), gehört er in `config.json` — sonst passt das Fenster nicht zum
|
||
kalibrierten Wert:
|
||
|
||
```json
|
||
{ "weekResetWeekday": 1, "weekResetHour": 21, "weekResetMinute": 59 }
|
||
```
|
||
|
||
(0 = Sonntag, 1 = Montag …) Ohne diese Angabe rechnet das Dashboard mit einem
|
||
rollierenden 7-Tage-Fenster und zeigt bewusst keinen Countdown, weil ein
|
||
rollierendes Fenster keinen Reset-Zeitpunkt hat.
|
||
|
||
### Gewichtung
|
||
|
||
Tokens zählen nicht gleich: Ausgabe wiegt stärker als ein Cache-Treffer, Opus
|
||
stärker als Sonnet oder Haiku. Die Faktoren stehen in `config.json`.
|
||
|
||
Weichen zwei Kalibrierungen mit **unterschiedlichem Modellmix** deutlich
|
||
voneinander ab, stimmen diese Gewichte nicht — dann dort nachziehen, statt das
|
||
Limit hin- und herzuschieben. Ein einzelner Kalibrierpunkt legt nur den
|
||
Maßstab fest, nicht die Gewichte.
|
||
|
||
## Aufbau
|
||
|
||
```
|
||
src/main/index.js Fenster, Tray, IPC, Autostart
|
||
src/main/collector.js Watch-Schleife, Zustandsaufbau
|
||
src/main/jsonl.js inkrementeller Reader, Parser, Dedup
|
||
src/main/blocks.js 5h-Blöcke, Wochenfenster, Burn-Rate
|
||
src/main/limits.js Plan-Erkennung, Kalibrierung, Gewichtung
|
||
src/main/oauth-usage.js Serverabfrage, gekapselt und ausfallsicher
|
||
src/main/calibrate.js Kalibrierung gegen die echte Anzeige
|
||
src/main/store.js state.json, config.json
|
||
src/main/paths.js Speicherorte für Konfiguration und Zustand
|
||
src/main/autostart.js Anmeldestart je Betriebssystem
|
||
src/preload.js contextBridge fürs Widget
|
||
src/settings-preload.js contextBridge für den Einstellungsdialog
|
||
src/errors-preload.js contextBridge fürs Fehler-/Ereignis-Log
|
||
src/renderer/ Widget-Oberfläche
|
||
src/settings/ Einstellungsdialog
|
||
src/errors/ Fehler-/Ereignis-Log-Fenster
|
||
```
|
||
|
||
Alle Dateizugriffe und Rechnungen passieren im Main-Prozess; der Renderer
|
||
bekommt fertige Zahlen und lässt nur den Countdown lokal weiterlaufen.
|
||
|
||
Zwei Details, die nicht offensichtlich sind:
|
||
|
||
- **Datei-Offsets werden nicht persistiert.** Innerhalb einer Sitzung sorgen sie
|
||
dafür, dass nur das Delta gelesen wird. Über einen Neustart hinweg wären sie
|
||
schädlich, weil der Aggregator leer startet und die Historie sonst
|
||
übersprungen würde. Ein Vollscan über 15 MB kostet rund 70 ms.
|
||
- **Dedup ist nicht optional.** Claude Code schreibt dieselbe Nachricht bei
|
||
Retries und beim Kompaktieren mehrfach ins Transkript; ohne Dedup über
|
||
`message.id` wären alle Zahlen zu hoch.
|
||
|
||
## Version & Updates
|
||
|
||
Die App erkennt beim Start selbst, ob es sich um eine Erstinstallation oder
|
||
ein Update handelt: Sie vergleicht `app.getVersion()` (aus `package.json`,
|
||
plattformunabhängig für Windows und Linux) mit der zuletzt in `state.json`
|
||
gemerkten Version. Bei einer neuen Version erscheint einmalig eine native
|
||
Systembenachrichtigung, und das Ereignis landet zeitgestempelt im
|
||
Fehler-/Ereignis-Log (siehe oben). Bestehende Konfiguration und Kalibrierung
|
||
bleiben davon unberührt — geschrieben wird ausschließlich das eine Feld
|
||
`state.json:appVersion`.
|
||
|
||
**Voraussetzung dafür ist eine bei jedem Release erhöhte Versionsnummer.**
|
||
Vor jedem Build deshalb zuerst `version` in `package.json` erhöhen (SemVer:
|
||
`1.1.0` → `1.2.0` für neue Funktionen, `1.1.1` für Fehlerbehebungen). Ohne
|
||
diesen Schritt erkennt die App den neuen Build nicht als Update, und der
|
||
Installer überschreibt in `release/` stillschweigend die Datei der
|
||
vorherigen Version, weil deren Name (`artifactName`) die Versionsnummer
|
||
enthält:
|
||
|
||
```powershell
|
||
npm version minor # oder: patch / major — schreibt package.json, kein Tag/Commit nötig
|
||
npm run dist
|
||
```
|
||
|
||
`npm version` legt standardmäßig auch einen Git-Commit und -Tag an; ohne das
|
||
genügt es, `version` in `package.json` von Hand zu ändern.
|
||
|
||
## Release bauen
|
||
|
||
Gebaut wird jeweils auf dem Zielsystem — electron-builder kann `.deb` nicht
|
||
sinnvoll von Windows aus erzeugen und umgekehrt keine signierten `.exe` unter
|
||
Linux. Beide Linux-Ziele (`AppImage`, `.deb`) brauchen zudem Linux-eigene
|
||
Werkzeuge (`mksquashfs` bzw. `fpm`) und lassen sich nicht unter Windows
|
||
erzeugen, auch nicht über WSL ohne eine dort eingerichtete Node.js- bzw.
|
||
Ruby-Umgebung.
|
||
|
||
### Windows
|
||
|
||
```powershell
|
||
npm run dist # Installer + portable Fassung nach release/
|
||
npm run pack # nur entpackt nach release/win-unpacked (schneller Test)
|
||
```
|
||
|
||
Ergebnis in `release/` (Version aus `package.json`, siehe oben):
|
||
|
||
| Datei | Zweck |
|
||
|---|---|
|
||
| `Claude Live Dashboard-<version>-x64.exe` | NSIS-Installer, ~97 MB |
|
||
| `Claude Live Dashboard-<version>-portable.exe` | läuft ohne Installation |
|
||
|
||
Der Installer läuft **ohne Administratorrechte** (`perMachine: false`),
|
||
installiert also ins Benutzerprofil. Zielverzeichnis ist wählbar, Desktop- und
|
||
Startmenü-Verknüpfung werden angelegt. Bei der Deinstallation bleiben
|
||
Konfiguration und Kalibrierung erhalten (`deleteAppDataOnUninstall: false`).
|
||
|
||
**Nicht signiert.** Ohne Code-Signing-Zertifikat zeigt Windows beim ersten
|
||
Start einen SmartScreen-Hinweis („Der Computer wurde durch Windows geschützt“)
|
||
— über *Weitere Informationen ▸ Trotzdem ausführen* zu bestätigen. Für eine
|
||
Weitergabe an andere wäre ein Zertifikat nötig; für den Eigenbedarf ist es
|
||
verzichtbar.
|
||
|
||
### Linux
|
||
|
||
```bash
|
||
npm run dist:linux # AppImage + .deb nach release/
|
||
npm run pack:linux # nur entpackt nach release/linux-unpacked
|
||
```
|
||
|
||
Ergebnis in `release/`:
|
||
|
||
| Datei | Zweck |
|
||
|---|---|
|
||
| `Claude Live Dashboard-<version>-x64.AppImage` | läuft ohne Installation |
|
||
| `Claude Live Dashboard-<version>-amd64.deb` | Installation per `sudo apt install ./…deb` |
|
||
|
||
Das AppImage muss einmal ausführbar gemacht werden (`chmod +x`) und braucht
|
||
`libfuse2`. Es trägt sich nicht selbst ins Anwendungsmenü ein — der Autostart
|
||
über das Tray-Menü funktioniert trotzdem, weil die App den Pfad des Abbilds aus
|
||
`APPIMAGE` liest und nicht den temporären Einhängepunkt. Das `.deb` bringt
|
||
Menüeintrag und Symbol mit und installiert nach `/opt/Claude Live Dashboard`.
|
||
|
||
> **Ubuntu 24.04 und AppImage.** Seit 24.04 beschränkt AppArmor unprivilegierte
|
||
> User-Namespaces; die Chromium-Sandbox kann dann beim Start fehlschlagen
|
||
> (`SUID sandbox helper binary … is not configured correctly`). Zwei Auswege:
|
||
> das AppImage mit `--no-sandbox` starten, oder ein AppArmor-Profil unter
|
||
> `/etc/apparmor.d/` hinterlegen. Beim `.deb` tritt das nicht auf — dessen
|
||
> `postinst` setzt das SUID-Bit auf `chrome-sandbox`.
|
||
|
||
### Konfiguration und Zustand
|
||
|
||
Beides liegt im Benutzerprofil, nicht im Programmverzeichnis — dort wäre es
|
||
nach der Installation im asar-Archiv und schreibgeschützt:
|
||
|
||
```
|
||
Windows: %APPDATA%\Claude Live Dashboard\config.json
|
||
%APPDATA%\Claude Live Dashboard\state.json
|
||
|
||
Linux: ~/.config/Claude Live Dashboard/config.json
|
||
~/.config/Claude Live Dashboard/state.json
|
||
```
|
||
|
||
Der Autostart ist kein Feld dieser Dateien: unter Windows steckt er in der
|
||
Registry, unter Linux in `~/.config/autostart/claude-live-dashboard.desktop`.
|
||
|
||
Beim ersten Start wird `config.json` aus der mitgelieferten Vorlage angelegt.
|
||
Erreichbar über das Tray-Menü (*Konfiguration öffnen* / *Datenordner öffnen*).
|
||
|
||
> Die mitgelieferte Vorlage enthält unter `limits` einen auf **diesem** System
|
||
> kalibrierten Wert als Rückfallebene. Für eine Weitergabe an andere sollte er
|
||
> entfernt werden — bei aktiver Serverabfrage wird er ohnehin nicht benutzt.
|
||
|
||
## Gegenprobe
|
||
|
||
```powershell
|
||
npm run dump # Modell-Summen und alle 5h-Blöcke auf der Konsole
|
||
npx ccusage blocks # unabhängige Referenz für Blockgrenzen und Summen
|
||
```
|
||
|
||
`npm run dump` gibt außerdem die Dauer des zweiten Polls aus — ohne neue Daten
|
||
muss er 0 Bytes lesen und im einstelligen Millisekundenbereich liegen.
|