From 7a694fbcb026fa49aba5945378e9ad52e5367690 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 21 Oct 2025 10:49:30 +0800 Subject: [PATCH] refactor: replace CodeMirror with plain textarea in config editors Replace JsonEditor (CodeMirror) with plain HTML textarea elements in Claude config editors to maintain consistency with Codex config editors and eliminate theme conflicts. Changes: - Replace JsonEditor with textarea in ClaudeConfigEditor.tsx - Replace JsonEditor with textarea in CommonConfigEditor.tsx - Remove unnecessary dark mode detection logic - Remove wrapper div with border class that caused double borders - Apply unified border design system (border-border-default) - Keep JsonEditor in UsageScriptModal for JavaScript code editing Benefits: - Eliminates CodeMirror theme conflicts with oneDark - Simplifies codebase by removing unnecessary abstractions - Consistent styling across all config input fields - Automatic theme adaptation via CSS variables --- src/components/JsonEditor.tsx | 36 ++++++++--- .../providers/forms/ClaudeConfigEditor.tsx | 61 ++++++++---------- .../providers/forms/CommonConfigEditor.tsx | 64 ++++++++++--------- 3 files changed, 88 insertions(+), 73 deletions(-) diff --git a/src/components/JsonEditor.tsx b/src/components/JsonEditor.tsx index d24184a..8f16ca7 100644 --- a/src/components/JsonEditor.tsx +++ b/src/components/JsonEditor.tsx @@ -78,6 +78,20 @@ const JsonEditor: React.FC = ({ // 创建编辑器扩展 const minHeightPx = height ? undefined : Math.max(1, rows) * 18; + + // 使用 baseTheme 定义基础样式,优先级低于 oneDark,但可以正确响应主题 + const baseTheme = EditorView.baseTheme({ + "&light .cm-editor, &dark .cm-editor": { + border: "1px solid hsl(var(--border))", + borderRadius: "0.5rem", + }, + "&light .cm-editor.cm-focused, &dark .cm-editor.cm-focused": { + outline: "none", + borderColor: "hsl(var(--primary))", + }, + }); + + // 使用 theme 定义尺寸和字体样式 const sizingTheme = EditorView.theme({ "&": height ? { height } : { minHeight: `${minHeightPx}px` }, ".cm-scroller": { overflow: "auto" }, @@ -86,20 +100,13 @@ const JsonEditor: React.FC = ({ "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", fontSize: "14px", }, - ".cm-editor": { - border: "1px solid hsl(var(--border))", - borderRadius: "0.5rem", - }, - ".cm-editor.cm-focused": { - outline: "none", - borderColor: "hsl(var(--primary))", - }, }); const extensions = [ basicSetup, language === "javascript" ? javascript() : json(), placeholder(placeholderText || ""), + baseTheme, sizingTheme, jsonLinter, EditorView.updateListener.of((update) => { @@ -113,6 +120,19 @@ const JsonEditor: React.FC = ({ // 如果启用深色模式,添加深色主题 if (darkMode) { extensions.push(oneDark); + // 在 oneDark 之后强制覆盖边框样式 + extensions.push( + EditorView.theme({ + ".cm-editor": { + border: "1px solid hsl(var(--border))", + borderRadius: "0.5rem", + }, + ".cm-editor.cm-focused": { + outline: "none", + borderColor: "hsl(var(--primary))", + }, + }), + ); } // 创建初始状态 diff --git a/src/components/providers/forms/ClaudeConfigEditor.tsx b/src/components/providers/forms/ClaudeConfigEditor.tsx index 2846526..ba0c3b8 100644 --- a/src/components/providers/forms/ClaudeConfigEditor.tsx +++ b/src/components/providers/forms/ClaudeConfigEditor.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from "react"; -import JsonEditor from "@/components/JsonEditor"; import { Save } from "lucide-react"; import { useTranslation } from "react-i18next"; import { @@ -33,34 +32,8 @@ const ClaudeConfigEditor: React.FC = ({ configError, }) => { const { t } = useTranslation(); - const [isDarkMode, setIsDarkMode] = useState(false); const [isCommonConfigModalOpen, setIsCommonConfigModalOpen] = useState(false); - useEffect(() => { - // 检测暗色模式 - const checkDarkMode = () => { - setIsDarkMode(document.documentElement.classList.contains("dark")); - }; - - checkDarkMode(); - - // 监听暗色模式变化 - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName === "class") { - checkDarkMode(); - } - }); - }); - - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ["class"], - }); - - return () => observer.disconnect(); - }, []); - useEffect(() => { if (commonConfigError && !isCommonConfigModalOpen) { setIsCommonConfigModalOpen(true); @@ -70,6 +43,7 @@ const ClaudeConfigEditor: React.FC = ({ const closeModal = () => { setIsCommonConfigModalOpen(false); }; + return (
@@ -84,7 +58,7 @@ const ClaudeConfigEditor: React.FC = ({ type="checkbox" checked={useCommonConfig} onChange={(e) => onCommonConfigToggle(e.target.checked)} - className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-border-default rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2" + className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-border-default rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2" /> {t("claudeConfig.writeCommonConfig")} @@ -103,10 +77,10 @@ const ClaudeConfigEditor: React.FC = ({ {commonConfigError}

)} - onChange(e.target.value)} placeholder={`{ "env": { "ANTHROPIC_BASE_URL": "https://your-api-endpoint.com", @@ -114,6 +88,16 @@ const ClaudeConfigEditor: React.FC = ({ } }`} rows={12} + className="w-full px-3 py-2 border border-border-default dark:bg-gray-800 dark:text-gray-100 rounded-lg text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20 focus:border-border-active transition-colors resize-y min-h-[14rem]" + autoComplete="off" + autoCorrect="off" + autoCapitalize="none" + spellCheck={false} + lang="en" + inputMode="text" + data-gramm="false" + data-gramm_editor="false" + data-enable-grammarly="false" /> {configError && (

{configError}

@@ -138,11 +122,20 @@ const ClaudeConfigEditor: React.FC = ({

{t("claudeConfig.commonConfigHint")}

- onCommonConfigSnippetChange(e.target.value)} rows={12} + className="w-full px-3 py-2 border border-border-default dark:bg-gray-800 dark:text-gray-100 rounded-lg text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20 focus:border-border-active transition-colors resize-y min-h-[14rem]" + autoComplete="off" + autoCorrect="off" + autoCapitalize="none" + spellCheck={false} + lang="en" + inputMode="text" + data-gramm="false" + data-gramm_editor="false" + data-enable-grammarly="false" /> {commonConfigError && (

diff --git a/src/components/providers/forms/CommonConfigEditor.tsx b/src/components/providers/forms/CommonConfigEditor.tsx index b7e6d1c..ee1be6d 100644 --- a/src/components/providers/forms/CommonConfigEditor.tsx +++ b/src/components/providers/forms/CommonConfigEditor.tsx @@ -6,9 +6,6 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; -import JsonEditor from "@/components/JsonEditor"; -import { useTheme } from "@/components/theme-provider"; -import { useMemo } from "react"; interface CommonConfigEditorProps { value: string; @@ -36,15 +33,6 @@ export function CommonConfigEditor({ onModalClose, }: CommonConfigEditorProps) { const { t } = useTranslation(); - const { theme } = useTheme(); - - const isDarkMode = useMemo(() => { - if (theme === "dark") return true; - if (theme === "light") return false; - return typeof window !== "undefined" - ? window.document.documentElement.classList.contains("dark") - : false; - }, [theme]); return ( <> @@ -60,7 +48,7 @@ export function CommonConfigEditor({ id="useCommonConfig" checked={useCommonConfig} onChange={(e) => onCommonConfigToggle(e.target.checked)} - className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-border-default rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2" + className="w-4 h-4 text-blue-500 bg-white dark:bg-gray-800 border-border-default rounded focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2" /> {t("claudeConfig.writeCommonConfig", { @@ -86,21 +74,28 @@ export function CommonConfigEditor({ {commonConfigError}

)} -
- onChange(e.target.value)} + placeholder={`{ "env": { "ANTHROPIC_BASE_URL": "https://your-api-endpoint.com", "ANTHROPIC_AUTH_TOKEN": "your-api-key-here" } }`} - darkMode={isDarkMode} - rows={14} - showValidation - /> -
+ rows={14} + className="w-full px-3 py-2 border border-border-default dark:bg-gray-800 dark:text-gray-100 rounded-lg text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:focus:ring-blue-400/20 focus:border-border-active transition-colors resize-y min-h-[16rem]" + autoComplete="off" + autoCorrect="off" + autoCapitalize="none" + spellCheck={false} + lang="en" + inputMode="text" + data-gramm="false" + data-gramm_editor="false" + data-enable-grammarly="false" + />

{t("claudeConfig.fullSettingsHint", { defaultValue: "请填写完整的 Claude Code 配置", @@ -126,14 +121,21 @@ export function CommonConfigEditor({ defaultValue: "通用配置片段将合并到所有启用它的供应商配置中", })}

-
- -
+