@@ -5,17 +5,21 @@ import React, {
5
5
useRef ,
6
6
useState ,
7
7
} from "react" ;
8
- import MonacoEditor , { Monaco } from "@monaco-editor/react" ;
9
- import { editor } from "monaco-editor" ;
8
+ import MonacoEditor , { Monaco , DiffEditor } from "@monaco-editor/react" ;
9
+ import { editor , Uri } from "monaco-editor" ;
10
10
import { Progress } from "../../@/components/ui/progress" ;
11
11
import { Icon } from "@speakeasy-api/moonshine" ;
12
12
import { Button } from "@/components/ui/button" ;
13
+ import IModelContentChangedEvent = editor . IModelContentChangedEvent ;
14
+ import ITextModel = editor . ITextModel ;
15
+ import ICodeEditor = editor . ICodeEditor ;
13
16
14
17
export interface EditorComponentProps {
15
18
readonly : boolean ;
16
19
value : string ;
20
+ original ?: string ;
17
21
loading ?: boolean ;
18
- title ? : string ;
22
+ title : string ;
19
23
index : number ;
20
24
maxOnClick ?: ( index : number ) => void ;
21
25
onChange : (
@@ -27,51 +31,89 @@ export interface EditorComponentProps {
27
31
const minLoadingTime = 150 ;
28
32
29
33
export function Editor ( props : EditorComponentProps ) {
30
- const editorRef = useRef < any > ( null ) ;
34
+ const editorRef = useRef < ICodeEditor | null > ( null ) ;
31
35
const monacoRef = useRef < Monaco | null > ( null ) ;
36
+ const modelRef = useRef < ITextModel | null > ( null ) ;
32
37
const [ lastLoadingTime , setLastLoadingTime ] = useState ( minLoadingTime ) ;
33
38
const [ progress , setProgress ] = useState ( 100 ) ;
34
39
35
- const handleEditorDidMount = useCallback ( ( editor : any , monaco : Monaco ) => {
36
- editorRef . current = editor ;
37
- monacoRef . current = monaco ;
40
+ const encodedTitle = useMemo ( ( ) => {
41
+ return btoa ( props . title ) ;
42
+ } , [ props . title ] ) ;
38
43
39
- const options = {
40
- base : "vs-dark" ,
41
- renderSideBySide : false ,
42
- inherit : true ,
43
- rules : [
44
- {
45
- foreground : "F3F0E3" ,
46
- token : "string" ,
47
- } ,
48
- {
49
- foreground : "679FE1" ,
50
- token : "type" ,
51
- } ,
52
- ] ,
53
- colors : {
54
- "editor.foreground" : "#F3F0E3" ,
55
- "editor.background" : "#212015" ,
56
- "editorCursor.foreground" : "#679FE1" ,
57
- "editor.lineHighlightBackground" : "#1D2A3A" ,
58
- "editorLineNumber.foreground" : "#6368747F" ,
59
- "editorLineNumber.activeForeground" : "#FBE331" ,
60
- "editor.inactiveSelectionBackground" : "#FF3C742D" ,
61
- "diffEditor.removedTextBackground" : "#FF3C741A" ,
62
- "diffEditor.insertedTextBackground" : "#1D2A3A" ,
63
- } ,
44
+ const EditorComponent =
45
+ props . original === undefined ? MonacoEditor : DiffEditor ;
46
+
47
+ const onChange = useCallback (
48
+ ( value : string , event : IModelContentChangedEvent ) => {
49
+ props . onChange ( value , event ) ;
50
+ } ,
51
+ [ props . onChange ] ,
52
+ ) ;
53
+
54
+ const handleEditorWillMount = useCallback ( ( monaco : Monaco ) => {
55
+ monacoRef . current = monaco ;
56
+ const matchesURI = ( uri : Uri | undefined ) => {
57
+ return uri ?. path . includes ( encodedTitle ) ;
64
58
} ;
65
- // @ts -ignore
66
- monaco . editor . defineTheme ( "speakeasy" , options ) ;
67
- monaco . editor . setTheme ( "speakeasy" ) ;
59
+ monaco . editor . onDidCreateModel ( ( model ) => {
60
+ if ( ! matchesURI ( model . uri ) ) {
61
+ return ;
62
+ }
63
+ if ( props . original && ! model . uri . path . includes ( "modified" ) ) {
64
+ return ;
65
+ }
66
+
67
+ modelRef . current = model ;
68
+ modelRef . current . onDidChangeContent ( ( event ) => {
69
+ if ( editorRef . current ?. hasTextFocus ( ) ) {
70
+ onChange ( model . getValue ( ) , event ) ;
71
+ }
72
+ } ) ;
73
+ } ) ;
68
74
} , [ ] ) ;
69
75
76
+ const handleEditorDidMount = useCallback (
77
+ ( editor : ICodeEditor , monaco : Monaco ) => {
78
+ editorRef . current = editor ;
79
+
80
+ const options = {
81
+ base : "vs-dark" ,
82
+ inherit : true ,
83
+ rules : [
84
+ {
85
+ foreground : "F3F0E3" ,
86
+ token : "string" ,
87
+ } ,
88
+ {
89
+ foreground : "679FE1" ,
90
+ token : "type" ,
91
+ } ,
92
+ ] ,
93
+ colors : {
94
+ "editor.foreground" : "#F3F0E3" ,
95
+ "editor.background" : "#212015" ,
96
+ "editorCursor.foreground" : "#679FE1" ,
97
+ "editor.lineHighlightBackground" : "#1D2A3A" ,
98
+ "editorLineNumber.foreground" : "#6368747F" ,
99
+ "editorLineNumber.activeForeground" : "#FBE331" ,
100
+ "editor.inactiveSelectionBackground" : "#FF3C742D" ,
101
+ "diffEditor.removedTextBackground" : "#FF3C741A" ,
102
+ "diffEditor.insertedTextBackground" : "#1D2A3A" ,
103
+ } ,
104
+ } satisfies editor . IStandaloneThemeData ;
105
+ monaco . editor . defineTheme ( "speakeasy" , options ) ;
106
+ monaco . editor . setTheme ( "speakeasy" ) ;
107
+ } ,
108
+ [ onChange ] ,
109
+ ) ;
110
+
70
111
const options : any = useMemo (
71
112
( ) => ( {
72
113
readOnly : props . readonly ,
73
114
minimap : { enabled : false } ,
74
115
automaticLayout : true ,
116
+ renderSideBySide : false ,
75
117
} ) ,
76
118
[ props . readonly ] ,
77
119
) ;
@@ -152,10 +194,15 @@ export function Editor(props: EditorComponentProps) {
152
194
</ h1 >
153
195
</ div >
154
196
) }
155
- < MonacoEditor
197
+ < EditorComponent
156
198
onMount = { handleEditorDidMount }
199
+ beforeMount = { handleEditorWillMount }
200
+ original = { props . original }
201
+ modified = { props . value }
157
202
value = { props . value }
158
- onChange = { props . onChange }
203
+ path = { encodedTitle }
204
+ originalModelPath = { encodedTitle + "/original" }
205
+ modifiedModelPath = { encodedTitle + "/modified" }
159
206
theme = { "vscode-dark" }
160
207
language = "yaml"
161
208
options = { options }
0 commit comments