1- import { useEffect , useState } from 'react'
2- import CodeMirror from '@uiw/react-codemirror'
3- import { css } from '@codemirror/lang-css'
4- import { json , jsonParseLinter } from '@codemirror/lang-json'
5- import { linter } from '@codemirror/lint'
6- import { python } from '@codemirror/lang-python'
7- import { oneDark } from '@codemirror/theme-one-dark'
8- import { EditorView } from '@codemirror/view'
9- import { StreamLanguage } from '@codemirror/language'
10- import { toml as tomlMode } from '@codemirror/legacy-modes/mode/toml'
11-
12- import { useTheme } from '@/components/use-theme'
1+ import { lazy , Suspense } from 'react'
132
143export type Language = 'python' | 'json' | 'toml' | 'css' | 'text'
154
16- interface CodeEditorProps {
5+ export interface CodeEditorProps {
176 value : string
187
198 onChange ?: ( value : string ) => void
@@ -27,109 +16,38 @@ interface CodeEditorProps {
2716 className ?: string
2817}
2918
30- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31- const languageExtensions : Record < Language , any [ ] > = {
32- python : [ python ( ) ] ,
33- json : [ json ( ) , linter ( jsonParseLinter ( ) ) ] ,
34- toml : [ StreamLanguage . define ( tomlMode ) ] ,
35- css : [ css ( ) ] ,
36- text : [ ] ,
37- }
19+ const CodeEditorImpl = lazy ( ( ) => import ( './CodeEditorImpl' ) )
3820
39- export function CodeEditor ( {
40- value,
41- onChange,
42- language = 'text' ,
43- readOnly = false ,
44- height = '400px' ,
21+ function CodeEditorFallback ( {
22+ height,
4523 minHeight,
4624 maxHeight,
47- placeholder,
48- theme,
4925 className = '' ,
50- } : CodeEditorProps ) {
51- const [ mounted , setMounted ] = useState ( false )
52- const { resolvedTheme } = useTheme ( )
53-
54- useEffect ( ( ) => {
55- setMounted ( true )
56- } , [ ] )
57-
58- if ( ! mounted ) {
59- return (
60- < div
61- className = { `rounded-md border bg-muted animate-pulse ${ className } ` }
62- style = { { height, minHeight, maxHeight } }
63- />
64- )
65- }
66-
67- const extensions = [
68- ...( languageExtensions [ language ] || [ ] ) ,
69- EditorView . lineWrapping ,
70- // 应用 JetBrains Mono 字体
71- EditorView . theme ( {
72- '&' : {
73- fontFamily : '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace' ,
74- } ,
75- '.cm-content' : {
76- fontFamily : '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace' ,
77- } ,
78- '.cm-gutters' : {
79- fontFamily : '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace' ,
80- } ,
81- '.cm-scroller' : {
82- fontFamily : '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace' ,
83- } ,
84- } ) ,
85- ]
86-
87- if ( readOnly ) {
88- extensions . push ( EditorView . editable . of ( false ) )
89- }
26+ } : Pick < CodeEditorProps , 'height' | 'minHeight' | 'maxHeight' | 'className' > ) {
27+ return (
28+ < div
29+ className = { `bg-muted animate-pulse rounded-md border ${ className } ` }
30+ style = { { height, minHeight, maxHeight } }
31+ />
32+ )
33+ }
9034
91- // 如果外部传了 theme prop 则使用,否则从 context 自动获取
92- const effectiveTheme = theme ?? resolvedTheme
35+ export function CodeEditor ( props : CodeEditorProps ) {
36+ const { height = '400px' , minHeight , maxHeight , className = '' } = props
9337
9438 return (
95- < div className = { `rounded-md overflow-hidden border custom-scrollbar ${ className } ` } >
96- < CodeMirror
97- value = { value }
98- height = { height }
99- minHeight = { minHeight }
100- maxHeight = { maxHeight }
101- theme = { effectiveTheme === 'dark' ? oneDark : undefined }
102- extensions = { extensions }
103- onChange = { onChange }
104- placeholder = { placeholder }
105- basicSetup = { {
106- lineNumbers : true ,
107- highlightActiveLineGutter : true ,
108- highlightSpecialChars : true ,
109- history : true ,
110- foldGutter : true ,
111- drawSelection : true ,
112- dropCursor : true ,
113- allowMultipleSelections : true ,
114- indentOnInput : true ,
115- syntaxHighlighting : true ,
116- bracketMatching : true ,
117- closeBrackets : true ,
118- autocompletion : true ,
119- rectangularSelection : true ,
120- crosshairCursor : true ,
121- highlightActiveLine : true ,
122- highlightSelectionMatches : true ,
123- closeBracketsKeymap : true ,
124- defaultKeymap : true ,
125- searchKeymap : true ,
126- historyKeymap : true ,
127- foldKeymap : true ,
128- completionKeymap : true ,
129- lintKeymap : true ,
130- } }
131- />
132- </ div >
39+ < Suspense
40+ fallback = {
41+ < CodeEditorFallback
42+ height = { height }
43+ minHeight = { minHeight }
44+ maxHeight = { maxHeight }
45+ className = { className }
46+ />
47+ }
48+ >
49+ < CodeEditorImpl { ...props } />
50+ </ Suspense >
13351 )
13452}
13553
0 commit comments