machinaos 0.0.1
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 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/bin/cli.js +159 -0
- package/client/.dockerignore +45 -0
- package/client/Dockerfile +68 -0
- package/client/eslint.config.js +29 -0
- package/client/index.html +13 -0
- package/client/nginx.conf +66 -0
- package/client/package.json +48 -0
- package/client/src/App.tsx +27 -0
- package/client/src/Dashboard.tsx +1173 -0
- package/client/src/ParameterPanel.tsx +301 -0
- package/client/src/components/AIAgentNode.tsx +321 -0
- package/client/src/components/APIKeyValidator.tsx +118 -0
- package/client/src/components/ClaudeChatModelNode.tsx +18 -0
- package/client/src/components/ConditionalEdge.tsx +189 -0
- package/client/src/components/CredentialsModal.tsx +306 -0
- package/client/src/components/EdgeConditionEditor.tsx +443 -0
- package/client/src/components/GeminiChatModelNode.tsx +18 -0
- package/client/src/components/GenericNode.tsx +357 -0
- package/client/src/components/LocationParameterPanel.tsx +154 -0
- package/client/src/components/ModelNode.tsx +286 -0
- package/client/src/components/OpenAIChatModelNode.tsx +18 -0
- package/client/src/components/OutputPanel.tsx +471 -0
- package/client/src/components/ParameterRenderer.tsx +1874 -0
- package/client/src/components/SkillEditorModal.tsx +417 -0
- package/client/src/components/SquareNode.tsx +797 -0
- package/client/src/components/StartNode.tsx +250 -0
- package/client/src/components/ToolkitNode.tsx +365 -0
- package/client/src/components/TriggerNode.tsx +463 -0
- package/client/src/components/auth/LoginPage.tsx +247 -0
- package/client/src/components/auth/ProtectedRoute.tsx +59 -0
- package/client/src/components/base/BaseChatModelNode.tsx +271 -0
- package/client/src/components/icons/AIProviderIcons.tsx +50 -0
- package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
- package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
- package/client/src/components/maps/index.ts +26 -0
- package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
- package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
- package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
- package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
- package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
- package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
- package/client/src/components/parameterPanel/index.ts +42 -0
- package/client/src/components/shared/DataPanel.tsx +142 -0
- package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
- package/client/src/components/ui/AIResultModal.tsx +204 -0
- package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
- package/client/src/components/ui/CodeEditor.tsx +81 -0
- package/client/src/components/ui/CollapsibleSection.tsx +88 -0
- package/client/src/components/ui/ComponentItem.tsx +154 -0
- package/client/src/components/ui/ComponentPalette.tsx +321 -0
- package/client/src/components/ui/ConsolePanel.tsx +1074 -0
- package/client/src/components/ui/ErrorBoundary.tsx +196 -0
- package/client/src/components/ui/InputNodesPanel.tsx +204 -0
- package/client/src/components/ui/MapSelector.tsx +314 -0
- package/client/src/components/ui/Modal.tsx +149 -0
- package/client/src/components/ui/NodeContextMenu.tsx +192 -0
- package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
- package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
- package/client/src/components/ui/SettingsPanel.tsx +243 -0
- package/client/src/components/ui/TopToolbar.tsx +736 -0
- package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
- package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
- package/client/src/config/antdTheme.ts +186 -0
- package/client/src/config/api.ts +54 -0
- package/client/src/contexts/AuthContext.tsx +221 -0
- package/client/src/contexts/ThemeContext.tsx +42 -0
- package/client/src/contexts/WebSocketContext.tsx +1971 -0
- package/client/src/factories/baseChatModelFactory.ts +256 -0
- package/client/src/hooks/useAndroidOperations.ts +164 -0
- package/client/src/hooks/useApiKeyValidation.ts +107 -0
- package/client/src/hooks/useApiKeys.ts +238 -0
- package/client/src/hooks/useAppTheme.ts +17 -0
- package/client/src/hooks/useComponentPalette.ts +51 -0
- package/client/src/hooks/useCopyPaste.ts +155 -0
- package/client/src/hooks/useDragAndDrop.ts +124 -0
- package/client/src/hooks/useDragVariable.ts +88 -0
- package/client/src/hooks/useExecution.ts +313 -0
- package/client/src/hooks/useParameterPanel.ts +176 -0
- package/client/src/hooks/useReactFlowNodes.ts +189 -0
- package/client/src/hooks/useToolSchema.ts +209 -0
- package/client/src/hooks/useWhatsApp.ts +196 -0
- package/client/src/hooks/useWorkflowManagement.ts +46 -0
- package/client/src/index.css +315 -0
- package/client/src/main.tsx +19 -0
- package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
- package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
- package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
- package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
- package/client/src/nodeDefinitions/chatNodes.ts +135 -0
- package/client/src/nodeDefinitions/codeNodes.ts +54 -0
- package/client/src/nodeDefinitions/documentNodes.ts +379 -0
- package/client/src/nodeDefinitions/index.ts +15 -0
- package/client/src/nodeDefinitions/locationNodes.ts +463 -0
- package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
- package/client/src/nodeDefinitions/skillNodes.ts +211 -0
- package/client/src/nodeDefinitions/toolNodes.ts +198 -0
- package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
- package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
- package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
- package/client/src/nodeDefinitions.ts +104 -0
- package/client/src/schemas/workflowSchema.ts +264 -0
- package/client/src/services/dynamicParameterService.ts +96 -0
- package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
- package/client/src/services/executionService.ts +232 -0
- package/client/src/services/workflowApi.ts +91 -0
- package/client/src/store/useAppStore.ts +582 -0
- package/client/src/styles/theme.ts +508 -0
- package/client/src/styles/zIndex.ts +17 -0
- package/client/src/types/ComponentTypes.ts +39 -0
- package/client/src/types/EdgeCondition.ts +231 -0
- package/client/src/types/INodeProperties.ts +288 -0
- package/client/src/types/NodeTypes.ts +28 -0
- package/client/src/utils/formatters.ts +33 -0
- package/client/src/utils/googleMapsLoader.ts +140 -0
- package/client/src/utils/locationUtils.ts +85 -0
- package/client/src/utils/nodeUtils.ts +31 -0
- package/client/src/utils/workflow.ts +30 -0
- package/client/src/utils/workflowExport.ts +120 -0
- package/client/src/vite-env.d.ts +12 -0
- package/client/tailwind.config.js +60 -0
- package/client/tsconfig.json +25 -0
- package/client/tsconfig.node.json +11 -0
- package/client/vite.config.js +35 -0
- package/docker-compose.prod.yml +107 -0
- package/docker-compose.yml +104 -0
- package/docs-MachinaOs/README.md +85 -0
- package/docs-MachinaOs/deployment/docker.mdx +228 -0
- package/docs-MachinaOs/deployment/production.mdx +345 -0
- package/docs-MachinaOs/docs.json +75 -0
- package/docs-MachinaOs/faq.mdx +309 -0
- package/docs-MachinaOs/favicon.svg +5 -0
- package/docs-MachinaOs/installation.mdx +160 -0
- package/docs-MachinaOs/introduction.mdx +114 -0
- package/docs-MachinaOs/logo/dark.svg +6 -0
- package/docs-MachinaOs/logo/light.svg +6 -0
- package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
- package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
- package/docs-MachinaOs/nodes/android.mdx +411 -0
- package/docs-MachinaOs/nodes/overview.mdx +181 -0
- package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
- package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
- package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
- package/docs-MachinaOs/quickstart.mdx +119 -0
- package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
- package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
- package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
- package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
- package/nul +0 -0
- package/package.json +70 -0
- package/scripts/build.js +158 -0
- package/scripts/check-ports.ps1 +33 -0
- package/scripts/clean.js +40 -0
- package/scripts/docker.js +93 -0
- package/scripts/kill-port.ps1 +154 -0
- package/scripts/start.js +210 -0
- package/scripts/stop.js +325 -0
- package/server/.dockerignore +44 -0
- package/server/Dockerfile +45 -0
- package/server/constants.py +249 -0
- package/server/core/__init__.py +1 -0
- package/server/core/cache.py +461 -0
- package/server/core/config.py +128 -0
- package/server/core/container.py +99 -0
- package/server/core/database.py +1211 -0
- package/server/core/logging.py +314 -0
- package/server/main.py +289 -0
- package/server/middleware/__init__.py +5 -0
- package/server/middleware/auth.py +89 -0
- package/server/models/__init__.py +1 -0
- package/server/models/auth.py +52 -0
- package/server/models/cache.py +24 -0
- package/server/models/database.py +211 -0
- package/server/models/nodes.py +455 -0
- package/server/package.json +9 -0
- package/server/pyproject.toml +72 -0
- package/server/requirements.txt +83 -0
- package/server/routers/__init__.py +1 -0
- package/server/routers/android.py +294 -0
- package/server/routers/auth.py +203 -0
- package/server/routers/database.py +151 -0
- package/server/routers/maps.py +142 -0
- package/server/routers/nodejs_compat.py +289 -0
- package/server/routers/webhook.py +90 -0
- package/server/routers/websocket.py +2127 -0
- package/server/routers/whatsapp.py +761 -0
- package/server/routers/workflow.py +200 -0
- package/server/services/__init__.py +1 -0
- package/server/services/ai.py +2415 -0
- package/server/services/android/__init__.py +27 -0
- package/server/services/android/broadcaster.py +114 -0
- package/server/services/android/client.py +608 -0
- package/server/services/android/manager.py +78 -0
- package/server/services/android/protocol.py +165 -0
- package/server/services/android_service.py +588 -0
- package/server/services/auth.py +131 -0
- package/server/services/chat_client.py +160 -0
- package/server/services/deployment/__init__.py +12 -0
- package/server/services/deployment/manager.py +706 -0
- package/server/services/deployment/state.py +47 -0
- package/server/services/deployment/triggers.py +275 -0
- package/server/services/event_waiter.py +785 -0
- package/server/services/execution/__init__.py +77 -0
- package/server/services/execution/cache.py +769 -0
- package/server/services/execution/conditions.py +373 -0
- package/server/services/execution/dlq.py +132 -0
- package/server/services/execution/executor.py +1351 -0
- package/server/services/execution/models.py +531 -0
- package/server/services/execution/recovery.py +235 -0
- package/server/services/handlers/__init__.py +126 -0
- package/server/services/handlers/ai.py +355 -0
- package/server/services/handlers/android.py +260 -0
- package/server/services/handlers/code.py +278 -0
- package/server/services/handlers/document.py +598 -0
- package/server/services/handlers/http.py +193 -0
- package/server/services/handlers/polyglot.py +105 -0
- package/server/services/handlers/tools.py +845 -0
- package/server/services/handlers/triggers.py +107 -0
- package/server/services/handlers/utility.py +822 -0
- package/server/services/handlers/whatsapp.py +476 -0
- package/server/services/maps.py +289 -0
- package/server/services/memory_store.py +103 -0
- package/server/services/node_executor.py +375 -0
- package/server/services/parameter_resolver.py +218 -0
- package/server/services/polyglot_client.py +169 -0
- package/server/services/scheduler.py +155 -0
- package/server/services/skill_loader.py +417 -0
- package/server/services/status_broadcaster.py +826 -0
- package/server/services/temporal/__init__.py +23 -0
- package/server/services/temporal/activities.py +344 -0
- package/server/services/temporal/client.py +76 -0
- package/server/services/temporal/executor.py +147 -0
- package/server/services/temporal/worker.py +251 -0
- package/server/services/temporal/workflow.py +355 -0
- package/server/services/temporal/ws_client.py +236 -0
- package/server/services/text.py +111 -0
- package/server/services/user_auth.py +172 -0
- package/server/services/websocket_client.py +29 -0
- package/server/services/workflow.py +597 -0
- package/server/skills/android-skill/SKILL.md +82 -0
- package/server/skills/assistant-personality/SKILL.md +45 -0
- package/server/skills/code-skill/SKILL.md +140 -0
- package/server/skills/http-skill/SKILL.md +161 -0
- package/server/skills/maps-skill/SKILL.md +170 -0
- package/server/skills/memory-skill/SKILL.md +154 -0
- package/server/skills/scheduler-skill/SKILL.md +84 -0
- package/server/skills/whatsapp-skill/SKILL.md +283 -0
- package/server/uv.lock +2916 -0
- package/server/whatsapp-rpc/.dockerignore +30 -0
- package/server/whatsapp-rpc/Dockerfile +44 -0
- package/server/whatsapp-rpc/Dockerfile.web +17 -0
- package/server/whatsapp-rpc/README.md +139 -0
- package/server/whatsapp-rpc/cli.js +95 -0
- package/server/whatsapp-rpc/configs/config.yaml +7 -0
- package/server/whatsapp-rpc/docker-compose.yml +35 -0
- package/server/whatsapp-rpc/docs/API.md +410 -0
- package/server/whatsapp-rpc/go.mod +67 -0
- package/server/whatsapp-rpc/go.sum +203 -0
- package/server/whatsapp-rpc/package.json +30 -0
- package/server/whatsapp-rpc/schema.json +1294 -0
- package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
- package/server/whatsapp-rpc/scripts/cli.js +162 -0
- package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
- package/server/whatsapp-rpc/src/go/config/config.go +49 -0
- package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
- package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
- package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
- package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
- package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
- package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
- package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
- package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
- package/server/whatsapp-rpc/web/app.py +609 -0
- package/server/whatsapp-rpc/web/requirements.txt +6 -0
- package/server/whatsapp-rpc/web/rpc_client.py +427 -0
- package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
- package/server/whatsapp-rpc/web/templates/base.html +150 -0
- package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
- package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
- package/server/whatsapp-rpc/web/templates/groups.html +328 -0
- package/server/whatsapp-rpc/web/templates/messages.html +465 -0
- package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
- package/server/whatsapp-rpc/web/templates/send.html +259 -0
- package/server/whatsapp-rpc/web/templates/settings.html +459 -0
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { Handle, Position, NodeProps } from 'reactflow';
|
|
3
|
+
import { NodeData } from '../types/NodeTypes';
|
|
4
|
+
import { useAppStore } from '../store/useAppStore';
|
|
5
|
+
import { nodeDefinitions } from '../nodeDefinitions';
|
|
6
|
+
import { useAppTheme } from '../hooks/useAppTheme';
|
|
7
|
+
import { ANDROID_SERVICE_NODE_TYPES } from '../nodeDefinitions/androidServiceNodes';
|
|
8
|
+
import { ANDROID_DEVICE_NODE_TYPES } from '../nodeDefinitions/androidDeviceNodes';
|
|
9
|
+
import { useWebSocket, useWhatsAppStatus } from '../contexts/WebSocketContext';
|
|
10
|
+
import { useApiKeys } from '../hooks/useApiKeys';
|
|
11
|
+
import { getAIProviderIcon } from './icons/AIProviderIcons';
|
|
12
|
+
import { PlayCircleFilled, ScheduleOutlined } from '@ant-design/icons';
|
|
13
|
+
|
|
14
|
+
// All Android node types combined
|
|
15
|
+
const ALL_ANDROID_NODE_TYPES = [...ANDROID_SERVICE_NODE_TYPES, ...ANDROID_DEVICE_NODE_TYPES];
|
|
16
|
+
|
|
17
|
+
// Android service nodes that can connect to Android Toolkit as tools
|
|
18
|
+
const ANDROID_TOOL_CAPABLE_NODES = ANDROID_SERVICE_NODE_TYPES;
|
|
19
|
+
|
|
20
|
+
// Nodes with 'tool' in their group can connect to AI Agent/Chat Agent tool handles
|
|
21
|
+
const hasToolGroup = (definition: any): boolean => {
|
|
22
|
+
const groups = definition?.group || [];
|
|
23
|
+
return groups.includes('tool');
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Google Maps node types
|
|
27
|
+
const GOOGLE_MAPS_NODE_TYPES = ['createMap', 'addLocations', 'showNearbyPlaces'];
|
|
28
|
+
|
|
29
|
+
// WhatsApp node types
|
|
30
|
+
const WHATSAPP_NODE_TYPES = ['whatsappConnect', 'whatsappSend', 'whatsappReceive', 'whatsappDb'];
|
|
31
|
+
|
|
32
|
+
// Nodes that should not have output handles (input-only nodes)
|
|
33
|
+
const NO_OUTPUT_NODE_TYPES = ['console'];
|
|
34
|
+
|
|
35
|
+
// AI Model node types with their provider IDs
|
|
36
|
+
const AI_MODEL_NODE_TYPES: Record<string, string> = {
|
|
37
|
+
'openaiChatModel': 'openai',
|
|
38
|
+
'anthropicChatModel': 'anthropic',
|
|
39
|
+
'geminiChatModel': 'gemini',
|
|
40
|
+
'openrouterChatModel': 'openrouter',
|
|
41
|
+
'groqChatModel': 'groq',
|
|
42
|
+
'cerebrasChatModel': 'cerebras',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
|
|
46
|
+
const theme = useAppTheme();
|
|
47
|
+
const { setSelectedNode, renamingNodeId, setRenamingNodeId, updateNodeData } = useAppStore();
|
|
48
|
+
const [hasApiKey, setHasApiKey] = useState(false);
|
|
49
|
+
const [isConfigured, setIsConfigured] = useState(false);
|
|
50
|
+
const isDisabled = data?.disabled === true;
|
|
51
|
+
|
|
52
|
+
// Inline rename state
|
|
53
|
+
const [isRenaming, setIsRenaming] = useState(false);
|
|
54
|
+
const [editLabel, setEditLabel] = useState('');
|
|
55
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
56
|
+
|
|
57
|
+
// Get Android status, node status, and API key status from WebSocket context
|
|
58
|
+
const { androidStatus, getNodeStatus, getApiKeyStatus } = useWebSocket();
|
|
59
|
+
const { getStoredApiKey, validateGoogleMapsKey } = useApiKeys();
|
|
60
|
+
const nodeStatus = getNodeStatus(id);
|
|
61
|
+
const executionStatus = nodeStatus?.status || 'idle';
|
|
62
|
+
|
|
63
|
+
// Check if this is a Google Maps node
|
|
64
|
+
const isGoogleMapsNode = type ? GOOGLE_MAPS_NODE_TYPES.includes(type) : false;
|
|
65
|
+
const googleMapsKeyStatus = isGoogleMapsNode ? getApiKeyStatus('google_maps') : undefined;
|
|
66
|
+
|
|
67
|
+
// Check if this is an AI model node and get reactive API key status
|
|
68
|
+
const isAIModelNode = type ? type in AI_MODEL_NODE_TYPES : false;
|
|
69
|
+
const aiProviderId = type && AI_MODEL_NODE_TYPES[type] ? AI_MODEL_NODE_TYPES[type] : null;
|
|
70
|
+
const aiKeyStatus = aiProviderId ? getApiKeyStatus(aiProviderId) : undefined;
|
|
71
|
+
|
|
72
|
+
const definition = nodeDefinitions[type as keyof typeof nodeDefinitions];
|
|
73
|
+
|
|
74
|
+
// Check if this is an Android node
|
|
75
|
+
const isAndroidNode = type ? ALL_ANDROID_NODE_TYPES.includes(type) : false;
|
|
76
|
+
|
|
77
|
+
// Check if this node can be used as a tool (connects to Android Toolkit or AI Agent/Chat Agent tool handle)
|
|
78
|
+
const isToolCapable = type ? (ANDROID_TOOL_CAPABLE_NODES.includes(type) || hasToolGroup(definition)) : false;
|
|
79
|
+
|
|
80
|
+
// Android connection status from WebSocket (real-time updates)
|
|
81
|
+
// Service nodes need a paired device to execute, not just relay connection
|
|
82
|
+
const isAndroidConnected = isAndroidNode && androidStatus.paired;
|
|
83
|
+
|
|
84
|
+
// Check if this is a WhatsApp node
|
|
85
|
+
const isWhatsAppNode = type ? WHATSAPP_NODE_TYPES.includes(type) : false;
|
|
86
|
+
|
|
87
|
+
// WhatsApp connection status from WebSocket (real-time updates)
|
|
88
|
+
const whatsappStatus = useWhatsAppStatus();
|
|
89
|
+
|
|
90
|
+
// Execution state - waiting is treated identically to executing
|
|
91
|
+
const isExecuting = executionStatus === 'executing' || executionStatus === 'waiting';
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// Sync with global renaming state
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (renamingNodeId === id) {
|
|
97
|
+
setIsRenaming(true);
|
|
98
|
+
setEditLabel(data?.label || definition?.displayName || type || '');
|
|
99
|
+
} else {
|
|
100
|
+
setIsRenaming(false);
|
|
101
|
+
}
|
|
102
|
+
}, [renamingNodeId, id, data?.label, definition?.displayName, type]);
|
|
103
|
+
|
|
104
|
+
// Focus and select input when entering rename mode
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (isRenaming && inputRef.current) {
|
|
107
|
+
inputRef.current.focus();
|
|
108
|
+
inputRef.current.select();
|
|
109
|
+
}
|
|
110
|
+
}, [isRenaming]);
|
|
111
|
+
|
|
112
|
+
// Handle save rename
|
|
113
|
+
const handleSaveRename = useCallback(() => {
|
|
114
|
+
const newLabel = editLabel.trim();
|
|
115
|
+
const originalLabel = data?.label || definition?.displayName || type || '';
|
|
116
|
+
|
|
117
|
+
// Only save if label changed and is not empty
|
|
118
|
+
if (newLabel && newLabel !== originalLabel) {
|
|
119
|
+
updateNodeData(id, { ...data, label: newLabel });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setIsRenaming(false);
|
|
123
|
+
setRenamingNodeId(null);
|
|
124
|
+
}, [editLabel, data, definition?.displayName, type, id, updateNodeData, setRenamingNodeId]);
|
|
125
|
+
|
|
126
|
+
// Handle cancel rename
|
|
127
|
+
const handleCancelRename = useCallback(() => {
|
|
128
|
+
setIsRenaming(false);
|
|
129
|
+
setRenamingNodeId(null);
|
|
130
|
+
}, [setRenamingNodeId]);
|
|
131
|
+
|
|
132
|
+
// Handle double-click to rename
|
|
133
|
+
const handleLabelDoubleClick = useCallback(() => {
|
|
134
|
+
setRenamingNodeId(id);
|
|
135
|
+
}, [id, setRenamingNodeId]);
|
|
136
|
+
|
|
137
|
+
// Check API key and configuration status
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const checkConfiguration = async () => {
|
|
140
|
+
try {
|
|
141
|
+
// Determine provider from node definition credentials
|
|
142
|
+
let provider = '';
|
|
143
|
+
const credentials = definition?.credentials?.[0];
|
|
144
|
+
if (credentials?.name) {
|
|
145
|
+
// Map credential names to provider keys
|
|
146
|
+
const credentialToProvider: Record<string, string> = {
|
|
147
|
+
'googleMapsApi': 'google_maps',
|
|
148
|
+
'openaiApi': 'openai',
|
|
149
|
+
'anthropicApi': 'anthropic',
|
|
150
|
+
'googleAiApi': 'gemini'
|
|
151
|
+
};
|
|
152
|
+
provider = credentialToProvider[credentials.name] || '';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Fallback: extract provider from node type if not found in credentials
|
|
156
|
+
if (!provider) {
|
|
157
|
+
if (type?.includes('map') || type?.includes('location')) provider = 'google_maps';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if API key exists via WebSocket
|
|
161
|
+
const apiKey = provider ? await getStoredApiKey(provider) : null;
|
|
162
|
+
setHasApiKey(!!apiKey);
|
|
163
|
+
|
|
164
|
+
// Check if service is configured (has required parameters)
|
|
165
|
+
const hasRequiredParams = data && Object.keys(data).length > 0;
|
|
166
|
+
setIsConfigured(hasRequiredParams && !!apiKey);
|
|
167
|
+
|
|
168
|
+
// For Google Maps nodes, trigger validation via WebSocket
|
|
169
|
+
if (isGoogleMapsNode && apiKey) {
|
|
170
|
+
await validateGoogleMapsKey(apiKey);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!apiKey && provider) {
|
|
174
|
+
console.warn(`[SquareNode] ${definition?.displayName} ${id}: No API key configured for ${provider}`);
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Configuration check error:', error);
|
|
178
|
+
setHasApiKey(false);
|
|
179
|
+
setIsConfigured(false);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
checkConfiguration();
|
|
184
|
+
}, [data, id, type, definition?.displayName, definition?.credentials, isGoogleMapsNode, getStoredApiKey, validateGoogleMapsKey]);
|
|
185
|
+
|
|
186
|
+
// Get settings panel controls from store
|
|
187
|
+
const { setWhatsAppSettingsOpen, setAndroidSettingsOpen } = useAppStore();
|
|
188
|
+
|
|
189
|
+
const handleParametersClick = (e: React.MouseEvent) => {
|
|
190
|
+
e.stopPropagation();
|
|
191
|
+
// For whatsappConnect, open the WhatsApp settings panel instead
|
|
192
|
+
if (type === 'whatsappConnect') {
|
|
193
|
+
setWhatsAppSettingsOpen(true);
|
|
194
|
+
} else if (type === 'androidDeviceSetup') {
|
|
195
|
+
// For androidDeviceSetup, open the Android settings panel
|
|
196
|
+
setAndroidSettingsOpen(true);
|
|
197
|
+
} else {
|
|
198
|
+
setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Get status indicator color based on execution state
|
|
203
|
+
const getStatusIndicatorColor = () => {
|
|
204
|
+
// For executing or waiting state, show blue/cyan
|
|
205
|
+
if (executionStatus === 'executing' || executionStatus === 'waiting') {
|
|
206
|
+
return theme.dracula.cyan;
|
|
207
|
+
}
|
|
208
|
+
if (executionStatus === 'success') {
|
|
209
|
+
return theme.dracula.green;
|
|
210
|
+
}
|
|
211
|
+
if (executionStatus === 'error') {
|
|
212
|
+
return theme.dracula.red;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Idle state - use Android or configuration status
|
|
216
|
+
if (isAndroidNode) {
|
|
217
|
+
return isAndroidConnected ? theme.dracula.green : theme.dracula.red;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// WhatsApp nodes - use WebSocket connection status
|
|
221
|
+
if (isWhatsAppNode) {
|
|
222
|
+
if (whatsappStatus.connected) return theme.dracula.green;
|
|
223
|
+
if (whatsappStatus.pairing) return theme.dracula.orange;
|
|
224
|
+
return theme.dracula.red;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Google Maps nodes - use WebSocket API key validation status
|
|
228
|
+
if (isGoogleMapsNode && googleMapsKeyStatus) {
|
|
229
|
+
return googleMapsKeyStatus.valid ? theme.dracula.green : theme.dracula.red;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// AI Model nodes - use reactive WebSocket API key status
|
|
233
|
+
if (isAIModelNode) {
|
|
234
|
+
if (aiKeyStatus?.valid && aiKeyStatus?.hasKey) return theme.dracula.green;
|
|
235
|
+
if (aiKeyStatus?.hasKey) return theme.dracula.orange;
|
|
236
|
+
return theme.dracula.red;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return isConfigured ? theme.dracula.green : hasApiKey ? theme.dracula.orange : theme.dracula.red;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const getStatusTitle = () => {
|
|
243
|
+
switch (executionStatus) {
|
|
244
|
+
case 'executing':
|
|
245
|
+
return 'Executing...';
|
|
246
|
+
case 'waiting':
|
|
247
|
+
return nodeStatus?.message || 'Waiting for event...';
|
|
248
|
+
case 'success':
|
|
249
|
+
return 'Execution successful';
|
|
250
|
+
case 'error':
|
|
251
|
+
return `Error: ${nodeStatus?.data?.error || 'Unknown error'}`;
|
|
252
|
+
default:
|
|
253
|
+
if (isAndroidNode) {
|
|
254
|
+
return isAndroidConnected ? 'Android device connected' : 'Android device not connected';
|
|
255
|
+
}
|
|
256
|
+
// WhatsApp nodes - use WebSocket connection status
|
|
257
|
+
if (isWhatsAppNode) {
|
|
258
|
+
if (whatsappStatus.connected) return 'WhatsApp connected';
|
|
259
|
+
if (whatsappStatus.pairing) return 'Pairing in progress...';
|
|
260
|
+
if (whatsappStatus.running) return 'WhatsApp service running';
|
|
261
|
+
return 'WhatsApp not connected';
|
|
262
|
+
}
|
|
263
|
+
// Google Maps nodes - use WebSocket API key validation status
|
|
264
|
+
if (isGoogleMapsNode && googleMapsKeyStatus) {
|
|
265
|
+
return googleMapsKeyStatus.valid
|
|
266
|
+
? 'Google Maps API key validated'
|
|
267
|
+
: `API key invalid: ${googleMapsKeyStatus.message || 'Validation failed'}`;
|
|
268
|
+
}
|
|
269
|
+
// AI Model nodes - use reactive WebSocket API key status
|
|
270
|
+
if (isAIModelNode) {
|
|
271
|
+
if (aiKeyStatus?.valid && aiKeyStatus?.hasKey) {
|
|
272
|
+
return `${aiProviderId?.charAt(0).toUpperCase()}${aiProviderId?.slice(1)} API key validated`;
|
|
273
|
+
}
|
|
274
|
+
if (aiKeyStatus?.hasKey) {
|
|
275
|
+
return 'API key found, validation pending';
|
|
276
|
+
}
|
|
277
|
+
return 'API key required - configure in Credentials';
|
|
278
|
+
}
|
|
279
|
+
return isConfigured
|
|
280
|
+
? 'Service configured and ready'
|
|
281
|
+
: hasApiKey
|
|
282
|
+
? 'API key found, service needs configuration'
|
|
283
|
+
: 'API key required';
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Common icon name to emoji mapping for fallback
|
|
288
|
+
const iconNameToEmoji: Record<string, string> = {
|
|
289
|
+
brain: '🧠',
|
|
290
|
+
memory: '🧠',
|
|
291
|
+
robot: '🤖',
|
|
292
|
+
ai: '🤖',
|
|
293
|
+
agent: '🤖',
|
|
294
|
+
chat: '💬',
|
|
295
|
+
message: '💬',
|
|
296
|
+
whatsapp: '📱',
|
|
297
|
+
phone: '📱',
|
|
298
|
+
email: '📧',
|
|
299
|
+
mail: '📧',
|
|
300
|
+
webhook: '🔗',
|
|
301
|
+
http: '🌐',
|
|
302
|
+
api: '🔌',
|
|
303
|
+
database: '🗄️',
|
|
304
|
+
file: '📄',
|
|
305
|
+
folder: '📁',
|
|
306
|
+
code: '💻',
|
|
307
|
+
python: '🐍',
|
|
308
|
+
javascript: '📜',
|
|
309
|
+
settings: '⚙️',
|
|
310
|
+
config: '⚙️',
|
|
311
|
+
clock: '⏰',
|
|
312
|
+
schedule: '📅',
|
|
313
|
+
location: '📍',
|
|
314
|
+
map: '🗺️',
|
|
315
|
+
search: '🔍',
|
|
316
|
+
filter: '🔍',
|
|
317
|
+
play: '▶️',
|
|
318
|
+
start: '▶️',
|
|
319
|
+
stop: '⏹️',
|
|
320
|
+
pause: '⏸️',
|
|
321
|
+
send: '📤',
|
|
322
|
+
receive: '📥',
|
|
323
|
+
upload: '⬆️',
|
|
324
|
+
download: '⬇️',
|
|
325
|
+
sync: '🔄',
|
|
326
|
+
refresh: '🔄',
|
|
327
|
+
warning: '⚠️',
|
|
328
|
+
error: '❌',
|
|
329
|
+
success: '✅',
|
|
330
|
+
info: '💡',
|
|
331
|
+
help: '❓',
|
|
332
|
+
user: '👤',
|
|
333
|
+
users: '👥',
|
|
334
|
+
key: '🔑',
|
|
335
|
+
lock: '🔒',
|
|
336
|
+
unlock: '🔓',
|
|
337
|
+
star: '⭐',
|
|
338
|
+
heart: '❤️',
|
|
339
|
+
thunder: '⚡',
|
|
340
|
+
lightning: '⚡',
|
|
341
|
+
fire: '🔥',
|
|
342
|
+
water: '💧',
|
|
343
|
+
cloud: '☁️',
|
|
344
|
+
sun: '☀️',
|
|
345
|
+
moon: '🌙',
|
|
346
|
+
camera: '📷',
|
|
347
|
+
image: '🖼️',
|
|
348
|
+
video: '🎬',
|
|
349
|
+
audio: '🔊',
|
|
350
|
+
music: '🎵',
|
|
351
|
+
bell: '🔔',
|
|
352
|
+
notification: '🔔',
|
|
353
|
+
link: '🔗',
|
|
354
|
+
chain: '🔗',
|
|
355
|
+
tool: '🔧',
|
|
356
|
+
wrench: '🔧',
|
|
357
|
+
hammer: '🔨',
|
|
358
|
+
gear: '⚙️',
|
|
359
|
+
cog: '⚙️',
|
|
360
|
+
bug: '🐛',
|
|
361
|
+
debug: '🐛',
|
|
362
|
+
test: '🧪',
|
|
363
|
+
experiment: '🧪',
|
|
364
|
+
lab: '🧪',
|
|
365
|
+
book: '📖',
|
|
366
|
+
docs: '📚',
|
|
367
|
+
note: '📝',
|
|
368
|
+
edit: '✏️',
|
|
369
|
+
pencil: '✏️',
|
|
370
|
+
trash: '🗑️',
|
|
371
|
+
delete: '🗑️',
|
|
372
|
+
copy: '📋',
|
|
373
|
+
paste: '📋',
|
|
374
|
+
cut: '✂️',
|
|
375
|
+
scissors: '✂️',
|
|
376
|
+
tag: '🏷️',
|
|
377
|
+
label: '🏷️',
|
|
378
|
+
flag: '🚩',
|
|
379
|
+
bookmark: '🔖',
|
|
380
|
+
pin: '📌',
|
|
381
|
+
target: '🎯',
|
|
382
|
+
goal: '🎯',
|
|
383
|
+
trophy: '🏆',
|
|
384
|
+
medal: '🏅',
|
|
385
|
+
gift: '🎁',
|
|
386
|
+
package: '📦',
|
|
387
|
+
box: '📦',
|
|
388
|
+
truck: '🚚',
|
|
389
|
+
shipping: '🚚',
|
|
390
|
+
cart: '🛒',
|
|
391
|
+
shop: '🛍️',
|
|
392
|
+
store: '🏪',
|
|
393
|
+
money: '💰',
|
|
394
|
+
dollar: '💵',
|
|
395
|
+
credit: '💳',
|
|
396
|
+
payment: '💳',
|
|
397
|
+
chart: '📊',
|
|
398
|
+
graph: '📈',
|
|
399
|
+
analytics: '📊',
|
|
400
|
+
stats: '📊',
|
|
401
|
+
dashboard: '📊',
|
|
402
|
+
terminal: '💻',
|
|
403
|
+
console: '💻',
|
|
404
|
+
server: '🖥️',
|
|
405
|
+
computer: '💻',
|
|
406
|
+
mobile: '📱',
|
|
407
|
+
tablet: '📱',
|
|
408
|
+
battery: '🔋',
|
|
409
|
+
wifi: '📶',
|
|
410
|
+
bluetooth: '📶',
|
|
411
|
+
antenna: '📡',
|
|
412
|
+
satellite: '🛰️',
|
|
413
|
+
rocket: '🚀',
|
|
414
|
+
plane: '✈️',
|
|
415
|
+
car: '🚗',
|
|
416
|
+
bike: '🚲',
|
|
417
|
+
train: '🚂',
|
|
418
|
+
ship: '🚢',
|
|
419
|
+
home: '🏠',
|
|
420
|
+
house: '🏠',
|
|
421
|
+
building: '🏢',
|
|
422
|
+
office: '🏢',
|
|
423
|
+
factory: '🏭',
|
|
424
|
+
hospital: '🏥',
|
|
425
|
+
school: '🏫',
|
|
426
|
+
university: '🎓',
|
|
427
|
+
graduation: '🎓',
|
|
428
|
+
world: '🌍',
|
|
429
|
+
globe: '🌐',
|
|
430
|
+
earth: '🌍',
|
|
431
|
+
android: '🤖',
|
|
432
|
+
apple: '🍎',
|
|
433
|
+
windows: '🪟',
|
|
434
|
+
linux: '🐧',
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// Check if string is likely an emoji (contains emoji characters)
|
|
438
|
+
const isEmoji = (str: string): boolean => {
|
|
439
|
+
// Emoji regex pattern - matches most common emojis
|
|
440
|
+
const emojiRegex = /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2300}-\u{23FF}]|[\u{2B50}]|[\u{231A}-\u{231B}]|[\u{25AA}-\u{25AB}]|[\u{25B6}]|[\u{25C0}]|[\u{25FB}-\u{25FE}]|[\u{2614}-\u{2615}]|[\u{2648}-\u{2653}]|[\u{267F}]|[\u{2693}]|[\u{26A1}]|[\u{26AA}-\u{26AB}]|[\u{26BD}-\u{26BE}]|[\u{26C4}-\u{26C5}]|[\u{26CE}]|[\u{26D4}]|[\u{26EA}]|[\u{26F2}-\u{26F3}]|[\u{26F5}]|[\u{26FA}]|[\u{26FD}]|[\u{2702}]|[\u{2705}]|[\u{2708}-\u{270D}]|[\u{270F}]|[\u{2712}]|[\u{2714}]|[\u{2716}]|[\u{271D}]|[\u{2721}]|[\u{2728}]|[\u{2733}-\u{2734}]|[\u{2744}]|[\u{2747}]|[\u{274C}]|[\u{274E}]|[\u{2753}-\u{2755}]|[\u{2757}]|[\u{2763}-\u{2764}]|[\u{2795}-\u{2797}]|[\u{27A1}]|[\u{27B0}]|[\u{27BF}]|[\u{E000}-\u{F8FF}]/u;
|
|
441
|
+
return emojiRegex.test(str);
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Helper to render icon (handles URLs, emojis, and icon names)
|
|
445
|
+
const renderIcon = (icon: string) => {
|
|
446
|
+
// Handle image URLs
|
|
447
|
+
if (icon.startsWith('http') || icon.startsWith('data:') || icon.startsWith('/')) {
|
|
448
|
+
return (
|
|
449
|
+
<img
|
|
450
|
+
src={icon}
|
|
451
|
+
alt="icon"
|
|
452
|
+
style={{
|
|
453
|
+
width: '28px',
|
|
454
|
+
height: '28px',
|
|
455
|
+
objectFit: 'contain',
|
|
456
|
+
borderRadius: '4px'
|
|
457
|
+
}}
|
|
458
|
+
/>
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// If it's already an emoji, return it directly
|
|
463
|
+
if (isEmoji(icon)) {
|
|
464
|
+
return icon;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Check if it's a known icon name and convert to emoji
|
|
468
|
+
const lowerIcon = icon.toLowerCase().trim();
|
|
469
|
+
if (iconNameToEmoji[lowerIcon]) {
|
|
470
|
+
return iconNameToEmoji[lowerIcon];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Fallback: return a generic icon instead of plain text
|
|
474
|
+
console.warn(`[SquareNode] Unknown icon name "${icon}" - using fallback. Add emoji directly or update iconNameToEmoji mapping.`);
|
|
475
|
+
return '📦';
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// Get service icon for display
|
|
479
|
+
const getServiceIcon = () => {
|
|
480
|
+
// Priority 0: Start node - use PlayCircleFilled icon with cyan color (neutral "begin" color)
|
|
481
|
+
if (type === 'start') {
|
|
482
|
+
return <PlayCircleFilled style={{ fontSize: 28, color: theme.dracula.cyan }} />;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Cron Scheduler node - use ScheduleOutlined with node definition color
|
|
486
|
+
if (type === 'cronScheduler') {
|
|
487
|
+
return <ScheduleOutlined style={{ fontSize: 28, color: definition?.defaults?.color || nodeColor }} />;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Priority 1: Check if this is an AI model node - use official provider icons
|
|
491
|
+
if (type && AI_MODEL_NODE_TYPES[type]) {
|
|
492
|
+
const providerId = AI_MODEL_NODE_TYPES[type];
|
|
493
|
+
const IconComponent = getAIProviderIcon(providerId);
|
|
494
|
+
if (IconComponent) {
|
|
495
|
+
return <IconComponent size={28} />;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Priority 2: Custom icon set on the node instance (via data.customIcon)
|
|
500
|
+
if (data?.customIcon && typeof data.customIcon === 'string') {
|
|
501
|
+
return renderIcon(data.customIcon);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Priority 3: Use the icon from the node definition if available
|
|
505
|
+
if (definition?.icon && typeof definition.icon === 'string') {
|
|
506
|
+
return renderIcon(definition.icon);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Fallback logic based on node type
|
|
510
|
+
if (type?.includes('createMap')) return '🗺️';
|
|
511
|
+
if (type?.includes('addLocations')) return '🌍';
|
|
512
|
+
if (type?.includes('showNearbyPlaces')) return '🔍';
|
|
513
|
+
|
|
514
|
+
return '📍';
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
// Get the node color from definition or use default
|
|
518
|
+
const nodeColor = definition?.defaults?.color || '#1A73E8';
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
<div
|
|
522
|
+
style={{
|
|
523
|
+
position: 'relative',
|
|
524
|
+
display: 'flex',
|
|
525
|
+
flexDirection: 'column',
|
|
526
|
+
alignItems: 'center',
|
|
527
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
528
|
+
fontSize: '11px',
|
|
529
|
+
cursor: 'pointer',
|
|
530
|
+
}}
|
|
531
|
+
>
|
|
532
|
+
{/* Main Square Node */}
|
|
533
|
+
<div
|
|
534
|
+
style={{
|
|
535
|
+
position: 'relative',
|
|
536
|
+
width: theme.nodeSize.square,
|
|
537
|
+
height: theme.nodeSize.square,
|
|
538
|
+
borderRadius: theme.borderRadius.lg,
|
|
539
|
+
background: theme.isDarkMode
|
|
540
|
+
? `linear-gradient(135deg, ${nodeColor}25 0%, ${theme.colors.background} 100%)`
|
|
541
|
+
: `linear-gradient(145deg, #ffffff 0%, ${nodeColor}08 100%)`,
|
|
542
|
+
border: `2px solid ${isExecuting
|
|
543
|
+
? (theme.isDarkMode ? theme.dracula.cyan : '#2563eb')
|
|
544
|
+
: selected
|
|
545
|
+
? theme.colors.focus
|
|
546
|
+
: theme.isDarkMode ? nodeColor + '80' : `${nodeColor}40`}`,
|
|
547
|
+
display: 'flex',
|
|
548
|
+
alignItems: 'center',
|
|
549
|
+
justifyContent: 'center',
|
|
550
|
+
color: theme.colors.text,
|
|
551
|
+
fontSize: theme.nodeSize.squareIcon,
|
|
552
|
+
fontWeight: '600',
|
|
553
|
+
transition: 'all 0.2s ease',
|
|
554
|
+
boxShadow: isExecuting
|
|
555
|
+
? theme.isDarkMode
|
|
556
|
+
? `0 4px 12px ${theme.dracula.cyan}66, 0 0 0 3px ${theme.dracula.cyan}4D`
|
|
557
|
+
: `0 0 0 3px rgba(37, 99, 235, 0.5), 0 4px 16px rgba(37, 99, 235, 0.35)`
|
|
558
|
+
: selected
|
|
559
|
+
? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
|
|
560
|
+
: theme.isDarkMode
|
|
561
|
+
? `0 2px 8px ${nodeColor}40`
|
|
562
|
+
: `0 2px 8px ${nodeColor}20, 0 4px 12px rgba(0,0,0,0.06)`,
|
|
563
|
+
animation: isExecuting ? 'pulse 1.5s ease-in-out infinite' : 'none',
|
|
564
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
565
|
+
}}
|
|
566
|
+
>
|
|
567
|
+
{/* Disabled Overlay */}
|
|
568
|
+
{isDisabled && (
|
|
569
|
+
<div style={{
|
|
570
|
+
position: 'absolute',
|
|
571
|
+
top: 0,
|
|
572
|
+
left: 0,
|
|
573
|
+
right: 0,
|
|
574
|
+
bottom: 0,
|
|
575
|
+
backgroundColor: 'rgba(128, 128, 128, 0.4)',
|
|
576
|
+
borderRadius: 'inherit',
|
|
577
|
+
zIndex: 35,
|
|
578
|
+
display: 'flex',
|
|
579
|
+
alignItems: 'center',
|
|
580
|
+
justifyContent: 'center',
|
|
581
|
+
pointerEvents: 'none',
|
|
582
|
+
}}>
|
|
583
|
+
<span style={{ fontSize: '20px', opacity: 0.8, color: theme.colors.textSecondary }}>||</span>
|
|
584
|
+
</div>
|
|
585
|
+
)}
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
{/* Service Icon */}
|
|
589
|
+
{getServiceIcon()}
|
|
590
|
+
|
|
591
|
+
{/* Parameters Button */}
|
|
592
|
+
<button
|
|
593
|
+
onClick={handleParametersClick}
|
|
594
|
+
style={{
|
|
595
|
+
position: 'absolute',
|
|
596
|
+
top: '-8px',
|
|
597
|
+
right: '-8px',
|
|
598
|
+
width: theme.nodeSize.paramButton,
|
|
599
|
+
height: theme.nodeSize.paramButton,
|
|
600
|
+
borderRadius: theme.borderRadius.sm,
|
|
601
|
+
backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
|
|
602
|
+
border: `1px solid ${theme.isDarkMode ? theme.colors.border : '#d1d5db'}`,
|
|
603
|
+
cursor: 'pointer',
|
|
604
|
+
display: 'flex',
|
|
605
|
+
alignItems: 'center',
|
|
606
|
+
justifyContent: 'center',
|
|
607
|
+
fontSize: theme.fontSize.xs,
|
|
608
|
+
color: theme.colors.textSecondary,
|
|
609
|
+
fontWeight: '400',
|
|
610
|
+
transition: theme.transitions.fast,
|
|
611
|
+
zIndex: 30,
|
|
612
|
+
boxShadow: theme.isDarkMode
|
|
613
|
+
? `0 1px 3px ${theme.colors.shadow}`
|
|
614
|
+
: '0 1px 4px rgba(0,0,0,0.1)'
|
|
615
|
+
}}
|
|
616
|
+
title="Edit Service Parameters"
|
|
617
|
+
>
|
|
618
|
+
⚙️
|
|
619
|
+
</button>
|
|
620
|
+
|
|
621
|
+
{/* Configuration/Execution Status Indicator */}
|
|
622
|
+
<div
|
|
623
|
+
style={{
|
|
624
|
+
position: 'absolute',
|
|
625
|
+
top: '-4px',
|
|
626
|
+
left: '-4px',
|
|
627
|
+
width: theme.nodeSize.statusIndicator,
|
|
628
|
+
height: theme.nodeSize.statusIndicator,
|
|
629
|
+
borderRadius: '50%',
|
|
630
|
+
backgroundColor: getStatusIndicatorColor(),
|
|
631
|
+
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
632
|
+
boxShadow: isExecuting
|
|
633
|
+
? theme.isDarkMode
|
|
634
|
+
? `0 0 6px ${theme.dracula.cyan}80`
|
|
635
|
+
: '0 0 4px rgba(37, 99, 235, 0.5)'
|
|
636
|
+
: theme.isDarkMode
|
|
637
|
+
? `0 1px 2px ${theme.colors.shadow}`
|
|
638
|
+
: '0 1px 3px rgba(0,0,0,0.15)',
|
|
639
|
+
zIndex: 30,
|
|
640
|
+
animation: isExecuting ? 'pulse 1s ease-in-out infinite' : 'none',
|
|
641
|
+
}}
|
|
642
|
+
title={getStatusTitle()}
|
|
643
|
+
/>
|
|
644
|
+
|
|
645
|
+
{/* Square Input Handle */}
|
|
646
|
+
<Handle
|
|
647
|
+
id="input-main"
|
|
648
|
+
type="target"
|
|
649
|
+
position={Position.Left}
|
|
650
|
+
isConnectable={isConnectable}
|
|
651
|
+
style={{
|
|
652
|
+
position: 'absolute',
|
|
653
|
+
left: '-6px',
|
|
654
|
+
top: '50%',
|
|
655
|
+
transform: 'translateY(-50%)',
|
|
656
|
+
width: theme.nodeSize.handle,
|
|
657
|
+
height: theme.nodeSize.handle,
|
|
658
|
+
backgroundColor: theme.isDarkMode ? theme.colors.background : '#ffffff',
|
|
659
|
+
border: `2px solid ${theme.isDarkMode ? theme.colors.textSecondary : '#6b7280'}`,
|
|
660
|
+
borderRadius: '50%',
|
|
661
|
+
zIndex: 20
|
|
662
|
+
}}
|
|
663
|
+
title="Service Input"
|
|
664
|
+
/>
|
|
665
|
+
|
|
666
|
+
{/* Square Output Handle - hidden for input-only nodes like console */}
|
|
667
|
+
{!NO_OUTPUT_NODE_TYPES.includes(type || '') && (
|
|
668
|
+
<Handle
|
|
669
|
+
id="output-main"
|
|
670
|
+
type="source"
|
|
671
|
+
position={Position.Right}
|
|
672
|
+
isConnectable={isConnectable}
|
|
673
|
+
style={{
|
|
674
|
+
position: 'absolute',
|
|
675
|
+
right: '-6px',
|
|
676
|
+
top: '50%',
|
|
677
|
+
transform: 'translateY(-50%)',
|
|
678
|
+
width: theme.nodeSize.handle,
|
|
679
|
+
height: theme.nodeSize.handle,
|
|
680
|
+
backgroundColor: isConfigured ? nodeColor : theme.colors.textSecondary,
|
|
681
|
+
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
682
|
+
borderRadius: '50%',
|
|
683
|
+
zIndex: 20
|
|
684
|
+
}}
|
|
685
|
+
title="Service Output"
|
|
686
|
+
/>
|
|
687
|
+
)}
|
|
688
|
+
|
|
689
|
+
{/* Top Tool Output Handle - for nodes that can connect to AI Agent/Chat Agent tool handle */}
|
|
690
|
+
{isToolCapable && (
|
|
691
|
+
<Handle
|
|
692
|
+
id="output-tool"
|
|
693
|
+
type="source"
|
|
694
|
+
position={Position.Top}
|
|
695
|
+
isConnectable={isConnectable}
|
|
696
|
+
style={{
|
|
697
|
+
position: 'absolute',
|
|
698
|
+
top: '-6px',
|
|
699
|
+
left: '50%',
|
|
700
|
+
transform: 'translateX(-50%)',
|
|
701
|
+
width: theme.nodeSize.handle,
|
|
702
|
+
height: theme.nodeSize.handle,
|
|
703
|
+
backgroundColor: ANDROID_TOOL_CAPABLE_NODES.includes(type || '') ? '#3DDC84' : nodeColor, // Android green for Android nodes, node color for others
|
|
704
|
+
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
705
|
+
borderRadius: '50%',
|
|
706
|
+
zIndex: 20
|
|
707
|
+
}}
|
|
708
|
+
title={ANDROID_TOOL_CAPABLE_NODES.includes(type || '') ? 'Connect to Android Toolkit' : 'Connect to AI Agent/Chat Agent tool handle'}
|
|
709
|
+
/>
|
|
710
|
+
)}
|
|
711
|
+
|
|
712
|
+
{/* Output Data Indicator - shows when node has execution output */}
|
|
713
|
+
{executionStatus === 'success' && nodeStatus?.data && (
|
|
714
|
+
<div
|
|
715
|
+
style={{
|
|
716
|
+
position: 'absolute',
|
|
717
|
+
bottom: '-4px',
|
|
718
|
+
right: '-4px',
|
|
719
|
+
width: theme.nodeSize.outputBadge,
|
|
720
|
+
height: theme.nodeSize.outputBadge,
|
|
721
|
+
borderRadius: theme.borderRadius.sm,
|
|
722
|
+
backgroundColor: theme.dracula.green,
|
|
723
|
+
border: `1px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
724
|
+
display: 'flex',
|
|
725
|
+
alignItems: 'center',
|
|
726
|
+
justifyContent: 'center',
|
|
727
|
+
fontSize: theme.fontSize.xs,
|
|
728
|
+
color: 'white',
|
|
729
|
+
fontWeight: 'bold',
|
|
730
|
+
zIndex: 30,
|
|
731
|
+
boxShadow: theme.isDarkMode
|
|
732
|
+
? '0 1px 3px rgba(0,0,0,0.2)'
|
|
733
|
+
: '0 1px 3px rgba(0,0,0,0.15)',
|
|
734
|
+
}}
|
|
735
|
+
title="Output data available - click node to view"
|
|
736
|
+
>
|
|
737
|
+
<span style={{ lineHeight: 1 }}>D</span>
|
|
738
|
+
</div>
|
|
739
|
+
)}
|
|
740
|
+
</div>
|
|
741
|
+
|
|
742
|
+
{/* Service Name Below Square */}
|
|
743
|
+
{isRenaming ? (
|
|
744
|
+
<input
|
|
745
|
+
ref={inputRef}
|
|
746
|
+
type="text"
|
|
747
|
+
value={editLabel}
|
|
748
|
+
onChange={(e) => setEditLabel(e.target.value)}
|
|
749
|
+
onKeyDown={(e) => {
|
|
750
|
+
if (e.key === 'Enter') {
|
|
751
|
+
handleSaveRename();
|
|
752
|
+
} else if (e.key === 'Escape') {
|
|
753
|
+
handleCancelRename();
|
|
754
|
+
}
|
|
755
|
+
e.stopPropagation();
|
|
756
|
+
}}
|
|
757
|
+
onBlur={handleSaveRename}
|
|
758
|
+
onClick={(e) => e.stopPropagation()}
|
|
759
|
+
style={{
|
|
760
|
+
marginTop: theme.spacing.sm,
|
|
761
|
+
width: '100%',
|
|
762
|
+
maxWidth: '120px',
|
|
763
|
+
padding: '2px 4px',
|
|
764
|
+
fontSize: theme.fontSize.sm,
|
|
765
|
+
fontWeight: theme.fontWeight.medium,
|
|
766
|
+
color: theme.colors.text,
|
|
767
|
+
backgroundColor: theme.colors.backgroundElevated,
|
|
768
|
+
border: `1px solid ${theme.dracula.purple}`,
|
|
769
|
+
borderRadius: theme.borderRadius.sm,
|
|
770
|
+
outline: 'none',
|
|
771
|
+
textAlign: 'center',
|
|
772
|
+
}}
|
|
773
|
+
/>
|
|
774
|
+
) : (
|
|
775
|
+
<div
|
|
776
|
+
onDoubleClick={handleLabelDoubleClick}
|
|
777
|
+
style={{
|
|
778
|
+
marginTop: theme.spacing.sm,
|
|
779
|
+
fontSize: theme.fontSize.sm,
|
|
780
|
+
fontWeight: theme.fontWeight.medium,
|
|
781
|
+
color: theme.colors.text,
|
|
782
|
+
lineHeight: '1.2',
|
|
783
|
+
textAlign: 'center',
|
|
784
|
+
maxWidth: '120px',
|
|
785
|
+
cursor: 'text',
|
|
786
|
+
}}
|
|
787
|
+
title="Double-click to rename"
|
|
788
|
+
>
|
|
789
|
+
{data?.label || definition?.displayName}
|
|
790
|
+
</div>
|
|
791
|
+
)}
|
|
792
|
+
|
|
793
|
+
</div>
|
|
794
|
+
);
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
export default SquareNode;
|