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