Last active
January 16, 2025 17:22
-
-
Save sandros94/6eb313fa95cecc19c37f66f45474a889 to your computer and use it in GitHub Desktop.
Revisions
-
sandros94 revised this gist
Jan 16, 2025 . 1 changed file with 214 additions and 66 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,86 +1,234 @@ <script setup lang="ts"> import { useScript } from '@unhead/vue' import { computed, ref, createError, onMounted, watch, useTemplateRef } from '#imports' const MONACO_CDN_BASE = 'https://unpkg.com/[email protected]/min/' const MDC_CDN_BASE = 'https://cdn.jsdelivr.net/npm/@nuxtlabs/[email protected]/' declare global { interface Window { require: any MonacoEnvironment: any } } const editorEl = useTemplateRef('editor') const code = defineModel<string>({ required: true }) const props = withDefaults(defineProps<{ fitContent?: boolean language?: string minimap?: boolean readOnly?: boolean theme?: string wordWrap?: 'on' | 'off' tabSize?: number }>(), { language: 'mdc', minimap: true, readOnly: false, theme: 'vs-dark', wordWrap: 'on', tabSize: 2, }) const monaco = ref() const editor = ref() const styling = computed(() => { if (props.fitContent) { return {} } else { return { height: '100%' } } }) // Cave's man approach to wait for window.require to be available const waitForRequire = async (attempt = 1): Promise<void> => { if (window.require) return if (attempt > 5) throw new Error('Monaco loader.js failed to initialize') const delays = [0, 100, 250, 500, 2000] await new Promise(resolve => setTimeout(resolve, delays[attempt - 1])) return waitForRequire(attempt + 1) } const { status, load } = useScript({ src: `${MONACO_CDN_BASE}vs/loader.js`, }, { trigger: 'manual', async use() { if (editorEl.value) return try { await waitForRequire() } catch { throw new Error('Failed to load Monaco: loader.js did not initialize') } window.require.config({ paths: { vs: `${MONACO_CDN_BASE}vs`, }, // 'vs/nls': { // availableLanguages: {}, // }, }) const _monaco = await new Promise<any>((resolve, reject) => { try { window.require(['vs/editor/editor.main'], resolve) } catch (e) { reject(new Error('Failed to load Monaco editor: ' + e)) } }) window.MonacoEnvironment = { getWorkerUrl: function () { return `data:text/javascript;charset=utf-8,${encodeURIComponent(` self.MonacoEnvironment = { baseUrl: '${MONACO_CDN_BASE}' }; importScripts('${MONACO_CDN_BASE}vs/base/worker/workerMain.js');`, )}` }, } const { language: mdc, formatter: mdcFormatter, foldingProvider: mdcFoldingProvider, } = await import(/* @vite-ignore */`${MDC_CDN_BASE}dist/index.mjs`) _monaco.languages.register({ id: 'mdc' }) _monaco.languages.setMonarchTokensProvider('mdc', mdc) _monaco.languages.registerDocumentFormattingEditProvider('mdc', { provideDocumentFormattingEdits: (model: any) => [{ range: model.getFullModelRange(), text: mdcFormatter(model.getValue(), { tabSize: props.tabSize, }), }], }) _monaco.languages.registerOnTypeFormattingEditProvider('mdc', { autoFormatTriggerCharacters: ['\n'], provideOnTypeFormattingEdits: (model: any) => [{ range: model.getFullModelRange(), text: mdcFormatter(model.getValue(), { tabSize: props.tabSize, isFormatOnType: true, }), }], }) _monaco.languages.registerFoldingRangeProvider('mdc', { provideFoldingRanges: (model: any) => mdcFoldingProvider(model), }) _monaco.languages.setLanguageConfiguration('mdc', { surroundingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: '\'', close: '\'' }, { open: '"', close: '"' }, { open: '`', close: '`' }, ], autoClosingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: '"', close: '"' }, ], }) if (!editorEl.value) { createError('MonacoEditor must be called in the browser') return } const _editor = _monaco.editor.create(editorEl.value, { value: code.value, language: props.language, tabSize: props.tabSize, wordWrap: props.wordWrap, wrappingStrategy: 'advanced', insertSpaces: true, theme: props.theme, autoIndent: 'full', folding: true, detectIndentation: false, formatOnType: true, formatOnPaste: true, automaticLayout: true, readOnly: props.readOnly, minimap: { enabled: props.minimap, }, lineNumbers: 'on', scrollBeyondLastLine: false, bracketPairColorization: { enabled: true, }, roundedSelection: false, fontSize: 14, padding: { top: 8, }, }) _editor.onDidChangeModelContent(() => { code.value = _editor.getValue() }) const updateHeight = () => { if (!props.fitContent) return const contentHeight = _editor.getContentHeight() editorEl.value!.style.height = `${contentHeight}px` _editor.layout({ width: editorEl.value!.offsetWidth, height: Math.min(contentHeight, editorEl.value!.offsetHeight), }) } _editor.onDidContentSizeChange(updateHeight) return { editor: _editor, monaco: _monaco, } }, }) onMounted(async () => { try { const l = await load() if (!l) return monaco.value = l.monaco editor.value = l.editor } catch (error) { console.error('Failed to initialize Monaco:', error) } }) // watch(code, (newCode) => { // if (editor.value && editor.value.getValue() !== newCode) { // editor.value.setValue(newCode) // } // }) watch(() => props.theme, (newTheme) => { if (monaco.value) { monaco.value.editor.setTheme(newTheme) } }) </script> <template> <div v-if="status !== 'loaded'"> <slot :status="status"> {{ status }} </slot> </div> <div v-else ref="editor" :style="styling" /> </template> -
sandros94 revised this gist
Nov 18, 2024 . No changes.There are no files selected for viewing
-
sandros94 created this gist
Nov 18, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,86 @@ <script setup lang="ts"> const md = ref('') const editorEl = useTemplateRef('editor') const MONACO_CDN_BASE = 'https://unpkg.com/[email protected]/dev/' const { onLoaded, status } = useScriptNpm({ packageName: 'monaco-editor', file: 'dev/vs/loader.js', version: '0.52.0', scriptOptions: { trigger: 'onNuxtReady', async use() { // @ts-expect-error: in-browser event window.require.config({ paths: { vs: `${MONACO_CDN_BASE}vs` } }) // @ts-expect-error: in-browser event const monaco = await new Promise<any>(resolve => window.require(['vs/editor/editor.main'], resolve)) // @ts-expect-error: in-browser event window.MonacoEnvironment = { getWorkerUrl: function () { return `data:text/javascript;charset=utf-8,${encodeURIComponent(` self.MonacoEnvironment = { baseUrl: '${MONACO_CDN_BASE}' }; importScripts('${MONACO_CDN_BASE}vs/base/worker/workerMain.js');`, )}` }, } // @ts-expect-error: in-browser event const { language: monarchMdc } = await import('https://cdn.jsdelivr.net/npm/@nuxtlabs/[email protected]/dist/index.mjs') monaco.languages.register({ id: 'mdc' }) monaco.languages.setMonarchTokensProvider('mdc', monarchMdc) monaco.languages.setLanguageConfiguration('mdc', { surroundingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: '<', close: '>' }, { open: "'", close: "'" }, { open: '"', close: '"' }, ], autoClosingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, { open: '(', close: ')' }, { open: "'", close: "'" }, { open: '"', close: '"' }, ], }) return { monaco } } }, }) onMounted(() => { onLoaded(({ monaco }) => { const editor = new monaco.editor.create(unref(editorEl), { value: md.value, language: 'mdc', tabSize: 2, wordWrap: 'on', insertSpaces: true, theme: 'vs-dark', autoIndent: true, formatOnPaste: true, formatOnType: true }) setTimeout(() => { editor.getAction('editor.action.formatDocument').run(); }, 1000) }) }) </script> <template> <div class="h-[100svh]"> <span v-if="status !== 'loaded'"> {{ status }} </span> <div v-else class="h-full" ref="editor" /> </div> </template>