diff --git a/docs/specs/V2_9_-_Spezifikation.md b/docs/specs/V2_9_-_Spezifikation.md new file mode 100644 index 0000000..10d02ba --- /dev/null +++ b/docs/specs/V2_9_-_Spezifikation.md @@ -0,0 +1,378 @@ +# V2.9 – Integrierte PDF-Vorschau und Dateinamen-Bearbeitung + +**Status:** Freigegeben +**Erstellt:** 2026-04-24 +**Überarbeitet:** 2026-04-24 (nach zwei ChatGPT-Reviews, finale Version) +**Autor:** Marcus (mit Claude als Mentor) + +--- + +## Ziel + +V2.9 erweitert den Tab „Verarbeitungslauf" um zwei eng verzahnte Funktionen: + +1. **Integrierte PDF-Vorschau** – beim Anklicken einer Zeile wird die erste Seite der + Quelldatei direkt im Detailbereich rechts gerendert (kein separates Fenster, kein + zusätzlicher Klick) +2. **Editierbarer Dateiname** – der von der KI vorgeschlagene Dateiname kann direkt + in der GUI korrigiert werden, bevor er als endgültig gilt + +Beide Funktionen zusammen ermöglichen einen natürlichen Review-Zyklus: +**KI benennt → Benutzer schaut rein → Benutzer korrigiert bei Bedarf → fertig.** + +--- + +## Hintergrund + +### Bisheriger Zustand + +- Der Detailbereich rechts zeigt nur KI-Begründung als TextArea +- Ob der vorgeschlagene Dateiname sinnvoll ist, kann der Benutzer nur anhand des + KI-Reasonings beurteilen – den tatsächlichen Dokumentinhalt sieht er nicht +- Der generierte Dateiname ist nach dem Lauf nicht mehr veränderbar +- Die Spike-Implementierung (PDFViewFX + jai-imageio-jpeg2000 für JBIG2-Unterstützung) + hat die technische Machbarkeit bereits bestätigt; der Spike-Code wird im Rahmen + von V2.9 durch produktionsreifen Code ersetzt + +### Motivation + +- Benutzer sollen schnell beurteilen können, ob der KI-Dateiname passt, + ohne ein externes Programm öffnen zu müssen +- Korrekturen sollen direkt in der Anwendung möglich sein – für nicht-technische + Benutzer (z. B. Familienmitglieder) ist das eine wesentliche UX-Verbesserung +- Die Anwendung wird vom reinen Batch-Prozessor zum assistierten + Dokumenten-Review-Werkzeug weiterentwickelt + +--- + +## Zielbild + +Nach Abschluss von V2.9 kann der Benutzer: + +1. Eine Zeile in der Ergebnisliste anklicken +2. Sofort die erste Seite der zugehörigen **Quelldatei** als Vorschau sehen – + ohne weiteren Klick, direkt im Detailbereich +3. Weitere Seiten bei Bedarf **auf Anfrage** laden (Lazy Rendering) +4. Den vorgeschlagenen Dateinamen **direkt in der GUI bearbeiten** und speichern +5. Den headless-Betrieb unverändert nutzen – V2.9 betrifft ausschließlich die GUI + +--- + +## Layout-Änderung im Tab „Verarbeitungslauf" + +### Bisheriges Layout + +``` +[ Fortschrittsbalken ] +[ Ergebnistabelle (~75% Breite) | KI-Begründung (~25%) ] +[ Buttons ] +[ Statuszeile ] +``` + +### Neues Layout + +``` +[ Fortschrittsbalken ] +[ Ergebnistabelle (~60% Breite) | Detailbereich (~40% Breite) ] +[ | KI-Begründung ] +[ | Dateiname (editierbar) ] +[ | PDF-Vorschau (Seite X/Y) ] +[ Buttons ] +[ Statuszeile ] +``` + +- Tabelle und Detailbereich sind durch einen **verschiebbaren Splitter** (SplitPane) + getrennt – der Benutzer kann das Verhältnis anpassen +- Standard-Split: 60% Tabelle / 40% Detailbereich +- Der Detailbereich ist vertikal aufgebaut: KI-Begründung oben (kompakt), + darunter Dateiname-Feld, darunter PDF-Vorschau (nimmt verfügbaren Restplatz) +- Die PDF-Vorschau rendert die erste Seite **„fit to width"** – Seitenverhältnis wird + beibehalten, die Seite füllt die verfügbare Panelbreite aus + +--- + +## Fachliche Anforderungen + +### PDF-Vorschau + +#### Grundverhalten + +- Beim Anklicken einer Zeile in der Ergebnisliste wird **automatisch** Seite 1 der + zugehörigen **Quelldatei** gerendert und im Vorschaubereich angezeigt +- Das Rendering erfolgt **asynchron im Hintergrund** – die GUI bleibt während des + Ladens reaktionsfähig +- Während des Renderings wird ein **Ladeindikator** (z. B. ProgressIndicator) angezeigt +- Die Vorschau zeigt immer die **Quelldatei**, nicht die umbenannte Zieldatei + +#### Lazy Rendering und Seitennavigation + +- Beim ersten Anklicken einer Zeile wird **ausschließlich Seite 1** gerendert +- Unterhalb der Vorschau wird die aktuelle Seite sowie die Gesamtseitenzahl + angezeigt: „Seite 1 / 12" +- Navigation: + - Button **„Nächste Seite"** lädt und rendert die jeweils nächste Seite on-demand + - Button **„Vorherige Seite"** lädt die vorherige Seite + - Bereits gerenderte Seiten werden **gecacht** – ein erneuter Wechsel auf eine + bereits gerenderte Seite erfordert kein erneutes Rendering + - Der Cache wird geleert wenn eine andere Zeile angeklickt wird +- Die Navigations-Buttons sind bei Seite 1 (Zurück) bzw. letzter Seite (Weiter) + deaktiviert + +#### Abbruchverhalten bei schnellem Wechsel (Latest Preview Request Wins) + +- Es gilt das Prinzip **„latest preview request wins"**: Wenn während eines laufenden + Renderings eine neue Vorschau-Anforderung eingeht – sei es durch Selektionswechsel + oder durch Seitennavigation innerhalb derselben PDF – wird das laufende Rendering + abgebrochen bzw. sein Ergebnis verworfen +- Nur das Ergebnis der zuletzt angeforderten Vorschau darf im Vorschaubereich landen +- Veraltete Render-Ergebnisse werden niemals angezeigt + +#### Fehlerfälle PDF-Vorschau + +| Situation | Verhalten | +|---|---| +| Quelldatei nicht mehr vorhanden | Meldung im Vorschaubereich: „Quelldatei nicht gefunden" | +| PDF nicht lesbar / korrupt | Meldung im Vorschaubereich: „PDF konnte nicht geöffnet werden" | +| PDF passwortgeschützt / verschlüsselt | Meldung im Vorschaubereich: „PDF ist passwortgeschützt und kann nicht angezeigt werden" | +| JBIG2-Bilder nicht vollständig dekodierbar | Seite wird teilweise gerendert; kein Fehler-Abbruch; kein Hinweis nötig | +| Kein Eintrag selektiert | Vorschaubereich zeigt neutralen Platzhaltertext | + +#### Technische Grundlage + +- Bibliothek: `com.dlsc.pdfviewfx:pdfviewfx` (bereits im Spike erfolgreich getestet) +- Zusatzabhängigkeit für JBIG2 und erweiterte Bildformate: + `com.github.jai-imageio:jai-imageio-jpeg2000` (bereits im Spike ergänzt) +- Der Spike-Code (`PdfViewerSpike.java`, Spike-Button in `GuiBatchRunTab`) wird + vollständig entfernt und durch die produktive Implementierung ersetzt +- Rendering läuft in einem dedizierten Background-Thread (nicht im JavaFX + Application Thread) + +--- + +### Editierbarer Dateiname + +#### Zustandsmodell + +Der Dateiname-Bereich kennt drei klar getrennte Zustände: + +| Zustand | Beschreibung | +|---|---| +| **KI-Vorschlag** | Der von der KI ursprünglich generierte Name – unveränderlich in der DB gespeichert; dient als Referenz für „Zurücksetzen auf KI-Vorschlag" | +| **Letzter gespeicherter Name** | Der zuletzt per „Dateiname übernehmen" bestätigte Name (= aktueller FS- und DB-Stand); ist nach dem Batch-Lauf zunächst identisch mit dem KI-Vorschlag | +| **Aktuelle Eingabe** | Der aktuell im Textfeld eingetippte, noch nicht gespeicherte Wert | + +**Anzeige-Regel:** Im Textfeld wird beim Selektieren einer Zeile immer der +**letzte gespeicherte Name** angezeigt – nicht der KI-Vorschlag. +Wurde noch nie manuell gespeichert, sind beide identisch. + +**Dirty-State-Regel:** Dirty-State besteht wenn die **aktuelle Eingabe** vom +**letzten gespeicherten Namen** abweicht. Der KI-Vorschlag ist keine Dirty-Basis. + +#### Anzeige + +- Unterhalb der KI-Begründung und oberhalb der PDF-Vorschau befindet sich ein + Bereich „Dateiname" +- Der Dateiname wird in einem **editierbaren Textfeld** (TextField) angezeigt +- Das Textfeld zeigt den **letzten gespeicherten Namen** ohne Dateierweiterung + (`.pdf` wird separat als nicht editierbares Label daneben angezeigt) +- Solange kein Eintrag selektiert ist, ist das Textfeld leer und deaktiviert +- Wenn das Textfeld vom letzten gespeicherten Namen abweicht (**Dirty State**), + wird dies durch eine visuelle Markierung am Textfeld angezeigt (z. B. farbiger Rand) + +#### Tastatur- und Schaltflächen-Verhalten + +| Aktion | Verhalten | +|---|---| +| **Enter** im Textfeld | Löst „Dateiname übernehmen" aus (sofern Validierung grün) | +| **Escape** im Textfeld | Verwirft aktuelle Eingabe; stellt **letzten gespeicherten Namen** wieder her | +| **„Dateiname übernehmen"** | Startet die atomare Speicher-Transaktion | +| **„Zurücksetzen auf KI-Vorschlag"** | Setzt das Textfeld auf den ursprünglichen KI-Vorschlag zurück (kein Speichern – nur Textfeld-Inhalt) | + +Hinweis: „Zurücksetzen auf KI-Vorschlag" und Escape haben **unterschiedliche Semantik**: +Escape = zurück zum letzten gespeicherten Stand; „Zurücksetzen" = zurück zum KI-Ursprung. + +#### Speichern-Transaktion (Alles oder Nichts) + +Das Speichern eines geänderten Dateinamens ist eine **atomare Operation** bestehend +aus zwei Persistenzschritten: + +1. Zieldatei im Dateisystem umbenennen +2. Eintrag in der SQLite-DB aktualisieren + +**Schlägt Schritt 1 oder 2 fehl, wird die gesamte Aktion abgebrochen:** +- Bereits durchgeführte Teilschritte werden zurückgerollt +- Dateisystem und DB bleiben im vorherigen Zustand +- Eine Fehlermeldung im Statusbereich informiert den Benutzer +- Das Textfeld behält den eingegebenen Wert – der Benutzer kann es erneut versuchen + +Nach erfolgreicher Transaktion (Projektionsschritt, nicht Teil der Transaktion): +- Tabellenspalte „Neuer Dateiname" wird aktualisiert +- Erfolgsmeldung im Statusbereich + +Mögliche Fehlerursachen für Schritt 1: Datei-Lock durch andere Prozesse (Scanner, AV), +fehlende Schreibrechte, Read-only-Dateisystem, Netzlaufwerk nicht erreichbar. + +#### Konfliktsemantik bei vorhandenem Zieldateinamen + +Existiert im Zielordner bereits eine Datei mit dem neu eingegebenen Namen, +wird anhand des **Fingerprints** (SHA-256 des Dateiinhalts) entschieden: + +| Situation | Verhalten | +|---|---| +| **Gleicher Fingerprint** | Dateien sind inhaltlich identisch → keine Aktion; Meldung im Statusbereich: „Identische Datei bereits vorhanden – keine Umbenennung nötig"; weder FS noch DB werden geändert | +| **Unterschiedlicher Fingerprint** | Warnung im Statusbereich; Dateiname im FS erhält automatisch ein Suffix `(1)`, `(2)` usw.; DB wird mit dem tatsächlichen neuen Namen inkl. Suffix aktualisiert | + +#### Validierung des Dateinamens + +Folgende Prüfungen erfolgen **live während der Eingabe**: + +| Prüfung | Verhalten bei Verletzung | +|---|---| +| Dateiname ist leer oder nur Leerzeichen | Speichern-Button deaktiviert, Hinweistext unterhalb des Feldes | +| Führende oder abschließende Leerzeichen | Speichern-Button deaktiviert, Hinweistext | +| Unerlaubte Zeichen (`\ / : * ? " < > \|`) | Speichern-Button deaktiviert, Hinweistext | +| Reservierte Windows-Namen (`CON`, `PRN`, `AUX`, `NUL`, `COM1`–`COM9`, `LPT1`–`LPT9`) | Speichern-Button deaktiviert, Hinweistext | +| Dateiname endet auf Punkt | Speichern-Button deaktiviert, Hinweistext | +| Dateiname + Zielpfad + `.pdf` überschreitet 259 Zeichen | Speichern-Button deaktiviert, Hinweistext | + +Die 259-Zeichen-Grenze ist eine **bewusste Produktregel** für maximale +Windows-Kompatibilität (Windows MAX_PATH = 260 Zeichen inkl. Null-Terminator). + +#### Zustände des Dateiname-Bereichs + +| Zeilenstatus | Verhalten | +|---|---| +| Kein Eintrag selektiert | Textfeld leer, deaktiviert | +| Eintrag mit Status `DONE` (erfolgreich) | Textfeld editierbar, letzter gespeicherter Name vorausgefüllt | +| Eintrag mit Status `FAILED_*` | Textfeld leer, deaktiviert (kein Dateiname vorhanden) | +| Eintrag mit Status `SKIPPED` | Textfeld deaktiviert | +| Lauf aktiv | Textfeld deaktiviert, alle Buttons deaktiviert | + +#### Verhalten bei fehlender Zieldatei + +Ist die Zieldatei zum Zeitpunkt des Speicherns nicht mehr im Zielordner vorhanden: + +- Schritt 1 der Transaktion schlägt fehl +- Gemäß Alles-oder-Nichts-Prinzip: DB wird **nicht** aktualisiert +- Fehlermeldung im Statusbereich: „Zieldatei nicht gefunden – Umbenennung nicht möglich" +- Das Textfeld behält den eingegebenen Wert + +#### Verhalten bei ungespeicherten Änderungen (Dirty State) + +Ein Hinweisdialog erscheint, wenn der Benutzer mit aktivem Dirty-State eine der +folgenden Aktionen ausführt: + +- Eine andere Zeile in der Ergebnistabelle anklicken +- Den Tab wechseln (Konfiguration ↔ Verarbeitungslauf) +- Die Anwendung schließen +- Einen neuen Lauf starten + +Dialog-Text: „Der Dateiname wurde geändert aber nicht gespeichert. Änderungen verwerfen?" +Optionen: **„Verwerfen"** (Dirty State wird geleert, Aktion wird fortgesetzt) / +**„Zurück"** (Dialog schließt, Benutzer bleibt im Textfeld) + +--- + +## Architektur + +### Manuelle Namenskorrektur als Application-Use-Case + +Die manuelle Dateinamen-Korrektur wird als **eigenständiger Application-Use-Case** +modelliert, nicht im GUI-Adapter implementiert: + +- Ein neuer Use-Case `ManualFileRenameUseCase` (o. ä.) kapselt die atomare Transaktion + aus FS-Rename + DB-Update +- Der `GuiBatchRunCoordinator` (GUI-Adapter) delegiert ausschließlich an diesen Use-Case +- Dateisystem- und DB-Zugriffe laufen ausschließlich über bestehende oder neue + Ports/Adapter – kein Direktzugriff aus dem GUI-Adapter +- Damit bleibt die hexagonale Architektur gewahrt und der Use-Case ist unabhängig + von der GUI testbar + +### Komponenten-Übersicht + +| Komponente | Änderung | +|---|---| +| `GuiBatchRunTab` | Hauptumbau: SplitPane, Detailbereich-Redesign, Spike-Code entfernen | +| `GuiBatchRunResultRow` | Neues Feld: `correctedFileName` als `Optional` | +| `GuiBatchRunCoordinator` | Delegiert Dateinamen-Korrektur an neuen Use-Case | +| `ManualFileRenameUseCase` | Neuer Application-Use-Case: atomares FS-Rename + DB-Update | +| `pom.xml` (GUI-Modul) | PDFViewFX + jai-imageio-jpeg2000 bleiben; Spike-Klasse entfernen | +| Domain / Ports | Ggf. neuer Port für Datei-Rename-Operation erforderlich | +| Headless-Betrieb | Unberührt | + +--- + +## Abhängigkeiten zwischen den Funktionen + +- PDF-Vorschau und editierbarer Dateiname sind **unabhängig voneinander nutzbar** +- Beide beziehen sich auf den in der Ergebnistabelle selektierten Eintrag +- Beim Selektionswechsel mit Dirty-State: Hinweisdialog erscheint (siehe oben) +- PDF-Vorschau-Cache wird beim Selektionswechsel geleert + +--- + +## Verhalten während eines laufenden Batch-Laufs + +- Der Detailbereich (PDF-Vorschau + Dateinamen-Editor) ist **vollständig deaktiviert** + während ein regulärer Lauf oder Mini-Lauf aktiv ist +- Bereits angezeigte Vorschau bleibt sichtbar, aber Navigation und Bearbeitung + sind gesperrt + +--- + +## Nicht in V2.9 enthalten + +- Löschen der Quelldatei nach Bestätigung (spätere Version) +- Vollständiger PDF-Viewer mit freiem Scrollen und Zoom (Issue #23: DPI-Optimierung) +- Historien-Tab / SQLite-Ansicht (Issue #7, V3.0) +- Automatischer Scheduler / System-Tray (Issues #20, #22) +- Kompakteres Layout der Konfigurationsseite (Issue #24) +- Anwendungs-Icon (Issue #21) + +--- + +## Abnahmekriterien + +### Fachliche Akzeptanz + +#### PDF-Vorschau +- [ ] Beim Anklicken einer Zeile wird Seite 1 der Quelldatei automatisch gerendert – ohne extra Klick +- [ ] Während des Renderings ist ein Ladeindikator sichtbar; die GUI bleibt reaktionsfähig +- [ ] Die Vorschau rendert „fit to width" mit beibehaltenem Seitenverhältnis +- [ ] Seitenanzahl wird angezeigt: „Seite 1 / X" +- [ ] „Nächste Seite" / „Vorherige Seite" laden Seiten on-demand +- [ ] Bereits gerenderte Seiten werden gecacht; Selektionswechsel leert den Cache +- [ ] Navigations-Buttons sind korrekt deaktiviert (erste / letzte Seite) +- [ ] Schneller Selektionswechsel oder Seitenwechsel während Rendering: nur das zuletzt angeforderte Ergebnis wird angezeigt (latest preview request wins) +- [ ] Quelldatei nicht vorhanden → verständliche Fehlermeldung im Vorschaubereich +- [ ] PDF nicht lesbar / korrupt → verständliche Fehlermeldung im Vorschaubereich +- [ ] PDF passwortgeschützt → verständliche Fehlermeldung im Vorschaubereich + +#### Dateiname-Editor +- [ ] Textfeld zeigt beim Selektieren den **letzten gespeicherten Namen** (nicht KI-Vorschlag) ohne `.pdf`-Erweiterung; `.pdf` als nicht editierbares Label daneben sichtbar +- [ ] Dateiname ist direkt im Textfeld editierbar +- [ ] Dirty-State (Abweichung von letztem gespeichertem Namen) wird visuell am Textfeld angezeigt +- [ ] Enter im Textfeld löst „Dateiname übernehmen" aus (wenn Validierung grün) +- [ ] Escape im Textfeld stellt den **letzten gespeicherten Namen** wieder her +- [ ] „Zurücksetzen auf KI-Vorschlag" setzt das Textfeld auf den KI-Ursprung zurück (ohne Speichern) +- [ ] Validierung prüft live: leer/nur Leerzeichen, führende/abschließende Leerzeichen, unerlaubte Zeichen, reservierte Windows-Namen, endet auf Punkt, Pfadlänge > 259 +- [ ] Bei Validierungsfehler: Speichern-Button deaktiviert, Hinweistext sichtbar +- [ ] „Dateiname übernehmen" ist atomar: FS und DB werden beide aktualisiert oder nichts davon +- [ ] Bei Fehler in FS oder DB: kein Teilupdate, Rollback, Fehlermeldung im Statusbereich, Textfeld behält Eingabe +- [ ] Nach Erfolg: Tabellenspalte und Statusbereich aktualisiert (Projektionsschritt) +- [ ] Dateikonflikt mit gleichem Fingerprint → keine Aktion, Meldung „Identische Datei bereits vorhanden" +- [ ] Dateikonflikt mit unterschiedlichem Fingerprint → Warnung, Suffix `(1)` usw., DB mit tatsächlichem Namen +- [ ] Zieldatei fehlt → Fehlermeldung, weder FS noch DB werden geändert +- [ ] Ungespeicherte Änderungen bei Selektionswechsel → Hinweisdialog erscheint +- [ ] Ungespeicherte Änderungen bei Tabwechsel → Hinweisdialog erscheint +- [ ] Ungespeicherte Änderungen beim App-Schließen → Hinweisdialog erscheint +- [ ] Ungespeicherte Änderungen bei Laufstart → Hinweisdialog erscheint +- [ ] Status `FAILED_*` und `SKIPPED` → Dateiname-Textfeld deaktiviert +- [ ] Während eines aktiven Laufs: Detailbereich vollständig deaktiviert + +### Technische DoD +- [ ] Spike-Button und `PdfViewerSpike.java` sind vollständig entfernt +- [ ] Tab „Verarbeitungslauf" zeigt Tabelle und Detailbereich nebeneinander (SplitPane, 60/40, verschiebbar) +- [ ] `ManualFileRenameUseCase` ist im Application-Modul implementiert und unabhängig von der GUI testbar +- [ ] headless-Betrieb ist unverändert funktionsfähig +- [ ] `mvn clean verify` ist grün