From 0e8783ccfb223dd4303c3f54a6cba983e99e9975 Mon Sep 17 00:00:00 2001 From: "dayuan.jiang" Date: Mon, 22 Dec 2025 21:49:29 +0900 Subject: [PATCH] fix: UI/UX improvements for model configuration dialog - Add gradient header styling with icon badge - Change Configuration section icon from Key to Settings2 - Add duplicate model detection with warning banner and inline removal - Filter out already-added models from suggestions dropdown - Add type-to-confirm for deleting providers with 3+ models - Enhance delete confirmation dialog with warning icon - Improve model selector discoverability (show model name + chevron) - Add truncation for long model names with title tooltip - Remove AI provider settings from Settings dialog (now in Model Config) - Extract ValidationButton into reusable component --- components/chat-panel.tsx | 1 - components/model-config-dialog.tsx | 179 ++++++++++++++++++++++++++--- components/model-selector.tsx | 16 ++- components/settings-dialog.tsx | 24 +--- 4 files changed, 177 insertions(+), 43 deletions(-) diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 19400d3..7bfe807 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -1388,7 +1388,6 @@ Continue from EXACTLY where you stopped.`, open={showSettingsDialog} onOpenChange={setShowSettingsDialog} onCloseProtectionChange={onCloseProtectionChange} - onOpenModelConfig={() => setShowModelConfigDialog(true)} drawioUi={drawioUi} onToggleDrawioUi={onToggleDrawioUi} darkMode={darkMode} diff --git a/components/model-config-dialog.tsx b/components/model-config-dialog.tsx index 1880e3f..5ea5860 100644 --- a/components/model-config-dialog.tsx +++ b/components/model-config-dialog.tsx @@ -13,6 +13,7 @@ import { Loader2, Plus, Server, + Settings2, Sparkles, Tag, Trash2, @@ -100,6 +101,42 @@ function ProviderLogo({ ) } +// Reusable validation button component +function ValidationButton({ + status, + onClick, + disabled, +}: { + status: ValidationStatus + onClick: () => void + disabled: boolean +}) { + return ( + + ) +} + export function ModelConfigDialog({ open, onOpenChange, @@ -120,6 +157,7 @@ export function ModelConfigDialog({ typeof setTimeout > | null>(null) const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false) + const [deleteConfirmText, setDeleteConfirmText] = useState("") const [validatingModelIndex, setValidatingModelIndex] = useState< number | null >(null) @@ -173,6 +211,26 @@ export function ModelConfigDialog({ ? SUGGESTED_MODELS[selectedProvider.provider] || [] : [] + // Filter out already-added models from suggestions + const existingModelIds = + selectedProvider?.models.map((m) => m.modelId) || [] + const availableSuggestions = suggestedModels.filter( + (modelId) => !existingModelIds.includes(modelId), + ) + + // Detect duplicate models in current config + const modelIdCounts = + selectedProvider?.models.reduce( + (acc, m) => { + acc[m.modelId] = (acc[m.modelId] || 0) + 1 + return acc + }, + {} as Record, + ) || {} + const duplicateModelIds = Object.keys(modelIdCounts).filter( + (id) => modelIdCounts[id] > 1, + ) + // Handle adding a new provider const handleAddProvider = (providerType: ProviderName) => { const newProvider = addProvider(providerType) @@ -329,9 +387,11 @@ export function ModelConfigDialog({ return ( - - - + + +
+ +
{dict.modelConfig?.title || "AI Model Configuration"}
@@ -508,7 +568,7 @@ export function ModelConfigDialog({ {/* Configuration Section */}
- + Configuration
@@ -987,14 +1047,21 @@ export function ModelConfigDialog({ ) } }} + disabled={ + availableSuggestions.length === + 0 + } > - Suggested + {availableSuggestions.length === + 0 + ? "All added" + : "Suggested"} - {suggestedModels.map( + {availableSuggestions.map( (modelId) => (
+ {/* Duplicate Warning Banner */} + {duplicateModelIds.length > 0 && ( +
+ + + { + duplicateModelIds.length + }{" "} + duplicate model + {duplicateModelIds.length > + 1 + ? "s" + : ""}{" "} + detected. Remove + duplicates to avoid + confusion. + +
+ )} + {/* Model List */}
{selectedProvider.models @@ -1049,7 +1136,7 @@ export function ModelConfigDialog({ "rounded-b-xl", )} > -
+
{/* Status icon */}
{validatingModelIndex !== @@ -1093,6 +1180,9 @@ export function ModelConfigDialog({ value={ model.modelId } + title={ + model.modelId + } onChange={( e, ) => { @@ -1111,7 +1201,7 @@ export function ModelConfigDialog({ }, ) }} - className="flex-1 font-mono text-sm h-8 border-0 bg-transparent focus-visible:bg-background focus-visible:ring-1" + className="flex-1 min-w-0 font-mono text-sm h-8 border-0 bg-transparent focus-visible:bg-background focus-visible:ring-1" /> +
+ )}
), )} @@ -1191,12 +1304,20 @@ export function ModelConfigDialog({ {/* Delete Confirmation Dialog */} { + setDeleteConfirmOpen(open) + if (!open) setDeleteConfirmText("") + }} > - + - Delete Provider - +
+ +
+ + Delete Provider + + Are you sure you want to delete{" "} {selectedProvider @@ -1209,11 +1330,43 @@ export function ModelConfigDialog({ be undone.
+ {selectedProvider && + selectedProvider.models.length >= 3 && ( +
+ + + setDeleteConfirmText(e.target.value) + } + placeholder="Type provider name..." + className="h-9" + /> +
+ )} Cancel = 3 && + deleteConfirmText !== + (selectedProvider.name || + PROVIDER_INFO[selectedProvider.provider] + .label) + } + className="bg-destructive text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50" > Delete diff --git a/components/model-selector.tsx b/components/model-selector.tsx index 10b7c64..37baa4c 100644 --- a/components/model-selector.tsx +++ b/components/model-selector.tsx @@ -1,6 +1,6 @@ "use client" -import { Bot, Check, Server, Settings2 } from "lucide-react" +import { Bot, Check, ChevronDown, Server, Settings2 } from "lucide-react" import { useMemo, useState } from "react" import { ModelSelectorContent, @@ -96,8 +96,8 @@ export function ModelSelector({ } const tooltipContent = selectedModel - ? `Model: ${selectedModel.modelId}` - : "Model: Server Default" + ? `${selectedModel.modelId} (click to change)` + : "Using server default model (click to change)" return ( @@ -105,11 +105,15 @@ export function ModelSelector({ - + + + {selectedModel ? selectedModel.modelId : "Default"} + + diff --git a/components/settings-dialog.tsx b/components/settings-dialog.tsx index 9f7738e..029fc8d 100644 --- a/components/settings-dialog.tsx +++ b/components/settings-dialog.tsx @@ -1,6 +1,6 @@ "use client" -import { Moon, Settings2, Sun } from "lucide-react" +import { Moon, Sun } from "lucide-react" import { useEffect, useState } from "react" import { Button } from "@/components/ui/button" import { @@ -19,7 +19,6 @@ interface SettingsDialogProps { open: boolean onOpenChange: (open: boolean) => void onCloseProtectionChange?: (enabled: boolean) => void - onOpenModelConfig: () => void drawioUi: "min" | "sketch" onToggleDrawioUi: () => void darkMode: boolean @@ -41,7 +40,6 @@ export function SettingsDialog({ open, onOpenChange, onCloseProtectionChange, - onOpenModelConfig, drawioUi, onToggleDrawioUi, darkMode, @@ -178,26 +176,6 @@ export function SettingsDialog({ )}
)} -
-
- -

- {dict.settings.aiProviderDescription} -

-
- -
-