From e4df1a32a5391d441730c8ebc9f9975268341bc3 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 15 Jan 2026 19:37:35 +0800 Subject: [PATCH] feat(opencode): implement isInConfig semantics for additive provider management OpenCode uses additive provider management where providers can exist in the database but not necessarily in the live opencode.json config. This commit implements proper isInConfig state: Backend: - Add get_opencode_live_provider_ids command to query live config Frontend: - Add getOpenCodeLiveProviderIds API method - ProviderList queries live provider IDs and computes isInConfig - ProviderCard receives and passes isInConfig to ProviderActions - ProviderActions already handles the add/remove button logic --- src-tauri/src/commands/provider.rs | 10 ++++++++++ src-tauri/src/lib.rs | 1 + src/components/providers/ProviderCard.tsx | 4 +++- src/components/providers/ProviderList.tsx | 22 ++++++++++++++++++++++ src/lib/api/providers.ts | 8 ++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/commands/provider.rs b/src-tauri/src/commands/provider.rs index 7746318d3..30b8cfd8e 100644 --- a/src-tauri/src/commands/provider.rs +++ b/src-tauri/src/commands/provider.rs @@ -339,3 +339,13 @@ pub fn import_opencode_providers_from_live(state: State<'_, AppState>) -> Result crate::services::provider::import_opencode_providers_from_live(state.inner()) .map_err(|e| e.to_string()) } + +/// 获取 OpenCode live 配置中的供应商 ID 列表 +/// +/// 用于前端判断供应商是否已添加到 opencode.json +#[tauri::command] +pub fn get_opencode_live_provider_ids() -> Result, String> { + crate::opencode_config::get_providers() + .map(|providers| providers.keys().cloned().collect()) + .map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ceab5e326..43bdbe009 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -877,6 +877,7 @@ pub fn run() { commands::sync_universal_provider, // OpenCode specific commands::import_opencode_providers_from_live, + commands::get_opencode_live_provider_ids, // Global upstream proxy commands::get_global_proxy_url, commands::set_global_proxy_url, diff --git a/src/components/providers/ProviderCard.tsx b/src/components/providers/ProviderCard.tsx index 2b3f9dc82..c661172a3 100644 --- a/src/components/providers/ProviderCard.tsx +++ b/src/components/providers/ProviderCard.tsx @@ -26,6 +26,7 @@ interface ProviderCardProps { provider: Provider; isCurrent: boolean; appId: AppId; + isInConfig?: boolean; // OpenCode: 是否已添加到 opencode.json onSwitch: (provider: Provider) => void; onEdit: (provider: Provider) => void; onDelete: (provider: Provider) => void; @@ -85,6 +86,7 @@ export function ProviderCard({ provider, isCurrent, appId, + isInConfig = true, onSwitch, onEdit, onDelete, @@ -362,7 +364,7 @@ export function ProviderCard({ onSwitch(provider)} diff --git a/src/components/providers/ProviderList.tsx b/src/components/providers/ProviderList.tsx index 1203cb7d3..4baed18e9 100644 --- a/src/components/providers/ProviderList.tsx +++ b/src/components/providers/ProviderList.tsx @@ -15,8 +15,10 @@ import { import { AnimatePresence, motion } from "framer-motion"; import { Search, X } from "lucide-react"; import { useTranslation } from "react-i18next"; +import { useQuery } from "@tanstack/react-query"; import type { Provider } from "@/types"; import type { AppId } from "@/lib/api"; +import { providersApi } from "@/lib/api/providers"; import { useDragSort } from "@/hooks/useDragSort"; import { useStreamCheck } from "@/hooks/useStreamCheck"; import { ProviderCard } from "@/components/providers/ProviderCard"; @@ -72,6 +74,22 @@ export function ProviderList({ appId, ); + // OpenCode: 查询 live 配置中的供应商 ID 列表,用于判断 isInConfig + const { data: opencodeLiveIds } = useQuery({ + queryKey: ["opencodeLiveProviderIds"], + queryFn: () => providersApi.getOpenCodeLiveProviderIds(), + enabled: appId === "opencode", + }); + + // OpenCode: 判断供应商是否已添加到 opencode.json + const isProviderInConfig = useCallback( + (providerId: string): boolean => { + if (appId !== "opencode") return true; // 非 OpenCode 应用始终返回 true + return opencodeLiveIds?.includes(providerId) ?? false; + }, + [appId, opencodeLiveIds], + ); + // 流式健康检查 const { checkProvider, isChecking } = useStreamCheck(appId); @@ -199,6 +217,7 @@ export function ProviderList({ provider={provider} isCurrent={provider.id === currentProviderId} appId={appId} + isInConfig={isProviderInConfig(provider.id)} onSwitch={onSwitch} onEdit={onEdit} onDelete={onDelete} @@ -308,6 +327,7 @@ interface SortableProviderCardProps { provider: Provider; isCurrent: boolean; appId: AppId; + isInConfig: boolean; onSwitch: (provider: Provider) => void; onEdit: (provider: Provider) => void; onDelete: (provider: Provider) => void; @@ -331,6 +351,7 @@ function SortableProviderCard({ provider, isCurrent, appId, + isInConfig, onSwitch, onEdit, onDelete, @@ -368,6 +389,7 @@ function SortableProviderCard({ provider={provider} isCurrent={isCurrent} appId={appId} + isInConfig={isInConfig} onSwitch={onSwitch} onEdit={onEdit} onDelete={onDelete} diff --git a/src/lib/api/providers.ts b/src/lib/api/providers.ts index 3e9d371b9..0c1b76ef4 100644 --- a/src/lib/api/providers.ts +++ b/src/lib/api/providers.ts @@ -82,6 +82,14 @@ export const providersApi = { async importOpenCodeFromLive(): Promise { return await invoke("import_opencode_providers_from_live"); }, + + /** + * 获取 OpenCode live 配置中的供应商 ID 列表 + * 用于前端判断供应商是否已添加到 opencode.json + */ + async getOpenCodeLiveProviderIds(): Promise { + return await invoke("get_opencode_live_provider_ids"); + }, }; // ============================================================================