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.
Files changed (288) hide show
  1. package/.env.template +71 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/bin/cli.js +159 -0
  5. package/client/.dockerignore +45 -0
  6. package/client/Dockerfile +68 -0
  7. package/client/eslint.config.js +29 -0
  8. package/client/index.html +13 -0
  9. package/client/nginx.conf +66 -0
  10. package/client/package.json +48 -0
  11. package/client/src/App.tsx +27 -0
  12. package/client/src/Dashboard.tsx +1173 -0
  13. package/client/src/ParameterPanel.tsx +301 -0
  14. package/client/src/components/AIAgentNode.tsx +321 -0
  15. package/client/src/components/APIKeyValidator.tsx +118 -0
  16. package/client/src/components/ClaudeChatModelNode.tsx +18 -0
  17. package/client/src/components/ConditionalEdge.tsx +189 -0
  18. package/client/src/components/CredentialsModal.tsx +306 -0
  19. package/client/src/components/EdgeConditionEditor.tsx +443 -0
  20. package/client/src/components/GeminiChatModelNode.tsx +18 -0
  21. package/client/src/components/GenericNode.tsx +357 -0
  22. package/client/src/components/LocationParameterPanel.tsx +154 -0
  23. package/client/src/components/ModelNode.tsx +286 -0
  24. package/client/src/components/OpenAIChatModelNode.tsx +18 -0
  25. package/client/src/components/OutputPanel.tsx +471 -0
  26. package/client/src/components/ParameterRenderer.tsx +1874 -0
  27. package/client/src/components/SkillEditorModal.tsx +417 -0
  28. package/client/src/components/SquareNode.tsx +797 -0
  29. package/client/src/components/StartNode.tsx +250 -0
  30. package/client/src/components/ToolkitNode.tsx +365 -0
  31. package/client/src/components/TriggerNode.tsx +463 -0
  32. package/client/src/components/auth/LoginPage.tsx +247 -0
  33. package/client/src/components/auth/ProtectedRoute.tsx +59 -0
  34. package/client/src/components/base/BaseChatModelNode.tsx +271 -0
  35. package/client/src/components/icons/AIProviderIcons.tsx +50 -0
  36. package/client/src/components/maps/GoogleMapsPicker.tsx +137 -0
  37. package/client/src/components/maps/MapsPreviewPanel.tsx +110 -0
  38. package/client/src/components/maps/index.ts +26 -0
  39. package/client/src/components/parameterPanel/InputSection.tsx +1094 -0
  40. package/client/src/components/parameterPanel/LocationPanelLayout.tsx +65 -0
  41. package/client/src/components/parameterPanel/MapsSection.tsx +92 -0
  42. package/client/src/components/parameterPanel/MiddleSection.tsx +571 -0
  43. package/client/src/components/parameterPanel/OutputSection.tsx +81 -0
  44. package/client/src/components/parameterPanel/ParameterPanelLayout.tsx +82 -0
  45. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +436 -0
  46. package/client/src/components/parameterPanel/index.ts +42 -0
  47. package/client/src/components/shared/DataPanel.tsx +142 -0
  48. package/client/src/components/shared/JSONTreeRenderer.tsx +106 -0
  49. package/client/src/components/ui/AIResultModal.tsx +204 -0
  50. package/client/src/components/ui/AndroidSettingsPanel.tsx +401 -0
  51. package/client/src/components/ui/CodeEditor.tsx +81 -0
  52. package/client/src/components/ui/CollapsibleSection.tsx +88 -0
  53. package/client/src/components/ui/ComponentItem.tsx +154 -0
  54. package/client/src/components/ui/ComponentPalette.tsx +321 -0
  55. package/client/src/components/ui/ConsolePanel.tsx +1074 -0
  56. package/client/src/components/ui/ErrorBoundary.tsx +196 -0
  57. package/client/src/components/ui/InputNodesPanel.tsx +204 -0
  58. package/client/src/components/ui/MapSelector.tsx +314 -0
  59. package/client/src/components/ui/Modal.tsx +149 -0
  60. package/client/src/components/ui/NodeContextMenu.tsx +192 -0
  61. package/client/src/components/ui/NodeOutputPanel.tsx +1150 -0
  62. package/client/src/components/ui/OutputDisplayPanel.tsx +381 -0
  63. package/client/src/components/ui/SettingsPanel.tsx +243 -0
  64. package/client/src/components/ui/TopToolbar.tsx +736 -0
  65. package/client/src/components/ui/WhatsAppSettingsPanel.tsx +345 -0
  66. package/client/src/components/ui/WorkflowSidebar.tsx +294 -0
  67. package/client/src/config/antdTheme.ts +186 -0
  68. package/client/src/config/api.ts +54 -0
  69. package/client/src/contexts/AuthContext.tsx +221 -0
  70. package/client/src/contexts/ThemeContext.tsx +42 -0
  71. package/client/src/contexts/WebSocketContext.tsx +1971 -0
  72. package/client/src/factories/baseChatModelFactory.ts +256 -0
  73. package/client/src/hooks/useAndroidOperations.ts +164 -0
  74. package/client/src/hooks/useApiKeyValidation.ts +107 -0
  75. package/client/src/hooks/useApiKeys.ts +238 -0
  76. package/client/src/hooks/useAppTheme.ts +17 -0
  77. package/client/src/hooks/useComponentPalette.ts +51 -0
  78. package/client/src/hooks/useCopyPaste.ts +155 -0
  79. package/client/src/hooks/useDragAndDrop.ts +124 -0
  80. package/client/src/hooks/useDragVariable.ts +88 -0
  81. package/client/src/hooks/useExecution.ts +313 -0
  82. package/client/src/hooks/useParameterPanel.ts +176 -0
  83. package/client/src/hooks/useReactFlowNodes.ts +189 -0
  84. package/client/src/hooks/useToolSchema.ts +209 -0
  85. package/client/src/hooks/useWhatsApp.ts +196 -0
  86. package/client/src/hooks/useWorkflowManagement.ts +46 -0
  87. package/client/src/index.css +315 -0
  88. package/client/src/main.tsx +19 -0
  89. package/client/src/nodeDefinitions/aiAgentNodes.ts +336 -0
  90. package/client/src/nodeDefinitions/aiModelNodes.ts +340 -0
  91. package/client/src/nodeDefinitions/androidDeviceNodes.ts +140 -0
  92. package/client/src/nodeDefinitions/androidServiceNodes.ts +383 -0
  93. package/client/src/nodeDefinitions/chatNodes.ts +135 -0
  94. package/client/src/nodeDefinitions/codeNodes.ts +54 -0
  95. package/client/src/nodeDefinitions/documentNodes.ts +379 -0
  96. package/client/src/nodeDefinitions/index.ts +15 -0
  97. package/client/src/nodeDefinitions/locationNodes.ts +463 -0
  98. package/client/src/nodeDefinitions/schedulerNodes.ts +220 -0
  99. package/client/src/nodeDefinitions/skillNodes.ts +211 -0
  100. package/client/src/nodeDefinitions/toolNodes.ts +198 -0
  101. package/client/src/nodeDefinitions/utilityNodes.ts +284 -0
  102. package/client/src/nodeDefinitions/whatsappNodes.ts +865 -0
  103. package/client/src/nodeDefinitions/workflowNodes.ts +41 -0
  104. package/client/src/nodeDefinitions.ts +104 -0
  105. package/client/src/schemas/workflowSchema.ts +264 -0
  106. package/client/src/services/dynamicParameterService.ts +96 -0
  107. package/client/src/services/execution/aiAgentExecutionService.ts +35 -0
  108. package/client/src/services/executionService.ts +232 -0
  109. package/client/src/services/workflowApi.ts +91 -0
  110. package/client/src/store/useAppStore.ts +582 -0
  111. package/client/src/styles/theme.ts +508 -0
  112. package/client/src/styles/zIndex.ts +17 -0
  113. package/client/src/types/ComponentTypes.ts +39 -0
  114. package/client/src/types/EdgeCondition.ts +231 -0
  115. package/client/src/types/INodeProperties.ts +288 -0
  116. package/client/src/types/NodeTypes.ts +28 -0
  117. package/client/src/utils/formatters.ts +33 -0
  118. package/client/src/utils/googleMapsLoader.ts +140 -0
  119. package/client/src/utils/locationUtils.ts +85 -0
  120. package/client/src/utils/nodeUtils.ts +31 -0
  121. package/client/src/utils/workflow.ts +30 -0
  122. package/client/src/utils/workflowExport.ts +120 -0
  123. package/client/src/vite-env.d.ts +12 -0
  124. package/client/tailwind.config.js +60 -0
  125. package/client/tsconfig.json +25 -0
  126. package/client/tsconfig.node.json +11 -0
  127. package/client/vite.config.js +35 -0
  128. package/docker-compose.prod.yml +107 -0
  129. package/docker-compose.yml +104 -0
  130. package/docs-MachinaOs/README.md +85 -0
  131. package/docs-MachinaOs/deployment/docker.mdx +228 -0
  132. package/docs-MachinaOs/deployment/production.mdx +345 -0
  133. package/docs-MachinaOs/docs.json +75 -0
  134. package/docs-MachinaOs/faq.mdx +309 -0
  135. package/docs-MachinaOs/favicon.svg +5 -0
  136. package/docs-MachinaOs/installation.mdx +160 -0
  137. package/docs-MachinaOs/introduction.mdx +114 -0
  138. package/docs-MachinaOs/logo/dark.svg +6 -0
  139. package/docs-MachinaOs/logo/light.svg +6 -0
  140. package/docs-MachinaOs/nodes/ai-agent.mdx +216 -0
  141. package/docs-MachinaOs/nodes/ai-models.mdx +240 -0
  142. package/docs-MachinaOs/nodes/android.mdx +411 -0
  143. package/docs-MachinaOs/nodes/overview.mdx +181 -0
  144. package/docs-MachinaOs/nodes/schedulers.mdx +316 -0
  145. package/docs-MachinaOs/nodes/webhooks.mdx +330 -0
  146. package/docs-MachinaOs/nodes/whatsapp.mdx +305 -0
  147. package/docs-MachinaOs/quickstart.mdx +119 -0
  148. package/docs-MachinaOs/tutorials/ai-agent-workflow.mdx +177 -0
  149. package/docs-MachinaOs/tutorials/android-automation.mdx +242 -0
  150. package/docs-MachinaOs/tutorials/first-workflow.mdx +134 -0
  151. package/docs-MachinaOs/tutorials/whatsapp-automation.mdx +185 -0
  152. package/nul +0 -0
  153. package/package.json +70 -0
  154. package/scripts/build.js +158 -0
  155. package/scripts/check-ports.ps1 +33 -0
  156. package/scripts/clean.js +40 -0
  157. package/scripts/docker.js +93 -0
  158. package/scripts/kill-port.ps1 +154 -0
  159. package/scripts/start.js +210 -0
  160. package/scripts/stop.js +325 -0
  161. package/server/.dockerignore +44 -0
  162. package/server/Dockerfile +45 -0
  163. package/server/constants.py +249 -0
  164. package/server/core/__init__.py +1 -0
  165. package/server/core/cache.py +461 -0
  166. package/server/core/config.py +128 -0
  167. package/server/core/container.py +99 -0
  168. package/server/core/database.py +1211 -0
  169. package/server/core/logging.py +314 -0
  170. package/server/main.py +289 -0
  171. package/server/middleware/__init__.py +5 -0
  172. package/server/middleware/auth.py +89 -0
  173. package/server/models/__init__.py +1 -0
  174. package/server/models/auth.py +52 -0
  175. package/server/models/cache.py +24 -0
  176. package/server/models/database.py +211 -0
  177. package/server/models/nodes.py +455 -0
  178. package/server/package.json +9 -0
  179. package/server/pyproject.toml +72 -0
  180. package/server/requirements.txt +83 -0
  181. package/server/routers/__init__.py +1 -0
  182. package/server/routers/android.py +294 -0
  183. package/server/routers/auth.py +203 -0
  184. package/server/routers/database.py +151 -0
  185. package/server/routers/maps.py +142 -0
  186. package/server/routers/nodejs_compat.py +289 -0
  187. package/server/routers/webhook.py +90 -0
  188. package/server/routers/websocket.py +2127 -0
  189. package/server/routers/whatsapp.py +761 -0
  190. package/server/routers/workflow.py +200 -0
  191. package/server/services/__init__.py +1 -0
  192. package/server/services/ai.py +2415 -0
  193. package/server/services/android/__init__.py +27 -0
  194. package/server/services/android/broadcaster.py +114 -0
  195. package/server/services/android/client.py +608 -0
  196. package/server/services/android/manager.py +78 -0
  197. package/server/services/android/protocol.py +165 -0
  198. package/server/services/android_service.py +588 -0
  199. package/server/services/auth.py +131 -0
  200. package/server/services/chat_client.py +160 -0
  201. package/server/services/deployment/__init__.py +12 -0
  202. package/server/services/deployment/manager.py +706 -0
  203. package/server/services/deployment/state.py +47 -0
  204. package/server/services/deployment/triggers.py +275 -0
  205. package/server/services/event_waiter.py +785 -0
  206. package/server/services/execution/__init__.py +77 -0
  207. package/server/services/execution/cache.py +769 -0
  208. package/server/services/execution/conditions.py +373 -0
  209. package/server/services/execution/dlq.py +132 -0
  210. package/server/services/execution/executor.py +1351 -0
  211. package/server/services/execution/models.py +531 -0
  212. package/server/services/execution/recovery.py +235 -0
  213. package/server/services/handlers/__init__.py +126 -0
  214. package/server/services/handlers/ai.py +355 -0
  215. package/server/services/handlers/android.py +260 -0
  216. package/server/services/handlers/code.py +278 -0
  217. package/server/services/handlers/document.py +598 -0
  218. package/server/services/handlers/http.py +193 -0
  219. package/server/services/handlers/polyglot.py +105 -0
  220. package/server/services/handlers/tools.py +845 -0
  221. package/server/services/handlers/triggers.py +107 -0
  222. package/server/services/handlers/utility.py +822 -0
  223. package/server/services/handlers/whatsapp.py +476 -0
  224. package/server/services/maps.py +289 -0
  225. package/server/services/memory_store.py +103 -0
  226. package/server/services/node_executor.py +375 -0
  227. package/server/services/parameter_resolver.py +218 -0
  228. package/server/services/polyglot_client.py +169 -0
  229. package/server/services/scheduler.py +155 -0
  230. package/server/services/skill_loader.py +417 -0
  231. package/server/services/status_broadcaster.py +826 -0
  232. package/server/services/temporal/__init__.py +23 -0
  233. package/server/services/temporal/activities.py +344 -0
  234. package/server/services/temporal/client.py +76 -0
  235. package/server/services/temporal/executor.py +147 -0
  236. package/server/services/temporal/worker.py +251 -0
  237. package/server/services/temporal/workflow.py +355 -0
  238. package/server/services/temporal/ws_client.py +236 -0
  239. package/server/services/text.py +111 -0
  240. package/server/services/user_auth.py +172 -0
  241. package/server/services/websocket_client.py +29 -0
  242. package/server/services/workflow.py +597 -0
  243. package/server/skills/android-skill/SKILL.md +82 -0
  244. package/server/skills/assistant-personality/SKILL.md +45 -0
  245. package/server/skills/code-skill/SKILL.md +140 -0
  246. package/server/skills/http-skill/SKILL.md +161 -0
  247. package/server/skills/maps-skill/SKILL.md +170 -0
  248. package/server/skills/memory-skill/SKILL.md +154 -0
  249. package/server/skills/scheduler-skill/SKILL.md +84 -0
  250. package/server/skills/whatsapp-skill/SKILL.md +283 -0
  251. package/server/uv.lock +2916 -0
  252. package/server/whatsapp-rpc/.dockerignore +30 -0
  253. package/server/whatsapp-rpc/Dockerfile +44 -0
  254. package/server/whatsapp-rpc/Dockerfile.web +17 -0
  255. package/server/whatsapp-rpc/README.md +139 -0
  256. package/server/whatsapp-rpc/cli.js +95 -0
  257. package/server/whatsapp-rpc/configs/config.yaml +7 -0
  258. package/server/whatsapp-rpc/docker-compose.yml +35 -0
  259. package/server/whatsapp-rpc/docs/API.md +410 -0
  260. package/server/whatsapp-rpc/go.mod +67 -0
  261. package/server/whatsapp-rpc/go.sum +203 -0
  262. package/server/whatsapp-rpc/package.json +30 -0
  263. package/server/whatsapp-rpc/schema.json +1294 -0
  264. package/server/whatsapp-rpc/scripts/clean.cjs +66 -0
  265. package/server/whatsapp-rpc/scripts/cli.js +162 -0
  266. package/server/whatsapp-rpc/src/go/cmd/server/main.go +91 -0
  267. package/server/whatsapp-rpc/src/go/config/config.go +49 -0
  268. package/server/whatsapp-rpc/src/go/rpc/rpc.go +446 -0
  269. package/server/whatsapp-rpc/src/go/rpc/server.go +112 -0
  270. package/server/whatsapp-rpc/src/go/whatsapp/history.go +166 -0
  271. package/server/whatsapp-rpc/src/go/whatsapp/messages.go +390 -0
  272. package/server/whatsapp-rpc/src/go/whatsapp/service.go +2130 -0
  273. package/server/whatsapp-rpc/src/go/whatsapp/types.go +261 -0
  274. package/server/whatsapp-rpc/src/python/pyproject.toml +15 -0
  275. package/server/whatsapp-rpc/src/python/whatsapp_rpc/__init__.py +4 -0
  276. package/server/whatsapp-rpc/src/python/whatsapp_rpc/client.py +427 -0
  277. package/server/whatsapp-rpc/web/app.py +609 -0
  278. package/server/whatsapp-rpc/web/requirements.txt +6 -0
  279. package/server/whatsapp-rpc/web/rpc_client.py +427 -0
  280. package/server/whatsapp-rpc/web/static/openapi.yaml +59 -0
  281. package/server/whatsapp-rpc/web/templates/base.html +150 -0
  282. package/server/whatsapp-rpc/web/templates/contacts.html +240 -0
  283. package/server/whatsapp-rpc/web/templates/dashboard.html +320 -0
  284. package/server/whatsapp-rpc/web/templates/groups.html +328 -0
  285. package/server/whatsapp-rpc/web/templates/messages.html +465 -0
  286. package/server/whatsapp-rpc/web/templates/messaging.html +681 -0
  287. package/server/whatsapp-rpc/web/templates/send.html +259 -0
  288. package/server/whatsapp-rpc/web/templates/settings.html +459 -0
@@ -0,0 +1,463 @@
1
+ /**
2
+ * TriggerNode - Visual component for trigger nodes (no input connections)
3
+ *
4
+ * Trigger nodes start workflows and have only output handles.
5
+ * Based on SquareNode design but without input handles.
6
+ *
7
+ * Used for: cronScheduler, webhookTrigger, whatsappReceive, start
8
+ */
9
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
10
+ import { Handle, Position, NodeProps } from 'reactflow';
11
+ import { NodeData } from '../types/NodeTypes';
12
+ import { useAppStore } from '../store/useAppStore';
13
+ import { nodeDefinitions } from '../nodeDefinitions';
14
+ import { useAppTheme } from '../hooks/useAppTheme';
15
+ import { useWebSocket, useWhatsAppStatus } from '../contexts/WebSocketContext';
16
+ import { PlayCircleFilled, ScheduleOutlined } from '@ant-design/icons';
17
+
18
+ const TriggerNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
19
+ const theme = useAppTheme();
20
+ const { setSelectedNode, renamingNodeId, setRenamingNodeId, updateNodeData } = useAppStore();
21
+ const [isConfigured, setIsConfigured] = useState(false);
22
+ const isDisabled = data?.disabled === true;
23
+
24
+ // Inline rename state
25
+ const [isRenaming, setIsRenaming] = useState(false);
26
+ const [editLabel, setEditLabel] = useState('');
27
+ const inputRef = useRef<HTMLInputElement>(null);
28
+
29
+ // Get node status from WebSocket context
30
+ const { getNodeStatus } = useWebSocket();
31
+ const nodeStatus = getNodeStatus(id);
32
+ const executionStatus = nodeStatus?.status || 'idle';
33
+
34
+ // Check if this is a WhatsApp trigger
35
+ const isWhatsAppTrigger = type === 'whatsappReceive';
36
+ const whatsappStatus = useWhatsAppStatus();
37
+
38
+ // Combine waiting and executing states for glow animation (matching SquareNode pattern)
39
+ // - waiting: Trigger is listening for events (cron scheduled, webhook listening)
40
+ // - executing: Trigger is actively running
41
+ // Both states show the glow animation to indicate active state
42
+ const isExecuting = executionStatus === 'executing' || executionStatus === 'waiting';
43
+
44
+ const definition = nodeDefinitions[type as keyof typeof nodeDefinitions];
45
+
46
+ // Check configuration status
47
+ useEffect(() => {
48
+ const hasRequiredParams = data && Object.keys(data).length > 0;
49
+ setIsConfigured(hasRequiredParams);
50
+ }, [data]);
51
+
52
+ // Sync with global renaming state
53
+ useEffect(() => {
54
+ if (renamingNodeId === id) {
55
+ setIsRenaming(true);
56
+ setEditLabel(data?.label || definition?.displayName || type || '');
57
+ } else {
58
+ setIsRenaming(false);
59
+ }
60
+ }, [renamingNodeId, id, data?.label, definition?.displayName, type]);
61
+
62
+ // Focus and select input when entering rename mode
63
+ useEffect(() => {
64
+ if (isRenaming && inputRef.current) {
65
+ inputRef.current.focus();
66
+ inputRef.current.select();
67
+ }
68
+ }, [isRenaming]);
69
+
70
+ // Handle save rename
71
+ const handleSaveRename = useCallback(() => {
72
+ const newLabel = editLabel.trim();
73
+ const originalLabel = data?.label || definition?.displayName || type || '';
74
+
75
+ // Only save if label changed and is not empty
76
+ if (newLabel && newLabel !== originalLabel) {
77
+ updateNodeData(id, { ...data, label: newLabel });
78
+ }
79
+
80
+ setIsRenaming(false);
81
+ setRenamingNodeId(null);
82
+ }, [editLabel, data, definition?.displayName, type, id, updateNodeData, setRenamingNodeId]);
83
+
84
+ // Handle cancel rename
85
+ const handleCancelRename = useCallback(() => {
86
+ setIsRenaming(false);
87
+ setRenamingNodeId(null);
88
+ }, [setRenamingNodeId]);
89
+
90
+ // Handle double-click to rename
91
+ const handleLabelDoubleClick = useCallback(() => {
92
+ setRenamingNodeId(id);
93
+ }, [id, setRenamingNodeId]);
94
+
95
+ const handleParametersClick = (e: React.MouseEvent) => {
96
+ e.stopPropagation();
97
+ setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
98
+ };
99
+
100
+ // Get status indicator color based on execution state
101
+ const getStatusIndicatorColor = () => {
102
+ // Combined executing/waiting state - show purple with glow
103
+ if (isExecuting) {
104
+ return theme.dracula.purple;
105
+ }
106
+ if (executionStatus === 'success') {
107
+ return theme.dracula.green;
108
+ }
109
+ if (executionStatus === 'error') {
110
+ return theme.dracula.red;
111
+ }
112
+
113
+ // WhatsApp trigger - use connection status when idle
114
+ if (isWhatsAppTrigger) {
115
+ if (whatsappStatus.connected) return theme.dracula.green;
116
+ if (whatsappStatus.pairing) return theme.dracula.orange;
117
+ return theme.dracula.red;
118
+ }
119
+
120
+ // Idle state - show configured status
121
+ return isConfigured ? theme.dracula.green : theme.dracula.orange;
122
+ };
123
+
124
+ const getStatusTitle = () => {
125
+ switch (executionStatus) {
126
+ case 'executing':
127
+ return 'Executing...';
128
+ case 'waiting':
129
+ return nodeStatus?.message || 'Waiting for trigger event...';
130
+ case 'success':
131
+ return 'Trigger fired successfully';
132
+ case 'error':
133
+ return `Error: ${nodeStatus?.data?.error || 'Unknown error'}`;
134
+ default:
135
+ // WhatsApp trigger status
136
+ if (isWhatsAppTrigger) {
137
+ if (whatsappStatus.connected) return 'WhatsApp connected - ready to receive';
138
+ if (whatsappStatus.pairing) return 'Pairing in progress...';
139
+ return 'WhatsApp not connected';
140
+ }
141
+ return isConfigured ? 'Trigger configured and ready' : 'Click to configure trigger';
142
+ }
143
+ };
144
+
145
+ // Check if string is likely an emoji
146
+ const isEmoji = (str: string): boolean => {
147
+ 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;
148
+ return emojiRegex.test(str);
149
+ };
150
+
151
+ // Helper to render icon
152
+ const renderIcon = (icon: string) => {
153
+ if (icon.startsWith('http') || icon.startsWith('data:') || icon.startsWith('/')) {
154
+ return (
155
+ <img
156
+ src={icon}
157
+ alt="icon"
158
+ style={{
159
+ width: '28px',
160
+ height: '28px',
161
+ objectFit: 'contain',
162
+ borderRadius: '4px'
163
+ }}
164
+ />
165
+ );
166
+ }
167
+
168
+ if (isEmoji(icon)) {
169
+ return icon;
170
+ }
171
+
172
+ // Fallback
173
+ return '⚡';
174
+ };
175
+
176
+ // Get trigger icon for display
177
+ const getTriggerIcon = () => {
178
+ // Start node - use PlayCircleFilled icon
179
+ if (type === 'start') {
180
+ return <PlayCircleFilled style={{ fontSize: 28, color: theme.dracula.cyan }} />;
181
+ }
182
+
183
+ // Cron Scheduler - use ScheduleOutlined
184
+ if (type === 'cronScheduler') {
185
+ return <ScheduleOutlined style={{ fontSize: 28, color: definition?.defaults?.color || nodeColor }} />;
186
+ }
187
+
188
+ // Use the icon from the node definition if available
189
+ if (definition?.icon && typeof definition.icon === 'string') {
190
+ return renderIcon(definition.icon);
191
+ }
192
+
193
+ // Fallback to lightning bolt for triggers
194
+ return '⚡';
195
+ };
196
+
197
+ // Get the node color from definition or use default trigger color
198
+ const nodeColor = definition?.defaults?.color || '#f59e0b';
199
+
200
+ return (
201
+ <div
202
+ style={{
203
+ position: 'relative',
204
+ display: 'flex',
205
+ flexDirection: 'column',
206
+ alignItems: 'center',
207
+ fontFamily: 'system-ui, -apple-system, sans-serif',
208
+ fontSize: '11px',
209
+ cursor: 'pointer',
210
+ }}
211
+ >
212
+ {/* Main Trigger Node */}
213
+ {/* Show glow animation for both executing and waiting states */}
214
+ <div
215
+ style={{
216
+ position: 'relative',
217
+ width: theme.nodeSize.square,
218
+ height: theme.nodeSize.square,
219
+ borderRadius: theme.borderRadius.lg,
220
+ background: theme.isDarkMode
221
+ ? `linear-gradient(135deg, ${nodeColor}25 0%, ${theme.colors.background} 100%)`
222
+ : `linear-gradient(145deg, #ffffff 0%, ${nodeColor}08 100%)`,
223
+ border: `2px solid ${
224
+ isExecuting
225
+ ? (theme.isDarkMode ? theme.dracula.purple : '#2563eb')
226
+ : selected
227
+ ? theme.colors.focus
228
+ : theme.isDarkMode ? nodeColor + '80' : `${nodeColor}40`
229
+ }`,
230
+ display: 'flex',
231
+ alignItems: 'center',
232
+ justifyContent: 'center',
233
+ color: theme.colors.text,
234
+ fontSize: theme.nodeSize.squareIcon,
235
+ fontWeight: '600',
236
+ transition: 'all 0.2s ease',
237
+ boxShadow: isExecuting
238
+ ? theme.isDarkMode
239
+ ? `0 4px 12px ${theme.dracula.purple}66, 0 0 0 3px ${theme.dracula.purple}4D`
240
+ : `0 0 0 3px rgba(37, 99, 235, 0.5), 0 4px 16px rgba(37, 99, 235, 0.35)`
241
+ : selected
242
+ ? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
243
+ : theme.isDarkMode
244
+ ? `0 2px 8px ${nodeColor}40`
245
+ : `0 2px 8px ${nodeColor}20, 0 4px 12px rgba(0,0,0,0.06)`,
246
+ // Subtle animation for both modes
247
+ animation: isExecuting ? 'pulse 1.5s ease-in-out infinite' : 'none',
248
+ opacity: isDisabled ? 0.5 : 1,
249
+ }}
250
+ >
251
+ {/* Disabled Overlay */}
252
+ {isDisabled && (
253
+ <div style={{
254
+ position: 'absolute',
255
+ top: 0,
256
+ left: 0,
257
+ right: 0,
258
+ bottom: 0,
259
+ backgroundColor: 'rgba(128, 128, 128, 0.4)',
260
+ borderRadius: 'inherit',
261
+ zIndex: 35,
262
+ display: 'flex',
263
+ alignItems: 'center',
264
+ justifyContent: 'center',
265
+ pointerEvents: 'none',
266
+ }}>
267
+ <span style={{ fontSize: '20px', opacity: 0.8, color: theme.colors.textSecondary }}>||</span>
268
+ </div>
269
+ )}
270
+
271
+ {/* Trigger Icon */}
272
+ {getTriggerIcon()}
273
+
274
+ {/* Parameters Button */}
275
+ <button
276
+ onClick={handleParametersClick}
277
+ style={{
278
+ position: 'absolute',
279
+ top: '-8px',
280
+ right: '-8px',
281
+ width: theme.nodeSize.paramButton,
282
+ height: theme.nodeSize.paramButton,
283
+ borderRadius: theme.borderRadius.sm,
284
+ backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
285
+ border: `1px solid ${theme.isDarkMode ? theme.colors.border : '#d1d5db'}`,
286
+ cursor: 'pointer',
287
+ display: 'flex',
288
+ alignItems: 'center',
289
+ justifyContent: 'center',
290
+ fontSize: theme.fontSize.xs,
291
+ color: theme.colors.textSecondary,
292
+ fontWeight: '400',
293
+ transition: theme.transitions.fast,
294
+ zIndex: 30,
295
+ boxShadow: theme.isDarkMode
296
+ ? `0 1px 3px ${theme.colors.shadow}`
297
+ : '0 1px 4px rgba(0,0,0,0.1)'
298
+ }}
299
+ title="Configure Trigger"
300
+ >
301
+ ⚙️
302
+ </button>
303
+
304
+ {/* Execution Status Indicator */}
305
+ {/* Status Indicator - glows for both waiting and executing states */}
306
+ <div
307
+ style={{
308
+ position: 'absolute',
309
+ top: '-4px',
310
+ left: '-4px',
311
+ width: theme.nodeSize.statusIndicator,
312
+ height: theme.nodeSize.statusIndicator,
313
+ borderRadius: '50%',
314
+ backgroundColor: getStatusIndicatorColor(),
315
+ border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
316
+ boxShadow: isExecuting
317
+ ? theme.isDarkMode
318
+ ? `0 0 6px ${theme.dracula.purple}80`
319
+ : '0 0 4px rgba(37, 99, 235, 0.5)'
320
+ : theme.isDarkMode
321
+ ? `0 1px 2px ${theme.colors.shadow}`
322
+ : '0 1px 3px rgba(0,0,0,0.15)',
323
+ zIndex: 30,
324
+ // Subtle pulse animation for both modes
325
+ animation: isExecuting ? 'pulse 1s ease-in-out infinite' : 'none',
326
+ }}
327
+ title={getStatusTitle()}
328
+ />
329
+
330
+ {/* NO INPUT HANDLE - Trigger nodes don't have inputs */}
331
+
332
+ {/* Trigger Badge - Lightning bolt indicator on bottom-left */}
333
+ <div
334
+ style={{
335
+ position: 'absolute',
336
+ bottom: '-4px',
337
+ left: '-4px',
338
+ width: theme.nodeSize.outputBadge,
339
+ height: theme.nodeSize.outputBadge,
340
+ borderRadius: theme.borderRadius.sm,
341
+ backgroundColor: theme.dracula.yellow,
342
+ border: `1px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
343
+ display: 'flex',
344
+ alignItems: 'center',
345
+ justifyContent: 'center',
346
+ fontSize: theme.fontSize.xs,
347
+ zIndex: 30,
348
+ boxShadow: theme.isDarkMode
349
+ ? `0 1px 3px ${theme.colors.shadow}`
350
+ : '0 1px 3px rgba(0,0,0,0.15)',
351
+ }}
352
+ title="Trigger Node - Starts workflow execution"
353
+ >
354
+ <span style={{ lineHeight: 1, color: theme.isDarkMode ? theme.colors.background : '#1a1d21' }}>⚡</span>
355
+ </div>
356
+
357
+ {/* Output Handle (right side) */}
358
+ <Handle
359
+ id="output-main"
360
+ type="source"
361
+ position={Position.Right}
362
+ isConnectable={isConnectable}
363
+ style={{
364
+ position: 'absolute',
365
+ right: '-6px',
366
+ top: '50%',
367
+ transform: 'translateY(-50%)',
368
+ width: theme.nodeSize.handle,
369
+ height: theme.nodeSize.handle,
370
+ backgroundColor: isConfigured ? nodeColor : theme.colors.textSecondary,
371
+ border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
372
+ borderRadius: '50%',
373
+ zIndex: 20
374
+ }}
375
+ title="Trigger Output"
376
+ />
377
+
378
+ {/* Output Data Indicator */}
379
+ {executionStatus === 'success' && nodeStatus?.data && (
380
+ <div
381
+ style={{
382
+ position: 'absolute',
383
+ bottom: '-4px',
384
+ right: '-4px',
385
+ width: theme.nodeSize.outputBadge,
386
+ height: theme.nodeSize.outputBadge,
387
+ borderRadius: theme.borderRadius.sm,
388
+ backgroundColor: theme.dracula.green,
389
+ border: `1px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
390
+ display: 'flex',
391
+ alignItems: 'center',
392
+ justifyContent: 'center',
393
+ fontSize: theme.fontSize.xs,
394
+ color: 'white',
395
+ fontWeight: 'bold',
396
+ zIndex: 30,
397
+ boxShadow: theme.isDarkMode
398
+ ? '0 1px 3px rgba(0,0,0,0.2)'
399
+ : '0 1px 3px rgba(0,0,0,0.15)',
400
+ }}
401
+ title="Output data available - click node to view"
402
+ >
403
+ <span style={{ lineHeight: 1 }}>D</span>
404
+ </div>
405
+ )}
406
+ </div>
407
+
408
+ {/* Trigger Name Below Node */}
409
+ {isRenaming ? (
410
+ <input
411
+ ref={inputRef}
412
+ type="text"
413
+ value={editLabel}
414
+ onChange={(e) => setEditLabel(e.target.value)}
415
+ onKeyDown={(e) => {
416
+ if (e.key === 'Enter') {
417
+ handleSaveRename();
418
+ } else if (e.key === 'Escape') {
419
+ handleCancelRename();
420
+ }
421
+ e.stopPropagation();
422
+ }}
423
+ onBlur={handleSaveRename}
424
+ onClick={(e) => e.stopPropagation()}
425
+ style={{
426
+ marginTop: theme.spacing.sm,
427
+ width: '100%',
428
+ maxWidth: '120px',
429
+ padding: '2px 4px',
430
+ fontSize: theme.fontSize.sm,
431
+ fontWeight: theme.fontWeight.medium,
432
+ color: theme.colors.text,
433
+ backgroundColor: theme.colors.backgroundElevated,
434
+ border: `1px solid ${theme.dracula.purple}`,
435
+ borderRadius: theme.borderRadius.sm,
436
+ outline: 'none',
437
+ textAlign: 'center',
438
+ }}
439
+ />
440
+ ) : (
441
+ <div
442
+ onDoubleClick={handleLabelDoubleClick}
443
+ style={{
444
+ marginTop: theme.spacing.sm,
445
+ fontSize: theme.fontSize.sm,
446
+ fontWeight: theme.fontWeight.medium,
447
+ color: theme.colors.text,
448
+ lineHeight: '1.2',
449
+ textAlign: 'center',
450
+ maxWidth: '120px',
451
+ cursor: 'text',
452
+ }}
453
+ title="Double-click to rename"
454
+ >
455
+ {data?.label || definition?.displayName}
456
+ </div>
457
+ )}
458
+
459
+ </div>
460
+ );
461
+ };
462
+
463
+ export default TriggerNode;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Login/Register Page with Dracula theme.
3
+ * Shows login form, or register form if registration is available.
4
+ */
5
+
6
+ import React, { useState } from 'react';
7
+ import { useAuth } from '../../contexts/AuthContext';
8
+ import { dracula } from '../../styles/theme';
9
+
10
+ const LoginPage: React.FC = () => {
11
+ const { login, register, canRegister, error, isLoading } = useAuth();
12
+
13
+ const [isRegistering, setIsRegistering] = useState(false);
14
+ const [email, setEmail] = useState('');
15
+ const [password, setPassword] = useState('');
16
+ const [displayName, setDisplayName] = useState('');
17
+ const [localError, setLocalError] = useState<string | null>(null);
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault();
21
+ setLocalError(null);
22
+
23
+ if (!email || !password) {
24
+ setLocalError('Email and password are required');
25
+ return;
26
+ }
27
+
28
+ if (isRegistering) {
29
+ if (!displayName) {
30
+ setLocalError('Display name is required');
31
+ return;
32
+ }
33
+ if (password.length < 8) {
34
+ setLocalError('Password must be at least 8 characters');
35
+ return;
36
+ }
37
+ await register(email, password, displayName);
38
+ } else {
39
+ await login(email, password);
40
+ }
41
+ };
42
+
43
+ const toggleMode = () => {
44
+ setIsRegistering(!isRegistering);
45
+ setLocalError(null);
46
+ };
47
+
48
+ const displayError = localError || error;
49
+
50
+ return (
51
+ <div style={styles.container}>
52
+ <div style={styles.card}>
53
+ {/* Logo/Title */}
54
+ <div style={styles.header}>
55
+ <h1 style={styles.title}>MachinaOs</h1>
56
+ <p style={styles.subtitle}>
57
+ {isRegistering ? 'Create your account' : 'Sign in to continue'}
58
+ </p>
59
+ </div>
60
+
61
+ {/* Error Message */}
62
+ {displayError && (
63
+ <div style={styles.errorBox}>
64
+ {displayError}
65
+ </div>
66
+ )}
67
+
68
+ {/* Form */}
69
+ <form onSubmit={handleSubmit} style={styles.form}>
70
+ {isRegistering && (
71
+ <div style={styles.inputGroup}>
72
+ <label style={styles.label}>Display Name</label>
73
+ <input
74
+ type="text"
75
+ value={displayName}
76
+ onChange={(e) => setDisplayName(e.target.value)}
77
+ style={styles.input}
78
+ placeholder="Your name"
79
+ disabled={isLoading}
80
+ />
81
+ </div>
82
+ )}
83
+
84
+ <div style={styles.inputGroup}>
85
+ <label style={styles.label}>Email</label>
86
+ <input
87
+ type="email"
88
+ value={email}
89
+ onChange={(e) => setEmail(e.target.value)}
90
+ style={styles.input}
91
+ placeholder="you@example.com"
92
+ disabled={isLoading}
93
+ autoComplete="email"
94
+ />
95
+ </div>
96
+
97
+ <div style={styles.inputGroup}>
98
+ <label style={styles.label}>Password</label>
99
+ <input
100
+ type="password"
101
+ value={password}
102
+ onChange={(e) => setPassword(e.target.value)}
103
+ style={styles.input}
104
+ placeholder={isRegistering ? 'At least 8 characters' : 'Your password'}
105
+ disabled={isLoading}
106
+ autoComplete={isRegistering ? 'new-password' : 'current-password'}
107
+ />
108
+ </div>
109
+
110
+ <button
111
+ type="submit"
112
+ style={{
113
+ ...styles.submitButton,
114
+ opacity: isLoading ? 0.7 : 1,
115
+ cursor: isLoading ? 'not-allowed' : 'pointer'
116
+ }}
117
+ disabled={isLoading}
118
+ >
119
+ {isLoading
120
+ ? 'Please wait...'
121
+ : isRegistering
122
+ ? 'Create Account'
123
+ : 'Sign In'}
124
+ </button>
125
+ </form>
126
+
127
+ {/* Toggle Login/Register */}
128
+ {canRegister && (
129
+ <div style={styles.toggleSection}>
130
+ <span style={styles.toggleText}>
131
+ {isRegistering ? 'Already have an account?' : "Don't have an account?"}
132
+ </span>
133
+ <button
134
+ onClick={toggleMode}
135
+ style={styles.toggleButton}
136
+ disabled={isLoading}
137
+ >
138
+ {isRegistering ? 'Sign In' : 'Register'}
139
+ </button>
140
+ </div>
141
+ )}
142
+ </div>
143
+ </div>
144
+ );
145
+ };
146
+
147
+ const styles: Record<string, React.CSSProperties> = {
148
+ container: {
149
+ display: 'flex',
150
+ justifyContent: 'center',
151
+ alignItems: 'center',
152
+ minHeight: '100vh',
153
+ backgroundColor: dracula.background,
154
+ padding: 20,
155
+ },
156
+ card: {
157
+ backgroundColor: dracula.currentLine,
158
+ borderRadius: 12,
159
+ padding: 40,
160
+ width: '100%',
161
+ maxWidth: 400,
162
+ boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4)',
163
+ },
164
+ header: {
165
+ textAlign: 'center',
166
+ marginBottom: 24,
167
+ },
168
+ title: {
169
+ color: dracula.purple,
170
+ fontSize: 32,
171
+ fontWeight: 700,
172
+ margin: 0,
173
+ marginBottom: 8,
174
+ },
175
+ subtitle: {
176
+ color: dracula.comment,
177
+ fontSize: 14,
178
+ margin: 0,
179
+ },
180
+ errorBox: {
181
+ backgroundColor: `${dracula.red}20`,
182
+ border: `1px solid ${dracula.red}`,
183
+ borderRadius: 6,
184
+ padding: '12px 16px',
185
+ marginBottom: 20,
186
+ color: dracula.red,
187
+ fontSize: 13,
188
+ },
189
+ form: {
190
+ display: 'flex',
191
+ flexDirection: 'column',
192
+ gap: 16,
193
+ },
194
+ inputGroup: {
195
+ display: 'flex',
196
+ flexDirection: 'column',
197
+ gap: 6,
198
+ },
199
+ label: {
200
+ color: dracula.foreground,
201
+ fontSize: 13,
202
+ fontWeight: 500,
203
+ },
204
+ input: {
205
+ backgroundColor: dracula.background,
206
+ border: `1px solid ${dracula.comment}50`,
207
+ borderRadius: 6,
208
+ padding: '12px 14px',
209
+ fontSize: 14,
210
+ color: dracula.foreground,
211
+ outline: 'none',
212
+ transition: 'border-color 0.2s, box-shadow 0.2s',
213
+ },
214
+ submitButton: {
215
+ backgroundColor: dracula.purple,
216
+ color: dracula.background,
217
+ border: 'none',
218
+ borderRadius: 6,
219
+ padding: '14px 20px',
220
+ fontSize: 15,
221
+ fontWeight: 600,
222
+ marginTop: 8,
223
+ transition: 'opacity 0.2s, transform 0.1s',
224
+ },
225
+ toggleSection: {
226
+ textAlign: 'center',
227
+ marginTop: 24,
228
+ paddingTop: 20,
229
+ borderTop: `1px solid ${dracula.comment}30`,
230
+ },
231
+ toggleText: {
232
+ color: dracula.comment,
233
+ fontSize: 13,
234
+ marginRight: 8,
235
+ },
236
+ toggleButton: {
237
+ background: 'none',
238
+ border: 'none',
239
+ color: dracula.cyan,
240
+ fontSize: 13,
241
+ fontWeight: 500,
242
+ cursor: 'pointer',
243
+ padding: 0,
244
+ },
245
+ };
246
+
247
+ export default LoginPage;