From 51071d65fb0b13a76e8ecc9a1cd8e4331b5abbdd Mon Sep 17 00:00:00 2001 From: one Date: Sat, 17 May 2025 01:32:54 +0800 Subject: [PATCH] refactor(CodeEditor): add more options to props for better customization, fix auto theme (#6071) * refactor(CodeEditor): add more options to props for better customization - A complete BasicSetupOptions could be passed in to override system-wise options - EditMcpJsonPopup now use customized options * fix: accommodate ThemeMode.auto * fix: typos --- .../src/components/CodeEditor/index.tsx | 88 +++++++++++-------- .../src/context/CodeStyleProvider.tsx | 4 +- .../settings/MCPSettings/EditMcpJsonPopup.tsx | 13 ++- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/renderer/src/components/CodeEditor/index.tsx b/src/renderer/src/components/CodeEditor/index.tsx index 744abf7f2a..d3e883a185 100644 --- a/src/renderer/src/components/CodeEditor/index.tsx +++ b/src/renderer/src/components/CodeEditor/index.tsx @@ -1,7 +1,7 @@ import { TOOL_SPECS, useCodeToolbar } from '@renderer/components/CodeToolbar' import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useSettings } from '@renderer/hooks/useSettings' -import CodeMirror, { Annotation, EditorView, Extension, keymap } from '@uiw/react-codemirror' +import CodeMirror, { Annotation, BasicSetupOptions, EditorView, Extension, keymap } from '@uiw/react-codemirror' import diff from 'fast-diff' import { ChevronsDownUp, @@ -22,10 +22,15 @@ interface Props { language: string onSave?: (newContent: string) => void onChange?: (newContent: string) => void - // options used to override the default behaviour + maxHeight?: string + /** 用于覆写编辑器的某些设置 */ options?: { - maxHeight?: string - } + collapsible?: boolean + wrappable?: boolean + keymap?: boolean + } & BasicSetupOptions + /** 用于覆写编辑器的样式,会直接传给 CodeMirror 的 style 属性 */ + style?: React.CSSProperties } /** @@ -33,11 +38,30 @@ interface Props { * * 目前必须和 CodeToolbar 配合使用。 */ -const CodeEditor = ({ children, language, onSave, onChange, options }: Props) => { - const { fontSize, codeShowLineNumbers, codeCollapsible, codeWrappable, codeEditor } = useSettings() +const CodeEditor = ({ children, language, onSave, onChange, maxHeight, options, style }: Props) => { + const { + fontSize, + codeShowLineNumbers: _lineNumbers, + codeCollapsible: _collapsible, + codeWrappable: _wrappable, + codeEditor + } = useSettings() + const collapsible = useMemo(() => options?.collapsible ?? _collapsible, [options?.collapsible, _collapsible]) + const wrappable = useMemo(() => options?.wrappable ?? _wrappable, [options?.wrappable, _wrappable]) + const enableKeymap = useMemo(() => options?.keymap ?? codeEditor.keymap, [options?.keymap, codeEditor.keymap]) + + // 合并 codeEditor 和 options 的 basicSetup,options 优先 + const customBasicSetup = useMemo(() => { + return { + lineNumbers: _lineNumbers, + ...(codeEditor as BasicSetupOptions), + ...(options as BasicSetupOptions) + } + }, [codeEditor, _lineNumbers, options]) + const { activeCmTheme, languageMap } = useCodeStyle() - const [isExpanded, setIsExpanded] = useState(!codeCollapsible) - const [isUnwrapped, setIsUnwrapped] = useState(!codeWrappable) + const [isExpanded, setIsExpanded] = useState(!collapsible) + const [isUnwrapped, setIsUnwrapped] = useState(!wrappable) const initialContent = useRef(children?.trimEnd() ?? '') const [langExtension, setLangExtension] = useState([]) const [editorReady, setEditorReady] = useState(false) @@ -75,13 +99,13 @@ const CodeEditor = ({ children, language, onSave, onChange, options }: Props) => tooltip: isExpanded ? t('code_block.collapse') : t('code_block.expand'), visible: () => { const scrollHeight = editorViewRef?.current?.scrollDOM?.scrollHeight - return codeCollapsible && (scrollHeight ?? 0) > 350 + return collapsible && (scrollHeight ?? 0) > 350 }, onClick: () => setIsExpanded((prev) => !prev) }) return () => removeTool(TOOL_SPECS.expand.id) - }, [codeCollapsible, isExpanded, registerTool, removeTool, t, editorReady]) + }, [collapsible, isExpanded, registerTool, removeTool, t, editorReady]) // 自动换行工具 useEffect(() => { @@ -89,12 +113,12 @@ const CodeEditor = ({ children, language, onSave, onChange, options }: Props) => ...TOOL_SPECS.wrap, icon: isUnwrapped ? : , tooltip: isUnwrapped ? t('code_block.wrap.on') : t('code_block.wrap.off'), - visible: () => codeWrappable, + visible: () => wrappable, onClick: () => setIsUnwrapped((prev) => !prev) }) return () => removeTool(TOOL_SPECS.wrap.id) - }, [codeWrappable, isUnwrapped, registerTool, removeTool, t]) + }, [wrappable, isUnwrapped, registerTool, removeTool, t]) const handleSave = useCallback(() => { const currentDoc = editorViewRef.current?.state.doc.toString() ?? '' @@ -132,12 +156,12 @@ const CodeEditor = ({ children, language, onSave, onChange, options }: Props) => }, [children]) useEffect(() => { - setIsExpanded(!codeCollapsible) - }, [codeCollapsible]) + setIsExpanded(!collapsible) + }, [collapsible]) useEffect(() => { - setIsUnwrapped(!codeWrappable) - }, [codeWrappable]) + setIsUnwrapped(!wrappable) + }, [wrappable]) // 保存功能的快捷键 const saveKeymap = useMemo(() => { @@ -154,19 +178,15 @@ const CodeEditor = ({ children, language, onSave, onChange, options }: Props) => }, [handleSave]) const enabledExtensions = useMemo(() => { - return [ - ...langExtension, - ...(isUnwrapped ? [] : [EditorView.lineWrapping]), - ...(codeEditor.keymap ? [saveKeymap] : []) - ] - }, [codeEditor.keymap, langExtension, isUnwrapped, saveKeymap]) + return [...langExtension, ...(isUnwrapped ? [] : [EditorView.lineWrapping]), ...(enableKeymap ? [saveKeymap] : [])] + }, [enableKeymap, langExtension, isUnwrapped, saveKeymap]) return ( if (onChange && viewUpdate.docChanged) onChange(value) }} basicSetup={{ - lineNumbers: codeShowLineNumbers, - highlightActiveLineGutter: codeEditor.highlightActiveLine, - foldGutter: codeEditor.foldGutter, dropCursor: true, allowMultipleSelections: true, indentOnInput: true, bracketMatching: true, closeBrackets: true, - autocompletion: codeEditor.autocompletion, rectangularSelection: true, crosshairCursor: true, - highlightActiveLine: codeEditor.highlightActiveLine, + highlightActiveLineGutter: false, highlightSelectionMatches: true, - closeBracketsKeymap: codeEditor.keymap, - searchKeymap: codeEditor.keymap, - foldKeymap: codeEditor.keymap, - completionKeymap: codeEditor.keymap, - lintKeymap: codeEditor.keymap + closeBracketsKeymap: enableKeymap, + searchKeymap: enableKeymap, + foldKeymap: enableKeymap, + completionKeymap: enableKeymap, + lintKeymap: enableKeymap, + ...customBasicSetup // override basicSetup }} style={{ fontSize: `${fontSize - 1}px`, - overflow: codeCollapsible && !isExpanded ? 'auto' : 'visible', + overflow: collapsible && !isExpanded ? 'auto' : 'visible', position: 'relative', border: '0.5px solid var(--color-code-background)', borderRadius: '5px', - marginTop: 0 + marginTop: 0, + ...style }} /> ) diff --git a/src/renderer/src/context/CodeStyleProvider.tsx b/src/renderer/src/context/CodeStyleProvider.tsx index 050f225615..23e50f8deb 100644 --- a/src/renderer/src/context/CodeStyleProvider.tsx +++ b/src/renderer/src/context/CodeStyleProvider.tsx @@ -1,3 +1,4 @@ +import { useTheme } from '@renderer/context/ThemeProvider' import { useMermaid } from '@renderer/hooks/useMermaid' import { useSettings } from '@renderer/hooks/useSettings' import { HighlightChunkResult, ShikiPreProperties, shikiStreamService } from '@renderer/services/ShikiStreamService' @@ -29,7 +30,8 @@ const defaultCodeStyleContext: CodeStyleContextType = { const CodeStyleContext = createContext(defaultCodeStyleContext) export const CodeStyleProvider: React.FC = ({ children }) => { - const { codeEditor, codePreview, theme } = useSettings() + const { codeEditor, codePreview } = useSettings() + const { theme } = useTheme() const [shikiThemes, setShikiThemes] = useState({}) useMermaid() diff --git a/src/renderer/src/pages/settings/MCPSettings/EditMcpJsonPopup.tsx b/src/renderer/src/pages/settings/MCPSettings/EditMcpJsonPopup.tsx index 894a6d0948..c8726bfa15 100644 --- a/src/renderer/src/pages/settings/MCPSettings/EditMcpJsonPopup.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/EditMcpJsonPopup.tsx @@ -126,7 +126,18 @@ const PopupContainer: React.FC = ({ resolve }) => { {jsonConfig && (
- + {jsonConfig}