diff --git a/frontend/app/package.json b/frontend/app/package.json index 917492f873..8aded3f5c1 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -69,6 +69,7 @@ "js-confetti": "^0.13.1", "lodash": "^4.18.1", "lucide-react": "^1.8.0", + "monaco-editor": "^0.55.1", "monaco-themes": "^0.4.8", "ms": "^2.1.3", "posthog-js": "^1.369.2", diff --git a/frontend/app/pnpm-lock.yaml b/frontend/app/pnpm-lock.yaml index 87ce4a5864..098266fbf0 100644 --- a/frontend/app/pnpm-lock.yaml +++ b/frontend/app/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: version: 6.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(solid-js@1.9.10) '@monaco-editor/react': specifier: ^4.7.0 - version: 4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.7.0(monaco-editor@0.55.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-accordion': specifier: ^1.2.3 version: 1.2.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -160,6 +160,9 @@ importers: lucide-react: specifier: ^1.8.0 version: 1.8.0(react@18.3.1) + monaco-editor: + specifier: ^0.55.1 + version: 0.55.1 monaco-themes: specifier: ^0.4.8 version: 0.4.8 @@ -2686,6 +2689,9 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dompurify@3.2.7: + resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} + dompurify@3.4.11: resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==} @@ -3531,6 +3537,11 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} + marked@14.0.0: + resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==} + engines: {node: '>= 18'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -3583,8 +3594,8 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - monaco-editor@0.52.2: - resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + monaco-editor@0.55.1: + resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==} monaco-themes@0.4.8: resolution: {integrity: sha512-QEDCwl5XUMPPpsg8TVNfi1VKIDtMsMTJAhUfxHSZ6hzkEfZgfTK2hicwyzkDKhAhrpMkHkwMtG13ynvdztJesw==} @@ -5077,10 +5088,10 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@monaco-editor/loader': 1.5.0 - monaco-editor: 0.52.2 + monaco-editor: 0.55.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -7063,6 +7074,10 @@ snapshots: '@babel/runtime': 7.28.3 csstype: 3.1.3 + dompurify@3.2.7: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dompurify@3.4.11: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -8063,6 +8078,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked@14.0.0: {} + math-intrinsics@1.1.0: {} merge-stream@2.0.0: {} @@ -8102,7 +8119,10 @@ snapshots: mitt@3.0.1: {} - monaco-editor@0.52.2: {} + monaco-editor@0.55.1: + dependencies: + dompurify: 3.2.7 + marked: 14.0.0 monaco-themes@0.4.8: dependencies: diff --git a/frontend/app/src/components/v1/ui/code-editor.tsx b/frontend/app/src/components/v1/ui/code-editor.tsx index 04927a25cc..23bb1b5242 100644 --- a/frontend/app/src/components/v1/ui/code-editor.tsx +++ b/frontend/app/src/components/v1/ui/code-editor.tsx @@ -1,5 +1,6 @@ import CopyToClipboard from './copy-to-clipboard'; import { useTheme } from '@/components/hooks/use-theme'; +import '@/lib/monaco-environment'; import { cn } from '@/lib/utils'; import Editor, { Monaco, OnMount } from '@monaco-editor/react'; import { useCallback, useEffect, useId, useRef } from 'react'; @@ -49,7 +50,8 @@ export function CodeEditor({ const existingSchemas = existingOptions.schemas || []; const otherSchemas = existingSchemas.filter( - (s) => !s.fileMatch?.includes(modelPath), + (s: (typeof existingSchemas)[number]) => + !s.fileMatch?.includes(modelPath), ); const newSchemas = hasJsonSchema diff --git a/frontend/app/src/lib/monaco-environment.ts b/frontend/app/src/lib/monaco-environment.ts new file mode 100644 index 0000000000..b75802e4ea --- /dev/null +++ b/frontend/app/src/lib/monaco-environment.ts @@ -0,0 +1,19 @@ +// Bundles Monaco locally instead of letting @monaco-editor/react fetch it +// from cdn.jsdelivr.net at runtime, which breaks air-gapped/self-hosted +// deployments. Importing this module configures both the editor's worker +// environment and the loader before any instance mounts. +import { loader } from '@monaco-editor/react'; +import * as monaco from 'monaco-editor'; +import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; +import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; + +self.MonacoEnvironment = { + getWorker(_workerId, label) { + if (label === 'json') { + return new jsonWorker(); + } + return new editorWorker(); + }, +}; + +loader.config({ monaco });