ChangeListener auf hvalueProperty feuert nicht zuverlässig: wenn hvalue
im Fit-Modus bereits 0.5 (oder identisch zum Reset-Wert) ist, gibt es
keine Wertänderung beim setFitToWidth(false), und der Listener läuft
nie an – der spätere JavaFX-eigene Reset auf 0.0 bleibt unkontrolliert.
AnimationTimer.handle() läuft einmal pro JavaFX-Frame, nach allen
Layout-, CSS- und Pulse-Passes des aktuellen Frames. Das ist der einzige
in JavaFX zuverlässige Mechanismus, um nach allem zu feuern, was JavaFX
in diesem Frame noch erledigt. stop() im ersten handle() macht den Timer
zum Single-Shot.
Folge-Zoom-Schritte (wasInFitMode == false) bleiben unverändert mit
einfachem Platform.runLater.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Beim Verlassen des Fit-Modus resettet JavaFX hvalue mehrfach auf 0.0,
auch nach unserem Platform.runLater-Aufruf. Verschachtelte runLater
können diesen Reset nicht zuverlässig überholen.
Lösung: Single-Shot-ChangeListener auf hvalueProperty. Er feuert beim
Reset, entfernt sich selbst und postet erst dann setHvalue(0.5)/
setVvalue(0.5) – garantiert nach dem Reset, ohne Timing-Annahmen.
Folge-Zoom-Schritte (wasInFitMode == false) bleiben unverändert mit
einfachem runLater.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Beim Verlassen des Fit-Modus (setFitToWidth(false)) löst JavaFX einen
H/V-Reset auf 0.0 aus, der innerhalb desselben Pulses passiert wie unser
setHvalue(0.5)-Aufruf im einfachen Platform.runLater. Resultat: Der Reset
überschreibt unseren Wert, die PDF springt links/oben bündig.
Lösung analog zu resetToFitView: doppelt verschachteltes runLater. Das
erste runLater stößt den Layout-Pass nach setFitToWidth(false) an; das
zweite feuert im darauffolgenden Pulse, wenn alle Layout-Folgen
abgeschlossen sind und setHvalue(0.5)/setVvalue(0.5) zuverlässig wirken.
Folge-Zoom-Schritte (wasInFitMode == false) bleiben mit einfachem
runLater und bewahren die aktuelle Scroll-Position.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Zwei zusammenwirkende Ursachen für die linksbündige Anzeige nach Zoom-Reset:
1. Die Property-Bindungen wurden vor setFitToWidth(true) gesetzt. Zu diesem
Zeitpunkt sizet der viewStack noch nach der zoom-großen ImageView, sodass
die Bindungen die imageView an die Zoom-Breite gekoppelt haben statt an
die Viewport-Breite.
2. Verbleibende H/V-Werte aus Pan-/Zoom-Modus (insbesondere hvalue=0.0 nach
Pan zum linken Rand) wurden nicht zurückgesetzt. Bei minimalsten
Rounding-/Border-Differenzen wirkt hvalue auch im fit-aktiven Modus und
richtet den Content links bündig aus.
Fix: setFitToWidth/Height(true) sofort; Bindings und setHvalue(0.5)/
setVvalue(0.5) im Platform.runLater nach abgeschlossenem Layout-Pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Beim ersten Zoom-Schritt sprang die ImageView abrupt von der visuell
sichtbaren Breite (durch fitHeight aspekt-erhaltend verkleinert) auf
naturalViewportWidth × zoomLevel, weil zoomLevel mit dem Wert 1.0 nicht
zur tatsächlich angezeigten Skalierung passte und gleichzeitig setFitHeight(0)
die Höhenrestriktion entfernte.
applyZoom() initialisiert nun beim Verlassen des Fit-Modus zoomLevel
auf currentVisualWidth / naturalImageWidth (= aktueller visueller
Skalierungsfaktor) und setzt naturalViewportWidth auf die natürliche
Bildbreite. Damit entspricht zoomLevel = 1.0 der pixel-genauen
Originaldarstellung. Der vom Caller intendierte Delta-Schritt wird vor
der Kalibrierung gesichert und nach der Kalibrierung auf den neuen
zoomLevel re-appliziert, damit applyZoom(zoomLevel + 0.10) nicht
unverändert auf den kalibrierten Wert feuert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bei aktivem fitToWidth/fitToHeight hat der ScrollPane keinen scrollbaren
Bereich – setHvalue(0.5)/setVvalue(0.5) sind in diesem Zustand wirkungslos.
Die Wiederherstellung der Property-Bindungen fitWidth/fitHeight an viewStack
versetzt den ImageView in exakt denselben Zustand wie nach der initialen
Konstruktor-Initialisierung. Der StackPane zentriert dann automatisch
über die bereits gesetzte Pos.CENTER-Ausrichtung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ein einzelner Platform.runLater-Aufruf kann feuern, bevor JavaFX das Layout
nach setFitToWidth(true) vollständig abgeschlossen hat. Durch Verschachtelung
eines zweiten runLater werden setHvalue(0.5) und setVvalue(0.5) erst nach dem
nächsten vollständigen Layout-Pass gesetzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug 1: deltaY vor Akkumulation auf einen Notch-Wert begrenzen.
Plattformspezifische Scroll-Multiplikatoren (Windows-Mausgeschwindigkeit,
hohe DPI-Mäuse) können Werte wie 120 statt 40 liefern. Ohne Normierung
akkumuliert sich ein Überlaufwert, der Folge-Events sofort auslöst.
Bug 2: resetToFitView() setzt nach setFitToWidth(true) explizit
scrollPane.setHvalue(0.5) und setVvalue(0.5) (nach layout()-Aufruf),
damit vorherige Pan-Scroll-Werte die Zentrierung nicht nachwirken.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
32c: ScrollPane.setPrefSize(0,0) und StackPane.setMinSize(0,0) verhindern,
dass der Vorschaubereich beim manuellen Zoom mitwächst.
32a: Zoom-Akkumulator nutzt if statt while – pro Mausrad-Raste wird genau
eine Zoom-Stufe (10 %) angewendet, auch bei großen deltaY-Werten.
32b: Beim ersten Zoom-Einstieg wird die Ansicht auf die Bildmitte
zentriert (H/V = 0.5). scrollPane.layout() vor der Scroll-Wert-
Restaurierung stellt sicher, dass die neuen Inhaltsgrenzen bekannt sind.
32d: Grab & Pan – im manuellen Zoom-Modus kann die Vorschau mit der Maus
verschoben werden. OPEN_HAND-Cursor signalisiert den Zoom-Modus,
CLOSED_HAND die aktive Pan-Geste.
32e: resetToFitView() setzt Pan-Zustand und Mauszeiger zurück, sodass
beim Laden einer neuen Datei der Fit-to-View-Modus vollständig
wiederhergestellt wird.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aufgabe 1 (#88): AiFailureMessageTranslator auf public gesetzt, damit der
Verlauf-Tab die technischen Fehlermeldungen in benutzerfreundliche deutsche
Texte übersetzen kann.
Aufgabe 2 (#77): Vollständige Bestandsaufnahme aller interaktiven GUI-Elemente.
13 neue Konstanten in GuiTooltipTexts ergänzt (Provider-Felder, Verarbeitungs-
limits, optionale Pfade, Vorschau-Navigation, Prompt-Buttons, Dateiname-Textfeld).
Alle fehlenden Tooltips in GuiConfigurationEditorWorkspace, GuiPromptEditorTab,
PdfPreviewPane und FileNameEditorPane gesetzt. Hartcodierte Strings in
GuiPromptEditorTab durch Konstantenreferenzen ersetzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strg+Klick, Shift+Klick und Strg+A (alle sichtbaren Eintraege) werden durch
JavaFX natuerlich unterstuetzt. Aktionsbuttons (Reset, Loeschen) arbeiten nun
auf allen selektierten Eintraegen. Bei Status-Reset wird ein Hinweis angezeigt,
wenn SUCCESS-Eintraege in der Auswahl enthalten sind (Partial-Success-Dialog).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Das Suchfeld löst loadOverview() nach 300 ms Tippinaktivität automatisch aus
(PauseTransition). Enter-Taste stoppt den Timer und sucht sofort. So wird die
Tabelle live gefiltert, ohne bei jedem Tastendruck eine DB-Anfrage zu starten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strg + Mausrad ändert den Zoomfaktor in 10-%-Stufen (Bereich 10–500 %).
Beim ersten Zoom verlässt die Vorschau den Fit-to-View-Modus; das ScrollPane
übernimmt dann die Scrollbarkeit. Laden einer neuen Datei setzt den Zoom
automatisch auf Fit-to-View zurück.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refreshHeader() setzt Tab-Titel auf '* Konfiguration' wenn editorState dirty ist.
Dialog bei Neu/Oeffnen/Schliessen war bereits vorhanden (unsavedChangesGuard).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GuiTooltipTexts: neue Konstanten fuer Batchrun-Buttons, Verlauf-Spalten,
KI-Begruendung, Fehlerbereich, Modell-Neu-Laden, Browser-Button, Prompt-Textarea.
Spaltenkopf-Tooltips via Label-als-Graphic-Pattern in GuiHistoryTab und
GuiBatchRunTab; Buttons in allen Tabs beruecksichtigt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
displayTextFor(ProcessingStatus) in ProcessingStatusPresentation ergaenzt.
Status-ComboBox als ComboBox<ProcessingStatus> mit StringConverter umgestellt;
Versuche-Tabelle und Detail-Statuslabel zeigen nun Anzeigetext statt Enum-Namen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
notifyRunEnded() in GuiHistoryTab ergänzt; GuiConfigurationEditorWorkspace
verdrahtet batchRunTab.runningProperty() und ruft notifyRunEnded() via
Platform.runLater() auf, sobald der Lauf endet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Schema-Analyse ergab Fall A: failure_message ist bereits in V1 vorhanden
und wird persistiert. Keine Flyway-Migration notwendig.
- GuiHistoryTab: TextArea 'Fehlerursache' ergaenzt; zeigt failure_message
des letzten Fehler-Attempts bei FAILED_FINAL, FAILED_RETRYABLE,
SKIPPED_FINAL_FAILURE; promptText-Platzhalter bei NULL/leer
- SqliteProcessingAttemptRepositoryAdapter: 1000-Zeichen-Limit fuer
failure_message vor Persistierung erzwungen (mit Kuerzungsmarkierung)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#90: Neuer technischer Prüfpunkt LOG_DIRECTORY_USABLE (12. Checkpoint):
- Zeigt konfigurierten log.directory-Wert und aufgelösten absoluten Pfad
- Prüft ob Verzeichnis beschreibbar/anlegbar ist (WARNING, kein ERROR)
- Liest tatsächlichen Log-Datei-Pfad via Log4j2 LoggerContext → RollingFileAppender
- LogDiagnosticsPort als neuer Outbound-Port (application-Modul)
- Log4jLogDiagnosticsAdapter als Implementierung im bootstrap-Modul
- TechnicalTestRequest erhält logDirectory-Feld
- GuiTechnicalTestCoordinator erhält logDirectoryProvider-Supplier
#89: docs/betrieb.md – MSI-Betrieb um Pfadwarnungen erweitert:
- Warnung: relative Pfade lösen sich in schreibgeschütztes C:\Program Files\ auf
- Warnung: Backslashes in .properties werden als Java-Escape-Sequenzen interpretiert
- Betroffene Parameter mit Empfehlung zu absoluten Forward-Slash-Pfaden
- Beschreibung des neuen Log-Verzeichnis-Prüfpunkts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nach Bestaetigung des Verwerfen-Dialogs fehlte der Aufruf, der den
Tab-Zustand zuruecksetzt. Neue Methode discardChanges() in
GuiPromptEditorTab setzt loadedContent, dirty und Tab-Titel zurueck;
ist der Tab sichtbar, wird loadPromptAsync() sofort ausgeloest, sonst
greift der bestehende selectedProperty-Listener beim naechsten Oeffnen.
GuiConfigurationEditorWorkspace ruft discardChanges() nach positivem
Bestaedigungsdialog auf. Neuer Smoke-Test verifiziert das Verhalten.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Einfuehren von GuiPromptEditorPortFactory als funktionalem Interface,
damit GuiConfigurationEditorWorkspace bei jedem Laden oder Speichern
einer Konfiguration einen passenden Port fuer den Prompt-Tab erzeugen
kann. GuiPromptEditorTab.notifyConfigurationChanged() aktualisiert Port,
Pfad und maxTitleLength und setzt Dirty-State sowie Tab-Titel zurueck.
BootstrapRunner uebergibt die Factory an GuiStartupContext. Damit werden
alle vier Symptome aus #79 behoben: leerer Tab, gesperrte Textarea,
fehlgeschlagenes Speichern und fehlender Dirty-State-Indikator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neuer Tab „Prompt" in der GUI-Hauptansicht ermöglicht das Lesen, Bearbeiten
und atomare Speichern der konfigurierten KI-Prompt-Datei ohne externen Editor.
Änderungen:
- PromptSaveResult: neue sealed interface mit Saved, WriteFailed, TargetDirectoryMissing,
AtomicMoveFailed als strukturierte Ergebnistypen für savePrompt()
- PromptPort: um savePrompt(String) erweitert (nicht mehr funktional – Teststubs angepasst)
- FilesystemPromptPortAdapter: savePrompt() mit Temp-Datei im selben Verzeichnis + ATOMIC_MOVE,
kein stiller Fallback bei AtomicMoveNotSupportedException
- DefaultPromptEditorUseCase: Use-Case-Klasse mit loadPrompt(), savePrompt(),
createDefaultPromptIfMissing() als Delegation an PromptPort und ResourceCreationPort
- GuiPromptEditorPort: GUI-internes Bridge-Interface (kein hexagonaler Port)
- GuiPromptEditorTab: JavaFX-Tab mit TextArea, Dirty-State-Tracking, Speichern/Reset/Anlegen,
injizierbare threadFactory + fxDispatcher für Testbarkeit
- GuiStartupContext: um promptEditorPort erweitert; alle Backward-Compat-Konstruktoren
und blank() mit noOpPromptEditorPort() versorgt
- GuiConfigurationEditorWorkspace: promptEditorTab integriert, Tab-Wechsel-Schutz erweitert
- BootstrapRunner: buildGuiPromptEditorPort() verdrahtet FilesystemPromptPortAdapter +
DefaultPromptEditorUseCase; noOpGuiPromptEditorPort() für Blank-Start-Fälle
- Tests: DefaultPromptEditorUseCaseTest, FilesystemPromptPortAdapterTest (savePrompt),
GuiPromptEditorTabSmokeTest (headless Monocle), GuiAdapterSmokeTest auf 3 Tabs aktualisiert
- docs/betrieb.md: Prompt-Tab dokumentiert, Pfad-Auflösungstabelle ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue Komponente BatchRunSummaryBanner aggregiert die Ergebnisliste nach
Laufende und zeigt je Kategorie Icon + Anzahl + Text an. Banner verschwindet
beim Start des nächsten Laufs. READY_FOR_AI, PROPOSAL_READY und PROCESSING
werden nicht gezählt (nicht im DocumentCompletionStatus-Enum enthalten);
Reset-Pending-Zeilen werden explizit ausgeschlossen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Neue zentrale Klasse ProcessingStatusPresentation als einzige autoritative
Quelle fuer Icons, CSS-Farben, Tooltip-Texte und Summary-Kategorielabels
aller DocumentCompletionStatus-Werte. GuiBatchRunResultRow delegiert
statusIcon() und statusColor() an diese Klasse und stellt neue Methode
statusTooltip() bereit. In GuiBatchRunTab erhalten Status-Icons Tooltips
per CellFactory; die duplizierte private statusColor()-Methode entfaellt.
Fuer FAILED_PERMANENT wird im Detailbereich ein erweiterter Erklaerungstext
gemaess Spezifikation #51 angezeigt. Unit-Tests fuer ProcessingStatusPresentation
(alle Status, Eindeutigkeit, korrekte Mapping-Werte) und statusTooltip() in
GuiBatchRunResultRowTest ergaenzt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ${revision}-Property im Parent-POM eingeführt; alle Kind-POM-<parent>-Blöcke
verwenden ${revision} statt hartkodierter Version
- flatten-maven-plugin 1.6.0 in <build><plugins> des Parent-POM aktiviert
(resolveCiFriendliesOnly), sodass installierte POMs keine unaufgelösten
${revision}-Referenzen enthalten
- MANIFEST.MF des Shade-JARs enthält Implementation-Version und Implementation-Title
- app.version im Packaging-Modul auf ${revision} umgestellt (war 2.5.0)
- ApplicationVersionProvider: neue Utility-Klasse im Bootstrap-Modul liest
Implementation-Version aus MANIFEST.MF, Fallback "dev" bei ungepacktem Betrieb
- ApplicationVersionProviderTest: prüft Fallback-Verhalten im Testlauf
- .gitignore: .flattened-pom.xml-Dateien ausgeschlossen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bei mehrfachem Provider-Wechsel oder Modelle-Neu-Laden konnten parallele
HTTP-Threads ihre Ergebnisse in dieselbe Meldungsliste schreiben. Mit
einem AtomicLong-Generationszaehler wird vor jedem Lauf eine Generation
festgehalten; bei der UI-Auslieferung auf dem JavaFX Application Thread
wird verworfen, was nicht mehr zur aktuellen Generation gehoert. Damit
ueberschreiben veraltete Worker den UI-Zustand nicht mehr.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Statt fuer jede openConfigurationFile-Anfrage einen neuen Thread zu
starten, werden Anfragen jetzt ueber einen Single-Thread-ExecutorService
mit Daemon-ThreadFactory eingereiht. Mehrfaches Klicken auf Oeffnen
erzeugt keine konkurrierenden Worker-Threads mehr; Anfragen werden
seriell hintereinander abgearbeitet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Die Felder currentDocument, currentRenderer, currentSourceFile,
currentPage und totalPages werden vom Worker-Thread geschrieben und
vom JavaFX Application Thread gelesen. Das volatile-Keyword garantiert
nun die Sichtbarkeit zwischen den Threads gemaess Java Memory Model.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neues funktionales Interface FilePickerDialog eingefuehrt, das Titel,
Anfangspfad und ExtensionFilter-Liste entgegennimmt. showNativeFileChooser
wendet die Filter auf den FileChooser an. pickFile reicht die Filter
durch. Test-Stubs verwenden die aktualisierte Drei-Parameter-Signatur.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pendingMessages.clear() wurde aus runTechnicalTestsAction() in den
Coordinator verlagert (erste Anweisung in triggerTechnicalTests()).
Damit liegen clear() und Worker-Start auf demselben Thread (FX),
und das Race-Fenster zwischen clear() und den per Platform.runLater
zurueckgefuehrten add()-Aufrufen entfaellt.
Die fachliche Produktionssemantik (Replace beim Trigger) bleibt
identisch. JavaDoc und Kommentare wurden auf Replace-Semantik
korrigiert. Der Smoke-Test trigger_twice_accumulatesTestEntries
wurde zu trigger_twice_replacesTestEntries umbenannt und prueft
nun die Replace-Erwartung des isolierten Coordinators.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
- 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>
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>
- 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>
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>
- 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>
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.