diff --git a/web/app/components/base/modern-monaco/init.ts b/web/app/components/base/modern-monaco/init.ts index 521f589cd7..92f366d8b6 100644 --- a/web/app/components/base/modern-monaco/init.ts +++ b/web/app/components/base/modern-monaco/init.ts @@ -12,6 +12,7 @@ export const LIGHT_THEME_ID = 'light-plus' export const DARK_THEME_ID = 'dark-plus' const assetPath = (pathname: string) => `${basePath}${HOIST_BASE_PATH}${pathname}` +const modernMonacoEntryPath = assetPath('/modern-monaco/index.mjs') const themeAssetPath = (themeId: string) => assetPath(`/tm-themes@${TM_THEMES_VERSION}/themes/${themeId}.json`) const grammarAssetPath = (languageId: string) => assetPath(`/tm-grammars@${TM_GRAMMARS_VERSION}/grammars/${languageId}.json`) @@ -26,7 +27,10 @@ let monacoInitPromise: Promise { if (!monacoInitPromise) { monacoInitPromise = (async () => { - const { init } = await import('modern-monaco') + const { init } = await import( + /* webpackIgnore: true */ + modernMonacoEntryPath, + ) as typeof import('modern-monaco') return init(DEFAULT_INIT_OPTIONS) })() } diff --git a/web/app/components/base/modern-monaco/modern-monaco-editor.tsx b/web/app/components/base/modern-monaco/modern-monaco-editor.tsx index 726429d346..ee9fd5a767 100644 --- a/web/app/components/base/modern-monaco/modern-monaco-editor.tsx +++ b/web/app/components/base/modern-monaco/modern-monaco-editor.tsx @@ -108,6 +108,7 @@ export const ModernMonacoEditor: FC = ({ const { theme: appTheme } = useTheme() const resolvedTheme = appTheme === Theme.light ? LIGHT_THEME_ID : DARK_THEME_ID const [isEditorReady, setIsEditorReady] = useState(false) + const [isEditorInitFailed, setIsEditorInitFailed] = useState(false) const containerRef = useRef(null) const editorRef = useRef(null) const modelRef = useRef(null) @@ -153,41 +154,50 @@ export const ModernMonacoEditor: FC = ({ let cleanup: (() => void) | undefined const setup = async () => { - const monaco = await initMonaco() - if (!monaco || disposed || !containerRef.current) - return + try { + setIsEditorInitFailed(false) + const monaco = await initMonaco() + if (!monaco || disposed || !containerRef.current) + return - monacoRef.current = monaco + monacoRef.current = monaco - const editor = monaco.editor.create(containerRef.current, setupRef.current.editorOptions) - editorRef.current = editor + const editor = monaco.editor.create(containerRef.current, setupRef.current.editorOptions) + editorRef.current = editor - const model = monaco.editor.createModel(valueRef.current, setupRef.current.language) - modelRef.current = model + const model = monaco.editor.createModel(valueRef.current, setupRef.current.language) + modelRef.current = model - editor.setModel(model) + editor.setModel(model) - monaco.editor.setTheme(setupRef.current.resolvedTheme) + monaco.editor.setTheme(setupRef.current.resolvedTheme) - const disposeCallbacks = bindEditorCallbacks( - editor, - monaco, - callbacksRef, - preventTriggerChangeEventRef, - ) - const resizeObserver = new ResizeObserver(() => { - editor.layout() - }) - resizeObserver.observe(containerRef.current) - callbacksRef.current.onReady?.(editor, monaco) - setIsEditorReady(true) + const disposeCallbacks = bindEditorCallbacks( + editor, + monaco, + callbacksRef, + preventTriggerChangeEventRef, + ) + const resizeObserver = new ResizeObserver(() => { + editor.layout() + }) + resizeObserver.observe(containerRef.current) + callbacksRef.current.onReady?.(editor, monaco) + setIsEditorReady(true) - cleanup = () => { - resizeObserver.disconnect() - disposeCallbacks() - editor.dispose() - model.dispose() + cleanup = () => { + resizeObserver.disconnect() + disposeCallbacks() + editor.dispose() + model.dispose() + setIsEditorReady(false) + setIsEditorInitFailed(false) + } + } + catch (error) { + console.error('[modern-monaco] failed to initialize editor', error) setIsEditorReady(false) + setIsEditorInitFailed(true) } } @@ -240,7 +250,7 @@ export const ModernMonacoEditor: FC = ({ ref={containerRef} className="h-full w-full" /> - {!isEditorReady && !!loading && ( + {!isEditorReady && !isEditorInitFailed && !!loading && (
{loading}
diff --git a/web/public/hoisted-modern-monaco/modern-monaco/core.mjs b/web/public/hoisted-modern-monaco/modern-monaco/core.mjs index 658e903805..21eb9f0b99 100644 --- a/web/public/hoisted-modern-monaco/modern-monaco/core.mjs +++ b/web/public/hoisted-modern-monaco/modern-monaco/core.mjs @@ -287,9 +287,8 @@ function hydrate(options) { return lazy(options); } async function loadMonaco(highlighter, workspace, lsp) { - let cdnUrl = `https://esm.sh/modern-monaco@${version}`; - let editorCoreModuleUrl = `${cdnUrl}/es2022/editor-core.mjs`; - let lspModuleUrl = `${cdnUrl}/es2022/lsp.mjs`; + let editorCoreModuleUrl = new URL("./editor-core.mjs", import.meta.url).toString(); + let lspModuleUrl = new URL("./lsp/index.mjs", import.meta.url).toString(); let importmapEl = null; if (importmapEl = document.querySelector("script[type='importmap']")) { try { diff --git a/web/public/hoisted-modern-monaco/modern-monaco/hoist-manifest.json b/web/public/hoisted-modern-monaco/modern-monaco/hoist-manifest.json index 8ff8b187e2..84b8eed9d5 100644 --- a/web/public/hoisted-modern-monaco/modern-monaco/hoist-manifest.json +++ b/web/public/hoisted-modern-monaco/modern-monaco/hoist-manifest.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-03-17T02:53:27.971Z", + "generatedAt": "2026-03-17T04:30:42.254Z", "modernMonacoVersion": "0.4.0", "tmGrammarsVersion": "1.31.2", "tmThemesVersion": "1.12.1", diff --git a/web/scripts/hoist-modern-monaco.ts b/web/scripts/hoist-modern-monaco.ts index eb7036365c..d5bc2b9d33 100644 --- a/web/scripts/hoist-modern-monaco.ts +++ b/web/scripts/hoist-modern-monaco.ts @@ -222,6 +222,22 @@ async function patchShikiAssetLoaders(shikiFilePath: string) { await writeFile(shikiFilePath, next) } +async function patchCoreModuleLoaders(coreFilePath: string) { + const original = await readFile(coreFilePath, 'utf8') + const next = original.replace( + ` let cdnUrl = \`https://esm.sh/modern-monaco@\${version}\`; + let editorCoreModuleUrl = \`\${cdnUrl}/es2022/editor-core.mjs\`; + let lspModuleUrl = \`\${cdnUrl}/es2022/lsp.mjs\`;`, + ` let editorCoreModuleUrl = new URL("./editor-core.mjs", import.meta.url).toString(); + let lspModuleUrl = new URL("./lsp/index.mjs", import.meta.url).toString();`, + ) + + if (next === original) + throw new Error('Failed to patch modern-monaco core module loaders') + + await writeFile(coreFilePath, next) +} + async function writeManifest(filePath: string, manifest: object) { await writeFile(filePath, `${JSON.stringify(manifest, null, 2)}\n`) } @@ -301,6 +317,7 @@ async function main() { await rm(MODERN_MONACO_PUBLIC_DIR, { force: true, recursive: true }) await cp(MODERN_MONACO_DIST_DIR, MODERN_MONACO_PUBLIC_DIR, { recursive: true }) await patchShikiAssetLoaders(path.join(MODERN_MONACO_PUBLIC_DIR, 'shiki.mjs')) + await patchCoreModuleLoaders(path.join(MODERN_MONACO_PUBLIC_DIR, 'core.mjs')) log(`Downloading typescript ESM -> ${localTypeScriptPath}`) await writeResponseToFile(`${ESM_SH}${localTypeScriptPath}`, typeScriptPublicPath)