diff --git a/apps/web/package.json b/apps/web/package.json
index 760c688..8d31720 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -11,6 +11,9 @@
"clean": "rimraf node_modules .next .turbo"
},
"dependencies": {
+ "@codemirror/lang-html": "^6.4.11",
+ "@codemirror/theme-one-dark": "^6.1.3",
+ "@codemirror/view": "^6.39.0",
"@plunk/db": "*",
"@plunk/shared": "*",
"@plunk/ui": "*",
@@ -31,6 +34,7 @@
"@tiptap/starter-kit": "^3.11.0",
"@tiptap/suggestion": "^3.11.0",
"@types/dagre": "^0.7.53",
+ "@uiw/react-codemirror": "^4.25.3",
"@xyflow/react": "^12.9.3",
"dagre": "^0.8.5",
"framer-motion": "^12.23.24",
diff --git a/apps/web/src/components/EmailEditor/EmailEditor.tsx b/apps/web/src/components/EmailEditor/EmailEditor.tsx
index 150bac1..f5b3de4 100644
--- a/apps/web/src/components/EmailEditor/EmailEditor.tsx
+++ b/apps/web/src/components/EmailEditor/EmailEditor.tsx
@@ -10,6 +10,7 @@ import {Variable} from './VariableExtension';
import {setAvailableVariables, VariableMention} from './VariableMention';
import {Toolbar} from './Toolbar';
import {ResizableImage} from './ResizableImage';
+import {HtmlEditor} from './HtmlEditor';
import {useContactFields, useContacts} from '../../lib/hooks/useContacts';
import {useConfig} from '../../lib/hooks/useConfig';
import {useEffect, useRef, useState} from 'react';
@@ -556,12 +557,11 @@ export function EmailEditor({value, onChange, placeholder, subject, from, replyT
>
) : (
<>
-
-
{selectedContactId && (
diff --git a/apps/web/src/components/EmailEditor/HtmlEditor.tsx b/apps/web/src/components/EmailEditor/HtmlEditor.tsx
new file mode 100644
index 0000000..faa0210
--- /dev/null
+++ b/apps/web/src/components/EmailEditor/HtmlEditor.tsx
@@ -0,0 +1,103 @@
+import CodeMirror from '@uiw/react-codemirror';
+import {html} from '@codemirror/lang-html';
+import {EditorView} from '@codemirror/view';
+import {useMemo} from 'react';
+
+interface HtmlEditorProps {
+ value: string;
+ onChange: (value: string) => void;
+ placeholder?: string;
+}
+
+export function HtmlEditor({value, onChange, placeholder}: HtmlEditorProps) {
+ // Custom theme configuration for better integration with the app
+ const theme = EditorView.theme({
+ '&': {
+ fontSize: '14px',
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
+ height: '100%',
+ },
+ '.cm-content': {
+ minHeight: '400px',
+ padding: '12px 16px',
+ caretColor: '#1f2937',
+ },
+ '.cm-line': {
+ padding: '0',
+ lineHeight: '1.6',
+ },
+ '.cm-gutters': {
+ backgroundColor: '#f9fafb',
+ color: '#9ca3af',
+ border: 'none',
+ borderRight: '1px solid #e5e7eb',
+ paddingRight: '8px',
+ paddingLeft: '8px',
+ },
+ '.cm-activeLineGutter': {
+ backgroundColor: '#f3f4f6',
+ color: '#374151',
+ },
+ '.cm-activeLine': {
+ backgroundColor: '#f9fafb',
+ },
+ '.cm-selectionBackground, ::selection': {
+ backgroundColor: '#dbeafe !important',
+ },
+ '.cm-focused .cm-selectionBackground, .cm-focused ::selection': {
+ backgroundColor: '#bfdbfe !important',
+ },
+ '.cm-cursor': {
+ borderLeftColor: '#1f2937',
+ },
+ '&.cm-focused': {
+ outline: 'none',
+ },
+ '.cm-scroller': {
+ overflow: 'auto',
+ maxHeight: '600px',
+ },
+ });
+
+ // Extensions with HTML support and line numbers
+ const extensions = useMemo(
+ () => [
+ html(),
+ theme,
+ EditorView.lineWrapping, // Enable line wrapping for long lines
+ ],
+ [theme]
+ );
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/src/pages/campaigns/[id].tsx b/apps/web/src/pages/campaigns/[id].tsx
index 172c71a..a9f26f5 100644
--- a/apps/web/src/pages/campaigns/[id].tsx
+++ b/apps/web/src/pages/campaigns/[id].tsx
@@ -577,7 +577,6 @@ export default function CampaignDetailsPage() {
setEditedCampaign({...editedCampaign, body});
setHasChanges(true);
}}
- placeholder="Welcome!
Your email content here...
"
/>
diff --git a/apps/web/src/pages/campaigns/create.tsx b/apps/web/src/pages/campaigns/create.tsx
index edcac9c..890f060 100644
--- a/apps/web/src/pages/campaigns/create.tsx
+++ b/apps/web/src/pages/campaigns/create.tsx
@@ -197,11 +197,7 @@ export default function CreateCampaignPage() {
-
+
diff --git a/yarn.lock b/yarn.lock
index bbc8935..5130783 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -957,6 +957,13 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.18.6":
+ version: 7.28.4
+ resolution: "@babel/runtime@npm:7.28.4"
+ checksum: 10c0/792ce7af9750fb9b93879cc9d1db175701c4689da890e6ced242ea0207c9da411ccf16dc04e689cc01158b28d7898c40d75598f4559109f761c12ce01e959bf7
+ languageName: node
+ linkType: hard
+
"@babel/template@npm:^7.26.9, @babel/template@npm:^7.27.0, @babel/template@npm:^7.27.2":
version: 7.27.2
resolution: "@babel/template@npm:7.27.2"
@@ -1015,6 +1022,144 @@ __metadata:
languageName: node
linkType: hard
+"@codemirror/autocomplete@npm:^6.0.0":
+ version: 6.20.0
+ resolution: "@codemirror/autocomplete@npm:6.20.0"
+ dependencies:
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.17.0"
+ "@lezer/common": "npm:^1.0.0"
+ checksum: 10c0/d0d1cf3eca6269811eb66edcf742ffa0a5423d7d115ab82b0d62a24d6cfcfb2a4c3779333b2cb68e3004af46556ac6203049f581d35785c46ffd1b852f6e8076
+ languageName: node
+ linkType: hard
+
+"@codemirror/commands@npm:^6.0.0, @codemirror/commands@npm:^6.1.0":
+ version: 6.10.0
+ resolution: "@codemirror/commands@npm:6.10.0"
+ dependencies:
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.4.0"
+ "@codemirror/view": "npm:^6.27.0"
+ "@lezer/common": "npm:^1.1.0"
+ checksum: 10c0/5122c026ef3578049a9d166a0afe9cd66abcc20fd5a2a084c97a60b519db6f38e3b8f05af813e5bdbbf249d3a95dce07a52cd30f77083da4a2da660c1b424a88
+ languageName: node
+ linkType: hard
+
+"@codemirror/lang-css@npm:^6.0.0":
+ version: 6.3.1
+ resolution: "@codemirror/lang-css@npm:6.3.1"
+ dependencies:
+ "@codemirror/autocomplete": "npm:^6.0.0"
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@lezer/common": "npm:^1.0.2"
+ "@lezer/css": "npm:^1.1.7"
+ checksum: 10c0/339387c5a1b90076ae41017e66d7da70dd2aca4e5e4d012c95df33d0f6e740410cf1fb53c4845e3814636d587ce6eff05ebca3173dcfc564a1f646d24f299180
+ languageName: node
+ linkType: hard
+
+"@codemirror/lang-html@npm:^6.4.11":
+ version: 6.4.11
+ resolution: "@codemirror/lang-html@npm:6.4.11"
+ dependencies:
+ "@codemirror/autocomplete": "npm:^6.0.0"
+ "@codemirror/lang-css": "npm:^6.0.0"
+ "@codemirror/lang-javascript": "npm:^6.0.0"
+ "@codemirror/language": "npm:^6.4.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.17.0"
+ "@lezer/common": "npm:^1.0.0"
+ "@lezer/css": "npm:^1.1.0"
+ "@lezer/html": "npm:^1.3.12"
+ checksum: 10c0/d117b5c91377dd81e6551cbfaf881138787826c0d0c90fc2a7a69550decfc49af193ec1cd5c12b90d91de1acf09c42d4c5fec5bea848d354246b4ec778a4a88f
+ languageName: node
+ linkType: hard
+
+"@codemirror/lang-javascript@npm:^6.0.0":
+ version: 6.2.4
+ resolution: "@codemirror/lang-javascript@npm:6.2.4"
+ dependencies:
+ "@codemirror/autocomplete": "npm:^6.0.0"
+ "@codemirror/language": "npm:^6.6.0"
+ "@codemirror/lint": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.17.0"
+ "@lezer/common": "npm:^1.0.0"
+ "@lezer/javascript": "npm:^1.0.0"
+ checksum: 10c0/af6faaa9566c57e233459d48e8afbdbf99b6d666695fa49b2d352cda15b6022edbe319e1b296f76526f48c313627bbcd2a340872e6627a17358edabc08e8e129
+ languageName: node
+ linkType: hard
+
+"@codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.4.0, @codemirror/language@npm:^6.6.0":
+ version: 6.11.3
+ resolution: "@codemirror/language@npm:6.11.3"
+ dependencies:
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.23.0"
+ "@lezer/common": "npm:^1.1.0"
+ "@lezer/highlight": "npm:^1.0.0"
+ "@lezer/lr": "npm:^1.0.0"
+ style-mod: "npm:^4.0.0"
+ checksum: 10c0/0cbc2a98bd9e94e8e186af30613741a553fc45479e3875c79bddc32340d4d75ecc36229c66e2ee5fcffbc8fdd49b442e43847799d1d68651e3b4f8ec20d3d092
+ languageName: node
+ linkType: hard
+
+"@codemirror/lint@npm:^6.0.0":
+ version: 6.9.2
+ resolution: "@codemirror/lint@npm:6.9.2"
+ dependencies:
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.35.0"
+ crelt: "npm:^1.0.5"
+ checksum: 10c0/f938391b6d216784d3196e9494c7d492d5524109fa36566708fba73f48e69d5d18332f7e9993f472eb34fe4271727b5636b6390abb804cae7ef70120427fece2
+ languageName: node
+ linkType: hard
+
+"@codemirror/search@npm:^6.0.0":
+ version: 6.5.11
+ resolution: "@codemirror/search@npm:6.5.11"
+ dependencies:
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.0.0"
+ crelt: "npm:^1.0.5"
+ checksum: 10c0/8f25647ceb9a255a6e5797c20ec787587537e8496f651d8815d3f8f6c9fc5bf586b6552dadfcc7ad24364c659fcd12315c5fa235a098ba15840bb76bed35cc09
+ languageName: node
+ linkType: hard
+
+"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0":
+ version: 6.5.2
+ resolution: "@codemirror/state@npm:6.5.2"
+ dependencies:
+ "@marijn/find-cluster-break": "npm:^1.0.0"
+ checksum: 10c0/1ef773394e32c077a8cfc1ec6d881aefb1918876f82161748e505c38d143aa1c6893c314cfec91097d28f704ec07b2a6c6b75abd435086208974256dee997282
+ languageName: node
+ linkType: hard
+
+"@codemirror/theme-one-dark@npm:^6.0.0, @codemirror/theme-one-dark@npm:^6.1.3":
+ version: 6.1.3
+ resolution: "@codemirror/theme-one-dark@npm:6.1.3"
+ dependencies:
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.0.0"
+ "@lezer/highlight": "npm:^1.0.0"
+ checksum: 10c0/de8483c69911bcd61a19679384de663ced9c8bed3c776f08581a8b724e9f456a17053b1cf6e9d1f2a475fa6bc42e905ec8ba1ee0a8b55213d18087d9d9150317
+ languageName: node
+ linkType: hard
+
+"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.35.0, @codemirror/view@npm:^6.39.0":
+ version: 6.39.0
+ resolution: "@codemirror/view@npm:6.39.0"
+ dependencies:
+ "@codemirror/state": "npm:^6.5.0"
+ crelt: "npm:^1.0.6"
+ style-mod: "npm:^4.1.0"
+ w3c-keyname: "npm:^2.2.4"
+ checksum: 10c0/04d4392d3d8a9cd8ab6855f18e7ad2a8f84f69102fbea1fb6dbba7e1d1447a7cb9907012cc84adcd2dc34bd1f11220fd48c07593083d9d5efd13b943cec53b2b
+ languageName: node
+ linkType: hard
+
"@corex/deepmerge@npm:^4.0.43":
version: 4.0.43
resolution: "@corex/deepmerge@npm:4.0.43"
@@ -2489,6 +2634,64 @@ __metadata:
languageName: node
linkType: hard
+"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0, @lezer/common@npm:^1.3.0":
+ version: 1.4.0
+ resolution: "@lezer/common@npm:1.4.0"
+ checksum: 10c0/6b7a0b5f9b969d5eb87b1a6407abf90809ed4cb290afa4ce0fd412102176144b1d01748f243b27fef1300e0444eb690a519395388cda936fbbb536fd0abb8ebc
+ languageName: node
+ linkType: hard
+
+"@lezer/css@npm:^1.1.0, @lezer/css@npm:^1.1.7":
+ version: 1.3.0
+ resolution: "@lezer/css@npm:1.3.0"
+ dependencies:
+ "@lezer/common": "npm:^1.2.0"
+ "@lezer/highlight": "npm:^1.0.0"
+ "@lezer/lr": "npm:^1.3.0"
+ checksum: 10c0/ca63614cbc1b3d884b80de86a76acfd35b19a0134ebeb0fb93f677f6dc034b79fb493168c3ef17143642264b59011dd28a26c6909861f02c8fd715e298477577
+ languageName: node
+ linkType: hard
+
+"@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.1.3":
+ version: 1.2.3
+ resolution: "@lezer/highlight@npm:1.2.3"
+ dependencies:
+ "@lezer/common": "npm:^1.3.0"
+ checksum: 10c0/3bcb4fce7a1a45b5973895d7cb2be47970a0098700f2a0970aef9878ffd37f540285a2d7388ec1f524726ec90cc5196b5701bbb9764b7e7300786d772b7d2ce2
+ languageName: node
+ linkType: hard
+
+"@lezer/html@npm:^1.3.12":
+ version: 1.3.12
+ resolution: "@lezer/html@npm:1.3.12"
+ dependencies:
+ "@lezer/common": "npm:^1.2.0"
+ "@lezer/highlight": "npm:^1.0.0"
+ "@lezer/lr": "npm:^1.0.0"
+ checksum: 10c0/d0e873e1b55e477462889eb388971b8ca7ef7318560b8f37c998c856a0cc203dc7b00bd55c93fd7f571eef8f5dd276bf2ff5fa7b14d9cb854324ed2466fa3f83
+ languageName: node
+ linkType: hard
+
+"@lezer/javascript@npm:^1.0.0":
+ version: 1.5.4
+ resolution: "@lezer/javascript@npm:1.5.4"
+ dependencies:
+ "@lezer/common": "npm:^1.2.0"
+ "@lezer/highlight": "npm:^1.1.3"
+ "@lezer/lr": "npm:^1.3.0"
+ checksum: 10c0/77b97c546d3661223ce77af15192efad42585d01e46022222273cd0c1cb20df8063d266037ae67774f051a6cb72db49804b6a46b06b926ee541807ef01741f6a
+ languageName: node
+ linkType: hard
+
+"@lezer/lr@npm:^1.0.0, @lezer/lr@npm:^1.3.0":
+ version: 1.4.5
+ resolution: "@lezer/lr@npm:1.4.5"
+ dependencies:
+ "@lezer/common": "npm:^1.0.0"
+ checksum: 10c0/06a6fcba5093eb654303f0bcca5009c404fe865d19c5e302e095c3d317ca50ceafd5753bb05aa26403996fc039dffa12288154fb9e201f49a94dea798d0cb6bc
+ languageName: node
+ linkType: hard
+
"@lottiefiles/dotlottie-react@npm:0.13.3":
version: 0.13.3
resolution: "@lottiefiles/dotlottie-react@npm:0.13.3"
@@ -2507,6 +2710,13 @@ __metadata:
languageName: node
linkType: hard
+"@marijn/find-cluster-break@npm:^1.0.0":
+ version: 1.0.2
+ resolution: "@marijn/find-cluster-break@npm:1.0.2"
+ checksum: 10c0/1a17a60b16083cc5f7ce89d7b7d8aa87ce4099723e3e9e34e229ef2cd8a980e69d481ca8ee90ffedfec5119af1aed581642fb60ed0365e7e90634c81ea6b630f
+ languageName: node
+ linkType: hard
+
"@mdx-js/mdx@npm:^3.1.1":
version: 3.1.1
resolution: "@mdx-js/mdx@npm:3.1.1"
@@ -6871,6 +7081,51 @@ __metadata:
languageName: node
linkType: hard
+"@uiw/codemirror-extensions-basic-setup@npm:4.25.3":
+ version: 4.25.3
+ resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.25.3"
+ dependencies:
+ "@codemirror/autocomplete": "npm:^6.0.0"
+ "@codemirror/commands": "npm:^6.0.0"
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/lint": "npm:^6.0.0"
+ "@codemirror/search": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.0.0"
+ 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"
+ checksum: 10c0/c982ec2fe2102d9ef292fdc5aa02a72bf8eebc021a7a3d55d54b1be3311da4cd6c09fb5a8a1a9608986f4e340e7a229c96d86a68b92a98d83a57b153d97fa104
+ languageName: node
+ linkType: hard
+
+"@uiw/react-codemirror@npm:^4.25.3":
+ version: 4.25.3
+ resolution: "@uiw/react-codemirror@npm:4.25.3"
+ dependencies:
+ "@babel/runtime": "npm:^7.18.6"
+ "@codemirror/commands": "npm:^6.1.0"
+ "@codemirror/state": "npm:^6.1.1"
+ "@codemirror/theme-one-dark": "npm:^6.0.0"
+ "@uiw/codemirror-extensions-basic-setup": "npm:4.25.3"
+ codemirror: "npm:^6.0.0"
+ 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"
+ checksum: 10c0/f8c69da682018f240c065621df4d6eb09fcb741ee14e249d3989cfc21959eedeedc529117ec4a564cf0a604c1a4f2e031cf8051502ff5f5562e0deca1b61b2f0
+ languageName: node
+ linkType: hard
+
"@ungap/structured-clone@npm:^1.0.0":
version: 1.3.0
resolution: "@ungap/structured-clone@npm:1.3.0"
@@ -8440,6 +8695,21 @@ __metadata:
languageName: node
linkType: hard
+"codemirror@npm:^6.0.0":
+ version: 6.0.2
+ resolution: "codemirror@npm:6.0.2"
+ dependencies:
+ "@codemirror/autocomplete": "npm:^6.0.0"
+ "@codemirror/commands": "npm:^6.0.0"
+ "@codemirror/language": "npm:^6.0.0"
+ "@codemirror/lint": "npm:^6.0.0"
+ "@codemirror/search": "npm:^6.0.0"
+ "@codemirror/state": "npm:^6.0.0"
+ "@codemirror/view": "npm:^6.0.0"
+ checksum: 10c0/8d198d8aebc32e56c966ac57b0fe8f832b7d601a2f62819ba3a294570233982bf4d5b499a764194b6b26dbc5313a156c2611cbc542234ea6eae6accf07a651ab
+ languageName: node
+ linkType: hard
+
"collapse-white-space@npm:^2.0.0":
version: 2.1.0
resolution: "collapse-white-space@npm:2.1.0"
@@ -8703,7 +8973,7 @@ __metadata:
languageName: node
linkType: hard
-"crelt@npm:^1.0.0":
+"crelt@npm:^1.0.0, crelt@npm:^1.0.5, crelt@npm:^1.0.6":
version: 1.0.6
resolution: "crelt@npm:1.0.6"
checksum: 10c0/e0fb76dff50c5eb47f2ea9b786c17f9425c66276025adee80876bdbf4a84ab72e899e56d3928431ab0cb057a105ef704df80fe5726ef0f7b1658f815521bdf09
@@ -17426,6 +17696,13 @@ __metadata:
languageName: node
linkType: hard
+"style-mod@npm:^4.0.0, style-mod@npm:^4.1.0":
+ version: 4.1.3
+ resolution: "style-mod@npm:4.1.3"
+ checksum: 10c0/36059006ea73cd96242ca8be06b625522d488bf8caca9c18436edf77092183381f08109577a4b3d35482f3395231099f195dbc854a46ce507fbf75c484f2cfcc
+ languageName: node
+ linkType: hard
+
"style-to-js@npm:^1.0.0":
version: 1.1.21
resolution: "style-to-js@npm:1.1.21"
@@ -18673,7 +18950,7 @@ __metadata:
languageName: node
linkType: hard
-"w3c-keyname@npm:^2.2.0":
+"w3c-keyname@npm:^2.2.0, w3c-keyname@npm:^2.2.4":
version: 2.2.8
resolution: "w3c-keyname@npm:2.2.8"
checksum: 10c0/37cf335c90efff31672ebb345577d681e2177f7ff9006a9ad47c68c5a9d265ba4a7b39d6c2599ceea639ca9315584ce4bd9c9fbf7a7217bfb7a599e71943c4c4
@@ -18716,6 +18993,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "web@workspace:apps/web"
dependencies:
+ "@codemirror/lang-html": "npm:^6.4.11"
+ "@codemirror/theme-one-dark": "npm:^6.1.3"
+ "@codemirror/view": "npm:^6.39.0"
"@plunk/db": "npm:*"
"@plunk/eslint-config": "npm:*"
"@plunk/shared": "npm:*"
@@ -18743,6 +19023,7 @@ __metadata:
"@types/node": "npm:^24.10.0"
"@types/react": "npm:^19.2.7"
"@types/react-dom": "npm:^19.2.3"
+ "@uiw/react-codemirror": "npm:^4.25.3"
"@xyflow/react": "npm:^12.9.3"
dagre: "npm:^0.8.5"
framer-motion: "npm:^12.23.24"