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 9332248..95e7e1d 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 @@ -1216,7 +1216,7 @@ public final class GuiConfigurationEditorWorkspace { editorTab.setClosable(false); configurationTab = editorTab; - sectionsBox.setSpacing(12); + sectionsBox.setSpacing(4); sectionsBox.setFillWidth(true); configurationLockBanner.setId("configuration-lock-banner"); @@ -1639,8 +1639,9 @@ public final class GuiConfigurationEditorWorkspace { * @return the provider block as a styled card node */ /** - * Erstellt einen kompakten Provider-Konfigurationsblock mit 2-Spalten-Layout. - * Basis-URL + Modell in einer Zeile, Timeout + API-Key in einer Zeile. + * Erstellt einen kompakten Provider-Konfigurationsblock. + * Basis-URL und Timeout stehen nebeneinander (4-Spalten-Layout, Timeout schmal rechts). + * Modell und API-Key belegen jeweils eine eigene Zeile über volle Breite. */ private VBox createProviderBlock(String displayName, AiProviderFamily family) { String ns = "ai.provider." + family.getIdentifier() + "."; @@ -1658,64 +1659,69 @@ public final class GuiConfigurationEditorWorkspace { editorState.values().providerConfiguration(family)) .orElse(GuiProviderConfigurationState.blank()); - // 2-Spalten-Layout mit kompaktem Abstand + // 4-Spalten-Layout: [label-links][feld-links(breit)][label-rechts][feld-rechts(schmal)] + // Erste Zeile: Basis-URL (breit) + Timeout (schmal) nebeneinander + // Weitere Zeilen: Modell und API-Key über volle Breite (Spalten 1–3) GridPane fieldGrid = new GridPane(); - fieldGrid.setHgap(12); + fieldGrid.setHgap(8); fieldGrid.setVgap(4); - // Konfiguriere Spalten: 2 Spalten nebeneinander - javafx.scene.layout.ColumnConstraints col1 = new javafx.scene.layout.ColumnConstraints(); - col1.setMinWidth(160); - col1.setPrefWidth(180); - col1.setHgrow(Priority.ALWAYS); - javafx.scene.layout.ColumnConstraints col2 = new javafx.scene.layout.ColumnConstraints(); - col2.setMinWidth(160); - col2.setPrefWidth(180); - col2.setHgrow(Priority.ALWAYS); - fieldGrid.getColumnConstraints().addAll(col1, col2); + // Spalte 0: Label links (fest) + javafx.scene.layout.ColumnConstraints colLabel1 = new javafx.scene.layout.ColumnConstraints(); + colLabel1.setMinWidth(75); + colLabel1.setPrefWidth(90); + colLabel1.setHgrow(Priority.NEVER); + // Spalte 1: Basis-URL-Feld (breit, wächst) + javafx.scene.layout.ColumnConstraints colField1 = new javafx.scene.layout.ColumnConstraints(); + colField1.setHgrow(Priority.ALWAYS); + // Spalte 2: Timeout-Label (fest) + javafx.scene.layout.ColumnConstraints colLabel2 = new javafx.scene.layout.ColumnConstraints(); + colLabel2.setMinWidth(80); + colLabel2.setPrefWidth(95); + colLabel2.setHgrow(Priority.NEVER); + // Spalte 3: Timeout-Feld (schmal, fest) + javafx.scene.layout.ColumnConstraints colField2 = new javafx.scene.layout.ColumnConstraints(); + colField2.setMinWidth(65); + colField2.setPrefWidth(80); + colField2.setHgrow(Priority.NEVER); + fieldGrid.getColumnConstraints().addAll(colLabel1, colField1, colLabel2, colField2); int gridRow = 0; - // --- Zeile 1: Basis-URL + Modell --- - // Basis-URL (linke Spalte) + // --- Zeile 1: Basis-URL (links, breit) + Timeout (rechts, schmal) --- TextField baseUrlField = boundTextField(pState.baseUrl(), val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( val, pState2.model(), pState2.timeoutSeconds(), pState2.apiKey()))); Label baseUrlError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "baseUrl", baseUrlError); - Label baseUrlLabel = new Label("Basis-URL:"); HBox baseUrlBox = new HBox(4, baseUrlField); HBox.setHgrow(baseUrlField, Priority.ALWAYS); - fieldGrid.add(baseUrlLabel, 0, gridRow); + fieldGrid.add(new Label("Basis-URL:"), 0, gridRow); fieldGrid.add(baseUrlBox, 1, gridRow); - gridRow++; - if (baseUrlError != null) { - fieldGrid.add(new Label(), 0, gridRow); - fieldGrid.add(baseUrlError, 1, gridRow); - gridRow++; - } - // --- Zeile 2: Timeout (nur linke Spalte, Modell auf nächster Zeile) --- - // Timeout (linke Spalte) TextField timeoutField = boundTextField(pState.timeoutSeconds(), val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( pState2.baseUrl(), pState2.model(), val, pState2.apiKey()))); Label timeoutError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "timeoutSeconds", timeoutError); - Label timeoutLabel = new Label("Timeout (Sek.):"); - HBox timeoutBox = new HBox(4, timeoutField); - HBox.setHgrow(timeoutField, Priority.ALWAYS); - fieldGrid.add(timeoutLabel, 0, gridRow); - fieldGrid.add(timeoutBox, 1, gridRow); + fieldGrid.add(new Label("Timeout (Sek.):"), 2, gridRow); + fieldGrid.add(timeoutField, 3, gridRow); gridRow++; - if (timeoutError != null) { - fieldGrid.add(new Label(), 0, gridRow); - fieldGrid.add(timeoutError, 1, gridRow); + + // Fehlerzeile für Basis-URL und/oder Timeout (gemeinsame Zeile) + if (baseUrlError != null || timeoutError != null) { + if (baseUrlError != null) { + fieldGrid.add(new Label(), 0, gridRow); + fieldGrid.add(baseUrlError, 1, gridRow); + } + if (timeoutError != null) { + fieldGrid.add(new Label(), 2, gridRow); + fieldGrid.add(timeoutError, 3, gridRow); + } gridRow++; } - // --- Zeile 3: Modell (linke Spalte) + API-Key (rechte Spalte) --- - // Modell (linke Spalte) + // --- Zeile 2: Modell (volle Breite, Spalten 1–3) --- GuiModelFieldContainer modelContainer = new GuiModelFieldContainer( pState.model(), val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( @@ -1724,18 +1730,19 @@ public final class GuiConfigurationEditorWorkspace { modelCatalogCoordinator.registerFieldContainer(family, modelContainer); Label modelError = createFieldErrorLabel(); fieldErrorLabels.put(ns + "model", modelError); - Label modelLabel = new Label("Modell:"); - fieldGrid.add(modelLabel, 0, gridRow); - fieldGrid.add(modelContainer.asNode(), 1, gridRow); + Node modelNode = modelContainer.asNode(); + fieldGrid.add(new Label("Modell:"), 0, gridRow); + fieldGrid.add(modelNode, 1, gridRow); + GridPane.setColumnSpan(modelNode, 3); gridRow++; if (modelError != null) { fieldGrid.add(new Label(), 0, gridRow); fieldGrid.add(modelError, 1, gridRow); + GridPane.setColumnSpan(modelError, 3); gridRow++; } - // --- Zeile 4: API-Key (linke Spalte) --- - // API-Key (linke Spalte) + // --- Zeile 3: API-Key (volle Breite, Spalten 1–3) --- TextField apiKeyField = boundTextField(pState.apiKey().propertyValue(), val -> updateProviderField(family, pState2 -> new GuiProviderConfigurationState( pState2.baseUrl(), pState2.model(), pState2.timeoutSeconds(), @@ -1744,19 +1751,21 @@ public final class GuiConfigurationEditorWorkspace { fieldErrorLabels.put(ns + "apiKey", apiKeyError); Label apiKeyOriginLabel = createApiKeyOriginLabel(); apiKeyOriginLabels.put(family, apiKeyOriginLabel); - Label apiKeyLabel = new Label("API-Key:"); HBox apiKeyBox = new HBox(4, apiKeyField); HBox.setHgrow(apiKeyField, Priority.ALWAYS); - fieldGrid.add(apiKeyLabel, 0, gridRow); + fieldGrid.add(new Label("API-Key:"), 0, gridRow); fieldGrid.add(apiKeyBox, 1, gridRow); + GridPane.setColumnSpan(apiKeyBox, 3); gridRow++; if (apiKeyError != null) { fieldGrid.add(new Label(), 0, gridRow); fieldGrid.add(apiKeyError, 1, gridRow); + GridPane.setColumnSpan(apiKeyError, 3); gridRow++; } fieldGrid.add(new Label(), 0, gridRow); fieldGrid.add(apiKeyOriginLabel, 1, gridRow); + GridPane.setColumnSpan(apiKeyOriginLabel, 3); block.getChildren().add(fieldGrid); return block; @@ -1913,7 +1922,8 @@ public final class GuiConfigurationEditorWorkspace { card.getChildren().add(sectionTitle("Meldungen")); messagesListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); - messagesListView.setPrefHeight(140); + messagesListView.setMinHeight(60); + messagesListView.setPrefHeight(60); messagesListView.setMaxHeight(200); messagesListView.setStyle("-fx-border-color: #d8d8d8;"); Label placeholder = new Label("Keine Meldungen vorhanden.");