From 5feb2ea228063dd8b5941ca505901301ad391c99 Mon Sep 17 00:00:00 2001 From: dtoro Date: Fri, 6 Mar 2026 23:22:48 +0100 Subject: [PATCH] replace monaco with codemirror --- package-lock.json | 319 ++++++++++++++++++++++---- package.json | 5 +- src/components/graph/ConfigNode.tsx | 212 ++++------------- src/components/graph/FunctionNode.tsx | 48 ++-- 4 files changed, 341 insertions(+), 243 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4d3dda9..64eadec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,19 +8,20 @@ "name": "reactflow-shadcn-app", "version": "0.1.0", "dependencies": { - "@monaco-editor/react": "^4.7.0", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-yaml": "^6.0.2", "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-menubar": "^1.1.16", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-switch": "^1.2.6", + "@uiw/react-codemirror": "^4.25.7", "@xyflow/react": "^12.10.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "js-yaml": "^4.1.0", "lucide-react": "^0.577.0", - "monaco-editor": "^0.53.0", "react": "18.2.0", "react-dom": "18.2.0", "reactflow": "^11.0.0", @@ -492,6 +493,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -540,6 +550,129 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", + "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.2.tgz", + "integrity": "sha512-vvX1fsih9HledO1c9zdotZYUZnE4xV0m6i3m25s5DIfXofuprk6cRcLUZvSk3CASUbwjQX21tOGbkY2BH8TpnQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-yaml": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz", + "integrity": "sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.2.0", + "@lezer/lr": "^1.0.0", + "@lezer/yaml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.2.tgz", + "integrity": "sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.5.tgz", + "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", + "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz", + "integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.39.16", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.16.tgz", + "integrity": "sha512-m6S22fFpKtOWhq8HuhzsI1WzUP/hB9THbDj0Tl5KX4gbO6Y91hwBl7Yky33NdvB6IffuRFiBxf1R8kJMyXmA4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@dotenvx/dotenvx": { "version": "1.53.0", "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.53.0.tgz", @@ -1322,6 +1455,58 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz", + "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", + "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.27.1", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", @@ -1363,29 +1548,6 @@ } } }, - "node_modules/@monaco-editor/loader": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", - "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", - "license": "MIT", - "dependencies": { - "state-local": "^1.0.6" - } - }, - "node_modules/@monaco-editor/react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", - "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", - "license": "MIT", - "dependencies": { - "@monaco-editor/loader": "^1.5.0" - }, - "peerDependencies": { - "monaco-editor": ">= 0.25.0 < 1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/@mswjs/interceptors": { "version": "0.41.3", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", @@ -3158,12 +3320,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/trusted-types": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz", - "integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==", - "license": "MIT" - }, "node_modules/@types/validate-npm-package-name": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-4.0.2.tgz", @@ -3171,6 +3327,59 @@ "dev": true, "license": "MIT" }, + "node_modules/@uiw/codemirror-extensions-basic-setup": { + "version": "4.25.7", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.7.tgz", + "integrity": "sha512-tPV/AGjF4yM22D5mnyH7EuYBkWO05wF5Y4x3lmQJo6LuHmhjh0RQsVDjqeIgNOkXT3UO9OdkL4dzxw465/JZVg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/autocomplete": ">=6.0.0", + "@codemirror/commands": ">=6.0.0", + "@codemirror/language": ">=6.0.0", + "@codemirror/lint": ">=6.0.0", + "@codemirror/search": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/react-codemirror": { + "version": "4.25.7", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.7.tgz", + "integrity": "sha512-s/EbEe0dFANWEgfLbfdIrrOGv0R7M1XhkKG3ShroBeH6uP9pVNQy81YHOLRCSVcytTp9zAWRNfXR/+XxZTvV7w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@codemirror/commands": "^6.1.0", + "@codemirror/state": "^6.1.1", + "@codemirror/theme-one-dark": "^6.0.0", + "@uiw/codemirror-extensions-basic-setup": "4.25.7", + "codemirror": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.11.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/theme-one-dark": ">=6.0.0", + "@codemirror/view": ">=6.0.0", + "codemirror": ">=6.0.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", @@ -3831,6 +4040,21 @@ "dev": true, "license": "MIT" }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3957,6 +4181,12 @@ } } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5705,15 +5935,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/monaco-editor": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0.tgz", - "integrity": "sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==", - "license": "MIT", - "dependencies": { - "@types/trusted-types": "^1.0.6" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7114,12 +7335,6 @@ "node": ">=0.10.0" } }, - "node_modules/state-local": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", - "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", - "license": "MIT" - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -7225,6 +7440,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -7788,6 +8009,12 @@ } } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", diff --git a/package.json b/package.json index a35ea16..bdb65d3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "preview": "vite preview" }, "dependencies": { - "@monaco-editor/react": "^4.7.0", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-yaml": "^6.0.2", + "@uiw/react-codemirror": "^4.25.7", "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-menubar": "^1.1.16", "@radix-ui/react-select": "^2.2.6", @@ -20,7 +22,6 @@ "clsx": "^2.1.1", "js-yaml": "^4.1.0", "lucide-react": "^0.577.0", - "monaco-editor": "^0.53.0", "react": "18.2.0", "react-dom": "18.2.0", "reactflow": "^11.0.0", diff --git a/src/components/graph/ConfigNode.tsx b/src/components/graph/ConfigNode.tsx index 75804dc..f37112a 100644 --- a/src/components/graph/ConfigNode.tsx +++ b/src/components/graph/ConfigNode.tsx @@ -1,5 +1,6 @@ -import React, { memo, useCallback, useContext, useEffect, useRef, useState } from 'react' -import Editor, { loader } from '@monaco-editor/react' +import React, { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' +import CodeMirror from '@uiw/react-codemirror' +import { yaml } from '@codemirror/lang-yaml' import FlowContext from '../../lib/flowContext' import { BaseNode, @@ -13,9 +14,7 @@ import { Menubar, MenubarContent, MenubarItem, - MenubarLabel, MenubarMenu, - MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, @@ -23,9 +22,6 @@ import { } from '../ui/menubar' import { InputHandle, OutputHandle } from './NodeHandles' -// Ensure Monaco can load its web workers under Vite by pointing to a CDN -loader.config({ paths: { vs: 'https://unpkg.com/monaco-editor@latest/min/vs' } }) - type Props = { id: string data: any @@ -38,8 +34,7 @@ export const ConfigNode = memo(function ConfigNode({ id, data }: Props) { const setNodes = ctx?.setNodes const storedYaml = ctx?.nodes?.find((n: any) => n.id === id)?.data?.yaml ?? '' - const editorRef = useRef(null) - const monacoRef = useRef(null) + const editorRef = useRef(null) const incomingEdges = ctx?.edges?.filter((e: any) => e.target === id) ?? [] const incomingIds = incomingEdges.map((e: any) => e.source).sort() @@ -56,166 +51,69 @@ export const ConfigNode = memo(function ConfigNode({ id, data }: Props) { }, []) const onChange = useCallback( - (val?: string) => { - const v = val ?? '' - setValue(v) - // eslint-disable-next-line no-console - console.debug('ConfigNode:onChange', id, v.substring(0, 60)) + (val: string) => { + setValue(val) if (setNodes) { - setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, yaml: v } } : n))) + setNodes((nds: any[]) => nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, yaml: val } } : n))) } }, [id, setNodes] ) - const handleEditorMount = useCallback((editor: any, monaco: any) => { - editorRef.current = editor - monacoRef.current = monaco - - // Prevent React Flow and other global handlers from interfering with typing - try { - editor.onKeyDown((e: any) => { - if (e?.browserEvent) { - e.browserEvent.stopPropagation() + const insertAt = useCallback( + (insertText: string, mode: 'prepend' | 'append' | 'cursor') => { + const ref = editorRef.current as { view: { state: { doc: { length: number; toString(): string }; selection: { main: { from: number } } }; dispatch: (arg: { changes: { from: number; to: number; insert: string } }) => void } } | null + if (ref?.view) { + const view = ref.view + const doc = view.state.doc + const len = doc.length + let from: number + if (mode === 'prepend') { + from = 0 + } else if (mode === 'append') { + from = len + } else { + const main = view.state.selection.main + from = main.from } - }) - editor.onMouseDown((e: any) => { - if (e?.event) { - e.event.stopPropagation() - } - }) - } catch { - // ignore if Monaco internals change - } - }, []) + view.dispatch({ changes: { from, to: from, insert: insertText } }) + const newVal = view.state.doc.toString() + onChange(newVal) + return + } + if (mode === 'prepend') { + onChange(insertText + value) + } else { + onChange(value + insertText) + } + }, + [onChange, value] + ) const insertIncludeFromNode = useCallback( (sourceNode: any, mode: 'prepend' | 'append' | 'cursor') => { const ref = `${sourceNode.id}.yaml` - const includeText = `!include ${ref}\n` - const editor = editorRef.current - const monaco = monacoRef.current - - try { - if (editor && monaco) { - const model = editor.getModel() - const lineCount = model.getLineCount() - const selection = editor.getSelection() - - let range - if (mode === 'prepend') { - range = new monaco.Range(1, 1, 1, 1) - } else if (mode === 'append') { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } else if (selection) { - range = new monaco.Range(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn) - } else { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } - - editor.executeEdits('insert-include', [{ range, text: includeText, forceMoveMarkers: true }]) - const newVal = editor.getModel().getValue() - onChange(newVal) - return - } - - if (mode === 'prepend') { - onChange(includeText + value) - } else { - onChange(value + '\n' + includeText) - } - } catch (err) { - // eslint-disable-next-line no-console - console.error('Insert include failed', err) - } + insertAt(`!include ${ref}\n`, mode) }, - [onChange, value] + [insertAt] ) const insertVariableReference = useCallback( (sourceNode: any, mode: 'prepend' | 'append' | 'cursor') => { - const insertText = `\${${sourceNode.id}}` - const editor = editorRef.current - const monaco = monacoRef.current - - try { - if (editor && monaco) { - const model = editor.getModel() - const lineCount = model.getLineCount() - const selection = editor.getSelection() - - let range - if (mode === 'prepend') { - range = new monaco.Range(1, 1, 1, 1) - } else if (mode === 'append') { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } else if (selection) { - range = new monaco.Range(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn) - } else { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } - - editor.executeEdits('insert-var', [{ range, text: insertText, forceMoveMarkers: true }]) - const newVal = editor.getModel().getValue() - onChange(newVal) - return - } - - if (mode === 'prepend') { - onChange(insertText + value) - } else { - onChange(value + insertText) - } - } catch (err) { - // eslint-disable-next-line no-console - console.error('Insert variable reference failed', err) - } + insertAt(`\${${sourceNode.id}}`, mode) }, - [onChange, value] + [insertAt] ) const insertFunctionCall = useCallback( (sourceNode: any, mode: 'prepend' | 'append' | 'cursor') => { - const insertText = `\${${sourceNode.id}()}` // user can add variable ids inside: ${funcId(var1, var2)} - const editor = editorRef.current - const monaco = monacoRef.current - - try { - if (editor && monaco) { - const model = editor.getModel() - const lineCount = model.getLineCount() - const selection = editor.getSelection() - - let range - if (mode === 'prepend') { - range = new monaco.Range(1, 1, 1, 1) - } else if (mode === 'append') { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } else if (selection) { - range = new monaco.Range(selection.startLineNumber, selection.startColumn, selection.startLineNumber, selection.startColumn) - } else { - range = new monaco.Range(lineCount + 1, 1, lineCount + 1, 1) - } - - editor.executeEdits('insert-fn', [{ range, text: insertText, forceMoveMarkers: true }]) - const newVal = editor.getModel().getValue() - onChange(newVal) - return - } - - if (mode === 'prepend') { - onChange(insertText + value) - } else { - onChange(value + insertText) - } - } catch (err) { - // eslint-disable-next-line no-console - console.error('Insert function call failed', err) - } + insertAt(`\${${sourceNode.id}()}`, mode) }, - [onChange, value] + [insertAt] ) + const extensions = useMemo(() => [yaml()], []) + return ( @@ -295,24 +193,16 @@ export const ConfigNode = memo(function ConfigNode({ id, data }: Props) { )} -
- +
diff --git a/src/components/graph/FunctionNode.tsx b/src/components/graph/FunctionNode.tsx index 6bbc2fd..0964032 100644 --- a/src/components/graph/FunctionNode.tsx +++ b/src/components/graph/FunctionNode.tsx @@ -1,5 +1,6 @@ -import React, { memo, useCallback, useContext, useEffect, useRef, useState } from 'react' -import Editor, { loader } from '@monaco-editor/react' +import React, { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' +import CodeMirror from '@uiw/react-codemirror' +import { javascript } from '@codemirror/lang-javascript' import FlowContext from '../../lib/flowContext' import { BaseNode, @@ -11,8 +12,6 @@ import { import { InputHandle, OutputHandle } from './NodeHandles' import { Code2 } from 'lucide-react' -loader.config({ paths: { vs: 'https://unpkg.com/monaco-editor@latest/min/vs' } }) - type Props = { id: string data: { body?: string } @@ -26,8 +25,6 @@ export const FunctionNode = memo(function FunctionNode({ id, data }: Props) { const [value, setValue] = useState(data?.body ?? DEFAULT_BODY) const ctx = useContext(FlowContext) const setNodes = ctx?.setNodes - const editorRef = useRef(null) - const monacoRef = useRef(null) useEffect(() => { if (setNodes) { @@ -39,29 +36,21 @@ export const FunctionNode = memo(function FunctionNode({ id, data }: Props) { }, []) const onChange = useCallback( - (val?: string) => { - const v = val ?? '' - setValue(v) + (val: string) => { + setValue(val) if (setNodes) { setNodes((nds: any[]) => - nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, body: v } } : n)) + nds.map((n) => (n.id === id ? { ...n, data: { ...n.data, body: val } } : n)) ) } }, [id, setNodes] ) - const handleEditorMount = useCallback((editor: any, monaco: any) => { - editorRef.current = editor - monacoRef.current = monaco - try { - editor.onKeyDown((e: any) => e?.browserEvent?.stopPropagation()) - editor.onMouseDown((e: any) => e?.event?.stopPropagation()) - } catch {} - }, []) - const storedBody = ctx?.nodes?.find((n: any) => n.id === id)?.data?.body ?? '' + const extensions = useMemo(() => [javascript()], []) + return ( @@ -73,23 +62,14 @@ export const FunctionNode = memo(function FunctionNode({ id, data }: Props) {

Call in config: ${{id}(var1, var2)}

-
- +