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.
Files changed (393) hide show
  1. package/README.md +143 -107
  2. package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
  3. package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
  4. package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
  5. package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
  6. package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
  7. package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
  8. package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
  9. package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
  10. package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
  11. package/client/dist/assets/index-DQ0nwhec.js +257 -0
  12. package/client/dist/assets/index-DxmbVskS.css +1 -0
  13. package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
  14. package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
  15. package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
  16. package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
  17. package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
  18. package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
  19. package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
  20. package/client/dist/index.html +37 -3
  21. package/client/index.html +28 -1
  22. package/client/package.json +44 -40
  23. package/client/src/App.tsx +2 -0
  24. package/client/src/Dashboard.tsx +157 -45
  25. package/client/src/ParameterPanel.tsx +3 -5
  26. package/client/src/adapters/nodeSpecToDescription.ts +1 -0
  27. package/client/src/assets/icons/NodeIcon.tsx +32 -0
  28. package/client/src/assets/icons/index.ts +4 -0
  29. package/client/src/assets/icons/stripe.svg +1 -0
  30. package/client/src/assets/icons/themedGlyphs.ts +404 -0
  31. package/client/src/components/AIAgentNode.tsx +77 -53
  32. package/client/src/components/GenericNode.tsx +34 -52
  33. package/client/src/components/OutputPanel.tsx +64 -147
  34. package/client/src/components/ParameterRenderer.tsx +5 -3
  35. package/client/src/components/SkillEditorModal.tsx +9 -18
  36. package/client/src/components/SquareNode.tsx +97 -115
  37. package/client/src/components/StartNode.tsx +32 -42
  38. package/client/src/components/SvgFilterDefs.tsx +54 -0
  39. package/client/src/components/TeamMonitorNode.tsx +12 -14
  40. package/client/src/components/ToolkitNode.tsx +35 -60
  41. package/client/src/components/TriggerNode.tsx +43 -77
  42. package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
  43. package/client/src/components/credentials/CredentialsModal.tsx +98 -30
  44. package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
  45. package/client/src/components/credentials/catalogueAdapter.ts +17 -1
  46. package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
  47. package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
  48. package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
  49. package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
  50. package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
  51. package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
  52. package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
  53. package/client/src/components/credentials/types.ts +12 -2
  54. package/client/src/components/credentials/useCredentialPanel.ts +43 -19
  55. package/client/src/components/icons/AIProviderIcons.tsx +16 -0
  56. package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
  57. package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
  58. package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
  59. package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
  60. package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
  61. package/client/src/components/parameterPanel/InputSection.tsx +9 -7
  62. package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
  63. package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
  64. package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
  65. package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
  66. package/client/src/components/ui/AIResultModal.tsx +1 -1
  67. package/client/src/components/ui/CollapsibleSection.tsx +9 -5
  68. package/client/src/components/ui/CommandPalette.tsx +147 -0
  69. package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
  70. package/client/src/components/ui/ComponentItem.tsx +13 -7
  71. package/client/src/components/ui/ComponentPalette.tsx +24 -13
  72. package/client/src/components/ui/ConsolePanel.tsx +19 -11
  73. package/client/src/components/ui/DropCap.tsx +28 -0
  74. package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
  75. package/client/src/components/ui/InputNodesPanel.tsx +1 -1
  76. package/client/src/components/ui/Modal.tsx +38 -6
  77. package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
  78. package/client/src/components/ui/SettingsPanel.tsx +42 -13
  79. package/client/src/components/ui/StatusBar.tsx +108 -0
  80. package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
  81. package/client/src/components/ui/TopToolbar.tsx +42 -25
  82. package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
  83. package/client/src/components/ui/action-button.tsx +40 -15
  84. package/client/src/components/ui/button.tsx +24 -1
  85. package/client/src/components/ui/dropdown-menu.tsx +24 -2
  86. package/client/src/components/ui/input.tsx +19 -2
  87. package/client/src/components/ui/select.tsx +15 -0
  88. package/client/src/components/ui/textarea.tsx +15 -2
  89. package/client/src/contexts/AuthContext.tsx +148 -109
  90. package/client/src/contexts/ThemeContext.tsx +93 -17
  91. package/client/src/contexts/WebSocketContext.tsx +373 -206
  92. package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
  93. package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
  94. package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
  95. package/client/src/hooks/useAppTheme.ts +209 -7
  96. package/client/src/hooks/useAutoSkillEdges.ts +7 -2
  97. package/client/src/hooks/useCatalogueQuery.ts +67 -1
  98. package/client/src/hooks/useDragVariable.ts +1 -1
  99. package/client/src/hooks/useNodeAllowlist.ts +115 -8
  100. package/client/src/hooks/useOnboarding.ts +20 -8
  101. package/client/src/hooks/useParameterPanel.ts +2 -1
  102. package/client/src/hooks/useReactFlowNodes.ts +2 -1
  103. package/client/src/hooks/useSound.ts +185 -0
  104. package/client/src/hooks/useWorkflowManagement.ts +6 -8
  105. package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
  106. package/client/src/index.css +65 -3
  107. package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
  108. package/client/src/lib/aiModelProviders.ts +8 -0
  109. package/client/src/lib/connectionConfig.ts +107 -0
  110. package/client/src/lib/queryPersist.ts +13 -5
  111. package/client/src/lib/sound.ts +393 -0
  112. package/client/src/main.tsx +20 -0
  113. package/client/src/store/useAppStore.ts +26 -0
  114. package/client/src/styles/canvasAnimations.ts +37 -36
  115. package/client/src/styles/theme.ts +36 -20
  116. package/client/src/test/setup.ts +1 -0
  117. package/client/src/themes/atomic.css +253 -0
  118. package/client/src/themes/base.css +373 -0
  119. package/client/src/themes/cyber.css +890 -0
  120. package/client/src/themes/dark.css +70 -0
  121. package/client/src/themes/edo.css +246 -0
  122. package/client/src/themes/greek.css +293 -0
  123. package/client/src/themes/light.css +78 -0
  124. package/client/src/themes/plague.css +253 -0
  125. package/client/src/themes/renaissance.css +727 -0
  126. package/client/src/themes/rot.css +249 -0
  127. package/client/src/themes/steampunk.css +272 -0
  128. package/client/src/themes/surveillance.css +289 -0
  129. package/client/src/themes/wasteland.css +250 -0
  130. package/client/src/types/INodeProperties.ts +5 -0
  131. package/client/src/types/NodeTypes.ts +11 -1
  132. package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
  133. package/client/src/types/cloudEvents.ts +78 -0
  134. package/client/src/vite-env.d.ts +7 -0
  135. package/client/tsconfig.json +1 -1
  136. package/client/vite.config.js +62 -2
  137. package/install.ps1 +1 -1
  138. package/install.sh +1 -1
  139. package/machina/commands/build.py +51 -7
  140. package/machina/pyproject.toml +4 -0
  141. package/machina/supervisor.py +12 -2
  142. package/machina/tree.py +71 -21
  143. package/package.json +4 -4
  144. package/scripts/install.js +16 -1
  145. package/server/config/ai_cli_providers.json +54 -0
  146. package/server/config/credential_providers.json +109 -2
  147. package/server/config/llm_defaults.json +24 -0
  148. package/server/config/model_registry.json +338 -499
  149. package/server/config/node_allowlist.json +16 -1
  150. package/server/config/pricing.json +8 -0
  151. package/server/constants.py +38 -15
  152. package/server/core/container.py +2 -2
  153. package/server/core/credentials_database.py +35 -2
  154. package/server/core/logging.py +4 -3
  155. package/server/main.py +99 -13
  156. package/server/models/node_metadata.py +1 -0
  157. package/server/nodejs/package.json +8 -6
  158. package/server/nodejs/src/index.ts +22 -5
  159. package/server/nodes/README.md +31 -4
  160. package/server/nodes/agent/_inline.py +2 -0
  161. package/server/nodes/agent/_specialized.py +6 -3
  162. package/server/nodes/agent/ai_agent.py +13 -3
  163. package/server/nodes/agent/chat_agent.py +6 -3
  164. package/server/nodes/agent/claude_code_agent.py +287 -75
  165. package/server/nodes/agent/codex_agent.py +239 -0
  166. package/server/nodes/agent/deep_agent.py +3 -3
  167. package/server/nodes/agent/rlm_agent.py +3 -3
  168. package/server/nodes/android/__init__.py +31 -1
  169. package/server/nodes/android/_base.py +9 -5
  170. package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
  171. package/server/nodes/android/_handlers.py +154 -0
  172. package/server/nodes/android/_option_loaders.py +44 -0
  173. package/server/nodes/android/_refresh.py +127 -0
  174. package/server/{services/android → nodes/android/_relay}/client.py +4 -4
  175. package/server/{routers/android.py → nodes/android/_router.py} +27 -8
  176. package/server/nodes/browser/browser.py +2 -2
  177. package/server/nodes/code/_base.py +6 -2
  178. package/server/nodes/code/_claude_code.py +134 -0
  179. package/server/nodes/document/embedding_generator.py +3 -3
  180. package/server/nodes/document/http_scraper.py +3 -3
  181. package/server/nodes/document/vector_store.py +5 -5
  182. package/server/nodes/email/__init__.py +11 -1
  183. package/server/nodes/email/_filters.py +21 -0
  184. package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
  185. package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
  186. package/server/nodes/email/email_read.py +1 -1
  187. package/server/nodes/email/email_receive.py +54 -5
  188. package/server/nodes/email/email_send.py +1 -1
  189. package/server/nodes/filesystem/shell.py +24 -1
  190. package/server/nodes/google/__init__.py +55 -1
  191. package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
  192. package/server/nodes/google/_base.py +2 -2
  193. package/server/nodes/google/_credentials.py +5 -5
  194. package/server/nodes/google/_filters.py +25 -0
  195. package/server/nodes/google/_handlers.py +57 -0
  196. package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
  197. package/server/nodes/google/_option_loaders.py +107 -0
  198. package/server/nodes/google/_refresh.py +66 -0
  199. package/server/nodes/google/_router.py +131 -0
  200. package/server/nodes/google/gmail_receive.py +41 -4
  201. package/server/nodes/groups.py +1 -0
  202. package/server/nodes/location/_credentials.py +45 -1
  203. package/server/{services/maps.py → nodes/location/_service.py} +18 -3
  204. package/server/nodes/location/gmaps_create.py +4 -4
  205. package/server/nodes/location/gmaps_locations.py +4 -4
  206. package/server/nodes/location/gmaps_nearby_places.py +4 -4
  207. package/server/nodes/model/_base.py +8 -3
  208. package/server/nodes/model/_credentials.py +96 -8
  209. package/server/nodes/model/_local_validator.py +345 -0
  210. package/server/nodes/model/lmstudio_chat_model.py +23 -0
  211. package/server/nodes/model/ollama_chat_model.py +25 -0
  212. package/server/nodes/proxy/_usage.py +2 -2
  213. package/server/nodes/proxy/proxy_config.py +14 -14
  214. package/server/nodes/proxy/proxy_request.py +4 -4
  215. package/server/nodes/scraper/_credentials.py +29 -1
  216. package/server/nodes/scraper/apify_actor.py +9 -9
  217. package/server/nodes/scraper/crawlee_scraper.py +5 -5
  218. package/server/nodes/search/brave_search.py +4 -0
  219. package/server/nodes/search/perplexity_search.py +9 -0
  220. package/server/nodes/search/serper_search.py +3 -0
  221. package/server/nodes/skill/simple_memory.py +12 -0
  222. package/server/nodes/social/_base.py +2 -2
  223. package/server/nodes/stripe/__init__.py +46 -0
  224. package/server/nodes/stripe/_credentials.py +33 -0
  225. package/server/nodes/stripe/_handlers.py +270 -0
  226. package/server/nodes/stripe/_install.py +127 -0
  227. package/server/nodes/stripe/_source.py +174 -0
  228. package/server/nodes/stripe/stripe_action.py +81 -0
  229. package/server/nodes/stripe/stripe_receive.py +92 -0
  230. package/server/nodes/telegram/_credentials.py +52 -1
  231. package/server/nodes/telegram/_handlers.py +19 -18
  232. package/server/nodes/telegram/_service.py +134 -32
  233. package/server/nodes/telegram/telegram_send.py +5 -6
  234. package/server/nodes/text/file_handler.py +2 -2
  235. package/server/nodes/text/text_generator.py +2 -2
  236. package/server/nodes/tool/agent_builder.py +630 -0
  237. package/server/nodes/tool/task_manager.py +144 -2
  238. package/server/nodes/twitter/__init__.py +38 -1
  239. package/server/nodes/twitter/_base.py +7 -7
  240. package/server/nodes/twitter/_credentials.py +1 -1
  241. package/server/nodes/twitter/_filters.py +37 -0
  242. package/server/nodes/twitter/_handlers.py +77 -0
  243. package/server/nodes/twitter/_oauth.py +124 -0
  244. package/server/nodes/twitter/_refresh.py +78 -0
  245. package/server/nodes/twitter/_router.py +29 -0
  246. package/server/nodes/twitter/twitter_receive.py +4 -0
  247. package/server/nodes/visuals.json +64 -19
  248. package/server/nodes/whatsapp/__init__.py +45 -5
  249. package/server/nodes/whatsapp/_base.py +3 -3
  250. package/server/nodes/whatsapp/_filters.py +137 -0
  251. package/server/nodes/whatsapp/_handlers.py +167 -0
  252. package/server/nodes/whatsapp/_option_loaders.py +68 -0
  253. package/server/nodes/whatsapp/_refresh.py +62 -0
  254. package/server/nodes/whatsapp/_runtime.py +1 -1
  255. package/server/pyproject.toml +29 -7
  256. package/server/routers/schemas.py +2 -2
  257. package/server/routers/webhook.py +26 -9
  258. package/server/routers/websocket.py +149 -810
  259. package/server/services/ai.py +89 -8
  260. package/server/services/auth.py +220 -43
  261. package/server/services/claude_oauth.py +126 -100
  262. package/server/services/cli_agent/__init__.py +78 -0
  263. package/server/services/cli_agent/_handlers.py +237 -0
  264. package/server/services/cli_agent/config.py +112 -0
  265. package/server/services/cli_agent/factory.py +48 -0
  266. package/server/services/cli_agent/lockfile.py +141 -0
  267. package/server/services/cli_agent/mcp_server.py +482 -0
  268. package/server/services/cli_agent/protocol.py +173 -0
  269. package/server/services/cli_agent/providers/__init__.py +9 -0
  270. package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
  271. package/server/services/cli_agent/providers/google_gemini.py +80 -0
  272. package/server/services/cli_agent/providers/openai_codex.py +310 -0
  273. package/server/services/cli_agent/service.py +607 -0
  274. package/server/services/cli_agent/session.py +618 -0
  275. package/server/services/cli_agent/types.py +227 -0
  276. package/server/services/cli_agent/workflow_tools.py +233 -0
  277. package/server/services/credential_registry.py +26 -1
  278. package/server/services/deployment/manager.py +26 -145
  279. package/server/services/deployment/poll_registry.py +59 -0
  280. package/server/services/event_waiter.py +76 -246
  281. package/server/services/events/__init__.py +54 -0
  282. package/server/services/events/cli.py +78 -0
  283. package/server/services/events/daemon.py +163 -0
  284. package/server/services/events/envelope.py +281 -0
  285. package/server/services/events/lifecycle.py +99 -0
  286. package/server/services/events/oauth_lifecycle.py +534 -0
  287. package/server/services/events/polling.py +60 -0
  288. package/server/services/events/push.py +36 -0
  289. package/server/services/events/source.py +63 -0
  290. package/server/services/events/triggers.py +118 -0
  291. package/server/services/events/verifiers/__init__.py +25 -0
  292. package/server/services/events/verifiers/base.py +28 -0
  293. package/server/services/events/verifiers/github.py +25 -0
  294. package/server/services/events/verifiers/hmac_basic.py +32 -0
  295. package/server/services/events/verifiers/standard_webhooks.py +47 -0
  296. package/server/services/events/verifiers/stripe.py +42 -0
  297. package/server/services/events/webhook.py +105 -0
  298. package/server/services/handlers/tools.py +28 -186
  299. package/server/services/llm/config.py +7 -0
  300. package/server/services/llm/factory.py +8 -2
  301. package/server/services/memory/__init__.py +52 -0
  302. package/server/services/memory/jsonl.py +80 -0
  303. package/server/services/memory/markdown.py +65 -0
  304. package/server/services/memory/state.py +112 -0
  305. package/server/services/memory/vector_store.py +40 -0
  306. package/server/services/model_registry.py +76 -0
  307. package/server/services/node_allowlist.py +71 -15
  308. package/server/services/node_executor.py +2 -2
  309. package/server/services/node_output_schemas.py +21 -10
  310. package/server/services/node_spec.py +1 -1
  311. package/server/services/oauth_utils.py +1 -1
  312. package/server/services/plugin/__init__.py +2 -0
  313. package/server/services/plugin/base.py +44 -2
  314. package/server/services/plugin/credential.py +288 -1
  315. package/server/services/plugin/deps.py +105 -0
  316. package/server/services/plugin/edge_walker.py +12 -4
  317. package/server/services/plugin/oauth.py +381 -0
  318. package/server/services/plugin/polling.py +247 -0
  319. package/server/services/plugin/registry.py +145 -0
  320. package/server/services/plugin/singleton.py +65 -0
  321. package/server/services/plugin/ws.py +81 -0
  322. package/server/services/process_service.py +31 -2
  323. package/server/services/status_broadcaster.py +155 -238
  324. package/server/services/temporal/workflow.py +7 -7
  325. package/server/services/workflow.py +21 -3
  326. package/server/services/ws_handler_registry.py +111 -28
  327. package/server/skills/GUIDE.md +16 -1
  328. package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
  329. package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
  330. package/server/tests/credentials/test_auth_service.py +16 -9
  331. package/server/tests/credentials/test_credential_broadcasts.py +219 -0
  332. package/server/tests/credentials/test_google_oauth.py +6 -6
  333. package/server/tests/credentials/test_oauth_utils.py +1 -1
  334. package/server/tests/credentials/test_twitter_oauth.py +2 -2
  335. package/server/tests/credentials/test_websocket_handlers.py +44 -20
  336. package/server/tests/llm/test_factory.py +1 -0
  337. package/server/tests/llm/test_wiring.py +5 -1
  338. package/server/tests/nodes/_compat.py +24 -24
  339. package/server/tests/nodes/test_agent_builder.py +439 -0
  340. package/server/tests/nodes/test_ai_tools.py +18 -14
  341. package/server/tests/nodes/test_code_fs_process.py +17 -8
  342. package/server/tests/nodes/test_email.py +10 -9
  343. package/server/tests/nodes/test_google_workspace.py +2 -2
  344. package/server/tests/nodes/test_specialized_agents.py +100 -53
  345. package/server/tests/nodes/test_stripe_plugin.py +293 -0
  346. package/server/tests/nodes/test_telegram_social.py +4 -4
  347. package/server/tests/nodes/test_twitter.py +1 -1
  348. package/server/tests/nodes/test_web_automation.py +2 -2
  349. package/server/tests/nodes/test_whatsapp.py +9 -9
  350. package/server/tests/services/cli_agent/__init__.py +0 -0
  351. package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
  352. package/server/tests/services/cli_agent/test_providers.py +358 -0
  353. package/server/tests/services/cli_agent/test_service.py +298 -0
  354. package/server/tests/services/memory/__init__.py +0 -0
  355. package/server/tests/services/memory/test_jsonl.py +188 -0
  356. package/server/tests/services/test_events.py +333 -0
  357. package/server/tests/test_node_spec.py +56 -16
  358. package/server/tests/test_plugin_helpers.py +116 -0
  359. package/server/tests/test_plugin_self_containment.py +486 -0
  360. package/server/tests/test_status_broadcasts.py +425 -0
  361. package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
  362. package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
  363. package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
  364. package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
  365. package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
  366. package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
  367. package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
  368. package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
  369. package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
  370. package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
  371. package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
  372. package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
  373. package/client/dist/assets/index-D9tZfgvi.js +0 -363
  374. package/client/dist/assets/index-al7snTkG.css +0 -1
  375. package/client/src/components/credentials/providers.tsx +0 -177
  376. package/server/routers/google.py +0 -277
  377. package/server/routers/maps.py +0 -142
  378. package/server/routers/twitter.py +0 -365
  379. package/server/services/claude_code_service.py +0 -106
  380. package/server/services/memory.py +0 -159
  381. package/server/services/node_option_loaders/__init__.py +0 -77
  382. package/server/services/node_option_loaders/android_loaders.py +0 -55
  383. package/server/services/node_option_loaders/google_loaders.py +0 -97
  384. package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
  385. package/server/services/twitter_oauth.py +0 -411
  386. package/server/services/websocket_client.py +0 -29
  387. /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
  388. /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
  389. /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
  390. /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
  391. /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
  392. /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
  393. /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
- <div className="mb-4 rounded-md border border-border bg-muted p-4">
64
- <div className="mb-4 flex items-center gap-2 border-b border-border pb-3">
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-foreground">{title}</div>
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-foreground">{label}</div>
84
- <div className="mt-0.5 text-xs text-muted-foreground">{description}</div>
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-foreground">
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-foreground">
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-foreground">Compaction Ratio</div>
301
- <div className="mt-0.5 text-xs text-muted-foreground">
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-foreground">
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-foreground">
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 { useWebSocket } from '../../contexts/WebSocketContext';
48
+ import { useStoredProviderCount } from '../../hooks/useCatalogueQuery';
51
49
  import { AI_PROVIDER_META } from '../icons/AIProviderIcons';
52
50
 
53
- const Divider = () => <div className="mx-1 h-6 w-px bg-border" />;
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 when API key statuses change
119
- const apiKeyCount = Object.values(apiKeyStatuses).filter(s => s.hasKey).length;
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
- <div className="flex h-12 items-center justify-between gap-3 border-b border-border bg-muted px-3">
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
- File
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-card"
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
- <span className="text-sm font-medium text-node-agent">{workflowName}</span>
234
- <Pencil className="h-3 w-3 text-muted-foreground" />
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
- <Button
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-medium',
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-accent/10'
49
- : 'hover:bg-muted'
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-muted text-muted-foreground'
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
- 'm-0 flex-1 truncate text-sm font-medium',
66
- isSelected ? 'text-accent' : 'text-foreground'
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-foreground' : 'text-muted-foreground'
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
- <div className="flex h-full w-[280px] flex-col overflow-hidden border-r border-border bg-card">
110
- {/* Header */}
111
- <div className="border-b border-border bg-muted px-4 py-5">
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-foreground">Workflows</h3>
118
- <p className="m-0 text-xs text-muted-foreground">{workflows.length} saved</p>
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-foreground">
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-muted">
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-foreground">No workflows yet</p>
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>