refactor(types): rename AppType to AppId for semantic clarity

Rename `AppType` to `AppId` across the entire frontend codebase to better
reflect its purpose as an application identifier rather than a type category.
This aligns frontend naming with backend command parameter conventions.

Changes:
- Rename type `AppType` to `AppId` in src/lib/api/types.ts
- Remove `AppType` export from src/lib/api/index.ts
- Update all component props from `appType` to `appId` (43 files)
- Update all variable names from `appType` to `appId`
- Synchronize documentation (CHANGELOG, refactoring plans)
- Update test files and MSW mocks

BREAKING CHANGE: `AppType` type is no longer exported. Use `AppId` instead.
All component props have been renamed from `appType` to `appId`.
This commit is contained in:
Jason
2025-10-30 14:59:15 +08:00
parent 80dd6e9381
commit 8e4a0a1bbb
43 changed files with 327 additions and 347 deletions

View File

@@ -149,7 +149,7 @@ export function ClaudeFormFields({
{/* 端点测速弹窗 */}
{shouldShowSpeedTest && isEndpointModalOpen && (
<EndpointSpeedTest
appType="claude"
appId="claude"
value={baseUrl}
onChange={onBaseUrlChange}
initialEndpoints={speedTestEndpoints}

View File

@@ -80,7 +80,7 @@ export function CodexFormFields({
{/* 端点测速弹窗 - Codex */}
{shouldShowSpeedTest && isEndpointModalOpen && (
<EndpointSpeedTest
appType="codex"
appId="codex"
value={codexBaseUrl}
onChange={onBaseUrlChange}
initialEndpoints={speedTestEndpoints}

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Zap, Loader2, Plus, X, AlertCircle, Save } from "lucide-react";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import { vscodeApi } from "@/lib/api/vscode";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -28,7 +28,7 @@ interface TestResult {
}
interface EndpointSpeedTestProps {
appType: AppType;
appId: AppId;
providerId?: string;
value: string;
onChange: (url: string) => void;
@@ -82,7 +82,7 @@ const buildInitialEntries = (
};
const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
appType,
appId,
providerId,
value,
onChange,
@@ -114,7 +114,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
if (!providerId) return;
const customEndpoints = await vscodeApi.getCustomEndpoints(
appType,
appId,
providerId,
);
@@ -167,7 +167,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
return () => {
cancelled = true;
};
}, [appType, visible, providerId, t]);
}, [appId, visible, providerId, t]);
useEffect(() => {
setEntries((prev) => {
@@ -284,7 +284,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
// 保存到后端
try {
if (providerId) {
await vscodeApi.addCustomEndpoint(appType, providerId, sanitized);
await vscodeApi.addCustomEndpoint(appId, providerId, sanitized);
}
// 更新本地状态
@@ -318,7 +318,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
entries,
normalizedSelected,
onChange,
appType,
appId,
providerId,
t,
]);
@@ -331,7 +331,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
// 如果有 providerId尝试从后端删除
if (entry.isCustom && providerId) {
try {
await vscodeApi.removeCustomEndpoint(appType, providerId, entry.url);
await vscodeApi.removeCustomEndpoint(appId, providerId, entry.url);
} catch (error) {
const errorMsg =
error instanceof Error ? error.message : String(error);
@@ -362,7 +362,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
return next;
});
},
[normalizedSelected, onChange, appType, providerId, t],
[normalizedSelected, onChange, appId, providerId, t],
);
const runSpeedTest = useCallback(async () => {
@@ -387,7 +387,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
try {
const results = await vscodeApi.testApiEndpoints(urls, {
timeoutSecs: ENDPOINT_TIMEOUT_SECS[appType],
timeoutSecs: ENDPOINT_TIMEOUT_SECS[appId],
});
const resultMap = new Map(
@@ -437,7 +437,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
} finally {
setIsTesting(false);
}
}, [entries, autoSelect, appType, normalizedSelected, onChange, t]);
}, [entries, autoSelect, appId, normalizedSelected, onChange, t]);
const handleSelect = useCallback(
async (url: string) => {
@@ -447,7 +447,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
const entry = entries.find((e) => e.url === url);
if (entry?.isCustom && providerId) {
try {
await vscodeApi.updateEndpointLastUsed(appType, providerId, url);
await vscodeApi.updateEndpointLastUsed(appId, providerId, url);
} catch (error) {
console.error(t("endpointTest.updateLastUsedFailed"), error);
}
@@ -455,7 +455,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
onChange(url);
},
[normalizedSelected, onChange, appType, entries, providerId, t],
[normalizedSelected, onChange, appId, entries, providerId, t],
);
return (

View File

@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import { Form } from "@/components/ui/form";
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import type { ProviderCategory, ProviderMeta } from "@/types";
import { providerPresets, type ProviderPreset } from "@/config/providerPresets";
import {
@@ -45,7 +45,7 @@ type PresetEntry = {
};
interface ProviderFormProps {
appType: AppType;
appId: AppId;
submitLabel: string;
onSubmit: (values: ProviderFormValues) => void;
onCancel: () => void;
@@ -60,7 +60,7 @@ interface ProviderFormProps {
}
export function ProviderForm({
appType,
appId,
submitLabel,
onSubmit,
onCancel,
@@ -86,7 +86,7 @@ export function ProviderForm({
// 使用 category hook
const { category } = useProviderCategory({
appType,
appId,
selectedPresetId,
isEditMode,
initialCategory: initialData?.category,
@@ -95,7 +95,7 @@ export function ProviderForm({
useEffect(() => {
setSelectedPresetId(initialData ? null : "custom");
setActivePreset(null);
}, [appType, initialData]);
}, [appId, initialData]);
const defaultValues: ProviderFormData = useMemo(
() => ({
@@ -103,11 +103,11 @@ export function ProviderForm({
websiteUrl: initialData?.websiteUrl ?? "",
settingsConfig: initialData?.settingsConfig
? JSON.stringify(initialData.settingsConfig, null, 2)
: appType === "codex"
: appId === "codex"
? CODEX_DEFAULT_CONFIG
: CLAUDE_DEFAULT_CONFIG,
}),
[initialData, appType],
[initialData, appId],
);
const form = useForm<ProviderFormData>({
@@ -130,7 +130,7 @@ export function ProviderForm({
// 使用 Base URL hook (仅 Claude 模式)
const { baseUrl, handleClaudeBaseUrlChange } = useBaseUrlState({
appType,
appType: appId,
category,
settingsConfig: form.watch("settingsConfig"),
codexConfig: "",
@@ -202,7 +202,7 @@ export function ProviderForm({
);
const presetEntries = useMemo(() => {
if (appType === "codex") {
if (appId === "codex") {
return codexProviderPresets.map<PresetEntry>((preset, index) => ({
id: `codex-${index}`,
preset,
@@ -212,7 +212,7 @@ export function ProviderForm({
id: `claude-${index}`,
preset,
}));
}, [appType]);
}, [appId]);
// 使用 Kimi 模型选择器 hook
const {
@@ -240,8 +240,8 @@ export function ProviderForm({
handleTemplateValueChange,
validateTemplateValues,
} = useTemplateValues({
selectedPresetId: appType === "claude" ? selectedPresetId : null,
presetEntries: appType === "claude" ? presetEntries : [],
selectedPresetId: appId === "claude" ? selectedPresetId : null,
presetEntries: appId === "claude" ? presetEntries : [],
settingsConfig: form.watch("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config),
});
@@ -256,7 +256,7 @@ export function ProviderForm({
} = useCommonConfigSnippet({
settingsConfig: form.watch("settingsConfig"),
onConfigChange: (config) => form.setValue("settingsConfig", config),
initialData: appType === "claude" ? initialData : undefined,
initialData: appId === "claude" ? initialData : undefined,
});
// 使用 Codex 通用配置片段 hook (仅 Codex 模式)
@@ -269,14 +269,14 @@ export function ProviderForm({
} = useCodexCommonConfig({
codexConfig,
onConfigChange: handleCodexConfigChange,
initialData: appType === "codex" ? initialData : undefined,
initialData: appId === "codex" ? initialData : undefined,
});
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
const handleSubmit = (values: ProviderFormData) => {
// 验证模板变量(仅 Claude 模式)
if (appType === "claude" && templateValueEntries.length > 0) {
if (appId === "claude" && templateValueEntries.length > 0) {
const validation = validateTemplateValues();
if (!validation.isValid && validation.missingField) {
form.setError("settingsConfig", {
@@ -293,7 +293,7 @@ export function ProviderForm({
let settingsConfig: string;
// Codex: 组合 auth 和 config
if (appType === "codex") {
if (appId === "codex") {
try {
const authJson = JSON.parse(codexAuth);
const configObj = {
@@ -359,7 +359,7 @@ export function ProviderForm({
shouldShowApiKeyLink: shouldShowClaudeApiKeyLink,
websiteUrl: claudeWebsiteUrl,
} = useApiKeyLink({
appType: "claude",
appId: "claude",
category,
selectedPresetId,
presetEntries,
@@ -371,7 +371,7 @@ export function ProviderForm({
shouldShowApiKeyLink: shouldShowCodexApiKeyLink,
websiteUrl: codexWebsiteUrl,
} = useApiKeyLink({
appType: "codex",
appId: "codex",
category,
selectedPresetId,
presetEntries,
@@ -380,7 +380,7 @@ export function ProviderForm({
// 使用自定义端点 hook
const customEndpointsMap = useCustomEndpoints({
appType,
appId,
selectedPresetId,
presetEntries,
draftCustomEndpoints,
@@ -390,7 +390,7 @@ export function ProviderForm({
// 使用端点测速候选 hook
const speedTestEndpoints = useSpeedTestEndpoints({
appType,
appId,
selectedPresetId,
presetEntries,
baseUrl,
@@ -405,7 +405,7 @@ export function ProviderForm({
form.reset(defaultValues);
// Codex 自定义模式:重置为空配置
if (appType === "codex") {
if (appId === "codex") {
resetCodexConfig({}, "");
}
return;
@@ -421,7 +421,7 @@ export function ProviderForm({
category: entry.preset.category,
});
if (appType === "codex") {
if (appId === "codex") {
const preset = entry.preset as CodexProviderPreset;
const auth = preset.auth ?? {};
const config = preset.config ?? "";
@@ -474,7 +474,7 @@ export function ProviderForm({
<BasicFormFields form={form} />
{/* Claude 专属字段 */}
{appType === "claude" && (
{appId === "claude" && (
<ClaudeFormFields
shouldShowApiKey={shouldShowApiKey(
form.watch("settingsConfig"),
@@ -510,7 +510,7 @@ export function ProviderForm({
)}
{/* Codex 专属字段 */}
{appType === "codex" && (
{appId === "codex" && (
<CodexFormFields
codexApiKey={codexApiKey}
onApiKeyChange={handleCodexApiKeyChange}
@@ -528,7 +528,7 @@ export function ProviderForm({
)}
{/* 配置编辑器Claude 使用通用配置编辑器Codex 使用专用编辑器 */}
{appType === "codex" ? (
{appId === "codex" ? (
<CodexConfigEditor
authValue={codexAuth}
configValue={codexConfig}

View File

@@ -1,5 +1,5 @@
import { useMemo } from "react";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import type { ProviderCategory } from "@/types";
import type { ProviderPreset } from "@/config/providerPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
@@ -10,7 +10,7 @@ type PresetEntry = {
};
interface UseApiKeyLinkProps {
appType: AppType;
appId: AppId;
category?: ProviderCategory;
selectedPresetId: string | null;
presetEntries: PresetEntry[];
@@ -21,7 +21,7 @@ interface UseApiKeyLinkProps {
* 管理 API Key 获取链接的显示和 URL
*/
export function useApiKeyLink({
appType,
appId,
category,
selectedPresetId,
presetEntries,
@@ -53,12 +53,7 @@ export function useApiKeyLink({
}, [selectedPresetId, presetEntries, formWebsiteUrl]);
return {
shouldShowApiKeyLink:
appType === "claude"
? shouldShowApiKeyLink
: appType === "codex"
? shouldShowApiKeyLink
: false,
shouldShowApiKeyLink: appId === "claude" ? shouldShowApiKeyLink : appId === "codex" ? shouldShowApiKeyLink : false,
websiteUrl: getWebsiteUrl,
};
}

View File

@@ -1,5 +1,5 @@
import { useMemo } from "react";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import type { CustomEndpoint } from "@/types";
import type { ProviderPreset } from "@/config/providerPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
@@ -10,7 +10,7 @@ type PresetEntry = {
};
interface UseCustomEndpointsProps {
appType: AppType;
appId: AppId;
selectedPresetId: string | null;
presetEntries: PresetEntry[];
draftCustomEndpoints: string[];
@@ -27,7 +27,7 @@ interface UseCustomEndpointsProps {
* 3. 当前选中的 Base URL
*/
export function useCustomEndpoints({
appType,
appId,
selectedPresetId,
presetEntries,
draftCustomEndpoints,
@@ -58,7 +58,7 @@ export function useCustomEndpoints({
}
// 3. 当前 Base URL
if (appType === "codex") {
if (appId === "codex") {
push(codexBaseUrl);
} else {
push(baseUrl);
@@ -80,7 +80,7 @@ export function useCustomEndpoints({
return customMap;
}, [
appType,
appId,
selectedPresetId,
presetEntries,
draftCustomEndpoints,

View File

@@ -1,11 +1,11 @@
import { useState, useEffect } from "react";
import type { ProviderCategory } from "@/types";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import { providerPresets } from "@/config/providerPresets";
import { codexProviderPresets } from "@/config/codexProviderPresets";
interface UseProviderCategoryProps {
appType: AppType;
appId: AppId;
selectedPresetId: string | null;
isEditMode: boolean;
initialCategory?: ProviderCategory;
@@ -16,7 +16,7 @@ interface UseProviderCategoryProps {
* 根据选择的预设自动更新类别
*/
export function useProviderCategory({
appType,
appId,
selectedPresetId,
isEditMode,
initialCategory,
@@ -47,14 +47,14 @@ export function useProviderCategory({
const [, type, indexStr] = match;
const index = parseInt(indexStr, 10);
if (type === "codex" && appType === "codex") {
if (type === "codex" && appId === "codex") {
const preset = codexProviderPresets[index];
if (preset) {
setCategory(
preset.category || (preset.isOfficial ? "official" : undefined),
);
}
} else if (type === "claude" && appType === "claude") {
} else if (type === "claude" && appId === "claude") {
const preset = providerPresets[index];
if (preset) {
setCategory(
@@ -62,7 +62,7 @@ export function useProviderCategory({
);
}
}
}, [appType, selectedPresetId, isEditMode, initialCategory]);
}, [appId, selectedPresetId, isEditMode, initialCategory]);
return { category, setCategory };
}

View File

@@ -1,5 +1,5 @@
import { useMemo } from "react";
import type { AppType } from "@/lib/api";
import type { AppId } from "@/lib/api";
import type { ProviderPreset } from "@/config/providerPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
import type { ProviderMeta, EndpointCandidate } from "@/types";
@@ -10,7 +10,7 @@ type PresetEntry = {
};
interface UseSpeedTestEndpointsProps {
appType: AppType;
appId: AppId;
selectedPresetId: string | null;
presetEntries: PresetEntry[];
baseUrl: string;
@@ -31,7 +31,7 @@ interface UseSpeedTestEndpointsProps {
* 4. 预设中的 endpointCandidates
*/
export function useSpeedTestEndpoints({
appType,
appId,
selectedPresetId,
presetEntries,
baseUrl,
@@ -39,7 +39,7 @@ export function useSpeedTestEndpoints({
initialData,
}: UseSpeedTestEndpointsProps) {
const claudeEndpoints = useMemo<EndpointCandidate[]>(() => {
if (appType !== "claude") return [];
if (appId !== "claude") return [];
const map = new Map<string, EndpointCandidate>();
// 所有端点都标记为 isCustom: true给用户完全的管理自由
@@ -94,10 +94,10 @@ export function useSpeedTestEndpoints({
}
return Array.from(map.values());
}, [appType, baseUrl, initialData, selectedPresetId, presetEntries]);
}, [appId, baseUrl, initialData, selectedPresetId, presetEntries]);
const codexEndpoints = useMemo<EndpointCandidate[]>(() => {
if (appType !== "codex") return [];
if (appId !== "codex") return [];
const map = new Map<string, EndpointCandidate>();
// 所有端点都标记为 isCustom: true给用户完全的管理自由
@@ -155,7 +155,7 @@ export function useSpeedTestEndpoints({
}
return Array.from(map.values());
}, [appType, codexBaseUrl, initialData, selectedPresetId, presetEntries]);
}, [appId, codexBaseUrl, initialData, selectedPresetId, presetEntries]);
return appType === "codex" ? codexEndpoints : claudeEndpoints;
return appId === "codex" ? codexEndpoints : claudeEndpoints;
}