feat(codex): add AiHubMix provider and enhance configuration UX

- Add new AiHubMix provider preset with dual endpoints
- Fix AnyRouter base_url inconsistency (add /v1 suffix)
- Add configuration wizard shortcut link for Codex custom mode
- Improve accessibility with aria-label for wizard button
- Remove unused isCustomMode prop from CodexConfigEditor
- Add internationalization for new UI elements (zh/en)
This commit is contained in:
Jason
2025-11-02 22:22:45 +08:00
parent faeca6b6ce
commit 972650377d
6 changed files with 50 additions and 10 deletions

View File

@@ -28,8 +28,6 @@ interface CodexConfigEditorProps {
configError: string; // config.toml 错误提示 configError: string; // config.toml 错误提示
isCustomMode?: boolean; // 是否为自定义模式
onWebsiteUrlChange?: (url: string) => void; // 更新网址回调 onWebsiteUrlChange?: (url: string) => void; // 更新网址回调
isTemplateModalOpen?: boolean; // 模态框状态 isTemplateModalOpen?: boolean; // 模态框状态

View File

@@ -453,6 +453,12 @@ export function ProviderForm({
presetCategoryLabels={presetCategoryLabels} presetCategoryLabels={presetCategoryLabels}
onPresetChange={handlePresetChange} onPresetChange={handlePresetChange}
category={category} category={category}
appId={appId}
onOpenWizard={
appId === "codex"
? () => setIsCodexTemplateModalOpen(true)
: undefined
}
/> />
)} )}
@@ -523,7 +529,6 @@ export function ProviderForm({
commonConfigError={codexCommonConfigError} commonConfigError={codexCommonConfigError}
authError={codexAuthError} authError={codexAuthError}
configError={codexConfigError} configError={codexConfigError}
isCustomMode={selectedPresetId === "custom"}
onWebsiteUrlChange={(url) => form.setValue("websiteUrl", url)} onWebsiteUrlChange={(url) => form.setValue("websiteUrl", url)}
onNameChange={(name) => form.setValue("name", name)} onNameChange={(name) => form.setValue("name", name)}
isTemplateModalOpen={isCodexTemplateModalOpen} isTemplateModalOpen={isCodexTemplateModalOpen}

View File

@@ -5,6 +5,7 @@ import { Zap } from "lucide-react";
import type { ProviderPreset } from "@/config/claudeProviderPresets"; import type { ProviderPreset } from "@/config/claudeProviderPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets"; import type { CodexProviderPreset } from "@/config/codexProviderPresets";
import type { ProviderCategory } from "@/types"; import type { ProviderCategory } from "@/types";
import type { AppId } from "@/lib/api";
type PresetEntry = { type PresetEntry = {
id: string; id: string;
@@ -18,6 +19,8 @@ interface ProviderPresetSelectorProps {
presetCategoryLabels: Record<string, string>; presetCategoryLabels: Record<string, string>;
onPresetChange: (value: string) => void; onPresetChange: (value: string) => void;
category?: ProviderCategory; // 新增:当前选中的分类 category?: ProviderCategory; // 新增:当前选中的分类
appId?: AppId;
onOpenWizard?: () => void; // Codex 专用:打开配置向导
} }
export function ProviderPresetSelector({ export function ProviderPresetSelector({
@@ -27,11 +30,13 @@ export function ProviderPresetSelector({
presetCategoryLabels, presetCategoryLabels,
onPresetChange, onPresetChange,
category, category,
appId,
onOpenWizard,
}: ProviderPresetSelectorProps) { }: ProviderPresetSelectorProps) {
const { t } = useTranslation(); const { t } = useTranslation();
// 根据分类获取提示文字 // 根据分类获取提示文字
const getCategoryHint = () => { const getCategoryHint = (): React.ReactNode => {
switch (category) { switch (category) {
case "official": case "official":
return t("providerForm.officialHint", { return t("providerForm.officialHint", {
@@ -50,6 +55,23 @@ export function ProviderPresetSelector({
defaultValue: "💡 第三方供应商需要填写 API Key 和请求地址", defaultValue: "💡 第三方供应商需要填写 API Key 和请求地址",
}); });
case "custom": case "custom":
// Codex 自定义:在此位置显示"手动配置…或者 使用配置向导"
if (appId === "codex" && onOpenWizard) {
return (
<>
{t("providerForm.manualConfig")}
<button
type="button"
onClick={onOpenWizard}
className="ml-1 text-blue-500 dark:text-blue-400 hover:text-blue-600 dark:hover:text-blue-300 underline-offset-2 hover:underline"
aria-label={t("providerForm.openConfigWizard")}
>
{t("providerForm.useConfigWizard")}
</button>
</>
);
}
// 其他情况沿用原提示
return t("providerForm.customApiKeyHint", { return t("providerForm.customApiKeyHint", {
defaultValue: "💡 自定义配置需手动填写所有必要字段", defaultValue: "💡 自定义配置需手动填写所有必要字段",
}); });

View File

@@ -70,6 +70,21 @@ export const codexProviderPresets: CodexProviderPreset[] = [
textColor: "#FFFFFF", textColor: "#FFFFFF",
}, },
}, },
{
name: "AiHubMix",
websiteUrl: "https://aihubmix.com",
category: "cn_official",
auth: generateThirdPartyAuth(""),
config: generateThirdPartyConfig(
"aihubmix",
"https://aihubmix.com/v1",
"gpt-5-codex",
),
endpointCandidates: [
"https://aihubmix.com/v1",
"https://api.aihubmix.com/v1",
],
},
{ {
name: "PackyCode", name: "PackyCode",
websiteUrl: "https://www.packyapi.com", websiteUrl: "https://www.packyapi.com",
@@ -80,7 +95,6 @@ export const codexProviderPresets: CodexProviderPreset[] = [
"https://www.packyapi.com/v1", "https://www.packyapi.com/v1",
"gpt-5-codex", "gpt-5-codex",
), ),
// Codex 请求地址候选(用于地址管理/测速)
endpointCandidates: [ endpointCandidates: [
"https://www.packyapi.com/v1", "https://www.packyapi.com/v1",
"https://api-slb.packyapi.com/v1", "https://api-slb.packyapi.com/v1",
@@ -93,14 +107,13 @@ export const codexProviderPresets: CodexProviderPreset[] = [
auth: generateThirdPartyAuth(""), auth: generateThirdPartyAuth(""),
config: generateThirdPartyConfig( config: generateThirdPartyConfig(
"anyrouter", "anyrouter",
"https://anyrouter.top", "https://anyrouter.top/v1",
"gpt-5-codex", "gpt-5-codex",
), ),
// Codex 请求地址候选(用于地址管理/测速)
endpointCandidates: [ endpointCandidates: [
"https://anyrouter.top", "https://anyrouter.top/v1",
"https://q.quuvv.cn", "https://q.quuvv.cn/v1",
"https://pmpjfbhq.cn-nb1.rainapp.top", "https://pmpjfbhq.cn-nb1.rainapp.top/v1",
], ],
}, },
]; ];

View File

@@ -226,6 +226,7 @@
"manageAndTest": "Manage & Test", "manageAndTest": "Manage & Test",
"configContent": "Config Content", "configContent": "Config Content",
"useConfigWizard": "Use Configuration Wizard", "useConfigWizard": "Use Configuration Wizard",
"openConfigWizard": "Open configuration wizard",
"manualConfig": "Manually configure provider, requires complete configuration, or", "manualConfig": "Manually configure provider, requires complete configuration, or",
"officialNoApiKey": "Official login does not require API Key, save directly", "officialNoApiKey": "Official login does not require API Key, save directly",
"codexOfficialNoApiKey": "Official does not require API Key, save directly", "codexOfficialNoApiKey": "Official does not require API Key, save directly",

View File

@@ -226,6 +226,7 @@
"manageAndTest": "管理与测速", "manageAndTest": "管理与测速",
"configContent": "配置内容", "configContent": "配置内容",
"useConfigWizard": "使用配置向导", "useConfigWizard": "使用配置向导",
"openConfigWizard": "打开配置向导",
"manualConfig": "手动配置供应商,需要填写完整的配置信息,或者", "manualConfig": "手动配置供应商,需要填写完整的配置信息,或者",
"officialNoApiKey": "官方登录无需填写 API Key直接保存即可", "officialNoApiKey": "官方登录无需填写 API Key直接保存即可",
"codexOfficialNoApiKey": "官方无需填写 API Key直接保存即可", "codexOfficialNoApiKey": "官方无需填写 API Key直接保存即可",