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:
co-authored by
Claude Opus 5.5
parent
3d2c9549fb
commit
907289d7dc
@@ -0,0 +1,78 @@
|
||||
// Reine Anzeige-Logik der Cockpit-Seiten — getrennt von den Komponenten, damit sie ohne
|
||||
// DOM testbar bleibt und Vites Hot-Reload die Komponenten-Dateien sauber tauschen kann.
|
||||
|
||||
import type { UpdateDetails } from "./abfragen"
|
||||
import type { Modell, Start } from "./typen"
|
||||
|
||||
// --- Rundinstrument -----------------------------------------------------------------
|
||||
// 240°-Bogen wie ein Zeigerinstrument, Mittelpunkt (100|95) in einer 200×150-Zeichnung.
|
||||
const MX = 100
|
||||
const MY = 95
|
||||
const START = 210 // Grad, links unten
|
||||
const SPANNE = 240
|
||||
|
||||
function punkt(grad: number, r: number): [number, number] {
|
||||
const rad = (grad * Math.PI) / 180
|
||||
return [MX + r * Math.cos(rad), MY - r * Math.sin(rad)]
|
||||
}
|
||||
|
||||
const r2 = (n: number) => Math.round(n * 100) / 100
|
||||
|
||||
/** SVG-Pfad eines Bogens von Anteil `von` bis `bis` (0…1) auf Radius `r`. */
|
||||
export function bogenPfad(von: number, bis: number, r: number): string {
|
||||
const a = Math.max(0, Math.min(1, von))
|
||||
const b = Math.max(a, Math.min(1, bis))
|
||||
const [x0, y0] = punkt(START - SPANNE * a, r)
|
||||
const [x1, y1] = punkt(START - SPANNE * b, r)
|
||||
const gross = (b - a) * SPANNE > 180 ? 1 : 0
|
||||
return `M ${r2(x0)} ${r2(y0)} A ${r} ${r} 0 ${gross} 1 ${r2(x1)} ${r2(y1)}`
|
||||
}
|
||||
|
||||
/** Farbe der Temperatur: grün bis 70 °C, dann Bernstein, ab 85 °C rot. */
|
||||
export function temperaturFarbe(grad: number | null | undefined) {
|
||||
if (grad == null) return "var(--text-3)"
|
||||
if (grad >= 85) return "var(--rot)"
|
||||
if (grad >= 70) return "var(--bernstein)"
|
||||
return "var(--gruen)"
|
||||
}
|
||||
|
||||
// --- Hauptwarnleuchte ---------------------------------------------------------------
|
||||
|
||||
/** Was die Hauptwarnleuchte sagt. Der schweigende Wächter geht allem anderen vor. */
|
||||
export function hauptleuchte(z: Start["zustand"]) {
|
||||
if (!z.waechter_wach) {
|
||||
return { art: "stumm" as const, oben: "Wächter", mitte: "SCHWEIGT", unten: "Seit über 5 Minuten kein Prüflauf" }
|
||||
}
|
||||
if (z.update_laeuft) {
|
||||
return { art: "info" as const, oben: "Wartung", mitte: "UPDATE LÄUFT", unten: "Hinweise ruhen bis zum Ende" }
|
||||
}
|
||||
if (z.anzahl === 0) {
|
||||
return { art: "ok" as const, oben: "Alles", mitte: "IN ORDNUNG", unten: "Keine offenen Hinweise" }
|
||||
}
|
||||
const mitte = `${z.anzahl} HINWEIS${z.anzahl === 1 ? "" : "E"}`
|
||||
return z.stufe === "rot"
|
||||
? { art: "rot" as const, oben: "Störung", mitte, unten: "Drücken zum Ansehen" }
|
||||
: { art: "gelb" as const, oben: "Achtung", mitte, unten: "Drücken zum Ansehen" }
|
||||
}
|
||||
|
||||
// --- Updates ------------------------------------------------------------------------
|
||||
|
||||
/** „Läuft“ und „Neu“ aus den Update-Details lesbar machen. */
|
||||
export function versionen(id: string, d: UpdateDetails | undefined): { laeuft: string; neu: string | null } {
|
||||
if (!d) return { laeuft: "…", neu: null }
|
||||
if (id === "os") return { laeuft: "Ubuntu", neu: d.count ? `${d.count} Pakete` : null }
|
||||
const pre = id === "engine" ? "b" : "v"
|
||||
const laeuft = d.installed_build != null ? `${pre}${d.installed_build}` : "–"
|
||||
const neuer = d.latest_build != null && d.installed_build != null && d.latest_build > d.installed_build
|
||||
return { laeuft, neu: neuer ? `${pre}${d.latest_build}` : null }
|
||||
}
|
||||
|
||||
// --- Modelle ------------------------------------------------------------------------
|
||||
|
||||
/** Rolle eines Modells aus seinen llama-swap-Aliasen. */
|
||||
export function rolleVon(m: Modell): "hirn" | "coder" | null {
|
||||
const a = m.aliases.map((x) => x.toLowerCase())
|
||||
if (a.includes("hermes") || a.includes("fast")) return "hirn"
|
||||
if (a.includes("coder")) return "coder"
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user