Welle 2 · „Was ist neu?“ vor dem Knopf: Release Notes, Debian-Änderungslisten und Lucys Kurzfassung

Vor einem App-Update sammelt der Orchestrator die Release Notes aller Versionen zwischen installiert und neu (GitHub),
beim Proxmox-Host die Debian-Änderungslisten der wichtigsten Pakete (neue Ausführer-Aktion changelog, nur lesend, nur
für anstehende Pakete). Lucy fasst sie über das Modell fast der KI-Box auf Deutsch zusammen: Handarbeit ganz oben,
höchstens fünf Stichpunkte, Brüche zuerst. Die Update-Tabelle hat „Was ist neu?“ je Gerät, und die Rückfrage vor dem
Update zeigt die Kurzfassung gleich mit. Gemerkt je Version; der Ausführer braucht ausfuehrer-einrichten.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-25 17:47:04 +02:00
co-authored by Claude Opus 5.5
parent 0190c25d5a
commit 45f11bc3dc
11 changed files with 604 additions and 7 deletions
@@ -1,9 +1,10 @@
import { useState } from "react"
import { Search } from "lucide-react"
import { ChevronRight, Search } from "lucide-react"
import { cn } from "cn"
import { Bestaetigen } from "@/components/cockpit/Bestaetigen"
import { Chip } from "@/components/cockpit/Chip"
import { Geraetesymbol } from "@/components/cockpit/Geraetesymbol"
import { Neuerungen } from "@/components/homelab/Neuerungen"
import { Button } from "@/components/ui/button"
import { geraetebild } from "@/lib/geraetebild"
import {
@@ -68,6 +69,12 @@ function PaketZelle({ b, onSuchen, gesperrt }: { b: BausteinStand | null; onSuch
const KNOPF_TEXT: Record<string, string> = { app: "App updaten", os: "Pakete einspielen", pakete: "Pakete einspielen" }
/** Wofür es „Was ist neu?“ gibt (seit 25.09.2026): eine neue App-Version, die Pakete des Proxmox-Hosts. */
function mitNeuerungen(ziel: ZielStand, b: BausteinStand | null | undefined): b is BausteinStand {
if (!b || b.zustand !== "neu") return false
return (b.id === "app" && !!b.verfuegbar) || (ziel.art === "proxmox-host" && b.id === "pakete")
}
function Zeile({ ziel, laeuft, gesperrt, onFrage }: {
ziel: ZielStand
laeuft: string | null
@@ -78,6 +85,8 @@ function Zeile({ ziel, laeuft, gesperrt, onFrage }: {
const sys = systemBaustein(ziel)
const knoepfe = ziel.bausteine.filter((b) => b.zustand === "neu" && b.aktion)
const ruhig = updateRang(ziel) >= 2
const neues = ziel.bausteine.find((b) => mitNeuerungen(ziel, b))
const [offen, setOffen] = useState(false)
return (
<div
role="row"
@@ -111,6 +120,16 @@ function Zeile({ ziel, laeuft, gesperrt, onFrage }: {
))
)}
</span>
{neues && (
<div className="col-span-full flex flex-col gap-2">
<button type="button" aria-expanded={offen} onClick={() => setOffen(!offen)}
className="inline-flex min-h-9 items-center gap-1.5 self-start text-sm text-cyan-text hover:underline">
<ChevronRight className={cn("size-4 transition-transform", offen && "rotate-90")} aria-hidden />
Was ist neu?
</button>
{offen && <Neuerungen zid={ziel.id} baustein={neues.id} className="flaeche-innen rounded-xl p-3" />}
</div>
)}
</div>
)
}
@@ -126,6 +145,8 @@ export function UpdateTabelle({ ziele, laufend, gesperrt, onAktion }: {
onAktion: (a: ZielAktion) => void
}) {
const [frage, setFrage] = useState<Frage>(null)
// Zeigt die Rückfrage ein Update mit „Was ist neu?“, steht Lucys Kurzfassung gleich darin (seit 25.09.2026).
const frageBaustein = frage ? frage.ziel.bausteine.find((b) => b.aktion?.pfad === frage.aktion.pfad && mitNeuerungen(frage.ziel, b)) : undefined
const sortiert = [...ziele].sort((a, b) => updateRang(a) - updateRang(b))
const aktuell = ziele.filter((z) => updateRang(z) === 3).length
return (
@@ -155,7 +176,9 @@ export function UpdateTabelle({ ziele, laufend, gesperrt, onAktion }: {
setFrage(null)
}}
onNein={() => setFrage(null)}
/>
>
{frageBaustein && frage && <Neuerungen zid={frage.ziel.id} baustein={frageBaustein.id} kompakt />}
</Bestaetigen>
</>
)
}