feat(v5): 5.5.0 — Kern und Oberfläche aufgeteilt, Übersicht und Einstellungen neu, Erst-Einrichtung mit Poster-Kulisse

Commander-Wünsche vom 02.09.2026 vor der Freigabe von 5.4.0 (die deshalb nie
veröffentlicht wurde):

Kern: kern/index.ts ist nur noch Bootstrap und Router; Dienste in
kern/dienste/ (Einstellungen, Werkzeuge, Laufwerke, Erkennung, Vorgänge,
Speicher, Benachrichtigung, Automatik), Kontext in kern/kontext.ts,
Protokoll-Datei in gemeinsam/protokoll.ts, Platz-Rechnung in gemeinsam/platz.ts.

Oberfläche: fenster/App.tsx ist nur noch der Rahmen; bausteine.tsx, dialoge/,
uebersicht/ (LaufwerkKachel, KompressionsKarte, SpeicherKarte, VerlaufKarte),
einstellungen/ (Karten je Funktion, SprachAuswahl), einrichtung/
(ErstEinrichtung, PosterHintergrund).

Neu: Bibliothek weg, dafür Verlauf; Speicher-und-Bilanz-Karte (statfs);
LED-Fußleiste; MakeMKV-Kopf in die Einstellungen; Sprach-Chips mit Dropdown
statt Allgemeinem Preset; Karten Automatik/Benachrichtigung/Allgemein/Update;
Durchsuchen bei Werkzeugen; Arbeitsordner lokal (Encode dort, dann in die
Ablage); Platz-Prüfung vor dem Rip (Dialog und Kern); Automatik mit Countdown;
Serien-Merker über Discs; Rettungs-Abbild mit Nullen nur nach gesehenen
Lesefehlern (kern/rip/rettung.ts, Phase rettet, iso:-Quelle); Warteschlange
pausieren; Discord/Telegram-Benachrichtigung; Protokoll-Datei je Tag;
Fensterlage 80 % mittig, gemerkt; Erst-Einrichtung in acht Schritten vor dem
Poster-Grid.

Beweise: 413 Tests (neu: speicher, benachrichtigung, automatik, rettung,
fensterlage, protokoll, kleinkram-550), drei Typprüfungen, Klick-Beweis mit
29 Schritten inklusive Erst-Einrichtung (zweiter Start mit
RIPPY_START_BEREICH=einrichtung), Bilder beweise/klick/01…09.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-02 17:51:15 +02:00
co-authored by Claude Fable 5.1
parent 6678c81592
commit 3dc6798c79
67 changed files with 6961 additions and 3950 deletions
+58 -10
View File
@@ -8,8 +8,8 @@
// Laufwerksliste: Sie ändert sich NUR durch eine neue Liste vom Kern.
import {
istKernNachricht,
type AutomatikStand,
type BeschaffungStand,
type BibliothekEintrag,
type DiscInfoStand,
type FensterNachricht,
type GeraeteInfo,
@@ -19,6 +19,7 @@ import {
type RipAuftragOptionen,
type RipAuftragTitel,
type RipStatus,
type SpeicherStand,
type TitelStand,
type VorgaengeStand,
type WerkzeugAuskunft,
@@ -46,7 +47,6 @@ export interface Verbindung {
ripProtokoll: Record<string, string[]>
einstellungen: Record<string, string>
werkzeuge: WerkzeugAuskunft | null
bibliothek: BibliothekEintrag[]
schluessel: {
quelle: string
angenommen: boolean | null
@@ -63,6 +63,12 @@ export interface Verbindung {
vorgaenge: VorgaengeStand | null
/** Was das Windows-Protokoll je Laufwerk sagt (5.4.0) — bleibt stehen. */
gesundheit: Record<string, LaufwerkBericht>
/** Freier Platz an Arbeitsordner und Ablage (5.5.0). */
speicher: SpeicherStand | null
/** Automatik-Countdown je Laufwerk (5.5.0). */
automatik: Record<string, AutomatikStand>
/** Poster für den Hintergrund der Erst-Einrichtung (5.5.0). */
poster: string[]
/** Kurze Ereigniszeilen (neueste zuerst): Disc rein/raus, Auswurf, Fehler. */
meldungen: string[]
}
@@ -71,7 +77,7 @@ type Horcher = (stand: Verbindung) => void
const MAX_MELDUNGEN = 30
const stand: Verbindung = {
export const LEERE_VERBINDUNG: Verbindung = {
verbunden: false,
kern: null,
pongLatenzMs: null,
@@ -84,13 +90,17 @@ const stand: Verbindung = {
ripProtokoll: {},
einstellungen: {},
werkzeuge: null,
bibliothek: [],
schluessel: null,
beschaffung: null,
vorgaenge: null,
gesundheit: {},
speicher: null,
automatik: {},
poster: [],
meldungen: [],
}
const stand: Verbindung = { ...LEERE_VERBINDUNG }
const horcher = new Set<Horcher>()
let gestartet = false
let port: MessagePort | null = null
@@ -103,8 +113,9 @@ function kopie(): Verbindung {
discInfos: { ...stand.discInfos },
vorschlaege: { ...stand.vorschlaege },
einstellungen: { ...stand.einstellungen },
bibliothek: [...stand.bibliothek],
gesundheit: { ...stand.gesundheit },
automatik: { ...stand.automatik },
poster: [...stand.poster],
meldungen: [...stand.meldungen],
}
}
@@ -140,6 +151,8 @@ function kernNachricht(nachricht: KernNachricht): void {
meldung(`Disc erkannt in ${ereignis.geraet.name}${ereignis.geraet.typ}`)
} else if (ereignis.art === 'disc-entnommen') {
meldung(`Disc entnommen aus ${ereignis.geraet.name}`)
const { [ereignis.geraet.id]: _weg, ...rest } = stand.automatik
stand.automatik = rest
} else if (ereignis.art === 'laufwerk-weg') {
meldung(`Laufwerk ${ereignis.id}: nicht mehr da`)
}
@@ -184,9 +197,6 @@ function kernNachricht(nachricht: KernNachricht): void {
case 'werkzeuge':
stand.werkzeuge = nachricht.auskunft
break
case 'bibliothek':
stand.bibliothek = nachricht.eintraege
break
case 'schluessel-status':
stand.schluessel = nachricht.stand
if (nachricht.stand.angenommen === false) meldung(`⚠ MakeMKV-Schlüssel: ${nachricht.stand.text}`)
@@ -207,9 +217,18 @@ function kernNachricht(nachricht: KernNachricht): void {
meldung(`Laufwerk ${nachricht.id}: ${nachricht.bericht.text}`)
}
break
case 'speicher':
stand.speicher = nachricht.stand
break
case 'automatik':
stand.automatik = { ...stand.automatik, [nachricht.stand.id]: nachricht.stand }
break
case 'poster-vorschau':
stand.poster = nachricht.pfade
break
case 'rip-status': {
stand.rips = { ...stand.rips, [nachricht.status.id]: nachricht.status }
const laeuft = ['liest-info', 'rippt', 'komprimiert', 'legt-ab'].includes(nachricht.status.phase)
const laeuft = ['liest-info', 'rettet', 'rippt', 'komprimiert', 'legt-ab'].includes(nachricht.status.phase)
if (laeuft && stand.ripStart[nachricht.status.id] === undefined) {
stand.ripStart = { ...stand.ripStart, [nachricht.status.id]: Date.now() }
// Ein NEUER Vorgang beginnt — das Protokoll des alten weicht.
@@ -257,8 +276,8 @@ function portAnnehmen(neuerPort: MessagePort): void {
senden({ art: 'ping', zeit: Date.now() })
senden({ art: 'einstellungen-laden' })
senden({ art: 'werkzeuge-laden' })
senden({ art: 'bibliothek-laden' })
senden({ art: 'vorgaenge-laden' })
senden({ art: 'speicher-laden' })
melden()
}
@@ -324,6 +343,10 @@ export function ripAbbrechen(id: string): void {
anKern({ art: 'rip-abbruch', id }, `Rip-Abbruch ${id}`)
}
export function rettungStarten(id: string): void {
anKern({ art: 'rettung-start', id }, `Rettungs-Abbild ${id}`)
}
export function metadatenSuchen(id: string, text: string): void {
senden({ art: 'metadaten-suchen', id, text })
}
@@ -372,3 +395,28 @@ export function fremdenOrdnerLoeschen(ordner: string): void {
export function laufwerkPruefen(id: string): void {
anKern({ art: 'laufwerk-pruefen', id }, `Laufwerk ${id} prüfen`)
}
// ── 5.5.0 ───────────────────────────────────────────────────────────────
export function kompressionPause(an: boolean): void {
anKern({ art: 'kompression-pause', an }, an ? 'Kompression anhalten' : 'Kompression weiter')
}
export function automatikStopp(id: string): void {
senden({ art: 'automatik-stopp', id })
}
export function automatikJetzt(id: string): void {
anKern({ art: 'automatik-jetzt', id }, 'Automatik jetzt')
}
export function benachrichtigungTesten(): void {
anKern({ art: 'benachrichtigung-testen' }, 'Testnachricht')
}
export function posterLaden(): void {
senden({ art: 'poster-laden' })
}
export function speicherLaden(): void {
senden({ art: 'speicher-laden' })
}