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:
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [3.5.0] - 2025-01-15
|
## [3.5.0] - 2025-01-15
|
||||||
|
|
||||||
|
### ⚠ Breaking Changes
|
||||||
|
|
||||||
|
- Tauri 命令仅接受参数 `app`(取值:`claude`/`codex`);移除对 `app_type`/`appType` 的兼容。
|
||||||
|
- 前端类型命名统一为 `AppId`(移除 `AppType` 导出),变量命名统一为 `appId`。
|
||||||
|
|
||||||
### ✨ New Features
|
### ✨ New Features
|
||||||
|
|
||||||
- **MCP (Model Context Protocol) Management** - Complete MCP server configuration management system
|
- **MCP (Model Context Protocol) Management** - Complete MCP server configuration management system
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ if (typeof window !== "undefined") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 问题 2: 无缓存机制
|
// 问题 2: 无缓存机制
|
||||||
getProviders: async (app?: AppType) => {
|
getProviders: async (app?: AppId) => {
|
||||||
try {
|
try {
|
||||||
return await invoke("get_providers", { app });
|
return await invoke("get_providers", { app });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -794,7 +794,7 @@ export function ProviderList({ providers, currentProviderId, appType }) {
|
|||||||
```typescript
|
```typescript
|
||||||
export function useDragSort(
|
export function useDragSort(
|
||||||
providers: Record<string, Provider>,
|
providers: Record<string, Provider>,
|
||||||
appType: AppType
|
appType: AppId
|
||||||
) {
|
) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -1133,36 +1133,35 @@ export const queryClient = new QueryClient({
|
|||||||
```typescript
|
```typescript
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { Provider } from "@/types";
|
import { Provider } from "@/types";
|
||||||
|
import type { AppId } from "@/lib/api";
|
||||||
export type AppType = "claude" | "codex";
|
|
||||||
|
|
||||||
export const providersApi = {
|
export const providersApi = {
|
||||||
getAll: async (appType: AppType): Promise<Record<string, Provider>> => {
|
getAll: async (appId: AppId): Promise<Record<string, Provider>> => {
|
||||||
return await invoke("get_providers", { app: appType });
|
return await invoke("get_providers", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
getCurrent: async (appType: AppType): Promise<string> => {
|
getCurrent: async (appId: AppId): Promise<string> => {
|
||||||
return await invoke("get_current_provider", { app: appType });
|
return await invoke("get_current_provider", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
add: async (provider: Provider, appType: AppType): Promise<boolean> => {
|
add: async (provider: Provider, appId: AppId): Promise<boolean> => {
|
||||||
return await invoke("add_provider", { provider, app: appType });
|
return await invoke("add_provider", { provider, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
update: async (provider: Provider, appType: AppType): Promise<boolean> => {
|
update: async (provider: Provider, appId: AppId): Promise<boolean> => {
|
||||||
return await invoke("update_provider", { provider, app: appType });
|
return await invoke("update_provider", { provider, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
delete: async (id: string, appType: AppType): Promise<boolean> => {
|
delete: async (id: string, appId: AppId): Promise<boolean> => {
|
||||||
return await invoke("delete_provider", { id, app: appType });
|
return await invoke("delete_provider", { id, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
switch: async (id: string, appType: AppType): Promise<boolean> => {
|
switch: async (id: string, appId: AppId): Promise<boolean> => {
|
||||||
return await invoke("switch_provider", { id, app: appType });
|
return await invoke("switch_provider", { id, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
importDefault: async (appType: AppType): Promise<boolean> => {
|
importDefault: async (appId: AppId): Promise<boolean> => {
|
||||||
return await invoke("import_default_config", { app: appType });
|
return await invoke("import_default_config", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
updateTrayMenu: async (): Promise<boolean> => {
|
updateTrayMenu: async (): Promise<boolean> => {
|
||||||
@@ -1171,9 +1170,9 @@ export const providersApi = {
|
|||||||
|
|
||||||
updateSortOrder: async (
|
updateSortOrder: async (
|
||||||
updates: Array<{ id: string; sortIndex: number }>,
|
updates: Array<{ id: string; sortIndex: number }>,
|
||||||
appType: AppType
|
appId: AppId
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
return await invoke("update_providers_sort_order", { updates, app: appType });
|
return await invoke("update_providers_sort_order", { updates, app: appId });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
@@ -1190,7 +1189,7 @@ export const providersApi = {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { providersApi, AppType } from "@/lib/api";
|
import { providersApi, type AppId } from "@/lib/api";
|
||||||
import { Provider } from "@/types";
|
import { Provider } from "@/types";
|
||||||
|
|
||||||
// 排序辅助函数
|
// 排序辅助函数
|
||||||
@@ -1213,7 +1212,7 @@ const sortProviders = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useProvidersQuery = (appType: AppType) => {
|
export const useProvidersQuery = (appType: AppId) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["providers", appType],
|
queryKey: ["providers", appType],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -1255,12 +1254,12 @@ export const useProvidersQuery = (appType: AppType) => {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { providersApi, AppType } from "@/lib/api";
|
import { providersApi, type AppId } from "@/lib/api";
|
||||||
import { Provider } from "@/types";
|
import { Provider } from "@/types";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export const useAddProviderMutation = (appType: AppType) => {
|
export const useAddProviderMutation = (appType: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -1285,7 +1284,7 @@ export const useAddProviderMutation = (appType: AppType) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSwitchProviderMutation = (appType: AppType) => {
|
export const useSwitchProviderMutation = (appType: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 定义查询 Hook
|
// 定义查询 Hook
|
||||||
export const useProvidersQuery = (appType: AppType) => {
|
export const useProvidersQuery = (appId: AppId) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['providers', appType],
|
queryKey: ['providers', appId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const data = await providersApi.getAll(appType)
|
const data = await providersApi.getAll(appId)
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -44,16 +44,16 @@ function MyComponent() {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 定义 Mutation Hook
|
// 定义 Mutation Hook
|
||||||
export const useAddProviderMutation = (appType: AppType) => {
|
export const useAddProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (provider: Provider) => {
|
mutationFn: async (provider: Provider) => {
|
||||||
return await providersApi.add(provider, appType)
|
return await providersApi.add(provider, appId)
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
// 重新获取数据
|
// 重新获取数据
|
||||||
queryClient.invalidateQueries({ queryKey: ['providers', appType] })
|
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||||
toast.success('添加成功')
|
toast.success('添加成功')
|
||||||
},
|
},
|
||||||
onError: (error: Error) => {
|
onError: (error: Error) => {
|
||||||
@@ -64,7 +64,7 @@ export const useAddProviderMutation = (appType: AppType) => {
|
|||||||
|
|
||||||
// 在组件中使用
|
// 在组件中使用
|
||||||
function AddProviderDialog() {
|
function AddProviderDialog() {
|
||||||
const mutation = useAddProviderMutation('claude')
|
const mutation = useAddProviderMutation('claude')
|
||||||
|
|
||||||
const handleSubmit = (data: Provider) => {
|
const handleSubmit = (data: Provider) => {
|
||||||
mutation.mutate(data)
|
mutation.mutate(data)
|
||||||
@@ -84,23 +84,23 @@ function AddProviderDialog() {
|
|||||||
### 乐观更新
|
### 乐观更新
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export const useSwitchProviderMutation = (appType: AppType) => {
|
export const useSwitchProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (providerId: string) => {
|
mutationFn: async (providerId: string) => {
|
||||||
return await providersApi.switch(providerId, appType)
|
return await providersApi.switch(providerId, appId)
|
||||||
},
|
},
|
||||||
// 乐观更新: 在请求发送前立即更新 UI
|
// 乐观更新: 在请求发送前立即更新 UI
|
||||||
onMutate: async (providerId) => {
|
onMutate: async (providerId) => {
|
||||||
// 取消正在进行的查询
|
// 取消正在进行的查询
|
||||||
await queryClient.cancelQueries({ queryKey: ['providers', appType] })
|
await queryClient.cancelQueries({ queryKey: ['providers', appId] })
|
||||||
|
|
||||||
// 保存当前数据(以便回滚)
|
// 保存当前数据(以便回滚)
|
||||||
const previousData = queryClient.getQueryData(['providers', appType])
|
const previousData = queryClient.getQueryData(['providers', appId])
|
||||||
|
|
||||||
// 乐观更新
|
// 乐观更新
|
||||||
queryClient.setQueryData(['providers', appType], (old: any) => ({
|
queryClient.setQueryData(['providers', appId], (old: any) => ({
|
||||||
...old,
|
...old,
|
||||||
currentProviderId: providerId,
|
currentProviderId: providerId,
|
||||||
}))
|
}))
|
||||||
@@ -109,12 +109,12 @@ export const useSwitchProviderMutation = (appType: AppType) => {
|
|||||||
},
|
},
|
||||||
// 如果失败,回滚
|
// 如果失败,回滚
|
||||||
onError: (err, providerId, context) => {
|
onError: (err, providerId, context) => {
|
||||||
queryClient.setQueryData(['providers', appType], context?.previousData)
|
queryClient.setQueryData(['providers', appId], context?.previousData)
|
||||||
toast.error('切换失败')
|
toast.error('切换失败')
|
||||||
},
|
},
|
||||||
// 无论成功失败,都重新获取数据
|
// 无论成功失败,都重新获取数据
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ['providers', appType] })
|
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ export const useSwitchProviderMutation = (appType: AppType) => {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 第二个查询依赖第一个查询的结果
|
// 第二个查询依赖第一个查询的结果
|
||||||
const { data: providers } = useProvidersQuery(appType)
|
const { data: providers } = useProvidersQuery(appId)
|
||||||
const currentProviderId = providers?.currentProviderId
|
const currentProviderId = providers?.currentProviderId
|
||||||
|
|
||||||
const { data: currentProvider } = useQuery({
|
const { data: currentProvider } = useQuery({
|
||||||
@@ -432,13 +432,13 @@ useEffect(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
load()
|
load()
|
||||||
}, [appType])
|
}, [appId])
|
||||||
```
|
```
|
||||||
|
|
||||||
**新代码** (React Query):
|
**新代码** (React Query):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const { data, isLoading, error } = useProvidersQuery(appType)
|
const { data, isLoading, error } = useProvidersQuery(appId)
|
||||||
const providers = data?.providers || {}
|
const providers = data?.providers || {}
|
||||||
const currentProviderId = data?.currentProviderId || ''
|
const currentProviderId = data?.currentProviderId || ''
|
||||||
```
|
```
|
||||||
@@ -688,7 +688,7 @@ const handleAdd = async (provider: Provider) => {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 在组件中
|
// 在组件中
|
||||||
const addMutation = useAddProviderMutation(appType)
|
const addMutation = useAddProviderMutation(appId)
|
||||||
|
|
||||||
const handleAdd = (provider: Provider) => {
|
const handleAdd = (provider: Provider) => {
|
||||||
addMutation.mutate(provider)
|
addMutation.mutate(provider)
|
||||||
@@ -709,7 +709,7 @@ const handleAdd = (provider: Provider) => {
|
|||||||
### Q: 如何在 mutation 成功后关闭对话框?
|
### Q: 如何在 mutation 成功后关闭对话框?
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const mutation = useAddProviderMutation(appType)
|
const mutation = useAddProviderMutation(appId)
|
||||||
|
|
||||||
const handleSubmit = (data: Provider) => {
|
const handleSubmit = (data: Provider) => {
|
||||||
mutation.mutate(data, {
|
mutation.mutate(data, {
|
||||||
@@ -741,13 +741,13 @@ const schema = z.object({
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
// 方式1: 使缓存失效,触发重新获取
|
// 方式1: 使缓存失效,触发重新获取
|
||||||
queryClient.invalidateQueries({ queryKey: ['providers', appType] })
|
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||||
|
|
||||||
// 方式2: 直接刷新
|
// 方式2: 直接刷新
|
||||||
queryClient.refetchQueries({ queryKey: ['providers', appType] })
|
queryClient.refetchQueries({ queryKey: ['providers', appId] })
|
||||||
|
|
||||||
// 方式3: 更新缓存数据
|
// 方式3: 更新缓存数据
|
||||||
queryClient.setQueryData(['providers', appType], newData)
|
queryClient.setQueryData(['providers', appId], newData)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Q: 如何在组件外部使用 toast?
|
### Q: 如何在组件外部使用 toast?
|
||||||
@@ -811,7 +811,7 @@ const sortedProviders = useMemo(
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['providers', appType],
|
queryKey: ['providers', appId],
|
||||||
queryFn: fetchProviders,
|
queryFn: fetchProviders,
|
||||||
staleTime: 1000 * 60 * 5, // 5分钟内不重新获取
|
staleTime: 1000 * 60 * 5, // 5分钟内不重新获取
|
||||||
gcTime: 1000 * 60 * 10, // 10分钟后清除缓存
|
gcTime: 1000 * 60 * 10, // 10分钟后清除缓存
|
||||||
|
|||||||
14
src/App.tsx
14
src/App.tsx
@@ -7,7 +7,7 @@ import { useProvidersQuery } from "@/lib/query";
|
|||||||
import {
|
import {
|
||||||
providersApi,
|
providersApi,
|
||||||
settingsApi,
|
settingsApi,
|
||||||
type AppType,
|
type AppId,
|
||||||
type ProviderSwitchEvent,
|
type ProviderSwitchEvent,
|
||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
import { useProviderActions } from "@/hooks/useProviderActions";
|
import { useProviderActions } from "@/hooks/useProviderActions";
|
||||||
@@ -26,7 +26,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
function App() {
|
function App() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [activeApp, setActiveApp] = useState<AppType>("claude");
|
const [activeApp, setActiveApp] = useState<AppId>("claude");
|
||||||
const [isEditMode, setIsEditMode] = useState(false);
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||||
const [isAddOpen, setIsAddOpen] = useState(false);
|
const [isAddOpen, setIsAddOpen] = useState(false);
|
||||||
@@ -222,7 +222,7 @@ function App() {
|
|||||||
<ProviderList
|
<ProviderList
|
||||||
providers={providers}
|
providers={providers}
|
||||||
currentProviderId={currentProviderId}
|
currentProviderId={currentProviderId}
|
||||||
appType={activeApp}
|
appId={activeApp}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isEditMode={isEditMode}
|
isEditMode={isEditMode}
|
||||||
onSwitch={switchProvider}
|
onSwitch={switchProvider}
|
||||||
@@ -239,7 +239,7 @@ function App() {
|
|||||||
<AddProviderDialog
|
<AddProviderDialog
|
||||||
open={isAddOpen}
|
open={isAddOpen}
|
||||||
onOpenChange={setIsAddOpen}
|
onOpenChange={setIsAddOpen}
|
||||||
appType={activeApp}
|
appId={activeApp}
|
||||||
onSubmit={addProvider}
|
onSubmit={addProvider}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -252,13 +252,13 @@ function App() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSubmit={handleEditProvider}
|
onSubmit={handleEditProvider}
|
||||||
appType={activeApp}
|
appId={activeApp}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{usageProvider && (
|
{usageProvider && (
|
||||||
<UsageScriptModal
|
<UsageScriptModal
|
||||||
provider={usageProvider}
|
provider={usageProvider}
|
||||||
appType={activeApp}
|
appId={activeApp}
|
||||||
isOpen={Boolean(usageProvider)}
|
isOpen={Boolean(usageProvider)}
|
||||||
onClose={() => setUsageProvider(null)}
|
onClose={() => setUsageProvider(null)}
|
||||||
onSave={(script) => {
|
onSave={(script) => {
|
||||||
@@ -290,7 +290,7 @@ function App() {
|
|||||||
<McpPanel
|
<McpPanel
|
||||||
open={isMcpOpen}
|
open={isMcpOpen}
|
||||||
onOpenChange={setIsMcpOpen}
|
onOpenChange={setIsMcpOpen}
|
||||||
appType={activeApp}
|
appId={activeApp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import { ClaudeIcon, CodexIcon } from "./BrandIcons";
|
import { ClaudeIcon, CodexIcon } from "./BrandIcons";
|
||||||
|
|
||||||
interface AppSwitcherProps {
|
interface AppSwitcherProps {
|
||||||
activeApp: AppType;
|
activeApp: AppId;
|
||||||
onSwitch: (app: AppType) => void;
|
onSwitch: (app: AppId) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppSwitcher({ activeApp, onSwitch }: AppSwitcherProps) {
|
export function AppSwitcher({ activeApp, onSwitch }: AppSwitcherProps) {
|
||||||
const handleSwitch = (app: AppType) => {
|
const handleSwitch = (app: AppId) => {
|
||||||
if (app === activeApp) return;
|
if (app === activeApp) return;
|
||||||
onSwitch(app);
|
onSwitch(app);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { RefreshCw, AlertCircle } from "lucide-react";
|
import { RefreshCw, AlertCircle } from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { type AppType } from "@/lib/api";
|
import { type AppId } from "@/lib/api";
|
||||||
import { useUsageQuery } from "@/lib/query/queries";
|
import { useUsageQuery } from "@/lib/query/queries";
|
||||||
import { UsageData } from "../types";
|
import { UsageData } from "../types";
|
||||||
|
|
||||||
interface UsageFooterProps {
|
interface UsageFooterProps {
|
||||||
providerId: string;
|
providerId: string;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
usageEnabled: boolean; // 是否启用了用量查询
|
usageEnabled: boolean; // 是否启用了用量查询
|
||||||
}
|
}
|
||||||
|
|
||||||
const UsageFooter: React.FC<UsageFooterProps> = ({
|
const UsageFooter: React.FC<UsageFooterProps> = ({ providerId, appId, usageEnabled }) => {
|
||||||
providerId,
|
|
||||||
appType,
|
|
||||||
usageEnabled,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
data: usage,
|
data: usage,
|
||||||
isLoading: loading,
|
isLoading: loading,
|
||||||
refetch,
|
refetch,
|
||||||
} = useUsageQuery(providerId, appType, usageEnabled);
|
} = useUsageQuery(providerId, appId, usageEnabled);
|
||||||
|
|
||||||
// 只在启用用量查询且有数据时显示
|
// 只在启用用量查询且有数据时显示
|
||||||
if (!usageEnabled || !usage) return null;
|
if (!usageEnabled || !usage) return null;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Play, Wand2 } from "lucide-react";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Provider, UsageScript } from "../types";
|
import { Provider, UsageScript } from "../types";
|
||||||
import { usageApi, type AppType } from "@/lib/api";
|
import { usageApi, type AppId } from "@/lib/api";
|
||||||
import JsonEditor from "./JsonEditor";
|
import JsonEditor from "./JsonEditor";
|
||||||
import * as prettier from "prettier/standalone";
|
import * as prettier from "prettier/standalone";
|
||||||
import * as parserBabel from "prettier/parser-babel";
|
import * as parserBabel from "prettier/parser-babel";
|
||||||
@@ -19,7 +19,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
|
|
||||||
interface UsageScriptModalProps {
|
interface UsageScriptModalProps {
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (script: UsageScript) => void;
|
onSave: (script: UsageScript) => void;
|
||||||
@@ -82,13 +82,7 @@ const PRESET_TEMPLATES: Record<string, string> = {
|
|||||||
})`,
|
})`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
const UsageScriptModal: React.FC<UsageScriptModalProps> = ({ provider, appId, isOpen, onClose, onSave }) => {
|
||||||
provider,
|
|
||||||
appType,
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [script, setScript] = useState<UsageScript>(() => {
|
const [script, setScript] = useState<UsageScript>(() => {
|
||||||
return (
|
return (
|
||||||
@@ -127,7 +121,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
|||||||
const handleTest = async () => {
|
const handleTest = async () => {
|
||||||
setTesting(true);
|
setTesting(true);
|
||||||
try {
|
try {
|
||||||
const result = await usageApi.query(provider.id, appType);
|
const result = await usageApi.query(provider.id, appId);
|
||||||
if (result.success && result.data && result.data.length > 0) {
|
if (result.success && result.data && result.data.length > 0) {
|
||||||
// 显示所有套餐数据
|
// 显示所有套餐数据
|
||||||
const summary = result.data
|
const summary = result.data
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { mcpApi, type AppType } from "@/lib/api";
|
import { mcpApi, type AppId } from "@/lib/api";
|
||||||
import { McpServer, McpServerSpec } from "@/types";
|
import { McpServer, McpServerSpec } from "@/types";
|
||||||
import { mcpPresets, getMcpPresetWithDescription } from "@/config/mcpPresets";
|
import { mcpPresets, getMcpPresetWithDescription } from "@/config/mcpPresets";
|
||||||
import McpWizardModal from "./McpWizardModal";
|
import McpWizardModal from "./McpWizardModal";
|
||||||
@@ -35,7 +35,7 @@ import {
|
|||||||
import { useMcpValidation } from "./useMcpValidation";
|
import { useMcpValidation } from "./useMcpValidation";
|
||||||
|
|
||||||
interface McpFormModalProps {
|
interface McpFormModalProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
editingId?: string;
|
editingId?: string;
|
||||||
initialData?: McpServer;
|
initialData?: McpServer;
|
||||||
onSave: (
|
onSave: (
|
||||||
@@ -53,7 +53,7 @@ interface McpFormModalProps {
|
|||||||
* Codex: 使用 TOML 格式
|
* Codex: 使用 TOML 格式
|
||||||
*/
|
*/
|
||||||
const McpFormModal: React.FC<McpFormModalProps> = ({
|
const McpFormModal: React.FC<McpFormModalProps> = ({
|
||||||
appType,
|
appId,
|
||||||
editingId,
|
editingId,
|
||||||
initialData,
|
initialData,
|
||||||
onSave,
|
onSave,
|
||||||
@@ -91,11 +91,11 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
isEditing ? hasAdditionalInfo : false,
|
isEditing ? hasAdditionalInfo : false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 根据 appType 决定初始格式
|
// 根据 appId 决定初始格式
|
||||||
const [formConfig, setFormConfig] = useState(() => {
|
const [formConfig, setFormConfig] = useState(() => {
|
||||||
const spec = initialData?.server;
|
const spec = initialData?.server;
|
||||||
if (!spec) return "";
|
if (!spec) return "";
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
return mcpServerToToml(spec);
|
return mcpServerToToml(spec);
|
||||||
}
|
}
|
||||||
return JSON.stringify(spec, null, 2);
|
return JSON.stringify(spec, null, 2);
|
||||||
@@ -109,11 +109,10 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
const [otherSideHasConflict, setOtherSideHasConflict] = useState(false);
|
const [otherSideHasConflict, setOtherSideHasConflict] = useState(false);
|
||||||
|
|
||||||
// 判断是否使用 TOML 格式
|
// 判断是否使用 TOML 格式
|
||||||
const useToml = appType === "codex";
|
const useToml = appId === "codex";
|
||||||
const syncTargetLabel =
|
const syncTargetLabel = appId === "claude" ? t("apps.codex") : t("apps.claude");
|
||||||
appType === "claude" ? t("apps.codex") : t("apps.claude");
|
const otherAppType: AppId = appId === "claude" ? "codex" : "claude";
|
||||||
const otherAppType: AppType = appType === "claude" ? "codex" : "claude";
|
const syncCheckboxId = useMemo(() => `sync-other-side-${appId}`, [appId]);
|
||||||
const syncCheckboxId = useMemo(() => `sync-other-side-${appType}`, [appType]);
|
|
||||||
|
|
||||||
// 检测另一侧是否有同名 MCP
|
// 检测另一侧是否有同名 MCP
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -418,7 +417,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getFormTitle = () => {
|
const getFormTitle = () => {
|
||||||
if (appType === "claude") {
|
if (appId === "claude") {
|
||||||
return isEditing ? t("mcp.editClaudeServer") : t("mcp.addClaudeServer");
|
return isEditing ? t("mcp.editClaudeServer") : t("mcp.addClaudeServer");
|
||||||
} else {
|
} else {
|
||||||
return isEditing ? t("mcp.editCodexServer") : t("mcp.addCodexServer");
|
return isEditing ? t("mcp.editCodexServer") : t("mcp.addCodexServer");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { type AppType } from "@/lib/api";
|
import { type AppId } from "@/lib/api";
|
||||||
import { McpServer } from "@/types";
|
import { McpServer } from "@/types";
|
||||||
import { useMcpActions } from "@/hooks/useMcpActions";
|
import { useMcpActions } from "@/hooks/useMcpActions";
|
||||||
import McpListItem from "./McpListItem";
|
import McpListItem from "./McpListItem";
|
||||||
@@ -19,14 +19,14 @@ import { ConfirmDialog } from "../ConfirmDialog";
|
|||||||
interface McpPanelProps {
|
interface McpPanelProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP 管理面板
|
* MCP 管理面板
|
||||||
* 采用与主界面一致的设计风格,右上角添加按钮,每个 MCP 占一行
|
* 采用与主界面一致的设计风格,右上角添加按钮,每个 MCP 占一行
|
||||||
*/
|
*/
|
||||||
const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appType }) => {
|
const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appId }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||||
const [editingId, setEditingId] = useState<string | null>(null);
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||||||
@@ -38,17 +38,16 @@ const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appType }) => {
|
|||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
// Use MCP actions hook
|
// Use MCP actions hook
|
||||||
const { servers, loading, reload, toggleEnabled, saveServer, deleteServer } =
|
const { servers, loading, reload, toggleEnabled, saveServer, deleteServer } = useMcpActions(appId);
|
||||||
useMcpActions(appType);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const setup = async () => {
|
const setup = async () => {
|
||||||
try {
|
try {
|
||||||
// Initialize: only import existing MCPs from corresponding client
|
// Initialize: only import existing MCPs from corresponding client
|
||||||
if (appType === "claude") {
|
if (appId === "claude") {
|
||||||
const mcpApi = await import("@/lib/api").then((m) => m.mcpApi);
|
const mcpApi = await import("@/lib/api").then((m) => m.mcpApi);
|
||||||
await mcpApi.importFromClaude();
|
await mcpApi.importFromClaude();
|
||||||
} else if (appType === "codex") {
|
} else if (appId === "codex") {
|
||||||
const mcpApi = await import("@/lib/api").then((m) => m.mcpApi);
|
const mcpApi = await import("@/lib/api").then((m) => m.mcpApi);
|
||||||
await mcpApi.importFromCodex();
|
await mcpApi.importFromCodex();
|
||||||
}
|
}
|
||||||
@@ -59,8 +58,8 @@ const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appType }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
setup();
|
setup();
|
||||||
// Re-initialize when appType changes
|
// Re-initialize when appId changes
|
||||||
}, [appType, reload]);
|
}, [appId, reload]);
|
||||||
|
|
||||||
const handleEdit = (id: string) => {
|
const handleEdit = (id: string) => {
|
||||||
setEditingId(id);
|
setEditingId(id);
|
||||||
@@ -113,8 +112,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appType }) => {
|
|||||||
[serverEntries],
|
[serverEntries],
|
||||||
);
|
);
|
||||||
|
|
||||||
const panelTitle =
|
const panelTitle = appId === "claude" ? t("mcp.claudeTitle") : t("mcp.codexTitle");
|
||||||
appType === "claude" ? t("mcp.claudeTitle") : t("mcp.codexTitle");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -203,7 +201,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ open, onOpenChange, appType }) => {
|
|||||||
{/* Form Modal */}
|
{/* Form Modal */}
|
||||||
{isFormOpen && (
|
{isFormOpen && (
|
||||||
<McpFormModal
|
<McpFormModal
|
||||||
appType={appType}
|
appId={appId}
|
||||||
editingId={editingId || undefined}
|
editingId={editingId || undefined}
|
||||||
initialData={editingId ? servers[editingId] : undefined}
|
initialData={editingId ? servers[editingId] : undefined}
|
||||||
existingIds={Object.keys(servers)}
|
existingIds={Object.keys(servers)}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import type { Provider, CustomEndpoint } from "@/types";
|
import type { Provider, CustomEndpoint } from "@/types";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import {
|
import {
|
||||||
ProviderForm,
|
ProviderForm,
|
||||||
type ProviderFormValues,
|
type ProviderFormValues,
|
||||||
@@ -22,14 +22,14 @@ import { codexProviderPresets } from "@/config/codexProviderPresets";
|
|||||||
interface AddProviderDialogProps {
|
interface AddProviderDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
onSubmit: (provider: Omit<Provider, "id">) => Promise<void> | void;
|
onSubmit: (provider: Omit<Provider, "id">) => Promise<void> | void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddProviderDialog({
|
export function AddProviderDialog({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
appType,
|
appId,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: AddProviderDialogProps) {
|
}: AddProviderDialogProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -68,7 +68,7 @@ export function AddProviderDialog({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (values.presetId) {
|
if (values.presetId) {
|
||||||
if (appType === "claude") {
|
if (appId === "claude") {
|
||||||
const presets = providerPresets;
|
const presets = providerPresets;
|
||||||
const presetIndex = parseInt(
|
const presetIndex = parseInt(
|
||||||
values.presetId.replace("claude-", ""),
|
values.presetId.replace("claude-", ""),
|
||||||
@@ -83,7 +83,7 @@ export function AddProviderDialog({
|
|||||||
preset.endpointCandidates.forEach(addUrl);
|
preset.endpointCandidates.forEach(addUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (appType === "codex") {
|
} else if (appId === "codex") {
|
||||||
const presets = codexProviderPresets;
|
const presets = codexProviderPresets;
|
||||||
const presetIndex = parseInt(
|
const presetIndex = parseInt(
|
||||||
values.presetId.replace("codex-", ""),
|
values.presetId.replace("codex-", ""),
|
||||||
@@ -101,12 +101,12 @@ export function AddProviderDialog({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appType === "claude") {
|
if (appId === "claude") {
|
||||||
const env = parsedConfig.env as Record<string, any> | undefined;
|
const env = parsedConfig.env as Record<string, any> | undefined;
|
||||||
if (env?.ANTHROPIC_BASE_URL) {
|
if (env?.ANTHROPIC_BASE_URL) {
|
||||||
addUrl(env.ANTHROPIC_BASE_URL);
|
addUrl(env.ANTHROPIC_BASE_URL);
|
||||||
}
|
}
|
||||||
} else if (appType === "codex") {
|
} else if (appId === "codex") {
|
||||||
const config = parsedConfig.config as string | undefined;
|
const config = parsedConfig.config as string | undefined;
|
||||||
if (config) {
|
if (config) {
|
||||||
const baseUrlMatch =
|
const baseUrlMatch =
|
||||||
@@ -139,11 +139,11 @@ export function AddProviderDialog({
|
|||||||
await onSubmit(providerData);
|
await onSubmit(providerData);
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
},
|
},
|
||||||
[appType, onSubmit, onOpenChange],
|
[appId, onSubmit, onOpenChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const submitLabel =
|
const submitLabel =
|
||||||
appType === "claude"
|
appId === "claude"
|
||||||
? t("provider.addClaudeProvider")
|
? t("provider.addClaudeProvider")
|
||||||
: t("provider.addCodexProvider");
|
: t("provider.addCodexProvider");
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ export function AddProviderDialog({
|
|||||||
|
|
||||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||||
<ProviderForm
|
<ProviderForm
|
||||||
appType={appType}
|
appId={appId}
|
||||||
submitLabel={t("common.add")}
|
submitLabel={t("common.add")}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onCancel={() => onOpenChange(false)}
|
onCancel={() => onOpenChange(false)}
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ import {
|
|||||||
ProviderForm,
|
ProviderForm,
|
||||||
type ProviderFormValues,
|
type ProviderFormValues,
|
||||||
} from "@/components/providers/forms/ProviderForm";
|
} from "@/components/providers/forms/ProviderForm";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
|
|
||||||
interface EditProviderDialogProps {
|
interface EditProviderDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
provider: Provider | null;
|
provider: Provider | null;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
onSubmit: (provider: Provider) => Promise<void> | void;
|
onSubmit: (provider: Provider) => Promise<void> | void;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EditProviderDialog({
|
export function EditProviderDialog({
|
||||||
@@ -30,7 +30,7 @@ export function EditProviderDialog({
|
|||||||
provider,
|
provider,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
appType,
|
appId,
|
||||||
}: EditProviderDialogProps) {
|
}: EditProviderDialogProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export function EditProviderDialog({
|
|||||||
|
|
||||||
<div className="flex-1 overflow-y-auto px-6 py-4">
|
<div className="flex-1 overflow-y-auto px-6 py-4">
|
||||||
<ProviderForm
|
<ProviderForm
|
||||||
appType={appType}
|
appId={appId}
|
||||||
submitLabel={t("common.save")}
|
submitLabel={t("common.save")}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onCancel={() => onOpenChange(false)}
|
onCancel={() => onOpenChange(false)}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
DraggableSyntheticListeners,
|
DraggableSyntheticListeners,
|
||||||
} from "@dnd-kit/core";
|
} from "@dnd-kit/core";
|
||||||
import type { Provider } from "@/types";
|
import type { Provider } from "@/types";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ProviderActions } from "@/components/providers/ProviderActions";
|
import { ProviderActions } from "@/components/providers/ProviderActions";
|
||||||
@@ -21,7 +21,7 @@ interface DragHandleProps {
|
|||||||
interface ProviderCardProps {
|
interface ProviderCardProps {
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
isCurrent: boolean;
|
isCurrent: boolean;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
isEditMode?: boolean;
|
isEditMode?: boolean;
|
||||||
onSwitch: (provider: Provider) => void;
|
onSwitch: (provider: Provider) => void;
|
||||||
onEdit: (provider: Provider) => void;
|
onEdit: (provider: Provider) => void;
|
||||||
@@ -61,7 +61,7 @@ const extractApiUrl = (provider: Provider, fallbackText: string) => {
|
|||||||
export function ProviderCard({
|
export function ProviderCard({
|
||||||
provider,
|
provider,
|
||||||
isCurrent,
|
isCurrent,
|
||||||
appType,
|
appId,
|
||||||
isEditMode = false,
|
isEditMode = false,
|
||||||
onSwitch,
|
onSwitch,
|
||||||
onEdit,
|
onEdit,
|
||||||
@@ -179,11 +179,7 @@ export function ProviderCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UsageFooter
|
<UsageFooter providerId={provider.id} appId={appId} usageEnabled={usageEnabled} />
|
||||||
providerId={provider.id}
|
|
||||||
appType={appType}
|
|
||||||
usageEnabled={usageEnabled}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
import type { Provider } from "@/types";
|
import type { Provider } from "@/types";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import { useDragSort } from "@/hooks/useDragSort";
|
import { useDragSort } from "@/hooks/useDragSort";
|
||||||
import { ProviderCard } from "@/components/providers/ProviderCard";
|
import { ProviderCard } from "@/components/providers/ProviderCard";
|
||||||
import { ProviderEmptyState } from "@/components/providers/ProviderEmptyState";
|
import { ProviderEmptyState } from "@/components/providers/ProviderEmptyState";
|
||||||
@@ -15,7 +15,7 @@ import { ProviderEmptyState } from "@/components/providers/ProviderEmptyState";
|
|||||||
interface ProviderListProps {
|
interface ProviderListProps {
|
||||||
providers: Record<string, Provider>;
|
providers: Record<string, Provider>;
|
||||||
currentProviderId: string;
|
currentProviderId: string;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
isEditMode?: boolean;
|
isEditMode?: boolean;
|
||||||
onSwitch: (provider: Provider) => void;
|
onSwitch: (provider: Provider) => void;
|
||||||
onEdit: (provider: Provider) => void;
|
onEdit: (provider: Provider) => void;
|
||||||
@@ -30,7 +30,7 @@ interface ProviderListProps {
|
|||||||
export function ProviderList({
|
export function ProviderList({
|
||||||
providers,
|
providers,
|
||||||
currentProviderId,
|
currentProviderId,
|
||||||
appType,
|
appId,
|
||||||
isEditMode = false,
|
isEditMode = false,
|
||||||
onSwitch,
|
onSwitch,
|
||||||
onEdit,
|
onEdit,
|
||||||
@@ -43,7 +43,7 @@ export function ProviderList({
|
|||||||
}: ProviderListProps) {
|
}: ProviderListProps) {
|
||||||
const { sortedProviders, sensors, handleDragEnd } = useDragSort(
|
const { sortedProviders, sensors, handleDragEnd } = useDragSort(
|
||||||
providers,
|
providers,
|
||||||
appType,
|
appId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -79,7 +79,7 @@ export function ProviderList({
|
|||||||
key={provider.id}
|
key={provider.id}
|
||||||
provider={provider}
|
provider={provider}
|
||||||
isCurrent={provider.id === currentProviderId}
|
isCurrent={provider.id === currentProviderId}
|
||||||
appType={appType}
|
appId={appId}
|
||||||
isEditMode={isEditMode}
|
isEditMode={isEditMode}
|
||||||
onSwitch={onSwitch}
|
onSwitch={onSwitch}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
@@ -98,7 +98,7 @@ export function ProviderList({
|
|||||||
interface SortableProviderCardProps {
|
interface SortableProviderCardProps {
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
isCurrent: boolean;
|
isCurrent: boolean;
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
isEditMode: boolean;
|
isEditMode: boolean;
|
||||||
onSwitch: (provider: Provider) => void;
|
onSwitch: (provider: Provider) => void;
|
||||||
onEdit: (provider: Provider) => void;
|
onEdit: (provider: Provider) => void;
|
||||||
@@ -111,7 +111,7 @@ interface SortableProviderCardProps {
|
|||||||
function SortableProviderCard({
|
function SortableProviderCard({
|
||||||
provider,
|
provider,
|
||||||
isCurrent,
|
isCurrent,
|
||||||
appType,
|
appId,
|
||||||
isEditMode,
|
isEditMode,
|
||||||
onSwitch,
|
onSwitch,
|
||||||
onEdit,
|
onEdit,
|
||||||
@@ -139,7 +139,7 @@ function SortableProviderCard({
|
|||||||
<ProviderCard
|
<ProviderCard
|
||||||
provider={provider}
|
provider={provider}
|
||||||
isCurrent={isCurrent}
|
isCurrent={isCurrent}
|
||||||
appType={appType}
|
appId={appId}
|
||||||
isEditMode={isEditMode}
|
isEditMode={isEditMode}
|
||||||
onSwitch={onSwitch}
|
onSwitch={onSwitch}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function ClaudeFormFields({
|
|||||||
{/* 端点测速弹窗 */}
|
{/* 端点测速弹窗 */}
|
||||||
{shouldShowSpeedTest && isEndpointModalOpen && (
|
{shouldShowSpeedTest && isEndpointModalOpen && (
|
||||||
<EndpointSpeedTest
|
<EndpointSpeedTest
|
||||||
appType="claude"
|
appId="claude"
|
||||||
value={baseUrl}
|
value={baseUrl}
|
||||||
onChange={onBaseUrlChange}
|
onChange={onBaseUrlChange}
|
||||||
initialEndpoints={speedTestEndpoints}
|
initialEndpoints={speedTestEndpoints}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export function CodexFormFields({
|
|||||||
{/* 端点测速弹窗 - Codex */}
|
{/* 端点测速弹窗 - Codex */}
|
||||||
{shouldShowSpeedTest && isEndpointModalOpen && (
|
{shouldShowSpeedTest && isEndpointModalOpen && (
|
||||||
<EndpointSpeedTest
|
<EndpointSpeedTest
|
||||||
appType="codex"
|
appId="codex"
|
||||||
value={codexBaseUrl}
|
value={codexBaseUrl}
|
||||||
onChange={onBaseUrlChange}
|
onChange={onBaseUrlChange}
|
||||||
initialEndpoints={speedTestEndpoints}
|
initialEndpoints={speedTestEndpoints}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Zap, Loader2, Plus, X, AlertCircle, Save } from "lucide-react";
|
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 { vscodeApi } from "@/lib/api/vscode";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
@@ -28,7 +28,7 @@ interface TestResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface EndpointSpeedTestProps {
|
interface EndpointSpeedTestProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
providerId?: string;
|
providerId?: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (url: string) => void;
|
onChange: (url: string) => void;
|
||||||
@@ -82,7 +82,7 @@ const buildInitialEntries = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
||||||
appType,
|
appId,
|
||||||
providerId,
|
providerId,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -114,7 +114,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
if (!providerId) return;
|
if (!providerId) return;
|
||||||
|
|
||||||
const customEndpoints = await vscodeApi.getCustomEndpoints(
|
const customEndpoints = await vscodeApi.getCustomEndpoints(
|
||||||
appType,
|
appId,
|
||||||
providerId,
|
providerId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [appType, visible, providerId, t]);
|
}, [appId, visible, providerId, t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEntries((prev) => {
|
setEntries((prev) => {
|
||||||
@@ -284,7 +284,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
// 保存到后端
|
// 保存到后端
|
||||||
try {
|
try {
|
||||||
if (providerId) {
|
if (providerId) {
|
||||||
await vscodeApi.addCustomEndpoint(appType, providerId, sanitized);
|
await vscodeApi.addCustomEndpoint(appId, providerId, sanitized);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
@@ -318,7 +318,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
entries,
|
entries,
|
||||||
normalizedSelected,
|
normalizedSelected,
|
||||||
onChange,
|
onChange,
|
||||||
appType,
|
appId,
|
||||||
providerId,
|
providerId,
|
||||||
t,
|
t,
|
||||||
]);
|
]);
|
||||||
@@ -331,7 +331,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
// 如果有 providerId,尝试从后端删除
|
// 如果有 providerId,尝试从后端删除
|
||||||
if (entry.isCustom && providerId) {
|
if (entry.isCustom && providerId) {
|
||||||
try {
|
try {
|
||||||
await vscodeApi.removeCustomEndpoint(appType, providerId, entry.url);
|
await vscodeApi.removeCustomEndpoint(appId, providerId, entry.url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg =
|
const errorMsg =
|
||||||
error instanceof Error ? error.message : String(error);
|
error instanceof Error ? error.message : String(error);
|
||||||
@@ -362,7 +362,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[normalizedSelected, onChange, appType, providerId, t],
|
[normalizedSelected, onChange, appId, providerId, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const runSpeedTest = useCallback(async () => {
|
const runSpeedTest = useCallback(async () => {
|
||||||
@@ -387,7 +387,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const results = await vscodeApi.testApiEndpoints(urls, {
|
const results = await vscodeApi.testApiEndpoints(urls, {
|
||||||
timeoutSecs: ENDPOINT_TIMEOUT_SECS[appType],
|
timeoutSecs: ENDPOINT_TIMEOUT_SECS[appId],
|
||||||
});
|
});
|
||||||
|
|
||||||
const resultMap = new Map(
|
const resultMap = new Map(
|
||||||
@@ -437,7 +437,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
} finally {
|
} finally {
|
||||||
setIsTesting(false);
|
setIsTesting(false);
|
||||||
}
|
}
|
||||||
}, [entries, autoSelect, appType, normalizedSelected, onChange, t]);
|
}, [entries, autoSelect, appId, normalizedSelected, onChange, t]);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
async (url: string) => {
|
async (url: string) => {
|
||||||
@@ -447,7 +447,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
const entry = entries.find((e) => e.url === url);
|
const entry = entries.find((e) => e.url === url);
|
||||||
if (entry?.isCustom && providerId) {
|
if (entry?.isCustom && providerId) {
|
||||||
try {
|
try {
|
||||||
await vscodeApi.updateEndpointLastUsed(appType, providerId, url);
|
await vscodeApi.updateEndpointLastUsed(appId, providerId, url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(t("endpointTest.updateLastUsedFailed"), error);
|
console.error(t("endpointTest.updateLastUsedFailed"), error);
|
||||||
}
|
}
|
||||||
@@ -455,7 +455,7 @@ const EndpointSpeedTest: React.FC<EndpointSpeedTestProps> = ({
|
|||||||
|
|
||||||
onChange(url);
|
onChange(url);
|
||||||
},
|
},
|
||||||
[normalizedSelected, onChange, appType, entries, providerId, t],
|
[normalizedSelected, onChange, appId, entries, providerId, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Form } from "@/components/ui/form";
|
import { Form } from "@/components/ui/form";
|
||||||
import { providerSchema, type ProviderFormData } from "@/lib/schemas/provider";
|
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 type { ProviderCategory, ProviderMeta } from "@/types";
|
||||||
import { providerPresets, type ProviderPreset } from "@/config/providerPresets";
|
import { providerPresets, type ProviderPreset } from "@/config/providerPresets";
|
||||||
import {
|
import {
|
||||||
@@ -45,7 +45,7 @@ type PresetEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface ProviderFormProps {
|
interface ProviderFormProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
submitLabel: string;
|
submitLabel: string;
|
||||||
onSubmit: (values: ProviderFormValues) => void;
|
onSubmit: (values: ProviderFormValues) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
@@ -60,7 +60,7 @@ interface ProviderFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ProviderForm({
|
export function ProviderForm({
|
||||||
appType,
|
appId,
|
||||||
submitLabel,
|
submitLabel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
@@ -86,7 +86,7 @@ export function ProviderForm({
|
|||||||
|
|
||||||
// 使用 category hook
|
// 使用 category hook
|
||||||
const { category } = useProviderCategory({
|
const { category } = useProviderCategory({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
isEditMode,
|
isEditMode,
|
||||||
initialCategory: initialData?.category,
|
initialCategory: initialData?.category,
|
||||||
@@ -95,7 +95,7 @@ export function ProviderForm({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedPresetId(initialData ? null : "custom");
|
setSelectedPresetId(initialData ? null : "custom");
|
||||||
setActivePreset(null);
|
setActivePreset(null);
|
||||||
}, [appType, initialData]);
|
}, [appId, initialData]);
|
||||||
|
|
||||||
const defaultValues: ProviderFormData = useMemo(
|
const defaultValues: ProviderFormData = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -103,11 +103,11 @@ export function ProviderForm({
|
|||||||
websiteUrl: initialData?.websiteUrl ?? "",
|
websiteUrl: initialData?.websiteUrl ?? "",
|
||||||
settingsConfig: initialData?.settingsConfig
|
settingsConfig: initialData?.settingsConfig
|
||||||
? JSON.stringify(initialData.settingsConfig, null, 2)
|
? JSON.stringify(initialData.settingsConfig, null, 2)
|
||||||
: appType === "codex"
|
: appId === "codex"
|
||||||
? CODEX_DEFAULT_CONFIG
|
? CODEX_DEFAULT_CONFIG
|
||||||
: CLAUDE_DEFAULT_CONFIG,
|
: CLAUDE_DEFAULT_CONFIG,
|
||||||
}),
|
}),
|
||||||
[initialData, appType],
|
[initialData, appId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const form = useForm<ProviderFormData>({
|
const form = useForm<ProviderFormData>({
|
||||||
@@ -130,7 +130,7 @@ export function ProviderForm({
|
|||||||
|
|
||||||
// 使用 Base URL hook (仅 Claude 模式)
|
// 使用 Base URL hook (仅 Claude 模式)
|
||||||
const { baseUrl, handleClaudeBaseUrlChange } = useBaseUrlState({
|
const { baseUrl, handleClaudeBaseUrlChange } = useBaseUrlState({
|
||||||
appType,
|
appType: appId,
|
||||||
category,
|
category,
|
||||||
settingsConfig: form.watch("settingsConfig"),
|
settingsConfig: form.watch("settingsConfig"),
|
||||||
codexConfig: "",
|
codexConfig: "",
|
||||||
@@ -202,7 +202,7 @@ export function ProviderForm({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const presetEntries = useMemo(() => {
|
const presetEntries = useMemo(() => {
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
return codexProviderPresets.map<PresetEntry>((preset, index) => ({
|
return codexProviderPresets.map<PresetEntry>((preset, index) => ({
|
||||||
id: `codex-${index}`,
|
id: `codex-${index}`,
|
||||||
preset,
|
preset,
|
||||||
@@ -212,7 +212,7 @@ export function ProviderForm({
|
|||||||
id: `claude-${index}`,
|
id: `claude-${index}`,
|
||||||
preset,
|
preset,
|
||||||
}));
|
}));
|
||||||
}, [appType]);
|
}, [appId]);
|
||||||
|
|
||||||
// 使用 Kimi 模型选择器 hook
|
// 使用 Kimi 模型选择器 hook
|
||||||
const {
|
const {
|
||||||
@@ -240,8 +240,8 @@ export function ProviderForm({
|
|||||||
handleTemplateValueChange,
|
handleTemplateValueChange,
|
||||||
validateTemplateValues,
|
validateTemplateValues,
|
||||||
} = useTemplateValues({
|
} = useTemplateValues({
|
||||||
selectedPresetId: appType === "claude" ? selectedPresetId : null,
|
selectedPresetId: appId === "claude" ? selectedPresetId : null,
|
||||||
presetEntries: appType === "claude" ? presetEntries : [],
|
presetEntries: appId === "claude" ? presetEntries : [],
|
||||||
settingsConfig: form.watch("settingsConfig"),
|
settingsConfig: form.watch("settingsConfig"),
|
||||||
onConfigChange: (config) => form.setValue("settingsConfig", config),
|
onConfigChange: (config) => form.setValue("settingsConfig", config),
|
||||||
});
|
});
|
||||||
@@ -256,7 +256,7 @@ export function ProviderForm({
|
|||||||
} = useCommonConfigSnippet({
|
} = useCommonConfigSnippet({
|
||||||
settingsConfig: form.watch("settingsConfig"),
|
settingsConfig: form.watch("settingsConfig"),
|
||||||
onConfigChange: (config) => form.setValue("settingsConfig", config),
|
onConfigChange: (config) => form.setValue("settingsConfig", config),
|
||||||
initialData: appType === "claude" ? initialData : undefined,
|
initialData: appId === "claude" ? initialData : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 使用 Codex 通用配置片段 hook (仅 Codex 模式)
|
// 使用 Codex 通用配置片段 hook (仅 Codex 模式)
|
||||||
@@ -269,14 +269,14 @@ export function ProviderForm({
|
|||||||
} = useCodexCommonConfig({
|
} = useCodexCommonConfig({
|
||||||
codexConfig,
|
codexConfig,
|
||||||
onConfigChange: handleCodexConfigChange,
|
onConfigChange: handleCodexConfigChange,
|
||||||
initialData: appType === "codex" ? initialData : undefined,
|
initialData: appId === "codex" ? initialData : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
|
const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false);
|
||||||
|
|
||||||
const handleSubmit = (values: ProviderFormData) => {
|
const handleSubmit = (values: ProviderFormData) => {
|
||||||
// 验证模板变量(仅 Claude 模式)
|
// 验证模板变量(仅 Claude 模式)
|
||||||
if (appType === "claude" && templateValueEntries.length > 0) {
|
if (appId === "claude" && templateValueEntries.length > 0) {
|
||||||
const validation = validateTemplateValues();
|
const validation = validateTemplateValues();
|
||||||
if (!validation.isValid && validation.missingField) {
|
if (!validation.isValid && validation.missingField) {
|
||||||
form.setError("settingsConfig", {
|
form.setError("settingsConfig", {
|
||||||
@@ -293,7 +293,7 @@ export function ProviderForm({
|
|||||||
let settingsConfig: string;
|
let settingsConfig: string;
|
||||||
|
|
||||||
// Codex: 组合 auth 和 config
|
// Codex: 组合 auth 和 config
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
try {
|
try {
|
||||||
const authJson = JSON.parse(codexAuth);
|
const authJson = JSON.parse(codexAuth);
|
||||||
const configObj = {
|
const configObj = {
|
||||||
@@ -359,7 +359,7 @@ export function ProviderForm({
|
|||||||
shouldShowApiKeyLink: shouldShowClaudeApiKeyLink,
|
shouldShowApiKeyLink: shouldShowClaudeApiKeyLink,
|
||||||
websiteUrl: claudeWebsiteUrl,
|
websiteUrl: claudeWebsiteUrl,
|
||||||
} = useApiKeyLink({
|
} = useApiKeyLink({
|
||||||
appType: "claude",
|
appId: "claude",
|
||||||
category,
|
category,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
@@ -371,7 +371,7 @@ export function ProviderForm({
|
|||||||
shouldShowApiKeyLink: shouldShowCodexApiKeyLink,
|
shouldShowApiKeyLink: shouldShowCodexApiKeyLink,
|
||||||
websiteUrl: codexWebsiteUrl,
|
websiteUrl: codexWebsiteUrl,
|
||||||
} = useApiKeyLink({
|
} = useApiKeyLink({
|
||||||
appType: "codex",
|
appId: "codex",
|
||||||
category,
|
category,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
@@ -380,7 +380,7 @@ export function ProviderForm({
|
|||||||
|
|
||||||
// 使用自定义端点 hook
|
// 使用自定义端点 hook
|
||||||
const customEndpointsMap = useCustomEndpoints({
|
const customEndpointsMap = useCustomEndpoints({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
draftCustomEndpoints,
|
draftCustomEndpoints,
|
||||||
@@ -390,7 +390,7 @@ export function ProviderForm({
|
|||||||
|
|
||||||
// 使用端点测速候选 hook
|
// 使用端点测速候选 hook
|
||||||
const speedTestEndpoints = useSpeedTestEndpoints({
|
const speedTestEndpoints = useSpeedTestEndpoints({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -405,7 +405,7 @@ export function ProviderForm({
|
|||||||
form.reset(defaultValues);
|
form.reset(defaultValues);
|
||||||
|
|
||||||
// Codex 自定义模式:重置为空配置
|
// Codex 自定义模式:重置为空配置
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
resetCodexConfig({}, "");
|
resetCodexConfig({}, "");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -421,7 +421,7 @@ export function ProviderForm({
|
|||||||
category: entry.preset.category,
|
category: entry.preset.category,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
const preset = entry.preset as CodexProviderPreset;
|
const preset = entry.preset as CodexProviderPreset;
|
||||||
const auth = preset.auth ?? {};
|
const auth = preset.auth ?? {};
|
||||||
const config = preset.config ?? "";
|
const config = preset.config ?? "";
|
||||||
@@ -474,7 +474,7 @@ export function ProviderForm({
|
|||||||
<BasicFormFields form={form} />
|
<BasicFormFields form={form} />
|
||||||
|
|
||||||
{/* Claude 专属字段 */}
|
{/* Claude 专属字段 */}
|
||||||
{appType === "claude" && (
|
{appId === "claude" && (
|
||||||
<ClaudeFormFields
|
<ClaudeFormFields
|
||||||
shouldShowApiKey={shouldShowApiKey(
|
shouldShowApiKey={shouldShowApiKey(
|
||||||
form.watch("settingsConfig"),
|
form.watch("settingsConfig"),
|
||||||
@@ -510,7 +510,7 @@ export function ProviderForm({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Codex 专属字段 */}
|
{/* Codex 专属字段 */}
|
||||||
{appType === "codex" && (
|
{appId === "codex" && (
|
||||||
<CodexFormFields
|
<CodexFormFields
|
||||||
codexApiKey={codexApiKey}
|
codexApiKey={codexApiKey}
|
||||||
onApiKeyChange={handleCodexApiKeyChange}
|
onApiKeyChange={handleCodexApiKeyChange}
|
||||||
@@ -528,7 +528,7 @@ export function ProviderForm({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 配置编辑器:Claude 使用通用配置编辑器,Codex 使用专用编辑器 */}
|
{/* 配置编辑器:Claude 使用通用配置编辑器,Codex 使用专用编辑器 */}
|
||||||
{appType === "codex" ? (
|
{appId === "codex" ? (
|
||||||
<CodexConfigEditor
|
<CodexConfigEditor
|
||||||
authValue={codexAuth}
|
authValue={codexAuth}
|
||||||
configValue={codexConfig}
|
configValue={codexConfig}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import type { ProviderCategory } from "@/types";
|
import type { ProviderCategory } from "@/types";
|
||||||
import type { ProviderPreset } from "@/config/providerPresets";
|
import type { ProviderPreset } from "@/config/providerPresets";
|
||||||
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
||||||
@@ -10,7 +10,7 @@ type PresetEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface UseApiKeyLinkProps {
|
interface UseApiKeyLinkProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
category?: ProviderCategory;
|
category?: ProviderCategory;
|
||||||
selectedPresetId: string | null;
|
selectedPresetId: string | null;
|
||||||
presetEntries: PresetEntry[];
|
presetEntries: PresetEntry[];
|
||||||
@@ -21,7 +21,7 @@ interface UseApiKeyLinkProps {
|
|||||||
* 管理 API Key 获取链接的显示和 URL
|
* 管理 API Key 获取链接的显示和 URL
|
||||||
*/
|
*/
|
||||||
export function useApiKeyLink({
|
export function useApiKeyLink({
|
||||||
appType,
|
appId,
|
||||||
category,
|
category,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
@@ -53,12 +53,7 @@ export function useApiKeyLink({
|
|||||||
}, [selectedPresetId, presetEntries, formWebsiteUrl]);
|
}, [selectedPresetId, presetEntries, formWebsiteUrl]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldShowApiKeyLink:
|
shouldShowApiKeyLink: appId === "claude" ? shouldShowApiKeyLink : appId === "codex" ? shouldShowApiKeyLink : false,
|
||||||
appType === "claude"
|
|
||||||
? shouldShowApiKeyLink
|
|
||||||
: appType === "codex"
|
|
||||||
? shouldShowApiKeyLink
|
|
||||||
: false,
|
|
||||||
websiteUrl: getWebsiteUrl,
|
websiteUrl: getWebsiteUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import type { CustomEndpoint } from "@/types";
|
import type { CustomEndpoint } from "@/types";
|
||||||
import type { ProviderPreset } from "@/config/providerPresets";
|
import type { ProviderPreset } from "@/config/providerPresets";
|
||||||
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
||||||
@@ -10,7 +10,7 @@ type PresetEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface UseCustomEndpointsProps {
|
interface UseCustomEndpointsProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
selectedPresetId: string | null;
|
selectedPresetId: string | null;
|
||||||
presetEntries: PresetEntry[];
|
presetEntries: PresetEntry[];
|
||||||
draftCustomEndpoints: string[];
|
draftCustomEndpoints: string[];
|
||||||
@@ -27,7 +27,7 @@ interface UseCustomEndpointsProps {
|
|||||||
* 3. 当前选中的 Base URL
|
* 3. 当前选中的 Base URL
|
||||||
*/
|
*/
|
||||||
export function useCustomEndpoints({
|
export function useCustomEndpoints({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
draftCustomEndpoints,
|
draftCustomEndpoints,
|
||||||
@@ -58,7 +58,7 @@ export function useCustomEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 当前 Base URL
|
// 3. 当前 Base URL
|
||||||
if (appType === "codex") {
|
if (appId === "codex") {
|
||||||
push(codexBaseUrl);
|
push(codexBaseUrl);
|
||||||
} else {
|
} else {
|
||||||
push(baseUrl);
|
push(baseUrl);
|
||||||
@@ -80,7 +80,7 @@ export function useCustomEndpoints({
|
|||||||
|
|
||||||
return customMap;
|
return customMap;
|
||||||
}, [
|
}, [
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
draftCustomEndpoints,
|
draftCustomEndpoints,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import type { ProviderCategory } from "@/types";
|
import type { ProviderCategory } from "@/types";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import { providerPresets } from "@/config/providerPresets";
|
import { providerPresets } from "@/config/providerPresets";
|
||||||
import { codexProviderPresets } from "@/config/codexProviderPresets";
|
import { codexProviderPresets } from "@/config/codexProviderPresets";
|
||||||
|
|
||||||
interface UseProviderCategoryProps {
|
interface UseProviderCategoryProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
selectedPresetId: string | null;
|
selectedPresetId: string | null;
|
||||||
isEditMode: boolean;
|
isEditMode: boolean;
|
||||||
initialCategory?: ProviderCategory;
|
initialCategory?: ProviderCategory;
|
||||||
@@ -16,7 +16,7 @@ interface UseProviderCategoryProps {
|
|||||||
* 根据选择的预设自动更新类别
|
* 根据选择的预设自动更新类别
|
||||||
*/
|
*/
|
||||||
export function useProviderCategory({
|
export function useProviderCategory({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
isEditMode,
|
isEditMode,
|
||||||
initialCategory,
|
initialCategory,
|
||||||
@@ -47,14 +47,14 @@ export function useProviderCategory({
|
|||||||
const [, type, indexStr] = match;
|
const [, type, indexStr] = match;
|
||||||
const index = parseInt(indexStr, 10);
|
const index = parseInt(indexStr, 10);
|
||||||
|
|
||||||
if (type === "codex" && appType === "codex") {
|
if (type === "codex" && appId === "codex") {
|
||||||
const preset = codexProviderPresets[index];
|
const preset = codexProviderPresets[index];
|
||||||
if (preset) {
|
if (preset) {
|
||||||
setCategory(
|
setCategory(
|
||||||
preset.category || (preset.isOfficial ? "official" : undefined),
|
preset.category || (preset.isOfficial ? "official" : undefined),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (type === "claude" && appType === "claude") {
|
} else if (type === "claude" && appId === "claude") {
|
||||||
const preset = providerPresets[index];
|
const preset = providerPresets[index];
|
||||||
if (preset) {
|
if (preset) {
|
||||||
setCategory(
|
setCategory(
|
||||||
@@ -62,7 +62,7 @@ export function useProviderCategory({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [appType, selectedPresetId, isEditMode, initialCategory]);
|
}, [appId, selectedPresetId, isEditMode, initialCategory]);
|
||||||
|
|
||||||
return { category, setCategory };
|
return { category, setCategory };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import type { ProviderPreset } from "@/config/providerPresets";
|
import type { ProviderPreset } from "@/config/providerPresets";
|
||||||
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
|
||||||
import type { ProviderMeta, EndpointCandidate } from "@/types";
|
import type { ProviderMeta, EndpointCandidate } from "@/types";
|
||||||
@@ -10,7 +10,7 @@ type PresetEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface UseSpeedTestEndpointsProps {
|
interface UseSpeedTestEndpointsProps {
|
||||||
appType: AppType;
|
appId: AppId;
|
||||||
selectedPresetId: string | null;
|
selectedPresetId: string | null;
|
||||||
presetEntries: PresetEntry[];
|
presetEntries: PresetEntry[];
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
@@ -31,7 +31,7 @@ interface UseSpeedTestEndpointsProps {
|
|||||||
* 4. 预设中的 endpointCandidates
|
* 4. 预设中的 endpointCandidates
|
||||||
*/
|
*/
|
||||||
export function useSpeedTestEndpoints({
|
export function useSpeedTestEndpoints({
|
||||||
appType,
|
appId,
|
||||||
selectedPresetId,
|
selectedPresetId,
|
||||||
presetEntries,
|
presetEntries,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -39,7 +39,7 @@ export function useSpeedTestEndpoints({
|
|||||||
initialData,
|
initialData,
|
||||||
}: UseSpeedTestEndpointsProps) {
|
}: UseSpeedTestEndpointsProps) {
|
||||||
const claudeEndpoints = useMemo<EndpointCandidate[]>(() => {
|
const claudeEndpoints = useMemo<EndpointCandidate[]>(() => {
|
||||||
if (appType !== "claude") return [];
|
if (appId !== "claude") return [];
|
||||||
|
|
||||||
const map = new Map<string, EndpointCandidate>();
|
const map = new Map<string, EndpointCandidate>();
|
||||||
// 所有端点都标记为 isCustom: true,给用户完全的管理自由
|
// 所有端点都标记为 isCustom: true,给用户完全的管理自由
|
||||||
@@ -94,10 +94,10 @@ export function useSpeedTestEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(map.values());
|
return Array.from(map.values());
|
||||||
}, [appType, baseUrl, initialData, selectedPresetId, presetEntries]);
|
}, [appId, baseUrl, initialData, selectedPresetId, presetEntries]);
|
||||||
|
|
||||||
const codexEndpoints = useMemo<EndpointCandidate[]>(() => {
|
const codexEndpoints = useMemo<EndpointCandidate[]>(() => {
|
||||||
if (appType !== "codex") return [];
|
if (appId !== "codex") return [];
|
||||||
|
|
||||||
const map = new Map<string, EndpointCandidate>();
|
const map = new Map<string, EndpointCandidate>();
|
||||||
// 所有端点都标记为 isCustom: true,给用户完全的管理自由
|
// 所有端点都标记为 isCustom: true,给用户完全的管理自由
|
||||||
@@ -155,7 +155,7 @@ export function useSpeedTestEndpoints({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(map.values());
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { FolderSearch, Undo2 } from "lucide-react";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import type { AppType } from "@/lib/api";
|
import type { AppId } from "@/lib/api";
|
||||||
import type { ResolvedDirectories } from "@/hooks/useSettings";
|
import type { ResolvedDirectories } from "@/hooks/useSettings";
|
||||||
|
|
||||||
interface DirectorySettingsProps {
|
interface DirectorySettingsProps {
|
||||||
@@ -14,9 +14,9 @@ interface DirectorySettingsProps {
|
|||||||
onResetAppConfig: () => Promise<void>;
|
onResetAppConfig: () => Promise<void>;
|
||||||
claudeDir?: string;
|
claudeDir?: string;
|
||||||
codexDir?: string;
|
codexDir?: string;
|
||||||
onDirectoryChange: (app: AppType, value?: string) => void;
|
onDirectoryChange: (app: AppId, value?: string) => void;
|
||||||
onBrowseDirectory: (app: AppType) => Promise<void>;
|
onBrowseDirectory: (app: AppId) => Promise<void>;
|
||||||
onResetDirectory: (app: AppType) => Promise<void>;
|
onResetDirectory: (app: AppId) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DirectorySettings({
|
export function DirectorySettings({
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { homeDir, join } from "@tauri-apps/api/path";
|
import { homeDir, join } from "@tauri-apps/api/path";
|
||||||
import { settingsApi, type AppType } from "@/lib/api";
|
import { settingsApi, type AppId } from "@/lib/api";
|
||||||
import type { SettingsFormState } from "./useSettingsForm";
|
import type { SettingsFormState } from "./useSettingsForm";
|
||||||
|
|
||||||
type DirectoryKey = "appConfig" | "claude" | "codex";
|
type DirectoryKey = "appConfig" | "claude" | "codex";
|
||||||
@@ -33,7 +33,7 @@ const computeDefaultAppConfigDir = async (): Promise<string | undefined> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const computeDefaultConfigDir = async (
|
const computeDefaultConfigDir = async (
|
||||||
app: AppType,
|
app: AppId,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
try {
|
try {
|
||||||
const home = await homeDir();
|
const home = await homeDir();
|
||||||
@@ -58,11 +58,11 @@ export interface UseDirectorySettingsResult {
|
|||||||
resolvedDirs: ResolvedDirectories;
|
resolvedDirs: ResolvedDirectories;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
initialAppConfigDir?: string;
|
initialAppConfigDir?: string;
|
||||||
updateDirectory: (app: AppType, value?: string) => void;
|
updateDirectory: (app: AppId, value?: string) => void;
|
||||||
updateAppConfigDir: (value?: string) => void;
|
updateAppConfigDir: (value?: string) => void;
|
||||||
browseDirectory: (app: AppType) => Promise<void>;
|
browseDirectory: (app: AppId) => Promise<void>;
|
||||||
browseAppConfigDir: () => Promise<void>;
|
browseAppConfigDir: () => Promise<void>;
|
||||||
resetDirectory: (app: AppType) => Promise<void>;
|
resetDirectory: (app: AppId) => Promise<void>;
|
||||||
resetAppConfigDir: () => Promise<void>;
|
resetAppConfigDir: () => Promise<void>;
|
||||||
resetAllDirectories: (claudeDir?: string, codexDir?: string) => void;
|
resetAllDirectories: (claudeDir?: string, codexDir?: string) => void;
|
||||||
}
|
}
|
||||||
@@ -187,14 +187,14 @@ export function useDirectorySettings({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updateDirectory = useCallback(
|
const updateDirectory = useCallback(
|
||||||
(app: AppType, value?: string) => {
|
(app: AppId, value?: string) => {
|
||||||
updateDirectoryState(app === "claude" ? "claude" : "codex", value);
|
updateDirectoryState(app === "claude" ? "claude" : "codex", value);
|
||||||
},
|
},
|
||||||
[updateDirectoryState],
|
[updateDirectoryState],
|
||||||
);
|
);
|
||||||
|
|
||||||
const browseDirectory = useCallback(
|
const browseDirectory = useCallback(
|
||||||
async (app: AppType) => {
|
async (app: AppId) => {
|
||||||
const key: DirectoryKey = app === "claude" ? "claude" : "codex";
|
const key: DirectoryKey = app === "claude" ? "claude" : "codex";
|
||||||
const currentValue =
|
const currentValue =
|
||||||
key === "claude"
|
key === "claude"
|
||||||
@@ -239,7 +239,7 @@ export function useDirectorySettings({
|
|||||||
}, [appConfigDir, resolvedDirs.appConfig, t, updateDirectoryState]);
|
}, [appConfigDir, resolvedDirs.appConfig, t, updateDirectoryState]);
|
||||||
|
|
||||||
const resetDirectory = useCallback(
|
const resetDirectory = useCallback(
|
||||||
async (app: AppType) => {
|
async (app: AppId) => {
|
||||||
const key: DirectoryKey = app === "claude" ? "claude" : "codex";
|
const key: DirectoryKey = app === "claude" ? "claude" : "codex";
|
||||||
if (!defaultsRef.current[key]) {
|
if (!defaultsRef.current[key]) {
|
||||||
const fallback = await computeDefaultConfigDir(app);
|
const fallback = await computeDefaultConfigDir(app);
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import { useQueryClient } from "@tanstack/react-query";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import type { Provider } from "@/types";
|
import type { Provider } from "@/types";
|
||||||
import { providersApi, type AppType } from "@/lib/api";
|
import { providersApi, type AppId } from "@/lib/api";
|
||||||
|
|
||||||
export function useDragSort(
|
export function useDragSort(
|
||||||
providers: Record<string, Provider>,
|
providers: Record<string, Provider>,
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
) {
|
) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
@@ -73,9 +73,9 @@ export function useDragSort(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await providersApi.updateSortOrder(updates, appType);
|
await providersApi.updateSortOrder(updates, appId);
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ["providers", appType],
|
queryKey: ["providers", appId],
|
||||||
});
|
});
|
||||||
toast.success(
|
toast.success(
|
||||||
t("provider.sortUpdated", {
|
t("provider.sortUpdated", {
|
||||||
@@ -91,7 +91,7 @@ export function useDragSort(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sortedProviders, appType, queryClient, t],
|
[sortedProviders, appId, queryClient, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { mcpApi, type AppType } from "@/lib/api";
|
import { mcpApi, type AppId } from "@/lib/api";
|
||||||
import type { McpServer } from "@/types";
|
import type { McpServer } from "@/types";
|
||||||
import {
|
import {
|
||||||
extractErrorMessage,
|
extractErrorMessage,
|
||||||
@@ -30,7 +30,7 @@ export interface UseMcpActionsResult {
|
|||||||
* - Delete server
|
* - Delete server
|
||||||
* - Error handling and toast notifications
|
* - Error handling and toast notifications
|
||||||
*/
|
*/
|
||||||
export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
export function useMcpActions(appId: AppId): UseMcpActionsResult {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [servers, setServers] = useState<Record<string, McpServer>>({});
|
const [servers, setServers] = useState<Record<string, McpServer>>({});
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -38,7 +38,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
const reload = useCallback(async () => {
|
const reload = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const cfg = await mcpApi.getConfig(appType);
|
const cfg = await mcpApi.getConfig(appId);
|
||||||
setServers(cfg.servers || {});
|
setServers(cfg.servers || {});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[useMcpActions] Failed to load MCP config", error);
|
console.error("[useMcpActions] Failed to load MCP config", error);
|
||||||
@@ -50,7 +50,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [appType, t]);
|
}, [appId, t]);
|
||||||
|
|
||||||
const toggleEnabled = useCallback(
|
const toggleEnabled = useCallback(
|
||||||
async (id: string, enabled: boolean) => {
|
async (id: string, enabled: boolean) => {
|
||||||
@@ -65,7 +65,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mcpApi.setEnabled(appType, id, enabled);
|
await mcpApi.setEnabled(appId, id, enabled);
|
||||||
toast.success(enabled ? t("mcp.msg.enabled") : t("mcp.msg.disabled"), {
|
toast.success(enabled ? t("mcp.msg.enabled") : t("mcp.msg.disabled"), {
|
||||||
duration: 1500,
|
duration: 1500,
|
||||||
});
|
});
|
||||||
@@ -79,7 +79,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[appType, servers, t],
|
[appId, servers, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveServer = useCallback(
|
const saveServer = useCallback(
|
||||||
@@ -90,7 +90,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const payload: McpServer = { ...server, id };
|
const payload: McpServer = { ...server, id };
|
||||||
await mcpApi.upsertServerInConfig(appType, id, payload, {
|
await mcpApi.upsertServerInConfig(appId, id, payload, {
|
||||||
syncOtherSide: options?.syncOtherSide,
|
syncOtherSide: options?.syncOtherSide,
|
||||||
});
|
});
|
||||||
await reload();
|
await reload();
|
||||||
@@ -104,13 +104,13 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[appType, reload, t],
|
[appId, reload, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteServer = useCallback(
|
const deleteServer = useCallback(
|
||||||
async (id: string) => {
|
async (id: string) => {
|
||||||
try {
|
try {
|
||||||
await mcpApi.deleteServerInConfig(appType, id);
|
await mcpApi.deleteServerInConfig(appId, id);
|
||||||
await reload();
|
await reload();
|
||||||
toast.success(t("mcp.msg.deleted"), { duration: 1500 });
|
toast.success(t("mcp.msg.deleted"), { duration: 1500 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -122,7 +122,7 @@ export function useMcpActions(appType: AppType): UseMcpActionsResult {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[appType, reload, t],
|
[appId, reload, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback } from "react";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { providersApi, settingsApi, type AppType } from "@/lib/api";
|
import { providersApi, settingsApi, type AppId } from "@/lib/api";
|
||||||
import type { Provider, UsageScript } from "@/types";
|
import type { Provider, UsageScript } from "@/types";
|
||||||
import {
|
import {
|
||||||
useAddProviderMutation,
|
useAddProviderMutation,
|
||||||
@@ -16,7 +16,7 @@ import { extractErrorMessage } from "@/utils/errorUtils";
|
|||||||
* Hook for managing provider actions (add, update, delete, switch)
|
* Hook for managing provider actions (add, update, delete, switch)
|
||||||
* Extracts business logic from App.tsx
|
* Extracts business logic from App.tsx
|
||||||
*/
|
*/
|
||||||
export function useProviderActions(activeApp: AppType) {
|
export function useProviderActions(activeApp: AppId) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { settingsApi, type AppType } from "@/lib/api";
|
import { settingsApi, type AppId } from "@/lib/api";
|
||||||
import { useSettingsQuery, useSaveSettingsMutation } from "@/lib/query";
|
import { useSettingsQuery, useSaveSettingsMutation } from "@/lib/query";
|
||||||
import type { Settings } from "@/types";
|
import type { Settings } from "@/types";
|
||||||
import { useSettingsForm, type SettingsFormState } from "./useSettingsForm";
|
import { useSettingsForm, type SettingsFormState } from "./useSettingsForm";
|
||||||
@@ -26,11 +26,11 @@ export interface UseSettingsResult {
|
|||||||
resolvedDirs: ResolvedDirectories;
|
resolvedDirs: ResolvedDirectories;
|
||||||
requiresRestart: boolean;
|
requiresRestart: boolean;
|
||||||
updateSettings: (updates: Partial<SettingsFormState>) => void;
|
updateSettings: (updates: Partial<SettingsFormState>) => void;
|
||||||
updateDirectory: (app: AppType, value?: string) => void;
|
updateDirectory: (app: AppId, value?: string) => void;
|
||||||
updateAppConfigDir: (value?: string) => void;
|
updateAppConfigDir: (value?: string) => void;
|
||||||
browseDirectory: (app: AppType) => Promise<void>;
|
browseDirectory: (app: AppId) => Promise<void>;
|
||||||
browseAppConfigDir: () => Promise<void>;
|
browseAppConfigDir: () => Promise<void>;
|
||||||
resetDirectory: (app: AppType) => Promise<void>;
|
resetDirectory: (app: AppId) => Promise<void>;
|
||||||
resetAppConfigDir: () => Promise<void>;
|
resetAppConfigDir: () => Promise<void>;
|
||||||
saveSettings: () => Promise<SaveResult | null>;
|
saveSettings: () => Promise<SaveResult | null>;
|
||||||
resetSettings: () => void;
|
resetSettings: () => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export type { AppType, AppId } from "./types";
|
export type { AppId } from "./types";
|
||||||
export { providersApi } from "./providers";
|
export { providersApi } from "./providers";
|
||||||
export { settingsApi } from "./settings";
|
export { settingsApi } from "./settings";
|
||||||
export { mcpApi } from "./mcp";
|
export { mcpApi } from "./mcp";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
McpServerSpec,
|
McpServerSpec,
|
||||||
McpStatus,
|
McpStatus,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import type { AppType } from "./types";
|
import type { AppId } from "./types";
|
||||||
|
|
||||||
export const mcpApi = {
|
export const mcpApi = {
|
||||||
async getStatus(): Promise<McpStatus> {
|
async getStatus(): Promise<McpStatus> {
|
||||||
@@ -31,7 +31,7 @@ export const mcpApi = {
|
|||||||
return await invoke("validate_mcp_command", { cmd });
|
return await invoke("validate_mcp_command", { cmd });
|
||||||
},
|
},
|
||||||
|
|
||||||
async getConfig(app: AppType = "claude"): Promise<McpConfigResponse> {
|
async getConfig(app: AppId = "claude"): Promise<McpConfigResponse> {
|
||||||
return await invoke("get_mcp_config", { app });
|
return await invoke("get_mcp_config", { app });
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export const mcpApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async upsertServerInConfig(
|
async upsertServerInConfig(
|
||||||
app: AppType,
|
app: AppId,
|
||||||
id: string,
|
id: string,
|
||||||
spec: McpServer,
|
spec: McpServer,
|
||||||
options?: { syncOtherSide?: boolean },
|
options?: { syncOtherSide?: boolean },
|
||||||
@@ -61,7 +61,7 @@ export const mcpApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async deleteServerInConfig(
|
async deleteServerInConfig(
|
||||||
app: AppType,
|
app: AppId,
|
||||||
id: string,
|
id: string,
|
||||||
options?: { syncOtherSide?: boolean },
|
options?: { syncOtherSide?: boolean },
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
@@ -76,7 +76,7 @@ export const mcpApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async setEnabled(
|
async setEnabled(
|
||||||
app: AppType,
|
app: AppId,
|
||||||
id: string,
|
id: string,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import type { Provider } from "@/types";
|
import type { Provider } from "@/types";
|
||||||
import type { AppType } from "./types";
|
import type { AppId } from "./types";
|
||||||
|
|
||||||
export interface ProviderSortUpdate {
|
export interface ProviderSortUpdate {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -9,37 +9,37 @@ export interface ProviderSortUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderSwitchEvent {
|
export interface ProviderSwitchEvent {
|
||||||
appType: AppType;
|
appType: AppId;
|
||||||
providerId: string;
|
providerId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const providersApi = {
|
export const providersApi = {
|
||||||
async getAll(appType: AppType): Promise<Record<string, Provider>> {
|
async getAll(appId: AppId): Promise<Record<string, Provider>> {
|
||||||
return await invoke("get_providers", { app: appType });
|
return await invoke("get_providers", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async getCurrent(appType: AppType): Promise<string> {
|
async getCurrent(appId: AppId): Promise<string> {
|
||||||
return await invoke("get_current_provider", { app: appType });
|
return await invoke("get_current_provider", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async add(provider: Provider, appType: AppType): Promise<boolean> {
|
async add(provider: Provider, appId: AppId): Promise<boolean> {
|
||||||
return await invoke("add_provider", { provider, app: appType });
|
return await invoke("add_provider", { provider, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(provider: Provider, appType: AppType): Promise<boolean> {
|
async update(provider: Provider, appId: AppId): Promise<boolean> {
|
||||||
return await invoke("update_provider", { provider, app: appType });
|
return await invoke("update_provider", { provider, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(id: string, appType: AppType): Promise<boolean> {
|
async delete(id: string, appId: AppId): Promise<boolean> {
|
||||||
return await invoke("delete_provider", { id, app: appType });
|
return await invoke("delete_provider", { id, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async switch(id: string, appType: AppType): Promise<boolean> {
|
async switch(id: string, appId: AppId): Promise<boolean> {
|
||||||
return await invoke("switch_provider", { id, app: appType });
|
return await invoke("switch_provider", { id, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async importDefault(appType: AppType): Promise<boolean> {
|
async importDefault(appId: AppId): Promise<boolean> {
|
||||||
return await invoke("import_default_config", { app: appType });
|
return await invoke("import_default_config", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateTrayMenu(): Promise<boolean> {
|
async updateTrayMenu(): Promise<boolean> {
|
||||||
@@ -48,9 +48,9 @@ export const providersApi = {
|
|||||||
|
|
||||||
async updateSortOrder(
|
async updateSortOrder(
|
||||||
updates: ProviderSortUpdate[],
|
updates: ProviderSortUpdate[],
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return await invoke("update_providers_sort_order", { updates, app: appType });
|
return await invoke("update_providers_sort_order", { updates, app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async onSwitched(
|
async onSwitched(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import type { Settings } from "@/types";
|
import type { Settings } from "@/types";
|
||||||
import type { AppType } from "./types";
|
import type { AppId } from "./types";
|
||||||
|
|
||||||
export interface ConfigTransferResult {
|
export interface ConfigTransferResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -30,12 +30,12 @@ export const settingsApi = {
|
|||||||
return await invoke("is_portable_mode");
|
return await invoke("is_portable_mode");
|
||||||
},
|
},
|
||||||
|
|
||||||
async getConfigDir(appType: AppType): Promise<string> {
|
async getConfigDir(appId: AppId): Promise<string> {
|
||||||
return await invoke("get_config_dir", { app: appType });
|
return await invoke("get_config_dir", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async openConfigFolder(appType: AppType): Promise<void> {
|
async openConfigFolder(appId: AppId): Promise<void> {
|
||||||
await invoke("open_config_folder", { app: appType });
|
await invoke("open_config_folder", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async selectConfigDirectory(defaultPath?: string): Promise<string | null> {
|
async selectConfigDirectory(defaultPath?: string): Promise<string | null> {
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export type AppType = "claude" | "codex";
|
// 前端统一使用 AppId 作为应用标识(与后端命令参数 `app` 一致)
|
||||||
// 为避免与后端 Rust `AppType` 枚举语义混淆,可使用更贴近“标识符”的别名
|
export type AppId = "claude" | "codex";
|
||||||
export type AppId = AppType;
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import type { UsageResult } from "@/types";
|
import type { UsageResult } from "@/types";
|
||||||
import type { AppType } from "./types";
|
import type { AppId } from "./types";
|
||||||
|
|
||||||
export const usageApi = {
|
export const usageApi = {
|
||||||
async query(providerId: string, appType: AppType): Promise<UsageResult> {
|
async query(providerId: string, appId: AppId): Promise<UsageResult> {
|
||||||
return await invoke("query_provider_usage", {
|
return await invoke("query_provider_usage", {
|
||||||
provider_id: providerId,
|
provider_id: providerId,
|
||||||
app: appType,
|
app: appId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import type { CustomEndpoint } from "@/types";
|
import type { CustomEndpoint } from "@/types";
|
||||||
import type { AppType } from "./types";
|
import type { AppId } from "./types";
|
||||||
|
|
||||||
export interface EndpointLatencyResult {
|
export interface EndpointLatencyResult {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -10,8 +10,8 @@ export interface EndpointLatencyResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const vscodeApi = {
|
export const vscodeApi = {
|
||||||
async getLiveProviderSettings(appType: AppType) {
|
async getLiveProviderSettings(appId: AppId) {
|
||||||
return await invoke("read_live_provider_settings", { app: appType });
|
return await invoke("read_live_provider_settings", { app: appId });
|
||||||
},
|
},
|
||||||
|
|
||||||
async testApiEndpoints(
|
async testApiEndpoints(
|
||||||
@@ -25,46 +25,46 @@ export const vscodeApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getCustomEndpoints(
|
async getCustomEndpoints(
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
providerId: string,
|
providerId: string,
|
||||||
): Promise<CustomEndpoint[]> {
|
): Promise<CustomEndpoint[]> {
|
||||||
return await invoke("get_custom_endpoints", {
|
return await invoke("get_custom_endpoints", {
|
||||||
app: appType,
|
app: appId,
|
||||||
provider_id: providerId,
|
provider_id: providerId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async addCustomEndpoint(
|
async addCustomEndpoint(
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
providerId: string,
|
providerId: string,
|
||||||
url: string,
|
url: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await invoke("add_custom_endpoint", {
|
await invoke("add_custom_endpoint", {
|
||||||
app: appType,
|
app: appId,
|
||||||
provider_id: providerId,
|
provider_id: providerId,
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async removeCustomEndpoint(
|
async removeCustomEndpoint(
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
providerId: string,
|
providerId: string,
|
||||||
url: string,
|
url: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await invoke("remove_custom_endpoint", {
|
await invoke("remove_custom_endpoint", {
|
||||||
app: appType,
|
app: appId,
|
||||||
provider_id: providerId,
|
provider_id: providerId,
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateEndpointLastUsed(
|
async updateEndpointLastUsed(
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
providerId: string,
|
providerId: string,
|
||||||
url: string,
|
url: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await invoke("update_endpoint_last_used", {
|
await invoke("update_endpoint_last_used", {
|
||||||
app: appType,
|
app: appId,
|
||||||
provider_id: providerId,
|
provider_id: providerId,
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { providersApi, settingsApi, type AppType } from "@/lib/api";
|
import { providersApi, settingsApi, type AppId } from "@/lib/api";
|
||||||
import type { Provider, Settings } from "@/types";
|
import type { Provider, Settings } from "@/types";
|
||||||
|
|
||||||
export const useAddProviderMutation = (appType: AppType) => {
|
export const useAddProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -15,11 +15,11 @@ export const useAddProviderMutation = (appType: AppType) => {
|
|||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
};
|
};
|
||||||
await providersApi.add(newProvider, appType);
|
await providersApi.add(newProvider, appId);
|
||||||
return newProvider;
|
return newProvider;
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: ["providers", appType] });
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
||||||
await providersApi.updateTrayMenu();
|
await providersApi.updateTrayMenu();
|
||||||
toast.success(
|
toast.success(
|
||||||
t("notifications.providerAdded", {
|
t("notifications.providerAdded", {
|
||||||
@@ -38,17 +38,17 @@ export const useAddProviderMutation = (appType: AppType) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateProviderMutation = (appType: AppType) => {
|
export const useUpdateProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (provider: Provider) => {
|
mutationFn: async (provider: Provider) => {
|
||||||
await providersApi.update(provider, appType);
|
await providersApi.update(provider, appId);
|
||||||
return provider;
|
return provider;
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: ["providers", appType] });
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
||||||
toast.success(
|
toast.success(
|
||||||
t("notifications.updateSuccess", {
|
t("notifications.updateSuccess", {
|
||||||
defaultValue: "供应商更新成功",
|
defaultValue: "供应商更新成功",
|
||||||
@@ -66,16 +66,16 @@ export const useUpdateProviderMutation = (appType: AppType) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDeleteProviderMutation = (appType: AppType) => {
|
export const useDeleteProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (providerId: string) => {
|
mutationFn: async (providerId: string) => {
|
||||||
await providersApi.delete(providerId, appType);
|
await providersApi.delete(providerId, appId);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: ["providers", appType] });
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
||||||
await providersApi.updateTrayMenu();
|
await providersApi.updateTrayMenu();
|
||||||
toast.success(
|
toast.success(
|
||||||
t("notifications.deleteSuccess", {
|
t("notifications.deleteSuccess", {
|
||||||
@@ -94,21 +94,21 @@ export const useDeleteProviderMutation = (appType: AppType) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSwitchProviderMutation = (appType: AppType) => {
|
export const useSwitchProviderMutation = (appId: AppId) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async (providerId: string) => {
|
mutationFn: async (providerId: string) => {
|
||||||
return await providersApi.switch(providerId, appType);
|
return await providersApi.switch(providerId, appId);
|
||||||
},
|
},
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.invalidateQueries({ queryKey: ["providers", appType] });
|
await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
|
||||||
await providersApi.updateTrayMenu();
|
await providersApi.updateTrayMenu();
|
||||||
toast.success(
|
toast.success(
|
||||||
t("notifications.switchSuccess", {
|
t("notifications.switchSuccess", {
|
||||||
defaultValue: "切换供应商成功",
|
defaultValue: "切换供应商成功",
|
||||||
appName: t(`apps.${appType}`, { defaultValue: appType }),
|
appName: t(`apps.${appId}`, { defaultValue: appId }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
type UseQueryResult,
|
type UseQueryResult,
|
||||||
keepPreviousData,
|
keepPreviousData,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { providersApi, settingsApi, usageApi, type AppType } from "@/lib/api";
|
import { providersApi, settingsApi, usageApi, type AppId } from "@/lib/api";
|
||||||
import type { Provider, Settings, UsageResult } from "@/types";
|
import type { Provider, Settings, UsageResult } from "@/types";
|
||||||
|
|
||||||
const sortProviders = (
|
const sortProviders = (
|
||||||
@@ -35,33 +35,33 @@ export interface ProvidersQueryData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useProvidersQuery = (
|
export const useProvidersQuery = (
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
): UseQueryResult<ProvidersQueryData> => {
|
): UseQueryResult<ProvidersQueryData> => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["providers", appType],
|
queryKey: ["providers", appId],
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
let providers: Record<string, Provider> = {};
|
let providers: Record<string, Provider> = {};
|
||||||
let currentProviderId = "";
|
let currentProviderId = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
providers = await providersApi.getAll(appType);
|
providers = await providersApi.getAll(appId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取供应商列表失败:", error);
|
console.error("获取供应商列表失败:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentProviderId = await providersApi.getCurrent(appType);
|
currentProviderId = await providersApi.getCurrent(appId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取当前供应商失败:", error);
|
console.error("获取当前供应商失败:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(providers).length === 0) {
|
if (Object.keys(providers).length === 0) {
|
||||||
try {
|
try {
|
||||||
const success = await providersApi.importDefault(appType);
|
const success = await providersApi.importDefault(appId);
|
||||||
if (success) {
|
if (success) {
|
||||||
providers = await providersApi.getAll(appType);
|
providers = await providersApi.getAll(appId);
|
||||||
currentProviderId = await providersApi.getCurrent(appType);
|
currentProviderId = await providersApi.getCurrent(appId);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("导入默认配置失败:", error);
|
console.error("导入默认配置失败:", error);
|
||||||
@@ -85,12 +85,12 @@ export const useSettingsQuery = (): UseQueryResult<Settings> => {
|
|||||||
|
|
||||||
export const useUsageQuery = (
|
export const useUsageQuery = (
|
||||||
providerId: string,
|
providerId: string,
|
||||||
appType: AppType,
|
appId: AppId,
|
||||||
enabled: boolean = true,
|
enabled: boolean = true,
|
||||||
): UseQueryResult<UsageResult> => {
|
): UseQueryResult<UsageResult> => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["usage", providerId, appType],
|
queryKey: ["usage", providerId, appId],
|
||||||
queryFn: async () => usageApi.query(providerId, appType),
|
queryFn: async () => usageApi.query(providerId, appId),
|
||||||
enabled: enabled && !!providerId,
|
enabled: enabled && !!providerId,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
staleTime: 5 * 60 * 1000, // 5分钟
|
staleTime: 5 * 60 * 1000, // 5分钟
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ describe("AddProviderDialog", () => {
|
|||||||
<AddProviderDialog
|
<AddProviderDialog
|
||||||
open
|
open
|
||||||
onOpenChange={handleOpenChange}
|
onOpenChange={handleOpenChange}
|
||||||
appType="claude"
|
appId="claude"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
@@ -97,7 +97,7 @@ describe("AddProviderDialog", () => {
|
|||||||
<AddProviderDialog
|
<AddProviderDialog
|
||||||
open
|
open
|
||||||
onOpenChange={vi.fn()}
|
onOpenChange={vi.fn()}
|
||||||
appType="claude"
|
appId="claude"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ const renderForm = (props?: Partial<React.ComponentProps<typeof McpFormModal>>)
|
|||||||
const onClose = overrideOnClose ?? vi.fn();
|
const onClose = overrideOnClose ?? vi.fn();
|
||||||
render(
|
render(
|
||||||
<McpFormModal
|
<McpFormModal
|
||||||
appType="claude"
|
appId="claude"
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
existingIds={[]}
|
existingIds={[]}
|
||||||
@@ -260,7 +260,7 @@ const renderForm = (props?: Partial<React.ComponentProps<typeof McpFormModal>>)
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("TOML 模式下自动提取 ID 并成功保存", async () => {
|
it("TOML 模式下自动提取 ID 并成功保存", async () => {
|
||||||
const { onSave } = renderForm({ appType: "codex" });
|
const { onSave } = renderForm({ appId: "codex" });
|
||||||
|
|
||||||
const configTextarea = screen.getByPlaceholderText(
|
const configTextarea = screen.getByPlaceholderText(
|
||||||
"mcp.form.tomlPlaceholder",
|
"mcp.form.tomlPlaceholder",
|
||||||
@@ -288,7 +288,7 @@ command = "run"
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("TOML 模式下缺少命令时展示错误提示并阻止提交", async () => {
|
it("TOML 模式下缺少命令时展示错误提示并阻止提交", async () => {
|
||||||
const { onSave } = renderForm({ appType: "codex" });
|
const { onSave } = renderForm({ appId: "codex" });
|
||||||
|
|
||||||
const configTextarea = screen.getByPlaceholderText(
|
const configTextarea = screen.getByPlaceholderText(
|
||||||
"mcp.form.tomlPlaceholder",
|
"mcp.form.tomlPlaceholder",
|
||||||
@@ -319,7 +319,7 @@ type = "stdio"
|
|||||||
} as McpServer;
|
} as McpServer;
|
||||||
|
|
||||||
const { onSave } = renderForm({
|
const { onSave } = renderForm({
|
||||||
appType: "claude",
|
appId: "claude",
|
||||||
editingId: "existing",
|
editingId: "existing",
|
||||||
initialData,
|
initialData,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ describe("ProviderList Component", () => {
|
|||||||
<ProviderList
|
<ProviderList
|
||||||
providers={{}}
|
providers={{}}
|
||||||
currentProviderId=""
|
currentProviderId=""
|
||||||
appType="claude"
|
appId="claude"
|
||||||
onSwitch={vi.fn()}
|
onSwitch={vi.fn()}
|
||||||
onEdit={vi.fn()}
|
onEdit={vi.fn()}
|
||||||
onDelete={vi.fn()}
|
onDelete={vi.fn()}
|
||||||
@@ -150,7 +150,7 @@ describe("ProviderList Component", () => {
|
|||||||
<ProviderList
|
<ProviderList
|
||||||
providers={{}}
|
providers={{}}
|
||||||
currentProviderId=""
|
currentProviderId=""
|
||||||
appType="claude"
|
appId="claude"
|
||||||
onSwitch={vi.fn()}
|
onSwitch={vi.fn()}
|
||||||
onEdit={vi.fn()}
|
onEdit={vi.fn()}
|
||||||
onDelete={vi.fn()}
|
onDelete={vi.fn()}
|
||||||
@@ -189,7 +189,7 @@ describe("ProviderList Component", () => {
|
|||||||
<ProviderList
|
<ProviderList
|
||||||
providers={{ a: providerA, b: providerB }}
|
providers={{ a: providerA, b: providerB }}
|
||||||
currentProviderId="b"
|
currentProviderId="b"
|
||||||
appType="claude"
|
appId="claude"
|
||||||
isEditMode
|
isEditMode
|
||||||
onSwitch={handleSwitch}
|
onSwitch={handleSwitch}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ vi.mock("@/components/providers/ProviderList", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/components/providers/AddProviderDialog", () => ({
|
vi.mock("@/components/providers/AddProviderDialog", () => ({
|
||||||
AddProviderDialog: ({ open, onOpenChange, onSubmit, appType }: any) =>
|
AddProviderDialog: ({ open, onOpenChange, onSubmit, appId }: any) =>
|
||||||
open ? (
|
open ? (
|
||||||
<div data-testid="add-provider-dialog">
|
<div data-testid="add-provider-dialog">
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onSubmit({
|
onSubmit({
|
||||||
name: `New ${appType} Provider`,
|
name: `New ${appId} Provider`,
|
||||||
settingsConfig: {},
|
settingsConfig: {},
|
||||||
category: "custom",
|
category: "custom",
|
||||||
sortIndex: 99,
|
sortIndex: 99,
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ const renderPanel = (props?: Partial<React.ComponentProps<typeof McpPanel>>) =>
|
|||||||
const client = createTestQueryClient();
|
const client = createTestQueryClient();
|
||||||
return render(
|
return render(
|
||||||
<QueryClientProvider client={client}>
|
<QueryClientProvider client={client}>
|
||||||
<McpPanel open onOpenChange={() => {}} appType="claude" {...props} />
|
<McpPanel open onOpenChange={() => {}} appId="claude" {...props} />
|
||||||
</QueryClientProvider>,
|
</QueryClientProvider>,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -229,4 +229,3 @@ describe("McpPanel integration", () => {
|
|||||||
await waitFor(() => expect(deleteServerMock).toHaveBeenCalledWith("sample"));
|
await waitFor(() => expect(deleteServerMock).toHaveBeenCalledWith("sample"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { http, HttpResponse } from "msw";
|
import { http, HttpResponse } from "msw";
|
||||||
import type { AppType } from "@/lib/api/types";
|
import type { AppId } from "@/lib/api/types";
|
||||||
import type { McpServer, Provider, Settings } from "@/types";
|
import type { McpServer, Provider, Settings } from "@/types";
|
||||||
import {
|
import {
|
||||||
addProvider,
|
addProvider,
|
||||||
@@ -37,19 +37,19 @@ const success = <T>(payload: T) => HttpResponse.json(payload as any);
|
|||||||
|
|
||||||
export const handlers = [
|
export const handlers = [
|
||||||
http.post(`${TAURI_ENDPOINT}/get_providers`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/get_providers`, async ({ request }) => {
|
||||||
const { app } = await withJson<{ app: AppType }>(request);
|
const { app } = await withJson<{ app: AppId }>(request);
|
||||||
return success(getProviders(app));
|
return success(getProviders(app));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/get_current_provider`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/get_current_provider`, async ({ request }) => {
|
||||||
const { app } = await withJson<{ app: AppType }>(request);
|
const { app } = await withJson<{ app: AppId }>(request);
|
||||||
return success(getCurrentProviderId(app));
|
return success(getCurrentProviderId(app));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/update_providers_sort_order`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/update_providers_sort_order`, async ({ request }) => {
|
||||||
const { updates = [], app } = await withJson<{
|
const { updates = [], app } = await withJson<{
|
||||||
updates: { id: string; sortIndex: number }[];
|
updates: { id: string; sortIndex: number }[];
|
||||||
app: AppType;
|
app: AppId;
|
||||||
}>(request);
|
}>(request);
|
||||||
updateSortOrder(app, updates);
|
updateSortOrder(app, updates);
|
||||||
return success(true);
|
return success(true);
|
||||||
@@ -58,7 +58,7 @@ export const handlers = [
|
|||||||
http.post(`${TAURI_ENDPOINT}/update_tray_menu`, () => success(true)),
|
http.post(`${TAURI_ENDPOINT}/update_tray_menu`, () => success(true)),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/switch_provider`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/switch_provider`, async ({ request }) => {
|
||||||
const { id, app } = await withJson<{ id: string; app: AppType }>(request);
|
const { id, app } = await withJson<{ id: string; app: AppId }>(request);
|
||||||
const providers = listProviders(app);
|
const providers = listProviders(app);
|
||||||
if (!providers[id]) {
|
if (!providers[id]) {
|
||||||
return HttpResponse.json(false, { status: 404 });
|
return HttpResponse.json(false, { status: 404 });
|
||||||
@@ -70,7 +70,7 @@ export const handlers = [
|
|||||||
http.post(`${TAURI_ENDPOINT}/add_provider`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/add_provider`, async ({ request }) => {
|
||||||
const { provider, app } = await withJson<{
|
const { provider, app } = await withJson<{
|
||||||
provider: Provider & { id?: string };
|
provider: Provider & { id?: string };
|
||||||
app: AppType;
|
app: AppId;
|
||||||
}>(request);
|
}>(request);
|
||||||
|
|
||||||
const newId = provider.id ?? `mock-${Date.now()}`;
|
const newId = provider.id ?? `mock-${Date.now()}`;
|
||||||
@@ -81,14 +81,14 @@ export const handlers = [
|
|||||||
http.post(`${TAURI_ENDPOINT}/update_provider`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/update_provider`, async ({ request }) => {
|
||||||
const { provider, app } = await withJson<{
|
const { provider, app } = await withJson<{
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
app: AppType;
|
app: AppId;
|
||||||
}>(request);
|
}>(request);
|
||||||
updateProvider(app, provider);
|
updateProvider(app, provider);
|
||||||
return success(true);
|
return success(true);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/delete_provider`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/delete_provider`, async ({ request }) => {
|
||||||
const { id, app } = await withJson<{ id: string; app: AppType }>(request);
|
const { id, app } = await withJson<{ id: string; app: AppId }>(request);
|
||||||
deleteProvider(app, id);
|
deleteProvider(app, id);
|
||||||
return success(true);
|
return success(true);
|
||||||
}),
|
}),
|
||||||
@@ -102,7 +102,7 @@ export const handlers = [
|
|||||||
|
|
||||||
// MCP APIs
|
// MCP APIs
|
||||||
http.post(`${TAURI_ENDPOINT}/get_mcp_config`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/get_mcp_config`, async ({ request }) => {
|
||||||
const { app } = await withJson<{ app: AppType }>(request);
|
const { app } = await withJson<{ app: AppId }>(request);
|
||||||
return success(getMcpConfig(app));
|
return success(getMcpConfig(app));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ export const handlers = [
|
|||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/set_mcp_enabled`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/set_mcp_enabled`, async ({ request }) => {
|
||||||
const { app, id, enabled } = await withJson<{
|
const { app, id, enabled } = await withJson<{
|
||||||
app: AppType;
|
app: AppId;
|
||||||
id: string;
|
id: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}>(request);
|
}>(request);
|
||||||
@@ -121,7 +121,7 @@ export const handlers = [
|
|||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/upsert_mcp_server_in_config`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/upsert_mcp_server_in_config`, async ({ request }) => {
|
||||||
const { app, id, spec } = await withJson<{
|
const { app, id, spec } = await withJson<{
|
||||||
app: AppType;
|
app: AppId;
|
||||||
id: string;
|
id: string;
|
||||||
spec: McpServer;
|
spec: McpServer;
|
||||||
}>(request);
|
}>(request);
|
||||||
@@ -130,7 +130,7 @@ export const handlers = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/delete_mcp_server_in_config`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/delete_mcp_server_in_config`, async ({ request }) => {
|
||||||
const { app, id } = await withJson<{ app: AppType; id: string }>(request);
|
const { app, id } = await withJson<{ app: AppId; id: string }>(request);
|
||||||
deleteMcpServer(app, id);
|
deleteMcpServer(app, id);
|
||||||
return success(true);
|
return success(true);
|
||||||
}),
|
}),
|
||||||
@@ -162,7 +162,7 @@ export const handlers = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
http.post(`${TAURI_ENDPOINT}/get_config_dir`, async ({ request }) => {
|
http.post(`${TAURI_ENDPOINT}/get_config_dir`, async ({ request }) => {
|
||||||
const { app } = await withJson<{ app: AppType }>(request);
|
const { app } = await withJson<{ app: AppId }>(request);
|
||||||
return success(app === "claude" ? "/default/claude" : "/default/codex");
|
return success(app === "claude" ? "/default/claude" : "/default/codex");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { AppType } from "@/lib/api/types";
|
import type { AppId } from "@/lib/api/types";
|
||||||
import type { McpServer, Provider, Settings } from "@/types";
|
import type { McpServer, Provider, Settings } from "@/types";
|
||||||
|
|
||||||
type ProvidersByApp = Record<AppType, Record<string, Provider>>;
|
type ProvidersByApp = Record<AppId, Record<string, Provider>>;
|
||||||
type CurrentProviderState = Record<AppType, string>;
|
type CurrentProviderState = Record<AppId, string>;
|
||||||
type McpConfigState = Record<AppType, Record<string, McpServer>>;
|
type McpConfigState = Record<AppId, Record<string, McpServer>>;
|
||||||
|
|
||||||
const createDefaultProviders = (): ProvidersByApp => ({
|
const createDefaultProviders = (): ProvidersByApp => ({
|
||||||
claude: {
|
claude: {
|
||||||
@@ -126,29 +126,29 @@ export const resetProviderState = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getProviders = (appType: AppType) =>
|
export const getProviders = (appType: AppId) =>
|
||||||
cloneProviders(providers)[appType] ?? {};
|
cloneProviders(providers)[appType] ?? {};
|
||||||
|
|
||||||
export const getCurrentProviderId = (appType: AppType) => current[appType] ?? "";
|
export const getCurrentProviderId = (appType: AppId) => current[appType] ?? "";
|
||||||
|
|
||||||
export const setCurrentProviderId = (appType: AppType, providerId: string) => {
|
export const setCurrentProviderId = (appType: AppId, providerId: string) => {
|
||||||
current[appType] = providerId;
|
current[appType] = providerId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateProviders = (appType: AppType, data: Record<string, Provider>) => {
|
export const updateProviders = (appType: AppId, data: Record<string, Provider>) => {
|
||||||
providers[appType] = cloneProviders({ [appType]: data } as ProvidersByApp)[appType];
|
providers[appType] = cloneProviders({ [appType]: data } as ProvidersByApp)[appType];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setProviders = (appType: AppType, data: Record<string, Provider>) => {
|
export const setProviders = (appType: AppId, data: Record<string, Provider>) => {
|
||||||
providers[appType] = JSON.parse(JSON.stringify(data)) as Record<string, Provider>;
|
providers[appType] = JSON.parse(JSON.stringify(data)) as Record<string, Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addProvider = (appType: AppType, provider: Provider) => {
|
export const addProvider = (appType: AppId, provider: Provider) => {
|
||||||
providers[appType] = providers[appType] ?? {};
|
providers[appType] = providers[appType] ?? {};
|
||||||
providers[appType][provider.id] = provider;
|
providers[appType][provider.id] = provider;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateProvider = (appType: AppType, provider: Provider) => {
|
export const updateProvider = (appType: AppId, provider: Provider) => {
|
||||||
if (!providers[appType]) return;
|
if (!providers[appType]) return;
|
||||||
providers[appType][provider.id] = {
|
providers[appType][provider.id] = {
|
||||||
...providers[appType][provider.id],
|
...providers[appType][provider.id],
|
||||||
@@ -156,7 +156,7 @@ export const updateProvider = (appType: AppType, provider: Provider) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteProvider = (appType: AppType, providerId: string) => {
|
export const deleteProvider = (appType: AppId, providerId: string) => {
|
||||||
if (!providers[appType]) return;
|
if (!providers[appType]) return;
|
||||||
delete providers[appType][providerId];
|
delete providers[appType][providerId];
|
||||||
if (current[appType] === providerId) {
|
if (current[appType] === providerId) {
|
||||||
@@ -166,7 +166,7 @@ export const deleteProvider = (appType: AppType, providerId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const updateSortOrder = (
|
export const updateSortOrder = (
|
||||||
appType: AppType,
|
appType: AppId,
|
||||||
updates: { id: string; sortIndex: number }[],
|
updates: { id: string; sortIndex: number }[],
|
||||||
) => {
|
) => {
|
||||||
if (!providers[appType]) return;
|
if (!providers[appType]) return;
|
||||||
@@ -178,7 +178,7 @@ export const updateSortOrder = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listProviders = (appType: AppType) =>
|
export const listProviders = (appType: AppId) =>
|
||||||
JSON.parse(JSON.stringify(providers[appType] ?? {})) as Record<string, Provider>;
|
JSON.parse(JSON.stringify(providers[appType] ?? {})) as Record<string, Provider>;
|
||||||
|
|
||||||
export const getSettings = () => JSON.parse(JSON.stringify(settingsState)) as Settings;
|
export const getSettings = () => JSON.parse(JSON.stringify(settingsState)) as Settings;
|
||||||
@@ -193,7 +193,7 @@ export const setAppConfigDirOverrideState = (value: string | null) => {
|
|||||||
appConfigDirOverride = value;
|
appConfigDirOverride = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMcpConfig = (appType: AppType) => {
|
export const getMcpConfig = (appType: AppId) => {
|
||||||
const servers = JSON.parse(
|
const servers = JSON.parse(
|
||||||
JSON.stringify(mcpConfigs[appType] ?? {}),
|
JSON.stringify(mcpConfigs[appType] ?? {}),
|
||||||
) as Record<string, McpServer>;
|
) as Record<string, McpServer>;
|
||||||
@@ -203,12 +203,12 @@ export const getMcpConfig = (appType: AppType) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setMcpConfig = (appType: AppType, value: Record<string, McpServer>) => {
|
export const setMcpConfig = (appType: AppId, value: Record<string, McpServer>) => {
|
||||||
mcpConfigs[appType] = JSON.parse(JSON.stringify(value)) as Record<string, McpServer>;
|
mcpConfigs[appType] = JSON.parse(JSON.stringify(value)) as Record<string, McpServer>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setMcpServerEnabled = (
|
export const setMcpServerEnabled = (
|
||||||
appType: AppType,
|
appType: AppId,
|
||||||
id: string,
|
id: string,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
) => {
|
) => {
|
||||||
@@ -220,7 +220,7 @@ export const setMcpServerEnabled = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const upsertMcpServer = (
|
export const upsertMcpServer = (
|
||||||
appType: AppType,
|
appType: AppId,
|
||||||
id: string,
|
id: string,
|
||||||
server: McpServer,
|
server: McpServer,
|
||||||
) => {
|
) => {
|
||||||
@@ -230,7 +230,7 @@ export const upsertMcpServer = (
|
|||||||
mcpConfigs[appType][id] = JSON.parse(JSON.stringify(server)) as McpServer;
|
mcpConfigs[appType][id] = JSON.parse(JSON.stringify(server)) as McpServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteMcpServer = (appType: AppType, id: string) => {
|
export const deleteMcpServer = (appType: AppId, id: string) => {
|
||||||
if (!mcpConfigs[appType]) return;
|
if (!mcpConfigs[appType]) return;
|
||||||
delete mcpConfigs[appType][id];
|
delete mcpConfigs[appType][id];
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user