1010</template >
1111
1212<script setup lang="ts">
13- import * as monaco from ' monaco-editor'
14- import { computed , nextTick , onBeforeUnmount , onMounted , ref , shallowRef , watch } from ' vue'
13+ import { computed , nextTick , onBeforeUnmount , ref , watch } from ' vue'
14+ import { useMonaco } from ' stream-monaco'
15+ import { useThemeStore } from ' @/stores/theme'
1516import { useUiSettingsStore } from ' @/stores/uiSettingsStore'
1617
1718type WorkspaceCodeSource = {
@@ -26,13 +27,31 @@ const props = defineProps<{
2627}>()
2728
2829const uiSettingsStore = useUiSettingsStore ()
30+ const themeStore = useThemeStore ()
2931const editorRef = ref <HTMLElement | null >(null )
30- const editor = shallowRef <monaco .editor .IStandaloneCodeEditor | null >(null )
31- const model = shallowRef <monaco .editor .ITextModel | null >(null )
32-
33- let resizeObserver: ResizeObserver | null = null
34- let themeObserver: MutationObserver | null = null
35- let currentSourceId: string | null = null
32+ const editorInitialized = ref (false )
33+ let createEditorTask: Promise <void > | null = null
34+ const resolvedTheme = computed (() => (themeStore .isDark ? ' vitesse-dark' : ' vitesse-light' ))
35+
36+ const { createEditor, updateCode, cleanupEditor, getEditorView, getEditor } = useMonaco ({
37+ readOnly: true ,
38+ domReadOnly: true ,
39+ automaticLayout: true ,
40+ wordWrap: ' on' ,
41+ wrappingIndent: ' same' ,
42+ scrollBeyondLastLine: false ,
43+ minimap: { enabled: false },
44+ lineNumbers: ' on' ,
45+ renderLineHighlight: ' none' ,
46+ contextmenu: false ,
47+ themes: [' vitesse-dark' , ' vitesse-light' ],
48+ theme: resolvedTheme .value ,
49+ fontFamily: uiSettingsStore .formattedCodeFontFamily ,
50+ padding: {
51+ top: 12 ,
52+ bottom: 12
53+ }
54+ })
3655
3756const LANGUAGE_ALIASES: Record <string , string > = {
3857 md: ' markdown' ,
@@ -117,105 +136,57 @@ const resolveLanguage = (source: WorkspaceCodeSource): string => {
117136
118137const resolvedLanguage = computed (() => resolveLanguage (props .source ))
119138
120- const getThemeName = () => {
121- return document .documentElement .classList .contains (' dark' ) ? ' vs-dark' : ' vs'
122- }
123-
124- const applyTheme = () => {
125- monaco .editor .setTheme (getThemeName ())
139+ const applyFontFamily = (fontFamily : string ) => {
140+ getEditorView ()?.updateOptions ({ fontFamily })
126141}
127142
128- const layoutEditor = () => {
129- editor .value ?.layout ()
130- }
131-
132- const disposeModel = () => {
133- model .value ?.dispose ()
134- model .value = null
135- currentSourceId = null
143+ const applyTheme = async () => {
144+ try {
145+ getEditor ().setTheme (resolvedTheme .value )
146+ } catch (error ) {
147+ console .warn (' [WorkspaceCodePane] Failed to apply Monaco theme:' , error )
148+ }
136149}
137150
138- const syncModel = () => {
139- if (! editor .value ) {
151+ const syncEditor = async () => {
152+ const editorElement = editorRef .value
153+ if (! editorElement ) {
140154 return
141155 }
142156
143- const nextLanguage = resolvedLanguage .value
144157 const nextContent = props .source .content ?? ' '
158+ const nextLanguage = resolvedLanguage .value
159+ const hasEditor = Boolean (editorElement .querySelector (' .monaco-editor' ))
145160
146- if (! model .value || currentSourceId !== props .source .id ) {
147- disposeModel ()
148- model .value = monaco .editor .createModel (nextContent , nextLanguage )
149- currentSourceId = props .source .id
150- editor .value .setModel (model .value )
151- return
152- }
153-
154- if (model .value .getLanguageId () !== nextLanguage ) {
155- monaco .editor .setModelLanguage (model .value , nextLanguage )
156- }
157-
158- if (model .value .getValue () !== nextContent ) {
159- model .value .setValue (nextContent )
160- }
161- }
161+ if (! hasEditor || ! editorInitialized .value ) {
162+ if (createEditorTask ) {
163+ await createEditorTask
164+ return
165+ }
162166
163- const ensureEditor = async () => {
164- if (editor .value || ! editorRef .value ) {
167+ createEditorTask = (async () => {
168+ await createEditor (editorElement , nextContent , nextLanguage )
169+ editorInitialized .value = true
170+ await applyTheme ()
171+ applyFontFamily (uiSettingsStore .formattedCodeFontFamily )
172+ })()
173+
174+ try {
175+ await createEditorTask
176+ } finally {
177+ createEditorTask = null
178+ }
165179 return
166180 }
167181
168- applyTheme ()
169-
170- editor .value = monaco .editor .create (editorRef .value , {
171- readOnly: true ,
172- domReadOnly: true ,
173- automaticLayout: false ,
174- wordWrap: ' on' ,
175- wrappingIndent: ' same' ,
176- scrollBeyondLastLine: false ,
177- minimap: { enabled: false },
178- lineNumbers: ' on' ,
179- renderLineHighlight: ' none' ,
180- contextmenu: false ,
181- fontFamily: uiSettingsStore .formattedCodeFontFamily ,
182- padding: {
183- top: 12 ,
184- bottom: 12
185- }
186- })
187-
188- syncModel ()
189- await nextTick ()
190- layoutEditor ()
182+ updateCode (nextContent , nextLanguage )
191183}
192184
193- onMounted (() => {
194- void ensureEditor ()
195-
196- if (typeof ResizeObserver !== ' undefined' && editorRef .value ) {
197- resizeObserver = new ResizeObserver (() => {
198- layoutEditor ()
199- })
200- resizeObserver .observe (editorRef .value )
201- }
202-
203- themeObserver = new MutationObserver (() => {
204- applyTheme ()
205- })
206- themeObserver .observe (document .documentElement , {
207- attributes: true ,
208- attributeFilter: [' class' ]
209- })
210- })
211-
212185watch (
213- () => [props . source . id , props .source .content , props .source .language , props . source . type ] as const ,
186+ () => [editorRef . value , props .source .id , props .source .content , resolvedLanguage . value ] as const ,
214187 async () => {
215- await ensureEditor ()
216- syncModel ()
217188 await nextTick ()
218- layoutEditor ()
189+ await syncEditor ()
219190 },
220191 {
221192 immediate: true ,
@@ -226,18 +197,37 @@ watch(
226197watch (
227198 () => uiSettingsStore .formattedCodeFontFamily ,
228199 (fontFamily ) => {
229- editor .value ?.updateOptions ({ fontFamily })
230- layoutEditor ()
200+ applyFontFamily (fontFamily )
201+ }
202+ )
203+
204+ watch (
205+ resolvedTheme ,
206+ () => {
207+ if (! editorInitialized .value ) {
208+ return
209+ }
210+
211+ void applyTheme ()
212+ },
213+ {
214+ flush: ' post'
231215 }
232216)
233217
218+ watch (editorRef , (value ) => {
219+ if (value ) {
220+ return
221+ }
222+
223+ cleanupEditor ()
224+ editorInitialized .value = false
225+ createEditorTask = null
226+ })
227+
234228onBeforeUnmount (() => {
235- resizeObserver ?.disconnect ()
236- resizeObserver = null
237- themeObserver ?.disconnect ()
238- themeObserver = null
239- editor .value ?.dispose ()
240- editor .value = null
241- disposeModel ()
229+ cleanupEditor ()
230+ editorInitialized .value = false
231+ createEditorTask = null
242232})
243233 </script >
0 commit comments