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:
@@ -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))",
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 创建初始状态
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user