2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 00:49:22 +08:00

feat: 使用全新的方式加载monaco

This commit is contained in:
2025-12-31 02:49:12 +08:00
parent 9530a06da5
commit b86fc187d7
7 changed files with 426 additions and 51 deletions

View File

@@ -37,7 +37,6 @@
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"ace-builds": "^1.43.5",
"alova": "^3.3.4",
"echarts": "^6.0.0",
"install": "^0.13.0",
@@ -45,6 +44,8 @@
"luxon": "^3.7.2",
"marked": "^17.0.0",
"mitt": "^3.0.1",
"monaco-editor": "^0.55.1",
"monaco-editor-nginx": "^2.0.2",
"node-forge": "^1.3.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
@@ -52,7 +53,6 @@
"vue": "^3.5.22",
"vue-echarts": "^8.0.1",
"vue-router": "^4.6.3",
"vue3-ace-editor": "^2.2.4",
"vue3-gettext": "4.0.0-beta.1"
},
"devDependencies": {

111
web/pnpm-lock.yaml generated
View File

@@ -44,9 +44,6 @@ importers:
'@xterm/xterm':
specifier: ^6.0.0
version: 6.0.0
ace-builds:
specifier: ^1.43.5
version: 1.43.5
alova:
specifier: ^3.3.4
version: 3.4.1
@@ -68,6 +65,12 @@ importers:
mitt:
specifier: ^3.0.1
version: 3.0.1
monaco-editor:
specifier: ^0.55.1
version: 0.55.1
monaco-editor-nginx:
specifier: ^2.0.2
version: 2.0.2(@babel/runtime@7.28.4)(@nginx/reference-lib@1.1.25)(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
node-forge:
specifier: ^1.3.1
version: 1.3.3
@@ -89,9 +92,6 @@ importers:
vue-router:
specifier: ^4.6.3
version: 4.6.4(vue@3.5.26(typescript@5.9.3))
vue3-ace-editor:
specifier: ^2.2.4
version: 2.2.4(ace-builds@1.43.5)(vue@3.5.26(typescript@5.9.3))
vue3-gettext:
specifier: 4.0.0-beta.1
version: 4.0.0-beta.1(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3))
@@ -345,6 +345,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.28.4':
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'}
'@babel/template@7.27.2':
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
@@ -779,6 +783,9 @@ packages:
'@marijn/find-cluster-break@1.0.2':
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
'@nginx/reference-lib@1.1.25':
resolution: {integrity: sha512-y8g/3Z17VKCvnGB2KRtAvlcEWvRUaO0Ln5MIAdBsqX6qsfMIDznFt0lnFQMVZM51AQgQeY4TP+nWTOiKe6+wsQ==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1039,6 +1046,9 @@ packages:
'@types/node@24.10.4':
resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/web-bluetooth@0.0.21':
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
@@ -1350,9 +1360,6 @@ packages:
'@xterm/xterm@6.0.0':
resolution: {integrity: sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==}
ace-builds@1.43.5:
resolution: {integrity: sha512-iH5FLBKdB7SVn9GR37UgA/tpQS8OTWIxWAuq3Ofaw+Qbc69FfPXsXd9jeW7KRG2xKpKMqBDnu0tHBrCWY5QI7A==}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -1716,6 +1723,9 @@ packages:
resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
engines: {node: '>= 4'}
dompurify@3.2.7:
resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
@@ -2414,6 +2424,11 @@ packages:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
marked@14.0.0:
resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
engines: {node: '>= 18'}
hasBin: true
marked@17.0.1:
resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==}
engines: {node: '>= 20'}
@@ -2481,6 +2496,18 @@ packages:
resolution: {integrity: sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==}
hasBin: true
monaco-editor-nginx@2.0.2:
resolution: {integrity: sha512-F/qejb0w0hxIyxbu2JMCe+MhnOV7bxGbUynnyMXk7Cy25Na5fD99u5QqRr7WfRhIn0xcdhBEkJ/jikQsxVnEOA==}
peerDependencies:
'@babel/runtime': '>=7.10.0'
'@nginx/reference-lib': '>=1.1.0'
monaco-editor: '>=0.22.3'
react: '>=16.9.0'
react-dom: '>=16.9.0'
monaco-editor@0.55.1:
resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
@@ -2749,6 +2776,15 @@ packages:
rate-limiter-flexible@5.0.5:
resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==}
react-dom@19.2.3:
resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
peerDependencies:
react: ^19.2.3
react@19.2.3:
resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
engines: {node: '>=0.10.0'}
read-package-json-fast@4.0.0:
resolution: {integrity: sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -2780,9 +2816,6 @@ packages:
remove@0.1.5:
resolution: {integrity: sha512-AJMA9oWvJzdTjwIGwSQZsjGQiRx73YTmiOWmfCp1fpLa/D4n7jKcpoA+CZiVLJqKcEKUuh1Suq80c5wF+L/qVQ==}
resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2834,6 +2867,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
@@ -3306,12 +3342,6 @@ packages:
peerDependencies:
typescript: '>=5.0.0'
vue3-ace-editor@2.2.4:
resolution: {integrity: sha512-FZkEyfpbH068BwjhMyNROxfEI8135Sc+x8ouxkMdCNkuj/Tuw83VP/gStFQqZHqljyX9/VfMTCdTqtOnJZGN8g==}
peerDependencies:
ace-builds: '*'
vue: ^3
vue3-gettext@4.0.0-beta.1:
resolution: {integrity: sha512-1A46SmubgTMyy7i5hj8ay50NFl6/vzwoIVZPuGCin/X3a/NVCAs99G0EbcnfJiR7NZNTJgUjvBzppufC7Kq+4A==}
engines: {node: '>= 20.19.0'}
@@ -3586,6 +3616,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/runtime@7.28.4': {}
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
@@ -4169,6 +4201,8 @@ snapshots:
'@marijn/find-cluster-break@1.0.2': {}
'@nginx/reference-lib@1.1.25': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -4358,6 +4392,9 @@ snapshots:
dependencies:
undici-types: 7.16.0
'@types/trusted-types@2.0.7':
optional: true
'@types/web-bluetooth@0.0.21': {}
'@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
@@ -4836,8 +4873,6 @@ snapshots:
'@xterm/xterm@6.0.0': {}
ace-builds@1.43.5: {}
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -5199,6 +5234,10 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@3.2.7:
optionalDependencies:
'@types/trusted-types': 2.0.7
domutils@2.8.0:
dependencies:
dom-serializer: 1.4.1
@@ -5952,6 +5991,8 @@ snapshots:
punycode.js: 2.3.1
uc.micro: 2.1.0
marked@14.0.0: {}
marked@17.0.1: {}
math-intrinsics@1.1.0: {}
@@ -6029,6 +6070,19 @@ snapshots:
dependencies:
commander: 14.0.2
monaco-editor-nginx@2.0.2(@babel/runtime@7.28.4)(@nginx/reference-lib@1.1.25)(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
'@nginx/reference-lib': 1.1.25
monaco-editor: 0.55.1
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
monaco-editor@0.55.1:
dependencies:
dompurify: 3.2.7
marked: 14.0.0
mrmime@2.0.1: {}
ms@2.0.0: {}
@@ -6279,6 +6333,13 @@ snapshots:
rate-limiter-flexible@5.0.5: {}
react-dom@19.2.3(react@19.2.3):
dependencies:
react: 19.2.3
scheduler: 0.27.0
react@19.2.3: {}
read-package-json-fast@4.0.0:
dependencies:
json-parse-even-better-errors: 4.0.0
@@ -6318,8 +6379,6 @@ snapshots:
dependencies:
seq: 0.3.5
resize-observer-polyfill@1.5.1: {}
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -6397,6 +6456,8 @@ snapshots:
optionalDependencies:
'@parcel/watcher': 2.5.1
scheduler@0.27.0: {}
scule@1.3.0: {}
seemly@0.3.10: {}
@@ -6953,12 +7014,6 @@ snapshots:
'@vue/language-core': 3.2.1
typescript: 5.9.3
vue3-ace-editor@2.2.4(ace-builds@1.43.5)(vue@3.5.26(typescript@5.9.3)):
dependencies:
ace-builds: 1.43.5
resize-observer-polyfill: 1.5.1
vue: 3.5.26(typescript@5.9.3)
vue3-gettext@4.0.0-beta.1(@vue/compiler-sfc@3.5.26)(vue@3.5.26(typescript@5.9.3)):
dependencies:
'@vue/compiler-sfc': 3.5.26

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import '@/utils/ace'
import { VAceEditor } from 'vue3-ace-editor'
import { useThemeStore } from '@/store'
import { getMonaco } from '@/utils/monaco'
import type * as Monaco from 'monaco-editor'
const value = defineModel<string>('value', { type: String, required: true })
const props = defineProps({
@@ -19,17 +20,105 @@ const props = defineProps({
required: false
}
})
const containerRef = ref<HTMLDivElement>()
const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor>()
const monacoRef = shallowRef<typeof Monaco>()
const loading = ref(true)
const themeStore = useThemeStore()
async function initEditor() {
if (!containerRef.value) return
const monaco = await getMonaco(themeStore.locale)
monacoRef.value = monaco
editorRef.value = monaco.editor.create(containerRef.value, {
value: value.value,
language: props.lang,
theme: 'vs-dark',
readOnly: props.readOnly,
automaticLayout: true,
smoothScrolling: true,
formatOnPaste: true,
formatOnType: true
})
editorRef.value.onDidChangeModelContent(() => {
const newValue = editorRef.value?.getValue() ?? ''
if (newValue !== value.value) {
value.value = newValue
}
})
loading.value = false
}
watch(value, (newValue) => {
if (editorRef.value && editorRef.value.getValue() !== newValue) {
editorRef.value.setValue(newValue)
}
})
watch(
() => props.lang,
(newLang) => {
if (editorRef.value && monacoRef.value) {
const model = editorRef.value.getModel()
if (model) {
monacoRef.value.editor.setModelLanguage(model, newLang)
}
}
}
)
watch(
() => props.readOnly,
(newReadOnly) => {
if (editorRef.value) {
editorRef.value.updateOptions({ readOnly: newReadOnly })
}
}
)
onMounted(() => {
initEditor()
})
onBeforeUnmount(() => {
editorRef.value?.dispose()
})
</script>
<template>
<v-ace-editor
v-model:value="value"
:lang="props.lang"
:options="{ useWorker: true }"
:readonly="props.readOnly"
theme="monokai"
:style="{ height: props.height }"
/>
<div class="common-editor" :style="{ height: props.height }">
<div v-if="loading" class="editor-loading">
<n-spin size="medium" />
</div>
<div ref="containerRef" class="editor-container" :style="{ height: props.height }" />
</div>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.common-editor {
position: relative;
width: 100%;
}
.editor-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.editor-container {
width: 100%;
}
</style>

View File

@@ -1,9 +1,11 @@
<script setup lang="ts">
import file from '@/api/panel/file'
import { useThemeStore } from '@/store'
import { decodeBase64 } from '@/utils'
import '@/utils/ace'
import { languageByPath } from '@/utils/file'
import { VAceEditor } from 'vue3-ace-editor'
import { getMonaco } from '@/utils/monaco'
import type * as Monaco from 'monaco-editor'
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
import { useGettext } from 'vue3-gettext'
const { $gettext } = useGettext()
@@ -18,9 +20,59 @@ const props = defineProps({
}
})
const themeStore = useThemeStore()
const containerRef = ref<HTMLDivElement>()
const editorRef = shallowRef<Monaco.editor.IStandaloneCodeEditor>()
const monacoRef = shallowRef<typeof Monaco>()
const loading = ref(true)
const disabled = ref(false) // 在出现错误的情况下禁用保存
const content = ref('')
async function initEditor() {
if (!containerRef.value) return
const monaco = await getMonaco(themeStore.locale)
monacoRef.value = monaco
editorRef.value = monaco.editor.create(containerRef.value, {
value: content.value,
language: languageByPath(props.path),
theme:
(languageByPath(props.path) == 'nginx' ? 'nginx-theme' : 'vs') +
(themeStore.darkMode ? '-dark' : ''),
readOnly: props.readOnly,
automaticLayout: true,
smoothScrolling: true,
formatOnPaste: true,
formatOnType: true
})
editorRef.value.onDidChangeModelContent(() => {
const newValue = editorRef.value?.getValue() ?? ''
if (newValue !== content.value) {
content.value = newValue
}
})
loading.value = false
}
watch(content, (newValue) => {
if (editorRef.value && editorRef.value.getValue() !== newValue) {
editorRef.value.setValue(newValue)
}
})
watch(
() => props.readOnly,
(newReadOnly) => {
if (editorRef.value) {
editorRef.value.updateOptions({ readOnly: newReadOnly })
}
}
)
const get = () => {
useRequest(file.content(encodeURIComponent(props.path)))
.onSuccess(({ data }) => {
@@ -44,6 +96,11 @@ const save = () => {
onMounted(() => {
get()
initEditor()
})
onBeforeUnmount(() => {
editorRef.value?.dispose()
})
defineExpose({
@@ -53,13 +110,33 @@ defineExpose({
</script>
<template>
<v-ace-editor
v-model:value="content"
:lang="languageByPath(props.path)"
:options="{ useWorker: true }"
theme="monokai"
style="height: 60vh"
/>
<div class="file-editor" style="height: 60vh">
<div v-if="loading" class="editor-loading">
<n-spin size="medium" />
</div>
<div ref="containerRef" class="editor-container" style="height: 60vh" />
</div>
</template>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.file-editor {
position: relative;
width: 100%;
}
.editor-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.editor-container {
width: 100%;
}
</style>

View File

@@ -160,6 +160,9 @@ const languageByPath = (path: string) => {
return 'html'
case 'ini':
case 'conf':
if (path.toLowerCase().includes('nginx')) {
return 'nginx'
}
return 'ini'
case 'java':
return 'java'

View File

@@ -0,0 +1,115 @@
import type * as Monaco from 'monaco-editor'
let monacoInstance: typeof Monaco | null = null
let isInitialized = false
let initPromise: Promise<typeof Monaco> | null = null
async function loadMonacoLocale(locale: string) {
switch (locale) {
case 'cs':
await import('monaco-editor/esm/nls.messages.cs.js')
break
case 'de':
await import('monaco-editor/esm/nls.messages.de.js')
break
case 'es':
await import('monaco-editor/esm/nls.messages.es.js')
break
case 'fr':
await import('monaco-editor/esm/nls.messages.fr.js')
break
case 'it':
await import('monaco-editor/esm/nls.messages.it.js')
break
case 'ja':
await import('monaco-editor/esm/nls.messages.ja.js')
break
case 'ko':
await import('monaco-editor/esm/nls.messages.ko.js')
break
case 'pl':
await import('monaco-editor/esm/nls.messages.pl.js')
break
case 'pt_BR':
await import('monaco-editor/esm/nls.messages.pt-br.js')
break
case 'ru':
await import('monaco-editor/esm/nls.messages.ru.js')
break
case 'tr':
await import('monaco-editor/esm/nls.messages.tr.js')
break
case 'zh_CN':
await import('monaco-editor/esm/nls.messages.zh-cn.js')
break
case 'zh_TW':
await import('monaco-editor/esm/nls.messages.zh-tw.js')
break
default:
break
}
}
async function setupMonacoWorkers() {
if (self.MonacoEnvironment) return
const [editorWorker, jsonWorker, cssWorker, htmlWorker, tsWorker] = await Promise.all([
import('monaco-editor/esm/vs/editor/editor.worker?worker'),
import('monaco-editor/esm/vs/language/json/json.worker?worker'),
import('monaco-editor/esm/vs/language/css/css.worker?worker'),
import('monaco-editor/esm/vs/language/html/html.worker?worker'),
import('monaco-editor/esm/vs/language/typescript/ts.worker?worker')
])
self.MonacoEnvironment = {
getWorker(_: any, label: string) {
if (label === 'json') {
return new jsonWorker.default()
}
if (label === 'css' || label === 'scss' || label === 'less') {
return new cssWorker.default()
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new htmlWorker.default()
}
if (label === 'typescript' || label === 'javascript') {
return new tsWorker.default()
}
return new editorWorker.default()
}
}
}
/**
* 获取 Monaco 实例
* @param locale 可选的语言设置
* @returns Monaco 实例
*/
export async function getMonaco(locale?: string): Promise<typeof Monaco> {
if (isInitialized && monacoInstance) {
return monacoInstance
}
if (initPromise) {
return initPromise
}
initPromise = (async () => {
if (locale) {
await loadMonacoLocale(locale)
}
await setupMonacoWorkers()
const monaco = await import('monaco-editor')
await import('monaco-editor-nginx')
monacoInstance = monaco
isInitialized = true
return monaco
})()
return initPromise
}
export type { Monaco }

36
web/types/monaco.d.ts vendored Normal file
View File

@@ -0,0 +1,36 @@
// Monaco Editor 本地化模块声明
declare module 'monaco-editor/esm/nls.messages.cs.js'
declare module 'monaco-editor/esm/nls.messages.de.js'
declare module 'monaco-editor/esm/nls.messages.es.js'
declare module 'monaco-editor/esm/nls.messages.fr.js'
declare module 'monaco-editor/esm/nls.messages.it.js'
declare module 'monaco-editor/esm/nls.messages.ja.js'
declare module 'monaco-editor/esm/nls.messages.ko.js'
declare module 'monaco-editor/esm/nls.messages.pl.js'
declare module 'monaco-editor/esm/nls.messages.pt-br.js'
declare module 'monaco-editor/esm/nls.messages.ru.js'
declare module 'monaco-editor/esm/nls.messages.tr.js'
declare module 'monaco-editor/esm/nls.messages.zh-cn.js'
declare module 'monaco-editor/esm/nls.messages.zh-tw.js'
// Monaco Editor Worker 模块声明
declare module 'monaco-editor/esm/vs/editor/editor.worker?worker' {
const EditorWorker: new () => Worker
export default EditorWorker
}
declare module 'monaco-editor/esm/vs/language/json/json.worker?worker' {
const JsonWorker: new () => Worker
export default JsonWorker
}
declare module 'monaco-editor/esm/vs/language/css/css.worker?worker' {
const CssWorker: new () => Worker
export default CssWorker
}
declare module 'monaco-editor/esm/vs/language/html/html.worker?worker' {
const HtmlWorker: new () => Worker
export default HtmlWorker
}
declare module 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' {
const TsWorker: new () => Worker
export default TsWorker
}