@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,161 +1,165 @@
1
- import { NodeDefinition, NodeData } from "@foresthubai/workflow-core/node";
2
- import { Connection, Edge, Node } from "@xyflow/react";
3
- import type { EdgeData } from "@foresthubai/workflow-core/edge";
4
- import { useCallback } from "react";
5
- import { getOrCreateCanvasStore, MAIN_CANVAS_ID } from "../stores/canvasStore";
6
- import {
7
- addNodeToStore,
8
- type Clipboard,
9
- connectNodesInStore,
10
- deleteEdgesFromStore,
11
- deleteNodeFromStore,
12
- pasteToStore,
13
- updateEdgeInStore,
14
- updateNodeInStore,
15
- } from "../utils/graphOperations";
16
- import type { EdgeType } from "@foresthubai/workflow-core/edge";
17
- import { useNodeDefinitions } from "./useNodeDefinitions";
18
-
19
- // Shared across all useGraph instances (one per canvas) so copy/paste works
20
- // across canvas switches within a single builder session.
21
- const clipboardRef: { current: Clipboard | null } = { current: null };
22
-
23
- /**
24
- * Hook that provides graph management actions for a specific canvas.
25
- *
26
- * All mutations are:
27
- * - Gated by readOnly (no-op when true)
28
- * - Automatically wrapped in a history checkpoint (undo/redo)
29
- *
30
- * Callers don't need to worry about either concern.
31
- */
32
- export const useGraph = (canvasId: string = MAIN_CANVAS_ID, readOnly: boolean = false) => {
33
- const canvasStore = getOrCreateCanvasStore(canvasId);
34
- const nodes = canvasStore((s) => s.nodes);
35
- const edges = canvasStore((s) => s.edges);
36
- const { withCheckpoint } = canvasStore;
37
-
38
- const { getNodeDefinition } = useNodeDefinitions();
39
-
40
- // Guarded checkpoint: skips when readOnly, otherwise wraps in undo history entry
41
- const guarded = useCallback(
42
- <R>(operation: () => R): R | undefined => {
43
- if (readOnly) return undefined;
44
- return withCheckpoint(operation);
45
- },
46
- [readOnly, withCheckpoint],
47
- );
48
-
49
- const addNode = useCallback(
50
- (nodeDef: NodeDefinition, position?: { x: number; y: number }) => {
51
- return guarded(() => addNodeToStore(canvasStore, nodeDef, position)) ?? null;
52
- },
53
- [canvasStore, guarded],
54
- );
55
-
56
- const updateNode = useCallback(
57
- (nodeId: string, updates: { arguments?: Record<string, unknown>; label?: string }) => {
58
- guarded(() => updateNodeInStore(canvasStore, nodeId, updates));
59
- },
60
- [canvasStore, guarded],
61
- );
62
-
63
- const deleteNode = useCallback(
64
- (nodeId: string) => {
65
- guarded(() => deleteNodeFromStore(canvasStore, nodeId, getNodeDefinition));
66
- },
67
- [canvasStore, getNodeDefinition, guarded],
68
- );
69
-
70
- const onConnect = useCallback(
71
- (connection: Connection): EdgeType | false => {
72
- return guarded(() => connectNodesInStore(canvasStore, connection)) ?? false;
73
- },
74
- [canvasStore, guarded],
75
- );
76
-
77
- const updateEdge = useCallback(
78
- (edgeId: string, updates: Record<string, unknown>) => {
79
- guarded(() => updateEdgeInStore(canvasStore, edgeId, updates));
80
- },
81
- [canvasStore, guarded],
82
- );
83
-
84
- const deleteEdges = useCallback(
85
- (edgeIds: string[]) => {
86
- guarded(() => deleteEdgesFromStore(canvasStore, edgeIds));
87
- },
88
- [canvasStore, guarded],
89
- );
90
-
91
- // Batch delete nodes and edges as a single undo entry
92
- const deleteSelected = useCallback(
93
- (nodeIds: string[], edgeIds: string[]) => {
94
- if (nodeIds.length === 0 && edgeIds.length === 0) return;
95
- guarded(() => {
96
- for (const nodeId of nodeIds) deleteNodeFromStore(canvasStore, nodeId, getNodeDefinition);
97
- if (edgeIds.length > 0) deleteEdgesFromStore(canvasStore, edgeIds);
98
- });
99
- },
100
- [canvasStore, getNodeDefinition, guarded],
101
- );
102
-
103
- // Copy is read-only — no checkpoint, no readOnly gate
104
- const copySelection = useCallback(
105
- (nodeIds: string[]) => {
106
- if (nodeIds.length === 0) return;
107
-
108
- const nodeIdSet = new Set(nodeIds);
109
-
110
- const copiedNodes = nodes
111
- .filter((node) => nodeIdSet.has(node.id))
112
- .map((node) => JSON.parse(JSON.stringify(node)) as Node<NodeData>);
113
-
114
- const copiedEdges = edges
115
- .filter((edge) => nodeIdSet.has(edge.source) && nodeIdSet.has(edge.target))
116
- .map((edge) => JSON.parse(JSON.stringify(edge)) as Edge<EdgeData>);
117
-
118
- clipboardRef.current = { nodes: copiedNodes, edges: copiedEdges };
119
- },
120
- [nodes, edges],
121
- );
122
-
123
- const pasteSelection = useCallback(
124
- (offset?: { x: number; y: number }) => {
125
- const clipboard = clipboardRef.current;
126
- if (!clipboard) return undefined;
127
- return guarded(() => pasteToStore(canvasStore, clipboard, offset, getNodeDefinition));
128
- },
129
- [canvasStore, getNodeDefinition, guarded],
130
- );
131
-
132
- // Batch add node + connect as a single undo entry (used by contextual node picker)
133
- const addNodeAndConnect = useCallback(
134
- (nodeDef: NodeDefinition, position: { x: number; y: number }, connection: Connection): string | null => {
135
- return (
136
- guarded(() => {
137
- const nodeId = addNodeToStore(canvasStore, nodeDef, position);
138
- if (nodeId == null) return null;
139
- connectNodesInStore(canvasStore, { ...connection, target: nodeId });
140
- return nodeId;
141
- }) ?? null
142
- );
143
- },
144
- [canvasStore, guarded],
145
- );
146
-
147
- return {
148
- nodes,
149
- edges,
150
- addNode,
151
- updateNode,
152
- updateEdge,
153
- deleteNode,
154
- deleteEdges,
155
- deleteSelected,
156
- onConnect,
157
- addNodeAndConnect,
158
- copySelection,
159
- pasteSelection,
160
- };
161
- };
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 { NodeDefinition, NodeData } from "@foresthubai/workflow-core/node";
6
+ import { Connection, Edge, Node } from "@xyflow/react";
7
+ import type { EdgeData } from "@foresthubai/workflow-core/edge";
8
+ import { useCallback } from "react";
9
+ import { getOrCreateCanvasStore, MAIN_CANVAS_ID } from "../stores/canvasStore";
10
+ import {
11
+ addNodeToStore,
12
+ type Clipboard,
13
+ connectNodesInStore,
14
+ deleteEdgesFromStore,
15
+ deleteNodeFromStore,
16
+ pasteToStore,
17
+ updateEdgeInStore,
18
+ updateNodeInStore,
19
+ } from "../utils/graphOperations";
20
+ import type { EdgeType } from "@foresthubai/workflow-core/edge";
21
+ import { useNodeDefinitions } from "./useNodeDefinitions";
22
+
23
+ // Shared across all useGraph instances (one per canvas) so copy/paste works
24
+ // across canvas switches within a single builder session.
25
+ const clipboardRef: { current: Clipboard | null } = { current: null };
26
+
27
+ /**
28
+ * Hook that provides graph management actions for a specific canvas.
29
+ *
30
+ * All mutations are:
31
+ * - Gated by readOnly (no-op when true)
32
+ * - Automatically wrapped in a history checkpoint (undo/redo)
33
+ *
34
+ * Callers don't need to worry about either concern.
35
+ */
36
+ export const useGraph = (canvasId: string = MAIN_CANVAS_ID, readOnly: boolean = false) => {
37
+ const canvasStore = getOrCreateCanvasStore(canvasId);
38
+ const nodes = canvasStore((s) => s.nodes);
39
+ const edges = canvasStore((s) => s.edges);
40
+ const { withCheckpoint } = canvasStore;
41
+
42
+ const { getNodeDefinition } = useNodeDefinitions();
43
+
44
+ // Guarded checkpoint: skips when readOnly, otherwise wraps in undo history entry
45
+ const guarded = useCallback(
46
+ <R>(operation: () => R): R | undefined => {
47
+ if (readOnly) return undefined;
48
+ return withCheckpoint(operation);
49
+ },
50
+ [readOnly, withCheckpoint],
51
+ );
52
+
53
+ const addNode = useCallback(
54
+ (nodeDef: NodeDefinition, position?: { x: number; y: number }) => {
55
+ return guarded(() => addNodeToStore(canvasStore, nodeDef, position)) ?? null;
56
+ },
57
+ [canvasStore, guarded],
58
+ );
59
+
60
+ const updateNode = useCallback(
61
+ (nodeId: string, updates: { arguments?: Record<string, unknown>; label?: string }) => {
62
+ guarded(() => updateNodeInStore(canvasStore, nodeId, updates));
63
+ },
64
+ [canvasStore, guarded],
65
+ );
66
+
67
+ const deleteNode = useCallback(
68
+ (nodeId: string) => {
69
+ guarded(() => deleteNodeFromStore(canvasStore, nodeId, getNodeDefinition));
70
+ },
71
+ [canvasStore, getNodeDefinition, guarded],
72
+ );
73
+
74
+ const onConnect = useCallback(
75
+ (connection: Connection): EdgeType | false => {
76
+ return guarded(() => connectNodesInStore(canvasStore, connection)) ?? false;
77
+ },
78
+ [canvasStore, guarded],
79
+ );
80
+
81
+ const updateEdge = useCallback(
82
+ (edgeId: string, updates: Record<string, unknown>) => {
83
+ guarded(() => updateEdgeInStore(canvasStore, edgeId, updates));
84
+ },
85
+ [canvasStore, guarded],
86
+ );
87
+
88
+ const deleteEdges = useCallback(
89
+ (edgeIds: string[]) => {
90
+ guarded(() => deleteEdgesFromStore(canvasStore, edgeIds));
91
+ },
92
+ [canvasStore, guarded],
93
+ );
94
+
95
+ // Batch delete nodes and edges as a single undo entry
96
+ const deleteSelected = useCallback(
97
+ (nodeIds: string[], edgeIds: string[]) => {
98
+ if (nodeIds.length === 0 && edgeIds.length === 0) return;
99
+ guarded(() => {
100
+ for (const nodeId of nodeIds) deleteNodeFromStore(canvasStore, nodeId, getNodeDefinition);
101
+ if (edgeIds.length > 0) deleteEdgesFromStore(canvasStore, edgeIds);
102
+ });
103
+ },
104
+ [canvasStore, getNodeDefinition, guarded],
105
+ );
106
+
107
+ // Copy is read-only — no checkpoint, no readOnly gate
108
+ const copySelection = useCallback(
109
+ (nodeIds: string[]) => {
110
+ if (nodeIds.length === 0) return;
111
+
112
+ const nodeIdSet = new Set(nodeIds);
113
+
114
+ const copiedNodes = nodes
115
+ .filter((node) => nodeIdSet.has(node.id))
116
+ .map((node) => JSON.parse(JSON.stringify(node)) as Node<NodeData>);
117
+
118
+ const copiedEdges = edges
119
+ .filter((edge) => nodeIdSet.has(edge.source) && nodeIdSet.has(edge.target))
120
+ .map((edge) => JSON.parse(JSON.stringify(edge)) as Edge<EdgeData>);
121
+
122
+ clipboardRef.current = { nodes: copiedNodes, edges: copiedEdges };
123
+ },
124
+ [nodes, edges],
125
+ );
126
+
127
+ const pasteSelection = useCallback(
128
+ (offset?: { x: number; y: number }) => {
129
+ const clipboard = clipboardRef.current;
130
+ if (!clipboard) return undefined;
131
+ return guarded(() => pasteToStore(canvasStore, clipboard, offset, getNodeDefinition));
132
+ },
133
+ [canvasStore, getNodeDefinition, guarded],
134
+ );
135
+
136
+ // Batch add node + connect as a single undo entry (used by contextual node picker)
137
+ const addNodeAndConnect = useCallback(
138
+ (nodeDef: NodeDefinition, position: { x: number; y: number }, connection: Connection): string | null => {
139
+ return (
140
+ guarded(() => {
141
+ const nodeId = addNodeToStore(canvasStore, nodeDef, position);
142
+ if (nodeId == null) return null;
143
+ connectNodesInStore(canvasStore, { ...connection, target: nodeId });
144
+ return nodeId;
145
+ }) ?? null
146
+ );
147
+ },
148
+ [canvasStore, guarded],
149
+ );
150
+
151
+ return {
152
+ nodes,
153
+ edges,
154
+ addNode,
155
+ updateNode,
156
+ updateEdge,
157
+ deleteNode,
158
+ deleteEdges,
159
+ deleteSelected,
160
+ onConnect,
161
+ addNodeAndConnect,
162
+ copySelection,
163
+ pasteSelection,
164
+ };
165
+ };
@@ -1,82 +1,86 @@
1
- import { useMemo, useCallback } from "react";
2
- import i18n from "../i18n";
3
- import { NodeCategory, NodeRegistry, NodeDefinition, NodeData } from "@foresthubai/workflow-core/node";
4
- import { useFunctionRegistry } from "./useFunctionRegistry";
5
- import { toFunctionInfo, type FunctionInfo } from "@foresthubai/workflow-core/function";
6
- import { FunctionCallNode, FunctionNodeDefinition, buildFunctionNodeDef as coreBuildFunctionNodeDef } from "@foresthubai/workflow-core/node";
7
-
8
- /**
9
- * Workflow-builder binding for {@link coreBuildFunctionNodeDef} passes
10
- * `i18n.t` so descriptions are translated. Consumers continue to call
11
- * `buildFunctionNodeDef(fn)` unchanged; core's signature is the pure
12
- * `(fn, t?)` form.
13
- */
14
- export function buildFunctionNodeDef(fn: FunctionInfo): FunctionNodeDefinition {
15
- return coreBuildFunctionNodeDef(fn, i18n.t.bind(i18n));
16
- }
17
-
18
- // Use function registry to provide dynamic node definitions based on available functions
19
- export const useNodeDefinitions = () => {
20
- // Get static node definitions from registry (these never change)
21
- const staticNodeDefs: NodeDefinition[] = NodeRegistry.getAll();
22
-
23
- // Subscribe to function registry (derived from all canvas stores)
24
- const { functions } = useFunctionRegistry();
25
-
26
- // Dynamically create node definitions for each function. The call-site node def is
27
- // built from the flat signature snapshot, so project the domain declaration here.
28
- const functionNodeDefs: FunctionNodeDefinition[] = useMemo(
29
- () => Object.values(functions).map((fn) => buildFunctionNodeDef(toFunctionInfo(fn))),
30
- [functions],
31
- );
32
-
33
- // Get node definition for a node instance (still depending on all functions)
34
- const getNodeDefinition = useCallback(
35
- (node: NodeData): NodeDefinition | undefined => {
36
- if (node.type === "FunctionCall") {
37
- const fnNode = node as FunctionCallNode;
38
- return functionNodeDefs.find((def) => def.type === "FunctionCall" && def.functionInfo.id === fnNode.functionInfo.id);
39
- }
40
- return NodeRegistry.getByType(node.type);
41
- },
42
- [functionNodeDefs],
43
- );
44
-
45
- const getNodeDefinitionsByCategory = useCallback(
46
- (category: NodeCategory) => {
47
- const staticNodes = NodeRegistry.getByCategory(category);
48
- if (category === NodeCategory.Function) {
49
- return [...staticNodes, ...functionNodeDefs];
50
- }
51
- return staticNodes;
52
- },
53
- [functionNodeDefs],
54
- );
55
-
56
- const getAllCategories = useCallback((): NodeCategory[] => {
57
- const staticCategories = NodeRegistry.getAllCategories();
58
- const allCategories = new Set([...staticCategories]);
59
- if (functionNodeDefs.length > 0) {
60
- allCategories.add(NodeCategory.Function);
61
- }
62
- const categoryOrder = [
63
- NodeCategory.Trigger,
64
- NodeCategory.Input,
65
- NodeCategory.Logic,
66
- NodeCategory.Data,
67
- NodeCategory.Function,
68
- NodeCategory.AI,
69
- NodeCategory.Tool,
70
- NodeCategory.Output,
71
- ];
72
- return categoryOrder.filter((cat) => allCategories.has(cat));
73
- }, [functionNodeDefs]);
74
-
75
- return {
76
- nodeDefinitions: [...staticNodeDefs, ...functionNodeDefs],
77
- getAllCategories,
78
- getNodeDefinition,
79
- getNodeDefinitionsByCategory,
80
- };
81
- };
82
-
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 { useMemo, useCallback } from "react";
6
+ import i18n from "../i18n";
7
+ import { NodeCategory, NodeRegistry, NodeDefinition, NodeData } from "@foresthubai/workflow-core/node";
8
+ import { useFunctionRegistry } from "./useFunctionRegistry";
9
+ import { toFunctionInfo, type FunctionInfo } from "@foresthubai/workflow-core/function";
10
+ import { FunctionCallNode, FunctionNodeDefinition, buildFunctionNodeDef as coreBuildFunctionNodeDef } from "@foresthubai/workflow-core/node";
11
+
12
+ /**
13
+ * Workflow-builder binding for {@link coreBuildFunctionNodeDef} — passes
14
+ * `i18n.t` so descriptions are translated. Consumers continue to call
15
+ * `buildFunctionNodeDef(fn)` unchanged; core's signature is the pure
16
+ * `(fn, t?)` form.
17
+ */
18
+ export function buildFunctionNodeDef(fn: FunctionInfo): FunctionNodeDefinition {
19
+ return coreBuildFunctionNodeDef(fn, i18n.t.bind(i18n));
20
+ }
21
+
22
+ // Use function registry to provide dynamic node definitions based on available functions
23
+ export const useNodeDefinitions = () => {
24
+ // Get static node definitions from registry (these never change)
25
+ const staticNodeDefs: NodeDefinition[] = NodeRegistry.getAll();
26
+
27
+ // Subscribe to function registry (derived from all canvas stores)
28
+ const { functions } = useFunctionRegistry();
29
+
30
+ // Dynamically create node definitions for each function. The call-site node def is
31
+ // built from the flat signature snapshot, so project the domain declaration here.
32
+ const functionNodeDefs: FunctionNodeDefinition[] = useMemo(
33
+ () => Object.values(functions).map((fn) => buildFunctionNodeDef(toFunctionInfo(fn))),
34
+ [functions],
35
+ );
36
+
37
+ // Get node definition for a node instance (still depending on all functions)
38
+ const getNodeDefinition = useCallback(
39
+ (node: NodeData): NodeDefinition | undefined => {
40
+ if (node.type === "FunctionCall") {
41
+ const fnNode = node as FunctionCallNode;
42
+ return functionNodeDefs.find((def) => def.type === "FunctionCall" && def.functionInfo.id === fnNode.functionInfo.id);
43
+ }
44
+ return NodeRegistry.getByType(node.type);
45
+ },
46
+ [functionNodeDefs],
47
+ );
48
+
49
+ const getNodeDefinitionsByCategory = useCallback(
50
+ (category: NodeCategory) => {
51
+ const staticNodes = NodeRegistry.getByCategory(category);
52
+ if (category === NodeCategory.Function) {
53
+ return [...staticNodes, ...functionNodeDefs];
54
+ }
55
+ return staticNodes;
56
+ },
57
+ [functionNodeDefs],
58
+ );
59
+
60
+ const getAllCategories = useCallback((): NodeCategory[] => {
61
+ const staticCategories = NodeRegistry.getAllCategories();
62
+ const allCategories = new Set([...staticCategories]);
63
+ if (functionNodeDefs.length > 0) {
64
+ allCategories.add(NodeCategory.Function);
65
+ }
66
+ const categoryOrder = [
67
+ NodeCategory.Trigger,
68
+ NodeCategory.Input,
69
+ NodeCategory.Logic,
70
+ NodeCategory.Data,
71
+ NodeCategory.Function,
72
+ NodeCategory.AI,
73
+ NodeCategory.Tool,
74
+ NodeCategory.Output,
75
+ ];
76
+ return categoryOrder.filter((cat) => allCategories.has(cat));
77
+ }, [functionNodeDefs]);
78
+
79
+ return {
80
+ nodeDefinitions: [...staticNodeDefs, ...functionNodeDefs],
81
+ getAllCategories,
82
+ getNodeDefinition,
83
+ getNodeDefinitionsByCategory,
84
+ };
85
+ };
86
+
@@ -1,26 +1,30 @@
1
- import { useMemo } from "react";
2
- import type { Diagnostic } from "@foresthubai/workflow-core/diagnostics";
3
-
4
- /**
5
- * Build a per-parameter error map (`paramId → messages`) from a resource's
6
- * diagnostics, keeping only `error`-severity entries. Shared by every config
7
- * panel that renders a parameter list (node / edge / channel / memory / model)
8
- * so the inline Map-building loop lives in exactly one place.
9
- *
10
- * Memoized on `diags`, so callers can read it from the diagnostics store with a
11
- * plain selector and pass the result straight through to `<ParameterEditor>`.
12
- */
13
- export function useParamErrors(diags: Diagnostic[] | undefined): Map<string, string[]> {
14
- return useMemo(() => {
15
- const map = new Map<string, string[]>();
16
- if (!diags) return map;
17
- for (const d of diags) {
18
- if (d.paramId && d.severity === "error") {
19
- const arr = map.get(d.paramId);
20
- if (arr) arr.push(d.message);
21
- else map.set(d.paramId, [d.message]);
22
- }
23
- }
24
- return map;
25
- }, [diags]);
26
- }
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 { useMemo } from "react";
6
+ import type { Diagnostic } from "@foresthubai/workflow-core/diagnostics";
7
+
8
+ /**
9
+ * Build a per-parameter error map (`paramId → messages`) from a resource's
10
+ * diagnostics, keeping only `error`-severity entries. Shared by every config
11
+ * panel that renders a parameter list (node / edge / channel / memory / model)
12
+ * so the inline Map-building loop lives in exactly one place.
13
+ *
14
+ * Memoized on `diags`, so callers can read it from the diagnostics store with a
15
+ * plain selector and pass the result straight through to `<ParameterEditor>`.
16
+ */
17
+ export function useParamErrors(diags: Diagnostic[] | undefined): Map<string, string[]> {
18
+ return useMemo(() => {
19
+ const map = new Map<string, string[]>();
20
+ if (!diags) return map;
21
+ for (const d of diags) {
22
+ if (d.paramId && d.severity === "error") {
23
+ const arr = map.get(d.paramId);
24
+ if (arr) arr.push(d.message);
25
+ else map.set(d.paramId, [d.message]);
26
+ }
27
+ }
28
+ return map;
29
+ }, [diags]);
30
+ }
@@ -1,30 +1,34 @@
1
- import { useEffect, useState } from "react";
2
-
3
- /**
4
- * Returns "light" if `<html>` has the `light` class, else "dark". Subscribes to
5
- * MutationObserver so toggles by the embedder propagate immediately.
6
- *
7
- * Dark is the default — the builder's CSS puts dark tokens on `:root` and
8
- * overrides them under `.light`. This hook exists so things that need an
9
- * explicit value notably ReactFlow's `colorMode` prop — stay in sync.
10
- */
11
- export function useResolvedTheme(): "dark" | "light" {
12
- const [theme, setTheme] = useState<"dark" | "light">(() => detect());
13
-
14
- useEffect(() => {
15
- if (typeof document === "undefined") return;
16
- const root = document.documentElement;
17
- const observer = new MutationObserver(() => setTheme(detect()));
18
- observer.observe(root, { attributes: true, attributeFilter: ["class"] });
19
- // Initial sync in case it changed between mount and effect.
20
- setTheme(detect());
21
- return () => observer.disconnect();
22
- }, []);
23
-
24
- return theme;
25
- }
26
-
27
- function detect(): "dark" | "light" {
28
- if (typeof document === "undefined") return "dark";
29
- return document.documentElement.classList.contains("light") ? "light" : "dark";
30
- }
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, useState } from "react";
6
+
7
+ /**
8
+ * Returns "light" if `<html>` has the `light` class, else "dark". Subscribes to
9
+ * MutationObserver so toggles by the embedder propagate immediately.
10
+ *
11
+ * Dark is the default — the builder's CSS puts dark tokens on `:root` and
12
+ * overrides them under `.light`. This hook exists so things that need an
13
+ * explicit value — notably ReactFlow's `colorMode` prop — stay in sync.
14
+ */
15
+ export function useResolvedTheme(): "dark" | "light" {
16
+ const [theme, setTheme] = useState<"dark" | "light">(() => detect());
17
+
18
+ useEffect(() => {
19
+ if (typeof document === "undefined") return;
20
+ const root = document.documentElement;
21
+ const observer = new MutationObserver(() => setTheme(detect()));
22
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
23
+ // Initial sync in case it changed between mount and effect.
24
+ setTheme(detect());
25
+ return () => observer.disconnect();
26
+ }, []);
27
+
28
+ return theme;
29
+ }
30
+
31
+ function detect(): "dark" | "light" {
32
+ if (typeof document === "undefined") return "dark";
33
+ return document.documentElement.classList.contains("light") ? "light" : "dark";
34
+ }