@foresthubai/workflow-builder 0.4.3 → 0.4.5

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 (414) hide show
  1. package/LICENSE +661 -661
  2. package/NOTICE +16 -16
  3. package/README.md +110 -110
  4. package/dist/BuilderLayout.d.ts.map +1 -1
  5. package/dist/BuilderLayout.js +52 -61
  6. package/dist/BuilderLayout.js.map +1 -1
  7. package/dist/Canvas.d.ts.map +1 -1
  8. package/dist/Canvas.js +10 -3
  9. package/dist/Canvas.js.map +1 -1
  10. package/dist/CanvasEditor.d.ts.map +1 -1
  11. package/dist/CanvasEditor.js.map +1 -1
  12. package/dist/CanvasTabsToolbar.d.ts.map +1 -1
  13. package/dist/CanvasTabsToolbar.js +3 -0
  14. package/dist/CanvasTabsToolbar.js.map +1 -1
  15. package/dist/RightConfigPanel.d.ts.map +1 -1
  16. package/dist/RightConfigPanel.js +3 -0
  17. package/dist/RightConfigPanel.js.map +1 -1
  18. package/dist/WorkflowBuilder.d.ts +7 -13
  19. package/dist/WorkflowBuilder.d.ts.map +1 -1
  20. package/dist/WorkflowBuilder.js +25 -13
  21. package/dist/WorkflowBuilder.js.map +1 -1
  22. package/dist/cn.d.ts.map +1 -1
  23. package/dist/cn.js +3 -0
  24. package/dist/cn.js.map +1 -1
  25. package/dist/components/ui/add-button.d.ts.map +1 -1
  26. package/dist/components/ui/add-button.js +3 -0
  27. package/dist/components/ui/add-button.js.map +1 -1
  28. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  29. package/dist/components/ui/alert-dialog.js +3 -0
  30. package/dist/components/ui/alert-dialog.js.map +1 -1
  31. package/dist/components/ui/alert.d.ts.map +1 -1
  32. package/dist/components/ui/alert.js +3 -0
  33. package/dist/components/ui/alert.js.map +1 -1
  34. package/dist/components/ui/badge.d.ts.map +1 -1
  35. package/dist/components/ui/badge.js.map +1 -1
  36. package/dist/components/ui/button.d.ts.map +1 -1
  37. package/dist/components/ui/button.js +3 -0
  38. package/dist/components/ui/button.js.map +1 -1
  39. package/dist/components/ui/card.d.ts.map +1 -1
  40. package/dist/components/ui/card.js +3 -0
  41. package/dist/components/ui/card.js.map +1 -1
  42. package/dist/components/ui/checkbox.d.ts.map +1 -1
  43. package/dist/components/ui/checkbox.js +3 -0
  44. package/dist/components/ui/checkbox.js.map +1 -1
  45. package/dist/components/ui/collapsible.d.ts.map +1 -1
  46. package/dist/components/ui/collapsible.js +3 -0
  47. package/dist/components/ui/collapsible.js.map +1 -1
  48. package/dist/components/ui/command.d.ts +2 -2
  49. package/dist/components/ui/command.d.ts.map +1 -1
  50. package/dist/components/ui/command.js +3 -0
  51. package/dist/components/ui/command.js.map +1 -1
  52. package/dist/components/ui/delete-button.d.ts.map +1 -1
  53. package/dist/components/ui/delete-button.js +3 -0
  54. package/dist/components/ui/delete-button.js.map +1 -1
  55. package/dist/components/ui/dialog.d.ts.map +1 -1
  56. package/dist/components/ui/dialog.js +3 -0
  57. package/dist/components/ui/dialog.js.map +1 -1
  58. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  59. package/dist/components/ui/dropdown-menu.js +3 -0
  60. package/dist/components/ui/dropdown-menu.js.map +1 -1
  61. package/dist/components/ui/input.d.ts +1 -1
  62. package/dist/components/ui/input.d.ts.map +1 -1
  63. package/dist/components/ui/input.js +3 -0
  64. package/dist/components/ui/input.js.map +1 -1
  65. package/dist/components/ui/label.d.ts.map +1 -1
  66. package/dist/components/ui/label.js +3 -0
  67. package/dist/components/ui/label.js.map +1 -1
  68. package/dist/components/ui/readonly-banner.d.ts.map +1 -1
  69. package/dist/components/ui/readonly-banner.js +3 -0
  70. package/dist/components/ui/readonly-banner.js.map +1 -1
  71. package/dist/components/ui/resizable.d.ts +1 -1
  72. package/dist/components/ui/resizable.d.ts.map +1 -1
  73. package/dist/components/ui/resizable.js +3 -0
  74. package/dist/components/ui/resizable.js.map +1 -1
  75. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  76. package/dist/components/ui/scroll-area.js +3 -0
  77. package/dist/components/ui/scroll-area.js.map +1 -1
  78. package/dist/components/ui/select.d.ts.map +1 -1
  79. package/dist/components/ui/select.js +3 -0
  80. package/dist/components/ui/select.js.map +1 -1
  81. package/dist/components/ui/separator.d.ts.map +1 -1
  82. package/dist/components/ui/separator.js +3 -0
  83. package/dist/components/ui/separator.js.map +1 -1
  84. package/dist/components/ui/switch.d.ts.map +1 -1
  85. package/dist/components/ui/switch.js +3 -0
  86. package/dist/components/ui/switch.js.map +1 -1
  87. package/dist/components/ui/textarea.d.ts +1 -1
  88. package/dist/components/ui/textarea.d.ts.map +1 -1
  89. package/dist/components/ui/textarea.js +3 -0
  90. package/dist/components/ui/textarea.js.map +1 -1
  91. package/dist/components/ui/toast.d.ts.map +1 -1
  92. package/dist/components/ui/toast.js +3 -0
  93. package/dist/components/ui/toast.js.map +1 -1
  94. package/dist/components/ui/toaster.d.ts.map +1 -1
  95. package/dist/components/ui/toaster.js +3 -0
  96. package/dist/components/ui/toaster.js.map +1 -1
  97. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  98. package/dist/components/ui/toggle-group.js +3 -0
  99. package/dist/components/ui/toggle-group.js.map +1 -1
  100. package/dist/components/ui/toggle.d.ts.map +1 -1
  101. package/dist/components/ui/toggle.js +3 -0
  102. package/dist/components/ui/toggle.js.map +1 -1
  103. package/dist/components/ui/tooltip.d.ts.map +1 -1
  104. package/dist/components/ui/tooltip.js +3 -0
  105. package/dist/components/ui/tooltip.js.map +1 -1
  106. package/dist/dialogs/NodePickerDialog.d.ts.map +1 -1
  107. package/dist/dialogs/NodePickerDialog.js +3 -0
  108. package/dist/dialogs/NodePickerDialog.js.map +1 -1
  109. package/dist/dialogs/ValidationDialog.d.ts.map +1 -1
  110. package/dist/dialogs/ValidationDialog.js +3 -0
  111. package/dist/dialogs/ValidationDialog.js.map +1 -1
  112. package/dist/graph/BaseNode.d.ts.map +1 -1
  113. package/dist/graph/BaseNode.js +21 -16
  114. package/dist/graph/BaseNode.js.map +1 -1
  115. package/dist/graph/CustomEdge.d.ts.map +1 -1
  116. package/dist/graph/CustomEdge.js +4 -1
  117. package/dist/graph/CustomEdge.js.map +1 -1
  118. package/dist/graph/CustomNode.d.ts.map +1 -1
  119. package/dist/graph/CustomNode.js +3 -0
  120. package/dist/graph/CustomNode.js.map +1 -1
  121. package/dist/graph/FunctionCallNode.d.ts.map +1 -1
  122. package/dist/graph/FunctionCallNode.js.map +1 -1
  123. package/dist/graph/PortHandle.d.ts.map +1 -1
  124. package/dist/graph/PortHandle.js.map +1 -1
  125. package/dist/graph/reactFlowRegistry.d.ts.map +1 -1
  126. package/dist/graph/reactFlowRegistry.js +3 -0
  127. package/dist/graph/reactFlowRegistry.js.map +1 -1
  128. package/dist/hooks/use-toast.d.ts.map +1 -1
  129. package/dist/hooks/use-toast.js +3 -0
  130. package/dist/hooks/use-toast.js.map +1 -1
  131. package/dist/hooks/useAvailableVariables.d.ts.map +1 -1
  132. package/dist/hooks/useAvailableVariables.js +3 -0
  133. package/dist/hooks/useAvailableVariables.js.map +1 -1
  134. package/dist/hooks/useCanvasHistory.d.ts.map +1 -1
  135. package/dist/hooks/useCanvasHistory.js +3 -0
  136. package/dist/hooks/useCanvasHistory.js.map +1 -1
  137. package/dist/hooks/useCanvasTabs.d.ts.map +1 -1
  138. package/dist/hooks/useCanvasTabs.js +3 -0
  139. package/dist/hooks/useCanvasTabs.js.map +1 -1
  140. package/dist/hooks/useFunctionDiagnosticsSync.d.ts.map +1 -1
  141. package/dist/hooks/useFunctionDiagnosticsSync.js +3 -0
  142. package/dist/hooks/useFunctionDiagnosticsSync.js.map +1 -1
  143. package/dist/hooks/useFunctionRegistry.d.ts.map +1 -1
  144. package/dist/hooks/useFunctionRegistry.js +3 -0
  145. package/dist/hooks/useFunctionRegistry.js.map +1 -1
  146. package/dist/hooks/useFunctions.d.ts.map +1 -1
  147. package/dist/hooks/useFunctions.js +3 -0
  148. package/dist/hooks/useFunctions.js.map +1 -1
  149. package/dist/hooks/useGraph.d.ts.map +1 -1
  150. package/dist/hooks/useGraph.js +3 -0
  151. package/dist/hooks/useGraph.js.map +1 -1
  152. package/dist/hooks/useNodeDefinitions.d.ts.map +1 -1
  153. package/dist/hooks/useNodeDefinitions.js +3 -0
  154. package/dist/hooks/useNodeDefinitions.js.map +1 -1
  155. package/dist/hooks/useParamErrors.d.ts.map +1 -1
  156. package/dist/hooks/useParamErrors.js +3 -0
  157. package/dist/hooks/useParamErrors.js.map +1 -1
  158. package/dist/hooks/useResolvedTheme.d.ts.map +1 -1
  159. package/dist/hooks/useResolvedTheme.js +3 -0
  160. package/dist/hooks/useResolvedTheme.js.map +1 -1
  161. package/dist/hooks/useResourceDiagnosticsSync.d.ts.map +1 -1
  162. package/dist/hooks/useResourceDiagnosticsSync.js +3 -0
  163. package/dist/hooks/useResourceDiagnosticsSync.js.map +1 -1
  164. package/dist/hooks/useSuppressThemeTransition.d.ts.map +1 -1
  165. package/dist/hooks/useSuppressThemeTransition.js +3 -0
  166. package/dist/hooks/useSuppressThemeTransition.js.map +1 -1
  167. package/dist/hooks/useWorkflowSerialization.d.ts.map +1 -1
  168. package/dist/hooks/useWorkflowSerialization.js +3 -0
  169. package/dist/hooks/useWorkflowSerialization.js.map +1 -1
  170. package/dist/i18n/index.d.ts.map +1 -1
  171. package/dist/i18n/index.js +3 -0
  172. package/dist/i18n/index.js.map +1 -1
  173. package/dist/index.d.ts +1 -1
  174. package/dist/index.d.ts.map +1 -1
  175. package/dist/index.js +3 -0
  176. package/dist/index.js.map +1 -1
  177. package/dist/inputs/ExpressionInput.d.ts.map +1 -1
  178. package/dist/inputs/ExpressionInput.js +3 -0
  179. package/dist/inputs/ExpressionInput.js.map +1 -1
  180. package/dist/inputs/ParameterEditor.d.ts.map +1 -1
  181. package/dist/inputs/ParameterEditor.js +3 -0
  182. package/dist/inputs/ParameterEditor.js.map +1 -1
  183. package/dist/inputs/PortSection.d.ts.map +1 -1
  184. package/dist/inputs/PortSection.js.map +1 -1
  185. package/dist/mode.d.ts +11 -0
  186. package/dist/mode.d.ts.map +1 -0
  187. package/dist/mode.js +8 -0
  188. package/dist/mode.js.map +1 -0
  189. package/dist/panels/BuilderSidebar.d.ts.map +1 -1
  190. package/dist/panels/BuilderSidebar.js +3 -0
  191. package/dist/panels/BuilderSidebar.js.map +1 -1
  192. package/dist/panels/ChannelConfigPanel.d.ts.map +1 -1
  193. package/dist/panels/ChannelConfigPanel.js +3 -0
  194. package/dist/panels/ChannelConfigPanel.js.map +1 -1
  195. package/dist/panels/ChannelsPanel.d.ts.map +1 -1
  196. package/dist/panels/ChannelsPanel.js +3 -0
  197. package/dist/panels/ChannelsPanel.js.map +1 -1
  198. package/dist/panels/DebugConsolePanel.d.ts.map +1 -1
  199. package/dist/panels/DebugConsolePanel.js +3 -0
  200. package/dist/panels/DebugConsolePanel.js.map +1 -1
  201. package/dist/panels/DebugContextPanel.d.ts.map +1 -1
  202. package/dist/panels/DebugContextPanel.js +3 -0
  203. package/dist/panels/DebugContextPanel.js.map +1 -1
  204. package/dist/panels/DebugExternalIOPanel.d.ts.map +1 -1
  205. package/dist/panels/DebugExternalIOPanel.js +3 -0
  206. package/dist/panels/DebugExternalIOPanel.js.map +1 -1
  207. package/dist/panels/DiagnosticsPanel.d.ts.map +1 -1
  208. package/dist/panels/DiagnosticsPanel.js +3 -0
  209. package/dist/panels/DiagnosticsPanel.js.map +1 -1
  210. package/dist/panels/EdgeConfigPanel.d.ts.map +1 -1
  211. package/dist/panels/EdgeConfigPanel.js +4 -1
  212. package/dist/panels/EdgeConfigPanel.js.map +1 -1
  213. package/dist/panels/FunctionConfigPanel.d.ts.map +1 -1
  214. package/dist/panels/FunctionConfigPanel.js +4 -1
  215. package/dist/panels/FunctionConfigPanel.js.map +1 -1
  216. package/dist/panels/FunctionListPanel.d.ts.map +1 -1
  217. package/dist/panels/FunctionListPanel.js +3 -0
  218. package/dist/panels/FunctionListPanel.js.map +1 -1
  219. package/dist/panels/MemoryConfigPanel.d.ts.map +1 -1
  220. package/dist/panels/MemoryConfigPanel.js +3 -0
  221. package/dist/panels/MemoryConfigPanel.js.map +1 -1
  222. package/dist/panels/MemoryPanel.d.ts.map +1 -1
  223. package/dist/panels/MemoryPanel.js +3 -0
  224. package/dist/panels/MemoryPanel.js.map +1 -1
  225. package/dist/panels/ModelConfigPanel.d.ts.map +1 -1
  226. package/dist/panels/ModelConfigPanel.js +3 -0
  227. package/dist/panels/ModelConfigPanel.js.map +1 -1
  228. package/dist/panels/ModelsPanel.d.ts.map +1 -1
  229. package/dist/panels/ModelsPanel.js +3 -0
  230. package/dist/panels/ModelsPanel.js.map +1 -1
  231. package/dist/panels/NodeConfigPanel.d.ts.map +1 -1
  232. package/dist/panels/NodeConfigPanel.js +4 -1
  233. package/dist/panels/NodeConfigPanel.js.map +1 -1
  234. package/dist/panels/NodeLibrary.d.ts.map +1 -1
  235. package/dist/panels/NodeLibrary.js +4 -1
  236. package/dist/panels/NodeLibrary.js.map +1 -1
  237. package/dist/panels/ResourceConfigPanel.d.ts.map +1 -1
  238. package/dist/panels/ResourceConfigPanel.js +4 -1
  239. package/dist/panels/ResourceConfigPanel.js.map +1 -1
  240. package/dist/panels/ResourceListPanel.d.ts.map +1 -1
  241. package/dist/panels/ResourceListPanel.js +4 -1
  242. package/dist/panels/ResourceListPanel.js.map +1 -1
  243. package/dist/panels/VariableConfigPanel.d.ts.map +1 -1
  244. package/dist/panels/VariableConfigPanel.js +4 -1
  245. package/dist/panels/VariableConfigPanel.js.map +1 -1
  246. package/dist/panels/VariablesPanel.d.ts.map +1 -1
  247. package/dist/panels/VariablesPanel.js +4 -1
  248. package/dist/panels/VariablesPanel.js.map +1 -1
  249. package/dist/stores/canvasStore.d.ts.map +1 -1
  250. package/dist/stores/canvasStore.js +3 -0
  251. package/dist/stores/canvasStore.js.map +1 -1
  252. package/dist/stores/debugStore.d.ts.map +1 -1
  253. package/dist/stores/debugStore.js +3 -0
  254. package/dist/stores/debugStore.js.map +1 -1
  255. package/dist/stores/diagnosticsStore.d.ts.map +1 -1
  256. package/dist/stores/diagnosticsStore.js +3 -0
  257. package/dist/stores/diagnosticsStore.js.map +1 -1
  258. package/dist/stores/editorStore.d.ts +1 -1
  259. package/dist/stores/editorStore.d.ts.map +1 -1
  260. package/dist/stores/editorStore.js +3 -0
  261. package/dist/stores/editorStore.js.map +1 -1
  262. package/dist/utils/categoryConstants.d.ts.map +1 -1
  263. package/dist/utils/categoryConstants.js +3 -0
  264. package/dist/utils/categoryConstants.js.map +1 -1
  265. package/dist/utils/channelOperations.d.ts.map +1 -1
  266. package/dist/utils/channelOperations.js +3 -0
  267. package/dist/utils/channelOperations.js.map +1 -1
  268. package/dist/utils/connectionRules.d.ts +3 -1
  269. package/dist/utils/connectionRules.d.ts.map +1 -1
  270. package/dist/utils/connectionRules.js +11 -16
  271. package/dist/utils/connectionRules.js.map +1 -1
  272. package/dist/utils/functionOperations.d.ts.map +1 -1
  273. package/dist/utils/functionOperations.js +3 -0
  274. package/dist/utils/functionOperations.js.map +1 -1
  275. package/dist/utils/graphOperations.d.ts.map +1 -1
  276. package/dist/utils/graphOperations.js +3 -0
  277. package/dist/utils/graphOperations.js.map +1 -1
  278. package/dist/utils/history.d.ts.map +1 -1
  279. package/dist/utils/history.js +4 -1
  280. package/dist/utils/history.js.map +1 -1
  281. package/dist/utils/memoryOperations.d.ts.map +1 -1
  282. package/dist/utils/memoryOperations.js +3 -0
  283. package/dist/utils/memoryOperations.js.map +1 -1
  284. package/dist/utils/migrateFunctionNodes.d.ts.map +1 -1
  285. package/dist/utils/migrateFunctionNodes.js +3 -0
  286. package/dist/utils/migrateFunctionNodes.js.map +1 -1
  287. package/dist/utils/modelOperations.d.ts.map +1 -1
  288. package/dist/utils/modelOperations.js +3 -0
  289. package/dist/utils/modelOperations.js.map +1 -1
  290. package/dist/utils/paramDisplay.d.ts.map +1 -1
  291. package/dist/utils/paramDisplay.js +3 -0
  292. package/dist/utils/paramDisplay.js.map +1 -1
  293. package/dist/utils/resourceHelpers.d.ts.map +1 -1
  294. package/dist/utils/resourceHelpers.js +3 -0
  295. package/dist/utils/resourceHelpers.js.map +1 -1
  296. package/dist/utils/translation.d.ts.map +1 -1
  297. package/dist/utils/translation.js +3 -0
  298. package/dist/utils/translation.js.map +1 -1
  299. package/dist/utils/variableOperations.d.ts.map +1 -1
  300. package/dist/utils/variableOperations.js +3 -0
  301. package/dist/utils/variableOperations.js.map +1 -1
  302. package/package.json +2 -2
  303. package/src/BuilderLayout.tsx +338 -345
  304. package/src/Canvas.tsx +270 -261
  305. package/src/CanvasEditor.tsx +146 -142
  306. package/src/CanvasTabsToolbar.tsx +180 -176
  307. package/src/RightConfigPanel.tsx +270 -266
  308. package/src/WorkflowBuilder.tsx +423 -408
  309. package/src/cn.ts +10 -6
  310. package/src/components/ui/add-button.tsx +43 -39
  311. package/src/components/ui/alert-dialog.tsx +145 -141
  312. package/src/components/ui/alert.tsx +63 -59
  313. package/src/components/ui/badge.tsx +40 -36
  314. package/src/components/ui/button.tsx +89 -85
  315. package/src/components/ui/card.tsx +83 -79
  316. package/src/components/ui/checkbox.tsx +32 -28
  317. package/src/components/ui/collapsible.tsx +13 -9
  318. package/src/components/ui/command.tsx +157 -153
  319. package/src/components/ui/delete-button.tsx +27 -23
  320. package/src/components/ui/dialog.tsx +129 -125
  321. package/src/components/ui/dropdown-menu.tsx +202 -198
  322. package/src/components/ui/input.tsx +59 -55
  323. package/src/components/ui/label.tsx +28 -24
  324. package/src/components/ui/readonly-banner.tsx +19 -15
  325. package/src/components/ui/resizable.tsx +47 -43
  326. package/src/components/ui/scroll-area.tsx +106 -102
  327. package/src/components/ui/select.tsx +164 -160
  328. package/src/components/ui/separator.tsx +33 -29
  329. package/src/components/ui/switch.tsx +31 -27
  330. package/src/components/ui/textarea.tsx +55 -51
  331. package/src/components/ui/toast.tsx +131 -127
  332. package/src/components/ui/toaster.tsx +37 -33
  333. package/src/components/ui/toggle-group.tsx +63 -59
  334. package/src/components/ui/toggle.tsx +47 -43
  335. package/src/components/ui/tooltip.tsx +36 -32
  336. package/src/dialogs/NodePickerDialog.tsx +88 -84
  337. package/src/dialogs/ValidationDialog.tsx +188 -184
  338. package/src/graph/BaseNode.tsx +563 -557
  339. package/src/graph/CustomEdge.tsx +189 -185
  340. package/src/graph/CustomNode.tsx +20 -16
  341. package/src/graph/FunctionCallNode.tsx +34 -30
  342. package/src/graph/PortHandle.tsx +193 -189
  343. package/src/graph/reactFlowRegistry.ts +30 -26
  344. package/src/hooks/use-toast.ts +129 -125
  345. package/src/hooks/useAvailableVariables.ts +24 -20
  346. package/src/hooks/useCanvasHistory.ts +26 -22
  347. package/src/hooks/useCanvasTabs.ts +172 -168
  348. package/src/hooks/useFunctionDiagnosticsSync.ts +44 -40
  349. package/src/hooks/useFunctionRegistry.ts +30 -26
  350. package/src/hooks/useFunctions.ts +48 -44
  351. package/src/hooks/useGraph.ts +165 -161
  352. package/src/hooks/useNodeDefinitions.ts +86 -82
  353. package/src/hooks/useParamErrors.ts +30 -26
  354. package/src/hooks/useResolvedTheme.ts +34 -30
  355. package/src/hooks/useResourceDiagnosticsSync.ts +62 -58
  356. package/src/hooks/useSuppressThemeTransition.ts +83 -79
  357. package/src/hooks/useWorkflowSerialization.ts +145 -141
  358. package/src/i18n/index.ts +57 -53
  359. package/src/i18n/locales/de.json +501 -501
  360. package/src/i18n/locales/en.json +557 -557
  361. package/src/index.ts +31 -27
  362. package/src/inputs/ExpressionInput.tsx +301 -297
  363. package/src/inputs/ParameterEditor.tsx +519 -515
  364. package/src/inputs/PortSection.tsx +148 -144
  365. package/src/mode.ts +15 -0
  366. package/src/panels/BuilderSidebar.tsx +305 -301
  367. package/src/panels/ChannelConfigPanel.tsx +53 -49
  368. package/src/panels/ChannelsPanel.tsx +32 -28
  369. package/src/panels/DebugConsolePanel.tsx +77 -73
  370. package/src/panels/DebugContextPanel.tsx +81 -77
  371. package/src/panels/DebugExternalIOPanel.tsx +184 -180
  372. package/src/panels/DiagnosticsPanel.tsx +174 -170
  373. package/src/panels/EdgeConfigPanel.tsx +108 -104
  374. package/src/panels/FunctionConfigPanel.tsx +183 -179
  375. package/src/panels/FunctionListPanel.tsx +49 -45
  376. package/src/panels/MemoryConfigPanel.tsx +59 -55
  377. package/src/panels/MemoryPanel.tsx +44 -40
  378. package/src/panels/ModelConfigPanel.tsx +45 -41
  379. package/src/panels/ModelsPanel.tsx +40 -36
  380. package/src/panels/NodeConfigPanel.tsx +634 -630
  381. package/src/panels/NodeLibrary.tsx +292 -288
  382. package/src/panels/ResourceConfigPanel.tsx +136 -132
  383. package/src/panels/ResourceListPanel.tsx +117 -113
  384. package/src/panels/VariableConfigPanel.tsx +165 -161
  385. package/src/panels/VariablesPanel.tsx +149 -145
  386. package/src/stores/canvasStore.test.ts +48 -44
  387. package/src/stores/canvasStore.ts +249 -245
  388. package/src/stores/debugStore.ts +78 -74
  389. package/src/stores/diagnosticsStore.ts +134 -130
  390. package/src/stores/editorStore.ts +194 -190
  391. package/src/styles/index.css +526 -526
  392. package/src/utils/categoryConstants.ts +30 -26
  393. package/src/utils/channelOperations.ts +90 -86
  394. package/src/utils/connectionRules.ts +140 -137
  395. package/src/utils/functionOperations.ts +183 -179
  396. package/src/utils/graphOperations.ts +554 -550
  397. package/src/utils/history.test.ts +92 -0
  398. package/src/utils/history.ts +211 -207
  399. package/src/utils/memoryOperations.ts +61 -57
  400. package/src/utils/migrateFunctionNodes.ts +111 -107
  401. package/src/utils/modelOperations.ts +59 -55
  402. package/src/utils/paramDisplay.ts +75 -71
  403. package/src/utils/resourceHelpers.ts +36 -32
  404. package/src/utils/translation.ts +32 -28
  405. package/src/utils/variableOperations.ts +79 -75
  406. package/tailwind-preset.ts +170 -166
  407. package/dist/lib/utils.d.ts +0 -3
  408. package/dist/lib/utils.d.ts.map +0 -1
  409. package/dist/lib/utils.js +0 -6
  410. package/dist/lib/utils.js.map +0 -1
  411. package/dist/toolbars/CanvasTabsToolbar.d.ts +0 -11
  412. package/dist/toolbars/CanvasTabsToolbar.d.ts.map +0 -1
  413. package/dist/toolbars/CanvasTabsToolbar.js +0 -101
  414. package/dist/toolbars/CanvasTabsToolbar.js.map +0 -1
@@ -1,58 +1,62 @@
1
- import { useEffect } from "react";
2
- import { useEditorStore } from "../stores/editorStore";
3
- import { useDiagnosticsStore } from "../stores/diagnosticsStore";
4
- import type { Diagnostic } from "@foresthubai/workflow-core/diagnostics";
5
-
6
- type EditorState = ReturnType<typeof useEditorStore.getState>;
7
- type DiagnosticsState = ReturnType<typeof useDiagnosticsStore.getState>;
8
-
9
- interface ResourceDiagnosticsSyncConfig<I extends { id: string }> {
10
- /** Pick the resource map (e.g. `s.channels`) off the editor store. */
11
- selectItems: (s: EditorState) => Record<string, I>;
12
- /** Validate one instance into its diagnostics. */
13
- validate: (item: I) => Diagnostic[];
14
- /** Read the matching diagnostics slot (e.g. `d.byChannelId`). */
15
- getStored: (d: DiagnosticsState) => Record<string, Diagnostic[]>;
16
- /** Write one instance's diagnostics. */
17
- set: (d: DiagnosticsState, id: string, diags: Diagnostic[]) => void;
18
- /** Drop one instance's diagnostics. */
19
- clear: (d: DiagnosticsState, id: string) => void;
20
- }
21
-
22
- /**
23
- * Keeps a project-scoped diagnostics slot (`byChannelId` / `byMemoryId` /
24
- * `byModelId`) in sync with the editor's resource map.
25
- *
26
- * These resources are project-scoped, not canvas-scoped, and are only rendered
27
- * visually when their sidebar tab is open. Tying diagnostic writes to card
28
- * lifecycles would mean errors vanish the moment that tab closes, so this hook
29
- * is mounted once at the workflow-builder root and reactively rewrites the store
30
- * whenever the resource map changes.
31
- *
32
- * Lifecycle handled implicitly by the effect:
33
- * - Load → setItems fires effect re-runs diagnostics written
34
- * - Edit → store mutates → effect re-runs → entry replaced
35
- * - Delete → item leaves → orphan branch → entry cleared
36
- * - Unmount store goes down with the app (no cleanup needed)
37
- */
38
- export function useResourceDiagnosticsSync<I extends { id: string }>(config: ResourceDiagnosticsSyncConfig<I>): void {
39
- const items = useEditorStore(config.selectItems);
40
-
41
- useEffect(() => {
42
- const ds = useDiagnosticsStore.getState();
43
-
44
- const seen = new Set<string>();
45
- for (const item of Object.values(items)) {
46
- seen.add(item.id);
47
- config.set(ds, item.id, config.validate(item));
48
- }
49
-
50
- // Drop entries for items that have been deleted.
51
- for (const id of Object.keys(config.getStored(ds))) {
52
- if (!seen.has(id)) config.clear(ds, id);
53
- }
54
- // `config` is recreated each render but only `items` drives a resync; the
55
- // effect reads the latest closure on every run.
56
- // eslint-disable-next-line react-hooks/exhaustive-deps
57
- }, [items]);
58
- }
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2026 ForestHub. All rights reserved.
3
+ // For commercial licensing, contact root@foresthub.ai
4
+
5
+ import { useEffect } from "react";
6
+ import { useEditorStore } from "../stores/editorStore";
7
+ import { useDiagnosticsStore } from "../stores/diagnosticsStore";
8
+ import type { Diagnostic } from "@foresthubai/workflow-core/diagnostics";
9
+
10
+ type EditorState = ReturnType<typeof useEditorStore.getState>;
11
+ type DiagnosticsState = ReturnType<typeof useDiagnosticsStore.getState>;
12
+
13
+ interface ResourceDiagnosticsSyncConfig<I extends { id: string }> {
14
+ /** Pick the resource map (e.g. `s.channels`) off the editor store. */
15
+ selectItems: (s: EditorState) => Record<string, I>;
16
+ /** Validate one instance into its diagnostics. */
17
+ validate: (item: I) => Diagnostic[];
18
+ /** Read the matching diagnostics slot (e.g. `d.byChannelId`). */
19
+ getStored: (d: DiagnosticsState) => Record<string, Diagnostic[]>;
20
+ /** Write one instance's diagnostics. */
21
+ set: (d: DiagnosticsState, id: string, diags: Diagnostic[]) => void;
22
+ /** Drop one instance's diagnostics. */
23
+ clear: (d: DiagnosticsState, id: string) => void;
24
+ }
25
+
26
+ /**
27
+ * Keeps a project-scoped diagnostics slot (`byChannelId` / `byMemoryId` /
28
+ * `byModelId`) in sync with the editor's resource map.
29
+ *
30
+ * These resources are project-scoped, not canvas-scoped, and are only rendered
31
+ * visually when their sidebar tab is open. Tying diagnostic writes to card
32
+ * lifecycles would mean errors vanish the moment that tab closes, so this hook
33
+ * is mounted once at the workflow-builder root and reactively rewrites the store
34
+ * whenever the resource map changes.
35
+ *
36
+ * Lifecycle handled implicitly by the effect:
37
+ * - Load → setItems fires → effect re-runs → diagnostics written
38
+ * - Edit → store mutates → effect re-runs entry replaced
39
+ * - Delete item leaves → orphan branch → entry cleared
40
+ * - Unmount → store goes down with the app (no cleanup needed)
41
+ */
42
+ export function useResourceDiagnosticsSync<I extends { id: string }>(config: ResourceDiagnosticsSyncConfig<I>): void {
43
+ const items = useEditorStore(config.selectItems);
44
+
45
+ useEffect(() => {
46
+ const ds = useDiagnosticsStore.getState();
47
+
48
+ const seen = new Set<string>();
49
+ for (const item of Object.values(items)) {
50
+ seen.add(item.id);
51
+ config.set(ds, item.id, config.validate(item));
52
+ }
53
+
54
+ // Drop entries for items that have been deleted.
55
+ for (const id of Object.keys(config.getStored(ds))) {
56
+ if (!seen.has(id)) config.clear(ds, id);
57
+ }
58
+ // `config` is recreated each render but only `items` drives a resync; the
59
+ // effect reads the latest closure on every run.
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, [items]);
62
+ }
@@ -1,79 +1,83 @@
1
- import { useEffect, useLayoutEffect } from "react";
2
-
3
- import { useResolvedTheme } from "./useResolvedTheme";
4
-
5
- /**
6
- * Makes color-mode switches snap instead of fade.
7
- *
8
- * The embedder toggles the `light` class on `<html>`. Two different mechanisms
9
- * recolor the UI when it does, and both would otherwise animate to the new
10
- * tokens over their transition duration:
11
- *
12
- * 1. CSS-variable cascade components with `transition-all` (canvas tabs, the
13
- * builder sidebar). These recolor the instant the class flips.
14
- * 2. React re-render ReactFlow's controls and node chrome recolor only once
15
- * `useResolvedTheme` re-renders and ReactFlow re-applies its `colorMode`
16
- * class, which commits a tick or two *after* the class flip.
17
- *
18
- * So a fixed one-frame suppression window catches (1) but misses (2). Instead:
19
- *
20
- * - A MutationObserver adds `theme-changing` (CSS kills all transitions under
21
- * it) the moment the class flips — a microtask, before any paint — and forces
22
- * a reflow so the cascade-driven colors commit with no transition.
23
- * - A layout effect keyed on the resolved theme removes it. Parent layout
24
- * effects run after children's, so this fires after ReactFlow has committed
25
- * its `colorMode` change; a reflow first flushes those colors while
26
- * transitions are still suppressed, then we restore them. Tying removal to
27
- * the React commit (not a timer) makes it deterministic regardless of how
28
- * late the re-render lands.
29
- *
30
- * A short fallback timer removes the class even if no re-render follows, so a
31
- * theme-only-affects-CSS flip can never leave transitions permanently off.
32
- *
33
- * Mount once at the builder root.
34
- */
35
- export function useSuppressThemeTransition(): void {
36
- // Re-renders on every color-mode flip; drives the layout effect below.
37
- const theme = useResolvedTheme();
38
-
39
- useEffect(() => {
40
- if (typeof document === "undefined") return;
41
- const root = document.documentElement;
42
-
43
- let wasLight = root.classList.contains("light");
44
- let fallback = 0;
45
-
46
- const observer = new MutationObserver(() => {
47
- const isLight = root.classList.contains("light");
48
- if (isLight === wasLight) return; // class changed for some other reason
49
- wasLight = isLight;
50
-
51
- root.classList.add("theme-changing");
52
- // Force a synchronous reflow so cascade-driven colors commit while
53
- // transitions are disabled, before the browser's next paint.
54
- void root.offsetHeight;
55
-
56
- // Safety net: if no React re-render follows (theme flip touched only CSS),
57
- // the layout effect won't run — drop the class anyway after a beat.
58
- clearTimeout(fallback);
59
- fallback = window.setTimeout(() => root.classList.remove("theme-changing"), 120);
60
- });
61
-
62
- observer.observe(root, { attributes: true, attributeFilter: ["class"] });
63
- return () => {
64
- observer.disconnect();
65
- clearTimeout(fallback);
66
- root.classList.remove("theme-changing");
67
- };
68
- }, []);
69
-
70
- useLayoutEffect(() => {
71
- if (typeof document === "undefined") return;
72
- const root = document.documentElement;
73
- if (!root.classList.contains("theme-changing")) return; // initial mount, no flip
74
- // ReactFlow has committed its colorMode change by now (child layout effects
75
- // run first). Flush those colors under suppression, then restore transitions.
76
- void root.offsetHeight;
77
- root.classList.remove("theme-changing");
78
- }, [theme]);
79
- }
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2026 ForestHub. All rights reserved.
3
+ // For commercial licensing, contact root@foresthub.ai
4
+
5
+ import { useEffect, useLayoutEffect } from "react";
6
+
7
+ import { useResolvedTheme } from "./useResolvedTheme";
8
+
9
+ /**
10
+ * Makes color-mode switches snap instead of fade.
11
+ *
12
+ * The embedder toggles the `light` class on `<html>`. Two different mechanisms
13
+ * recolor the UI when it does, and both would otherwise animate to the new
14
+ * tokens over their transition duration:
15
+ *
16
+ * 1. CSS-variable cascade components with `transition-all` (canvas tabs, the
17
+ * builder sidebar). These recolor the instant the class flips.
18
+ * 2. React re-render ReactFlow's controls and node chrome recolor only once
19
+ * `useResolvedTheme` re-renders and ReactFlow re-applies its `colorMode`
20
+ * class, which commits a tick or two *after* the class flip.
21
+ *
22
+ * So a fixed one-frame suppression window catches (1) but misses (2). Instead:
23
+ *
24
+ * - A MutationObserver adds `theme-changing` (CSS kills all transitions under
25
+ * it) the moment the class flips — a microtask, before any paint and forces
26
+ * a reflow so the cascade-driven colors commit with no transition.
27
+ * - A layout effect keyed on the resolved theme removes it. Parent layout
28
+ * effects run after children's, so this fires after ReactFlow has committed
29
+ * its `colorMode` change; a reflow first flushes those colors while
30
+ * transitions are still suppressed, then we restore them. Tying removal to
31
+ * the React commit (not a timer) makes it deterministic regardless of how
32
+ * late the re-render lands.
33
+ *
34
+ * A short fallback timer removes the class even if no re-render follows, so a
35
+ * theme-only-affects-CSS flip can never leave transitions permanently off.
36
+ *
37
+ * Mount once at the builder root.
38
+ */
39
+ export function useSuppressThemeTransition(): void {
40
+ // Re-renders on every color-mode flip; drives the layout effect below.
41
+ const theme = useResolvedTheme();
42
+
43
+ useEffect(() => {
44
+ if (typeof document === "undefined") return;
45
+ const root = document.documentElement;
46
+
47
+ let wasLight = root.classList.contains("light");
48
+ let fallback = 0;
49
+
50
+ const observer = new MutationObserver(() => {
51
+ const isLight = root.classList.contains("light");
52
+ if (isLight === wasLight) return; // class changed for some other reason
53
+ wasLight = isLight;
54
+
55
+ root.classList.add("theme-changing");
56
+ // Force a synchronous reflow so cascade-driven colors commit while
57
+ // transitions are disabled, before the browser's next paint.
58
+ void root.offsetHeight;
59
+
60
+ // Safety net: if no React re-render follows (theme flip touched only CSS),
61
+ // the layout effect won't run — drop the class anyway after a beat.
62
+ clearTimeout(fallback);
63
+ fallback = window.setTimeout(() => root.classList.remove("theme-changing"), 120);
64
+ });
65
+
66
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
67
+ return () => {
68
+ observer.disconnect();
69
+ clearTimeout(fallback);
70
+ root.classList.remove("theme-changing");
71
+ };
72
+ }, []);
73
+
74
+ useLayoutEffect(() => {
75
+ if (typeof document === "undefined") return;
76
+ const root = document.documentElement;
77
+ if (!root.classList.contains("theme-changing")) return; // initial mount, no flip
78
+ // ReactFlow has committed its colorMode change by now (child layout effects
79
+ // run first). Flush those colors under suppression, then restore transitions.
80
+ void root.offsetHeight;
81
+ root.classList.remove("theme-changing");
82
+ }, [theme]);
83
+ }
@@ -1,141 +1,145 @@
1
- import { useCallback } from "react";
2
- import { serialize, deserialize, type ApiWorkflow, type Workflow, type Canvas } from "@foresthubai/workflow-core/workflow";
3
- import { migrate } from "@foresthubai/workflow-core/migration";
4
- import type { NodeData } from "@foresthubai/workflow-core/node";
5
- import type { EdgeData } from "@foresthubai/workflow-core/edge";
6
- import type { Channel } from "@foresthubai/workflow-core/channel";
7
- import type { Memory } from "@foresthubai/workflow-core/memory";
8
- import type { Model } from "@foresthubai/workflow-core/model";
9
- import { Edge, Node } from "@xyflow/react";
10
- import { clearAllCanvasStores, getOrCreateCanvasStore, getAllCanvasStores, notifyCanvasRegistryChange, MAIN_CANVAS_ID } from "../stores/canvasStore";
11
- import { useEditorStore } from "../stores/editorStore";
12
- import { getReactFlowType } from "../utils/graphOperations";
13
-
14
- /**
15
- * Store-bound wrapper around the headless `serialize`/`deserialize` in
16
- * `@foresthubai/workflow-core/workflow`. All conversion logic lives in core;
17
- * this hook only mediates Zustand I/O.
18
- *
19
- * Core sets each node's outer `type` to the domain node type (e.g.
20
- * "Agent"); React Flow needs the *display* type — `getReactFlowType`
21
- * handles that on import.
22
- */
23
- export function useWorkflowSerialization() {
24
- const importProject = useCallback((workflow: ApiWorkflow): void => {
25
- // Migrate at the load boundary so an older saved document is brought current
26
- // before deserialize ever sees it. A no-op on an already-current document.
27
- const state = deserialize(migrate(workflow));
28
-
29
- clearAllCanvasStores();
30
-
31
- // Bodies canvas stores. The function declarations are already in domain shape
32
- // (FunctionDeclaration) on state.functions; the body is the canvas at the same id.
33
- for (const [canvasId, canvas] of Object.entries(state.canvases)) {
34
- const store = getOrCreateCanvasStore(canvasId);
35
- const rfNodes: Node<NodeData>[] = canvas.nodes.map((n) => ({
36
- id: n.id,
37
- type: getReactFlowType(n.type),
38
- position: n.position,
39
- data: n,
40
- }));
41
- const rfEdges: Edge<EdgeData>[] = canvas.edges.map((e) => ({
42
- id: e.id,
43
- type: e.type,
44
- source: e.source,
45
- sourceHandle: e.sourceHandle,
46
- target: e.target,
47
- targetHandle: e.targetHandle,
48
- ...(e.data ? { data: e.data } : {}),
49
- }));
50
- store.getState().initialize(rfNodes, rfEdges);
51
- // `initialize` rebuilds node-output vars from the nodes. Replace with the
52
- // fully merged set core already computed (includes declared + fnarg).
53
- store.getState().setVariables(() => canvas.variables);
54
- }
55
-
56
- // Reconcile project-scoped resources to exactly what this workflow declares.
57
- // The editor stores are module-level and outlive the previously loaded project,
58
- // so a workflow that declares none of a given kind must still reset it —
59
- // otherwise the prior project's channels/memory/models bleed in (the canvas
60
- // stores get this for free via clearAllCanvasStores above, as does setFunctions).
61
- const { channels, memory, models } = state;
62
- useEditorStore.getState().setChannels(() => channels);
63
- useEditorStore.getState().setMemory(() => memory);
64
- useEditorStore.getState().setModels(() => models);
65
- useEditorStore.getState().setFunctions(() => state.functions);
66
-
67
- // The function canvas stores were created after clearAllCanvasStores' notify,
68
- // so tell WorkflowBuilder to re-subscribe to the new set.
69
- notifyCanvasRegistryChange();
70
- }, []);
71
-
72
- const exportProject = useCallback((): ApiWorkflow => {
73
- return serialize(readStateFromStores());
74
- }, []);
75
-
76
- // Inverse of importProject: reset every (module-level) store to empty so a fresh
77
- // builder never inherits the previously mounted project. Used by the clear() handle
78
- // and on mount when no initialWorkflow is supplied.
79
- const clearProject = useCallback((): void => {
80
- clearAllCanvasStores(); // drops all canvas stores, re-seeds an empty main, notifies
81
- const editor = useEditorStore.getState();
82
- // A stale non-main activeCanvasId would lazily resurrect a phantom function body.
83
- editor.setActiveCanvas(MAIN_CANVAS_ID);
84
- // Project-scoped resources aren't touched by clearAllCanvasStores. Empty only when
85
- // non-empty so an already-clean store doesn't bump mutationCount (as importProject).
86
- editor.setChannels((c) => (Object.keys(c).length ? {} : c));
87
- editor.setMemory((m) => (Object.keys(m).length ? {} : m));
88
- editor.setModels((m) => (Object.keys(m).length ? {} : m));
89
- editor.setFunctions((f) => (Object.keys(f).length ? {} : f));
90
- editor.clearSelection();
91
- }, []);
92
-
93
- return { exportProject, importProject, clearProject };
94
- }
95
-
96
- /**
97
- * Read the editor's live Zustand state into a {@link Workflow} literal.
98
- * Peels the React Flow wrapper without recomputing anything; channels and
99
- * memory files are unprefixed for the core shape.
100
- *
101
- * Exported so the imperative handle can pass live state to
102
- * `validateWorkflowState` without a serialize/deserialize round-trip.
103
- */
104
- export function readStateFromStores(): Workflow {
105
- const canvases: Record<string, Canvas> = {};
106
- for (const [id, store] of Object.entries(getAllCanvasStores())) {
107
- const s = store.getState();
108
- canvases[id] = {
109
- nodes: s.nodes.map((n) => ({ ...n.data, position: n.position })),
110
- edges: s.edges.map((e) => ({
111
- id: e.id,
112
- type: e.type,
113
- source: e.source,
114
- sourceHandle: e.sourceHandle,
115
- target: e.target,
116
- targetHandle: e.targetHandle,
117
- ...(e.data ? { data: e.data } : {}),
118
- })),
119
- variables: s.variables,
120
- };
121
- }
122
-
123
- const channels: Record<string, Channel> = {};
124
- for (const ch of Object.values(useEditorStore.getState().channels)) channels[ch.id] = ch;
125
-
126
- const memory: Record<string, Memory> = {};
127
- for (const m of Object.values(useEditorStore.getState().memory)) memory[m.id] = m;
128
-
129
- const models: Record<string, Model> = {};
130
- for (const m of Object.values(useEditorStore.getState().models)) models[m.id] = m;
131
-
132
- // Function declarations are project-scoped and already in domain shape; the body
133
- // for each is the canvas at the same id (above).
134
- return {
135
- canvases,
136
- functions: useEditorStore.getState().functions,
137
- channels,
138
- memory,
139
- models,
140
- };
141
- }
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2026 ForestHub. All rights reserved.
3
+ // For commercial licensing, contact root@foresthub.ai
4
+
5
+ import { useCallback } from "react";
6
+ import { serialize, deserialize, type ApiWorkflow, type Workflow, type Canvas } from "@foresthubai/workflow-core/workflow";
7
+ import { migrate } from "@foresthubai/workflow-core/migration";
8
+ import type { NodeData } from "@foresthubai/workflow-core/node";
9
+ import type { EdgeData } from "@foresthubai/workflow-core/edge";
10
+ import type { Channel } from "@foresthubai/workflow-core/channel";
11
+ import type { Memory } from "@foresthubai/workflow-core/memory";
12
+ import type { Model } from "@foresthubai/workflow-core/model";
13
+ import { Edge, Node } from "@xyflow/react";
14
+ import { clearAllCanvasStores, getOrCreateCanvasStore, getAllCanvasStores, notifyCanvasRegistryChange, MAIN_CANVAS_ID } from "../stores/canvasStore";
15
+ import { useEditorStore } from "../stores/editorStore";
16
+ import { getReactFlowType } from "../utils/graphOperations";
17
+
18
+ /**
19
+ * Store-bound wrapper around the headless `serialize`/`deserialize` in
20
+ * `@foresthubai/workflow-core/workflow`. All conversion logic lives in core;
21
+ * this hook only mediates Zustand I/O.
22
+ *
23
+ * Core sets each node's outer `type` to the domain node type (e.g.
24
+ * "Agent"); React Flow needs the *display* type — `getReactFlowType`
25
+ * handles that on import.
26
+ */
27
+ export function useWorkflowSerialization() {
28
+ const importProject = useCallback((workflow: ApiWorkflow): void => {
29
+ // Migrate at the load boundary so an older saved document is brought current
30
+ // before deserialize ever sees it. A no-op on an already-current document.
31
+ const state = deserialize(migrate(workflow));
32
+
33
+ clearAllCanvasStores();
34
+
35
+ // Bodies canvas stores. The function declarations are already in domain shape
36
+ // (FunctionDeclaration) on state.functions; the body is the canvas at the same id.
37
+ for (const [canvasId, canvas] of Object.entries(state.canvases)) {
38
+ const store = getOrCreateCanvasStore(canvasId);
39
+ const rfNodes: Node<NodeData>[] = canvas.nodes.map((n) => ({
40
+ id: n.id,
41
+ type: getReactFlowType(n.type),
42
+ position: n.position,
43
+ data: n,
44
+ }));
45
+ const rfEdges: Edge<EdgeData>[] = canvas.edges.map((e) => ({
46
+ id: e.id,
47
+ type: e.type,
48
+ source: e.source,
49
+ sourceHandle: e.sourceHandle,
50
+ target: e.target,
51
+ targetHandle: e.targetHandle,
52
+ ...(e.data ? { data: e.data } : {}),
53
+ }));
54
+ store.getState().initialize(rfNodes, rfEdges);
55
+ // `initialize` rebuilds node-output vars from the nodes. Replace with the
56
+ // fully merged set core already computed (includes declared + fnarg).
57
+ store.getState().setVariables(() => canvas.variables);
58
+ }
59
+
60
+ // Reconcile project-scoped resources to exactly what this workflow declares.
61
+ // The editor stores are module-level and outlive the previously loaded project,
62
+ // so a workflow that declares none of a given kind must still reset it —
63
+ // otherwise the prior project's channels/memory/models bleed in (the canvas
64
+ // stores get this for free via clearAllCanvasStores above, as does setFunctions).
65
+ const { channels, memory, models } = state;
66
+ useEditorStore.getState().setChannels(() => channels);
67
+ useEditorStore.getState().setMemory(() => memory);
68
+ useEditorStore.getState().setModels(() => models);
69
+ useEditorStore.getState().setFunctions(() => state.functions);
70
+
71
+ // The function canvas stores were created after clearAllCanvasStores' notify,
72
+ // so tell WorkflowBuilder to re-subscribe to the new set.
73
+ notifyCanvasRegistryChange();
74
+ }, []);
75
+
76
+ const exportProject = useCallback((): ApiWorkflow => {
77
+ return serialize(readStateFromStores());
78
+ }, []);
79
+
80
+ // Inverse of importProject: reset every (module-level) store to empty so a fresh
81
+ // builder never inherits the previously mounted project. Used by the clear() handle
82
+ // and on mount when no initialWorkflow is supplied.
83
+ const clearProject = useCallback((): void => {
84
+ clearAllCanvasStores(); // drops all canvas stores, re-seeds an empty main, notifies
85
+ const editor = useEditorStore.getState();
86
+ // A stale non-main activeCanvasId would lazily resurrect a phantom function body.
87
+ editor.setActiveCanvas(MAIN_CANVAS_ID);
88
+ // Project-scoped resources aren't touched by clearAllCanvasStores. Empty only when
89
+ // non-empty so an already-clean store doesn't bump mutationCount (as importProject).
90
+ editor.setChannels((c) => (Object.keys(c).length ? {} : c));
91
+ editor.setMemory((m) => (Object.keys(m).length ? {} : m));
92
+ editor.setModels((m) => (Object.keys(m).length ? {} : m));
93
+ editor.setFunctions((f) => (Object.keys(f).length ? {} : f));
94
+ editor.clearSelection();
95
+ }, []);
96
+
97
+ return { exportProject, importProject, clearProject };
98
+ }
99
+
100
+ /**
101
+ * Read the editor's live Zustand state into a {@link Workflow} literal.
102
+ * Peels the React Flow wrapper without recomputing anything; channels and
103
+ * memory files are unprefixed for the core shape.
104
+ *
105
+ * Exported so the imperative handle can pass live state to
106
+ * `validateWorkflowState` without a serialize/deserialize round-trip.
107
+ */
108
+ export function readStateFromStores(): Workflow {
109
+ const canvases: Record<string, Canvas> = {};
110
+ for (const [id, store] of Object.entries(getAllCanvasStores())) {
111
+ const s = store.getState();
112
+ canvases[id] = {
113
+ nodes: s.nodes.map((n) => ({ ...n.data, position: n.position })),
114
+ edges: s.edges.map((e) => ({
115
+ id: e.id,
116
+ type: e.type,
117
+ source: e.source,
118
+ sourceHandle: e.sourceHandle,
119
+ target: e.target,
120
+ targetHandle: e.targetHandle,
121
+ ...(e.data ? { data: e.data } : {}),
122
+ })),
123
+ variables: s.variables,
124
+ };
125
+ }
126
+
127
+ const channels: Record<string, Channel> = {};
128
+ for (const ch of Object.values(useEditorStore.getState().channels)) channels[ch.id] = ch;
129
+
130
+ const memory: Record<string, Memory> = {};
131
+ for (const m of Object.values(useEditorStore.getState().memory)) memory[m.id] = m;
132
+
133
+ const models: Record<string, Model> = {};
134
+ for (const m of Object.values(useEditorStore.getState().models)) models[m.id] = m;
135
+
136
+ // Function declarations are project-scoped and already in domain shape; the body
137
+ // for each is the canvas at the same id (above).
138
+ return {
139
+ canvases,
140
+ functions: useEditorStore.getState().functions,
141
+ channels,
142
+ memory,
143
+ models,
144
+ };
145
+ }