umbau(boxwart): neue Oberflaeche im Cockpit-Stil (Richtung A), Werkzeuge angehoben

Drei Seiten statt zehn: Start, Updates, Modelle — am PC mit Kopfnavigation, am Handy
mit Leiste unten. Umsetzung von Mockup A („Cockpit“), vom User am 23.09. gewaehlt.

- Start: Hauptwarnleuchte + 8 Warnlampen, Rundinstrumente mit Live-Werten aus dem
  Strom (Speicher, Temperatur, Platte) und Laufzeit-Zaehlwerk, Checkliste der
  Waechter-Hinweise mit ihren Knoepfen, Flugplan (heute gelaufen / geplant), Radar-Kasten.
- Updates: Bausteine mit „Laeuft → Neu“ und Zusammenfassung, laufende Auftraege,
  Verlauf der Sonntagslaeufe (neu: GET /api/updates/verlauf), Sicherungen samt
  Zurueckspielen mit Rueckfrage.
- Modelle: Speicherbalken, Rollen Hirn/Coder/Dritte Rolle, wer die Modelle nutzt
  (7 Tage + 24 h je Stunde), Modell-Radar, weitere Eintraege, Modelle selbst suchen.
- Schubladen: Dienste mit Protokoll und Neustart, Einstellungen (HF-Zugang), Hermes-Link.
- Werkzeuge: Vite 8, React 19.3 mit React Compiler 1.0 (Babel), vitest 5, Tailwind 4.3,
  shadcn 4 (Radix) fuer Dialog/Schublade/Knopf, Schriften Barlow/Barlow Condensed/
  JetBrains Mono. Entfernt: recharts, cmdk, Kraftgraph, zustand, Inter, Space Grotesk.
- Startbuendel 115 KB gzip (Budget 140); Updates/Modelle/Schubladen laden bei Bedarf.
- 16 Oberflaechen-Tests (Instrument-Bogen, Hauptleuchte, Zeitformate, Versionen).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-23 21:39:07 +02:00
co-authored by Claude Opus 5.5
parent 3d2c9549fb
commit 907289d7dc
223 changed files with 8572 additions and 15393 deletions
+22
View File
@@ -235,6 +235,28 @@ def _letzter_lauf(job_id: str) -> dict | None:
return {"status": row[0], "started_at": row[1], "finished_at": row[2], "error": row[3]}
def update_laeufe(anzahl: int = 8) -> list[dict]:
"""Letzte Läufe aller Update-Jobs (Name enthält „update“), neueste zuerst."""
ids = {str(j.get("id")): str(j.get("name", "")) for j in _hermes_jobs()
if "update" in str(j.get("name", "")).lower()}
if not ids or not HERMES_EXEC_DB.exists():
return []
platz = ",".join("?" * len(ids))
try:
con = sqlite3.connect(f"file:{HERMES_EXEC_DB}?mode=ro", uri=True, timeout=2)
try:
rows = con.execute(
f"select job_id, status, started_at, finished_at, error from executions "
f"where job_id in ({platz}) order by started_at desc limit ?",
(*ids, anzahl)).fetchall()
finally:
con.close()
except sqlite3.Error:
return []
return [{"job": ids.get(r[0], r[0]), "status": r[1], "start": r[2], "ende": r[3],
"fehler": (r[4] or None)} for r in rows]
def werkzeugfehler_im_lauf(zeilen: list[str], job_id: str, start_iso: str) -> tuple[int, str]:
"""Zählt 'Tool X returned error'-Zeilen eines Laufs in errors.log. Hermes markiert jede
Zeile mit [cron_<job>_<JJJJMMTT>_<HHMMSS>]; der Tag des Laufstarts reicht zur Zuordnung."""