diff --git a/src/components/ProviderForm.tsx b/src/components/ProviderForm.tsx index 9c94882..a1ebeea 100644 --- a/src/components/ProviderForm.tsx +++ b/src/components/ProviderForm.tsx @@ -12,7 +12,11 @@ import { validateJsonConfig, } from "../utils/providerConfigUtils"; import { providerPresets } from "../config/providerPresets"; -import { codexProviderPresets, generateThirdPartyAuth, generateThirdPartyConfig } from "../config/codexProviderPresets"; +import { + codexProviderPresets, + generateThirdPartyAuth, + generateThirdPartyConfig, +} from "../config/codexProviderPresets"; import PresetSelector from "./ProviderForm/PresetSelector"; import ApiKeyInput from "./ProviderForm/ApiKeyInput"; import ClaudeConfigEditor from "./ProviderForm/ClaudeConfigEditor"; @@ -60,7 +64,7 @@ const ProviderForm: React.FC = ({ : "", }); const [category, setCategory] = useState( - initialData?.category, + initialData?.category ); // Claude 模型配置状态 @@ -72,10 +76,11 @@ const ProviderForm: React.FC = ({ const [codexAuth, setCodexAuthState] = useState(""); const [codexConfig, setCodexConfigState] = useState(""); const [codexApiKey, setCodexApiKey] = useState(""); - const [isCodexTemplateModalOpen, setIsCodexTemplateModalOpen] = useState(false); + const [isCodexTemplateModalOpen, setIsCodexTemplateModalOpen] = + useState(false); // -1 表示自定义,null 表示未选择,>= 0 表示预设索引 const [selectedCodexPreset, setSelectedCodexPreset] = useState( - showPresets && isCodex ? -1 : null, + showPresets && isCodex ? -1 : null ); const setCodexAuth = (value: string) => { @@ -140,7 +145,7 @@ const ProviderForm: React.FC = ({ } try { const stored = window.localStorage.getItem( - CODEX_COMMON_CONFIG_STORAGE_KEY, + CODEX_COMMON_CONFIG_STORAGE_KEY ); if (stored && stored.trim()) { return stored.trim(); @@ -154,7 +159,7 @@ const ProviderForm: React.FC = ({ const isUpdatingFromCodexCommonConfig = useRef(false); // -1 表示自定义,null 表示未选择,>= 0 表示预设索引 const [selectedPreset, setSelectedPreset] = useState( - showPresets ? -1 : null, + showPresets ? -1 : null ); const [apiKey, setApiKey] = useState(""); const [codexAuthError, setCodexAuthError] = useState(""); @@ -223,11 +228,11 @@ const ProviderForm: React.FC = ({ const configString = JSON.stringify( initialData.settingsConfig, null, - 2, + 2 ); const hasCommon = hasCommonConfigSnippet( configString, - commonConfigSnippet, + commonConfigSnippet ); setUseCommonConfig(hasCommon); setSettingsConfigError(validateSettingsConfig(configString)); @@ -243,14 +248,14 @@ const ProviderForm: React.FC = ({ if (config.env) { setClaudeModel(config.env.ANTHROPIC_MODEL || ""); setClaudeSmallFastModel( - config.env.ANTHROPIC_SMALL_FAST_MODEL || "", + config.env.ANTHROPIC_SMALL_FAST_MODEL || "" ); setBaseUrl(config.env.ANTHROPIC_BASE_URL || ""); // 初始化基础 URL // 初始化 Kimi 模型选择 setKimiAnthropicModel(config.env.ANTHROPIC_MODEL || ""); setKimiAnthropicSmallFastModel( - config.env.ANTHROPIC_SMALL_FAST_MODEL || "", + config.env.ANTHROPIC_SMALL_FAST_MODEL || "" ); } } @@ -258,7 +263,7 @@ const ProviderForm: React.FC = ({ // Codex 初始化时检查 TOML 通用配置 const hasCommon = hasTomlCommonConfigSnippet( codexConfig, - codexCommonConfigSnippet, + codexCommonConfigSnippet ); setUseCodexCommonConfig(hasCommon); } @@ -278,7 +283,7 @@ const ProviderForm: React.FC = ({ if (selectedPreset !== null && selectedPreset >= 0) { const preset = providerPresets[selectedPreset]; setCategory( - preset?.category || (preset?.isOfficial ? "official" : undefined), + preset?.category || (preset?.isOfficial ? "official" : undefined) ); } else if (selectedPreset === -1) { setCategory("custom"); @@ -287,7 +292,7 @@ const ProviderForm: React.FC = ({ if (selectedCodexPreset !== null && selectedCodexPreset >= 0) { const preset = codexProviderPresets[selectedCodexPreset]; setCategory( - preset?.category || (preset?.isOfficial ? "official" : undefined), + preset?.category || (preset?.isOfficial ? "official" : undefined) ); } else if (selectedCodexPreset === -1) { setCategory("custom"); @@ -302,7 +307,7 @@ const ProviderForm: React.FC = ({ if (commonConfigSnippet.trim()) { window.localStorage.setItem( COMMON_CONFIG_STORAGE_KEY, - commonConfigSnippet, + commonConfigSnippet ); } else { window.localStorage.removeItem(COMMON_CONFIG_STORAGE_KEY); @@ -365,7 +370,7 @@ const ProviderForm: React.FC = ({ } } else { const currentSettingsError = validateSettingsConfig( - formData.settingsConfig, + formData.settingsConfig ); setSettingsConfigError(currentSettingsError); if (currentSettingsError) { @@ -396,7 +401,7 @@ const ProviderForm: React.FC = ({ }; const handleChange = ( - e: React.ChangeEvent, + e: React.ChangeEvent ) => { const { name, value } = e.target; @@ -426,7 +431,7 @@ const ProviderForm: React.FC = ({ const { updatedConfig, error: snippetError } = updateCommonConfigSnippet( formData.settingsConfig, commonConfigSnippet, - checked, + checked ); if (snippetError) { @@ -459,7 +464,7 @@ const ProviderForm: React.FC = ({ const { updatedConfig } = updateCommonConfigSnippet( formData.settingsConfig, previousSnippet, - false, + false ); // 直接更新 formData,不通过 handleChange updateSettingsConfigValue(updatedConfig); @@ -481,7 +486,7 @@ const ProviderForm: React.FC = ({ const removeResult = updateCommonConfigSnippet( formData.settingsConfig, previousSnippet, - false, + false ); if (removeResult.error) { setCommonConfigError(removeResult.error); @@ -493,7 +498,7 @@ const ProviderForm: React.FC = ({ const addResult = updateCommonConfigSnippet( removeResult.updatedConfig, value, - true, + true ); if (addResult.error) { @@ -533,7 +538,7 @@ const ProviderForm: React.FC = ({ }); setSettingsConfigError(validateSettingsConfig(configString)); setCategory( - preset.category || (preset.isOfficial ? "official" : undefined), + preset.category || (preset.isOfficial ? "official" : undefined) ); // 设置选中的预设 @@ -559,7 +564,7 @@ const ProviderForm: React.FC = ({ if (preset.name?.includes("Kimi")) { setKimiAnthropicModel(config.env.ANTHROPIC_MODEL || ""); setKimiAnthropicSmallFastModel( - config.env.ANTHROPIC_SMALL_FAST_MODEL || "", + config.env.ANTHROPIC_SMALL_FAST_MODEL || "" ); } } else { @@ -605,7 +610,7 @@ const ProviderForm: React.FC = ({ // Codex: 应用预设 const applyCodexPreset = ( preset: (typeof codexProviderPresets)[0], - index: number, + index: number ) => { const authString = JSON.stringify(preset.auth || {}, null, 2); setCodexAuth(authString); @@ -619,7 +624,7 @@ const ProviderForm: React.FC = ({ setSelectedCodexPreset(index); setCategory( - preset.category || (preset.isOfficial ? "official" : undefined), + preset.category || (preset.isOfficial ? "official" : undefined) ); // 清空 API Key,让用户重新输入 @@ -629,7 +634,7 @@ const ProviderForm: React.FC = ({ // Codex: 处理点击自定义按钮 const handleCodexCustomClick = () => { setSelectedCodexPreset(-1); - + // 设置自定义模板 const customAuth = generateThirdPartyAuth(""); const customConfig = generateThirdPartyConfig( @@ -637,7 +642,7 @@ const ProviderForm: React.FC = ({ "https://your-api-endpoint.com/v1", "gpt-5-codex" ); - + setFormData({ name: "", websiteUrl: "", @@ -657,7 +662,7 @@ const ProviderForm: React.FC = ({ const configString = setApiKeyInConfig( formData.settingsConfig, key.trim(), - { createIfMissing: selectedPreset !== null && selectedPreset !== -1 }, + { createIfMissing: selectedPreset !== null && selectedPreset !== -1 } ); // 更新表单配置 @@ -700,11 +705,8 @@ const ProviderForm: React.FC = ({ // Codex: 处理通用配置开关 const handleCodexCommonConfigToggle = (checked: boolean) => { const snippet = codexCommonConfigSnippet.trim(); - const { updatedConfig, error: snippetError } = updateTomlCommonConfigSnippet( - codexConfig, - snippet, - checked, - ); + const { updatedConfig, error: snippetError } = + updateTomlCommonConfigSnippet(codexConfig, snippet, checked); if (snippetError) { setCodexCommonConfigError(snippetError); @@ -735,7 +737,7 @@ const ProviderForm: React.FC = ({ const { updatedConfig } = updateTomlCommonConfigSnippet( codexConfig, previousSnippet, - false, + false ); setCodexConfig(updatedConfig); setUseCodexCommonConfig(false); @@ -748,12 +750,12 @@ const ProviderForm: React.FC = ({ const removeResult = updateTomlCommonConfigSnippet( codexConfig, previousSnippet, - false, + false ); const addResult = updateTomlCommonConfigSnippet( removeResult.updatedConfig, sanitizedValue, - true, + true ); if (addResult.error) { @@ -775,7 +777,7 @@ const ProviderForm: React.FC = ({ try { window.localStorage.setItem( CODEX_COMMON_CONFIG_STORAGE_KEY, - sanitizedValue, + sanitizedValue ); } catch { // ignore localStorage 写入失败 @@ -788,7 +790,7 @@ const ProviderForm: React.FC = ({ if (!isUpdatingFromCodexCommonConfig.current) { const hasCommon = hasTomlCommonConfigSnippet( value, - codexCommonConfigSnippet, + codexCommonConfigSnippet ); setUseCodexCommonConfig(hasCommon); } @@ -901,7 +903,7 @@ const ProviderForm: React.FC = ({ // 处理模型输入变化,自动更新 JSON 配置 const handleModelChange = ( field: "ANTHROPIC_MODEL" | "ANTHROPIC_SMALL_FAST_MODEL", - value: string, + value: string ) => { if (field === "ANTHROPIC_MODEL") { setClaudeModel(value); @@ -931,7 +933,7 @@ const ProviderForm: React.FC = ({ // Kimi 模型选择处理函数 const handleKimiModelChange = ( field: "ANTHROPIC_MODEL" | "ANTHROPIC_SMALL_FAST_MODEL", - value: string, + value: string ) => { if (field === "ANTHROPIC_MODEL") { setKimiAnthropicModel(value); @@ -956,7 +958,7 @@ const ProviderForm: React.FC = ({ useEffect(() => { if (!initialData) return; const parsedKey = getApiKeyFromConfig( - JSON.stringify(initialData.settingsConfig), + JSON.stringify(initialData.settingsConfig) ); if (parsedKey) setApiKey(parsedKey); }, [initialData]); @@ -1222,7 +1224,13 @@ const ProviderForm: React.FC = ({ onWebsiteUrlChange={(url) => { setFormData({ ...formData, - websiteUrl: url + websiteUrl: url, + }); + }} + onNameChange={(name) => { + setFormData({ + ...formData, + name, }); }} isTemplateModalOpen={isCodexTemplateModalOpen} @@ -1268,7 +1276,7 @@ const ProviderForm: React.FC = ({ onChange={(e) => handleModelChange( "ANTHROPIC_SMALL_FAST_MODEL", - e.target.value, + e.target.value ) } placeholder="例如: GLM-4.5-Air" diff --git a/src/components/ProviderForm/CodexConfigEditor.tsx b/src/components/ProviderForm/CodexConfigEditor.tsx index ce5e617..dd907eb 100644 --- a/src/components/ProviderForm/CodexConfigEditor.tsx +++ b/src/components/ProviderForm/CodexConfigEditor.tsx @@ -39,6 +39,8 @@ interface CodexConfigEditorProps { isTemplateModalOpen?: boolean; // 新增:模态框状态 setIsTemplateModalOpen?: (open: boolean) => void; // 新增:设置模态框状态 + + onNameChange?: (name: string) => void; // 新增:更新供应商名称回调 } const CodexConfigEditor: React.FC = ({ @@ -66,6 +68,8 @@ const CodexConfigEditor: React.FC = ({ onWebsiteUrlChange, + onNameChange, + isTemplateModalOpen: externalTemplateModalOpen, setIsTemplateModalOpen: externalSetTemplateModalOpen, @@ -100,6 +104,8 @@ const CodexConfigEditor: React.FC = ({ // 移除自动填充逻辑,因为现在在点击自定义按钮时就已经填充 + const [templateDisplayName, setTemplateDisplayName] = useState(""); + useEffect(() => { if (commonConfigError && !isCommonConfigModalOpen) { setIsCommonConfigModalOpen(true); @@ -175,6 +181,13 @@ const CodexConfigEditor: React.FC = ({ } } + if (onNameChange) { + const trimmedName = templateDisplayName.trim(); + if (trimmedName) { + onNameChange(trimmedName); + } + } + setTemplateApiKey(""); setTemplateProviderName(""); @@ -185,6 +198,8 @@ const CodexConfigEditor: React.FC = ({ setTemplateModelName("gpt-5-codex"); + setTemplateDisplayName(""); + closeTemplateModal(); }; @@ -228,13 +243,7 @@ const CodexConfigEditor: React.FC = ({ onChange={(e) => handleAuthChange(e.target.value)} onBlur={onAuthBlur} placeholder={`{ - - - "OPENAI_API_KEY": "sk-your-api-key-here" - - - }`} rows={6} required @@ -383,6 +392,30 @@ const CodexConfigEditor: React.FC = ({ 供应商名称 + { + setTemplateDisplayName(e.target.value); + if (onNameChange) { + onNameChange(e.target.value); + } + }} + onKeyDown={handleTemplateInputKeyDown} + placeholder="例如:Codex 官方(可选)" + className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100" + /> + +

+ 将显示在供应商列表中,可使用中文 +

+ + +
+ + = ({
)} -