diff --git a/src/components/settings/AiProviderSettings.tsx b/src/components/settings/AiProviderSettings.tsx
index e7f3924..7bd6e08 100644
--- a/src/components/settings/AiProviderSettings.tsx
+++ b/src/components/settings/AiProviderSettings.tsx
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Check, CheckCircle2, ChevronDown, Plus, Save, Trash2, Wifi } from 'lucide-react'
+import { Check, CheckCircle2, ChevronDown, Cpu, Plus, Save, Trash2, Wifi } from 'lucide-react'
import type { AiBillingMode, AiProviderConfig } from '../../../shared/types/ai.types'
import {
AI_PROVIDER_FAMILY_ORDER,
@@ -23,7 +23,6 @@ function connectionSnapshot(provider: AiProviderConfig, apiKey: string): string
const model = selectedModel(provider)
return JSON.stringify({
id: provider.id,
- name: provider.name.trim(),
protocol: provider.protocol,
authStyle: provider.authStyle,
baseUrl: provider.baseUrl.trim(),
@@ -77,8 +76,17 @@ export default function AiProviderSettings() {
const currentSnapshot = useMemo(() => (draft ? connectionSnapshot(draft, apiKey) : ''), [apiKey, draft])
const verified = !!currentSnapshot && verifiedSnapshot === currentSnapshot && testState === 'success'
const currentModel = selectedModel(draft)
- const isStored = !!draft && store.providers.some((provider) => provider.id === draft.id)
- const canTest = validDraft(draft, apiKey) && testState !== 'testing'
+ const storedProvider = store.providers.find((provider) => provider.id === draft?.id)
+ const isStored = !!storedProvider
+ const dirty =
+ !!draft && (!storedProvider || JSON.stringify(draft) !== JSON.stringify(storedProvider) || !!apiKey.trim())
+ const connectionChanged = !storedProvider || currentSnapshot !== connectionSnapshot(storedProvider, '')
+ const needsTest = connectionChanged && !verified
+ const busy = saving || testState === 'testing'
+ const canTest = validDraft(draft, apiKey) && !busy
+ const canSave = dirty && validDraft(draft, apiKey) && !needsTest && !busy
+ const displayedMessage =
+ verifiedSnapshot && verifiedSnapshot !== currentSnapshot ? (dirty && needsTest ? t('ai.retest') : '') : message
const family = draft ? resolveProviderFamily(draft) : null
const needsEndpoint = family === 'custom' || family === 'local' || draft?.presetId === 'vllm'
@@ -100,7 +108,6 @@ export default function AiProviderSettings() {
const updateDraft = (changes: Partial
) => {
if (!draft) return
setDraft({ ...draft, ...changes })
- if (testState !== 'idle') setMessage(t('ai.retest'))
}
const chooseModel = (modelId: string) => {
@@ -172,13 +179,16 @@ export default function AiProviderSettings() {
}
const save = async () => {
- if (!draft || !verified) return
+ if (!draft || !canSave) return
setSaving(true)
try {
await store.saveProvider(draft, apiKey)
- if (!store.activeProviderId || !isStored) await store.setActiveProvider(draft.id)
+ if (!store.activeProviderId) await store.setActiveProvider(draft.id)
setApiKey('')
- setMessage(t('ai.saved'))
+ setMessage('')
+ setTestAnswer('')
+ setVerifiedSnapshot(null)
+ setTestState('idle')
} catch (error) {
setMessage(error instanceof Error ? error.message : String(error))
setTestState('error')
@@ -199,38 +209,41 @@ export default function AiProviderSettings() {
}
return (
-
-
-