From 5427ae04e48e4a429afa691320adf4ebafe30099 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 14 Oct 2025 10:22:57 +0800 Subject: [PATCH] feat(mcp): add overwrite warning and improve sync option visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move sync checkbox to footer alongside save/cancel buttons for better visibility - Add real-time conflict detection to check for duplicate MCP IDs on other side - Display amber warning icon when sync would overwrite existing config - Add i18n keys for overwrite warning (zh: "将覆盖 {{target}} 中的同名配置") - Update UI layout to use justify-between for better spacing --- src/components/mcp/McpFormModal.tsx | 119 ++++++++++++++++++---------- src/i18n/locales/en.json | 3 +- src/i18n/locales/zh.json | 3 +- 3 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/components/mcp/McpFormModal.tsx b/src/components/mcp/McpFormModal.tsx index 35868e9..4ab0ff3 100644 --- a/src/components/mcp/McpFormModal.tsx +++ b/src/components/mcp/McpFormModal.tsx @@ -1,6 +1,6 @@ -import React, { useMemo, useState } from "react"; +import React, { useMemo, useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { X, Save, AlertCircle, ChevronDown, ChevronUp } from "lucide-react"; +import { X, Save, AlertCircle, ChevronDown, ChevronUp, AlertTriangle } from "lucide-react"; import { McpServer, McpServerSpec } from "../../types"; import { mcpPresets, @@ -118,16 +118,40 @@ const McpFormModal: React.FC = ({ const [isWizardOpen, setIsWizardOpen] = useState(false); const [idError, setIdError] = useState(""); const [syncOtherSide, setSyncOtherSide] = useState(false); + const [otherSideHasConflict, setOtherSideHasConflict] = useState(false); // 判断是否使用 TOML 格式 const useToml = appType === "codex"; const syncTargetLabel = appType === "claude" ? t("apps.codex") : t("apps.claude"); + const otherAppType: AppType = appType === "claude" ? "codex" : "claude"; const syncCheckboxId = useMemo( () => `sync-other-side-${appType}`, [appType], ); + // 检测另一侧是否有同名 MCP + useEffect(() => { + const checkOtherSide = async () => { + const currentId = formId.trim(); + if (!currentId) { + setOtherSideHasConflict(false); + return; + } + + try { + const otherConfig = await window.api.getMcpConfig(otherAppType); + const hasConflict = Object.keys(otherConfig.servers || {}).includes(currentId); + setOtherSideHasConflict(hasConflict); + } catch (error) { + console.error("检查另一侧 MCP 配置失败:", error); + setOtherSideHasConflict(false); + } + }; + + checkOtherSide(); + }, [formId, otherAppType]); + const wizardInitialSpec = useMemo(() => { const fallback = initialData?.server; if (!formConfig.trim()) { @@ -666,50 +690,59 @@ const McpFormModal: React.FC = ({ )} - - {/* 双端同步选项 */} -
- setSyncOtherSide(event.target.checked)} - /> - -
{/* Footer */} -
- - +
+ {/* 双端同步选项 */} +
+
+ setSyncOtherSide(event.target.checked)} + /> + +
+ {syncOtherSide && otherSideHasConflict && ( +
+ + + {t("mcp.form.willOverwriteWarning", { target: syncTargetLabel })} + +
+ )} +
+ + {/* 操作按钮 */} +
+ + +
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index a5c737f..4701b22 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -300,7 +300,8 @@ "tomlPlaceholder": "type = \"stdio\"\ncommand = \"uvx\"\nargs = [\"mcp-server-fetch\"]", "useWizard": "Config Wizard", "syncOtherSide": "Mirror to {{target}}", - "syncOtherSideHint": "Apply the same settings to {{target}}; existing entries with the same id will be overwritten." + "syncOtherSideHint": "Apply the same settings to {{target}}; existing entries with the same id will be overwritten.", + "willOverwriteWarning": "Will overwrite existing config in {{target}}" }, "wizard": { "title": "MCP Configuration Wizard", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 0f855a4..5ed5204 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -300,7 +300,8 @@ "tomlPlaceholder": "type = \"stdio\"\ncommand = \"uvx\"\nargs = [\"mcp-server-fetch\"]", "useWizard": "配置向导", "syncOtherSide": "同步到 {{target}}", - "syncOtherSideHint": "勾选后会把当前配置同时写入 {{target}},若存在同名配置将被覆盖" + "syncOtherSideHint": "勾选后会把当前配置同时写入 {{target}},若存在同名配置将被覆盖", + "willOverwriteWarning": "将覆盖 {{target}} 中的同名配置" }, "wizard": { "title": "MCP 配置向导",