Files
mission-control-v2/frontend/dist/assets/ChronikView-yUg-fPj_.js
T
HitonabiandClaude Opus 5 5aa5a484a7 feat(frontend): v3-Umbau P3 — ein Store, eine Statusleiste, endlich aria-live
Vierte Etappe. Der Client-Zustand hatte bisher keinen Ort: zwei handgebaute
useSyncExternalStore-Speicher, drei CustomEvent-Kanaele und ein localStorage-Griff
mitten in einem useState — verteilt ueber fuenf Dateien.

## Ein Store (Befund B-08/B-09)

app/store.ts (Zustand, 1,2 kB gzip) haelt genau vier Dinge: Bedienvorlieben
(Schiene, Expertenmodus, Palette), die Lage des Ereignisstroms und die
Meldungs-Warteschlange. Der Kopfkommentar nennt die Regel, nach der etwas dort
landen darf — und ein Test prueft sie: gespeichert werden AUSSCHLIESSLICH
Schienen- und Expertenmodus-Zustand, kein Strom, keine Meldungen, kein Geheimnis.

Der Metrik-Verlauf bleibt bewusst DRAUSSEN (lib/metricsStore.ts): Er nimmt ab P4
jede Sekunde einen Messpunkt entgegen, ein Store-Update pro Sekunde wuerde jede
abonnierende Komponente neu rendern.

Die drei CustomEvent-Kanaele sind ERSATZLOS weg — `grep -r dispatchEvent src`
liefert nichts mehr. Wer die Schublade oeffnen will, setzt `?system=`; wer springen
will, navigiert. Auch der letzte Rest von P2 (`mc-palette-open`) ist aufgeloest.

## Statusleiste (Spezifikation §4.1)

Der Zustand der Box stand bisher verstreut: die Ampel unten in der Sidebar, die
Auslastung nur im Cockpit, das aktive Modell nur im Modell-Manager. Wer in der
Konsole arbeitete, sah gar nichts.

Jetzt eine feste Leiste ueber allen Ansichten: Motor/Hirn-Ampel (klickbar zum
Agenten) · aktive Rolle + Durchsatz mit Sparkline · geteilter Speicher ALS BALKEN
(nicht als Prozent — 128 GB Unified Memory ist die Kernzahl dieser Box) · CPU/GPU
· Temperatur (ab 85 °C bernstein) · Betriebszeit · Token-Summe · Live-Anzeige.

Die Sparklines sind eigenes SVG, ~20 Zeilen. Fuer eine 56-Pixel-Linie waere eine
Diagramm-Bibliothek Verschwendung.

Dafuer neu im Backend: `uptime_s` in /api/system/status (psutil.boot_time).
Gehoert dorthin, weil die Box sich woechentlich selbst neu startet — dann ist
"laeuft seit 20 Minuten" die Antwort auf eine ganze Klasse von Fragen.
Liefert psutil nichts, steht dort None und die Leiste blendet das Feld aus,
statt eine Zahl zu erfinden.

## aria-live (Befund B-11) — und diesmal mit Absender

Im ganzen Frontend gab es KEIN einziges aria-live. app/shell/Meldungen.tsx ist
jetzt die eine Stelle fuer beilaeufige Rueckmeldungen; Fehler bekommen
`assertive` und bleiben stehen, alles andere `polite` und raeumt sich weg.

Wichtig: Die Region ist keine Attrappe. Gespeist wird sie von den Uebergaengen des
Ereignisstroms und vom Konsolen-Neustart (mit dem echten Grund aus ApiError.detail).
Der erste Verbindungsaufbau wird bewusst NICHT gemeldet — sonst begruesst jede
Seite den Nutzer mit "Verbindung wieder da".

## Ehrliche Anzeige statt stillem Altern (Befund B-15/B-18)

lib/events.ts fuehrt die Stromlage jetzt im Store: live · nachlauf · getrennt.
Beim Wechsel wird EINMAL invalidiert — vorher las relax() den Zustand erst beim
naechsten Refetch, nach einem Riss blieb die UI bis zu 150 s im langsamen Modus.

## Bahnbreiten-Deckel (Befund B-10)

Die Arbeitsflaeche endet bei 1600 px. Ohne Deckel zog sich das Cockpit auf einem
3440-px-Ultrawide auf ueber 3 000 px, waehrend die Wurzel-Schriftgroesse mitwuchs
und die Zeilen GROESSER statt lesbarer machte.

## Nebenbei

Der Buendel-Budget-Check aus P0 war fragil: `ls dist/assets/index-*.js | head -1`
kann den falschen treffen, weil Rollup auch kleine geteilte Module "index-*.js"
nennt (gemessen: ein 67-Byte-Chunk neben dem 374-kB-Einstieg). Er waere dann still
immer gruen gewesen. Beide Ampel-Dateien lesen den Einstieg jetzt aus index.html.

## Verifiziert im Browser

  Statusleiste: alle Felder mit Live-Daten, "Laeuft seit 1 Std 10 min"
  Backend abgewuergt  -> Leiste springt auf "Nachlauf" UND die aria-live-Region
                         meldet "Verbindung zur Zentrale verloren" (polite)
  Backend zurueck     -> "Live", ohne Begruessungs-Meldung
  Cockpit-Kachel      -> /cockpit?system=logs, Schublade offen, Reiter "System-Logs"
  Arbeitsflaeche      -> max-width 1600 px

33/33 Tests gruen (7 neue fuer den Store) · ESLint 0 Fehler · tsc sauber ·
Einstieg 118 153 B gzip / Budget 125 000.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 09:07:09 +02:00

32 lines
9.9 KiB
JavaScript

import{c as d,ah as S,r as h,j as e,ai as M,L as p,a0 as z,R as j,a4 as C,aj as D,ak as A,b as B,al as Z,u as E,k as L,n as k,q}from"./index-CB2Jz083.js";/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const R=d("AlarmClock",[["circle",{cx:"12",cy:"13",r:"8",key:"3y4lt7"}],["path",{d:"M12 9v4l2 2",key:"1c63tq"}],["path",{d:"M5 3 2 6",key:"18tl5t"}],["path",{d:"m22 6-3-3",key:"1opdir"}],["path",{d:"M6.38 18.7 4 21",key:"17xu3x"}],["path",{d:"M17.64 18.67 20 21",key:"kv2oe2"}]]);/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const y=d("Archive",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",key:"1s80jp"}],["path",{d:"M10 12h4",key:"a56b0p"}]]);/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const T=d("Bell",[["path",{d:"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9",key:"1qo2s2"}],["path",{d:"M10.3 21a1.94 1.94 0 0 0 3.4 0",key:"qgo35s"}]]);/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const $=d("MessageCircle",[["path",{d:"M7.9 20A9 9 0 1 0 4 16.1L2 22Z",key:"vv11sd"}]]);/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const U=d("Moon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]]);/**
* @license lucide-react v0.460.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const H=d("Radar",[["path",{d:"M19.07 4.93A10 10 0 0 0 6.99 3.34",key:"z3du51"}],["path",{d:"M4 6h.01",key:"oypzma"}],["path",{d:"M2.29 9.62A10 10 0 1 0 21.31 8.35",key:"qzzz0"}],["path",{d:"M16.24 7.76A6 6 0 1 0 8.23 16.67",key:"1yjesh"}],["path",{d:"M12 18h.01",key:"mhygvu"}],["path",{d:"M17.99 11.66A6 6 0 0 1 15.77 16.67",key:"1u2y91"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}],["path",{d:"m13.41 10.59 5.66-5.66",key:"mhq4k0"}]]),O={sentry:{icon:A,label:"Health-Wächter",cls:"text-red-300 bg-red-500/10 border-red-500/25"},alarm:{icon:D,label:"Alarm",cls:"text-red-200 bg-red-500/15 border-red-500/40"},notify:{icon:T,label:"Meldung",cls:"text-sky-300 bg-sky-500/10 border-sky-500/25"},"chef-gutachter":{icon:U,label:"Morgenlage",cls:"text-violet-300 bg-violet-500/10 border-violet-500/25"},radar:{icon:H,label:"Radar",cls:"text-teal-300 bg-teal-500/10 border-teal-500/25"},auftragsbuch:{icon:C,label:"Auftragsbuch",cls:"text-primary bg-primary/10 border-primary/25"},zeitmaschine:{icon:j,label:"Zeitmaschine",cls:"text-amber-300 bg-amber-500/10 border-amber-500/25"},reminder:{icon:R,label:"Erinnerung",cls:"text-amber-300 bg-amber-500/10 border-amber-500/25"},traum:{icon:z,label:"Traum",cls:"text-violet-300 bg-violet-500/10 border-violet-500/25"}},P={icon:$,label:"Box",cls:"text-muted-foreground bg-background/40 border-border/50"};function W(r){return new Date(r*1e3).toLocaleDateString("de-DE",{weekday:"long",day:"2-digit",month:"long"})}function _(){const{data:r=[],isLoading:u}=S(),[i,x]=h.useState(!0),b=h.useMemo(()=>{const o=[];for(const n of r){const a=W(n.ts),l=o[o.length-1];l&&l.day===a?l.items.push(n):o.push({day:a,items:[n]})}return o},[r]);return e.jsxs("div",{className:"space-y-7",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Chronik"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was deine Box von allein getan und gemeldet hat — und die Zeitmaschine, falls du zu einem früheren Stand zurück willst."})]}),e.jsx(J,{}),e.jsxs("section",{className:"space-y-4",children:[e.jsxs("p",{className:"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3.5 w-3.5"})," Verlauf"]}),u&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(p,{className:"h-4 w-4 animate-spin"})," Chronik wird geladen …"]}),!u&&r.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Einträge. Sobald die Box etwas von allein tut oder meldet (Updates, Wächter, Träume), steht es hier."}),b.map(({day:o,items:n})=>e.jsxs("div",{children:[e.jsx("p",{className:"mb-2 text-xs font-bold text-foreground/70",children:o}),e.jsxs("div",{className:"space-y-2 border-l-2 border-border/40 pl-4",children:[(i?n.slice(0,8):n).map(a=>{const l=O[a.source]??P,m=l.icon;return e.jsxs("div",{className:"relative rounded-xl border border-border/50 bg-card/40 p-3 backdrop-blur-sm",children:[e.jsx("span",{className:"absolute -left-[23px] top-4 h-2.5 w-2.5 rounded-full bg-border ring-4 ring-background/60"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("span",{className:B("flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-[10px] font-bold",l.cls),children:[e.jsx(m,{className:"h-3 w-3"})," ",l.label]}),a.subject&&e.jsx("span",{className:"text-[10px] font-semibold text-muted-foreground",children:a.subject}),e.jsx("span",{className:"ml-auto font-mono text-[10px] text-muted-foreground/60",children:new Date(a.ts*1e3).toLocaleTimeString("de-DE",{hour:"2-digit",minute:"2-digit"})})]}),e.jsx("p",{className:"mt-1.5 whitespace-pre-wrap text-xs leading-relaxed text-foreground/85",children:a.text})]},a.id)}),i&&n.length>8&&e.jsxs("button",{onClick:()=>x(!1),className:"text-[11px] text-muted-foreground underline hover:text-foreground",children:[n.length-8," weitere anzeigen"]})]})]},o))]})]})}function J(){const{data:r}=Z(),u=E(),{showAlert:i,showConfirm:x,dialogElement:b}=L(),[o,n]=h.useState(null),[a,l]=h.useState(!1),[m,v]=h.useState(!1),c=(r==null?void 0:r.backups)??[],g=m?c:c.slice(0,4);function f(t){const s=t.snapshot.match(/^(\d{4})-?(\d{2})-?(\d{2})[T_-](\d{2})[-:]?(\d{2})/);return s?`${s[3]}.${s[2]}.${s[1]}, ${s[4]}:${s[5]} Uhr`:t.snapshot}async function w(t){n(t.file);try{await k("/api/zeitmaschine/restore",{method:"POST",body:JSON.stringify({file:t.file})}),i("Zeitmaschine läuft","Die Wiederherstellung wurde gestartet. Die Dienste starten gleich neu — die Zentrale ist kurz weg und meldet sich von selbst zurück. Vorher wurde automatisch ein Sicherheits-Backup des jetzigen Zustands gemacht.")}catch(s){i("Start fehlgeschlagen",String((s==null?void 0:s.message)||s))}finally{n(null)}}async function N(){l(!0);try{const t=await k("/api/system/backup",{method:"POST"});t.ok?i("Gesichert",`Snapshot ${t.snapshot} wurde angelegt.`):i("Backup fehlgeschlagen",t.error||"Unbekannter Fehler"),u.invalidateQueries({queryKey:q.zeitmaschine})}catch(t){i("Backup fehlgeschlagen",String((t==null?void 0:t.message)||t))}finally{l(!1)}}return e.jsxs("section",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("p",{className:"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(y,{className:"h-3.5 w-3.5"})," Zeitmaschine — gesicherte Stände"]}),e.jsxs("button",{onClick:N,disabled:a||!(r!=null&&r.available),className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-card/45 px-2.5 text-[11px] font-semibold text-muted-foreground hover:text-foreground hover:border-primary/40 transition-all cursor-pointer disabled:opacity-50",children:[a?e.jsx(p,{className:"h-3.5 w-3.5 animate-spin"}):e.jsx(y,{className:"h-3.5 w-3.5"})," Jetzt sichern"]})]}),r&&!r.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Die Snapshots liegen auf der Box — im lokalen Dev-Modus nicht verfügbar."}),(r==null?void 0:r.available)&&c.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-5 text-center text-xs text-muted-foreground",children:'Noch keine Snapshots. Das nächtliche Backup (03:30) legt sie automatisch an — oder „Jetzt sichern".'}),g.length>0&&e.jsx("div",{className:"grid gap-2 sm:grid-cols-2",children:g.map((t,s)=>e.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-xl border border-border/50 bg-card/40 p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("p",{className:"text-xs font-semibold text-foreground",children:[f(t)," ",s===0&&e.jsx("span",{className:"ml-1 rounded bg-emerald-500/10 px-1.5 py-0.5 text-[9px] font-bold text-emerald-300",children:"neuester"})]}),e.jsxs("p",{className:"font-mono text-[10px] text-muted-foreground/70 truncate",title:t.file,children:[t.size_mb.toLocaleString("de-DE")," MB · ",t.file]})]}),e.jsxs("button",{disabled:o!==null,onClick:()=>x("Wirklich zu diesem Stand zurück?",`Die Box wird auf den Snapshot vom ${f(t)} zurückgesetzt (Gedächtnis, Hermes-Config, Modell-Router). Der JETZIGE Zustand wird vorher automatisch gesichert — der Schritt ist also umkehrbar. Die Dienste starten neu, die Zentrale ist kurz weg.`,()=>w(t)),className:"flex h-8 shrink-0 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-2.5 text-[10px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[o===t.file?e.jsx(p,{className:"h-3 w-3 animate-spin"}):e.jsx(j,{className:"h-3 w-3"})," Zurück"]})]},t.file))}),c.length>4&&!m&&e.jsxs("button",{onClick:()=>v(!0),className:"text-[11px] text-muted-foreground underline hover:text-foreground",children:["Alle ",c.length," Snapshots anzeigen"]}),b]})}export{_ as ChronikView};