1- import Editor , { Monaco } from '@monaco-editor/react' ;
2- import React from 'react' ;
1+ import Editor , { Monaco , OnMount } from '@monaco-editor/react' ;
2+ import React , { useRef } from 'react' ;
33import { useSettingsStore } from '../../store/settings' ;
44import { useEvent } from '../../hooks/useEvent' ;
55import { sandboxGlobal } from './lib/sandbox' ;
6+ import { ValidatorFn } from './validator/fetch' ;
67
78interface CodeEditorProps {
89 height ?: string | number ;
910 value ?: string ;
1011 readOnly ?: boolean ;
1112 onChange ?: ( code : string ) => void ;
13+ codeValidator ?: ValidatorFn [ ] ;
1214}
1315
1416export const CodeEditor : React . FC < CodeEditorProps > = React . memo ( ( props ) => {
15- const { readOnly = false } = props ;
17+ const { readOnly = false , codeValidator = [ ] } = props ;
1618 const colorScheme = useSettingsStore ( ( state ) => state . colorScheme ) ;
1719 const theme = colorScheme === 'dark' ? 'vs-dark' : 'light' ;
20+ const editorRef = useRef < any > ( null ) ;
21+ const monacoRef = useRef < Monaco | null > ( null ) ;
22+
23+ const validateCode = useEvent (
24+ ( code : string ) : { isValid : boolean ; errors : string [ ] } => {
25+ for ( const validator of codeValidator ) {
26+ const result = validator ( code ) ;
27+ if ( ! result . isValid ) {
28+ return result ;
29+ }
30+ }
31+
32+ return { isValid : true , errors : [ ] } ;
33+ }
34+ ) ;
35+
36+ const handleEditorDidMount : OnMount = useEvent ( ( editor , monaco ) => {
37+ editorRef . current = editor ;
38+ monacoRef . current = monaco ;
39+
40+ monaco . languages . typescript . javascriptDefaults . addExtraLib (
41+ sandboxGlobal ,
42+ 'global.ts'
43+ ) ;
44+
45+ const validateAndReport = ( ) => {
46+ const model = editor . getModel ( ) ;
47+ if ( ! model ) {
48+ return ;
49+ }
50+
51+ const code = model . getValue ( ) ;
52+ const validation = validateCode ( code ) ;
53+
54+ if ( ! validation . isValid ) {
55+ const markers = validation . errors . map ( ( error , index ) => ( {
56+ severity : monaco . MarkerSeverity . Error ,
57+ startLineNumber : 1 ,
58+ startColumn : 1 ,
59+ endLineNumber : 1 ,
60+ endColumn : 1 ,
61+ message : error ,
62+ source : 'tianji-validator' ,
63+ } ) ) ;
64+
65+ monaco . editor . setModelMarkers ( model , 'tianji-validator' , markers ) ;
66+ } else {
67+ // Clear custom markers if validation passes
68+ monaco . editor . setModelMarkers ( model , 'tianji-validator' , [ ] ) ;
69+ }
70+ } ;
71+
72+ // Initial validation
73+ if ( props . value ) {
74+ setTimeout ( validateAndReport , 100 ) ;
75+ }
76+
77+ // Listen for content changes
78+ const model = editor . getModel ( ) ;
79+ if ( model ) {
80+ model . onDidChangeContent ( ( ) => {
81+ setTimeout ( validateAndReport , 300 ) ; // Debounce validation
82+ } ) ;
83+ }
84+ } ) ;
1885
1986 const handleEditorWillMount = useEvent ( ( monaco : Monaco ) => {
2087 monaco . languages . typescript . javascriptDefaults . addExtraLib (
@@ -27,14 +94,21 @@ export const CodeEditor: React.FC<CodeEditorProps> = React.memo((props) => {
2794 < Editor
2895 height = { props . height }
2996 theme = { theme }
30- defaultLanguage = "javascript "
97+ defaultLanguage = "typescript "
3198 value = { props . value }
3299 options = { {
33100 tabSize : 2 ,
34101 readOnly,
102+ automaticLayout : true ,
103+ scrollBeyondLastLine : false ,
104+ wordWrap : 'on' ,
105+ wrappingIndent : 'indent' ,
106+ formatOnPaste : true ,
107+ formatOnType : true ,
35108 } }
36109 onChange = { ( val ) => props . onChange ?.( val ?? '' ) }
37110 beforeMount = { handleEditorWillMount }
111+ onMount = { handleEditorDidMount }
38112 />
39113 ) ;
40114} ) ;
0 commit comments