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
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
HelpCircle,
|
|
10
10
|
RotateCcw,
|
|
11
11
|
X,
|
|
12
|
+
Volume2,
|
|
12
13
|
} from 'lucide-react';
|
|
14
|
+
import { useAppStore } from '../../store/useAppStore';
|
|
13
15
|
|
|
14
16
|
import { Button } from '@/components/ui/button';
|
|
15
17
|
import { ActionButton } from '@/components/ui/action-button';
|
|
@@ -44,12 +46,13 @@ interface SettingsPanelProps {
|
|
|
44
46
|
// Reusable row + section primitives
|
|
45
47
|
// ---------------------------------------------------------------------------
|
|
46
48
|
|
|
47
|
-
type SectionTone = 'agent' | 'model' | 'workflow';
|
|
49
|
+
type SectionTone = 'agent' | 'model' | 'workflow' | 'tool';
|
|
48
50
|
|
|
49
51
|
const TONE_CLASSES: Record<SectionTone, string> = {
|
|
50
52
|
agent: 'bg-node-agent-soft text-node-agent',
|
|
51
53
|
model: 'bg-node-model-soft text-node-model',
|
|
52
54
|
workflow: 'bg-node-workflow-soft text-node-workflow',
|
|
55
|
+
tool: 'bg-node-tool-soft text-node-tool',
|
|
53
56
|
};
|
|
54
57
|
|
|
55
58
|
interface SectionProps {
|
|
@@ -60,12 +63,17 @@ interface SectionProps {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
const Section: React.FC<SectionProps> = ({ title, Icon, tone, children }) => (
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
// bg-bg-elevated + border-default — settings sections are elevated
|
|
67
|
+
// cards stacked inside the modal body. font-display + tracking gives
|
|
68
|
+
// Renaissance/Cyber their typographic identity on section headers.
|
|
69
|
+
<div className="mb-4 rounded-md border border-border-default bg-bg-elevated p-4">
|
|
70
|
+
<div className="mb-4 flex items-center gap-2 border-b border-border-default pb-3">
|
|
65
71
|
<div className={`flex h-8 w-8 items-center justify-center rounded-md ${TONE_CLASSES[tone]}`}>
|
|
66
72
|
<Icon className="h-4 w-4" />
|
|
67
73
|
</div>
|
|
68
|
-
<div className="text-base font-semibold text-
|
|
74
|
+
<div className="font-display text-base font-semibold tracking-[var(--type-tracking-display)] text-fg-default [text-transform:var(--type-uppercase)]">
|
|
75
|
+
{title}
|
|
76
|
+
</div>
|
|
69
77
|
</div>
|
|
70
78
|
{children}
|
|
71
79
|
</div>
|
|
@@ -80,8 +88,8 @@ interface RowProps {
|
|
|
80
88
|
const Row: React.FC<RowProps> = ({ label, description, children }) => (
|
|
81
89
|
<div className="flex items-center justify-between py-2">
|
|
82
90
|
<div className="flex-1">
|
|
83
|
-
<div className="text-sm font-medium text-
|
|
84
|
-
<div className="mt-0.5 text-xs text-muted
|
|
91
|
+
<div className="text-sm font-medium text-fg-default">{label}</div>
|
|
92
|
+
<div className="mt-0.5 text-xs text-fg-muted">{description}</div>
|
|
85
93
|
</div>
|
|
86
94
|
{children}
|
|
87
95
|
</div>
|
|
@@ -103,6 +111,12 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
103
111
|
const isLoading = settingsQuery.isLoading;
|
|
104
112
|
const isSaving = saveMutation.isPending;
|
|
105
113
|
|
|
114
|
+
// Sound preference lives in the global UI store (not the per-user
|
|
115
|
+
// server settings row) because it's gated by browser autoplay policy
|
|
116
|
+
// and persists locally only.
|
|
117
|
+
const soundEnabled = useAppStore((s) => s.soundEnabled);
|
|
118
|
+
const setSoundEnabled = useAppStore((s) => s.setSoundEnabled);
|
|
119
|
+
|
|
106
120
|
// Hydrate Dashboard's controlled state from the cached settings row
|
|
107
121
|
// exactly once per open. The query is shared with useOnboarding so
|
|
108
122
|
// cross-component reads stay in sync.
|
|
@@ -149,7 +163,7 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
149
163
|
|
|
150
164
|
const headerActions = (
|
|
151
165
|
<div className="flex items-center gap-4">
|
|
152
|
-
<div className="flex items-center gap-2 text-[15px] font-semibold text-
|
|
166
|
+
<div className="flex items-center gap-2 font-display text-[15px] font-semibold tracking-[var(--type-tracking-display)] text-fg-default [text-transform:var(--type-uppercase)]">
|
|
153
167
|
<SettingsIcon className="h-4 w-4" />
|
|
154
168
|
<span>Settings</span>
|
|
155
169
|
</div>
|
|
@@ -266,7 +280,7 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
266
280
|
disabled={isSaving}
|
|
267
281
|
className="pr-6"
|
|
268
282
|
/>
|
|
269
|
-
<span className="pointer-events-none absolute top-1/2 right-2 -translate-y-1/2 text-xs text-muted
|
|
283
|
+
<span className="pointer-events-none absolute top-1/2 right-2 -translate-y-1/2 text-xs text-fg-muted">
|
|
270
284
|
s
|
|
271
285
|
</span>
|
|
272
286
|
</div>
|
|
@@ -292,13 +306,13 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
292
306
|
/>
|
|
293
307
|
</Row>
|
|
294
308
|
|
|
295
|
-
<div className="my-1 border-b border-border" />
|
|
309
|
+
<div className="my-1 border-b border-border-default" />
|
|
296
310
|
|
|
297
311
|
<div className="py-2">
|
|
298
312
|
<div className="mb-2 flex items-center justify-between">
|
|
299
313
|
<div className="flex-1">
|
|
300
|
-
<div className="text-sm font-medium text-
|
|
301
|
-
<div className="mt-0.5 text-xs text-muted
|
|
314
|
+
<div className="text-sm font-medium text-fg-default">Compaction Ratio</div>
|
|
315
|
+
<div className="mt-0.5 text-xs text-fg-muted">
|
|
302
316
|
Fraction of context window that triggers memory compaction
|
|
303
317
|
</div>
|
|
304
318
|
</div>
|
|
@@ -315,12 +329,12 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
315
329
|
disabled={isSaving}
|
|
316
330
|
className="my-3"
|
|
317
331
|
/>
|
|
318
|
-
<div className="flex justify-between text-[10px] text-muted
|
|
332
|
+
<div className="flex justify-between text-[10px] text-fg-muted">
|
|
319
333
|
<span>10%</span>
|
|
320
334
|
<span>50%</span>
|
|
321
335
|
<span>90%</span>
|
|
322
336
|
</div>
|
|
323
|
-
<div className="mt-1 text-xs leading-snug text-muted
|
|
337
|
+
<div className="mt-1 text-xs leading-snug text-fg-muted">
|
|
324
338
|
Lower = compact sooner (saves tokens, loses detail). Higher = compact later (preserves context, uses more tokens).
|
|
325
339
|
</div>
|
|
326
340
|
</div>
|
|
@@ -345,6 +359,21 @@ const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
345
359
|
</Row>
|
|
346
360
|
</Section>
|
|
347
361
|
|
|
362
|
+
{/* Audio — per-theme WebAudio sound packs. On by default;
|
|
363
|
+
honours browser autoplay policy (engine resumes on first
|
|
364
|
+
user gesture via Sounds.unlock()). */}
|
|
365
|
+
<Section title="Audio" Icon={Volume2} tone="tool">
|
|
366
|
+
<Row
|
|
367
|
+
label="Sound Effects"
|
|
368
|
+
description="Play per-theme click / hover / save / error sounds. Each theme ships a different pack (parchment, terminal, marble, ink, clockwork, ...). Enabled by default — toggle off if you prefer silence."
|
|
369
|
+
>
|
|
370
|
+
<Switch
|
|
371
|
+
checked={soundEnabled}
|
|
372
|
+
onCheckedChange={setSoundEnabled}
|
|
373
|
+
/>
|
|
374
|
+
</Row>
|
|
375
|
+
</Section>
|
|
376
|
+
|
|
348
377
|
{/* Help */}
|
|
349
378
|
<Section title="Help" Icon={HelpCircle} tone="model">
|
|
350
379
|
<Row
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar — fixed-bottom system console line.
|
|
3
|
+
*
|
|
4
|
+
* Lives below the canvas, shows workflow name, node count, WebSocket
|
|
5
|
+
* connection state, active theme, and a live clock. Designed to read
|
|
6
|
+
* as a "shell prompt" line under Cyber and a "manuscript footer" under
|
|
7
|
+
* Renaissance via the new-contract typography tokens (font-mono,
|
|
8
|
+
* tracking, uppercase).
|
|
9
|
+
*
|
|
10
|
+
* Per the handoff this is the `.statusbar` surface — it always exists,
|
|
11
|
+
* height is 24px, sits above the ConsolePanel toggle bar.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import { useEffect, useState } from 'react';
|
|
16
|
+
import { useWebSocket } from '../../contexts/WebSocketContext';
|
|
17
|
+
import { useTheme, type ThemeName } from '../../contexts/ThemeContext';
|
|
18
|
+
import { cn } from '@/lib/utils';
|
|
19
|
+
|
|
20
|
+
const THEME_LABEL: Record<ThemeName, string> = {
|
|
21
|
+
light: 'LIGHT',
|
|
22
|
+
dark: 'DARK',
|
|
23
|
+
renaissance: 'RENAISSANCE',
|
|
24
|
+
greek: 'GREEK',
|
|
25
|
+
edo: 'EDO',
|
|
26
|
+
steampunk: 'STEAMPUNK',
|
|
27
|
+
atomic: 'ATOMIC',
|
|
28
|
+
cyber: 'CYBER',
|
|
29
|
+
wasteland: 'WASTELAND',
|
|
30
|
+
rot: 'ROT',
|
|
31
|
+
plague: 'PLAGUE',
|
|
32
|
+
surveillance: 'SURVEILLANCE',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
interface StatusBarProps {
|
|
36
|
+
workflowName?: string;
|
|
37
|
+
nodeCount?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const useClock = (): string => {
|
|
41
|
+
const [time, setTime] = useState(() => new Date());
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const id = window.setInterval(() => setTime(new Date()), 1000);
|
|
44
|
+
return () => window.clearInterval(id);
|
|
45
|
+
}, []);
|
|
46
|
+
return time.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const Sep: React.FC = () => <span className="opacity-40">|</span>;
|
|
50
|
+
|
|
51
|
+
export const StatusBar: React.FC<StatusBarProps> = ({ workflowName, nodeCount }) => {
|
|
52
|
+
const { isReady, isConnected } = useWebSocket();
|
|
53
|
+
const { theme } = useTheme();
|
|
54
|
+
const clock = useClock();
|
|
55
|
+
|
|
56
|
+
const wsStatus = isReady ? 'ONLINE' : isConnected ? 'CONNECTING' : 'OFFLINE';
|
|
57
|
+
const wsTone = isReady ? 'text-success' : isConnected ? 'text-warning' : 'text-destructive';
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
// Fixed-bottom strip: bg-bg-panel + 1px top rule. font-mono carries
|
|
61
|
+
// through to IM Fell English under Renaissance and JetBrains Mono
|
|
62
|
+
// under Cyber, system mono under light/dark.
|
|
63
|
+
<div
|
|
64
|
+
className={cn(
|
|
65
|
+
// `statusbar` is the handoff structural hook for per-theme
|
|
66
|
+
// decorations (gauge readouts on Steampunk, REC blink on
|
|
67
|
+
// Surveillance, illuminated footer on Renaissance).
|
|
68
|
+
'statusbar flex h-6 items-center gap-3 border-t border-border-default bg-bg-panel px-3.5',
|
|
69
|
+
'font-mono text-[11px] tracking-[0.04em] text-fg-muted',
|
|
70
|
+
'[text-transform:var(--type-uppercase)]',
|
|
71
|
+
)}
|
|
72
|
+
role="contentinfo"
|
|
73
|
+
aria-label="Status bar"
|
|
74
|
+
>
|
|
75
|
+
<span className={cn('flex items-center gap-1.5 font-medium', wsTone)}>
|
|
76
|
+
{/* `pip` is the handoff hook for the per-theme blinking dot
|
|
77
|
+
(Surveillance fires `surv-blink`, Cyber fires `cyber-blink`). */}
|
|
78
|
+
<span
|
|
79
|
+
className={cn(
|
|
80
|
+
'pip inline-block h-1.5 w-1.5 rounded-full',
|
|
81
|
+
isReady ? 'bg-success animate-pulse' : isConnected ? 'bg-warning' : 'bg-destructive',
|
|
82
|
+
)}
|
|
83
|
+
/>
|
|
84
|
+
{wsStatus}
|
|
85
|
+
</span>
|
|
86
|
+
|
|
87
|
+
<Sep />
|
|
88
|
+
|
|
89
|
+
<span title={workflowName ?? 'No workflow'}>
|
|
90
|
+
WF: <span className="text-fg-default">{workflowName ?? '—'}</span>
|
|
91
|
+
</span>
|
|
92
|
+
|
|
93
|
+
<Sep />
|
|
94
|
+
|
|
95
|
+
<span>NODES: <span className="text-fg-default">{nodeCount ?? 0}</span></span>
|
|
96
|
+
|
|
97
|
+
<span className="ml-auto flex items-center gap-3">
|
|
98
|
+
<span>
|
|
99
|
+
THEME: <span className="text-fg-default">{THEME_LABEL[theme]}</span>
|
|
100
|
+
</span>
|
|
101
|
+
<Sep />
|
|
102
|
+
<span className="tabular-nums">{clock}</span>
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default StatusBar;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThemeSwitcher — picks the active visual theme.
|
|
3
|
+
*
|
|
4
|
+
* Renders one DropdownMenu item per theme listed in `AVAILABLE_THEMES`,
|
|
5
|
+
* grouped into System / Utopian / Dystopian sections per the design
|
|
6
|
+
* handoff's dual taxonomy.
|
|
7
|
+
*
|
|
8
|
+
* Adding a new theme: drop a CSS file under client/src/themes/, import
|
|
9
|
+
* it in main.tsx, add the name to `AVAILABLE_THEMES` in ThemeContext,
|
|
10
|
+
* and add an entry to `THEME_META` + the matching `THEME_GROUPS` row
|
|
11
|
+
* below.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import { Check, Palette } from 'lucide-react';
|
|
16
|
+
import {
|
|
17
|
+
DropdownMenu,
|
|
18
|
+
DropdownMenuContent,
|
|
19
|
+
DropdownMenuItem,
|
|
20
|
+
DropdownMenuLabel,
|
|
21
|
+
DropdownMenuSeparator,
|
|
22
|
+
DropdownMenuTrigger,
|
|
23
|
+
} from '@/components/ui/dropdown-menu';
|
|
24
|
+
import { Button } from '@/components/ui/button';
|
|
25
|
+
import { useTheme, type ThemeName } from '../../contexts/ThemeContext';
|
|
26
|
+
|
|
27
|
+
interface ThemeMeta {
|
|
28
|
+
label: string;
|
|
29
|
+
blurb: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const THEME_META: Record<ThemeName, ThemeMeta> = {
|
|
33
|
+
light: { label: 'Light', blurb: 'Clean default' },
|
|
34
|
+
dark: { label: 'Dark', blurb: 'Solarized + Dracula' },
|
|
35
|
+
renaissance: { label: 'Renaissance', blurb: 'Illuminated codex' },
|
|
36
|
+
greek: { label: 'Greek', blurb: 'Sun-bleached marble agora' },
|
|
37
|
+
edo: { label: 'Edo', blurb: 'Washi paper, sumi ink' },
|
|
38
|
+
steampunk: { label: 'Steampunk', blurb: 'Riveted brass + leather' },
|
|
39
|
+
atomic: { label: 'Atomic Modern', blurb: 'Eames mid-century' },
|
|
40
|
+
cyber: { label: 'Cyber-Tyranny', blurb: 'Neon night market' },
|
|
41
|
+
wasteland: { label: 'Wasteland', blurb: 'Irradiated scrap + rust' },
|
|
42
|
+
rot: { label: 'Necromantic Rot',blurb: 'Moss-overgrown crypt' },
|
|
43
|
+
plague: { label: 'Plague City', blurb: 'Quarantine notices' },
|
|
44
|
+
surveillance: { label: 'Surveillance', blurb: 'Institutional CCTV' },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface ThemeGroup {
|
|
48
|
+
heading: string;
|
|
49
|
+
themes: readonly ThemeName[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Order of sections + members within each section. Drives the
|
|
53
|
+
* dropdown layout; AVAILABLE_THEMES flat list is unchanged. */
|
|
54
|
+
const THEME_GROUPS: readonly ThemeGroup[] = [
|
|
55
|
+
{ heading: 'System', themes: ['light', 'dark'] },
|
|
56
|
+
{ heading: 'Utopian', themes: ['renaissance', 'greek', 'edo', 'steampunk', 'atomic'] },
|
|
57
|
+
{ heading: 'Dystopian', themes: ['cyber', 'wasteland', 'rot', 'plague', 'surveillance'] },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
export const ThemeSwitcher: React.FC<{ className?: string }> = ({ className }) => {
|
|
61
|
+
const { theme, setTheme } = useTheme();
|
|
62
|
+
const activeMeta = THEME_META[theme];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<DropdownMenu>
|
|
66
|
+
<DropdownMenuTrigger asChild>
|
|
67
|
+
<Button
|
|
68
|
+
variant="outline"
|
|
69
|
+
size="icon-sm"
|
|
70
|
+
title={`Theme: ${activeMeta.label}`}
|
|
71
|
+
className="border-action-secret-border bg-action-secret-soft text-action-secret hover:bg-action-secret/25"
|
|
72
|
+
>
|
|
73
|
+
<Palette />
|
|
74
|
+
</Button>
|
|
75
|
+
</DropdownMenuTrigger>
|
|
76
|
+
<DropdownMenuContent align="end" className={className ?? 'min-w-[240px]'}>
|
|
77
|
+
{THEME_GROUPS.map((group, groupIdx) => (
|
|
78
|
+
<React.Fragment key={group.heading}>
|
|
79
|
+
{groupIdx > 0 && <DropdownMenuSeparator />}
|
|
80
|
+
<DropdownMenuLabel className="text-xs uppercase tracking-wider text-muted-foreground">
|
|
81
|
+
{group.heading}
|
|
82
|
+
</DropdownMenuLabel>
|
|
83
|
+
{group.themes.map((name) => {
|
|
84
|
+
const meta = THEME_META[name];
|
|
85
|
+
const isActive = name === theme;
|
|
86
|
+
return (
|
|
87
|
+
<DropdownMenuItem
|
|
88
|
+
key={name}
|
|
89
|
+
onSelect={() => setTheme(name)}
|
|
90
|
+
className="flex items-center gap-2"
|
|
91
|
+
>
|
|
92
|
+
<span className="flex h-4 w-4 items-center justify-center">
|
|
93
|
+
{isActive && <Check className="h-3.5 w-3.5" />}
|
|
94
|
+
</span>
|
|
95
|
+
<span className="flex-1">
|
|
96
|
+
<span className="block text-sm font-medium">{meta.label}</span>
|
|
97
|
+
<span className="block text-[11px] text-muted-foreground">{meta.blurb}</span>
|
|
98
|
+
</span>
|
|
99
|
+
</DropdownMenuItem>
|
|
100
|
+
);
|
|
101
|
+
})}
|
|
102
|
+
</React.Fragment>
|
|
103
|
+
))}
|
|
104
|
+
</DropdownMenuContent>
|
|
105
|
+
</DropdownMenu>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default ThemeSwitcher;
|
|
@@ -14,8 +14,6 @@ import {
|
|
|
14
14
|
Pencil,
|
|
15
15
|
Settings as SettingsIcon,
|
|
16
16
|
KeyRound,
|
|
17
|
-
Sun,
|
|
18
|
-
Moon,
|
|
19
17
|
LogOut,
|
|
20
18
|
Save,
|
|
21
19
|
Play,
|
|
@@ -43,14 +41,17 @@ import {
|
|
|
43
41
|
} from '@/components/ui/dropdown-menu';
|
|
44
42
|
import { Button } from '@/components/ui/button';
|
|
45
43
|
import { ActionButton } from '@/components/ui/action-button';
|
|
44
|
+
import { ThemeSwitcher } from '@/components/ui/ThemeSwitcher';
|
|
46
45
|
import { cn } from '@/lib/utils';
|
|
47
|
-
import { useTheme } from '../../contexts/ThemeContext';
|
|
48
46
|
import { useAuth } from '../../contexts/AuthContext';
|
|
49
47
|
import { useApiKeys, GlobalModelState } from '../../hooks/useApiKeys';
|
|
50
|
-
import {
|
|
48
|
+
import { useStoredProviderCount } from '../../hooks/useCatalogueQuery';
|
|
51
49
|
import { AI_PROVIDER_META } from '../icons/AIProviderIcons';
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
// New-contract token: --border-default ↔ Tailwind utility `bg-border-default`.
|
|
52
|
+
// Same colour as `bg-border` under light/dark, retints automatically under
|
|
53
|
+
// renaissance / cyber via the per-theme [data-theme="..."] block.
|
|
54
|
+
const Divider = () => <div className="mx-1 h-6 w-px bg-border-default" />;
|
|
54
55
|
|
|
55
56
|
interface TopToolbarProps {
|
|
56
57
|
workflowName: string;
|
|
@@ -107,16 +108,17 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
107
108
|
}) => {
|
|
108
109
|
const [isEditing, setIsEditing] = useState(false);
|
|
109
110
|
const [tempName, setTempName] = useState(workflowName);
|
|
110
|
-
const { isDarkMode, toggleTheme } = useTheme();
|
|
111
111
|
const { user, logout } = useAuth();
|
|
112
112
|
|
|
113
113
|
// Global Model Selector state
|
|
114
114
|
const { getValidatedAiProviders, saveGlobalModel, isConnected: apiKeysConnected } = useApiKeys();
|
|
115
|
-
const { apiKeyStatuses } = useWebSocket();
|
|
116
115
|
const [globalModelState, setGlobalModelState] = useState<GlobalModelState>({ providers: [], global_provider: null, global_model: null });
|
|
117
116
|
|
|
118
|
-
// Re-fetch validated providers
|
|
119
|
-
|
|
117
|
+
// Re-fetch validated providers whenever the count of stored
|
|
118
|
+
// credentials changes. Read from the catalogue (single source of
|
|
119
|
+
// truth — `provider.stored` flag); the retired
|
|
120
|
+
// `apiKeyStatuses[id].hasKey` mirror duplicated this answer.
|
|
121
|
+
const apiKeyCount = useStoredProviderCount();
|
|
120
122
|
useEffect(() => {
|
|
121
123
|
if (!apiKeysConnected) return;
|
|
122
124
|
getValidatedAiProviders().then(state => setGlobalModelState(state));
|
|
@@ -153,7 +155,15 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
153
155
|
};
|
|
154
156
|
|
|
155
157
|
return (
|
|
156
|
-
|
|
158
|
+
// border-default + bg-bg-panel are the new-contract tokens. They
|
|
159
|
+
// resolve to the existing colours under light/dark (no visual
|
|
160
|
+
// change) but pick up the parchment / void surfaces under
|
|
161
|
+
// renaissance / cyber automatically.
|
|
162
|
+
// `toolbar` is the design-handoff structural class — per-theme CSS
|
|
163
|
+
// attaches panel textures (vellum on Renaissance, scanlines on Cyber,
|
|
164
|
+
// marble veins on Greek, riveted leather on Steampunk, etc.) +
|
|
165
|
+
// border treatments via `:root[data-theme="..."] .toolbar`.
|
|
166
|
+
<div className="toolbar flex h-12 items-center justify-between gap-3 border-b border-border-default bg-bg-panel px-3">
|
|
157
167
|
{/* ---------- Left Section ---------- */}
|
|
158
168
|
<div className="flex items-center gap-1.5">
|
|
159
169
|
<Button
|
|
@@ -178,7 +188,13 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
178
188
|
className="border-action-save-border bg-action-save-soft text-action-save hover:bg-action-save/25"
|
|
179
189
|
>
|
|
180
190
|
<FileText />
|
|
181
|
-
|
|
191
|
+
{/* font-display + tracking-display drive the per-theme display
|
|
192
|
+
font + letter-spacing; uppercase is gated by --type-uppercase
|
|
193
|
+
via the new-contract `tracking-display` token (light/dark
|
|
194
|
+
use 0 + none, renaissance + cyber turn it on). */}
|
|
195
|
+
<span className="font-display tracking-[var(--type-tracking-display)] [text-transform:var(--type-uppercase)]">
|
|
196
|
+
File
|
|
197
|
+
</span>
|
|
182
198
|
<ChevronDown />
|
|
183
199
|
</Button>
|
|
184
200
|
</DropdownMenuTrigger>
|
|
@@ -228,10 +244,17 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
228
244
|
<button
|
|
229
245
|
onClick={handleNameClick}
|
|
230
246
|
title="Click to rename"
|
|
231
|
-
className="flex items-center gap-1.5 rounded-sm bg-transparent px-3 py-1.5 transition-colors hover:bg-
|
|
247
|
+
className="flex items-center gap-1.5 rounded-sm bg-transparent px-3 py-1.5 transition-colors hover:bg-bg-hover"
|
|
232
248
|
>
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
{/* font-display + tracking-display + [text-transform] are
|
|
250
|
+
theme-driven via the new-contract typography tokens. Under
|
|
251
|
+
light/dark the workflow name reads as our regular sans-serif;
|
|
252
|
+
under Renaissance it becomes Cinzel uppercase, under Cyber it
|
|
253
|
+
becomes Major Mono Display uppercase. */}
|
|
254
|
+
<span className="text-sm font-display font-medium tracking-[var(--type-tracking-display)] text-fg-default [text-transform:var(--type-uppercase)]">
|
|
255
|
+
{workflowName}
|
|
256
|
+
</span>
|
|
257
|
+
<Pencil className="h-3 w-3 text-fg-muted" />
|
|
235
258
|
</button>
|
|
236
259
|
)}
|
|
237
260
|
</div>
|
|
@@ -352,15 +375,7 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
352
375
|
<KeyRound />
|
|
353
376
|
</Button>
|
|
354
377
|
|
|
355
|
-
<
|
|
356
|
-
variant="outline"
|
|
357
|
-
size="icon-sm"
|
|
358
|
-
onClick={toggleTheme}
|
|
359
|
-
title={isDarkMode ? 'Switch to Light mode' : 'Switch to Dark mode'}
|
|
360
|
-
className="border-action-secret-border bg-action-secret-soft text-action-secret hover:bg-action-secret/25"
|
|
361
|
-
>
|
|
362
|
-
{isDarkMode ? <Sun /> : <Moon />}
|
|
363
|
-
</Button>
|
|
378
|
+
<ThemeSwitcher />
|
|
364
379
|
|
|
365
380
|
{user && (
|
|
366
381
|
<Button
|
|
@@ -408,10 +423,12 @@ const TopToolbar: React.FC<TopToolbarProps> = ({
|
|
|
408
423
|
Save
|
|
409
424
|
</ActionButton>
|
|
410
425
|
|
|
411
|
-
{/* Status Indicator
|
|
426
|
+
{/* Status Indicator — font-mono tracks the new-contract --font-mono
|
|
427
|
+
so renaissance gets IM Fell English and cyber gets JetBrains
|
|
428
|
+
Mono. Stays system mono under light/dark. */}
|
|
412
429
|
<div
|
|
413
430
|
className={cn(
|
|
414
|
-
'flex items-center gap-2 rounded-sm px-3 py-1 text-xs font-
|
|
431
|
+
'flex items-center gap-2 rounded-sm px-3 py-1 text-xs font-mono',
|
|
415
432
|
hasUnsavedChanges ? 'text-warning' : 'text-success'
|
|
416
433
|
)}
|
|
417
434
|
>
|
|
@@ -40,13 +40,18 @@ const WorkflowCard: React.FC<WorkflowCardProps> = ({
|
|
|
40
40
|
onSelect,
|
|
41
41
|
onDelete,
|
|
42
42
|
}) => (
|
|
43
|
+
// Card: bg-bg-app (page-level surface, sits below panel chrome) +
|
|
44
|
+
// border-default. Selected state lifts to bg-bg-active and gains a
|
|
45
|
+
// 3px accent left edge — matches handoff `.wf-card.selected`.
|
|
43
46
|
<Card
|
|
44
47
|
onClick={onSelect}
|
|
48
|
+
// `wf-card` + `row` co-classes are the handoff structural hooks for
|
|
49
|
+
// per-theme card decorations + the global hover-sound delegate.
|
|
45
50
|
className={cn(
|
|
46
|
-
'group relative mb-2 cursor-pointer p-3 transition-colors',
|
|
51
|
+
'wf-card row group relative mb-2 cursor-pointer border-border-default bg-bg-app p-3 transition-colors',
|
|
47
52
|
isSelected
|
|
48
|
-
? 'border-accent border-l-[3px] bg-
|
|
49
|
-
: 'hover:bg-
|
|
53
|
+
? 'selected border-accent border-l-[3px] bg-bg-active'
|
|
54
|
+
: 'hover:bg-bg-hover'
|
|
50
55
|
)}
|
|
51
56
|
>
|
|
52
57
|
<div className="mb-1 flex items-center gap-2">
|
|
@@ -55,25 +60,29 @@ const WorkflowCard: React.FC<WorkflowCardProps> = ({
|
|
|
55
60
|
'flex h-6 w-6 shrink-0 items-center justify-center rounded-sm border',
|
|
56
61
|
isSelected
|
|
57
62
|
? 'border-accent bg-accent/20 text-accent'
|
|
58
|
-
: 'border-border bg-
|
|
63
|
+
: 'border-border-default bg-bg-elevated text-fg-muted'
|
|
59
64
|
)}
|
|
60
65
|
>
|
|
61
66
|
<Code2 className="h-3 w-3" />
|
|
62
67
|
</div>
|
|
63
68
|
<h4
|
|
64
69
|
className={cn(
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
// Display typography on the workflow name — Cinzel under
|
|
71
|
+
// Renaissance, Major Mono Display under Cyber.
|
|
72
|
+
'm-0 flex-1 truncate font-display text-sm font-medium tracking-[var(--type-tracking-display)] [text-transform:var(--type-uppercase)]',
|
|
73
|
+
isSelected ? 'text-accent' : 'text-fg-default'
|
|
67
74
|
)}
|
|
68
75
|
>
|
|
69
76
|
{workflow.name}
|
|
70
77
|
</h4>
|
|
71
78
|
</div>
|
|
72
79
|
|
|
80
|
+
{/* Metadata uses font-mono — picks up IM Fell English / JetBrains
|
|
81
|
+
Mono / system mono per theme. */}
|
|
73
82
|
<div
|
|
74
83
|
className={cn(
|
|
75
|
-
'flex items-center justify-between text-xs',
|
|
76
|
-
isSelected ? 'text-
|
|
84
|
+
'flex items-center justify-between font-mono text-xs',
|
|
85
|
+
isSelected ? 'text-fg-default' : 'text-fg-muted'
|
|
77
86
|
)}
|
|
78
87
|
>
|
|
79
88
|
<span>{workflow.nodeCount} nodes</span>
|
|
@@ -106,16 +115,23 @@ const WorkflowSidebar: React.FC<WorkflowSidebarProps> = ({
|
|
|
106
115
|
onDeleteWorkflow,
|
|
107
116
|
}) => {
|
|
108
117
|
return (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
118
|
+
// Sidebar shell: bg-bg-panel matches the handoff `.sidebar` token.
|
|
119
|
+
// `sidebar` co-class activates per-theme decorations (panel textures,
|
|
120
|
+
// border treatments) declared under `:root[data-theme="..."] .sidebar`.
|
|
121
|
+
<div className="sidebar flex h-full w-[280px] flex-col overflow-hidden border-r border-border-default bg-bg-panel">
|
|
122
|
+
{/* Header — bg-bg-app drops one elevation step below the panel
|
|
123
|
+
chrome, giving the heading area a subtle "page" backdrop
|
|
124
|
+
that's distinct from the card list. */}
|
|
125
|
+
<div className="border-b border-border-default bg-bg-app px-4 py-5">
|
|
112
126
|
<div className="flex items-center gap-2">
|
|
113
127
|
<div className="flex h-9 w-9 items-center justify-center rounded-md bg-accent/20 text-accent">
|
|
114
128
|
<FolderOpen className="h-4 w-4" />
|
|
115
129
|
</div>
|
|
116
130
|
<div>
|
|
117
|
-
<h3 className="m-0 text-base font-semibold text-
|
|
118
|
-
|
|
131
|
+
<h3 className="m-0 font-display text-base font-semibold tracking-[var(--type-tracking-display)] text-fg-default [text-transform:var(--type-uppercase)]">
|
|
132
|
+
Workflows
|
|
133
|
+
</h3>
|
|
134
|
+
<p className="m-0 font-mono text-xs text-fg-muted">{workflows.length} saved</p>
|
|
119
135
|
</div>
|
|
120
136
|
</div>
|
|
121
137
|
</div>
|
|
@@ -123,11 +139,11 @@ const WorkflowSidebar: React.FC<WorkflowSidebarProps> = ({
|
|
|
123
139
|
{/* List */}
|
|
124
140
|
<div className="flex-1 overflow-y-auto p-3">
|
|
125
141
|
{workflows.length === 0 ? (
|
|
126
|
-
<div className="px-4 py-12 text-center text-sm text-muted
|
|
127
|
-
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-lg border-2 border-dashed border-border bg-
|
|
142
|
+
<div className="px-4 py-12 text-center text-sm text-fg-muted">
|
|
143
|
+
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-lg border-2 border-dashed border-border-default bg-bg-elevated">
|
|
128
144
|
<FilePlus className="h-7 w-7 stroke-1" />
|
|
129
145
|
</div>
|
|
130
|
-
<p className="m-0 font-medium text-
|
|
146
|
+
<p className="m-0 font-medium text-fg-default">No workflows yet</p>
|
|
131
147
|
<p className="mt-2 text-xs leading-relaxed">
|
|
132
148
|
Create your first workflow<br />to get started
|
|
133
149
|
</p>
|