diff --git a/src/components/ProviderForm/ApiKeyInput.tsx b/src/components/providers/forms/ApiKeyInput.tsx similarity index 100% rename from src/components/ProviderForm/ApiKeyInput.tsx rename to src/components/providers/forms/ApiKeyInput.tsx diff --git a/src/components/ProviderForm/ClaudeConfigEditor.tsx b/src/components/providers/forms/ClaudeConfigEditor.tsx similarity index 98% rename from src/components/ProviderForm/ClaudeConfigEditor.tsx rename to src/components/providers/forms/ClaudeConfigEditor.tsx index 1def4e6..a340f90 100644 --- a/src/components/ProviderForm/ClaudeConfigEditor.tsx +++ b/src/components/providers/forms/ClaudeConfigEditor.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; -import JsonEditor from "../JsonEditor"; +import JsonEditor from "@/components/JsonEditor"; import { X, Save } from "lucide-react"; -import { isLinux } from "../../lib/platform"; +import { isLinux } from "@/lib/platform"; import { useTranslation } from "react-i18next"; interface ClaudeConfigEditorProps { diff --git a/src/components/providers/forms/ClaudeFormFields.tsx b/src/components/providers/forms/ClaudeFormFields.tsx index e02c36b..42f9ee7 100644 --- a/src/components/providers/forms/ClaudeFormFields.tsx +++ b/src/components/providers/forms/ClaudeFormFields.tsx @@ -1,10 +1,9 @@ import { useTranslation } from "react-i18next"; import { FormLabel } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput"; -import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest"; -import KimiModelSelector from "@/components/ProviderForm/KimiModelSelector"; -import { Zap } from "lucide-react"; +import EndpointSpeedTest from "./EndpointSpeedTest"; +import KimiModelSelector from "./KimiModelSelector"; +import { ApiKeySection, EndpointField } from "./shared"; import type { ProviderCategory } from "@/types"; import type { TemplateValueConfig } from "@/config/providerPresets"; @@ -90,38 +89,13 @@ export function ClaudeFormFields({ <> {/* API Key 输入框 */} {shouldShowApiKey && ( -
- - {/* API Key 获取链接 */} - {shouldShowApiKeyLink && websiteUrl && ( -
- - {t("providerForm.getApiKey", { - defaultValue: "获取 API Key", - })} - -
- )} -
+ )} {/* 模板变量输入 */} @@ -161,40 +135,19 @@ export function ClaudeFormFields({ {/* Base URL 输入框 */} {shouldShowSpeedTest && ( -
-
- - {t("providerForm.apiEndpoint", { defaultValue: "API 端点" })} - - -
- onBaseUrlChange(e.target.value)} - placeholder={t("providerForm.apiEndpointPlaceholder", { - defaultValue: "https://api.example.com", - })} - autoComplete="off" - /> -
-

- {t("providerForm.apiHint", { - defaultValue: "API 端点地址用于连接服务器", - })} -

-
-
+ onEndpointModalToggle(true)} + /> )} {/* 端点测速弹窗 */} diff --git a/src/components/ProviderForm/CodexConfigEditor.tsx b/src/components/providers/forms/CodexConfigEditor.tsx similarity index 99% rename from src/components/ProviderForm/CodexConfigEditor.tsx rename to src/components/providers/forms/CodexConfigEditor.tsx index eae23be..a0c9fb4 100644 --- a/src/components/ProviderForm/CodexConfigEditor.tsx +++ b/src/components/providers/forms/CodexConfigEditor.tsx @@ -2,13 +2,13 @@ import React, { useState, useEffect, useRef } from "react"; import { X, Save } from "lucide-react"; -import { isLinux } from "../../lib/platform"; +import { isLinux } from "@/lib/platform"; import { useTranslation } from "react-i18next"; import { generateThirdPartyAuth, generateThirdPartyConfig, -} from "../../config/codexProviderPresets"; +} from "@/config/codexProviderPresets"; interface CodexConfigEditorProps { authValue: string; diff --git a/src/components/providers/forms/CodexFormFields.tsx b/src/components/providers/forms/CodexFormFields.tsx index 6672779..21f2852 100644 --- a/src/components/providers/forms/CodexFormFields.tsx +++ b/src/components/providers/forms/CodexFormFields.tsx @@ -1,9 +1,6 @@ import { useTranslation } from "react-i18next"; -import { FormLabel } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import ApiKeyInput from "@/components/ProviderForm/ApiKeyInput"; -import EndpointSpeedTest from "@/components/ProviderForm/EndpointSpeedTest"; -import { Zap } from "lucide-react"; +import EndpointSpeedTest from "./EndpointSpeedTest"; +import { ApiKeySection, EndpointField } from "./shared"; import type { ProviderCategory } from "@/types"; interface EndpointCandidate { @@ -49,77 +46,39 @@ export function CodexFormFields({ return ( <> {/* Codex API Key 输入框 */} -
- - {/* Codex API Key 获取链接 */} - {shouldShowApiKeyLink && websiteUrl && ( -
- - {t("providerForm.getApiKey", { - defaultValue: "获取 API Key", - })} - -
- )} -
+ {/* Codex Base URL 输入框 */} {shouldShowSpeedTest && ( -
-
- - {t("codexConfig.apiUrlLabel", { defaultValue: "API 端点" })} - - -
- onBaseUrlChange(e.target.value)} - placeholder={t("providerForm.codexApiEndpointPlaceholder", { - defaultValue: "https://api.example.com/v1", - })} - autoComplete="off" - /> -
-

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

-
-
+ onEndpointModalToggle(true)} + /> )} {/* 端点测速弹窗 - Codex */} diff --git a/src/components/ProviderForm/EndpointSpeedTest.tsx b/src/components/providers/forms/EndpointSpeedTest.tsx similarity index 100% rename from src/components/ProviderForm/EndpointSpeedTest.tsx rename to src/components/providers/forms/EndpointSpeedTest.tsx diff --git a/src/components/ProviderForm/KimiModelSelector.tsx b/src/components/providers/forms/KimiModelSelector.tsx similarity index 100% rename from src/components/ProviderForm/KimiModelSelector.tsx rename to src/components/providers/forms/KimiModelSelector.tsx diff --git a/src/components/ProviderForm/PresetSelector.tsx b/src/components/providers/forms/PresetSelector.tsx similarity index 100% rename from src/components/ProviderForm/PresetSelector.tsx rename to src/components/providers/forms/PresetSelector.tsx diff --git a/src/components/providers/forms/ProviderForm.tsx b/src/components/providers/forms/ProviderForm.tsx index c02d7a2..3d4c300 100644 --- a/src/components/providers/forms/ProviderForm.tsx +++ b/src/components/providers/forms/ProviderForm.tsx @@ -13,7 +13,7 @@ import { type CodexProviderPreset, } from "@/config/codexProviderPresets"; import { applyTemplateValues } from "@/utils/providerConfigUtils"; -import CodexConfigEditor from "@/components/ProviderForm/CodexConfigEditor"; +import CodexConfigEditor from "./CodexConfigEditor"; import { CommonConfigEditor } from "./CommonConfigEditor"; import { ProviderPresetSelector } from "./ProviderPresetSelector"; import { BasicFormFields } from "./BasicFormFields"; diff --git a/src/components/providers/forms/shared/ApiKeySection.tsx b/src/components/providers/forms/shared/ApiKeySection.tsx new file mode 100644 index 0000000..4b41a69 --- /dev/null +++ b/src/components/providers/forms/shared/ApiKeySection.tsx @@ -0,0 +1,76 @@ +import { useTranslation } from "react-i18next"; +import ApiKeyInput from "../ApiKeyInput"; +import type { ProviderCategory } from "@/types"; + +interface ApiKeySectionProps { + id?: string; + label?: string; + value: string; + onChange: (value: string) => void; + category?: ProviderCategory; + shouldShowLink: boolean; + websiteUrl: string; + placeholder?: { + official: string; + thirdParty: string; + }; + disabled?: boolean; +} + +export function ApiKeySection({ + id, + label, + value, + onChange, + category, + shouldShowLink, + websiteUrl, + placeholder, + disabled, +}: ApiKeySectionProps) { + const { t } = useTranslation(); + + const defaultPlaceholder = { + official: t("providerForm.officialNoApiKey", { + defaultValue: "官方供应商无需 API Key", + }), + thirdParty: t("providerForm.apiKeyAutoFill", { + defaultValue: "输入 API Key,将自动填充到配置", + }), + }; + + const finalPlaceholder = placeholder || defaultPlaceholder; + + return ( +
+ + {/* API Key 获取链接 */} + {shouldShowLink && websiteUrl && ( + + )} +
+ ); +} diff --git a/src/components/providers/forms/shared/EndpointField.tsx b/src/components/providers/forms/shared/EndpointField.tsx new file mode 100644 index 0000000..ab5eee1 --- /dev/null +++ b/src/components/providers/forms/shared/EndpointField.tsx @@ -0,0 +1,63 @@ +import { useTranslation } from "react-i18next"; +import { FormLabel } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Zap } from "lucide-react"; + +interface EndpointFieldProps { + id: string; + label: string; + value: string; + onChange: (value: string) => void; + placeholder: string; + hint: string; + showManageButton?: boolean; + onManageClick?: () => void; + manageButtonLabel?: string; +} + +export function EndpointField({ + id, + label, + value, + onChange, + placeholder, + hint, + showManageButton = true, + onManageClick, + manageButtonLabel, +}: EndpointFieldProps) { + const { t } = useTranslation(); + + const defaultManageLabel = t("providerForm.manageAndTest", { + defaultValue: "管理和测速", + }); + + return ( +
+
+ {label} + {showManageButton && onManageClick && ( + + )} +
+ onChange(e.target.value)} + placeholder={placeholder} + autoComplete="off" + /> +
+

{hint}

+
+
+ ); +} diff --git a/src/components/providers/forms/shared/index.ts b/src/components/providers/forms/shared/index.ts new file mode 100644 index 0000000..f9b85fb --- /dev/null +++ b/src/components/providers/forms/shared/index.ts @@ -0,0 +1,2 @@ +export { ApiKeySection } from "./ApiKeySection"; +export { EndpointField } from "./EndpointField";