diff --git a/.gitignore b/.gitignore index 952394e..e0e42ac 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ logs/ *.db *.sqlite dist/ + +# Design-Canvas-Arbeitsdateien (leben im Claude-Artifact, nicht im Git) +design-entwuerfe/ diff --git a/beweise/ui-kino-mix.png b/beweise/ui-kino-mix.png new file mode 100644 index 0000000..61a20a4 Binary files /dev/null and b/beweise/ui-kino-mix.png differ diff --git a/rippy-windows/bau/lizenzen/QUELLEN.md b/rippy-windows/bau/lizenzen/QUELLEN.md index be5bb1c..e7e8da5 100644 --- a/rippy-windows/bau/lizenzen/QUELLEN.md +++ b/rippy-windows/bau/lizenzen/QUELLEN.md @@ -23,3 +23,13 @@ holt es beim Einrichten vom Hersteller. - Projekt: https://xiph.org/flac/ - ⚠ `flac.exe` braucht `libFLAC.dll` DANEBEN — mit nur der EXE endet jeder Aufruf ohne eine Zeile Ausgabe (§ 9, gemessen). + +## Schriften (im Renderer-Bundle, seit dem Kino-Mix 31.08.2026) + +- **Bricolage Grotesque** (Display), **Instrument Sans** (Fließtext), + **JetBrains Mono** (Technik) — alle drei unter der SIL Open Font + License 1.1; Volltexte in `SCHRIFT-*-OFL.txt`. +- Bezogen als npm-Pakete `@fontsource-variable/bricolage-grotesque`, + `@fontsource/instrument-sans`, `@fontsource/jetbrains-mono` (je 5.3.0); + Vite bündelt die woff2-Dateien ins Fenster — Rippy lädt zur Laufzeit + keine Schriften aus dem Netz. diff --git a/rippy-windows/bau/lizenzen/SCHRIFT-BricolageGrotesque-OFL.txt b/rippy-windows/bau/lizenzen/SCHRIFT-BricolageGrotesque-OFL.txt new file mode 100644 index 0000000..49135ef --- /dev/null +++ b/rippy-windows/bau/lizenzen/SCHRIFT-BricolageGrotesque-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/rippy-windows/bau/lizenzen/SCHRIFT-InstrumentSans-OFL.txt b/rippy-windows/bau/lizenzen/SCHRIFT-InstrumentSans-OFL.txt new file mode 100644 index 0000000..9a39a8e --- /dev/null +++ b/rippy-windows/bau/lizenzen/SCHRIFT-InstrumentSans-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Instrument Sans Project Authors (https://github.com/Instrument/instrument-sans) InstrumentSans-Italic[wdth,wght].ttf: Copyright 2022 The Instrument Sans Project Authors (https://github.com/Instrument/instrument-sans) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/rippy-windows/bau/lizenzen/SCHRIFT-JetBrainsMono-OFL.txt b/rippy-windows/bau/lizenzen/SCHRIFT-JetBrainsMono-OFL.txt new file mode 100644 index 0000000..8f7ed67 --- /dev/null +++ b/rippy-windows/bau/lizenzen/SCHRIFT-JetBrainsMono-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) JetBrainsMono-Italic[wght].ttf: Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/rippy-windows/package-lock.json b/rippy-windows/package-lock.json index 0adfa1c..0a174cf 100644 --- a/rippy-windows/package-lock.json +++ b/rippy-windows/package-lock.json @@ -13,6 +13,9 @@ "koffi": "^3.1.6" }, "devDependencies": { + "@fontsource-variable/bricolage-grotesque": "^5.3.0", + "@fontsource/instrument-sans": "^5.3.0", + "@fontsource/jetbrains-mono": "^5.3.0", "@tailwindcss/vite": "^4.3.3", "@types/node": "^24.0.0", "@types/react": "^19.2.18", @@ -1086,6 +1089,36 @@ "node": ">=18" } }, + "node_modules/@fontsource-variable/bricolage-grotesque": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/bricolage-grotesque/-/bricolage-grotesque-5.3.0.tgz", + "integrity": "sha512-TLi9Q4hJjS2UvoTMRSS2nHu6c4R56lAw60NR9QYtVRCHn0XtsFpiEhNffZ8Glsoxu6wEEwLKBP8lb94J52PNBA==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/instrument-sans": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/instrument-sans/-/instrument-sans-5.3.0.tgz", + "integrity": "sha512-QwXc4hb/px3XvSPS2CPAOgey8nyrQFxgxrmXQQ+pN+P51hKdAcchxpg8rSbjANFfPu6VKXhDqkVDucXMZ9CM5g==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", diff --git a/rippy-windows/package.json b/rippy-windows/package.json index 7985cd2..d82f3b8 100644 --- a/rippy-windows/package.json +++ b/rippy-windows/package.json @@ -21,6 +21,9 @@ "koffi": "^3.1.6" }, "devDependencies": { + "@fontsource-variable/bricolage-grotesque": "^5.3.0", + "@fontsource/instrument-sans": "^5.3.0", + "@fontsource/jetbrains-mono": "^5.3.0", "@tailwindcss/vite": "^4.3.3", "@types/node": "^24.0.0", "@types/react": "^19.2.18", diff --git a/rippy-windows/src/fenster/App.tsx b/rippy-windows/src/fenster/App.tsx index da879b0..b27bafa 100644 --- a/rippy-windows/src/fenster/App.tsx +++ b/rippy-windows/src/fenster/App.tsx @@ -1,11 +1,15 @@ -// Die Oberfläche — seit dieser Runde im Kino-Look: Die Laufwerks-Kachel -// wird zur Bühne (TMDb-Backdrop als Panorama, Poster, Beschreibung), die -// Bibliothek zum Poster-Regal, der MakeMKV-Schlüssel bekommt seinen -// Update-Knopf (§ 9). Die Regeln bleiben: Übersicht/Einstellungen/ -// Bibliothek, EINE Einstellungs-Quelle über den Kern (§ 6.7), R2/R4. +// Die Oberfläche im Kino-Mix (Design-Entscheid des Commanders, 31.08.2026, +// beweise/…-Canvas „Rippy Redesign"): die Kinosaal-Bühne — warmes Schwarz, +// Saallicht, Filmkorn, Gold, Poster mit Goldkante, Plakat-Titel — plus die +// Schaltpult-Präzision: REC-Glühen, Info-Kacheln, Segment-Anzeige mit +// großer Prozentzahl, Mono-Protokoll, LED-Systemleiste. Der Look zieht +// sich durch ALLE Bereiche (Übersicht, Einstellungen, Bibliothek, +// Erst-Einrichtung, Dialoge). Die Regeln bleiben: EINE Einstellungs-Quelle +// über den Kern (§ 6.7), R2 (nichts verschwindet ohne Auskunft), R4 +// (Fehler stehen im Fenster — bei Systemfehlern als rote Karte). import { useEffect, useState } from 'react' import type { DiscInfoStand, GeraeteInfo, HauptStatus, RipStatus } from '../gemeinsam/nachrichten' -import { presetEmpfehlung } from '../gemeinsam/preset-empfehlung' +import { DEFAULT_HB_PRESET, presetEmpfehlung } from '../gemeinsam/preset-empfehlung' import { aufVerbindung, auswerfen, @@ -43,8 +47,8 @@ function restzeitText(prozent: number, startMs: number | undefined): string { if (startMs === undefined || prozent <= 2 || prozent >= 100) return '' const verstrichen = (Date.now() - startMs) / 1000 const restS = Math.round((verstrichen * (100 - prozent)) / prozent) - if (restS < 90) return `noch ~${restS} s` - return `noch ~${Math.round(restS / 60)} min` + if (restS < 90) return `REST ~${restS} S` + return `REST ~${Math.round(restS / 60)} MIN` } function istHauptStatus(wert: unknown): wert is HauptStatus { @@ -67,22 +71,14 @@ const TYP_NAMEN: Record = { unknown: 'unbekannt', } -const TYP_SYMBOL: Record = { - cd: '💿', - dvd: '📀', - bluray: '📀', - uhd: '📀', - iso: '💽', -} - -const PHASEN_NAMEN: Record = { - 'liest-info': 'liest Titel-Info', - rippt: 'rippt (verlustfrei)', - komprimiert: 'komprimiert', - 'legt-ab': 'legt ab', - fertig: 'fertig', - fehler: 'Fehler', - abgebrochen: 'abgebrochen', +const PHASEN_MONO: Record = { + 'liest-info': 'PHASE 1/4 — LIEST TITEL-INFO', + rippt: 'PHASE 2/4 — RIPPT (VERLUSTFREI)', + komprimiert: 'PHASE 3/4 — KOMPRIMIERT', + 'legt-ab': 'PHASE 4/4 — LEGT AB', + fertig: 'FERTIG', + fehler: 'FEHLER', + abgebrochen: 'ABGEBROCHEN', } const LAUFENDE_PHASEN = ['liest-info', 'rippt', 'komprimiert', 'legt-ab'] @@ -94,42 +90,107 @@ function sicherheitsWort(confidence: number): string { return 'unsicher' } +/** Welches Preset diese Disc bekäme — dieselbe Kette wie im Kern + * (eigene Wahl → allgemeines Preset → Empfehlung → Default). */ +function presetAnzeige(typ: GeraeteInfo['typ'], verbindung: Verbindung): string { + if (typ === 'cd') return 'FLAC (verlustfrei)' + if (typ === 'unknown') return 'ISO-Abbild' + const werte = verbindung.einstellungen + if (werte['transcodeEnabled'] === 'false') return 'ohne Kompression' + const schluessel = + typ === 'dvd' ? 'transcodePresetDvd' : typ === 'uhd' ? 'transcodePresetUhd' : 'transcodePresetBluray' + const eigene = werte[schluessel] ?? '' + const allgemein = werte['transcodePreset'] ?? '' + const empfehlung = presetEmpfehlung(typ, verbindung.werkzeuge?.backends ?? [], verbindung.werkzeuge?.presets ?? []) + const gewaehlt = eigene.length > 0 ? eigene : allgemein.length > 0 ? allgemein : empfehlung.length > 0 ? empfehlung : DEFAULT_HB_PRESET + return gewaehlt === 'keine' ? 'ohne Kompression' : gewaehlt +} + +// ── Ikonografie: eine Handvoll Strich-Symbole, alle aus demselben Guss ── +function IconDisc({ groesse, farbe }: { groesse: number; farbe: string }) { + return ( + + + + + + ) +} + +function IconPlay() { + return ( + + + + ) +} + +function IconStop() { + return ( + + + + ) +} + +function IconEject() { + return ( + + + + ) +} + +function IconStift() { + return ( + + + + ) +} + function Badge({ text, ton }: { text: string; ton: 'gruen' | 'gelb' | 'blau' | 'grau' }) { const farben = { - gruen: 'border-emerald-500/40 bg-emerald-500/10 text-emerald-300', - gelb: 'border-amber-500/40 bg-amber-500/10 text-amber-300', - blau: 'border-sky-500/40 bg-sky-500/10 text-sky-300', - grau: 'border-slate-600/60 bg-slate-800/60 text-slate-300', + gruen: 'border-gruen/40 bg-gruen/10 text-gruen', + gelb: 'border-warn/40 bg-warn/10 text-warn', + blau: 'border-gold/40 bg-gold/10 text-gold-hell', + grau: 'border-white/15 bg-white/5 text-dunst', } return ( - + {text} ) } -interface KachelProps { - titel: string - zustand: 'ok' | 'fehler' | 'wartet' - zeilen: string[] +/** Die kleinen Mess-Kacheln aus dem Schaltpult (MEDIUM / GRÖSSE / …). */ +function InfoKachel({ label, wert, farbe }: { label: string; wert: string; farbe?: string }) { + return ( +
+
{label}
+
+ {wert} +
+
+ ) } -function Kachel({ titel, zustand, zeilen }: KachelProps) { - const punktFarbe = - zustand === 'ok' ? 'bg-emerald-400' : zustand === 'fehler' ? 'bg-red-400' : 'bg-amber-400' +/** Die goldene Aussteuerungsanzeige — 20 Segmente, gefüllt nach Prozent. */ +function SegmentBalken({ prozent }: { prozent: number }) { + const segmente = 20 + const anteil = Math.max(0, Math.min(100, prozent)) / 100 + const voll = Math.floor(anteil * segmente) + const halb = anteil * segmente - voll >= 0.3 return ( -
-
- -

{titel}

-
-
- {zeilen.map((zeile, i) => ( -

- {zeile} -

- ))} -
+
+ {Array.from({ length: segmente }, (_, i) => ( +
+ ))}
) } @@ -158,10 +219,10 @@ function AendernDialog({ onClick={schliessen} >
e.stopPropagation()} > -

Was ist das wirklich?

+

Was ist das wirklich?

{vorschlaege.length === 0 ? ( -

Keine Treffer — anderen Suchtext probieren.

+

Keine Treffer — anderen Suchtext probieren.

) : (
{vorschlaege.map((v) => ( @@ -190,26 +251,31 @@ function AendernDialog({ zuordnungSetzen(id, v) schliessen() }} - className="flex w-full items-center gap-3 rounded-lg border border-slate-700/60 bg-slate-800/40 p-2 text-left hover:border-sky-400/60 hover:bg-slate-800" + className="flex w-full items-center gap-3 rounded-xl border border-white/10 bg-karte/60 p-2 text-left hover:border-gold/50 hover:bg-karte" > {v.posterPfad.length > 0 ? ( - + ) : ( -
🎬
+ + + )}
-

+

{v.titel} {v.jahr !== null ? ` (${v.jahr})` : ''}

-

{v.typ === 'tv' ? 'Serie' : 'Film'}

+

{v.typ === 'tv' ? 'SERIE' : 'FILM'}

))}
)} -
-
@@ -218,8 +284,8 @@ function AendernDialog({ ) } -// Die Bühne: Backdrop als Panorama, Poster, Titel — und darunter der -// laufende Vorgang mit Phase, Restzeit und aufklappbarem Protokoll. +// Die Bühne: Backdrop als Panorama, Poster mit Goldkante, Plakat-Titel — +// und beim Arbeiten die Aussteuerungsanzeige samt Mono-Protokoll. function LaufwerkKachel({ geraet, rip, @@ -227,6 +293,7 @@ function LaufwerkKachel({ vorschlaege, ripStartMs, protokoll, + verbindung, }: { geraet: GeraeteInfo rip: RipStatus | undefined @@ -234,6 +301,7 @@ function LaufwerkKachel({ vorschlaege: MetaVorschlag[] ripStartMs: number | undefined protokoll: string[] + verbindung: Verbindung }) { const [aendernOffen, setAendernOffen] = useState(false) const [protokollOffen, setProtokollOffen] = useState(false) @@ -244,190 +312,202 @@ function LaufwerkKachel({ const erkannt = discDa && info !== undefined && info.titel.length > 0 return ( -
+
{backdrop.length > 0 && ( <> -
-
+
+
)}
-
+
-

- Laufwerk {geraet.id}: - {geraet.modell} -

+ LAUFWERK {geraet.id}: + · + {geraet.modell.length > 0 ? geraet.modell : 'MODELL UNBEKANNT'}
{ripLaeuft ? ( ) : ( )}
{!discDa && ( -
- 💿 -

- {geraet.status === 'empty' ? 'Keine Disc eingelegt — Disc rein genügt.' : geraet.grund} +

+ +

+ {geraet.status === 'empty' ? 'Keine Disc eingelegt — Disc rein genügt, Rippy meldet sich.' : geraet.grund}

)} {discDa && ( -
+
{poster.length > 0 ? ( ) : ( -
- {TYP_SYMBOL[geraet.typ] ?? '💿'} +
+
)}
{erkannt ? ( - <> -

- {info.titel} - {info.jahr !== null && ({info.jahr})} -

-
- - {geraet.groesseBytes > 0 && } - 0 ? ` · ${info.quelle}` : ''}`} - ton={info.confidence >= 0.8 ? 'gruen' : 'gelb'} - /> -
- {info.beschreibung.length > 0 && ( -

- {info.beschreibung} -

- )} - +

+ {info.titel} + {info.jahr !== null && ({info.jahr})} +

) : ( - <> -

- {TYP_NAMEN[geraet.typ]} - {geraet.groesseBytes > 0 && ( - {gb(geraet.groesseBytes)} - )} -

-

- {geraet.typ === 'cd' - ? 'Titel kommen beim Rippen von MusicBrainz.' - : 'Noch keine Zuordnung — „Ändern …" hilft nach.'} -

- +

+ {TYP_NAMEN[geraet.typ]} +

)} -
+ {erkannt && info.beschreibung.length > 0 && ( +

{info.beschreibung}

+ )} + {!erkannt && ( +

+ {geraet.typ === 'cd' + ? 'Titel kommen beim Rippen von MusicBrainz.' + : 'Noch keine Zuordnung — „Ändern …" hilft nach.'} +

+ )} + +
+ + 0 ? gb(geraet.groesseBytes) : '—'} /> + 0 ? ` · ${info.quelle}` : ''}` + : '—' + } + farbe={erkannt ? (info.confidence >= 0.8 ? 'text-gruen' : 'text-warn') : undefined} + /> + +
+ +
+ {erkannt && ( + = 0.8 ? 'gruen' : 'gelb'} + /> + )} {info !== undefined && info.schonGerippt !== null && ( )}
- {geraet.grund.length > 0 &&

{geraet.grund}

} + {geraet.grund.length > 0 &&

{geraet.grund}

}
)} {rip !== undefined && ( -
-
-

- {PHASEN_NAMEN[rip.phase]} - {rip.lesefehler && rip.phase === 'fertig' ? ' — mit Lesefehlern' : ''} -

- {ripLaeuft && rip.prozent >= 0 && ( -

- {rip.prozent} % - {restzeitText(rip.prozent, ripStartMs)} +

+
+
+
+ + {PHASEN_MONO[rip.phase]} + {rip.lesefehler && rip.phase === 'fertig' ? ' — MIT LESEFEHLERN' : ''} + + {ripLaeuft && ( + {restzeitText(rip.prozent, ripStartMs)} + )} +
+ {ripLaeuft && } +

+ > {rip.text}

+ {rip.dateien.map((datei) => ( +

+ {datei} +

+ ))} +
+ {ripLaeuft && rip.prozent >= 0 && ( +
+ {rip.prozent} + % +
)}
- {ripLaeuft && ( -
-
-
- )} -

{rip.text}

- {rip.dateien.map((datei) => ( -

- {datei} -

- ))} {protokoll.length > 1 && ( )} {protokollOffen && ( -
+
{protokoll.map((zeile, i) => ( -

- {zeile} +

+ > {zeile}

))}
@@ -448,31 +528,35 @@ function LaufwerkKachel({ ) } -// Die MakeMKV-Schlüssel-Karte (§ 9.1) — mit dem Update-Weg aus § 9: -// „MakeMKV aktualisieren" holt den Installer über die Quellenkette, -// prüft ihn und lässt Windows fragen (UAC). Danach prüft Rippy neu. +// Die MakeMKV-Karte (§ 9.1) — amber Glas mit dem Update-Weg aus § 9. function SchluesselKarte({ verbindung }: { verbindung: Verbindung }) { const s = verbindung.schluessel const b = verbindung.beschaffung - const zustand: 'ok' | 'fehler' | 'wartet' = - s === null ? 'wartet' : s.angenommen === true ? 'ok' : s.weiterBetrieb ? 'wartet' : 'fehler' const punktFarbe = - zustand === 'ok' ? 'bg-emerald-400' : zustand === 'fehler' ? 'bg-red-400' : 'bg-amber-400' + s === null + ? 'bg-warn' + : s.angenommen === true + ? 'bg-gruen' + : s.weiterBetrieb + ? 'bg-warn' + : 'bg-rot' return ( -
-
+
+
+ + + MAKEMKV-SCHLÜSSEL +
- -

MakeMKV-Schlüssel

- {s !== null && s.version.length > 0 && } -
-
+ {s !== null && s.version.length > 0 && ( + MakeMKV {s.version} + )} {s !== null && s.angenommen === false && ( @@ -480,46 +564,51 @@ function SchluesselKarte({ verbindung }: { verbindung: Verbindung }) {
-
- {s === null ? ( -

wird geprüft …

- ) : ( - <> -

{s.text}

-

- zuletzt geprüft: {s.geholtAm.slice(0, 16).replace('T', ' ')} - {s.gueltigBis.length > 0 && ` · Key gültig bis ${s.gueltigBis}`} -

- - )} - {b !== null && ( -
-

- {b.text} -

- {b.laeuft && b.anteil !== null && ( -
-
-
- )} -
- )} -
+ {s === null ? ( +

wird geprüft …

+ ) : ( + <> +

{s.text}

+

+ zuletzt geprüft: {s.geholtAm.slice(0, 16).replace('T', ' ')} + {s.gueltigBis.length > 0 && ` · Key gültig bis ${s.gueltigBis}`} +

+ + )} + {b !== null && ( +
+

+ {b.text} +

+ {b.laeuft && b.anteil !== null && ( +
+
+
+ )} +
+ )}
) } // ── Einstellungen ─────────────────────────────────────────────────────── +const FELD_KLASSE = + 'mt-1 w-full rounded-lg border border-white/12 bg-samt px-3 py-2 font-mono text-xs text-schnee placeholder:text-nebel focus:border-gold/60 focus:outline-none' + +function Kicker({ text }: { text: string }) { + return

{text}

+} + function Feld({ name, wert, @@ -537,8 +626,8 @@ function Feld({ useEffect(() => setEntwurf(wert), [wert]) return ( ) @@ -569,12 +658,8 @@ function PresetWahl({ }) { return (