import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Switch } from '@/components/ui/switch' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { useModelProvider } from '@/hooks/useModelProvider' import { IconPencil, IconEye, IconTool, IconAlertTriangle, IconLoader2, IconSparkles, } from '@tabler/icons-react' import { useState, useEffect } from 'react' import { useTranslation } from '@/i18n/react-i18next-compat' import { toast } from 'sonner' // No need to define our own interface, we'll use the existing Model type type DialogEditModelProps = { provider: ModelProvider modelId?: string // Optional model ID to edit } export const DialogEditModel = ({ provider, modelId, }: DialogEditModelProps) => { const { t } = useTranslation() const { updateProvider } = useModelProvider() const [selectedModelId, setSelectedModelId] = useState('') const [displayName, setDisplayName] = useState('') const [originalDisplayName, setOriginalDisplayName] = useState('') const [originalCapabilities, setOriginalCapabilities] = useState< Record >({}) const [isOpen, setIsOpen] = useState(false) const [isLoading, setIsLoading] = useState(false) const [capabilities, setCapabilities] = useState>({ vision: false, tools: false, proactive: false, }) // Initialize with the provided model ID or the first model if available useEffect(() => { if (isOpen && !selectedModelId || !isOpen) { if (modelId) { setSelectedModelId(modelId) } else if (provider.models && provider.models.length > 0) { setSelectedModelId(provider.models[0].id) } } }, [modelId, isOpen, selectedModelId, provider.models]) // Get the currently selected model const selectedModel = provider.models.find( (m: Model) => m.id === selectedModelId ) // Helper function to convert capabilities array to object const capabilitiesToObject = (capabilitiesList: string[]) => ({ vision: capabilitiesList.includes('vision'), tools: capabilitiesList.includes('tools'), proactive: capabilitiesList.includes('proactive'), }) // Initialize capabilities and display name from selected model useEffect(() => { if (selectedModel) { const modelCapabilities = selectedModel.capabilities || [] const capsObject = capabilitiesToObject(modelCapabilities) setCapabilities(capsObject) setOriginalCapabilities(capsObject) // Use existing displayName if available, otherwise fall back to model ID const displayNameValue = (selectedModel as Model & { displayName?: string }).displayName || selectedModel.id setDisplayName(displayNameValue) setOriginalDisplayName(displayNameValue) } }, [selectedModel]) // Update model capabilities - only update local state const handleCapabilityChange = (capability: string, enabled: boolean) => { setCapabilities((prev) => ({ ...prev, [capability]: enabled, })) } // Handle display name change const handleDisplayNameChange = (newName: string) => { setDisplayName(newName) } // Check if there are unsaved changes const hasUnsavedChanges = () => { const nameChanged = displayName !== originalDisplayName const capabilitiesChanged = JSON.stringify(capabilities) !== JSON.stringify(originalCapabilities) return nameChanged || capabilitiesChanged } // Handle save changes const handleSaveChanges = async () => { if (!selectedModel?.id || isLoading) return setIsLoading(true) try { const nameChanged = displayName !== originalDisplayName const capabilitiesChanged = JSON.stringify(capabilities) !== JSON.stringify(originalCapabilities) // Build the update object for the selected model const modelUpdate: Partial & { _userConfiguredCapabilities?: boolean } = {} if (nameChanged) { modelUpdate.displayName = displayName } if (capabilitiesChanged) { modelUpdate.capabilities = Object.entries(capabilities) .filter(([, isEnabled]) => isEnabled) .map(([capName]) => capName) modelUpdate._userConfiguredCapabilities = true } // Update the model in the provider models array const updatedModels = provider.models.map((m: Model) => m.id === selectedModelId ? { ...m, ...modelUpdate } : m ) // Update the provider with the updated models updateProvider(provider.provider, { ...provider, models: updatedModels, }) // Update original values if (nameChanged) setOriginalDisplayName(displayName) if (capabilitiesChanged) setOriginalCapabilities(capabilities) // Show success toast and close dialog toast.success('Model updated successfully') setIsOpen(false) } catch (error) { console.error('Failed to update model:', error) toast.error('Failed to update model. Please try again.') } finally { setIsLoading(false) } } if (!selectedModel) { return null } // Handle dialog close - reset to original values if not saved const handleDialogChange = (open: boolean) => { if (!open && hasUnsavedChanges()) { // Reset to original values when closing without saving setDisplayName(originalDisplayName) setCapabilities(originalCapabilities) } setIsOpen(open) } // Handle keyboard events for Enter key const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && hasUnsavedChanges() && !isLoading) { e.preventDefault() handleSaveChanges() } } return (
{t('providers:editModel.title', { modelId: selectedModel.id })} {t('providers:editModel.description')} {/* Model Display Name Section */}
handleDisplayNameChange(e.target.value)} placeholder="Enter display name" className="w-full" disabled={isLoading} />

This is the name that will be shown in the interface. The original model file remains unchanged.

{/* Warning Banner */}

{t('providers:editModel.warning.title')}

{t('providers:editModel.warning.description')}

{t('providers:editModel.capabilities')}

{t('providers:editModel.tools')}
handleCapabilityChange('tools', checked) } disabled={isLoading} />
{t('providers:editModel.vision')}
handleCapabilityChange('vision', checked) } disabled={isLoading} />
{t('providers:editModel.proactive')}
handleCapabilityChange('proactive', checked) } disabled={isLoading || !(capabilities.tools && capabilities.vision)} />
{/* Save Button */}
) }