Die sechs Punkte des Commanders vom 02.09.2026 abends und seine Entscheide:
* Restzeit der Kompression aus HandBrakes eigener ETA-Klammer (Formatstring
aus dem Binary 1.11.2 gelesen), KompressionStand.restS.
* Kachel zeigt nach dem Rip nur noch eine Zeile — Balken, Prozent, Ziel in
der Kompressions-Karte und im Verlauf ("nur in der Kompressions-Karte").
* Kopfzeile bleibt beim Scrollen stehen (sticky), Sektionswahl darunter.
* Untertitel-Vorwahl je Extra ("NUR für Extras, IMMER OPTIONAL"): welche
Untertitel-Spur beim Abspielen an ist — weiche Spur, nie eingebrannt;
RipAuftragTitel/RohDatei.untertitelVorwahl, sprachwahl.
* Mehrere Filme je Disc: "anderer Film …" je Titel mit TMDb-Suche;
filme.ts filmGruppen trennt am Rip-Ende in eigene Roh-Ordner, Vorgänge
und Kompressions-Aufträge mit voller Zuordnung (erkennung.zuordnungAus).
* Roh-Ordner und Dateien tragen den Filmnamen (filme.ts rohOrdnerName,
ablage/dateinamen.ts: Akira (1988).mkv, - Fassung n, - Extra nn).
* Hell-Modus: Einstellung design (dunkel/hell/system), data-design auf
<html>, Token --color-tinte statt white/-Alpha-Klassen.
* Vorgangs-Leiste (Commander: "Phase 1–4 plus Komprimierung — zu einem
machen"): gemeinsam/vorgangsleiste.ts + uebersicht/VorgangsLeiste.tsx,
vier Stationen (Rettung als Zwischenstation), Segment-Balken in der
aktiven Station; Kachel, Kompressions-Karte, Verlauf. PHASE-x/4-Texte weg.
* Automatik-Kasten leert sich, sobald ein Rip startet.
Beweise: 446 Tests (neu: filme, dateinamen, vorgangsleiste; erweitert:
auftrag, handbrake, automatik, ablegen), drei Typprüfungen, Klick-Beweis
33 Schritte inkl. Vorwahl, anderer Film, Vorgangs-Leiste, Restzeit,
Hell-Modus (Bilder beweise/klick/01…11).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
91 lines
4.2 KiB
TypeScript
91 lines
4.2 KiB
TypeScript
// Die Vorgangs-Leiste (5.6.0) — der ganze Weg einer Disc in Stationen, mit
|
|
// dem Segment-Balken in der aktiven Station. Logik in
|
|
// gemeinsam/vorgangsleiste.ts; hier nur das Zeichnen. `kompakt` = Punkte
|
|
// (Verlauf, Warteschlange).
|
|
import { Fragment } from 'react'
|
|
import { STATIONS_NAMEN, stationen, stationsZustand, type LeisteStand } from '../../gemeinsam/vorgangsleiste'
|
|
import { SegmentBalken } from '../bausteine'
|
|
|
|
export function VorgangsLeiste({ stand, restText, kompakt }: { stand: LeisteStand; restText?: string; kompakt?: boolean }) {
|
|
const liste = stationen(stand.rettung)
|
|
const zustaende = stationsZustand(liste, stand.station, stand.zustand)
|
|
const titel = `${STATIONS_NAMEN[stand.station]}: ${
|
|
stand.zustand === 'laeuft' ? 'läuft' : stand.zustand === 'wartet' ? 'wartet' : stand.zustand === 'fertig' ? 'fertig' : stand.zustand === 'fehler' ? 'Fehler' : 'abgebrochen'
|
|
}`
|
|
|
|
if (kompakt) {
|
|
return (
|
|
<span className="inline-flex flex-none items-center gap-1" title={titel} aria-label={titel}>
|
|
{liste.map((s, i) => (
|
|
<span
|
|
key={s}
|
|
className={`inline-block h-1.5 w-1.5 rounded-full ${
|
|
zustaende[i] === 'fertig'
|
|
? 'bg-gold'
|
|
: zustaende[i] === 'aktiv'
|
|
? stand.zustand === 'wartet'
|
|
? 'bg-gold/50'
|
|
: 'animate-pulse bg-gold-hell'
|
|
: zustaende[i] === 'fehler'
|
|
? 'bg-rot'
|
|
: 'bg-tinte/20'
|
|
}`}
|
|
/>
|
|
))}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-1.5" title={titel}>
|
|
{liste.map((s, i) => {
|
|
const z = zustaende[i]
|
|
const aktiv = z === 'aktiv'
|
|
const rahmen =
|
|
z === 'fehler'
|
|
? 'border-rot/50 bg-rot/10'
|
|
: aktiv
|
|
? 'min-w-[200px] flex-1 border-gold/50 bg-gold/10'
|
|
: z === 'fertig'
|
|
? 'border-gold/30 bg-gold/5'
|
|
: 'border-tinte/10'
|
|
return (
|
|
<Fragment key={s}>
|
|
{i > 0 && <span className={`h-px w-3 flex-none ${zustaende[i - 1] === 'fertig' ? 'bg-gold/60' : 'bg-tinte/15'}`} />}
|
|
<div className={`flex items-center gap-2 rounded-lg border px-2 py-1 ${rahmen}`}>
|
|
<span
|
|
className={`flex h-4 w-4 flex-none items-center justify-center rounded-full font-mono text-[9px] ${
|
|
z === 'fertig'
|
|
? 'bg-gold text-buehne'
|
|
: aktiv
|
|
? 'bg-gold-hell text-buehne'
|
|
: z === 'fehler'
|
|
? 'bg-rot text-buehne'
|
|
: 'border border-tinte/20 text-nebel'
|
|
}`}
|
|
>
|
|
{z === 'fertig' ? '✓' : z === 'fehler' ? '!' : String(i + 1)}
|
|
</span>
|
|
<span className={`font-mono text-[10px] tracking-[0.12em] ${aktiv ? 'text-gold-hell' : z === 'fertig' ? 'text-gold/80' : z === 'fehler' ? 'text-rot' : 'text-nebel'}`}>
|
|
{STATIONS_NAMEN[s].toUpperCase()}
|
|
</span>
|
|
{aktiv && stand.zustand === 'laeuft' && stand.prozent >= 0 && (
|
|
<div className="min-w-[80px] flex-1">
|
|
<SegmentBalken prozent={stand.prozent} />
|
|
</div>
|
|
)}
|
|
{aktiv && stand.zustand === 'laeuft' && stand.prozent >= 0 && <span className="font-mono text-[10.5px] text-gold-hell">{stand.prozent} %</span>}
|
|
{aktiv && stand.zustand === 'laeuft' && stand.prozent < 0 && stand.hinweis.length === 0 && (
|
|
<span className="inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-gold-hell" />
|
|
)}
|
|
{aktiv && restText !== undefined && restText.length > 0 && <span className="font-mono text-[10px] text-nebel">{restText}</span>}
|
|
{aktiv && stand.hinweis.length > 0 && <span className="font-mono text-[10px] text-nebel">{stand.hinweis}</span>}
|
|
{z === 'fehler' && <span className="font-mono text-[10px] text-rot">{stand.zustand === 'abgebrochen' ? 'abgebrochen' : 'Fehler'}</span>}
|
|
</div>
|
|
</Fragment>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|