import { useCallback } from "react"; import { useTranslation } from "react-i18next"; import { Plus } from "lucide-react"; import { Button } from "@/components/ui/button"; import { FullScreenPanel } from "@/components/common/FullScreenPanel"; import type { Provider, CustomEndpoint } from "@/types"; import type { AppId } from "@/lib/api"; import { ProviderForm, type ProviderFormValues, } from "@/components/providers/forms/ProviderForm"; import { providerPresets } from "@/config/claudeProviderPresets"; import { codexProviderPresets } from "@/config/codexProviderPresets"; import { geminiProviderPresets } from "@/config/geminiProviderPresets"; interface AddProviderDialogProps { open: boolean; onOpenChange: (open: boolean) => void; appId: AppId; onSubmit: (provider: Omit) => Promise | void; } export function AddProviderDialog({ open, onOpenChange, appId, onSubmit, }: AddProviderDialogProps) { const { t } = useTranslation(); const handleSubmit = useCallback( async (values: ProviderFormValues) => { const parsedConfig = JSON.parse(values.settingsConfig) as Record< string, unknown >; // 构造基础提交数据 const providerData: Omit = { name: values.name.trim(), notes: values.notes?.trim() || undefined, websiteUrl: values.websiteUrl?.trim() || undefined, settingsConfig: parsedConfig, icon: values.icon?.trim() || undefined, iconColor: values.iconColor?.trim() || undefined, ...(values.presetCategory ? { category: values.presetCategory } : {}), ...(values.meta ? { meta: values.meta } : {}), }; const hasCustomEndpoints = providerData.meta?.custom_endpoints && Object.keys(providerData.meta.custom_endpoints).length > 0; if (!hasCustomEndpoints) { // 收集端点候选(仅在缺少自定义端点时兜底) const urlSet = new Set(); const addUrl = (rawUrl?: string) => { const url = (rawUrl || "").trim().replace(/\/+$/, ""); if (url && url.startsWith("http")) { urlSet.add(url); } }; if (values.presetId) { if (appId === "claude") { const presets = providerPresets; const presetIndex = parseInt( values.presetId.replace("claude-", ""), ); if ( !isNaN(presetIndex) && presetIndex >= 0 && presetIndex < presets.length ) { const preset = presets[presetIndex]; if (preset?.endpointCandidates) { preset.endpointCandidates.forEach(addUrl); } } } else if (appId === "codex") { const presets = codexProviderPresets; const presetIndex = parseInt(values.presetId.replace("codex-", "")); if ( !isNaN(presetIndex) && presetIndex >= 0 && presetIndex < presets.length ) { const preset = presets[presetIndex]; if (Array.isArray(preset.endpointCandidates)) { preset.endpointCandidates.forEach(addUrl); } } } else if (appId === "gemini") { const presets = geminiProviderPresets; const presetIndex = parseInt( values.presetId.replace("gemini-", ""), ); if ( !isNaN(presetIndex) && presetIndex >= 0 && presetIndex < presets.length ) { const preset = presets[presetIndex]; if (Array.isArray(preset.endpointCandidates)) { preset.endpointCandidates.forEach(addUrl); } } } } if (appId === "claude") { const env = parsedConfig.env as Record | undefined; if (env?.ANTHROPIC_BASE_URL) { addUrl(env.ANTHROPIC_BASE_URL); } } else if (appId === "codex") { const config = parsedConfig.config as string | undefined; if (config) { const baseUrlMatch = config.match( /base_url\s*=\s*["']([^"']+)["']/, ); if (baseUrlMatch?.[1]) { addUrl(baseUrlMatch[1]); } } } else if (appId === "gemini") { const env = parsedConfig.env as Record | undefined; if (env?.GOOGLE_GEMINI_BASE_URL) { addUrl(env.GOOGLE_GEMINI_BASE_URL); } } const urls = Array.from(urlSet); if (urls.length > 0) { const now = Date.now(); const customEndpoints: Record = {}; urls.forEach((url) => { customEndpoints[url] = { url, addedAt: now, lastUsed: undefined, }; }); providerData.meta = { ...(providerData.meta ?? {}), custom_endpoints: customEndpoints, }; } } await onSubmit(providerData); onOpenChange(false); }, [appId, onSubmit, onOpenChange], ); const submitLabel = appId === "claude" ? t("provider.addClaudeProvider") : appId === "codex" ? t("provider.addCodexProvider") : t("provider.addGeminiProvider"); const footer = ( <> ); return ( onOpenChange(false)} footer={footer} > onOpenChange(false)} showButtons={false} /> ); }