#71: Prompt-Editor-Tab in der GUI implementieren
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>
This commit is contained in:
+87
-4
@@ -21,6 +21,7 @@ import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiConfigurationFileLoader;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiConfigurationFileWriter;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiConfigurationLoadException;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiPromptEditorPort;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiStartupContext;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLaunchOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLauncher;
|
||||
@@ -89,6 +90,7 @@ import de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingCoordina
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultBatchRunProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultManualFileCopyUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultManualFileRenameUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultPromptEditorUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultResetDocumentStatusUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultResolveHistoricalDocumentContextUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.validation.editor.EditorConfigurationValidator;
|
||||
@@ -827,7 +829,8 @@ public class BootstrapRunner {
|
||||
manualRenamePort,
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion);
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
}
|
||||
|
||||
Path configPath = Paths.get(configPathOverride.get());
|
||||
@@ -852,17 +855,20 @@ public class BootstrapRunner {
|
||||
manualRenamePort,
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion);
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
}
|
||||
|
||||
LOG.info("GUI startup: configuration file confirmed at: {}", configPath.toAbsolutePath());
|
||||
try {
|
||||
GuiConfigurationEditorState loadedState = loadGuiConfigurationState(configPath);
|
||||
GuiPromptEditorPort promptEditorPort = buildGuiPromptEditorPort(
|
||||
loadedState.values().promptTemplateFile());
|
||||
return new GuiStartupContext(loadedState, Optional.empty(), loader, writer,
|
||||
modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
|
||||
technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
|
||||
miniRunLauncher, resetPort, manualRenamePort, manualCopyPort,
|
||||
historicalDocumentContextPort, applicationVersion);
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort);
|
||||
} catch (GuiConfigurationLoadException e) {
|
||||
LOG.error("GUI startup: configuration could not be loaded, starting without it: {}",
|
||||
e.getMessage(), e);
|
||||
@@ -883,10 +889,87 @@ public class BootstrapRunner {
|
||||
manualRenamePort,
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion);
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt einen vollständig verdrahteten {@link GuiPromptEditorPort} für den angegebenen
|
||||
* Prompt-Dateipfad.
|
||||
* <p>
|
||||
* Kombiniert {@link FilesystemPromptPortAdapter} und
|
||||
* {@link de.gecheckt.pdf.umbenenner.adapter.out.resourcecreation.FilesystemResourceCreationAdapter}
|
||||
* in einem {@link DefaultPromptEditorUseCase}. Der zurückgegebene Port delegiert alle
|
||||
* drei Operationen (Laden, Speichern, Standard-Anlegen) an den Use-Case.
|
||||
*
|
||||
* @param promptFilePath konfigurierter Pfad zur Prompt-Datei; darf nicht {@code null} sein
|
||||
* @return vollständig verdrahteter Port; nie {@code null}
|
||||
*/
|
||||
private GuiPromptEditorPort buildGuiPromptEditorPort(String promptFilePath) {
|
||||
FilesystemPromptPortAdapter promptPortAdapter =
|
||||
new FilesystemPromptPortAdapter(Paths.get(promptFilePath));
|
||||
de.gecheckt.pdf.umbenenner.application.validation.technicaltest.ResourceCreationPort
|
||||
resourceCreationPort =
|
||||
new de.gecheckt.pdf.umbenenner.adapter.out.resourcecreation.FilesystemResourceCreationAdapter();
|
||||
DefaultPromptEditorUseCase useCase = new DefaultPromptEditorUseCase(
|
||||
promptPortAdapter, resourceCreationPort);
|
||||
return new GuiPromptEditorPort() {
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingResult loadCurrentPrompt() {
|
||||
return useCase.loadPrompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult save(String content) {
|
||||
return useCase.savePrompt(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
|
||||
createDefaultPromptIfMissing(
|
||||
de.gecheckt.pdf.umbenenner.application.validation.technicaltest
|
||||
.CorrectionSuggestion.CreatePromptFile suggestion) {
|
||||
return useCase.createDefaultPromptIfMissing(suggestion);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt einen No-Op-{@link GuiPromptEditorPort} zurück, der alle Operationen als
|
||||
* nicht verfügbar meldet.
|
||||
* <p>
|
||||
* Wird eingesetzt, wenn beim GUI-Start noch keine Konfiguration geladen ist und daher
|
||||
* kein Prompt-Dateipfad bekannt ist.
|
||||
*
|
||||
* @return no-op Port; nie {@code null}
|
||||
*/
|
||||
private static GuiPromptEditorPort noOpGuiPromptEditorPort() {
|
||||
return new GuiPromptEditorPort() {
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingResult loadCurrentPrompt() {
|
||||
return new de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingFailure(
|
||||
"NO_OP", "Kein Prompt-Pfad konfiguriert. Bitte zuerst eine Konfiguration öffnen.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult save(String content) {
|
||||
return new de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult.WriteFailed(
|
||||
"Kein Prompt-Pfad konfiguriert. Bitte zuerst eine Konfiguration öffnen.", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
|
||||
createDefaultPromptIfMissing(
|
||||
de.gecheckt.pdf.umbenenner.application.validation.technicaltest
|
||||
.CorrectionSuggestion.CreatePromptFile suggestion) {
|
||||
return new de.gecheckt.pdf.umbenenner.application.validation.technicaltest
|
||||
.CorrectionOutcome.NotAttempted(
|
||||
suggestion, "Kein Prompt-Pfad konfiguriert.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes exactly one batch run triggered by the GUI's processing-run tab.
|
||||
* <p>
|
||||
|
||||
Reference in New Issue
Block a user