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
This commit is contained in:
Jason
2025-10-21 10:49:30 +08:00
parent cbd1903b90
commit 7a694fbcb0
3 changed files with 88 additions and 73 deletions

View File

@@ -78,6 +78,20 @@ const JsonEditor: React.FC<JsonEditorProps> = ({
// 创建编辑器扩展
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<JsonEditorProps> = ({
"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<JsonEditorProps> = ({
// 如果启用深色模式,添加深色主题
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))",
},
}),
);
}
// 创建初始状态

View File

@@ -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<ClaudeConfigEditorProps> = ({
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<ClaudeConfigEditorProps> = ({
const closeModal = () => {
setIsCommonConfigModalOpen(false);
};
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
@@ -84,7 +58,7 @@ const ClaudeConfigEditor: React.FC<ClaudeConfigEditorProps> = ({
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")}
</label>
@@ -103,10 +77,10 @@ const ClaudeConfigEditor: React.FC<ClaudeConfigEditorProps> = ({
{commonConfigError}
</p>
)}
<JsonEditor
<textarea
id="settingsConfig"
value={value}
onChange={onChange}
darkMode={isDarkMode}
onChange={(e) => onChange(e.target.value)}
placeholder={`{
"env": {
"ANTHROPIC_BASE_URL": "https://your-api-endpoint.com",
@@ -114,6 +88,16 @@ const ClaudeConfigEditor: React.FC<ClaudeConfigEditorProps> = ({
}
}`}
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 && (
<p className="text-xs text-red-500 dark:text-red-400">{configError}</p>
@@ -138,11 +122,20 @@ const ClaudeConfigEditor: React.FC<ClaudeConfigEditorProps> = ({
<p className="text-sm text-gray-500 dark:text-gray-400">
{t("claudeConfig.commonConfigHint")}
</p>
<JsonEditor
<textarea
value={commonConfigSnippet}
onChange={onCommonConfigSnippetChange}
darkMode={isDarkMode}
onChange={(e) => 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 && (
<p className="text-sm text-red-500 dark:text-red-400">

View File

@@ -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"
/>
<span>
{t("claudeConfig.writeCommonConfig", {
@@ -86,21 +74,28 @@ export function CommonConfigEditor({
{commonConfigError}
</p>
)}
<div className="rounded-md border">
<JsonEditor
value={value}
onChange={onChange}
placeholder={`{
<textarea
id="settingsConfig"
value={value}
onChange={(e) => 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
/>
</div>
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"
/>
<p className="text-xs text-muted-foreground">
{t("claudeConfig.fullSettingsHint", {
defaultValue: "请填写完整的 Claude Code 配置",
@@ -126,14 +121,21 @@ export function CommonConfigEditor({
defaultValue: "通用配置片段将合并到所有启用它的供应商配置中",
})}
</p>
<div className="rounded-md border">
<JsonEditor
value={commonConfigSnippet}
onChange={onCommonConfigSnippetChange}
darkMode={isDarkMode}
rows={12}
/>
</div>
<textarea
value={commonConfigSnippet}
onChange={(e) => 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 && (
<p className="text-sm text-red-500 dark:text-red-400">
{commonConfigError}