Spaces:
Running
Running
| <script lang="ts"> | |
| import { onMount, onDestroy } from "svelte"; | |
| import * as monaco from "monaco-editor"; | |
| import type { editor } from "monaco-editor"; | |
| import { contentManager } from "../services/content-manager"; | |
| import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker"; | |
| import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; | |
| import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker"; | |
| import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; | |
| import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; | |
| export let language: string = "html"; | |
| export let theme: string = "vs-dark"; | |
| export let readOnly: boolean = false; | |
| let editorContainer: HTMLDivElement; | |
| let editorInstance: editor.IStandaloneCodeEditor | null = null; | |
| let isInternalUpdate = false; | |
| let contentUnsubscribe: (() => void) | null = null; | |
| (self as any).MonacoEnvironment = { | |
| getWorker: function (_: any, label: string) { | |
| switch (label) { | |
| case "json": | |
| return new jsonWorker(); | |
| case "css": | |
| case "scss": | |
| case "less": | |
| return new cssWorker(); | |
| case "html": | |
| case "handlebars": | |
| case "razor": | |
| case "xml": | |
| return new htmlWorker(); | |
| case "typescript": | |
| case "javascript": | |
| return new tsWorker(); | |
| default: | |
| return new editorWorker(); | |
| } | |
| }, | |
| }; | |
| onMount(() => { | |
| const currentContent = contentManager.getCurrentContent(); | |
| editorInstance = monaco.editor.create(editorContainer, { | |
| value: currentContent, | |
| language: language, | |
| theme: theme, | |
| minimap: { | |
| enabled: false, | |
| }, | |
| fontSize: 13, | |
| fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", monospace', | |
| lineHeight: 20, | |
| wordWrap: "on", | |
| scrollBeyondLastLine: false, | |
| renderWhitespace: "selection", | |
| automaticLayout: true, | |
| tabSize: 2, | |
| insertSpaces: true, | |
| formatOnPaste: true, | |
| formatOnType: true, | |
| suggestOnTriggerCharacters: true, | |
| quickSuggestions: { | |
| other: true, | |
| comments: false, | |
| strings: true, | |
| }, | |
| scrollbar: { | |
| verticalScrollbarSize: 10, | |
| horizontalScrollbarSize: 10, | |
| }, | |
| }); | |
| // Subscribe to ContentManager changes | |
| contentUnsubscribe = contentManager.subscribe((state) => { | |
| if (editorInstance && state.content !== editorInstance.getValue()) { | |
| isInternalUpdate = true; | |
| // Preserve cursor position | |
| const position = editorInstance.getPosition(); | |
| const scrollTop = editorInstance.getScrollTop(); | |
| editorInstance.setValue(state.content); | |
| // Restore cursor position if possible | |
| if (position) { | |
| editorInstance.setPosition(position); | |
| } | |
| editorInstance.setScrollTop(scrollTop); | |
| isInternalUpdate = false; | |
| } | |
| }); | |
| // Handle user changes | |
| editorInstance.onDidChangeModelContent(() => { | |
| if (!isInternalUpdate) { | |
| const newValue = editorInstance?.getValue() || ""; | |
| contentManager.updateFromUI(newValue); | |
| } | |
| }); | |
| const resizeObserver = new ResizeObserver(() => { | |
| editorInstance?.layout(); | |
| }); | |
| resizeObserver.observe(editorContainer); | |
| return () => { | |
| resizeObserver.disconnect(); | |
| }; | |
| }); | |
| onDestroy(() => { | |
| if (contentUnsubscribe) { | |
| contentUnsubscribe(); | |
| } | |
| editorInstance?.dispose(); | |
| }); | |
| $: if (editorInstance) { | |
| const model = editorInstance.getModel(); | |
| if (model) { | |
| monaco.editor.setModelLanguage(model, language); | |
| } | |
| } | |
| $: if (editorInstance && theme) { | |
| monaco.editor.setTheme(theme); | |
| } | |
| $: if (editorInstance) { | |
| editorInstance.updateOptions({ readOnly }); | |
| } | |
| </script> | |
| <div bind:this={editorContainer} class="monaco-editor-container"></div> | |
| <style> | |
| .monaco-editor-container { | |
| width: 100%; | |
| height: 100%; | |
| min-height: 200px; | |
| } | |
| </style> | |