File size: 4,691 Bytes
794cf6c
bc7e9cd
794cf6c
 
bc7e9cd
794cf6c
 
 
 
 
 
 
 
 
 
 
 
 
 
bc7e9cd
794cf6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bc7e9cd
 
794cf6c
bc7e9cd
794cf6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bc7e9cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
794cf6c
 
 
bc7e9cd
794cf6c
 
 
 
 
 
 
 
 
 
 
 
 
 
bc7e9cd
 
 
794cf6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<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>