machinaos 0.0.76 → 0.0.78
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/README.md +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { memo, useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
2
2
|
import { nodePropsEqual } from './nodeMemoEquality';
|
|
3
3
|
import { Handle, Position, NodeProps } from 'reactflow';
|
|
4
|
-
import { NodeData } from '../types/NodeTypes';
|
|
4
|
+
import { NodeData, NodeStyle } from '../types/NodeTypes';
|
|
5
5
|
import { useAppStore } from '../store/useAppStore';
|
|
6
6
|
import { useAppTheme } from '../hooks/useAppTheme';
|
|
7
7
|
import EditableNodeLabel from './ui/EditableNodeLabel';
|
|
@@ -9,6 +9,7 @@ import { useWebSocket, useWhatsAppStatus, useNodeStatus } from '../contexts/WebS
|
|
|
9
9
|
import { getCachedNodeSpec, isNodeInBackendGroup, resolveNodeDescription, useNodeSpec } from '../lib/nodeSpec';
|
|
10
10
|
import { NodeIcon } from '../assets/icons';
|
|
11
11
|
import { AI_MODEL_PROVIDER_MAP } from '../lib/aiModelProviders';
|
|
12
|
+
import { useProviderStored } from '../hooks/useCatalogueQuery';
|
|
12
13
|
|
|
13
14
|
// Nodes with 'tool' in their group can connect to AI Agent/Zeenie tool handles
|
|
14
15
|
const hasToolGroup = (definition: any): boolean => {
|
|
@@ -31,7 +32,13 @@ const CREDENTIAL_TO_PROVIDER: Record<string, string> = {
|
|
|
31
32
|
|
|
32
33
|
const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
|
|
33
34
|
const theme = useAppTheme();
|
|
34
|
-
|
|
35
|
+
// Slice selectors so an unrelated store mutation (sidebar toggle,
|
|
36
|
+
// workflow rename, parameter save on another node) does NOT re-render
|
|
37
|
+
// every canvas node. Setters are stable refs — single-field selector
|
|
38
|
+
// is the cheapest read.
|
|
39
|
+
const setSelectedNode = useAppStore((s) => s.setSelectedNode);
|
|
40
|
+
const setRenamingNodeId = useAppStore((s) => s.setRenamingNodeId);
|
|
41
|
+
const updateNodeData = useAppStore((s) => s.updateNodeData);
|
|
35
42
|
const isDisabled = data?.disabled === true;
|
|
36
43
|
|
|
37
44
|
// Get Android status + API key status from the broad WebSocket
|
|
@@ -139,11 +146,12 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
139
146
|
return '';
|
|
140
147
|
}, [definition?.credentials, type]);
|
|
141
148
|
|
|
142
|
-
// "Is a credential configured for this provider?"
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
|
|
149
|
+
// "Is a credential configured for this provider?" reads from the
|
|
150
|
+
// server-driven catalogue (single source of truth — the `stored`
|
|
151
|
+
// flag is computed from `auth_service.has_valid_key()` on every
|
|
152
|
+
// catalogue read). The retired `apiKeyStatuses[id].hasKey` mirror
|
|
153
|
+
// duplicated this answer with no synchronisation contract.
|
|
154
|
+
const hasApiKey = useProviderStored(providerId);
|
|
147
155
|
const isConfigured = hasApiKey && !!data && Object.keys(data).length > 0;
|
|
148
156
|
|
|
149
157
|
const handleParametersClick = (e: React.MouseEvent) => {
|
|
@@ -151,45 +159,39 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
151
159
|
setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
|
|
152
160
|
};
|
|
153
161
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (executionStatus === '
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
if (executionStatus === 'error') {
|
|
164
|
-
return theme.dracula.red;
|
|
162
|
+
// Bucket execution + connection state into one of five status tokens
|
|
163
|
+
// consumed by `.sq-node-pip[data-status="..."]` in base.css. Per-theme
|
|
164
|
+
// CSS overrides these without fighting inline `backgroundColor`.
|
|
165
|
+
// Buckets: 'idle' | 'executing' | 'waiting' | 'success' | 'error'.
|
|
166
|
+
// Matches TriggerNode/StartNode/ToolkitNode contract (Wave 26.B).
|
|
167
|
+
const pipStatus: 'idle' | 'executing' | 'waiting' | 'success' | 'error' = (() => {
|
|
168
|
+
if (executionStatus === 'executing' || executionStatus === 'waiting' || isGlowing) {
|
|
169
|
+
return 'executing';
|
|
165
170
|
}
|
|
171
|
+
if (executionStatus === 'success') return 'success';
|
|
172
|
+
if (executionStatus === 'error') return 'error';
|
|
166
173
|
|
|
167
|
-
// Idle
|
|
174
|
+
// Idle bucket — derive from connection / configuration signals.
|
|
168
175
|
if (isAndroidNode) {
|
|
169
|
-
return isAndroidConnected ?
|
|
176
|
+
return isAndroidConnected ? 'success' : 'error';
|
|
170
177
|
}
|
|
171
|
-
|
|
172
|
-
// WhatsApp nodes - use WebSocket connection status
|
|
173
178
|
if (isWhatsAppNode) {
|
|
174
|
-
if (whatsappStatus.connected) return
|
|
175
|
-
if (whatsappStatus.pairing) return
|
|
176
|
-
return
|
|
179
|
+
if (whatsappStatus.connected) return 'success';
|
|
180
|
+
if (whatsappStatus.pairing) return 'waiting';
|
|
181
|
+
return 'error';
|
|
177
182
|
}
|
|
178
|
-
|
|
179
|
-
// Google Maps nodes - use WebSocket API key validation status
|
|
180
183
|
if (isGoogleMapsNode && googleMapsKeyStatus) {
|
|
181
|
-
return googleMapsKeyStatus.valid ?
|
|
184
|
+
return googleMapsKeyStatus.valid ? 'success' : 'error';
|
|
182
185
|
}
|
|
183
|
-
|
|
184
|
-
// AI Model nodes - use reactive WebSocket API key status
|
|
185
186
|
if (isAIModelNode) {
|
|
186
|
-
if (aiKeyStatus?.valid &&
|
|
187
|
-
if (
|
|
188
|
-
return
|
|
187
|
+
if (aiKeyStatus?.valid && hasApiKey) return 'success';
|
|
188
|
+
if (hasApiKey) return 'waiting';
|
|
189
|
+
return 'error';
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
if (isConfigured) return 'success';
|
|
192
|
+
if (hasApiKey) return 'waiting';
|
|
193
|
+
return 'idle';
|
|
194
|
+
})();
|
|
193
195
|
|
|
194
196
|
const getStatusTitle = () => {
|
|
195
197
|
switch (executionStatus) {
|
|
@@ -218,12 +220,14 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
218
220
|
? 'Google Maps API key validated'
|
|
219
221
|
: `API key invalid: ${googleMapsKeyStatus.message || 'Validation failed'}`;
|
|
220
222
|
}
|
|
221
|
-
// AI Model nodes -
|
|
223
|
+
// AI Model nodes - "is stored" from catalogue (hasApiKey),
|
|
224
|
+
// "is validated" from apiKeyStatuses. Independent concerns
|
|
225
|
+
// post-Wave-12; the two sources don't drift across tabs.
|
|
222
226
|
if (isAIModelNode) {
|
|
223
|
-
if (aiKeyStatus?.valid &&
|
|
227
|
+
if (aiKeyStatus?.valid && hasApiKey) {
|
|
224
228
|
return `${aiProviderId?.charAt(0).toUpperCase()}${aiProviderId?.slice(1)} API key validated`;
|
|
225
229
|
}
|
|
226
|
-
if (
|
|
230
|
+
if (hasApiKey) {
|
|
227
231
|
return 'API key found, validation pending';
|
|
228
232
|
}
|
|
229
233
|
return 'API key required - configure in Credentials';
|
|
@@ -248,8 +252,16 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
248
252
|
const nodeColor = definition?.defaults?.color || '#1A73E8';
|
|
249
253
|
|
|
250
254
|
return (
|
|
255
|
+
// `sq-node` + `selected` co-classes are the design-handoff structural
|
|
256
|
+
// hooks for per-theme square-node decorations (rivets on Steampunk
|
|
257
|
+
// sq-node-box::before/::after, hanko seal on Edo, REC LED + surv-blink
|
|
258
|
+
// on Surveillance, tombstone shape on Rot, gold-foil emblem on
|
|
259
|
+
// Renaissance, neon underglow on Cyber, etc.).
|
|
251
260
|
<div
|
|
261
|
+
className={`sq-node ${selected ? 'selected' : ''}`}
|
|
262
|
+
data-executing={isExecuting ? '' : undefined}
|
|
252
263
|
style={{
|
|
264
|
+
'--node-color': nodeColor,
|
|
253
265
|
position: 'relative',
|
|
254
266
|
display: 'flex',
|
|
255
267
|
flexDirection: 'column',
|
|
@@ -257,23 +269,16 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
257
269
|
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
258
270
|
fontSize: '11px',
|
|
259
271
|
cursor: 'pointer',
|
|
260
|
-
}}
|
|
272
|
+
} as NodeStyle}
|
|
261
273
|
>
|
|
262
274
|
{/* Main Square Node */}
|
|
263
275
|
<div
|
|
276
|
+
className="sq-node-box"
|
|
264
277
|
style={{
|
|
278
|
+
'--node-color': nodeColor,
|
|
265
279
|
position: 'relative',
|
|
266
280
|
width: theme.nodeSize.square,
|
|
267
281
|
height: theme.nodeSize.square,
|
|
268
|
-
borderRadius: theme.borderRadius.lg,
|
|
269
|
-
background: theme.isDarkMode
|
|
270
|
-
? `linear-gradient(135deg, ${nodeColor}25 0%, ${theme.colors.background} 100%)`
|
|
271
|
-
: `linear-gradient(145deg, #ffffff 0%, ${nodeColor}08 100%)`,
|
|
272
|
-
border: `2px solid ${isExecuting
|
|
273
|
-
? (theme.isDarkMode ? theme.dracula.cyan : '#2563eb')
|
|
274
|
-
: selected
|
|
275
|
-
? theme.colors.focus
|
|
276
|
-
: theme.isDarkMode ? nodeColor + '80' : `${nodeColor}40`}`,
|
|
277
282
|
display: 'flex',
|
|
278
283
|
alignItems: 'center',
|
|
279
284
|
justifyContent: 'center',
|
|
@@ -281,18 +286,8 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
281
286
|
fontSize: theme.nodeSize.squareIcon,
|
|
282
287
|
fontWeight: '600',
|
|
283
288
|
transition: 'all 0.2s ease',
|
|
284
|
-
boxShadow: isExecuting
|
|
285
|
-
? theme.isDarkMode
|
|
286
|
-
? `0 4px 12px ${theme.dracula.cyan}66, 0 0 0 3px ${theme.dracula.cyan}4D`
|
|
287
|
-
: `0 0 0 3px rgba(37, 99, 235, 0.5), 0 4px 16px rgba(37, 99, 235, 0.35)`
|
|
288
|
-
: selected
|
|
289
|
-
? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
|
|
290
|
-
: theme.isDarkMode
|
|
291
|
-
? `0 2px 8px ${nodeColor}40`
|
|
292
|
-
: `0 2px 8px ${nodeColor}20, 0 4px 12px rgba(0,0,0,0.06)`,
|
|
293
|
-
animation: isExecuting ? 'pulse 1.5s ease-in-out infinite' : 'none',
|
|
294
289
|
opacity: isDisabled ? 0.5 : 1,
|
|
295
|
-
}}
|
|
290
|
+
} as NodeStyle}
|
|
296
291
|
>
|
|
297
292
|
{/* Disabled Overlay */}
|
|
298
293
|
{isDisabled && (
|
|
@@ -318,7 +313,9 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
318
313
|
{/* Service Icon */}
|
|
319
314
|
<NodeIcon icon={iconRef} className="h-7 w-7 text-3xl" />
|
|
320
315
|
|
|
321
|
-
{/* Parameters Button
|
|
316
|
+
{/* Parameters Button — visual styles (background, border, hover)
|
|
317
|
+
owned by `.sq-node-gear` in base.css + per-theme overrides.
|
|
318
|
+
Inline keeps positioning + sizing only. */}
|
|
322
319
|
<button
|
|
323
320
|
onClick={handleParametersClick}
|
|
324
321
|
style={{
|
|
@@ -327,71 +324,55 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
327
324
|
right: '-8px',
|
|
328
325
|
width: theme.nodeSize.paramButton,
|
|
329
326
|
height: theme.nodeSize.paramButton,
|
|
330
|
-
borderRadius: theme.borderRadius.sm,
|
|
331
|
-
backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
|
|
332
|
-
border: `1px solid ${theme.isDarkMode ? theme.colors.border : '#d1d5db'}`,
|
|
333
|
-
cursor: 'pointer',
|
|
334
|
-
display: 'flex',
|
|
335
|
-
alignItems: 'center',
|
|
336
|
-
justifyContent: 'center',
|
|
337
327
|
fontSize: theme.fontSize.xs,
|
|
338
|
-
color: theme.colors.textSecondary,
|
|
339
|
-
fontWeight: '400',
|
|
340
|
-
transition: theme.transitions.fast,
|
|
341
328
|
zIndex: 30,
|
|
342
|
-
boxShadow: theme.isDarkMode
|
|
343
|
-
? `0 1px 3px ${theme.colors.shadow}`
|
|
344
|
-
: '0 1px 4px rgba(0,0,0,0.1)'
|
|
345
329
|
}}
|
|
346
330
|
title="Edit Service Parameters"
|
|
331
|
+
className="sq-node-gear"
|
|
347
332
|
>
|
|
348
333
|
⚙️
|
|
349
334
|
</button>
|
|
350
335
|
|
|
351
|
-
{/* Configuration/Execution Status Indicator
|
|
336
|
+
{/* Configuration/Execution Status Indicator.
|
|
337
|
+
Background color is owned by `.sq-node-pip[data-status="..."]`
|
|
338
|
+
in base.css (and per-theme overrides). We pass only the
|
|
339
|
+
execution/connection bucket here — no inline color. */}
|
|
352
340
|
<div
|
|
341
|
+
className="sq-node-pip"
|
|
342
|
+
data-status={pipStatus}
|
|
353
343
|
style={{
|
|
354
344
|
position: 'absolute',
|
|
355
345
|
top: '-4px',
|
|
356
346
|
left: '-4px',
|
|
357
347
|
width: theme.nodeSize.statusIndicator,
|
|
358
348
|
height: theme.nodeSize.statusIndicator,
|
|
359
|
-
borderRadius: '50%',
|
|
360
|
-
backgroundColor: getStatusIndicatorColor(),
|
|
361
|
-
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
362
|
-
boxShadow: isExecuting
|
|
363
|
-
? theme.isDarkMode
|
|
364
|
-
? `0 0 6px ${theme.dracula.cyan}80`
|
|
365
|
-
: '0 0 4px rgba(37, 99, 235, 0.5)'
|
|
366
|
-
: theme.isDarkMode
|
|
367
|
-
? `0 1px 2px ${theme.colors.shadow}`
|
|
368
|
-
: '0 1px 3px rgba(0,0,0,0.15)',
|
|
369
349
|
zIndex: 30,
|
|
370
|
-
animation: isExecuting ? 'pulse 1s ease-in-out infinite' : 'none',
|
|
371
350
|
}}
|
|
372
351
|
title={getStatusTitle()}
|
|
373
352
|
/>
|
|
374
353
|
|
|
375
|
-
{/* Square Input Handle
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
position
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
354
|
+
{/* Square Input Handle (gated by spec.hideInputHandle, mirrors
|
|
355
|
+
the hideOutputHandle pattern below — auto-derived True for
|
|
356
|
+
usable_as_tool=True nodes via BaseNode.__init_subclass__). */}
|
|
357
|
+
{!getCachedNodeSpec(type || '')?.hideInputHandle && (
|
|
358
|
+
<Handle
|
|
359
|
+
id="input-main"
|
|
360
|
+
type="target"
|
|
361
|
+
position={Position.Left}
|
|
362
|
+
isConnectable={isConnectable}
|
|
363
|
+
className="sq-node-handle in"
|
|
364
|
+
style={{
|
|
365
|
+
position: 'absolute',
|
|
366
|
+
left: '-6px',
|
|
367
|
+
top: '50%',
|
|
368
|
+
transform: 'translateY(-50%)',
|
|
369
|
+
width: theme.nodeSize.handle,
|
|
370
|
+
height: theme.nodeSize.handle,
|
|
371
|
+
zIndex: 20
|
|
372
|
+
}}
|
|
373
|
+
title="Service Input"
|
|
374
|
+
/>
|
|
375
|
+
)}
|
|
395
376
|
|
|
396
377
|
{/* Square Output Handle (Wave 10.E: spec.hideOutputHandle replaces the
|
|
397
378
|
local NO_OUTPUT_NODE_TYPES list) */}
|
|
@@ -401,18 +382,17 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
401
382
|
type="source"
|
|
402
383
|
position={Position.Right}
|
|
403
384
|
isConnectable={isConnectable}
|
|
385
|
+
className="sq-node-handle out"
|
|
404
386
|
style={{
|
|
387
|
+
'--node-color': isConfigured ? nodeColor : theme.colors.textSecondary,
|
|
405
388
|
position: 'absolute',
|
|
406
389
|
right: '-6px',
|
|
407
390
|
top: '50%',
|
|
408
391
|
transform: 'translateY(-50%)',
|
|
409
392
|
width: theme.nodeSize.handle,
|
|
410
393
|
height: theme.nodeSize.handle,
|
|
411
|
-
backgroundColor: isConfigured ? nodeColor : theme.colors.textSecondary,
|
|
412
|
-
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
413
|
-
borderRadius: '50%',
|
|
414
394
|
zIndex: 20
|
|
415
|
-
}}
|
|
395
|
+
} as NodeStyle}
|
|
416
396
|
title="Service Output"
|
|
417
397
|
/>
|
|
418
398
|
)}
|
|
@@ -422,7 +402,10 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
422
402
|
`nodeColor` carries the spec's brand color (Android green for
|
|
423
403
|
Android services, dracula accents elsewhere). The tooltip
|
|
424
404
|
reads from the spec's top-position output handle when one is
|
|
425
|
-
declared there; otherwise falls back to a generic label.
|
|
405
|
+
declared there; otherwise falls back to a generic label.
|
|
406
|
+
Visual styles (background, border, radius) owned by
|
|
407
|
+
`.sq-node-handle.out` in base.css + per-theme overrides — we
|
|
408
|
+
pass `--node-color` for the CSS var only. */}
|
|
426
409
|
{isToolCapable && (() => {
|
|
427
410
|
const spec = getCachedNodeSpec(type || '');
|
|
428
411
|
const topOut = spec?.handles?.find(h => h.kind === 'output' && h.position === 'top');
|
|
@@ -433,18 +416,17 @@ const SquareNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectab
|
|
|
433
416
|
type="source"
|
|
434
417
|
position={Position.Top}
|
|
435
418
|
isConnectable={isConnectable}
|
|
419
|
+
className="sq-node-handle out"
|
|
436
420
|
style={{
|
|
421
|
+
'--node-color': nodeColor,
|
|
437
422
|
position: 'absolute',
|
|
438
423
|
top: '-6px',
|
|
439
424
|
left: '50%',
|
|
440
425
|
transform: 'translateX(-50%)',
|
|
441
426
|
width: theme.nodeSize.handle,
|
|
442
427
|
height: theme.nodeSize.handle,
|
|
443
|
-
backgroundColor: nodeColor,
|
|
444
|
-
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
445
|
-
borderRadius: '50%',
|
|
446
428
|
zIndex: 20,
|
|
447
|
-
}}
|
|
429
|
+
} as NodeStyle}
|
|
448
430
|
title={tooltip}
|
|
449
431
|
/>
|
|
450
432
|
);
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import React, { memo, useCallback } from 'react';
|
|
2
2
|
import { nodePropsEqual } from './nodeMemoEquality';
|
|
3
3
|
import { Handle, Position, NodeProps } from 'reactflow';
|
|
4
|
-
import { NodeData } from '../types/NodeTypes';
|
|
4
|
+
import { NodeData, NodeStyle } from '../types/NodeTypes';
|
|
5
5
|
import { useAppStore } from '../store/useAppStore';
|
|
6
6
|
import { useAppTheme } from '../hooks/useAppTheme';
|
|
7
7
|
import { PlayCircle } from 'lucide-react';
|
|
8
|
+
import { resolveNodeDescription } from '../lib/nodeSpec';
|
|
8
9
|
import EditableNodeLabel from './ui/EditableNodeLabel';
|
|
9
10
|
|
|
10
11
|
const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectable, selected }) => {
|
|
11
|
-
const
|
|
12
|
+
const setSelectedNode = useAppStore((s) => s.setSelectedNode);
|
|
13
|
+
const setRenamingNodeId = useAppStore((s) => s.setRenamingNodeId);
|
|
14
|
+
const updateNodeData = useAppStore((s) => s.updateNodeData);
|
|
12
15
|
const theme = useAppTheme();
|
|
13
16
|
|
|
14
17
|
const defaultLabel = 'Start';
|
|
15
18
|
|
|
19
|
+
// Definition-driven color (Wave 26.A): backend NodeSpec is SSOT for
|
|
20
|
+
// node color. Falls back to dracula cyan if the spec hasn't loaded.
|
|
21
|
+
const definition = resolveNodeDescription(type || '');
|
|
22
|
+
const nodeColor = definition?.defaults?.color || theme.dracula.cyan;
|
|
23
|
+
|
|
16
24
|
const handleLabelChange = useCallback(
|
|
17
25
|
(newLabel: string) => updateNodeData(id, { label: newLabel }),
|
|
18
26
|
[id, updateNodeData]
|
|
@@ -28,11 +36,17 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
|
|
|
28
36
|
setSelectedNode({ id, type, data, position: { x: 0, y: 0 } });
|
|
29
37
|
};
|
|
30
38
|
|
|
31
|
-
const nodeColor = theme.dracula.cyan; // Cyan color for start node (neutral "begin" color)
|
|
32
|
-
|
|
33
39
|
return (
|
|
40
|
+
// `sq-node` + `selected` co-classes activate per-theme square-node
|
|
41
|
+
// decorations (Renaissance wax seal, Steampunk rivets, Edo hanko
|
|
42
|
+
// seal, Surveillance REC LED, Cyber neon underglow). Visual styling
|
|
43
|
+
// (background, border, radius, shadow) lives in base.css
|
|
44
|
+
// `.sq-node-box { ... }` defaults + per-theme overrides and reads
|
|
45
|
+
// `var(--node-color)` for the per-definition accent.
|
|
34
46
|
<div
|
|
47
|
+
className={`sq-node ${selected ? 'selected' : ''}`}
|
|
35
48
|
style={{
|
|
49
|
+
'--node-color': nodeColor,
|
|
36
50
|
position: 'relative',
|
|
37
51
|
display: 'flex',
|
|
38
52
|
flexDirection: 'column',
|
|
@@ -40,82 +54,60 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
|
|
|
40
54
|
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
41
55
|
fontSize: '11px',
|
|
42
56
|
cursor: 'pointer',
|
|
43
|
-
}}
|
|
57
|
+
} as NodeStyle}
|
|
44
58
|
>
|
|
45
|
-
{/* Main Square Node */}
|
|
59
|
+
{/* Main Square Node — layout only; visuals live in CSS. */}
|
|
46
60
|
<div
|
|
61
|
+
className="sq-node-box"
|
|
47
62
|
style={{
|
|
48
63
|
position: 'relative',
|
|
49
64
|
width: '60px',
|
|
50
65
|
height: '60px',
|
|
51
|
-
borderRadius: '8px',
|
|
52
|
-
background: theme.isDarkMode
|
|
53
|
-
? `linear-gradient(135deg, ${nodeColor}20 0%, ${theme.colors.background} 100%)`
|
|
54
|
-
: `linear-gradient(145deg, #ffffff 0%, ${nodeColor}10 100%)`,
|
|
55
|
-
border: `2px solid ${selected
|
|
56
|
-
? theme.colors.focus
|
|
57
|
-
: theme.isDarkMode ? `${nodeColor}60` : `${nodeColor}50`}`,
|
|
58
66
|
display: 'flex',
|
|
59
67
|
alignItems: 'center',
|
|
60
68
|
justifyContent: 'center',
|
|
61
69
|
color: theme.colors.text,
|
|
62
70
|
fontSize: '28px',
|
|
63
71
|
fontWeight: '600',
|
|
64
|
-
transition: 'all 0.2s ease',
|
|
65
|
-
boxShadow: selected
|
|
66
|
-
? `0 4px 12px ${theme.colors.focusRing}, 0 0 0 1px ${theme.colors.focusRing}`
|
|
67
|
-
: theme.isDarkMode
|
|
68
|
-
? `0 2px 8px ${nodeColor}30`
|
|
69
|
-
: `0 2px 8px ${nodeColor}25, 0 4px 12px rgba(0,0,0,0.06)`,
|
|
70
72
|
}}
|
|
71
73
|
>
|
|
72
|
-
{/* Play Icon */}
|
|
74
|
+
{/* Play Icon (color reads from definition-driven nodeColor) */}
|
|
73
75
|
<PlayCircle className="h-7 w-7" style={{ color: nodeColor }} />
|
|
74
76
|
|
|
75
|
-
{/* Parameters Button */}
|
|
77
|
+
{/* Parameters Button — CSS owns bg/border via .sq-node-gear */}
|
|
76
78
|
<button
|
|
77
79
|
onClick={handleParametersClick}
|
|
80
|
+
className="sq-node-gear"
|
|
78
81
|
style={{
|
|
79
82
|
position: 'absolute',
|
|
80
83
|
top: '-8px',
|
|
81
84
|
right: '-8px',
|
|
82
85
|
width: '16px',
|
|
83
86
|
height: '16px',
|
|
84
|
-
borderRadius: '3px',
|
|
85
|
-
backgroundColor: theme.isDarkMode ? theme.colors.backgroundAlt : '#ffffff',
|
|
86
|
-
border: `1px solid ${theme.isDarkMode ? theme.colors.border : `${nodeColor}40`}`,
|
|
87
87
|
cursor: 'pointer',
|
|
88
88
|
display: 'flex',
|
|
89
89
|
alignItems: 'center',
|
|
90
90
|
justifyContent: 'center',
|
|
91
91
|
fontSize: '8px',
|
|
92
|
-
color: theme.colors.textSecondary,
|
|
93
92
|
fontWeight: '400',
|
|
94
|
-
transition: 'all 0.2s ease',
|
|
95
93
|
zIndex: 30,
|
|
96
|
-
boxShadow: theme.isDarkMode
|
|
97
|
-
? `0 1px 3px ${theme.colors.shadow}`
|
|
98
|
-
: `0 1px 4px ${nodeColor}20`
|
|
99
94
|
}}
|
|
100
95
|
title="Edit Parameters"
|
|
101
96
|
>
|
|
102
|
-
{'
|
|
97
|
+
{'⚙️'}
|
|
103
98
|
</button>
|
|
104
99
|
|
|
105
|
-
{/* Status Indicator
|
|
100
|
+
{/* Status Indicator — Start is always "ready" (success bucket).
|
|
101
|
+
CSS owns bg color via per-status rule on .sq-node-pip. */}
|
|
106
102
|
<div
|
|
103
|
+
className="sq-node-pip"
|
|
104
|
+
data-status="success"
|
|
107
105
|
style={{
|
|
108
106
|
position: 'absolute',
|
|
109
107
|
top: '-4px',
|
|
110
108
|
left: '-4px',
|
|
111
109
|
width: '10px',
|
|
112
110
|
height: '10px',
|
|
113
|
-
borderRadius: '50%',
|
|
114
|
-
backgroundColor: nodeColor,
|
|
115
|
-
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
116
|
-
boxShadow: theme.isDarkMode
|
|
117
|
-
? `0 1px 2px ${theme.colors.shadow}`
|
|
118
|
-
: '0 1px 3px rgba(0,0,0,0.15)',
|
|
119
111
|
zIndex: 30,
|
|
120
112
|
}}
|
|
121
113
|
title="Workflow start point"
|
|
@@ -134,12 +126,13 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
|
|
|
134
126
|
}}
|
|
135
127
|
/>
|
|
136
128
|
|
|
137
|
-
{/* Output Handle */}
|
|
129
|
+
{/* Output Handle — CSS owns bg/border via .sq-node-handle.out */}
|
|
138
130
|
<Handle
|
|
139
131
|
id="output-main"
|
|
140
132
|
type="source"
|
|
141
133
|
position={Position.Right}
|
|
142
134
|
isConnectable={isConnectable}
|
|
135
|
+
className="sq-node-handle out"
|
|
143
136
|
style={{
|
|
144
137
|
position: 'absolute',
|
|
145
138
|
right: '-6px',
|
|
@@ -147,10 +140,7 @@ const StartNode: React.FC<NodeProps<NodeData>> = ({ id, type, data, isConnectabl
|
|
|
147
140
|
transform: 'translateY(-50%)',
|
|
148
141
|
width: '8px',
|
|
149
142
|
height: '8px',
|
|
150
|
-
|
|
151
|
-
border: `2px solid ${theme.isDarkMode ? theme.colors.background : '#ffffff'}`,
|
|
152
|
-
borderRadius: '50%',
|
|
153
|
-
zIndex: 20
|
|
143
|
+
zIndex: 20,
|
|
154
144
|
}}
|
|
155
145
|
title="Workflow Output"
|
|
156
146
|
/>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mounts a hidden inline <svg><defs> block once at the app root so per-
|
|
3
|
+
* theme CSS rules like `filter: url(#ink-blot)` resolve. The SVG itself
|
|
4
|
+
* has zero pixel footprint (width=0 height=0); the filter definitions
|
|
5
|
+
* inside <defs> are document-wide referenceable by ID.
|
|
6
|
+
*
|
|
7
|
+
* Filter IDs declared:
|
|
8
|
+
* #ink-blot - Renaissance edge warble (turbulence + displacement map)
|
|
9
|
+
* #noise - Wasteland paper-grain turbulence
|
|
10
|
+
* #crt - Cyber chromatic aberration / scanline
|
|
11
|
+
*
|
|
12
|
+
* Audit: only `#ink-blot` is currently referenced in the upstream design
|
|
13
|
+
* handoff CSS (renaissance.css line 271). `#noise` and `#crt` are shipped
|
|
14
|
+
* preemptively so per-theme CSS can adopt them without a second wave.
|
|
15
|
+
*/
|
|
16
|
+
import React from 'react';
|
|
17
|
+
|
|
18
|
+
export const SvgFilterDefs: React.FC = () => (
|
|
19
|
+
<svg
|
|
20
|
+
width={0}
|
|
21
|
+
height={0}
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
style={{ position: 'absolute', overflow: 'hidden' }}
|
|
24
|
+
>
|
|
25
|
+
<defs>
|
|
26
|
+
<filter id="ink-blot">
|
|
27
|
+
<feTurbulence
|
|
28
|
+
type="fractalNoise"
|
|
29
|
+
baseFrequency="0.02 0.04"
|
|
30
|
+
numOctaves="2"
|
|
31
|
+
seed="3"
|
|
32
|
+
/>
|
|
33
|
+
<feDisplacementMap in="SourceGraphic" scale="2" />
|
|
34
|
+
</filter>
|
|
35
|
+
<filter id="noise">
|
|
36
|
+
<feTurbulence
|
|
37
|
+
type="fractalNoise"
|
|
38
|
+
baseFrequency="0.9"
|
|
39
|
+
numOctaves="2"
|
|
40
|
+
stitchTiles="stitch"
|
|
41
|
+
/>
|
|
42
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" />
|
|
43
|
+
</filter>
|
|
44
|
+
<filter id="crt">
|
|
45
|
+
<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" />
|
|
46
|
+
<feOffset in="SourceGraphic" dx="-1" dy="0" result="r" />
|
|
47
|
+
<feOffset in="SourceGraphic" dx="1" dy="0" result="b" />
|
|
48
|
+
<feBlend in="r" in2="b" mode="screen" />
|
|
49
|
+
</filter>
|
|
50
|
+
</defs>
|
|
51
|
+
</svg>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export default SvgFilterDefs;
|