machinaos 0.0.1 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template +71 -71
- package/LICENSE +21 -21
- package/README.md +145 -87
- package/bin/cli.js +62 -106
- package/client/.dockerignore +45 -45
- package/client/Dockerfile +68 -68
- package/client/dist/assets/index-DFSC53FP.css +1 -0
- package/client/dist/assets/index-fJ-1gTf5.js +613 -0
- package/client/dist/index.html +14 -0
- package/client/eslint.config.js +34 -16
- package/client/nginx.conf +66 -66
- package/client/package.json +61 -48
- package/client/src/App.tsx +27 -27
- package/client/src/Dashboard.tsx +1200 -1172
- package/client/src/ParameterPanel.tsx +302 -300
- package/client/src/components/AIAgentNode.tsx +315 -321
- package/client/src/components/APIKeyValidator.tsx +117 -117
- package/client/src/components/ClaudeChatModelNode.tsx +17 -17
- package/client/src/components/CredentialsModal.tsx +1200 -306
- package/client/src/components/GeminiChatModelNode.tsx +17 -17
- package/client/src/components/GenericNode.tsx +356 -356
- package/client/src/components/LocationParameterPanel.tsx +153 -153
- package/client/src/components/ModelNode.tsx +285 -285
- package/client/src/components/OpenAIChatModelNode.tsx +17 -17
- package/client/src/components/OutputPanel.tsx +470 -470
- package/client/src/components/ParameterRenderer.tsx +1873 -1873
- package/client/src/components/SkillEditorModal.tsx +3 -3
- package/client/src/components/SquareNode.tsx +812 -796
- package/client/src/components/ToolkitNode.tsx +365 -365
- package/client/src/components/auth/LoginPage.tsx +247 -247
- package/client/src/components/auth/ProtectedRoute.tsx +59 -59
- package/client/src/components/base/BaseChatModelNode.tsx +270 -270
- package/client/src/components/icons/AIProviderIcons.tsx +50 -50
- package/client/src/components/maps/GoogleMapsPicker.tsx +136 -136
- package/client/src/components/maps/MapsPreviewPanel.tsx +109 -109
- package/client/src/components/maps/index.ts +25 -25
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -1094
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +64 -64
- package/client/src/components/parameterPanel/MapsSection.tsx +91 -91
- package/client/src/components/parameterPanel/MiddleSection.tsx +867 -571
- package/client/src/components/parameterPanel/OutputSection.tsx +80 -80
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +81 -81
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -436
- package/client/src/components/parameterPanel/index.ts +41 -41
- package/client/src/components/shared/DataPanel.tsx +142 -142
- package/client/src/components/shared/JSONTreeRenderer.tsx +105 -105
- package/client/src/components/ui/AIResultModal.tsx +203 -203
- package/client/src/components/ui/ApiKeyInput.tsx +93 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -81
- package/client/src/components/ui/CollapsibleSection.tsx +87 -87
- package/client/src/components/ui/ComponentItem.tsx +153 -153
- package/client/src/components/ui/ComponentPalette.tsx +320 -320
- package/client/src/components/ui/ConsolePanel.tsx +151 -43
- package/client/src/components/ui/ErrorBoundary.tsx +195 -195
- package/client/src/components/ui/InputNodesPanel.tsx +203 -203
- package/client/src/components/ui/MapSelector.tsx +313 -313
- package/client/src/components/ui/Modal.tsx +151 -148
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -1150
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -381
- package/client/src/components/ui/QRCodeDisplay.tsx +182 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -736
- package/client/src/components/ui/WorkflowSidebar.tsx +293 -293
- package/client/src/config/antdTheme.ts +186 -186
- package/client/src/contexts/AuthContext.tsx +221 -221
- package/client/src/contexts/ThemeContext.tsx +42 -42
- package/client/src/contexts/WebSocketContext.tsx +2144 -1971
- package/client/src/factories/baseChatModelFactory.ts +255 -255
- package/client/src/hooks/useAndroidOperations.ts +118 -164
- package/client/src/hooks/useApiKeyValidation.ts +106 -106
- package/client/src/hooks/useApiKeys.ts +238 -238
- package/client/src/hooks/useAppTheme.ts +17 -17
- package/client/src/hooks/useComponentPalette.ts +50 -50
- package/client/src/hooks/useDragAndDrop.ts +123 -123
- package/client/src/hooks/useDragVariable.ts +88 -88
- package/client/src/hooks/useExecution.ts +319 -313
- package/client/src/hooks/useParameterPanel.ts +176 -176
- package/client/src/hooks/useReactFlowNodes.ts +188 -188
- package/client/src/hooks/useToolSchema.ts +209 -209
- package/client/src/hooks/useWhatsApp.ts +196 -196
- package/client/src/hooks/useWorkflowManagement.ts +45 -45
- package/client/src/index.css +314 -314
- package/client/src/nodeDefinitions/aiAgentNodes.ts +335 -335
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -340
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -383
- package/client/src/nodeDefinitions/chatNodes.ts +135 -135
- package/client/src/nodeDefinitions/codeNodes.ts +54 -54
- package/client/src/nodeDefinitions/index.ts +14 -14
- package/client/src/nodeDefinitions/locationNodes.ts +462 -462
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -220
- package/client/src/nodeDefinitions/skillNodes.ts +17 -5
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -284
- package/client/src/nodeDefinitions/whatsappNodes.ts +821 -865
- package/client/src/nodeDefinitions.ts +101 -103
- package/client/src/services/dynamicParameterService.ts +95 -95
- package/client/src/services/execution/aiAgentExecutionService.ts +34 -34
- package/client/src/services/executionService.ts +227 -231
- package/client/src/services/workflowApi.ts +91 -91
- package/client/src/store/useAppStore.ts +578 -581
- package/client/src/styles/theme.ts +513 -508
- package/client/src/styles/zIndex.ts +16 -16
- package/client/src/types/ComponentTypes.ts +38 -38
- package/client/src/types/INodeProperties.ts +287 -287
- package/client/src/types/NodeTypes.ts +27 -27
- package/client/src/utils/formatters.ts +32 -32
- package/client/src/utils/googleMapsLoader.ts +139 -139
- package/client/src/utils/locationUtils.ts +84 -84
- package/client/src/utils/nodeUtils.ts +30 -30
- package/client/src/utils/workflow.ts +29 -29
- package/client/src/vite-env.d.ts +12 -12
- package/client/tailwind.config.js +59 -59
- package/client/tsconfig.json +25 -25
- package/client/vite.config.js +35 -35
- package/package.json +78 -70
- package/scripts/build.js +153 -45
- package/scripts/clean.js +40 -40
- package/scripts/start.js +234 -210
- package/scripts/stop.js +301 -325
- package/server/.dockerignore +44 -44
- package/server/Dockerfile +45 -45
- package/server/constants.py +244 -249
- package/server/core/cache.py +460 -460
- package/server/core/config.py +127 -127
- package/server/core/container.py +98 -98
- package/server/core/database.py +1296 -1210
- package/server/core/logging.py +313 -313
- package/server/main.py +288 -288
- package/server/middleware/__init__.py +5 -5
- package/server/middleware/auth.py +89 -89
- package/server/models/auth.py +52 -52
- package/server/models/cache.py +24 -24
- package/server/models/database.py +235 -210
- package/server/models/nodes.py +435 -455
- package/server/pyproject.toml +75 -72
- package/server/requirements.txt +83 -83
- package/server/routers/android.py +294 -294
- package/server/routers/auth.py +203 -203
- package/server/routers/database.py +150 -150
- package/server/routers/maps.py +141 -141
- package/server/routers/nodejs_compat.py +288 -288
- package/server/routers/webhook.py +90 -90
- package/server/routers/websocket.py +2239 -2127
- package/server/routers/whatsapp.py +761 -761
- package/server/routers/workflow.py +199 -199
- package/server/services/ai.py +2444 -2414
- package/server/services/android_service.py +588 -588
- package/server/services/auth.py +130 -130
- package/server/services/chat_client.py +160 -160
- package/server/services/deployment/manager.py +706 -706
- package/server/services/event_waiter.py +675 -785
- package/server/services/execution/executor.py +1351 -1351
- package/server/services/execution/models.py +1 -1
- package/server/services/handlers/__init__.py +122 -126
- package/server/services/handlers/ai.py +390 -355
- package/server/services/handlers/android.py +69 -260
- package/server/services/handlers/code.py +278 -278
- package/server/services/handlers/http.py +193 -193
- package/server/services/handlers/tools.py +146 -32
- package/server/services/handlers/triggers.py +107 -107
- package/server/services/handlers/utility.py +822 -822
- package/server/services/handlers/whatsapp.py +423 -476
- package/server/services/maps.py +288 -288
- package/server/services/memory_store.py +103 -103
- package/server/services/node_executor.py +372 -375
- package/server/services/scheduler.py +155 -155
- package/server/services/skill_loader.py +1 -1
- package/server/services/status_broadcaster.py +834 -826
- package/server/services/temporal/__init__.py +23 -23
- package/server/services/temporal/activities.py +344 -344
- package/server/services/temporal/client.py +76 -76
- package/server/services/temporal/executor.py +147 -147
- package/server/services/temporal/worker.py +251 -251
- package/server/services/temporal/workflow.py +355 -355
- package/server/services/temporal/ws_client.py +236 -236
- package/server/services/text.py +110 -110
- package/server/services/user_auth.py +172 -172
- package/server/services/websocket_client.py +29 -29
- package/server/services/workflow.py +597 -597
- package/server/skills/android-skill/SKILL.md +4 -4
- package/server/skills/code-skill/SKILL.md +123 -89
- package/server/skills/maps-skill/SKILL.md +3 -3
- package/server/skills/memory-skill/SKILL.md +1 -1
- package/server/skills/web-search-skill/SKILL.md +154 -0
- package/server/skills/whatsapp-skill/SKILL.md +3 -3
- package/server/uv.lock +461 -100
- package/server/whatsapp-rpc/.dockerignore +30 -30
- package/server/whatsapp-rpc/Dockerfile +44 -44
- package/server/whatsapp-rpc/Dockerfile.web +17 -17
- package/server/whatsapp-rpc/README.md +139 -139
- package/server/whatsapp-rpc/bin/whatsapp-rpc-server +0 -0
- package/server/whatsapp-rpc/cli.js +95 -95
- package/server/whatsapp-rpc/configs/config.yaml +6 -6
- package/server/whatsapp-rpc/docker-compose.yml +35 -35
- package/server/whatsapp-rpc/docs/API.md +410 -410
- package/server/whatsapp-rpc/node_modules/.package-lock.json +259 -0
- package/server/whatsapp-rpc/node_modules/chalk/license +9 -0
- package/server/whatsapp-rpc/node_modules/chalk/package.json +83 -0
- package/server/whatsapp-rpc/node_modules/chalk/readme.md +297 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.d.ts +325 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/index.js +225 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/utilities.js +33 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/server/whatsapp-rpc/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/server/whatsapp-rpc/node_modules/commander/LICENSE +22 -0
- package/server/whatsapp-rpc/node_modules/commander/Readme.md +1148 -0
- package/server/whatsapp-rpc/node_modules/commander/esm.mjs +16 -0
- package/server/whatsapp-rpc/node_modules/commander/index.js +26 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/argument.js +145 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/command.js +2179 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/error.js +43 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/help.js +462 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/option.js +329 -0
- package/server/whatsapp-rpc/node_modules/commander/lib/suggestSimilar.js +100 -0
- package/server/whatsapp-rpc/node_modules/commander/package-support.json +16 -0
- package/server/whatsapp-rpc/node_modules/commander/package.json +80 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/esm.d.mts +3 -0
- package/server/whatsapp-rpc/node_modules/commander/typings/index.d.ts +884 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/README.md +89 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/index.js +39 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/enoent.js +59 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/parse.js +91 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/escape.js +47 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/readShebang.js +23 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/lib/util/resolveCommand.js +52 -0
- package/server/whatsapp-rpc/node_modules/cross-spawn/package.json +73 -0
- package/server/whatsapp-rpc/node_modules/execa/index.d.ts +955 -0
- package/server/whatsapp-rpc/node_modules/execa/index.js +309 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/command.js +119 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/error.js +87 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/kill.js +102 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/pipe.js +42 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/promise.js +36 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stdio.js +49 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/stream.js +133 -0
- package/server/whatsapp-rpc/node_modules/execa/lib/verbose.js +19 -0
- package/server/whatsapp-rpc/node_modules/execa/license +9 -0
- package/server/whatsapp-rpc/node_modules/execa/package.json +90 -0
- package/server/whatsapp-rpc/node_modules/execa/readme.md +822 -0
- package/server/whatsapp-rpc/node_modules/get-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/get-stream/package.json +53 -0
- package/server/whatsapp-rpc/node_modules/get-stream/readme.md +291 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array-buffer.js +84 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/array.js +32 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/buffer.js +20 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/contents.js +101 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.d.ts +119 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/index.js +5 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/string.js +36 -0
- package/server/whatsapp-rpc/node_modules/get-stream/source/utils.js +11 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/README.md +95 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/index.js +97 -0
- package/server/whatsapp-rpc/node_modules/get-them-args/package.json +36 -0
- package/server/whatsapp-rpc/node_modules/human-signals/LICENSE +201 -0
- package/server/whatsapp-rpc/node_modules/human-signals/README.md +168 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/core.js +273 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.d.ts +73 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/main.js +70 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/realtime.js +16 -0
- package/server/whatsapp-rpc/node_modules/human-signals/build/src/signals.js +34 -0
- package/server/whatsapp-rpc/node_modules/human-signals/package.json +61 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.d.ts +81 -0
- package/server/whatsapp-rpc/node_modules/is-stream/index.js +29 -0
- package/server/whatsapp-rpc/node_modules/is-stream/license +9 -0
- package/server/whatsapp-rpc/node_modules/is-stream/package.json +44 -0
- package/server/whatsapp-rpc/node_modules/is-stream/readme.md +60 -0
- package/server/whatsapp-rpc/node_modules/isexe/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/isexe/README.md +51 -0
- package/server/whatsapp-rpc/node_modules/isexe/index.js +57 -0
- package/server/whatsapp-rpc/node_modules/isexe/mode.js +41 -0
- package/server/whatsapp-rpc/node_modules/isexe/package.json +31 -0
- package/server/whatsapp-rpc/node_modules/isexe/test/basic.js +221 -0
- package/server/whatsapp-rpc/node_modules/isexe/windows.js +42 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.editorconfig +12 -0
- package/server/whatsapp-rpc/node_modules/kill-port/.gitattributes +1 -0
- package/server/whatsapp-rpc/node_modules/kill-port/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/README.md +140 -0
- package/server/whatsapp-rpc/node_modules/kill-port/cli.js +25 -0
- package/server/whatsapp-rpc/node_modules/kill-port/example.js +21 -0
- package/server/whatsapp-rpc/node_modules/kill-port/index.js +46 -0
- package/server/whatsapp-rpc/node_modules/kill-port/logo.png +0 -0
- package/server/whatsapp-rpc/node_modules/kill-port/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/kill-port/pnpm-lock.yaml +4606 -0
- package/server/whatsapp-rpc/node_modules/kill-port/test.js +16 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/README.md +78 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/merge-stream/package.json +19 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.d.ts +52 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/index.js +71 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/license +9 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/mimic-fn/readme.md +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.d.ts +90 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/index.js +52 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.d.ts +31 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/index.js +12 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/package.json +41 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/node_modules/path-key/readme.md +57 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/package.json +49 -0
- package/server/whatsapp-rpc/node_modules/npm-run-path/readme.md +104 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.d.ts +59 -0
- package/server/whatsapp-rpc/node_modules/onetime/index.js +41 -0
- package/server/whatsapp-rpc/node_modules/onetime/license +9 -0
- package/server/whatsapp-rpc/node_modules/onetime/package.json +45 -0
- package/server/whatsapp-rpc/node_modules/onetime/readme.md +94 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.d.ts +40 -0
- package/server/whatsapp-rpc/node_modules/path-key/index.js +16 -0
- package/server/whatsapp-rpc/node_modules/path-key/license +9 -0
- package/server/whatsapp-rpc/node_modules/path-key/package.json +39 -0
- package/server/whatsapp-rpc/node_modules/path-key/readme.md +61 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/index.js +19 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/package.json +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-command/readme.md +34 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.d.ts +22 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/index.js +2 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/license +9 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/package.json +35 -0
- package/server/whatsapp-rpc/node_modules/shebang-regex/readme.md +33 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/LICENSE +21 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/README.md +60 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/index.js +47 -0
- package/server/whatsapp-rpc/node_modules/shell-exec/package.json +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/LICENSE.txt +16 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/README.md +74 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js +10 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js +279 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js +42 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/cjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts +12 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js +4 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/browser.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts +48 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js +275 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/index.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/package.json +3 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts +29 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.d.ts.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js +39 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/dist/mjs/signals.js.map +1 -0
- package/server/whatsapp-rpc/node_modules/signal-exit/package.json +106 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/index.js +14 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/license +9 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/strip-final-newline/readme.md +35 -0
- package/server/whatsapp-rpc/node_modules/which/CHANGELOG.md +166 -0
- package/server/whatsapp-rpc/node_modules/which/LICENSE +15 -0
- package/server/whatsapp-rpc/node_modules/which/README.md +54 -0
- package/server/whatsapp-rpc/node_modules/which/bin/node-which +52 -0
- package/server/whatsapp-rpc/node_modules/which/package.json +43 -0
- package/server/whatsapp-rpc/node_modules/which/which.js +125 -0
- package/server/whatsapp-rpc/package-lock.json +272 -0
- package/server/whatsapp-rpc/package.json +30 -30
- package/server/whatsapp-rpc/schema.json +1294 -1294
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -66
- package/server/whatsapp-rpc/scripts/cli.js +162 -162
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -166
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -15
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -4
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -427
- package/server/whatsapp-rpc/web/app.py +609 -609
- package/server/whatsapp-rpc/web/requirements.txt +6 -6
- package/server/whatsapp-rpc/web/rpc_client.py +427 -427
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -59
- package/server/whatsapp-rpc/web/templates/base.html +149 -149
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -240
- package/server/whatsapp-rpc/web/templates/dashboard.html +319 -319
- package/server/whatsapp-rpc/web/templates/groups.html +328 -328
- package/server/whatsapp-rpc/web/templates/messages.html +465 -465
- package/server/whatsapp-rpc/web/templates/messaging.html +680 -680
- package/server/whatsapp-rpc/web/templates/send.html +258 -258
- package/server/whatsapp-rpc/web/templates/settings.html +459 -459
- package/client/src/components/ui/AndroidSettingsPanel.tsx +0 -401
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +0 -345
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +0 -140
- package/docker-compose.prod.yml +0 -107
- package/docker-compose.yml +0 -104
- package/docs-MachinaOs/README.md +0 -85
- package/docs-MachinaOs/deployment/docker.mdx +0 -228
- package/docs-MachinaOs/deployment/production.mdx +0 -345
- package/docs-MachinaOs/docs.json +0 -75
- package/docs-MachinaOs/faq.mdx +0 -309
- package/docs-MachinaOs/favicon.svg +0 -5
- package/docs-MachinaOs/installation.mdx +0 -160
- package/docs-MachinaOs/introduction.mdx +0 -114
- package/docs-MachinaOs/logo/dark.svg +0 -6
- package/docs-MachinaOs/logo/light.svg +0 -6
- package/docs-MachinaOs/nodes/ai-agent.mdx +0 -216
- package/docs-MachinaOs/nodes/ai-models.mdx +0 -240
- package/docs-MachinaOs/nodes/android.mdx +0 -411
- package/docs-MachinaOs/nodes/overview.mdx +0 -181
- package/docs-MachinaOs/nodes/schedulers.mdx +0 -316
- package/docs-MachinaOs/nodes/webhooks.mdx +0 -330
- package/docs-MachinaOs/nodes/whatsapp.mdx +0 -305
- package/docs-MachinaOs/quickstart.mdx +0 -119
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +0 -177
- package/docs-MachinaOs/tutorials/android-automation.mdx +0 -242
- package/docs-MachinaOs/tutorials/first-workflow.mdx +0 -134
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +0 -185
- package/nul +0 -0
- package/scripts/check-ports.ps1 +0 -33
- package/scripts/kill-port.ps1 +0 -154
package/client/src/Dashboard.tsx
CHANGED
|
@@ -1,1173 +1,1201 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
ReactFlow,
|
|
4
|
-
ReactFlowProvider,
|
|
5
|
-
Controls,
|
|
6
|
-
useNodesState,
|
|
7
|
-
useEdgesState,
|
|
8
|
-
useReactFlow,
|
|
9
|
-
ConnectionMode,
|
|
10
|
-
ConnectionLineType,
|
|
11
|
-
SelectionMode,
|
|
12
|
-
Node,
|
|
13
|
-
Edge,
|
|
14
|
-
} from 'reactflow';
|
|
15
|
-
import GenericNode from './components/GenericNode';
|
|
16
|
-
import AIAgentNode from './components/AIAgentNode';
|
|
17
|
-
import ModelNode from './components/ModelNode';
|
|
18
|
-
import SquareNode from './components/SquareNode';
|
|
19
|
-
import TriggerNode from './components/TriggerNode';
|
|
20
|
-
import ToolkitNode from './components/ToolkitNode';
|
|
21
|
-
import ConditionalEdge from './components/ConditionalEdge';
|
|
22
|
-
import NodeContextMenu from './components/ui/NodeContextMenu';
|
|
23
|
-
import { nodeDefinitions } from './nodeDefinitions';
|
|
24
|
-
import { ANDROID_SERVICE_NODE_TYPES } from './nodeDefinitions/androidServiceNodes';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import
|
|
33
|
-
import
|
|
34
|
-
import
|
|
35
|
-
import
|
|
36
|
-
import
|
|
37
|
-
import
|
|
38
|
-
import
|
|
39
|
-
import
|
|
40
|
-
import
|
|
41
|
-
import
|
|
42
|
-
import
|
|
43
|
-
import
|
|
44
|
-
import
|
|
45
|
-
import
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
types[type] =
|
|
77
|
-
} else if (type === '
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
types[type] =
|
|
82
|
-
} else if (type === '
|
|
83
|
-
//
|
|
84
|
-
types[type] =
|
|
85
|
-
} else if (type
|
|
86
|
-
//
|
|
87
|
-
types[type] = SquareNode;
|
|
88
|
-
} else if (
|
|
89
|
-
//
|
|
90
|
-
types[type] = SquareNode;
|
|
91
|
-
} else if (
|
|
92
|
-
//
|
|
93
|
-
types[type] = SquareNode;
|
|
94
|
-
} else if (
|
|
95
|
-
//
|
|
96
|
-
types[type] = SquareNode;
|
|
97
|
-
} else if (
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} else if (
|
|
113
|
-
//
|
|
114
|
-
types[type] =
|
|
115
|
-
} else if (
|
|
116
|
-
//
|
|
117
|
-
types[type] =
|
|
118
|
-
} else if (definition?.group?.includes('
|
|
119
|
-
//
|
|
120
|
-
types[type] =
|
|
121
|
-
} else
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
filter: drop-shadow(0 0
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
filter: drop-shadow(0 0
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const [
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (
|
|
364
|
-
className = '
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
//
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}), [theme.
|
|
483
|
-
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
//
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
console.log('[Workflow Run]
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (!
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
target
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
//
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
{
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
{/*
|
|
1142
|
-
<
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
{
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ReactFlow,
|
|
4
|
+
ReactFlowProvider,
|
|
5
|
+
Controls,
|
|
6
|
+
useNodesState,
|
|
7
|
+
useEdgesState,
|
|
8
|
+
useReactFlow,
|
|
9
|
+
ConnectionMode,
|
|
10
|
+
ConnectionLineType,
|
|
11
|
+
SelectionMode,
|
|
12
|
+
Node,
|
|
13
|
+
Edge,
|
|
14
|
+
} from 'reactflow';
|
|
15
|
+
import GenericNode from './components/GenericNode';
|
|
16
|
+
import AIAgentNode from './components/AIAgentNode';
|
|
17
|
+
import ModelNode from './components/ModelNode';
|
|
18
|
+
import SquareNode from './components/SquareNode';
|
|
19
|
+
import TriggerNode from './components/TriggerNode';
|
|
20
|
+
import ToolkitNode from './components/ToolkitNode';
|
|
21
|
+
import ConditionalEdge from './components/ConditionalEdge';
|
|
22
|
+
import NodeContextMenu from './components/ui/NodeContextMenu';
|
|
23
|
+
import { nodeDefinitions } from './nodeDefinitions';
|
|
24
|
+
import { ANDROID_SERVICE_NODE_TYPES } from './nodeDefinitions/androidServiceNodes';
|
|
25
|
+
import { SCHEDULER_NODE_TYPES } from './nodeDefinitions/schedulerNodes';
|
|
26
|
+
import { CHAT_NODE_TYPES } from './nodeDefinitions/chatNodes';
|
|
27
|
+
import { CODE_NODE_TYPES } from './nodeDefinitions/codeNodes';
|
|
28
|
+
import { UTILITY_NODE_TYPES } from './nodeDefinitions/utilityNodes';
|
|
29
|
+
import { TOOL_NODE_TYPES } from './nodeDefinitions/toolNodes';
|
|
30
|
+
import { SKILL_NODE_TYPES } from './nodeDefinitions/skillNodes';
|
|
31
|
+
import { DOCUMENT_NODE_TYPES } from './nodeDefinitions/documentNodes';
|
|
32
|
+
import ParameterPanel from './ParameterPanel';
|
|
33
|
+
import LocationParameterPanel from './components/LocationParameterPanel';
|
|
34
|
+
import { useAppStore } from './store/useAppStore';
|
|
35
|
+
import ComponentPalette from './components/ui/ComponentPalette';
|
|
36
|
+
import TopToolbar from './components/ui/TopToolbar';
|
|
37
|
+
import WorkflowSidebar from './components/ui/WorkflowSidebar';
|
|
38
|
+
import SettingsPanel, { WorkflowSettings, defaultSettings } from './components/ui/SettingsPanel';
|
|
39
|
+
import AIResultModal from './components/ui/AIResultModal';
|
|
40
|
+
import CredentialsModal from './components/CredentialsModal';
|
|
41
|
+
import ErrorBoundary from './components/ui/ErrorBoundary';
|
|
42
|
+
import ConsolePanel from './components/ui/ConsolePanel';
|
|
43
|
+
import { useAppTheme } from './hooks/useAppTheme';
|
|
44
|
+
import { useWorkflowManagement } from './hooks/useWorkflowManagement';
|
|
45
|
+
import { useDragAndDrop } from './hooks/useDragAndDrop';
|
|
46
|
+
import { useComponentPalette } from './hooks/useComponentPalette';
|
|
47
|
+
import { useReactFlowNodes } from './hooks/useReactFlowNodes';
|
|
48
|
+
import { useCopyPaste } from './hooks/useCopyPaste';
|
|
49
|
+
import { useWebSocket } from './contexts/WebSocketContext';
|
|
50
|
+
import { useTheme } from './contexts/ThemeContext';
|
|
51
|
+
import {
|
|
52
|
+
sanitizeNodesForComparison,
|
|
53
|
+
sanitizeEdgesForComparison,
|
|
54
|
+
generateWorkflowId
|
|
55
|
+
} from './utils/workflow';
|
|
56
|
+
import { importWorkflowFromFile } from './utils/workflowExport';
|
|
57
|
+
|
|
58
|
+
import 'reactflow/dist/style.css';
|
|
59
|
+
|
|
60
|
+
// Node types configuration - defined outside component to prevent recreation on re-renders
|
|
61
|
+
// This is required by React Flow to avoid performance issues
|
|
62
|
+
const createNodeTypes = (): Record<string, React.ComponentType<any>> => {
|
|
63
|
+
const types: Record<string, React.ComponentType<any>> = {};
|
|
64
|
+
|
|
65
|
+
// Trigger nodes (no input handles) - check by group or specific types
|
|
66
|
+
const TRIGGER_NODE_TYPES = ['start', 'cronScheduler', 'webhookTrigger', 'whatsappReceive', 'chatTrigger'];
|
|
67
|
+
|
|
68
|
+
Object.keys(nodeDefinitions).forEach(type => {
|
|
69
|
+
const definition = nodeDefinitions[type];
|
|
70
|
+
|
|
71
|
+
// Trigger nodes - no input connections (start workflows)
|
|
72
|
+
if (TRIGGER_NODE_TYPES.includes(type)) {
|
|
73
|
+
types[type] = TriggerNode;
|
|
74
|
+
} else if (type === 'openaiChatModel' || type === 'anthropicChatModel' || type === 'geminiChatModel' || type === 'openrouterChatModel' || type === 'groqChatModel' || type === 'cerebrasChatModel') {
|
|
75
|
+
// AI chat model nodes use square design
|
|
76
|
+
types[type] = SquareNode;
|
|
77
|
+
} else if (type === 'aiAgent' || type === 'chatAgent') {
|
|
78
|
+
types[type] = AIAgentNode;
|
|
79
|
+
} else if (type === 'simpleMemory') {
|
|
80
|
+
// Simple Memory node for AI conversation history - uses circular ModelNode design
|
|
81
|
+
types[type] = ModelNode;
|
|
82
|
+
} else if (type === 'whatsappSend' || type === 'whatsappDb') {
|
|
83
|
+
// WhatsApp action nodes use SquareNode (whatsappReceive is a trigger)
|
|
84
|
+
types[type] = SquareNode;
|
|
85
|
+
} else if (ANDROID_SERVICE_NODE_TYPES.includes(type)) {
|
|
86
|
+
// Android service nodes use SquareNode component
|
|
87
|
+
types[type] = SquareNode;
|
|
88
|
+
} else if (SCHEDULER_NODE_TYPES.includes(type)) {
|
|
89
|
+
// Timer uses SquareNode (has input), cronScheduler already handled as trigger above
|
|
90
|
+
types[type] = SquareNode;
|
|
91
|
+
} else if (CHAT_NODE_TYPES.includes(type)) {
|
|
92
|
+
// Chat nodes use SquareNode component
|
|
93
|
+
types[type] = SquareNode;
|
|
94
|
+
} else if (CODE_NODE_TYPES.includes(type)) {
|
|
95
|
+
// Code execution nodes use SquareNode component
|
|
96
|
+
types[type] = SquareNode;
|
|
97
|
+
} else if (UTILITY_NODE_TYPES.includes(type)) {
|
|
98
|
+
// Utility nodes (HTTP, Webhooks) use SquareNode component
|
|
99
|
+
// Note: webhookTrigger is already handled as trigger above
|
|
100
|
+
types[type] = SquareNode;
|
|
101
|
+
} else if (TOOL_NODE_TYPES.includes(type)) {
|
|
102
|
+
// Most tool nodes use circular ModelNode
|
|
103
|
+
// Exception: androidTool uses ToolkitNode with top/bottom handles
|
|
104
|
+
if (type === 'androidTool') {
|
|
105
|
+
types[type] = ToolkitNode;
|
|
106
|
+
} else {
|
|
107
|
+
types[type] = ModelNode;
|
|
108
|
+
}
|
|
109
|
+
} else if (SKILL_NODE_TYPES.includes(type)) {
|
|
110
|
+
// Skill nodes use ToolkitNode (vertical handle layout like Android Toolkit)
|
|
111
|
+
types[type] = ToolkitNode;
|
|
112
|
+
} else if (DOCUMENT_NODE_TYPES.includes(type)) {
|
|
113
|
+
// Document processing nodes use SquareNode component
|
|
114
|
+
types[type] = SquareNode;
|
|
115
|
+
} else if (definition?.group?.includes('model')) {
|
|
116
|
+
// Fallback for other model nodes
|
|
117
|
+
types[type] = ModelNode;
|
|
118
|
+
} else if (definition?.group?.includes('service')) {
|
|
119
|
+
// Any node with 'service' group uses SquareNode component
|
|
120
|
+
types[type] = SquareNode;
|
|
121
|
+
} else {
|
|
122
|
+
types[type] = GenericNode;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return types;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Create node types once at module load time
|
|
130
|
+
const moduleNodeTypes = createNodeTypes();
|
|
131
|
+
|
|
132
|
+
// Edge types configuration - enables conditional edge rendering
|
|
133
|
+
const moduleEdgeTypes = {
|
|
134
|
+
conditional: ConditionalEdge,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Edge styles generator using theme colors - supports light and dark modes
|
|
138
|
+
const getEdgeStyles = (colors: {
|
|
139
|
+
edgeDefault: string;
|
|
140
|
+
edgeSelected: string;
|
|
141
|
+
edgeExecuting: string;
|
|
142
|
+
edgeCompleted: string;
|
|
143
|
+
edgeError: string;
|
|
144
|
+
}, isDark: boolean) => `
|
|
145
|
+
/* Base style for ALL edges */
|
|
146
|
+
.react-flow__edge path {
|
|
147
|
+
stroke: ${colors.edgeDefault} !important;
|
|
148
|
+
stroke-width: 2px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.react-flow__edge.selected path {
|
|
152
|
+
stroke: ${colors.edgeSelected} !important;
|
|
153
|
+
stroke-width: 4px !important;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Executing edge - subtle blue in light mode, cyan in dark mode */
|
|
157
|
+
.react-flow__edge.executing path {
|
|
158
|
+
stroke: ${isDark ? colors.edgeExecuting : '#2563eb'} !important;
|
|
159
|
+
stroke-width: 3px !important;
|
|
160
|
+
stroke-dasharray: 8 4;
|
|
161
|
+
animation: dashFlow 0.5s linear infinite;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Completed edge - subtle green in both modes */
|
|
165
|
+
.react-flow__edge.completed path {
|
|
166
|
+
stroke: ${isDark ? colors.edgeCompleted : '#16a34a'} !important;
|
|
167
|
+
stroke-width: 2px !important;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Error edge - keep red for visibility */
|
|
171
|
+
.react-flow__edge.error path {
|
|
172
|
+
stroke: ${colors.edgeError} !important;
|
|
173
|
+
stroke-width: 3px !important;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Pending edge - animated dash in both modes */
|
|
177
|
+
.react-flow__edge.pending path {
|
|
178
|
+
stroke: ${isDark ? colors.edgeDefault : '#6b7280'} !important;
|
|
179
|
+
stroke-width: 2px !important;
|
|
180
|
+
stroke-dasharray: 8 4;
|
|
181
|
+
animation: dashFlow 0.5s linear infinite;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Memory connection active */
|
|
185
|
+
.react-flow__edge.memory-active path {
|
|
186
|
+
stroke: ${isDark ? '#ff79c6' : '#db2777'} !important;
|
|
187
|
+
stroke-width: 3px !important;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Tool connection active */
|
|
191
|
+
.react-flow__edge.tool-active path {
|
|
192
|
+
stroke: ${isDark ? '#ffb86c' : '#ea580c'} !important;
|
|
193
|
+
stroke-width: 3px !important;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@keyframes dashFlow {
|
|
197
|
+
0% { stroke-dashoffset: 24; }
|
|
198
|
+
100% { stroke-dashoffset: 0; }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Executing node - visible glow in both modes */
|
|
202
|
+
.react-flow__node.executing {
|
|
203
|
+
filter: ${isDark
|
|
204
|
+
? `drop-shadow(0 0 8px ${colors.edgeExecuting}) drop-shadow(0 0 16px ${colors.edgeExecuting}80)`
|
|
205
|
+
: 'drop-shadow(0 0 10px rgba(37, 99, 235, 0.8)) drop-shadow(0 0 20px rgba(37, 99, 235, 0.6))'};
|
|
206
|
+
animation: ${isDark ? 'nodeGlowDark' : 'nodeGlowLight'} 1.2s ease-in-out infinite;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@keyframes nodeGlowDark {
|
|
210
|
+
0%, 100% {
|
|
211
|
+
filter: drop-shadow(0 0 8px ${colors.edgeExecuting}) drop-shadow(0 0 16px ${colors.edgeExecuting}80);
|
|
212
|
+
}
|
|
213
|
+
50% {
|
|
214
|
+
filter: drop-shadow(0 0 14px ${colors.edgeExecuting}) drop-shadow(0 0 24px ${colors.edgeExecuting});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@keyframes nodeGlowLight {
|
|
219
|
+
0%, 100% {
|
|
220
|
+
filter: drop-shadow(0 0 10px rgba(37, 99, 235, 0.8)) drop-shadow(0 0 20px rgba(37, 99, 235, 0.6));
|
|
221
|
+
}
|
|
222
|
+
50% {
|
|
223
|
+
filter: drop-shadow(0 0 16px rgba(37, 99, 235, 1)) drop-shadow(0 0 30px rgba(37, 99, 235, 0.8));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
const initialNodes: Node[] = [];
|
|
229
|
+
const initialEdges: Edge[] = [];
|
|
230
|
+
|
|
231
|
+
// Inner component that uses useReactFlow() - must be inside ReactFlowProvider
|
|
232
|
+
const DashboardContent: React.FC = () => {
|
|
233
|
+
const theme = useAppTheme();
|
|
234
|
+
const { isDarkMode } = useTheme();
|
|
235
|
+
const {
|
|
236
|
+
currentWorkflow,
|
|
237
|
+
hasUnsavedChanges,
|
|
238
|
+
savedWorkflows,
|
|
239
|
+
sidebarVisible,
|
|
240
|
+
componentPaletteVisible,
|
|
241
|
+
updateWorkflow,
|
|
242
|
+
loadSavedWorkflows,
|
|
243
|
+
createNewWorkflow,
|
|
244
|
+
saveWorkflow,
|
|
245
|
+
deleteWorkflow,
|
|
246
|
+
migrateCurrentWorkflow,
|
|
247
|
+
toggleSidebar,
|
|
248
|
+
toggleComponentPalette,
|
|
249
|
+
proMode,
|
|
250
|
+
toggleProMode,
|
|
251
|
+
exportWorkflowToJSON,
|
|
252
|
+
exportWorkflowToFile,
|
|
253
|
+
setCurrentWorkflow,
|
|
254
|
+
selectedNode,
|
|
255
|
+
setSelectedNode,
|
|
256
|
+
renamingNodeId,
|
|
257
|
+
setRenamingNodeId,
|
|
258
|
+
// Per-workflow UI state (n8n pattern)
|
|
259
|
+
setWorkflowExecuting,
|
|
260
|
+
setWorkflowExecutionOrder,
|
|
261
|
+
setWorkflowViewport,
|
|
262
|
+
clearWorkflowExecutionState,
|
|
263
|
+
} = useAppStore();
|
|
264
|
+
|
|
265
|
+
// ReactFlow state management (local state for performance)
|
|
266
|
+
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
|
267
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
268
|
+
|
|
269
|
+
// ReactFlow instance for viewport control (n8n pattern - per-workflow viewport)
|
|
270
|
+
const reactFlowInstance = useReactFlow();
|
|
271
|
+
|
|
272
|
+
// AI execution state - result and modal are local, execution tracking is per-workflow
|
|
273
|
+
const [executionResult, setExecutionResult] = React.useState<any>(null);
|
|
274
|
+
const [showResult, setShowResult] = React.useState(false);
|
|
275
|
+
|
|
276
|
+
// Get per-workflow execution state (n8n pattern - isolated per workflow)
|
|
277
|
+
// Subscribe to workflowUIStates directly so Zustand triggers re-renders when it changes
|
|
278
|
+
const workflowUIStates = useAppStore(state => state.workflowUIStates);
|
|
279
|
+
const workflowUIState = React.useMemo(() => {
|
|
280
|
+
if (!currentWorkflow?.id) return null;
|
|
281
|
+
return workflowUIStates[currentWorkflow.id] || { isExecuting: false, executedNodes: [], executionOrder: [], selectedNodeId: null };
|
|
282
|
+
}, [workflowUIStates, currentWorkflow?.id]);
|
|
283
|
+
const isExecuting = workflowUIState?.isExecuting || false;
|
|
284
|
+
const executedNodes = React.useMemo(() => new Set(workflowUIState?.executedNodes || []), [workflowUIState?.executedNodes]);
|
|
285
|
+
const executionOrder = workflowUIState?.executionOrder || [];
|
|
286
|
+
// Custom hooks for different concerns
|
|
287
|
+
const {
|
|
288
|
+
handleWorkflowNameChange,
|
|
289
|
+
handleSave,
|
|
290
|
+
handleNew,
|
|
291
|
+
handleOpen,
|
|
292
|
+
handleSelectWorkflow,
|
|
293
|
+
} = useWorkflowManagement();
|
|
294
|
+
|
|
295
|
+
const { collapsedSections, searchQuery, setSearchQuery, toggleSection } = useComponentPalette();
|
|
296
|
+
const { saveNodeParameters, executeWorkflow, deployWorkflow, cancelDeployment, nodeStatuses, deploymentStatus, workflowLock } = useWebSocket();
|
|
297
|
+
|
|
298
|
+
// Scope deployment and lock to current workflow (n8n pattern)
|
|
299
|
+
// Only show as "running" or "locked" if it applies to the currently viewed workflow
|
|
300
|
+
const isCurrentWorkflowDeployed = deploymentStatus.isRunning &&
|
|
301
|
+
deploymentStatus.workflow_id === currentWorkflow?.id;
|
|
302
|
+
const isCurrentWorkflowLocked = workflowLock.locked &&
|
|
303
|
+
workflowLock.workflow_id === currentWorkflow?.id;
|
|
304
|
+
const { onDragOver, onDrop, handleComponentDragStart } = useDragAndDrop({ nodes, setNodes, saveNodeParameters });
|
|
305
|
+
const { onConnect, onNodesDelete, onEdgesDelete } = useReactFlowNodes({ setNodes, setEdges });
|
|
306
|
+
const { copySelectedNodes, pasteNodes } = useCopyPaste({ nodes, edges, setNodes, setEdges, saveNodeParameters });
|
|
307
|
+
|
|
308
|
+
// Toggle disabled state on selected nodes
|
|
309
|
+
const toggleDisableSelected = React.useCallback(() => {
|
|
310
|
+
setNodes(nds => nds.map(node => {
|
|
311
|
+
if (node.selected) {
|
|
312
|
+
return {
|
|
313
|
+
...node,
|
|
314
|
+
data: {
|
|
315
|
+
...node.data,
|
|
316
|
+
disabled: !node.data?.disabled,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return node;
|
|
321
|
+
}));
|
|
322
|
+
}, [setNodes]);
|
|
323
|
+
|
|
324
|
+
// Note: executedNodes and executionOrder are now derived from per-workflow state above
|
|
325
|
+
|
|
326
|
+
// Settings state with localStorage persistence
|
|
327
|
+
const [settings, setSettings] = React.useState<WorkflowSettings>(() => {
|
|
328
|
+
try {
|
|
329
|
+
const saved = localStorage.getItem('workflow_settings');
|
|
330
|
+
return saved ? { ...defaultSettings, ...JSON.parse(saved) } : defaultSettings;
|
|
331
|
+
} catch {
|
|
332
|
+
return defaultSettings;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
const [settingsOpen, setSettingsOpen] = React.useState(false);
|
|
336
|
+
const [credentialsOpen, setCredentialsOpen] = React.useState(false);
|
|
337
|
+
const [consolePanelOpen, setConsolePanelOpen] = React.useState(false);
|
|
338
|
+
|
|
339
|
+
// Context menu state for node right-click
|
|
340
|
+
const [contextMenu, setContextMenu] = React.useState<{
|
|
341
|
+
nodeId: string;
|
|
342
|
+
x: number;
|
|
343
|
+
y: number;
|
|
344
|
+
} | null>(null);
|
|
345
|
+
|
|
346
|
+
// Persist settings to localStorage
|
|
347
|
+
React.useEffect(() => {
|
|
348
|
+
localStorage.setItem('workflow_settings', JSON.stringify(settings));
|
|
349
|
+
}, [settings]);
|
|
350
|
+
|
|
351
|
+
// Update nodes with execution status classes
|
|
352
|
+
const styledNodes = React.useMemo(() => {
|
|
353
|
+
return nodes.map(node => {
|
|
354
|
+
const nodeStatus = nodeStatuses[node.id];
|
|
355
|
+
let className = '';
|
|
356
|
+
|
|
357
|
+
if (nodeStatus?.status === 'executing' || nodeStatus?.status === 'waiting') {
|
|
358
|
+
className = 'executing';
|
|
359
|
+
} else if (nodeStatus?.status === 'success') {
|
|
360
|
+
className = 'completed';
|
|
361
|
+
} else if (nodeStatus?.status === 'error') {
|
|
362
|
+
className = 'error';
|
|
363
|
+
} else if (isExecuting && executionOrder.includes(node.id) && !executedNodes.has(node.id)) {
|
|
364
|
+
className = 'pending';
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
...node,
|
|
369
|
+
className
|
|
370
|
+
};
|
|
371
|
+
});
|
|
372
|
+
}, [nodes, nodeStatuses, isExecuting, executionOrder, executedNodes]);
|
|
373
|
+
|
|
374
|
+
// Update edges with execution status classes
|
|
375
|
+
const styledEdges = React.useMemo(() => {
|
|
376
|
+
return edges.map(edge => {
|
|
377
|
+
const sourceStatus = nodeStatuses[edge.source];
|
|
378
|
+
const targetStatus = nodeStatuses[edge.target];
|
|
379
|
+
const targetNode = nodes.find(n => n.id === edge.target);
|
|
380
|
+
|
|
381
|
+
let className = '';
|
|
382
|
+
|
|
383
|
+
// Check if this edge connects to an AI Agent's memory or tools/skill handle
|
|
384
|
+
const isMemoryConnection = edge.targetHandle === 'input-memory';
|
|
385
|
+
const isToolConnection = edge.targetHandle === 'input-tools';
|
|
386
|
+
const isSkillConnection = edge.targetHandle === 'input-skill';
|
|
387
|
+
const isAIAgentTarget = targetNode?.type === 'aiAgent' || targetNode?.type === 'chatAgent';
|
|
388
|
+
|
|
389
|
+
// Highlight memory/tool connections when AI Agent is executing and using them
|
|
390
|
+
if (isAIAgentTarget && targetStatus?.status === 'executing') {
|
|
391
|
+
const phase = targetStatus?.data?.phase as string | undefined;
|
|
392
|
+
const hasMemory = targetStatus?.data?.has_memory;
|
|
393
|
+
|
|
394
|
+
// Memory connection highlights during memory phases
|
|
395
|
+
if (isMemoryConnection && hasMemory) {
|
|
396
|
+
if (phase === 'loading_memory' || phase === 'memory_loaded' || phase === 'saving_memory') {
|
|
397
|
+
className = 'memory-active';
|
|
398
|
+
} else if (phase === 'invoking_llm') {
|
|
399
|
+
// Keep memory edge highlighted during LLM invocation to show context is being used
|
|
400
|
+
className = 'memory-active';
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
// Tool connection highlights when the specific tool node is executing
|
|
404
|
+
// Only highlight the edge whose source (tool node) is actually being used
|
|
405
|
+
else if (isToolConnection) {
|
|
406
|
+
const toolNodeStatus = sourceStatus?.status;
|
|
407
|
+
if (toolNodeStatus === 'executing') {
|
|
408
|
+
// This specific tool is being executed - highlight its edge
|
|
409
|
+
className = 'tool-active';
|
|
410
|
+
} else if ((phase === 'invoking_llm' || phase === 'building_graph') && toolNodeStatus === 'success') {
|
|
411
|
+
// Tool completed successfully - keep edge showing success
|
|
412
|
+
className = 'completed';
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Skill connection highlights during skill loading phase (Zeenie)
|
|
416
|
+
// Skills provide context to LLM, so highlight only when loading skills
|
|
417
|
+
else if (isSkillConnection) {
|
|
418
|
+
if (phase === 'loading_skills') {
|
|
419
|
+
className = 'skill-active';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Standard edge status classes - ONLY apply during active execution or deployment
|
|
425
|
+
// When not executing/deploying, all edges should have the same default cyan color
|
|
426
|
+
const isActiveExecution = isExecuting || isCurrentWorkflowDeployed;
|
|
427
|
+
if (!className && isActiveExecution) {
|
|
428
|
+
const srcStatus = sourceStatus?.status;
|
|
429
|
+
const tgtStatus = targetStatus?.status;
|
|
430
|
+
|
|
431
|
+
// Edge is executing if target is currently executing (data flowing into it)
|
|
432
|
+
if (tgtStatus === 'executing') {
|
|
433
|
+
className = 'executing';
|
|
434
|
+
}
|
|
435
|
+
// Edge is completed if both source and target are successful during this execution
|
|
436
|
+
else if (srcStatus === 'success' && tgtStatus === 'success') {
|
|
437
|
+
className = 'completed';
|
|
438
|
+
}
|
|
439
|
+
// Edge has error if target has error
|
|
440
|
+
else if (tgtStatus === 'error') {
|
|
441
|
+
className = 'error';
|
|
442
|
+
}
|
|
443
|
+
// Edge shows data flowing when source completed and target is waiting for inputs
|
|
444
|
+
// This indicates data has been produced and is available to the target
|
|
445
|
+
else if (srcStatus === 'success' && tgtStatus === 'waiting') {
|
|
446
|
+
className = 'executing';
|
|
447
|
+
}
|
|
448
|
+
// Edge is pending if source completed but target hasn't started
|
|
449
|
+
else if (srcStatus === 'success' && !tgtStatus) {
|
|
450
|
+
className = 'pending';
|
|
451
|
+
}
|
|
452
|
+
// Edge is pending if source is waiting (hasn't produced output yet)
|
|
453
|
+
// This keeps downstream edges from glowing until source completes
|
|
454
|
+
else if (srcStatus === 'waiting') {
|
|
455
|
+
className = 'pending';
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
...edge,
|
|
461
|
+
className
|
|
462
|
+
};
|
|
463
|
+
});
|
|
464
|
+
}, [edges, nodeStatuses, isExecuting, isCurrentWorkflowDeployed, nodes]);
|
|
465
|
+
|
|
466
|
+
// Memoize ReactFlow options to prevent unnecessary re-renders
|
|
467
|
+
const defaultEdgeOptions = React.useMemo(() => ({
|
|
468
|
+
type: 'smoothstep',
|
|
469
|
+
animated: true,
|
|
470
|
+
style: { stroke: theme.dracula.cyan, strokeWidth: 3 },
|
|
471
|
+
}), [theme.dracula.cyan]);
|
|
472
|
+
|
|
473
|
+
const connectionLineStyle = React.useMemo(() => ({
|
|
474
|
+
stroke: theme.dracula.cyan,
|
|
475
|
+
strokeWidth: 2
|
|
476
|
+
}), [theme.dracula.cyan]);
|
|
477
|
+
|
|
478
|
+
const reactFlowStyle = React.useMemo(() => ({
|
|
479
|
+
width: '100%',
|
|
480
|
+
height: '100%',
|
|
481
|
+
backgroundColor: theme.colors.background,
|
|
482
|
+
}), [theme.colors.background]);
|
|
483
|
+
|
|
484
|
+
const snapGrid: [number, number] = React.useMemo(() => [20, 20], []);
|
|
485
|
+
|
|
486
|
+
const proOptions = React.useMemo(() => ({ hideAttribution: true }), []);
|
|
487
|
+
|
|
488
|
+
// Use useRef for nodeTypes to guarantee the same object reference across all renders
|
|
489
|
+
// including React.StrictMode's double-render cycle. useMemo can't guarantee this
|
|
490
|
+
// because it may run during both render cycles.
|
|
491
|
+
const nodeTypesRef = React.useRef(moduleNodeTypes);
|
|
492
|
+
const edgeTypesRef = React.useRef(moduleEdgeTypes);
|
|
493
|
+
|
|
494
|
+
// Execute entire workflow from start node to end
|
|
495
|
+
const handleRun = async () => {
|
|
496
|
+
if (!currentWorkflow) return;
|
|
497
|
+
const workflowId = currentWorkflow.id;
|
|
498
|
+
|
|
499
|
+
// Use per-workflow state setters (n8n pattern)
|
|
500
|
+
setWorkflowExecuting(workflowId, true);
|
|
501
|
+
setExecutionResult(null);
|
|
502
|
+
clearWorkflowExecutionState(workflowId);
|
|
503
|
+
setWorkflowExecuting(workflowId, true); // Re-set after clear
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
// Check if there's a start node
|
|
507
|
+
const startNode = nodes.find(node => node.type === 'start');
|
|
508
|
+
if (!startNode) {
|
|
509
|
+
alert('No Start node found in workflow.\n\nAdd a Start node to begin workflow execution.');
|
|
510
|
+
setWorkflowExecuting(workflowId, false);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Build execution order for visual feedback (BFS from start node)
|
|
515
|
+
const buildOrder = () => {
|
|
516
|
+
const order: string[] = [];
|
|
517
|
+
const visited = new Set<string>();
|
|
518
|
+
const queue = [startNode.id];
|
|
519
|
+
const adjacencyMap = new Map<string, string[]>();
|
|
520
|
+
|
|
521
|
+
edges.forEach(edge => {
|
|
522
|
+
const sources = adjacencyMap.get(edge.source) || [];
|
|
523
|
+
sources.push(edge.target);
|
|
524
|
+
adjacencyMap.set(edge.source, sources);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
while (queue.length > 0) {
|
|
528
|
+
const currentId = queue.shift()!;
|
|
529
|
+
if (visited.has(currentId)) continue;
|
|
530
|
+
visited.add(currentId);
|
|
531
|
+
order.push(currentId);
|
|
532
|
+
|
|
533
|
+
const connected = adjacencyMap.get(currentId) || [];
|
|
534
|
+
connected.forEach(id => {
|
|
535
|
+
if (!visited.has(id)) queue.push(id);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return order;
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const order = buildOrder();
|
|
542
|
+
setWorkflowExecutionOrder(workflowId, order);
|
|
543
|
+
|
|
544
|
+
console.log('[Workflow Run] Starting workflow execution with', nodes.length, 'nodes and', edges.length, 'edges');
|
|
545
|
+
console.log('[Workflow Run] Execution order:', order);
|
|
546
|
+
|
|
547
|
+
// Execute the entire workflow via WebSocket
|
|
548
|
+
const workflowResult = await executeWorkflow(nodes, edges);
|
|
549
|
+
|
|
550
|
+
console.log('[Workflow Run] Execution complete:', workflowResult);
|
|
551
|
+
|
|
552
|
+
// Build result for display
|
|
553
|
+
const result = {
|
|
554
|
+
success: workflowResult.success,
|
|
555
|
+
nodeId: 'workflow',
|
|
556
|
+
nodeName: currentWorkflow.name || 'Workflow',
|
|
557
|
+
timestamp: new Date().toISOString(),
|
|
558
|
+
executionTime: workflowResult.execution_time || 0,
|
|
559
|
+
outputs: workflowResult.node_results || {},
|
|
560
|
+
data: workflowResult,
|
|
561
|
+
error: workflowResult.error || (workflowResult.errors?.length > 0 ? workflowResult.errors[0].error : undefined),
|
|
562
|
+
nodeData: workflowResult,
|
|
563
|
+
// Workflow-specific display data
|
|
564
|
+
nodesExecuted: workflowResult.nodes_executed || [],
|
|
565
|
+
executionOrder: workflowResult.execution_order || [],
|
|
566
|
+
totalNodes: workflowResult.total_nodes || 0,
|
|
567
|
+
completedNodes: workflowResult.completed_nodes || 0,
|
|
568
|
+
nodeResults: workflowResult.node_results || {},
|
|
569
|
+
errors: workflowResult.errors || [],
|
|
570
|
+
// For backwards compatibility with AI result modal
|
|
571
|
+
response: workflowResult.success
|
|
572
|
+
? `Workflow executed successfully. ${workflowResult.completed_nodes}/${workflowResult.total_nodes} nodes completed.`
|
|
573
|
+
: `Workflow failed: ${workflowResult.error || 'Unknown error'}`,
|
|
574
|
+
model: 'workflow'
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// Set result and show modal
|
|
578
|
+
setExecutionResult(result);
|
|
579
|
+
setShowResult(true);
|
|
580
|
+
|
|
581
|
+
} catch (error: any) {
|
|
582
|
+
console.error('Workflow execution error:', error);
|
|
583
|
+
|
|
584
|
+
// Create error result for modal display
|
|
585
|
+
const errorResult = {
|
|
586
|
+
success: false,
|
|
587
|
+
nodeId: 'workflow',
|
|
588
|
+
nodeName: currentWorkflow?.name || 'Workflow',
|
|
589
|
+
timestamp: new Date().toISOString(),
|
|
590
|
+
executionTime: 0,
|
|
591
|
+
error: error.message || 'Unknown execution error',
|
|
592
|
+
response: `Error: ${error.message}`,
|
|
593
|
+
model: 'workflow'
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
setExecutionResult(errorResult);
|
|
597
|
+
setShowResult(true);
|
|
598
|
+
} finally {
|
|
599
|
+
setWorkflowExecuting(workflowId, false);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// Deploy workflow - runs continuously until cancelled
|
|
604
|
+
const handleDeploy = async () => {
|
|
605
|
+
if (!currentWorkflow) return;
|
|
606
|
+
|
|
607
|
+
// Check if there's at least one trigger node (workflow entry points)
|
|
608
|
+
// Trigger types: start, cronScheduler, webhookTrigger, whatsappReceive, workflowTrigger, chatTrigger
|
|
609
|
+
const triggerTypes = ['start', 'cronScheduler', 'webhookTrigger', 'whatsappReceive', 'workflowTrigger', 'chatTrigger'];
|
|
610
|
+
const hasTriggerNode = nodes.some(node => triggerTypes.includes(node.type || ''));
|
|
611
|
+
if (!hasTriggerNode) {
|
|
612
|
+
alert('No trigger node found in workflow.\n\nAdd a trigger node (Cron Scheduler, WhatsApp Receive, Webhook, Chat Trigger, etc.) to begin deployment.');
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
try {
|
|
617
|
+
// Settings are already synced to backend via WebSocket from SettingsPanel
|
|
618
|
+
// Backend will use the stored settings
|
|
619
|
+
|
|
620
|
+
// DEBUG: Log edges being sent to deployment
|
|
621
|
+
console.log('[Dashboard] Deploying with edges:', {
|
|
622
|
+
edgeCount: edges.length,
|
|
623
|
+
edges: edges.map(e => ({
|
|
624
|
+
id: e.id,
|
|
625
|
+
source: e.source,
|
|
626
|
+
target: e.target,
|
|
627
|
+
sourceHandle: e.sourceHandle,
|
|
628
|
+
targetHandle: e.targetHandle
|
|
629
|
+
})),
|
|
630
|
+
// Check for toolkit connections specifically
|
|
631
|
+
toolkitEdges: edges.filter(e =>
|
|
632
|
+
e.target?.includes('androidTool') || e.source?.includes('androidTool')
|
|
633
|
+
)
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
const result = await deployWorkflow(currentWorkflow.id, nodes, edges, 'default');
|
|
637
|
+
|
|
638
|
+
if (!result.success) {
|
|
639
|
+
console.error('[Dashboard] Deployment failed:', result.error);
|
|
640
|
+
alert(`Failed to start deployment: ${result.error}`);
|
|
641
|
+
}
|
|
642
|
+
} catch (error: any) {
|
|
643
|
+
console.error('[Dashboard] Deployment error:', error);
|
|
644
|
+
alert(`Deployment error: ${error.message}`);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// Cancel running deployment for current workflow
|
|
649
|
+
const handleCancelDeployment = async () => {
|
|
650
|
+
try {
|
|
651
|
+
const workflowId = currentWorkflow?.id;
|
|
652
|
+
console.log('[Dashboard] Cancelling deployment for workflow:', workflowId);
|
|
653
|
+
const result = await cancelDeployment(workflowId);
|
|
654
|
+
|
|
655
|
+
if (result.success) {
|
|
656
|
+
console.log('[Dashboard] Deployment cancelled:', result);
|
|
657
|
+
} else {
|
|
658
|
+
console.error('[Dashboard] Failed to cancel deployment:', result.message);
|
|
659
|
+
}
|
|
660
|
+
} catch (error: any) {
|
|
661
|
+
console.error('[Dashboard] Cancel deployment error:', error);
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const handleExportJSON = async () => {
|
|
666
|
+
try {
|
|
667
|
+
const jsonString = exportWorkflowToJSON();
|
|
668
|
+
await navigator.clipboard.writeText(jsonString);
|
|
669
|
+
alert('Workflow JSON copied to clipboard');
|
|
670
|
+
} catch (error) {
|
|
671
|
+
console.error('Export JSON error:', error);
|
|
672
|
+
alert('Failed to export workflow JSON');
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const handleExportFile = () => {
|
|
677
|
+
try {
|
|
678
|
+
exportWorkflowToFile();
|
|
679
|
+
} catch (error) {
|
|
680
|
+
console.error('Export file error:', error);
|
|
681
|
+
alert('Failed to export workflow file');
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const handleImportJSON = () => {
|
|
686
|
+
const fileInput = document.createElement('input');
|
|
687
|
+
fileInput.type = 'file';
|
|
688
|
+
fileInput.accept = '.json';
|
|
689
|
+
fileInput.onchange = async (e) => {
|
|
690
|
+
try {
|
|
691
|
+
const file = (e.target as HTMLInputElement).files?.[0];
|
|
692
|
+
if (!file) return;
|
|
693
|
+
|
|
694
|
+
const importedWorkflow = await importWorkflowFromFile(file);
|
|
695
|
+
|
|
696
|
+
// Check for name conflict with existing workflows
|
|
697
|
+
const existingNames = savedWorkflows.map(w => w.name.toLowerCase());
|
|
698
|
+
let finalName = importedWorkflow.name;
|
|
699
|
+
|
|
700
|
+
if (existingNames.includes(finalName.toLowerCase())) {
|
|
701
|
+
// Name conflict detected - prompt user for new name
|
|
702
|
+
const suggestedName = `${importedWorkflow.name} (imported)`;
|
|
703
|
+
const userInput = window.prompt(
|
|
704
|
+
`A workflow named "${importedWorkflow.name}" already exists.\n\nEnter a new name for the imported workflow:`,
|
|
705
|
+
suggestedName
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
if (userInput === null) {
|
|
709
|
+
// User cancelled the import
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
finalName = userInput.trim();
|
|
714
|
+
|
|
715
|
+
if (!finalName) {
|
|
716
|
+
alert('Workflow name cannot be empty');
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Check if the new name also conflicts
|
|
721
|
+
if (existingNames.includes(finalName.toLowerCase())) {
|
|
722
|
+
alert(`A workflow named "${finalName}" also exists. Please try again with a different name.`);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
const workflow = {
|
|
728
|
+
...importedWorkflow,
|
|
729
|
+
name: finalName,
|
|
730
|
+
id: generateWorkflowId(),
|
|
731
|
+
createdAt: new Date(),
|
|
732
|
+
lastModified: new Date()
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
console.log('Importing workflow:', workflow);
|
|
736
|
+
|
|
737
|
+
// Save node parameters to database
|
|
738
|
+
for (const node of workflow.nodes) {
|
|
739
|
+
if (node.data && Object.keys(node.data).length > 0) {
|
|
740
|
+
try {
|
|
741
|
+
await saveNodeParameters(node.id, node.data);
|
|
742
|
+
console.log(`Saved parameters for node ${node.id}:`, node.data);
|
|
743
|
+
} catch (error) {
|
|
744
|
+
console.error(`Failed to save parameters for node ${node.id}:`, error);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Set as current workflow first
|
|
750
|
+
setCurrentWorkflow(workflow);
|
|
751
|
+
|
|
752
|
+
// Auto-save to database so it appears in sidebar immediately
|
|
753
|
+
await saveWorkflow();
|
|
754
|
+
|
|
755
|
+
console.log('Workflow imported and saved successfully');
|
|
756
|
+
alert(`Workflow "${workflow.name}" imported with ${workflow.nodes.length} nodes and ${workflow.edges.length} connections`);
|
|
757
|
+
} catch (error: any) {
|
|
758
|
+
console.error('Import error:', error);
|
|
759
|
+
alert(`Failed to import workflow: ${error.message}`);
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
fileInput.click();
|
|
763
|
+
};
|
|
764
|
+
// Load saved workflows on mount and auto-select most recent or create new if none exist
|
|
765
|
+
const hasMigrated = React.useRef(false);
|
|
766
|
+
const hasInitialized = React.useRef(false);
|
|
767
|
+
useEffect(() => {
|
|
768
|
+
if (hasInitialized.current) return;
|
|
769
|
+
hasInitialized.current = true;
|
|
770
|
+
|
|
771
|
+
console.log('[Dashboard] Mount effect - loading workflows', {
|
|
772
|
+
hasCurrentWorkflow: !!currentWorkflow,
|
|
773
|
+
currentWorkflowId: currentWorkflow?.id,
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
const initWorkflows = async () => {
|
|
777
|
+
await loadSavedWorkflows();
|
|
778
|
+
// loadSavedWorkflows auto-loads the most recent workflow if none is set
|
|
779
|
+
// Only create new if still no workflow after loading
|
|
780
|
+
const state = useAppStore.getState();
|
|
781
|
+
if (!state.currentWorkflow) {
|
|
782
|
+
console.log('[Dashboard] No saved workflows found, creating new one');
|
|
783
|
+
createNewWorkflow();
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
if (!currentWorkflow) {
|
|
788
|
+
initWorkflows();
|
|
789
|
+
} else if (!hasMigrated.current) {
|
|
790
|
+
console.log('[Dashboard] Migrating current workflow');
|
|
791
|
+
migrateCurrentWorkflow();
|
|
792
|
+
hasMigrated.current = true;
|
|
793
|
+
loadSavedWorkflows(); // Still load sidebar list
|
|
794
|
+
}
|
|
795
|
+
}, [loadSavedWorkflows, currentWorkflow, createNewWorkflow, migrateCurrentWorkflow]);
|
|
796
|
+
|
|
797
|
+
// Sync workflow state → ReactFlow state (when loading workflows or data changes)
|
|
798
|
+
// Note: Database is the source of truth for parameters - node.data should NOT store parameters
|
|
799
|
+
// Parameters are loaded from database when parameter panel opens (useParameterPanel hook)
|
|
800
|
+
// and when backend executes nodes (NodeExecutor._prepare_parameters)
|
|
801
|
+
useEffect(() => {
|
|
802
|
+
if (currentWorkflow && currentWorkflow.id) {
|
|
803
|
+
const workflowNodes = currentWorkflow.nodes || [];
|
|
804
|
+
setNodes(workflowNodes);
|
|
805
|
+
setEdges(currentWorkflow.edges || []);
|
|
806
|
+
// Do NOT sync database parameters to node.data
|
|
807
|
+
// Database is the single source of truth for parameters
|
|
808
|
+
// This prevents dual storage issues where node.data could diverge from database
|
|
809
|
+
}
|
|
810
|
+
}, [currentWorkflow?.id, currentWorkflow?.lastModified, setNodes, setEdges]);
|
|
811
|
+
|
|
812
|
+
// Sync ReactFlow state → workflow state (debounced for performance)
|
|
813
|
+
useEffect(() => {
|
|
814
|
+
if (!currentWorkflow || !currentWorkflow.id) return;
|
|
815
|
+
|
|
816
|
+
const timeoutId = setTimeout(() => {
|
|
817
|
+
try {
|
|
818
|
+
const currentNodesStr = JSON.stringify(sanitizeNodesForComparison(nodes));
|
|
819
|
+
const currentEdgesStr = JSON.stringify(sanitizeEdgesForComparison(edges));
|
|
820
|
+
const workflowNodesStr = JSON.stringify(sanitizeNodesForComparison(currentWorkflow.nodes || []));
|
|
821
|
+
const workflowEdgesStr = JSON.stringify(sanitizeEdgesForComparison(currentWorkflow.edges || []));
|
|
822
|
+
|
|
823
|
+
if (currentNodesStr !== workflowNodesStr || currentEdgesStr !== workflowEdgesStr) {
|
|
824
|
+
console.log('[Dashboard] Syncing ReactFlow -> Store', {
|
|
825
|
+
reactFlowEdgeCount: edges.length,
|
|
826
|
+
storeEdgeCount: (currentWorkflow.edges || []).length,
|
|
827
|
+
newEdges: edges.filter(e => !(currentWorkflow.edges || []).find(we => we.id === e.id))
|
|
828
|
+
});
|
|
829
|
+
updateWorkflow({ nodes, edges });
|
|
830
|
+
}
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.warn('Failed to sync workflow state:', error);
|
|
833
|
+
}
|
|
834
|
+
}, theme.constants.debounceDelay.workflowUpdate);
|
|
835
|
+
|
|
836
|
+
return () => clearTimeout(timeoutId);
|
|
837
|
+
}, [nodes, edges, currentWorkflow?.id, updateWorkflow]);
|
|
838
|
+
|
|
839
|
+
// Track previous workflow ID for viewport save/restore (n8n pattern)
|
|
840
|
+
const prevWorkflowIdRef = React.useRef<string | null>(null);
|
|
841
|
+
// Track if we've already restored viewport for current workflow (prevent duplicate restores)
|
|
842
|
+
const viewportRestoredForRef = React.useRef<string | null>(null);
|
|
843
|
+
|
|
844
|
+
// Save viewport when switching workflows, restore after nodes load (n8n pattern)
|
|
845
|
+
useEffect(() => {
|
|
846
|
+
const currentId = currentWorkflow?.id;
|
|
847
|
+
const prevId = prevWorkflowIdRef.current;
|
|
848
|
+
|
|
849
|
+
// Save viewport of previous workflow before switching
|
|
850
|
+
if (prevId && prevId !== currentId) {
|
|
851
|
+
try {
|
|
852
|
+
const viewport = reactFlowInstance.getViewport();
|
|
853
|
+
setWorkflowViewport(prevId, viewport);
|
|
854
|
+
} catch {
|
|
855
|
+
// Failed to save viewport - ignore
|
|
856
|
+
}
|
|
857
|
+
// Reset the restored flag when switching to new workflow
|
|
858
|
+
viewportRestoredForRef.current = null;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
prevWorkflowIdRef.current = currentId || null;
|
|
862
|
+
}, [currentWorkflow?.id, reactFlowInstance, setWorkflowViewport]);
|
|
863
|
+
|
|
864
|
+
// Restore viewport AFTER nodes are loaded and rendered
|
|
865
|
+
// Only restores saved viewport - never auto-centers
|
|
866
|
+
useEffect(() => {
|
|
867
|
+
const currentId = currentWorkflow?.id;
|
|
868
|
+
if (!currentId) return;
|
|
869
|
+
|
|
870
|
+
// Skip if we already restored viewport for this workflow
|
|
871
|
+
if (viewportRestoredForRef.current === currentId) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Get saved viewport from store
|
|
876
|
+
const uiState = workflowUIStates[currentId];
|
|
877
|
+
const savedViewport = uiState?.viewport;
|
|
878
|
+
|
|
879
|
+
// Only restore if we have a saved viewport
|
|
880
|
+
if (!savedViewport) {
|
|
881
|
+
viewportRestoredForRef.current = currentId;
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Use delay to ensure ReactFlow has finished rendering nodes
|
|
886
|
+
const timeoutId = setTimeout(() => {
|
|
887
|
+
try {
|
|
888
|
+
reactFlowInstance.setViewport(savedViewport, { duration: 0 });
|
|
889
|
+
viewportRestoredForRef.current = currentId;
|
|
890
|
+
} catch {
|
|
891
|
+
// Viewport restore failed - ignore silently
|
|
892
|
+
}
|
|
893
|
+
}, 100);
|
|
894
|
+
|
|
895
|
+
return () => clearTimeout(timeoutId);
|
|
896
|
+
}, [currentWorkflow?.id, nodes.length, workflowUIStates, reactFlowInstance]);
|
|
897
|
+
|
|
898
|
+
// Node context menu handler (right-click)
|
|
899
|
+
const onNodeContextMenu = React.useCallback(
|
|
900
|
+
(event: React.MouseEvent, node: Node) => {
|
|
901
|
+
event.preventDefault();
|
|
902
|
+
// Select the node when right-clicking
|
|
903
|
+
setSelectedNode(node);
|
|
904
|
+
setContextMenu({
|
|
905
|
+
nodeId: node.id,
|
|
906
|
+
x: event.clientX,
|
|
907
|
+
y: event.clientY,
|
|
908
|
+
});
|
|
909
|
+
},
|
|
910
|
+
[setSelectedNode]
|
|
911
|
+
);
|
|
912
|
+
|
|
913
|
+
// Close context menu
|
|
914
|
+
const closeContextMenu = React.useCallback(() => {
|
|
915
|
+
setContextMenu(null);
|
|
916
|
+
}, []);
|
|
917
|
+
|
|
918
|
+
// Context menu actions
|
|
919
|
+
const handleContextMenuRename = React.useCallback(() => {
|
|
920
|
+
if (contextMenu) {
|
|
921
|
+
setRenamingNodeId(contextMenu.nodeId);
|
|
922
|
+
}
|
|
923
|
+
closeContextMenu();
|
|
924
|
+
}, [contextMenu, setRenamingNodeId, closeContextMenu]);
|
|
925
|
+
|
|
926
|
+
const handleContextMenuCopy = React.useCallback(() => {
|
|
927
|
+
if (contextMenu) {
|
|
928
|
+
// Select the node first, then copy
|
|
929
|
+
const node = nodes.find(n => n.id === contextMenu.nodeId);
|
|
930
|
+
if (node) {
|
|
931
|
+
setNodes(nds => nds.map(n => ({ ...n, selected: n.id === contextMenu.nodeId })));
|
|
932
|
+
// Small delay to ensure selection is applied before copy
|
|
933
|
+
setTimeout(() => copySelectedNodes(), 0);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
closeContextMenu();
|
|
937
|
+
}, [contextMenu, nodes, setNodes, copySelectedNodes, closeContextMenu]);
|
|
938
|
+
|
|
939
|
+
const handleContextMenuDelete = React.useCallback(() => {
|
|
940
|
+
if (contextMenu) {
|
|
941
|
+
onNodesDelete([nodes.find(n => n.id === contextMenu.nodeId)].filter(Boolean) as Node[]);
|
|
942
|
+
}
|
|
943
|
+
closeContextMenu();
|
|
944
|
+
}, [contextMenu, nodes, onNodesDelete, closeContextMenu]);
|
|
945
|
+
|
|
946
|
+
// Keyboard shortcut handler for workflow operations
|
|
947
|
+
useEffect(() => {
|
|
948
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
949
|
+
// Ignore shortcuts when typing in input/textarea
|
|
950
|
+
if (event.target instanceof HTMLInputElement ||
|
|
951
|
+
event.target instanceof HTMLTextAreaElement) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// Ignore shortcuts when renaming a node
|
|
956
|
+
if (renamingNodeId) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// F2 to rename selected node
|
|
961
|
+
if (event.key === 'F2' && selectedNode) {
|
|
962
|
+
event.preventDefault();
|
|
963
|
+
setRenamingNodeId(selectedNode.id);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Check for Ctrl/Cmd key shortcuts
|
|
968
|
+
if (event.ctrlKey || event.metaKey) {
|
|
969
|
+
switch (event.key.toLowerCase()) {
|
|
970
|
+
case 's':
|
|
971
|
+
event.preventDefault();
|
|
972
|
+
handleSave();
|
|
973
|
+
break;
|
|
974
|
+
case 'c':
|
|
975
|
+
event.preventDefault();
|
|
976
|
+
copySelectedNodes();
|
|
977
|
+
break;
|
|
978
|
+
case 'v':
|
|
979
|
+
event.preventDefault();
|
|
980
|
+
pasteNodes();
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
} else {
|
|
984
|
+
// Non-modifier shortcuts
|
|
985
|
+
switch (event.key.toLowerCase()) {
|
|
986
|
+
case 'd':
|
|
987
|
+
// Toggle disable on selected nodes
|
|
988
|
+
event.preventDefault();
|
|
989
|
+
toggleDisableSelected();
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
996
|
+
|
|
997
|
+
return () => {
|
|
998
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
999
|
+
};
|
|
1000
|
+
}, [handleSave, copySelectedNodes, pasteNodes, toggleDisableSelected, selectedNode, renamingNodeId, setRenamingNodeId]);
|
|
1001
|
+
|
|
1002
|
+
return (
|
|
1003
|
+
<>
|
|
1004
|
+
<style>{getEdgeStyles(theme.colors, isDarkMode)}</style>
|
|
1005
|
+
<div style={{
|
|
1006
|
+
width: '100%',
|
|
1007
|
+
height: '100vh',
|
|
1008
|
+
display: 'flex',
|
|
1009
|
+
flexDirection: 'column',
|
|
1010
|
+
backgroundColor: theme.colors.background,
|
|
1011
|
+
fontFamily: 'system-ui, sans-serif',
|
|
1012
|
+
}}>
|
|
1013
|
+
{/* Top Toolbar */}
|
|
1014
|
+
<TopToolbar
|
|
1015
|
+
workflowName={currentWorkflow?.name || 'Untitled Workflow'}
|
|
1016
|
+
onWorkflowNameChange={handleWorkflowNameChange}
|
|
1017
|
+
onSave={handleSave}
|
|
1018
|
+
onNew={handleNew}
|
|
1019
|
+
onOpen={handleOpen}
|
|
1020
|
+
onRun={handleRun}
|
|
1021
|
+
isRunning={isExecuting}
|
|
1022
|
+
onDeploy={handleDeploy}
|
|
1023
|
+
onCancelDeployment={handleCancelDeployment}
|
|
1024
|
+
isDeploying={isCurrentWorkflowDeployed}
|
|
1025
|
+
hasUnsavedChanges={hasUnsavedChanges}
|
|
1026
|
+
sidebarVisible={sidebarVisible}
|
|
1027
|
+
onToggleSidebar={toggleSidebar}
|
|
1028
|
+
componentPaletteVisible={componentPaletteVisible}
|
|
1029
|
+
onToggleComponentPalette={toggleComponentPalette}
|
|
1030
|
+
proMode={proMode}
|
|
1031
|
+
onToggleProMode={toggleProMode}
|
|
1032
|
+
onOpenSettings={() => setSettingsOpen(true)}
|
|
1033
|
+
onOpenCredentials={() => setCredentialsOpen(true)}
|
|
1034
|
+
onExportJSON={handleExportJSON}
|
|
1035
|
+
onExportFile={handleExportFile}
|
|
1036
|
+
onImportJSON={handleImportJSON}
|
|
1037
|
+
/>
|
|
1038
|
+
|
|
1039
|
+
{/* Main Content Area */}
|
|
1040
|
+
<div style={{
|
|
1041
|
+
flex: 1,
|
|
1042
|
+
display: 'flex',
|
|
1043
|
+
overflow: 'hidden',
|
|
1044
|
+
}}>
|
|
1045
|
+
{/* Left Workflow Sidebar */}
|
|
1046
|
+
<div style={{
|
|
1047
|
+
width: sidebarVisible ? '280px' : '0px',
|
|
1048
|
+
overflow: 'hidden',
|
|
1049
|
+
transition: 'width 0.3s ease',
|
|
1050
|
+
borderRight: sidebarVisible ? `1px solid ${theme.colors.border}` : 'none',
|
|
1051
|
+
display: 'flex',
|
|
1052
|
+
flexDirection: 'column',
|
|
1053
|
+
}}>
|
|
1054
|
+
{sidebarVisible && (
|
|
1055
|
+
<WorkflowSidebar
|
|
1056
|
+
workflows={savedWorkflows}
|
|
1057
|
+
currentWorkflowId={currentWorkflow?.id}
|
|
1058
|
+
onSelectWorkflow={handleSelectWorkflow}
|
|
1059
|
+
onDeleteWorkflow={deleteWorkflow}
|
|
1060
|
+
/>
|
|
1061
|
+
)}
|
|
1062
|
+
</div>
|
|
1063
|
+
|
|
1064
|
+
{/* Canvas Area */}
|
|
1065
|
+
<div style={{
|
|
1066
|
+
flex: 1,
|
|
1067
|
+
display: 'flex',
|
|
1068
|
+
position: 'relative',
|
|
1069
|
+
}}>
|
|
1070
|
+
<div style={{
|
|
1071
|
+
flex: 1,
|
|
1072
|
+
backgroundColor: theme.colors.backgroundAlt,
|
|
1073
|
+
}}>
|
|
1074
|
+
<ErrorBoundary>
|
|
1075
|
+
<ReactFlow
|
|
1076
|
+
nodes={styledNodes}
|
|
1077
|
+
edges={styledEdges}
|
|
1078
|
+
onNodesChange={onNodesChange}
|
|
1079
|
+
onEdgesChange={onEdgesChange}
|
|
1080
|
+
onNodesDelete={onNodesDelete}
|
|
1081
|
+
onEdgesDelete={onEdgesDelete}
|
|
1082
|
+
onConnect={onConnect}
|
|
1083
|
+
onDragOver={onDragOver}
|
|
1084
|
+
onDrop={onDrop}
|
|
1085
|
+
onNodeContextMenu={onNodeContextMenu}
|
|
1086
|
+
nodeTypes={nodeTypesRef.current}
|
|
1087
|
+
edgeTypes={edgeTypesRef.current}
|
|
1088
|
+
connectionMode={ConnectionMode.Loose}
|
|
1089
|
+
deleteKeyCode={isCurrentWorkflowLocked ? [] : ['Delete', 'Backspace']}
|
|
1090
|
+
edgesFocusable={!isCurrentWorkflowLocked}
|
|
1091
|
+
edgesUpdatable={!isCurrentWorkflowLocked}
|
|
1092
|
+
nodesDraggable={!isCurrentWorkflowLocked}
|
|
1093
|
+
nodesConnectable={!isCurrentWorkflowLocked}
|
|
1094
|
+
nodesFocusable={!isCurrentWorkflowLocked}
|
|
1095
|
+
elementsSelectable={!isCurrentWorkflowLocked}
|
|
1096
|
+
selectNodesOnDrag={false}
|
|
1097
|
+
selectionOnDrag={true}
|
|
1098
|
+
selectionMode={SelectionMode.Partial}
|
|
1099
|
+
selectionKeyCode="Control"
|
|
1100
|
+
panOnDrag={true}
|
|
1101
|
+
panOnScroll={false}
|
|
1102
|
+
zoomOnScroll={true}
|
|
1103
|
+
preventScrolling={true}
|
|
1104
|
+
proOptions={proOptions}
|
|
1105
|
+
defaultEdgeOptions={defaultEdgeOptions}
|
|
1106
|
+
connectionLineStyle={connectionLineStyle}
|
|
1107
|
+
connectionLineType={ConnectionLineType.SmoothStep}
|
|
1108
|
+
snapToGrid={true}
|
|
1109
|
+
snapGrid={snapGrid}
|
|
1110
|
+
style={reactFlowStyle}
|
|
1111
|
+
>
|
|
1112
|
+
<Controls />
|
|
1113
|
+
</ReactFlow>
|
|
1114
|
+
</ErrorBoundary>
|
|
1115
|
+
</div>
|
|
1116
|
+
|
|
1117
|
+
{/* Right Component Palette */}
|
|
1118
|
+
<div style={{
|
|
1119
|
+
width: componentPaletteVisible ? theme.layout.sidebarWidth : '0px',
|
|
1120
|
+
overflow: 'hidden',
|
|
1121
|
+
transition: 'width 0.3s ease',
|
|
1122
|
+
borderLeft: componentPaletteVisible ? `1px solid ${theme.colors.border}` : 'none',
|
|
1123
|
+
display: 'flex',
|
|
1124
|
+
flexDirection: 'column',
|
|
1125
|
+
}}>
|
|
1126
|
+
{componentPaletteVisible && (
|
|
1127
|
+
<ComponentPalette
|
|
1128
|
+
nodeDefinitions={nodeDefinitions}
|
|
1129
|
+
searchQuery={searchQuery}
|
|
1130
|
+
onSearchChange={setSearchQuery}
|
|
1131
|
+
collapsedSections={collapsedSections}
|
|
1132
|
+
onToggleSection={toggleSection}
|
|
1133
|
+
onDragStart={handleComponentDragStart}
|
|
1134
|
+
proMode={proMode}
|
|
1135
|
+
/>
|
|
1136
|
+
)}
|
|
1137
|
+
</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
</div>
|
|
1140
|
+
|
|
1141
|
+
{/* Console Panel - n8n-style debug output at bottom */}
|
|
1142
|
+
<ConsolePanel
|
|
1143
|
+
isOpen={consolePanelOpen}
|
|
1144
|
+
onToggle={() => setConsolePanelOpen(prev => !prev)}
|
|
1145
|
+
nodes={nodes}
|
|
1146
|
+
/>
|
|
1147
|
+
|
|
1148
|
+
{/* Parameter Panels */}
|
|
1149
|
+
<ErrorBoundary>
|
|
1150
|
+
<ParameterPanel />
|
|
1151
|
+
<LocationParameterPanel />
|
|
1152
|
+
</ErrorBoundary>
|
|
1153
|
+
|
|
1154
|
+
{/* AI Result Modal */}
|
|
1155
|
+
<AIResultModal
|
|
1156
|
+
isOpen={showResult}
|
|
1157
|
+
onClose={() => setShowResult(false)}
|
|
1158
|
+
result={executionResult}
|
|
1159
|
+
/>
|
|
1160
|
+
|
|
1161
|
+
{/* Settings Panel Modal */}
|
|
1162
|
+
<SettingsPanel
|
|
1163
|
+
isOpen={settingsOpen}
|
|
1164
|
+
onClose={() => setSettingsOpen(false)}
|
|
1165
|
+
settings={settings}
|
|
1166
|
+
onSettingsChange={setSettings}
|
|
1167
|
+
/>
|
|
1168
|
+
|
|
1169
|
+
{/* Credentials Modal */}
|
|
1170
|
+
<CredentialsModal
|
|
1171
|
+
visible={credentialsOpen}
|
|
1172
|
+
onClose={() => setCredentialsOpen(false)}
|
|
1173
|
+
/>
|
|
1174
|
+
|
|
1175
|
+
{/* Node Context Menu (right-click) */}
|
|
1176
|
+
{contextMenu && (
|
|
1177
|
+
<NodeContextMenu
|
|
1178
|
+
nodeId={contextMenu.nodeId}
|
|
1179
|
+
x={contextMenu.x}
|
|
1180
|
+
y={contextMenu.y}
|
|
1181
|
+
onClose={closeContextMenu}
|
|
1182
|
+
onRename={handleContextMenuRename}
|
|
1183
|
+
onCopy={handleContextMenuCopy}
|
|
1184
|
+
onDelete={handleContextMenuDelete}
|
|
1185
|
+
/>
|
|
1186
|
+
)}
|
|
1187
|
+
</div>
|
|
1188
|
+
</>
|
|
1189
|
+
);
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
// Outer wrapper component that provides ReactFlowProvider context
|
|
1193
|
+
const Dashboard: React.FC = () => {
|
|
1194
|
+
return (
|
|
1195
|
+
<ReactFlowProvider>
|
|
1196
|
+
<DashboardContent />
|
|
1197
|
+
</ReactFlowProvider>
|
|
1198
|
+
);
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1173
1201
|
export default Dashboard;
|