diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index 695032e..728e7b0 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -1573,10 +1573,12 @@ public final class GuiConfigurationEditorWorkspace { TextField lockField = boundTextField( editorState.values().runtimeLockFile(), val -> updateValues(editorState.values().withRuntimeLockFile(val))); + applyTooltip(lockField, GuiTooltipTexts.PFADE_LOCK_DATEI); TextField logDirField = boundTextField( editorState.values().logDirectory(), val -> updateValues(editorState.values().withLogDirectory(val))); + applyTooltip(logDirField, GuiTooltipTexts.PFADE_LOG_VERZEICHNIS); VBox optionalContent = new VBox(4); optionalContent.setPadding(new Insets(6, 0, 0, 0)); @@ -1858,6 +1860,7 @@ public final class GuiConfigurationEditorWorkspace { val, pState2.model(), pState2.timeoutSeconds(), pState2.apiKey()))); Label baseUrlError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "baseUrl", baseUrlError); + applyTooltip(baseUrlField, GuiTooltipTexts.PROVIDER_BASIS_URL); HBox baseUrlBox = new HBox(4, baseUrlField); HBox.setHgrow(baseUrlField, Priority.ALWAYS); fieldGrid.add(new Label("Basis-URL:"), 0, gridRow); @@ -1866,6 +1869,7 @@ public final class GuiConfigurationEditorWorkspace { TextField timeoutField = boundTextField(pState.timeoutSeconds(), val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( pState2.baseUrl(), pState2.model(), val, pState2.apiKey()))); + applyTooltip(timeoutField, GuiTooltipTexts.PROVIDER_TIMEOUT); Label timeoutError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "timeoutSeconds", timeoutError); fieldGrid.add(new Label("Timeout (Sek.):"), 2, gridRow); @@ -1924,6 +1928,7 @@ public final class GuiConfigurationEditorWorkspace { val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( pState2.baseUrl(), pState2.model(), pState2.timeoutSeconds(), GuiProviderApiKeyState.unresolved(val)))); + applyTooltip(apiKeyField, GuiTooltipTexts.PROVIDER_API_KEY); Label apiKeyError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "apiKey", apiKeyError); Label apiKeyOriginLabel = createApiKeyOriginLabel(); @@ -2015,6 +2020,7 @@ public final class GuiConfigurationEditorWorkspace { TextField maxRetriesField = boundTextField( editorState.values().maxRetriesTransient(), val -> updateValues(editorState.values().withMaxRetriesTransient(val))); + applyTooltip(maxRetriesField, GuiTooltipTexts.LIMITS_MAX_RETRIES); grid.add(new Label("Max. Retries:"), 2, row); grid.add(maxRetriesField, 3, row); row++; @@ -2023,6 +2029,7 @@ public final class GuiConfigurationEditorWorkspace { TextField logLevelField = boundTextField( editorState.values().logLevel(), val -> updateValues(editorState.values().withLogLevel(val))); + applyTooltip(logLevelField, GuiTooltipTexts.LIMITS_LOG_LEVEL); grid.add(new Label("Log-Level:"), 0, row); grid.add(logLevelField, 1, row); @@ -2032,6 +2039,7 @@ public final class GuiConfigurationEditorWorkspace { sensitiveCheck.setSelected(sensitive); sensitiveCheck.selectedProperty().addListener((obs, oldVal, newVal) -> updateValues(editorState.values().withLogAiSensitive(Boolean.toString(newVal)))); + applyTooltip(sensitiveCheck, GuiTooltipTexts.LIMITS_SENSIBLE_KI_AUSGABE); grid.add(new Label(), 2, row); grid.add(sensitiveCheck, 3, row); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java index 876586d..7dc3a97 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiPromptEditorTab.java @@ -8,7 +8,6 @@ import java.util.function.Function; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingFailure; import de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingSuccess; import de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult; @@ -245,14 +244,13 @@ public class GuiPromptEditorTab { statusLabel.setStyle("-fx-text-fill: #555555;"); // Buttons verdrahten - saveButton.setTooltip(new Tooltip("Prompt-Datei speichern (atomar, UTF-8).")); + saveButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_SPEICHERN)); saveButton.setOnAction(e -> requestSave()); - resetButton.setTooltip(new Tooltip("Textfeld mit dem Standard-Prompt-Inhalt befüllen, ohne zu speichern.")); + resetButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_ZURUECKSETZEN)); resetButton.setOnAction(e -> resetToDefault()); - createDefaultButton.setTooltip(new Tooltip( - "Standard-Prompt-Datei am konfigurierten Pfad anlegen.")); + createDefaultButton.setTooltip(new Tooltip(GuiTooltipTexts.PROMPT_STANDARD_ANLEGEN)); createDefaultButton.setOnAction(e -> requestCreateDefault()); createDefaultButton.setVisible(false); createDefaultButton.setManaged(false); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java index 7fa9cbb..2e71832 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiTooltipTexts.java @@ -65,6 +65,14 @@ public final class GuiTooltipTexts { public static final String PFADE_PROMPT = "Externe Textdatei mit den KI-Anweisungen."; + /** Tooltip für das Eingabefeld „Lock-Datei". */ + public static final String PFADE_LOCK_DATEI = + "Pfad zur Lock-Datei, die parallele Instanzen verhindert (optional)."; + + /** Tooltip für das Eingabefeld „Log-Verzeichnis". */ + public static final String PFADE_LOG_VERZEICHNIS = + "Verzeichnis für Log-Dateien. Leer = Standardverzeichnis logs/ im Programmverzeichnis."; + // ------------------------------------------------------------------------- // Konfigurationstab – Provider // ------------------------------------------------------------------------- @@ -77,6 +85,18 @@ public final class GuiTooltipTexts { public static final String PROVIDER_MODELL = "Das konkrete Sprachmodell des gewählten Providers."; + /** Tooltip für das Eingabefeld „Basis-URL". */ + public static final String PROVIDER_BASIS_URL = + "Basis-URL des KI-Dienstes (z. B. https://api.openai.com/v1)."; + + /** Tooltip für das Eingabefeld „Timeout". */ + public static final String PROVIDER_TIMEOUT = + "Zeitlimit für KI-Anfragen in Sekunden."; + + /** Tooltip für das Eingabefeld „API-Key". */ + public static final String PROVIDER_API_KEY = + "API-Schlüssel für den konfigurierten KI-Dienst. Umgebungsvariable hat Vorrang."; + // ------------------------------------------------------------------------- // Konfigurationstab – Verarbeitungslimits // ------------------------------------------------------------------------- @@ -93,10 +113,26 @@ public final class GuiTooltipTexts { public static final String LIMITS_MAX_TITLE_LENGTH = "Maximale Länge des Dateinamens in Zeichen (ohne Datum und Erweiterung). Gültig: 10–120."; + /** Tooltip für das Eingabefeld „max.retries.transient". */ + public static final String LIMITS_MAX_RETRIES = + "Maximale Anzahl transienter Wiederholversuche je Dokument (Ganzzahl ≥ 1)."; + + /** Tooltip für das Eingabefeld „Log-Level". */ + public static final String LIMITS_LOG_LEVEL = + "Log-Detailstufe (z. B. INFO, DEBUG, WARN). Leer = Standardwert INFO."; + + /** Tooltip für die Checkbox „Sensible KI-Ausgabe". */ + public static final String LIMITS_SENSIBLE_KI_AUSGABE = + "Vollständige KI-Antworten in die Log-Datei schreiben (nur für Diagnosezwecke empfohlen)."; + // ------------------------------------------------------------------------- // Verarbeitungslauf-Tab – Dateiname-Editor // ------------------------------------------------------------------------- + /** Tooltip für das Dateiname-Textfeld im Dateiname-Editor. */ + public static final String DATEINAME_TEXTFELD = + "Dateiname bearbeiten. Format: JJJJ-MM-TT - Titel.pdf"; + /** Tooltip für den Button „Dateiname übernehmen". */ public static final String DATEINAME_UEBERNEHMEN = "Benennt die Zieldatei um und aktualisiert die Datenbank. Nicht rückgängig zu machen."; @@ -133,6 +169,14 @@ public final class GuiTooltipTexts { public static final String BATCHRUN_MESSAGE_AREA = "Statusmeldungen und Fortschrittsinformationen des aktuellen Verarbeitungslaufs."; + /** Tooltip für den Navigations-Button „Vorherige Seite" in der PDF-Vorschau. */ + public static final String PREVIEW_VORHERIGE_SEITE = + "Vorherige Seite der Vorschau anzeigen."; + + /** Tooltip für den Navigations-Button „Nächste Seite" in der PDF-Vorschau. */ + public static final String PREVIEW_NAECHSTE_SEITE = + "Nächste Seite der Vorschau anzeigen."; + /** Tooltip für Spalte „Status" in der Verarbeitungslauf-Tabelle. */ public static final String BATCHRUN_COL_STATUS = "Verarbeitungsergebnis: Erfolg, Fehler oder übersprungen."; @@ -233,6 +277,18 @@ public final class GuiTooltipTexts { public static final String PROMPT_TEXTAREA = "KI-Anweisungstext. Dieser Prompt wird bei jedem Verarbeitungsversuch an das Sprachmodell gesendet."; + /** Tooltip für den Button „Speichern" im Prompt-Editor-Tab. */ + public static final String PROMPT_SPEICHERN = + "Prompt-Datei speichern (atomar, UTF-8)."; + + /** Tooltip für den Button „Auf Standard zurücksetzen" im Prompt-Editor-Tab. */ + public static final String PROMPT_ZURUECKSETZEN = + "Textfeld mit dem Standard-Prompt-Inhalt befüllen, ohne zu speichern."; + + /** Tooltip für den Button „Standard-Prompt erstellen" im Prompt-Editor-Tab. */ + public static final String PROMPT_STANDARD_ANLEGEN = + "Standard-Prompt-Datei am konfigurierten Pfad anlegen."; + /** Nicht instanziierbar – reine Konstantenklasse. */ private GuiTooltipTexts() { throw new UnsupportedOperationException("Nicht instanziierbar"); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/AiFailureMessageTranslator.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/AiFailureMessageTranslator.java index aa2fc61..e503e77 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/AiFailureMessageTranslator.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/AiFailureMessageTranslator.java @@ -2,7 +2,7 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun; /** * Übersetzt strukturierte Fehlermeldungen aus der Anwendungsschicht in - * benutzerfreundliche deutsche Texte für den Detailbereich des Verarbeitungslauf-Tabs. + * benutzerfreundliche deutsche Texte für die Darstellungsschicht der GUI. *

* Die Klasse wertet die englischsprachige Fehlermeldung aus dem Verarbeitungsversuch * musterbasiert aus und liefert eine für den Endbenutzer lesbare Beschreibung des @@ -12,8 +12,10 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun; * Die Mustererkennung erfolgt ohne Berücksichtigung der Groß-/Kleinschreibung * und prüft die definierten Schlüsselbegriffe in festgelegter Reihenfolge, * damit spezifischere Muster vor allgemeineren greifen. + *

+ * Die Klasse wird sowohl im Verarbeitungslauf-Tab als auch im Verlauf-Tab verwendet. */ -final class AiFailureMessageTranslator { +public final class AiFailureMessageTranslator { private AiFailureMessageTranslator() { } @@ -28,7 +30,7 @@ final class AiFailureMessageTranslator { * @param technicalMessage die rohe technische Fehlermeldung; darf {@code null} sein * @return eine nicht-leere deutsche Benutzerfehlermeldung ohne führendes Warnsymbol */ - static String translate(String technicalMessage) { + public static String translate(String technicalMessage) { if (technicalMessage == null || technicalMessage.isBlank()) { return "Verarbeitung fehlgeschlagen. Bitte Konfiguration prüfen und ggf. erneut verarbeiten."; } diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/FileNameEditorPane.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/FileNameEditorPane.java index d8b1665..edc7f45 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/FileNameEditorPane.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/FileNameEditorPane.java @@ -76,6 +76,9 @@ public final class FileNameEditorPane { sectionTitle.setStyle("-fx-font-weight: bold;"); textField.setId("filename-editor-text-field"); + Tooltip textFieldTooltip = new Tooltip(GuiTooltipTexts.DATEINAME_TEXTFELD); + textFieldTooltip.setShowDelay(Duration.millis(300)); + textField.setTooltip(textFieldTooltip); HBox.setHgrow(textField, Priority.ALWAYS); HBox inputRow = new HBox(4, textField); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java index 68ef6e7..b7c35f3 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/PdfPreviewPane.java @@ -22,10 +22,13 @@ import javafx.embed.swing.SwingFXUtils; import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Pos; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tooltip; +import javafx.util.Duration; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.ScrollEvent; @@ -213,9 +216,15 @@ public final class PdfPreviewPane { prevButton.setId("pdf-preview-prev-button"); prevButton.setOnAction(e -> navigateToPreviousPage()); + Tooltip prevTooltip = new Tooltip(GuiTooltipTexts.PREVIEW_VORHERIGE_SEITE); + prevTooltip.setShowDelay(Duration.millis(300)); + prevButton.setTooltip(prevTooltip); nextButton.setId("pdf-preview-next-button"); nextButton.setOnAction(e -> navigateToNextPage()); + Tooltip nextTooltip = new Tooltip(GuiTooltipTexts.PREVIEW_NAECHSTE_SEITE); + nextTooltip.setShowDelay(Duration.millis(300)); + nextButton.setTooltip(nextTooltip); pageLabel.setId("pdf-preview-page-label"); pageLabel.setStyle("-fx-text-fill: #555555;"); diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java index 2128b7b..b741a9c 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/history/GuiHistoryTab.java @@ -16,6 +16,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiTooltipTexts; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.AiFailureMessageTranslator; import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.ProcessingStatusPresentation; import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord; import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttempt; @@ -810,7 +811,8 @@ public final class GuiHistoryTab { } } - failureArea.setText(failureMessage != null ? failureMessage : ""); + failureArea.setText(failureMessage != null + ? AiFailureMessageTranslator.translate(failureMessage) : ""); failureArea.setPromptText("Keine Fehlerdetails gespeichert."); }