Commit Graph

329 Commits

Author SHA1 Message Date
marcus 014b11abd2 Doku: R4 Dokumentations-Review umgesetzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 15:04:56 +02:00
marcus 6ff463b7ef Repository aufräumen 2026-04-28 09:57:11 +02:00
marcus 8bb0aabb51 Code aufräumen 2026-04-28 08:52:59 +02:00
marcus 27b4292c2f Imports aufgeräumt 2026-04-28 08:44:24 +02:00
marcus 0b5a441a5d Fix #44: Ersetze Status-Icons durch Unicode-Symbole mit Farben
- SUCCESS: ✓ (U+2713) mit #2e7d32 (grün)
- FAILED_RETRYABLE: ↻ (U+21BB) mit #d98200 (orange)
- FAILED_PERMANENT: × (U+00D7) mit #c62828 (rot)
- SKIPPED_ALREADY_PROCESSED: ≡ (U+2261) mit #1565c0 (blau-grau)
- SKIPPED_FINAL_FAILURE: ⊘ (U+2298) mit #757575 (grau)

Die neue Methode statusColor() gibt CSS-Hex-Farben zurück.
Font: 16px, font-weight: bold

Betroffene Dateien:
- GuiBatchRunResultRow.java: neue statusColor() Methode
- GuiBatchRunResultRowTest.java: Icons aktualisiert
- GuiBatchRunCoordinatorTest.java: Icons aktualisiert
- GuiBatchRunTabSmokeTest.java: Icons aktualisiert

Alle 323 Tests bestanden.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-28 07:55:49 +02:00
marcus 3877359b42 Fix #24: Meldungs-ListView dehnt sich vertikal aus
setMaxHeight(200) entfernt und VBox.setVgrow(messagesListView, ALWAYS)
gesetzt. Die ListView füllt nun den verbleibenden Platz innerhalb der
Meldungs-Card; der Button darunter bleibt davon unberührt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 07:35:21 +02:00
marcus 769d15fd86 Fix #24: Meldungsbereich füllt verbleibenden vertikalen Platz
VBox.setVgrow(card, ALWAYS) auf dem Meldungs-Card macht die Sektion
dehnbar innerhalb von sectionsBox. Damit das VGrow überhaupt wirken kann,
wurde scrollPane.setFitToHeight(true) ergänzt – ohne diese Voraussetzung
bleibt tabContent auf seine natürliche Höhe beschränkt und das VGrow
läuft ins Leere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 07:28:10 +02:00
marcus 6317a27378 Fix #24: GridPane-Platzhalter in Provider-Block auf managed=false gesetzt
Die leeren Label-Spacer in Spalte 0 der Fehler- und Info-Zeilen des
fieldGrid waren managed=true (JavaFX-Default). JavaFX reservierte dadurch
pro Zeile ~20px Hoehe, selbst wenn das eigentliche Fehler-Label in
Spalte 1 unmanaged/unsichtbar war. Fuenf betroffene Zeilen (baseUrl-Fehler,
Timeout-Fehler, Modell-Fehler, API-Key-Fehler, API-Key-Herkunft) erzeugten
zusammen ~96-120px ungenutzten Leerraum unterhalb der Felder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 07:20:43 +02:00
marcus 4fa4c152a5 Fix #24: Provider-Bereich kompakter – VBox-Spacing auf 2px, unteres Padding auf 4px 2026-04-28 06:56:00 +02:00
marcus ec23b2455a Fix #24: Provider-Bereich kompakter – Spacing und Padding an Pfade-Bereich angepasst
- VBox-Spacing in Provider-Block von 2 auf 4 Pixels erhöht (wie im Pfade-Bereich)
- Padding in der Provider-Box von 6px auf 8px erhöht (wie im createCardContainer)
- Abstände zwischen Basis-URL, Modell und API-Key sind nun einheitlich mit dem Pfade-Bereich

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 19:04:02 +02:00
marcus 7f2cccf317 Fix #35: Einzelinstanz-Schutz ueber Loopback-ServerSocket
Eine zweite parallele Instanz wird beim Start abgewiesen. Der Schutz
greift fuer GUI- und Headless-Pfad gleichermassen vor der Modusweiche
in BootstrapRunner.

Umsetzung als ServerSocket-Bind auf 127.0.0.1:47832: stale-lock-frei,
da das Betriebssystem den Port beim Prozessende automatisch freigibt,
robust unter Windows mit gemappten Laufwerken und UNC-Pfaden, und ohne
Konflikt mit dem bestehenden RunLockPort, der nur den Batch-Lauf
schuetzt. Bei kollidierender Bindung erscheint im GUI-Modus ein
Swing-Dialog (JavaFX ist hier noch nicht initialisiert) und im
Headless-Modus eine Logmeldung; beide Pfade enden mit Exit-Code 1.
Ein ShutdownHook und try-with-resources geben den Port deterministisch
wieder frei.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:11:54 +02:00
marcus a5fae8cf55 Fix #42: KI-Prompt weist explizit zur Kuerzung bei Zeichenlimit an
Wenn die KI einen Titel nahe am Zeichenlimit erzeugt, kam es regelmaessig
zur Limit-Ueberschreitung um wenige Zeichen, was den Lauf in
FAILED_RETRYABLE und nach Wiederholung in FAILED_FINAL trieb.

Der Default-Prompt enthaelt jetzt eine explizite Kuerzungsanweisung
(Abkuerzungen, kompaktere Datumsformate, Weglassen unwichtiger Details)
mit dynamisch eingesetztem Zeichenlimit. Die bestehende Validierung
in AiResponseValidator bleibt als Sicherheitsnetz unveraendert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:11:42 +02:00
marcus 191d398604 Fix #44: Differenzierte Icons fuer alle Verarbeitungsstatus
Die beiden SKIPPED-Statuswerte teilten sich bisher Icon und Farbe.
Jeder Status erhaelt jetzt ein eigenes Unicode-Icon und passende Farbe:
SUCCESS gruen, FAILED_RETRYABLE orange, FAILED_PERMANENT rot,
SKIPPED_ALREADY_PROCESSED blau (Naechster-Track), SKIPPED_FINAL_FAILURE
grau (Durchgestrichener Kreis).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 16:11:32 +02:00
marcus f204ad1f1e Fix #24 (fortgesetzt): Provider-Bereich kompakter, Meldungen kleiner, Abstände reduziert
- Basis-URL und Timeout stehen jetzt nebeneinander (4-Spalten-Layout,
  Timeout schmal rechts), Modell und API-Key belegen jeweils volle Breite
- Meldungsbereich: minHeight/prefHeight von 140px auf 60px reduziert
- Sektionsabstände (sectionsBox spacing) von 12 auf 4px reduziert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:40:27 +02:00
marcus ac3513504d Fix #24 (fortgesetzt): Restliche Bereiche der Konfigurationsseite kompakter
Verbesserungen für kompakteres Layout:
- createCardContainer(): spacing 8→4, padding 12px→8px
- createFieldGrid(): vgap 8→4 (reduziert vertikale Abstände)
- createProviderBlock():
  * spacing 8→2, padding 10px→6px
  * Basis-URL, Modell, Timeout, API-Key in kompaktem GridPane
  * Reduzierter vertikaler Abstand zwischen Feldern
- createProcessingLimitsSection():
  * Umgestellt auf 2-Spalten-GridPane für Feldgruppen
  * Max. Seiten + Max. Zeichen nebeneinander
  * Max. Titellänge + Max. Retries nebeneinander
  * Log-Level + Sensible KI-Ausgabe nebeneinander
- Abstände zwischen Sektionen global reduziert:
  * sectionsBox spacing: 12→6
  * tabContent spacing: 8→4

Ziel: Konfigurationsseite passt jetzt komplett auf 1920x1080 ohne Scrollen.
Alle Kommentare auf Deutsch.

Build: .\mvnw.cmd clean verify -pl pdf-umbenenner-adapter-in-gui --also-make
Build-Status: ERFOLGREICH (322 Tests bestanden)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 15:28:11 +02:00
marcus 65d8379c15 Fix #24 (teilweise): Pfade-Bereich kompakter gestalten
- Quellordner + Zielordner nebeneinander in 2-Spalten-Layout
- SQLite-Datei + Prompt-Datei nebeneinander in 2-Spalten-Layout
- Vertikale Abstände zwischen Feldern reduziert (von 0 zu 4)
- Lock-Datei und Log-Verzeichnis in ausklappbare TitledPane verschoben
  (standardmäßig eingeklappt, Label: "Weitere Optionen (Click zum Aufklappen)")
- Neue Hilfsmethode buildTwoPathFieldsRow() für 2-Spalten-Pfad-Layouts
- Import für TitledPane hinzugefügt
- Alle Kommentare auf Deutsch

Build: .\mvnw.cmd clean verify -pl pdf-umbenenner-adapter-in-gui --also-make
Build-Status: ERFOLGREICH (322 Tests bestanden)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 15:17:56 +02:00
marcus a3642608b4 Fix #48: Fehlerbehandlung für Legacy-Datumsformat in stringToInstant()
Fehler: stringToInstant() in SqliteDocumentRecordRepositoryAdapter verwendete
nur Instant.parse(), das nur ISO-8601 versteht. Ältere DB-Einträge haben aber
das Format 'yyyy-MM-dd HH:mm:ss' (Leerzeichen statt T, kein Z).

Lösung: Fallback-Logik implementiert
1. Versuch: Instant.parse() für ISO-8601 Format
2. Fallback: DateTimeFormatter für 'yyyy-MM-dd HH:mm:ss' → als UTC parsen

Die Methode bleibt robust und gibt null zurück, wenn beide Parser fehlschlagen.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 14:33:36 +02:00
marcus ff86a07f0e Fix #48: Korrekte Abschlussmeldung bei SKIPPED-only-Läufen und neutrale Farbe
Problem 1: Falsche Meldung bei reinen SKIPPED-Läufen (0 failed, >0 skipped)
- Die Nachricht enthielt immer "fehlgeschlagen" auch wenn failedCount=0
- Neue Logik formuliert Meldung basierend auf tatsächlichen Zählern

Problem 2: Falsche rote Farbe für SKIPPED-only-Läufe
- Farblogik basierte auf Keywords im Text statt auf failedCount
- Neue Logik färbt rot nur wenn failedCount > 0
- Farbe neutral (kein Hintergrund) für SKIPPED-only-Läufe

Neue buildSummaryMessage()-Methode mit drei Fällen:
- Alle erfolgreich (0 failed, 0 skipped)
- Nur übersprungen (0 failed, >0 skipped)
- Mit Fehlern (failed > 0)

Neue Farblogik in appendSummary() direkt auf failedCount basieren.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 14:15:18 +02:00
marcus d9670ddfbe Fix #47: Hinweisbereich im Verarbeitungslauf-Tab verbessert
- Padding unterhalb der Selektions-Button-Leiste ergaenzt
- Hinweisbereich wird nur eingeblendet wenn eine Meldung vorliegt
- Farbliche Unterscheidung: Erfolg gruen, Fehler rot, neutral Standard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 14:01:32 +02:00
marcus 03b23eb6a9 Fix #46: Button 'Zuruecksetzen auf KI-Vorschlag' wird korrekt deaktiviert
Der Reset-Button wird jetzt explizit deaktiviert, wenn kein KI-Vorschlag
vorhanden ist (finalFileName ist Optional.empty()). Die Bedingung in
refreshUiState() wurde geklaert und dokumentiert.
2026-04-27 13:51:36 +02:00
marcus 1d77173c49 Fix #31: Manuelle Dateinamen-Eingabe für nicht verarbeitete Dateien
Nicht-erfolgreiche Zeilen (FAILED, FAILED_RETRYABLE, SKIPPED_FINAL_FAILURE)
können im Detailbereich des Verarbeitungslauf-Tabs nun einen manuellen
Zieldateinamen erhalten. Beim Bestätigen wird die Quelldatei mit dem
benutzerdefinierten Namen ins Zielverzeichnis kopiert und der Stammsatz
atomar auf SUCCESS gehoben.

Neuer Inbound-Port ManualFileCopyUseCase mit sealed Result-Hierarchie,
Default-Implementierung mit Best-Effort-Rollback bei Persistenzfehler
sowie GUI-Brücke GuiManualFileCopyPort. Die GUI entscheidet anhand des
Status zwischen Umbenennen (SUCCESS, SKIPPED_ALREADY_PROCESSED) und
Kopieren (FAILED_*, SKIPPED_FINAL_FAILURE).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:22:44 +02:00
marcus fb0e9809f6 Fix #45: Fehlender Abstand unterhalb der PDF-Navigationsbuttons
Die Buttons 'Vorherige' und 'Naechste' in der PDF-Vorschau hatten keinen
Abstand nach unten zur Trennlinie/zum Panel-Rand. Padding unten am navBar-
Container (HBox) hinzugefügt, konsistent mit dem Padding oben.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 12:47:58 +02:00
marcus c3f8103572 Fix #43: Benutzerfreundliche Fehlermeldungen bei FAILED-Einträgen im Detailbereich
Jeder Fehlertyp erhält jetzt eine präzise deutsche Meldung:
- Kein lesbarer Text (NO_USABLE_TEXT) → OCR-Hinweis
- Titel zu lang → Titeltext + tatsächliche Länge + Limit
- Defekte/nicht extrahierbare PDF → Beschädigungshinweis
- Verbindungsfehler/Timeout → Verbindungs- und Konfigurationshinweis
- Unbekannter Fehler → neutraler Fallback ohne Log-Verweis

Der Verweis auf "Details im Anwendungslog" wurde vollständig entfernt.
Das "Fehler:"-Präfix in buildDetailText() entfällt; bei vorhandener
Fehlermeldung wird NO_REASONING_TEXT unterdrückt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 12:24:50 +02:00
marcus 3f5602de01 Fix #30: Detailbereich bei SKIPPED-Zeilen mit historischen Informationen befüllen
- Teile DocumentCompletionStatus.SKIPPED in SKIPPED_ALREADY_PROCESSED und
  SKIPPED_FINAL_FAILURE auf, um den Skip-Grund unterscheidbar zu machen
- Führe neuen Typ HistoricalDocumentContext ein (lastTargetFileName,
  lastSuccessInstant, lastFailureInstant, wasEverSuccessful)
- Führe ResolveHistoricalDocumentContextUseCase und
  DefaultResolveHistoricalDocumentContextUseCase ein
- Ersetze GuiHistoricalFileNamePort durch GuiHistoricalDocumentContextPort
- Lade historischen Kontext für übersprungene Zeilen im Coordinator-Worker-Thread
- Zeige im Detailbereich je nach Skip-Grund:
  SKIPPED_ALREADY_PROCESSED: "Bereits erfolgreich verarbeitet am [Datum]. Zieldatei: [Name]."
  SKIPPED_FINAL_FAILURE: "Endgültig fehlgeschlagen am [Datum]. Erneute Verarbeitung nur nach Reset möglich."
- Passe alle betroffenen Tests an

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 12:00:27 +02:00
marcus 1db6e27be8 Fix #41: Historischen KI-Dateinamen für übersprungene Dokumente in Ergebnistabelle anzeigen
Neue Komponenten:
- ResolveHistoricalFileNameUseCase (port/in) und DefaultResolveHistoricalFileNameUseCase (usecase)
- GuiHistoricalFileNamePort (GUI-interner Port, folgt dem Muster von GuiManualFileRenamePort)

GuiBatchRunCoordinator ruft in toRow() für SKIPPED-Zeilen ohne finalName den
historicalFileNamePort auf und trägt den Rückgabewert als neuen Dateinamen ein.

Bootstrap verdrahtet resolveHistoricalFileNameForGui als GuiHistoricalFileNamePort
und übergibt ihn über GuiStartupContext an den GUI-Adapter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 10:54:31 +02:00
marcus 385bda5331 Fix #39, #40: Behebe UX-Bugs im Detailbereich des Tabs Verarbeitungslauf
Issue #40: Entferne Platzhaltertext aus Dateiname-Eingabefeld
- Der promptText "Basisname ohne .pdf" wird nicht mehr angezeigt,
  wenn keine Zeile selektiert ist
- Das Feld bleibt leer und ausgegraut im deaktivierten Zustand

Issue #39: Addiere fehlende rechte Padding zu Detailbereich
- Der Detailbereich hatte nur links Padding (SECONDARY_SPACING),
  aber nicht rechts
- Jetzt ist das Padding symmetrisch auf beiden Seiten

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 10:29:38 +02:00
marcus 5d4230b4cb Fix #25: Entferne statisches .pdf-Label neben Dateinamen-Eingabefeld
Das überflüssige Label rechts neben dem Dateinamen-Eingabefeld in der
FileNameEditorPane wird entfernt. Die Dateiendung ist implizit bekannt.

- Entferne Feldinitialisierung extensionLabel
- Entferne Label aus HBox inputRow
- Entferne entsprechende Initialisierungen
- Aktualisiere JavaDoc-Kommentar

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-27 09:58:05 +02:00
marcus 3feafcbce8 Fix #36: Falscher Verwerfen-Dialog beim Klick auf Dateiname uebernehmen
Nach erfolgreicher Umbenennung loeste resultItems.set() in
upsertResultRowByFingerprint() den selectedItemProperty-Listener aus,
der handleSelectionChange() mit noch aktivem Dirty-State aufrief.

Drei Korrekturen:
1. fileNameEditor.clearDirtyState() in handleRenameResult() vor dem
   Zeilen-Upsert: setzt lastSavedName = aktueller Textfeldinhalt, damit
   isDirty() false ist bevor die Tabellenzeile ersetzt wird.
2. selectionSyncInProgress-Schutz um resultItems.set() in
   upsertResultRowByFingerprint(): unterbindet mehrfache JavaFX-interne
   Change-Events (oldRow > null > newRow) waehrend des Upserts.
3. Neue Methode FileNameEditorPane.clearDirtyState() eingeführt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 08:47:58 +02:00
marcus 5165ea6f1d Icons hinzugefügt. 2026-04-25 08:26:26 +02:00
marcus 0e20f93c0d Feature #21/#20: Anwendungs-Icon und System-Tray einbinden
Schließt Issue #21: Alle vier Icon-Größen (16/32/64/128 px) werden beim
Start am primären Stage gesetzt; JavaFX wählt automatisch die passende
Größe je nach Kontext (Titelleiste, Taskleiste, Alt+Tab).

Schließt Issue #20: Beim Klick auf den X-Button wird das Fenster in den
Windows System-Tray minimiert (stage.hide()) statt die Anwendung zu
beenden. Platform.setImplicitExit(false) hält die JavaFX-Runtime aktiv.
Das Tray-Icon zeigt ein Kontextmenü mit "Öffnen" und "Beenden";
Doppelklick öffnet das Fenster ebenfalls. Beim Beenden über das Tray-Menü
wird das Icon sauber entfernt.

Die gesamte AWT-Tray-Logik ist in SystemTrayManager gekapselt. Der
Headless-Betrieb bleibt unberührt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 08:24:54 +02:00
marcus 234b3461b7 Doku #34: Dokumentation auf V2.9-Stand aktualisieren
- CLAUDE.md: Aktiver Implementierungsstand auf V2.9 aktualisiert;
  neue Klassen (PdfPreviewPane, FileNameEditorPane, ManualFileRenameUseCase,
  FilesystemTargetFileRenameAdapter, GuiManualFileRenamePort) und neuer Port
  (TargetFileRenamePort) dokumentiert; PDFBox-Direktrendering, Vollbild-Start
  und automatisches Laden der letzten Konfiguration beschrieben
- README.md: Versionshinweis auf V2.9 aktualisiert; neue Features genannt
- docs/betrieb.md: Startverhalten (Vollbild, letzte Konfiguration automatisch laden)
  ergaenzt; GUI-Tab-Beschreibung um PDF-Vorschau und Dateiname-Editor erweitert
- docs/gui-bedienanleitung.md: Abschnitt 2.1 fuer automatisches Laden aktualisiert;
  neuer Abschnitt 13b fuer PDF-Vorschau und editierbaren Dateiname-Bereich
- docs/befundliste.md: V2.9-Fixes (#27, #28, #29, #33) dokumentiert
- docs/specs/technik-und-architektur.md: TargetFileRenamePort in Port-Liste
  ergaenzt; PDFBox-Direktrendering im Adapter-Out-Abschnitt erwaehnt
- docs/specs/fachliche-anforderungen.md: Nicht-Ziele praezisiert;
  neuer Abschnitt 14a fuer manuelle Dateiname-Korrektur nach Verarbeitungslauf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:56:13 +02:00
marcus 6b078aa3e7 Fix #33: Letzte Konfigurationsdatei beim Neustart automatisch laden
Nutzt java.util.prefs.Preferences mit dem Schluessel "lastConfigPath"
um den Pfad der zuletzt geladenen Konfigurationsdatei zu speichern.

Beim naechsten Start wird diese Datei automatisch geladen, sofern sie
noch existiert. Falls nicht oder falls nie eine Datei geladen wurde,
startet die GUI normal ohne Fehlermeldung.

Geaenderte Klassen:
- GuiConfigurationEditorWorkspace: Speichern des Pfads nach erfolgreichem Laden,
  neue Methode autoLoadLastConfiguration() fuer automatisches Laden beim Start
- PdfUmbenennerGuiApplication: Aufruf von autoLoadLastConfiguration() nach
  Initialisierung des Fensters

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:27:52 +02:00
marcus 7e2fec4c7b Fix #28: Anwendung standardmaessig im Vollbild starten
Fuegt stage.setMaximized(true) in PdfUmbenennerGuiApplication.start() hinzu,
so dass das Fenster beim Start automatisch maximiert wird.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:22:51 +02:00
marcus 591c7ff94c Fix #29: Eigenes PDF-Rendering mit PDFBox statt PDFViewFX
Ersetzt die PDFView-basierte Vorschau durch direktes Rendering einzelner Seiten
mit PDFBox (Loader.loadPDF + PDFRenderer.renderImageWithDPI bei 120 DPI).
BufferedImage wird über SwingFXUtils.toFXImage in eine JavaFX-Image konvertiert
und in einer ImageView angezeigt. fit-to-view entsteht nativ durch Binding von
fitWidth/fitHeight an den StackPane-Bereich bei preserveRatio=true. Keine
Scrollbalken, keine Zoom-Einschraenkungen, Seitenanfang immer sichtbar.

Lazy Rendering mit In-Memory-Cache fuer bereits gerenderte Seiten; asynchrones
Oeffnen und Rendering auf pdf-preview-worker-Thread; "latest preview request
wins"-Prinzip bleibt erhalten. pdfviewfx-Abhaengigkeit aus adapter-in-gui pom
entfernt, pdfbox stattdessen explizit aufgenommen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:05:02 +02:00
marcus 673023d921 Fix #29: Fit-to-view via statischem A4-Fallback statt asynchronem Seitenverhältnis
Ersetzt fetchAspectRatioAsync/fitToView durch updateZoom() mit A4-Dimensionen
(595x842 Punkte) als Fallback. Scrollbalken werden per CSS und ScrollPane-Policy
ausgeblendet. Zoom-Listener auf pdfView statt viewStack.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:51:26 +02:00
marcus 71d79ab30c Fix #29: Layout-Umbau und fit-to-view PDF-Vorschau ohne Scrollbalken
GuiBatchRunTab: Buttons "Erneut verarbeiten" / "Status zurücksetzen" und
Meldungsbereich in die linke SplitPane-Spalte unterhalb der Tabelle
verschoben. Detailbereich (rechte Spalte) erstreckt sich dadurch vollständig
von oben bis unten – mehr Platz für die PDF-Vorschau.

PdfPreviewPane: Gesamten suppressScrollReset / ChangeListener-Code entfernt.
Seite wird jetzt immer fit-to-view ohne Scrollbalken angezeigt: Seitenverhältnis
wird asynchron per renderPage(0.05f) ermittelt, Zoom über setZoomFactor() gesetzt
und bei Größenänderungen der Anzeigefläche automatisch neu berechnet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:28:29 +02:00
marcus 8f4e18b248 Fix #29: Seitenanfang zuverlaessig via vvalue-Einmal-Listener erzwingen
Der bisherige ImageView-imageProperty-Listener mit Platform.runLater()
wurde von PDFViewFX nach dem Rendering noch einmal ueberschrieben, weil
die interne Scroll-Korrektur ebenfalls asynchron laeuft und spaeter
ausgefuehrt wird.

Neuer Ansatz: Nach jedem pdfView.load() und pdfView.setPage()-Aufruf
wird ein einmaliger ChangeListener auf die vvalueProperty des internen
ScrollPane registriert (scheduleScrollToTop). Sobald PDFViewFX seine
interne Scroll-Position durchschreibt und der Wert von 0 abweicht,
korrigiert der Listener ihn sofort auf 0 und entfernt sich danach selbst.
Damit greift der Eingriff immer nach dem internen PDFViewFX-Scroll,
unabhaengig von der Renderzeit.

Zusaetzlich wird ein aktiver Listener bei schnellen Seitenwechseln
(cancelScrollToTopListener) und bei clear() sauber aufgeraeumt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 14:58:43 +02:00
marcus 0387be0e96 Fix #27 und #29: pdfviewfx 3.3.2 und zuverlaessiger Seitenanfang via ImageView-Listener
pdfviewfx wird von 3.1.1 auf 3.3.2 aktualisiert. Version 3.3.1 behebt
'Do not interrupt rendering', wodurch ClosedByInterruptException bei
schnellem Seitenwechsel (#27 Folge-Bug) und das Ausbleiben weiterer
Renderings ab Seite 3+ (#29 Folge-Bug) nicht mehr auftreten.

Das 100-ms-PauseTransition-Workaround fuer den Seitenanfang wird ersetzt
durch einen Listener auf die imageProperty des internen ImageView der
PDFView-Skin. Der Listener scrollt erst dann zum Seitenanfang, wenn
das Rendering tatsaechlich abgeschlossen ist und pendingScrollToTop
gesetzt wurde (bei loadSource und Seitenwechsel-Buttons). Dadurch wird
der Seitenanfang zuverlaessig angezeigt, unabhaengig von der Renderzeit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 14:30:40 +02:00
marcus ca16855e81 Fix #27 und #29: Gezielter Scroll-Schutz und zuverlaessiger Seitenanfang
Bug #27: Den zu aggressiven ScrollEvent::consume-Filter durch einen
gezielten Filter auf dem internen ScrollPane der PDFView-Skin ersetzt.
Der Filter konsumiert nur dann, wenn die Seite keinen ueberlaufenden
Inhalt hat oder der Scroll-Inhalt an der oberen bzw. unteren Grenze
angekommen ist. Dadurch bleibt Inhalts-Scrolling innerhalb einer Seite
weiterhin moeglich; nur der Seitenwechsel per Mausrad wird verhindert.

Bug #29: Platform.runLater() durch eine PauseTransition (100 ms) ersetzt,
die nach dem vollstaendigen Rendering-Durchlauf der PDFView-Skin den
internen ScrollPane explizit auf vValue=0 zuruecksetzt. So wird der
Seitenanfang zuverlaessig angezeigt, ohne dass die Skin die Position
nachtraeglich ueberschreibt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:50:43 +02:00
marcus 7e31057bfa Fix #27 und #29: Mausrad-Seitenwechsel und Seitenanfang in PDF-Vorschau
Bug #27: ScrollEvent-Filter auf PDFView verhindert Seitenwechsel durch Mausrad.
Seitenwechsel sind ausschliesslich ueber die Navigations-Buttons moeglich.
Die Seitenzahl wird nur noch bei Button-Klick aktualisiert.

Bug #29: Nach dem Laden einer PDF und bei Seitenwechsel ueber Buttons wird
die Seite jetzt explizit von oben angezeigt. Der setPage()-Aufruf erfolgt
via Platform.runLater() nach dem Layout-Pass, sodass stets der Seitenanfang
sichtbar ist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:30:53 +02:00
marcus d3fbfc4094 V2.9: Integrierte PDF-Vorschau und editierbarer Dateiname im Verarbeitungslauf
Neu im Tab "Verarbeitungslauf":
- Integrierte PDF-Vorschau der Quelldatei mit Lazy Rendering (Seite 1 sofort,
  weitere Seiten on-demand), Cache pro Selektion, "latest preview request wins"
- Editierbarer KI-Dateinamenvorschlag mit Live-Validierung, Dirty-State-Dialog
  bei Zeilen-/Tabwechsel, Schließen und Laufstart, atomare FS+DB-Transaktion
  inkl. Rollback und Fingerprint-basierter Konfliktauflösung

Architektur:
- Neuer Application-Use-Case ManualFileRenameUseCase und Outbound-Port
  TargetFileRenamePort mit Filesystem-Adapter
- Neuer GuiManualFileRenamePort, verdrahtet im Bootstrap
- GuiBatchRunResultRow um correctedFileName erweitert
- GuiBatchRunTab auf SplitPane-Layout (60/40) umgebaut, Detail-Panel mit
  KI-Begründung, FileNameEditorPane und PdfPreviewPane
- Spike-Code (PdfViewerSpike) entfernt, produktive Implementierung ersetzt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:30:55 +02:00
marcus f6b265b370 Spezifikation für V2.9 (PDF Ansicht + manuelles Umbenennen hinzugefügt 2026-04-24 11:47:02 +02:00
marcus 3a98304a5c Refactoring #19: Benutzerfreundliche deutsche KI-Fehlermeldungen im Begründungsbereich
Neue Klasse AiFailureMessageTranslator übersetzt technische englische
Fehlertexte (z.B. HTTP-Statuscodes, Verbindungsfehler) in lesbare
deutsche Benutzerhinweise. GuiBatchRunTab nutzt den Translator vor der
Anzeige; das Datenmodell (aiFailureMessage) bleibt unverändert.
Unit-Tests decken alle definierten Mapping-Fälle ab.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:25:46 +02:00
marcus b87e8498e6 Fix #19: Fehlergrund bei fehlgeschlagenem KI-Aufruf im Begründungsbereich anzeigen
- DocumentCompletionEvent um optionales Feld failureMessage erweitert
- DocumentProcessingCoordinator leitet Fehlermeldung bei Fehler-Status durch
- GuiBatchRunResultRow um aiFailureMessage (Optional<String>) ergänzt
- GuiBatchRunCoordinator.toRow() befüllt aiFailureMessage aus dem Event
- GuiBatchRunTab.buildDetailText() zeigt bei fehlendem Reasoning und
  vorhandenem Fehlergrund: "⚠ Fehler: <Meldung>" vor dem Hinweistext
- Alle Tests angepasst und neue Unit-Tests für aiFailureMessage ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:17:43 +02:00
marcus 67275eb2f5 Fix #17: Fehler und Warnungen nicht mehr als INFO loggen
Verarbeitungsfehler (PreCheckFailed, AiFunctionalFailure) und
Retry-Entscheidungen (FAILED_RETRYABLE, FAILED_FINAL) werden nun auf
WARN-Level geloggt. EmptyList- und IncompleteConfiguration-Ergebnisse
des Modellabrufs sowie fehlende Quelldateien im Mini-Lauf ebenfalls.
Tests angepasst: Assertions prüfen jetzt das korrekte WARN-Level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 17:58:30 +02:00
marcus 955adc0c45 Fix #18: Leeres Datumsfeld wird als Fehler behandelt statt als fehlendes Datum
Die KI liefert manchmal "date": "" statt das Feld wegzulassen. Laut Spezifikation
ist date optional – fehlt es oder ist es leer, soll das aktuelle Datum als Fallback
verwendet werden.

Änderung in AiResponseValidator:
- Leere Strings (nach trim) werden identisch wie fehlende date-Felder behandelt
- Fallback auf aktuelles Datum über ClockPort mit DateSource.FALLBACK_CURRENT
- Validierungsfehler "date could not be parsed" wird nicht mehr geworfen

Neuer Test:
- validate_aiProvidesEmptyDateString_usesFallbackCurrentDate überprüft, dass
  "date": "" zum Fallback-Datum führt

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-23 17:42:39 +02:00
marcus e7f5590934 Fix #15: Log-Eintrag am Laufende liest lokale Zähler statt RunSummary
Synthetisierte Fehlzeilen (fehlende Quelldatei) werden nur im lokalen
failedCount gezählt, nicht in RunSummary. Der abschließende Log-Eintrag
verwendete bisher summary.failedCount() und zeigte daher fehlgeschlagen=0
obwohl die GUI-Anzeige korrekt war.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 17:25:00 +02:00
marcus c46294159c Fix #16: TitleCharacterRule um Punkt, Komma und Ampersand erweitern
- Erweitere TitleCharacterRule.isAllowed() um die Zeichen: . (Punkt), , (Komma), & (Ampersand)
- Passe JavaDoc-Kommentare auf Deutsch an
- Aktualisiere TitleCharacterRuleTest: ändere Punkt-Test von disallowed zu allowed
- Füge Tests für Komma und Ampersand hinzu
- Füge Tests hinzu, die Windows-Sonderzeichen (\ / : * ? " < > |) weiterhin als ungültig bestätigen
- Aktualisiere TargetFilenameBuildingServiceTest für den neuen Test-Fall
- Dokumentation: fachliche-anforderungen.md und CLAUDE.md aktualisiert

mvn clean verify erfolgreich bestanden

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-23 17:07:07 +02:00
marcus 1df541d0f9 Fix #15: Fehlende Quelldatei beim Mini-Lauf wird korrekt als fehlgeschlagen gezählt
appendSummary verwendete bisher summary.failedCount() aus dem RunSummary-Objekt
des echten Laufs, das von synthesizeMissingSourceFileRows() nachträglich hochgezählte
failedCount nicht kannte. Nun werden die lokalen Zähler (successCount, failedCount,
skippedCount) verwendet, die über onDocumentCompleted und die Synthese konsistent
gepflegt werden. Test ergänzt um Assertion auf '1 fehlgeschlagen' in der Zusammenfassung.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 16:16:39 +02:00
marcus 09605ee495 Fix V2.8: selectedRows-Leerproblem und isRunning()-Inkonsistenz behoben
- markSelectedRowsAsResetPending() schützt selectedRows jetzt mit
  selectionSyncInProgress=true, sodass der TableView-SelectionModel-
  Listener die Selektion nicht löscht, wenn Zeilen ersetzt werden
- isRunning() und updateButtonStates() verwenden runningProperty.get()
  statt coordinator.isRunning() für konsistentes Verhalten zwischen
  Button-Zustand und Selektion
- Diagnose-LOG am Anfang von handleReprocessSelected() gibt isRunning()
  und selectedRows.size() aus (Laufend=false, Selektion>0 erwartet)
- Alle [TEMP-TRACE]-Logs entfernt aus GuiBatchRunCoordinator,
  SqliteUnitOfWorkAdapter, SqliteDocumentRecordRepositoryAdapter
  und DocumentProcessingCoordinator

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-23 15:35:57 +02:00