# 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<String>` |
| `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
