From b3b3c0732acf47f73adbdb1c40fc2d759a08ad57 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Mon, 26 Jan 2026 14:09:39 +0800 Subject: [PATCH] feat(ui): add merge preview to common config editor components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add real-time merge preview functionality to all config editor components (Claude, Codex, Gemini) that shows the final merged configuration when common config is enabled. UI changes: - Add finalConfig/finalEnv prop to display merged result - Add toggle button to show/hide merge preview (Eye/EyeOff icons) - Auto-show preview when common config is enabled - Display custom config editor with label when preview is visible - Show read-only merged preview below custom config - Add visual indicators: "只读" badge, green hint text The preview helps users understand how their custom configuration combines with the shared common config template before saving. --- .../providers/forms/CodexConfigEditor.tsx | 5 + .../providers/forms/CodexConfigSections.tsx | 100 +++++++++++++++-- .../providers/forms/CommonConfigEditor.tsx | 101 ++++++++++++++++-- .../providers/forms/GeminiConfigEditor.tsx | 4 + .../providers/forms/GeminiConfigSections.tsx | 100 +++++++++++++++-- 5 files changed, 279 insertions(+), 31 deletions(-) diff --git a/src/components/providers/forms/CodexConfigEditor.tsx b/src/components/providers/forms/CodexConfigEditor.tsx index 42ba3cae3..b3f4d14a5 100644 --- a/src/components/providers/forms/CodexConfigEditor.tsx +++ b/src/components/providers/forms/CodexConfigEditor.tsx @@ -30,6 +30,9 @@ interface CodexConfigEditorProps { onExtract?: () => void; isExtracting?: boolean; + + /** 最终合并后的配置(只读预览) */ + finalConfig?: string; } const CodexConfigEditor: React.FC = ({ @@ -47,6 +50,7 @@ const CodexConfigEditor: React.FC = ({ configError, onExtract, isExtracting, + finalConfig, }) => { const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false); @@ -76,6 +80,7 @@ const CodexConfigEditor: React.FC = ({ onEditCommonConfig={() => setIsCommonConfigModalOpen(true)} commonConfigError={commonConfigError} configError={configError} + finalConfig={finalConfig} /> {/* Common Config Modal */} diff --git a/src/components/providers/forms/CodexConfigSections.tsx b/src/components/providers/forms/CodexConfigSections.tsx index 99ada4273..8077a97ea 100644 --- a/src/components/providers/forms/CodexConfigSections.tsx +++ b/src/components/providers/forms/CodexConfigSections.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { Eye, EyeOff } from "lucide-react"; import JsonEditor from "@/components/JsonEditor"; +import { Label } from "@/components/ui/label"; interface CodexAuthSectionProps { value: string; @@ -83,6 +85,8 @@ interface CodexConfigSectionProps { onEditCommonConfig: () => void; commonConfigError?: string; configError?: string; + /** 最终合并后的配置(只读预览) */ + finalConfig?: string; } /** @@ -96,9 +100,11 @@ export const CodexConfigSection: React.FC = ({ onEditCommonConfig, commonConfigError, configError, + finalConfig, }) => { const { t } = useTranslation(); const [isDarkMode, setIsDarkMode] = useState(false); + const [showPreview, setShowPreview] = useState(false); useEffect(() => { setIsDarkMode(document.documentElement.classList.contains("dark")); @@ -115,6 +121,13 @@ export const CodexConfigSection: React.FC = ({ return () => observer.disconnect(); }, []); + // 当启用通用配置时,自动显示预览 + useEffect(() => { + if (useCommonConfig && finalConfig) { + setShowPreview(true); + } + }, [useCommonConfig, finalConfig]); + return (
@@ -136,7 +149,32 @@ export const CodexConfigSection: React.FC = ({
-
+
+
+ {useCommonConfig && finalConfig && ( + + )} +
-
+
+
+ {useCommonConfig && finalConfig && ( + + )} +
+ + {/* 合并预览(只读)- 放在自定义配置下面 */} + {useCommonConfig && showPreview && finalConfig && ( +
+
+ + + {t("claudeConfig.mergedPreviewHint", { + defaultValue: "通用配置 + 自定义配置 = 最终配置", + })} + +
+
+ {}} // 只读 + darkMode={isDarkMode} + rows={8} + showValidation={false} + language="json" + readOnly={true} + /> +
+ {t("common.readonly", { defaultValue: "只读" })} +
+
+
+ )}
void; isExtracting?: boolean; + /** 最终合并后的 env 配置(只读预览) */ + finalEnv?: string; } const GeminiConfigEditor: React.FC = ({ @@ -34,6 +36,7 @@ const GeminiConfigEditor: React.FC = ({ configError, onExtract, isExtracting, + finalEnv, }) => { const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false); @@ -56,6 +59,7 @@ const GeminiConfigEditor: React.FC = ({ onCommonConfigToggle={onCommonConfigToggle} onEditCommonConfig={() => setIsCommonConfigModalOpen(true)} commonConfigError={commonConfigError} + finalEnv={finalEnv} /> {/* Config JSON Section */} diff --git a/src/components/providers/forms/GeminiConfigSections.tsx b/src/components/providers/forms/GeminiConfigSections.tsx index 999f986dc..4ae74d67b 100644 --- a/src/components/providers/forms/GeminiConfigSections.tsx +++ b/src/components/providers/forms/GeminiConfigSections.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import { Eye, EyeOff } from "lucide-react"; import JsonEditor from "@/components/JsonEditor"; +import { Label } from "@/components/ui/label"; interface GeminiEnvSectionProps { value: string; @@ -11,6 +13,8 @@ interface GeminiEnvSectionProps { onCommonConfigToggle: (checked: boolean) => void; onEditCommonConfig: () => void; commonConfigError?: string; + /** 最终合并后的 env 配置(只读预览) */ + finalEnv?: string; } /** @@ -25,9 +29,11 @@ export const GeminiEnvSection: React.FC = ({ onCommonConfigToggle, onEditCommonConfig, commonConfigError, + finalEnv, }) => { const { t } = useTranslation(); const [isDarkMode, setIsDarkMode] = useState(false); + const [showPreview, setShowPreview] = useState(false); useEffect(() => { setIsDarkMode(document.documentElement.classList.contains("dark")); @@ -44,6 +50,13 @@ export const GeminiEnvSection: React.FC = ({ return () => observer.disconnect(); }, []); + // 当启用通用配置时,自动显示预览 + useEffect(() => { + if (useCommonConfig && finalEnv) { + setShowPreview(true); + } + }, [useCommonConfig, finalEnv]); + const handleChange = (newValue: string) => { onChange(newValue); if (onBlur) { @@ -74,7 +87,32 @@ export const GeminiEnvSection: React.FC = ({
-
+
+
+ {useCommonConfig && finalEnv && ( + + )} +
+ + {/* 合并预览(只读)- 放在自定义配置下面 */} + {useCommonConfig && showPreview && finalEnv && ( +
+
+ + + {t("geminiConfig.mergedPreviewHint", { + defaultValue: "通用配置 + 自定义配置 = 最终配置", + })} + +
+
+ {}} // 只读 + darkMode={isDarkMode} + rows={4} + showValidation={false} + language="javascript" + readOnly={true} + /> +
+ {t("common.readonly", { defaultValue: "只读" })} +
+
+
+ )} {error && (

{error}