From a32aeaf73ebc28b11d190135618afde174be0d62 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 16 Oct 2025 18:50:44 +0800 Subject: [PATCH] feat: add Codex support to ProviderForm - Create useCodexConfigState hook for managing Codex configuration - Handles auth.json (JSON) and config.toml (TOML) separately - Bidirectional sync with Base URL extraction - API Key management from auth.OPENAI_API_KEY - Integrate Codex-specific UI components - Codex API Key input - Codex Base URL input with endpoint speed test - CodexConfigEditor for auth/config editing - Update handlePresetChange to support Codex presets - Update handleSubmit to compose Codex auth+config - Conditional rendering: Claude uses JsonEditor, Codex uses CodexConfigEditor --- .../providers/forms/ProviderForm.tsx | 193 +++++++++++++++--- src/components/providers/forms/hooks/index.ts | 1 + .../forms/hooks/useCodexConfigState.ts | 182 +++++++++++++++++ 3 files changed, 342 insertions(+), 34 deletions(-) create mode 100644 src/components/providers/forms/hooks/useCodexConfigState.ts diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index 930f40d..f5f014d 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -25,8 +25,15 @@ import { import { applyTemplateValues } from "@/utils/providerConfigUtils"; import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput"; import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest"; +import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor"; import { Zap } from "lucide-react"; -import { useProviderCategory, useApiKeyState, useBaseUrlState, useModelState } from "./hooks"; +import { + useProviderCategory, + useApiKeyState, + useBaseUrlState, + useModelState, + useCodexConfigState, +} from "./hooks"; const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {}, config: {} }, null, 2); const CODEX_DEFAULT_CONFIG = JSON.stringify({ auth: {}, config: "" }, null, 2); @@ -129,6 +136,22 @@ export function ProviderForm({ onConfigChange: (config) => form.setValue("settingsConfig", config), }); + // 使用 Codex 配置 hook (仅 Codex 模式) + const { + codexAuth, + codexConfig, + codexApiKey, + codexBaseUrl, + codexAuthError, + setCodexAuth, + handleCodexApiKeyChange, + handleCodexBaseUrlChange, + handleCodexConfigChange, + resetCodexConfig, + } = useCodexConfigState({ initialData }); + + const [isCodexEndpointModalOpen, setIsCodexEndpointModalOpen] = useState(false); + useEffect(() => { form.reset(defaultValues); }, [defaultValues, form]); @@ -142,11 +165,31 @@ export function ProviderForm({ }, [theme]); const handleSubmit = (values: ProviderFormData) => { + let settingsConfig: string; + + // Codex: 组合 auth 和 config + if (appType === "codex") { + try { + const authJson = JSON.parse(codexAuth); + const configObj = { + auth: authJson, + config: codexConfig ?? "", + }; + settingsConfig = JSON.stringify(configObj); + } catch (err) { + // 如果解析失败,使用表单中的配置 + settingsConfig = values.settingsConfig.trim(); + } + } else { + // Claude: 使用表单配置 + settingsConfig = values.settingsConfig.trim(); + } + const payload: ProviderFormValues = { ...values, name: values.name.trim(), websiteUrl: values.websiteUrl?.trim() ?? "", - settingsConfig: values.settingsConfig.trim(), + settingsConfig, }; if (activePreset) { @@ -216,6 +259,11 @@ export function ProviderForm({ if (value === "custom") { setActivePreset(null); form.reset(defaultValues); + + // Codex 自定义模式:重置为空配置 + if (appType === "codex") { + resetCodexConfig({}, ""); + } return; } @@ -231,15 +279,17 @@ export function ProviderForm({ if (appType === "codex") { const preset = entry.preset as CodexProviderPreset; - const config = { - auth: preset.auth ?? {}, - config: preset.config ?? "", - }; + const auth = preset.auth ?? {}; + const config = preset.config ?? ""; + // 重置 Codex 配置 + resetCodexConfig(auth, config); + + // 更新表单其他字段 form.reset({ name: preset.name, websiteUrl: preset.websiteUrl ?? "", - settingsConfig: JSON.stringify(config, null, 2), + settingsConfig: JSON.stringify({ auth, config }, null, 2), }); return; } @@ -460,34 +510,109 @@ export function ProviderForm({ )} - ( - - - {t("provider.configJson", { defaultValue: "配置 JSON" })} + {/* Codex API Key 输入框 */} + {appType === "codex" && !isEditMode && ( +
+ +
+ )} + + {/* Codex Base URL 输入框 */} + {appType === "codex" && shouldShowSpeedTest && ( +
+
+ + {t("codexConfig.apiUrlLabel", { defaultValue: "API 端点" })} - -
- -
-
- - - )} - /> + +
+ handleCodexBaseUrlChange(e.target.value)} + placeholder={t("providerForm.codexApiEndpointPlaceholder", { defaultValue: "https://api.example.com/v1" })} + autoComplete="off" + /> +
+

+ {t("providerForm.codexApiHint", { defaultValue: "Codex API 端点地址" })} +

+
+
+ )} + + {/* 端点测速弹窗 - Codex */} + {appType === "codex" && shouldShowSpeedTest && isCodexEndpointModalOpen && ( + setIsCodexEndpointModalOpen(false)} + /> + )} + + {/* 配置编辑器:Claude 使用 JSON 编辑器,Codex 使用专用编辑器 */} + {appType === "codex" ? ( + {}} + commonConfigSnippet="" + onCommonConfigSnippetChange={() => {}} + commonConfigError="" + authError={codexAuthError} + /> + ) : ( + ( + + + {t("provider.configJson", { defaultValue: "配置 JSON" })} + + +
+ +
+
+ +
+ )} + /> + )}