@foresthubai/workflow-builder 0.4.4 → 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 (396) hide show
  1. package/dist/BuilderLayout.d.ts.map +1 -1
  2. package/dist/BuilderLayout.js +52 -61
  3. package/dist/BuilderLayout.js.map +1 -1
  4. package/dist/Canvas.d.ts.map +1 -1
  5. package/dist/Canvas.js +10 -3
  6. package/dist/Canvas.js.map +1 -1
  7. package/dist/CanvasEditor.d.ts.map +1 -1
  8. package/dist/CanvasEditor.js.map +1 -1
  9. package/dist/CanvasTabsToolbar.d.ts.map +1 -1
  10. package/dist/CanvasTabsToolbar.js +3 -0
  11. package/dist/CanvasTabsToolbar.js.map +1 -1
  12. package/dist/RightConfigPanel.d.ts.map +1 -1
  13. package/dist/RightConfigPanel.js +3 -0
  14. package/dist/RightConfigPanel.js.map +1 -1
  15. package/dist/WorkflowBuilder.d.ts +7 -13
  16. package/dist/WorkflowBuilder.d.ts.map +1 -1
  17. package/dist/WorkflowBuilder.js +25 -13
  18. package/dist/WorkflowBuilder.js.map +1 -1
  19. package/dist/cn.d.ts.map +1 -1
  20. package/dist/cn.js +3 -0
  21. package/dist/cn.js.map +1 -1
  22. package/dist/components/ui/add-button.d.ts.map +1 -1
  23. package/dist/components/ui/add-button.js +3 -0
  24. package/dist/components/ui/add-button.js.map +1 -1
  25. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  26. package/dist/components/ui/alert-dialog.js +3 -0
  27. package/dist/components/ui/alert-dialog.js.map +1 -1
  28. package/dist/components/ui/alert.d.ts.map +1 -1
  29. package/dist/components/ui/alert.js +3 -0
  30. package/dist/components/ui/alert.js.map +1 -1
  31. package/dist/components/ui/badge.d.ts.map +1 -1
  32. package/dist/components/ui/badge.js.map +1 -1
  33. package/dist/components/ui/button.d.ts.map +1 -1
  34. package/dist/components/ui/button.js +3 -0
  35. package/dist/components/ui/button.js.map +1 -1
  36. package/dist/components/ui/card.d.ts.map +1 -1
  37. package/dist/components/ui/card.js +3 -0
  38. package/dist/components/ui/card.js.map +1 -1
  39. package/dist/components/ui/checkbox.d.ts.map +1 -1
  40. package/dist/components/ui/checkbox.js +3 -0
  41. package/dist/components/ui/checkbox.js.map +1 -1
  42. package/dist/components/ui/collapsible.d.ts.map +1 -1
  43. package/dist/components/ui/collapsible.js +3 -0
  44. package/dist/components/ui/collapsible.js.map +1 -1
  45. package/dist/components/ui/command.d.ts.map +1 -1
  46. package/dist/components/ui/command.js +3 -0
  47. package/dist/components/ui/command.js.map +1 -1
  48. package/dist/components/ui/delete-button.d.ts.map +1 -1
  49. package/dist/components/ui/delete-button.js +3 -0
  50. package/dist/components/ui/delete-button.js.map +1 -1
  51. package/dist/components/ui/dialog.d.ts.map +1 -1
  52. package/dist/components/ui/dialog.js +3 -0
  53. package/dist/components/ui/dialog.js.map +1 -1
  54. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  55. package/dist/components/ui/dropdown-menu.js +3 -0
  56. package/dist/components/ui/dropdown-menu.js.map +1 -1
  57. package/dist/components/ui/input.d.ts.map +1 -1
  58. package/dist/components/ui/input.js +3 -0
  59. package/dist/components/ui/input.js.map +1 -1
  60. package/dist/components/ui/label.d.ts.map +1 -1
  61. package/dist/components/ui/label.js +3 -0
  62. package/dist/components/ui/label.js.map +1 -1
  63. package/dist/components/ui/readonly-banner.d.ts.map +1 -1
  64. package/dist/components/ui/readonly-banner.js +3 -0
  65. package/dist/components/ui/readonly-banner.js.map +1 -1
  66. package/dist/components/ui/resizable.d.ts.map +1 -1
  67. package/dist/components/ui/resizable.js +3 -0
  68. package/dist/components/ui/resizable.js.map +1 -1
  69. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  70. package/dist/components/ui/scroll-area.js +3 -0
  71. package/dist/components/ui/scroll-area.js.map +1 -1
  72. package/dist/components/ui/select.d.ts.map +1 -1
  73. package/dist/components/ui/select.js +3 -0
  74. package/dist/components/ui/select.js.map +1 -1
  75. package/dist/components/ui/separator.d.ts.map +1 -1
  76. package/dist/components/ui/separator.js +3 -0
  77. package/dist/components/ui/separator.js.map +1 -1
  78. package/dist/components/ui/switch.d.ts.map +1 -1
  79. package/dist/components/ui/switch.js +3 -0
  80. package/dist/components/ui/switch.js.map +1 -1
  81. package/dist/components/ui/textarea.d.ts.map +1 -1
  82. package/dist/components/ui/textarea.js +3 -0
  83. package/dist/components/ui/textarea.js.map +1 -1
  84. package/dist/components/ui/toast.d.ts.map +1 -1
  85. package/dist/components/ui/toast.js +3 -0
  86. package/dist/components/ui/toast.js.map +1 -1
  87. package/dist/components/ui/toaster.d.ts.map +1 -1
  88. package/dist/components/ui/toaster.js +3 -0
  89. package/dist/components/ui/toaster.js.map +1 -1
  90. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  91. package/dist/components/ui/toggle-group.js +3 -0
  92. package/dist/components/ui/toggle-group.js.map +1 -1
  93. package/dist/components/ui/toggle.d.ts.map +1 -1
  94. package/dist/components/ui/toggle.js +3 -0
  95. package/dist/components/ui/toggle.js.map +1 -1
  96. package/dist/components/ui/tooltip.d.ts.map +1 -1
  97. package/dist/components/ui/tooltip.js +3 -0
  98. package/dist/components/ui/tooltip.js.map +1 -1
  99. package/dist/dialogs/NodePickerDialog.d.ts.map +1 -1
  100. package/dist/dialogs/NodePickerDialog.js +3 -0
  101. package/dist/dialogs/NodePickerDialog.js.map +1 -1
  102. package/dist/dialogs/ValidationDialog.d.ts.map +1 -1
  103. package/dist/dialogs/ValidationDialog.js +3 -0
  104. package/dist/dialogs/ValidationDialog.js.map +1 -1
  105. package/dist/graph/BaseNode.d.ts.map +1 -1
  106. package/dist/graph/BaseNode.js +11 -6
  107. package/dist/graph/BaseNode.js.map +1 -1
  108. package/dist/graph/CustomEdge.d.ts.map +1 -1
  109. package/dist/graph/CustomEdge.js +4 -1
  110. package/dist/graph/CustomEdge.js.map +1 -1
  111. package/dist/graph/CustomNode.d.ts.map +1 -1
  112. package/dist/graph/CustomNode.js +3 -0
  113. package/dist/graph/CustomNode.js.map +1 -1
  114. package/dist/graph/FunctionCallNode.d.ts.map +1 -1
  115. package/dist/graph/FunctionCallNode.js.map +1 -1
  116. package/dist/graph/PortHandle.d.ts.map +1 -1
  117. package/dist/graph/PortHandle.js.map +1 -1
  118. package/dist/graph/reactFlowRegistry.d.ts.map +1 -1
  119. package/dist/graph/reactFlowRegistry.js +3 -0
  120. package/dist/graph/reactFlowRegistry.js.map +1 -1
  121. package/dist/hooks/use-toast.d.ts.map +1 -1
  122. package/dist/hooks/use-toast.js +3 -0
  123. package/dist/hooks/use-toast.js.map +1 -1
  124. package/dist/hooks/useAvailableVariables.d.ts.map +1 -1
  125. package/dist/hooks/useAvailableVariables.js +3 -0
  126. package/dist/hooks/useAvailableVariables.js.map +1 -1
  127. package/dist/hooks/useCanvasHistory.d.ts.map +1 -1
  128. package/dist/hooks/useCanvasHistory.js +3 -0
  129. package/dist/hooks/useCanvasHistory.js.map +1 -1
  130. package/dist/hooks/useCanvasTabs.d.ts.map +1 -1
  131. package/dist/hooks/useCanvasTabs.js +3 -0
  132. package/dist/hooks/useCanvasTabs.js.map +1 -1
  133. package/dist/hooks/useFunctionDiagnosticsSync.d.ts.map +1 -1
  134. package/dist/hooks/useFunctionDiagnosticsSync.js +3 -0
  135. package/dist/hooks/useFunctionDiagnosticsSync.js.map +1 -1
  136. package/dist/hooks/useFunctionRegistry.d.ts.map +1 -1
  137. package/dist/hooks/useFunctionRegistry.js +3 -0
  138. package/dist/hooks/useFunctionRegistry.js.map +1 -1
  139. package/dist/hooks/useFunctions.d.ts.map +1 -1
  140. package/dist/hooks/useFunctions.js +3 -0
  141. package/dist/hooks/useFunctions.js.map +1 -1
  142. package/dist/hooks/useGraph.d.ts.map +1 -1
  143. package/dist/hooks/useGraph.js +3 -0
  144. package/dist/hooks/useGraph.js.map +1 -1
  145. package/dist/hooks/useNodeDefinitions.d.ts.map +1 -1
  146. package/dist/hooks/useNodeDefinitions.js +3 -0
  147. package/dist/hooks/useNodeDefinitions.js.map +1 -1
  148. package/dist/hooks/useParamErrors.d.ts.map +1 -1
  149. package/dist/hooks/useParamErrors.js +3 -0
  150. package/dist/hooks/useParamErrors.js.map +1 -1
  151. package/dist/hooks/useResolvedTheme.d.ts.map +1 -1
  152. package/dist/hooks/useResolvedTheme.js +3 -0
  153. package/dist/hooks/useResolvedTheme.js.map +1 -1
  154. package/dist/hooks/useResourceDiagnosticsSync.d.ts.map +1 -1
  155. package/dist/hooks/useResourceDiagnosticsSync.js +3 -0
  156. package/dist/hooks/useResourceDiagnosticsSync.js.map +1 -1
  157. package/dist/hooks/useSuppressThemeTransition.d.ts.map +1 -1
  158. package/dist/hooks/useSuppressThemeTransition.js +3 -0
  159. package/dist/hooks/useSuppressThemeTransition.js.map +1 -1
  160. package/dist/hooks/useWorkflowSerialization.d.ts.map +1 -1
  161. package/dist/hooks/useWorkflowSerialization.js +3 -0
  162. package/dist/hooks/useWorkflowSerialization.js.map +1 -1
  163. package/dist/i18n/index.d.ts.map +1 -1
  164. package/dist/i18n/index.js +3 -0
  165. package/dist/i18n/index.js.map +1 -1
  166. package/dist/index.d.ts +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +3 -0
  169. package/dist/index.js.map +1 -1
  170. package/dist/inputs/ExpressionInput.d.ts.map +1 -1
  171. package/dist/inputs/ExpressionInput.js +3 -0
  172. package/dist/inputs/ExpressionInput.js.map +1 -1
  173. package/dist/inputs/ParameterEditor.d.ts.map +1 -1
  174. package/dist/inputs/ParameterEditor.js +3 -0
  175. package/dist/inputs/ParameterEditor.js.map +1 -1
  176. package/dist/inputs/PortSection.d.ts.map +1 -1
  177. package/dist/inputs/PortSection.js.map +1 -1
  178. package/dist/mode.d.ts +11 -0
  179. package/dist/mode.d.ts.map +1 -0
  180. package/dist/mode.js +8 -0
  181. package/dist/mode.js.map +1 -0
  182. package/dist/panels/BuilderSidebar.d.ts.map +1 -1
  183. package/dist/panels/BuilderSidebar.js +3 -0
  184. package/dist/panels/BuilderSidebar.js.map +1 -1
  185. package/dist/panels/ChannelConfigPanel.d.ts.map +1 -1
  186. package/dist/panels/ChannelConfigPanel.js +3 -0
  187. package/dist/panels/ChannelConfigPanel.js.map +1 -1
  188. package/dist/panels/ChannelsPanel.d.ts.map +1 -1
  189. package/dist/panels/ChannelsPanel.js +3 -0
  190. package/dist/panels/ChannelsPanel.js.map +1 -1
  191. package/dist/panels/DebugConsolePanel.d.ts.map +1 -1
  192. package/dist/panels/DebugConsolePanel.js +3 -0
  193. package/dist/panels/DebugConsolePanel.js.map +1 -1
  194. package/dist/panels/DebugContextPanel.d.ts.map +1 -1
  195. package/dist/panels/DebugContextPanel.js +3 -0
  196. package/dist/panels/DebugContextPanel.js.map +1 -1
  197. package/dist/panels/DebugExternalIOPanel.d.ts.map +1 -1
  198. package/dist/panels/DebugExternalIOPanel.js +3 -0
  199. package/dist/panels/DebugExternalIOPanel.js.map +1 -1
  200. package/dist/panels/DiagnosticsPanel.d.ts.map +1 -1
  201. package/dist/panels/DiagnosticsPanel.js +3 -0
  202. package/dist/panels/DiagnosticsPanel.js.map +1 -1
  203. package/dist/panels/EdgeConfigPanel.d.ts.map +1 -1
  204. package/dist/panels/EdgeConfigPanel.js +4 -1
  205. package/dist/panels/EdgeConfigPanel.js.map +1 -1
  206. package/dist/panels/FunctionConfigPanel.d.ts.map +1 -1
  207. package/dist/panels/FunctionConfigPanel.js +4 -1
  208. package/dist/panels/FunctionConfigPanel.js.map +1 -1
  209. package/dist/panels/FunctionListPanel.d.ts.map +1 -1
  210. package/dist/panels/FunctionListPanel.js +3 -0
  211. package/dist/panels/FunctionListPanel.js.map +1 -1
  212. package/dist/panels/MemoryConfigPanel.d.ts.map +1 -1
  213. package/dist/panels/MemoryConfigPanel.js +3 -0
  214. package/dist/panels/MemoryConfigPanel.js.map +1 -1
  215. package/dist/panels/MemoryPanel.d.ts.map +1 -1
  216. package/dist/panels/MemoryPanel.js +3 -0
  217. package/dist/panels/MemoryPanel.js.map +1 -1
  218. package/dist/panels/ModelConfigPanel.d.ts.map +1 -1
  219. package/dist/panels/ModelConfigPanel.js +3 -0
  220. package/dist/panels/ModelConfigPanel.js.map +1 -1
  221. package/dist/panels/ModelsPanel.d.ts.map +1 -1
  222. package/dist/panels/ModelsPanel.js +3 -0
  223. package/dist/panels/ModelsPanel.js.map +1 -1
  224. package/dist/panels/NodeConfigPanel.d.ts.map +1 -1
  225. package/dist/panels/NodeConfigPanel.js +4 -1
  226. package/dist/panels/NodeConfigPanel.js.map +1 -1
  227. package/dist/panels/NodeLibrary.d.ts.map +1 -1
  228. package/dist/panels/NodeLibrary.js +4 -1
  229. package/dist/panels/NodeLibrary.js.map +1 -1
  230. package/dist/panels/ResourceConfigPanel.d.ts.map +1 -1
  231. package/dist/panels/ResourceConfigPanel.js +4 -1
  232. package/dist/panels/ResourceConfigPanel.js.map +1 -1
  233. package/dist/panels/ResourceListPanel.d.ts.map +1 -1
  234. package/dist/panels/ResourceListPanel.js +4 -1
  235. package/dist/panels/ResourceListPanel.js.map +1 -1
  236. package/dist/panels/VariableConfigPanel.d.ts.map +1 -1
  237. package/dist/panels/VariableConfigPanel.js +4 -1
  238. package/dist/panels/VariableConfigPanel.js.map +1 -1
  239. package/dist/panels/VariablesPanel.d.ts.map +1 -1
  240. package/dist/panels/VariablesPanel.js +4 -1
  241. package/dist/panels/VariablesPanel.js.map +1 -1
  242. package/dist/stores/canvasStore.d.ts.map +1 -1
  243. package/dist/stores/canvasStore.js +3 -0
  244. package/dist/stores/canvasStore.js.map +1 -1
  245. package/dist/stores/debugStore.d.ts.map +1 -1
  246. package/dist/stores/debugStore.js +3 -0
  247. package/dist/stores/debugStore.js.map +1 -1
  248. package/dist/stores/diagnosticsStore.d.ts.map +1 -1
  249. package/dist/stores/diagnosticsStore.js +3 -0
  250. package/dist/stores/diagnosticsStore.js.map +1 -1
  251. package/dist/stores/editorStore.d.ts +1 -1
  252. package/dist/stores/editorStore.d.ts.map +1 -1
  253. package/dist/stores/editorStore.js +3 -0
  254. package/dist/stores/editorStore.js.map +1 -1
  255. package/dist/utils/categoryConstants.d.ts.map +1 -1
  256. package/dist/utils/categoryConstants.js +3 -0
  257. package/dist/utils/categoryConstants.js.map +1 -1
  258. package/dist/utils/channelOperations.d.ts.map +1 -1
  259. package/dist/utils/channelOperations.js +3 -0
  260. package/dist/utils/channelOperations.js.map +1 -1
  261. package/dist/utils/connectionRules.d.ts +3 -1
  262. package/dist/utils/connectionRules.d.ts.map +1 -1
  263. package/dist/utils/connectionRules.js +11 -16
  264. package/dist/utils/connectionRules.js.map +1 -1
  265. package/dist/utils/functionOperations.d.ts.map +1 -1
  266. package/dist/utils/functionOperations.js +3 -0
  267. package/dist/utils/functionOperations.js.map +1 -1
  268. package/dist/utils/graphOperations.d.ts.map +1 -1
  269. package/dist/utils/graphOperations.js +3 -0
  270. package/dist/utils/graphOperations.js.map +1 -1
  271. package/dist/utils/history.d.ts.map +1 -1
  272. package/dist/utils/history.js +4 -1
  273. package/dist/utils/history.js.map +1 -1
  274. package/dist/utils/memoryOperations.d.ts.map +1 -1
  275. package/dist/utils/memoryOperations.js +3 -0
  276. package/dist/utils/memoryOperations.js.map +1 -1
  277. package/dist/utils/migrateFunctionNodes.d.ts.map +1 -1
  278. package/dist/utils/migrateFunctionNodes.js +3 -0
  279. package/dist/utils/migrateFunctionNodes.js.map +1 -1
  280. package/dist/utils/modelOperations.d.ts.map +1 -1
  281. package/dist/utils/modelOperations.js +3 -0
  282. package/dist/utils/modelOperations.js.map +1 -1
  283. package/dist/utils/paramDisplay.d.ts.map +1 -1
  284. package/dist/utils/paramDisplay.js +3 -0
  285. package/dist/utils/paramDisplay.js.map +1 -1
  286. package/dist/utils/resourceHelpers.d.ts.map +1 -1
  287. package/dist/utils/resourceHelpers.js +3 -0
  288. package/dist/utils/resourceHelpers.js.map +1 -1
  289. package/dist/utils/translation.d.ts.map +1 -1
  290. package/dist/utils/translation.js +3 -0
  291. package/dist/utils/translation.js.map +1 -1
  292. package/dist/utils/variableOperations.d.ts.map +1 -1
  293. package/dist/utils/variableOperations.js +3 -0
  294. package/dist/utils/variableOperations.js.map +1 -1
  295. package/package.json +2 -2
  296. package/src/BuilderLayout.tsx +54 -61
  297. package/src/Canvas.tsx +12 -3
  298. package/src/CanvasEditor.tsx +4 -0
  299. package/src/CanvasTabsToolbar.tsx +4 -0
  300. package/src/RightConfigPanel.tsx +4 -0
  301. package/src/WorkflowBuilder.tsx +33 -18
  302. package/src/cn.ts +4 -0
  303. package/src/components/ui/add-button.tsx +4 -0
  304. package/src/components/ui/alert-dialog.tsx +4 -0
  305. package/src/components/ui/alert.tsx +4 -0
  306. package/src/components/ui/badge.tsx +4 -0
  307. package/src/components/ui/button.tsx +4 -0
  308. package/src/components/ui/card.tsx +4 -0
  309. package/src/components/ui/checkbox.tsx +4 -0
  310. package/src/components/ui/collapsible.tsx +4 -0
  311. package/src/components/ui/command.tsx +4 -0
  312. package/src/components/ui/delete-button.tsx +4 -0
  313. package/src/components/ui/dialog.tsx +4 -0
  314. package/src/components/ui/dropdown-menu.tsx +4 -0
  315. package/src/components/ui/input.tsx +4 -0
  316. package/src/components/ui/label.tsx +4 -0
  317. package/src/components/ui/readonly-banner.tsx +4 -0
  318. package/src/components/ui/resizable.tsx +4 -0
  319. package/src/components/ui/scroll-area.tsx +4 -0
  320. package/src/components/ui/select.tsx +4 -0
  321. package/src/components/ui/separator.tsx +4 -0
  322. package/src/components/ui/switch.tsx +4 -0
  323. package/src/components/ui/textarea.tsx +4 -0
  324. package/src/components/ui/toast.tsx +4 -0
  325. package/src/components/ui/toaster.tsx +4 -0
  326. package/src/components/ui/toggle-group.tsx +4 -0
  327. package/src/components/ui/toggle.tsx +4 -0
  328. package/src/components/ui/tooltip.tsx +4 -0
  329. package/src/dialogs/NodePickerDialog.tsx +4 -0
  330. package/src/dialogs/ValidationDialog.tsx +4 -0
  331. package/src/graph/BaseNode.tsx +12 -6
  332. package/src/graph/CustomEdge.tsx +5 -1
  333. package/src/graph/CustomNode.tsx +4 -0
  334. package/src/graph/FunctionCallNode.tsx +4 -0
  335. package/src/graph/PortHandle.tsx +4 -0
  336. package/src/graph/reactFlowRegistry.ts +4 -0
  337. package/src/hooks/use-toast.ts +4 -0
  338. package/src/hooks/useAvailableVariables.ts +4 -0
  339. package/src/hooks/useCanvasHistory.ts +4 -0
  340. package/src/hooks/useCanvasTabs.ts +4 -0
  341. package/src/hooks/useFunctionDiagnosticsSync.ts +4 -0
  342. package/src/hooks/useFunctionRegistry.ts +4 -0
  343. package/src/hooks/useFunctions.ts +4 -0
  344. package/src/hooks/useGraph.ts +4 -0
  345. package/src/hooks/useNodeDefinitions.ts +4 -0
  346. package/src/hooks/useParamErrors.ts +4 -0
  347. package/src/hooks/useResolvedTheme.ts +4 -0
  348. package/src/hooks/useResourceDiagnosticsSync.ts +4 -0
  349. package/src/hooks/useSuppressThemeTransition.ts +4 -0
  350. package/src/hooks/useWorkflowSerialization.ts +4 -0
  351. package/src/i18n/index.ts +4 -0
  352. package/src/index.ts +5 -1
  353. package/src/inputs/ExpressionInput.tsx +4 -0
  354. package/src/inputs/ParameterEditor.tsx +4 -0
  355. package/src/inputs/PortSection.tsx +4 -0
  356. package/src/mode.ts +15 -0
  357. package/src/panels/BuilderSidebar.tsx +4 -0
  358. package/src/panels/ChannelConfigPanel.tsx +4 -0
  359. package/src/panels/ChannelsPanel.tsx +4 -0
  360. package/src/panels/DebugConsolePanel.tsx +4 -0
  361. package/src/panels/DebugContextPanel.tsx +4 -0
  362. package/src/panels/DebugExternalIOPanel.tsx +4 -0
  363. package/src/panels/DiagnosticsPanel.tsx +4 -0
  364. package/src/panels/EdgeConfigPanel.tsx +5 -1
  365. package/src/panels/FunctionConfigPanel.tsx +5 -1
  366. package/src/panels/FunctionListPanel.tsx +4 -0
  367. package/src/panels/MemoryConfigPanel.tsx +4 -0
  368. package/src/panels/MemoryPanel.tsx +4 -0
  369. package/src/panels/ModelConfigPanel.tsx +4 -0
  370. package/src/panels/ModelsPanel.tsx +4 -0
  371. package/src/panels/NodeConfigPanel.tsx +5 -1
  372. package/src/panels/NodeLibrary.tsx +5 -1
  373. package/src/panels/ResourceConfigPanel.tsx +5 -1
  374. package/src/panels/ResourceListPanel.tsx +5 -1
  375. package/src/panels/VariableConfigPanel.tsx +5 -1
  376. package/src/panels/VariablesPanel.tsx +5 -1
  377. package/src/stores/canvasStore.test.ts +4 -0
  378. package/src/stores/canvasStore.ts +4 -0
  379. package/src/stores/debugStore.ts +4 -0
  380. package/src/stores/diagnosticsStore.ts +4 -0
  381. package/src/stores/editorStore.ts +5 -1
  382. package/src/utils/categoryConstants.ts +4 -0
  383. package/src/utils/channelOperations.ts +4 -0
  384. package/src/utils/connectionRules.ts +12 -9
  385. package/src/utils/functionOperations.ts +4 -0
  386. package/src/utils/graphOperations.ts +4 -0
  387. package/src/utils/history.test.ts +92 -0
  388. package/src/utils/history.ts +5 -1
  389. package/src/utils/memoryOperations.ts +4 -0
  390. package/src/utils/migrateFunctionNodes.ts +4 -0
  391. package/src/utils/modelOperations.ts +4 -0
  392. package/src/utils/paramDisplay.ts +4 -0
  393. package/src/utils/resourceHelpers.ts +4 -0
  394. package/src/utils/translation.ts +4 -0
  395. package/src/utils/variableOperations.ts +4 -0
  396. package/tailwind-preset.ts +4 -0
@@ -1,3 +1,7 @@
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
+
1
5
  import type { FunctionDeclaration } from "@foresthubai/workflow-core/function";
2
6
  import type { NodeDefinition } from "@foresthubai/workflow-core/node";
3
7
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -16,7 +20,7 @@ import { DebugConsolePanel } from "./panels/DebugConsolePanel";
16
20
  import type { CanvasTab } from "./hooks/useCanvasTabs";
17
21
  import { getOrCreateCanvasStore, MAIN_CANVAS_ID } from "./stores/canvasStore";
18
22
  import { useEditorStore } from "./stores/editorStore";
19
- import { isReadOnly } from "./WorkflowBuilder";
23
+ import { isReadOnly } from "./mode";
20
24
 
21
25
  /**
22
26
  * Chrome composer. Stable across canvas switches — only the {@link CanvasEditor}
@@ -196,69 +200,55 @@ export const BuilderLayout = ({
196
200
  [graph],
197
201
  );
198
202
 
199
- // Debug mode: clicking a node sets the debug cursor.
200
- // useEffect(() => {
201
- // if (!isDebugMode || selectedNodeIds.length !== 1) return;
202
- // const nodeId = selectedNodeIds[0];
203
- // const phase = useDebugStore.getState().phase;
204
- // if (phase.status === "idle") {
205
- // useDebugStore
206
- // .getState()
207
- // .setPhase({ status: "paused", sessionId: phase.sessionId, cursorNodeId: nodeId });
208
- // } else if (phase.status === "paused") {
209
- // useDebugStore.getState().setPhase({ ...phase, cursorNodeId: nodeId });
210
- // }
211
- // }, [isDebugMode, selectedNodeIds]);
212
-
213
- // Keyboard handlers undo/redo/copy/paste/delete/escape.
214
- useEffect(() => {
215
- const handleKeyDown = (event: KeyboardEvent) => {
216
- const target = event.target as HTMLElement;
217
- if (target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable) {
218
- return;
219
- }
220
- if (readOnly) {
221
- if (event.key === "Escape") clearSelection();
222
- return;
223
- }
224
- if ((event.ctrlKey || event.metaKey) && event.key === "z" && !event.shiftKey) {
225
- event.preventDefault();
226
- if (canUndo()) {
227
- clearSelection();
228
- undo();
229
- }
230
- return;
231
- }
232
- if ((event.ctrlKey || event.metaKey) && (event.key === "y" || (event.key === "z" && event.shiftKey))) {
233
- event.preventDefault();
234
- if (canRedo()) {
235
- clearSelection();
236
- redo();
237
- }
238
- return;
203
+ // Keyboard handlers undo/redo/copy/paste/delete/escape. Attached to the
204
+ // builder's root element (focusable via tabIndex), NOT to document: an
205
+ // embedded component must not steal Ctrl+Z/Delete from the host page. The
206
+ // handler fires only while focus is inside the builder, which clicking the
207
+ // canvas or any panel establishes.
208
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
209
+ const target = event.target as HTMLElement;
210
+ if (target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable) {
211
+ return;
212
+ }
213
+ if (readOnly) {
214
+ if (event.key === "Escape") clearSelection();
215
+ return;
216
+ }
217
+ if ((event.ctrlKey || event.metaKey) && event.key === "z" && !event.shiftKey) {
218
+ event.preventDefault();
219
+ if (canUndo()) {
220
+ clearSelection();
221
+ undo();
239
222
  }
240
- if ((event.ctrlKey || event.metaKey) && event.key === "c") {
241
- if (selection.kind === "graph" && selection.nodeIds.length > 0) {
242
- event.preventDefault();
243
- graph.copySelection(selection.nodeIds);
244
- }
245
- return;
223
+ return;
224
+ }
225
+ if ((event.ctrlKey || event.metaKey) && (event.key === "y" || (event.key === "z" && event.shiftKey))) {
226
+ event.preventDefault();
227
+ if (canRedo()) {
228
+ clearSelection();
229
+ redo();
246
230
  }
247
- if ((event.ctrlKey || event.metaKey) && event.key === "v") {
231
+ return;
232
+ }
233
+ if ((event.ctrlKey || event.metaKey) && event.key === "c") {
234
+ if (selection.kind === "graph" && selection.nodeIds.length > 0) {
248
235
  event.preventDefault();
249
- handlePaste();
250
- return;
251
- }
252
- if (event.key === "Delete" || event.key === "Backspace") {
253
- deleteSelected();
254
- }
255
- if (event.key === "Escape") {
256
- clearSelection();
236
+ graph.copySelection(selection.nodeIds);
257
237
  }
258
- };
259
- document.addEventListener("keydown", handleKeyDown);
260
- return () => document.removeEventListener("keydown", handleKeyDown);
261
- }, [canUndo, canRedo, undo, redo, clearSelection, deleteSelected, selection, graph, handlePaste, readOnly]);
238
+ return;
239
+ }
240
+ if ((event.ctrlKey || event.metaKey) && event.key === "v") {
241
+ event.preventDefault();
242
+ handlePaste();
243
+ return;
244
+ }
245
+ if (event.key === "Delete" || event.key === "Backspace") {
246
+ deleteSelected();
247
+ }
248
+ if (event.key === "Escape") {
249
+ clearSelection();
250
+ }
251
+ };
262
252
 
263
253
  const isFunctionCanvas = activeCanvasId !== MAIN_CANVAS_ID;
264
254
 
@@ -292,7 +282,10 @@ export const BuilderLayout = ({
292
282
  );
293
283
 
294
284
  return (
295
- <div className="h-full bg-canvas-bg flex flex-col">
285
+ // tabIndex makes the root focusable so clicks anywhere inside land focus
286
+ // here (nearest focusable ancestor) and the scoped key handler receives
287
+ // shortcuts; outline-none suppresses the focus ring this would add.
288
+ <div className="h-full bg-canvas-bg flex flex-col outline-none" tabIndex={-1} onKeyDown={handleKeyDown}>
296
289
  <div className="flex-1 flex overflow-hidden">
297
290
  <BuilderSidebar
298
291
  canvasId={activeCanvasId}
package/src/Canvas.tsx CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  import {
2
6
  applyEdgeChanges,
3
7
  applyNodeChanges,
@@ -24,7 +28,7 @@ import { isValidConnection as validateConnection } from "./utils/connectionRules
24
28
  import { getOrCreateCanvasStore } from "./stores/canvasStore";
25
29
  import { useEditorStore } from "./stores/editorStore";
26
30
  import { nodeTypes, edgeTypes } from "./graph/reactFlowRegistry";
27
- import { isReadOnly } from "./WorkflowBuilder";
31
+ import { isReadOnly } from "./mode";
28
32
 
29
33
  interface CanvasProps {
30
34
  canvasId: string;
@@ -55,10 +59,15 @@ const CanvasArea = ({
55
59
  const { screenToFlowPosition, getViewport, fitView } = useReactFlow();
56
60
  const nodesInitialized = useNodesInitialized();
57
61
 
62
+ // Measure THIS canvas's container, never document.querySelector(".react-flow"):
63
+ // that grabs the first match on the page and breaks with two mounted builders
64
+ // or a host app that uses React Flow itself.
65
+ const containerRef = useRef<HTMLDivElement>(null);
66
+
58
67
  // Expose viewport center calculation to parent via ref so new nodes can be dropped there.
59
68
  useEffect(() => {
60
69
  viewportCenterRef.current = () => {
61
- const container = document.querySelector(".react-flow");
70
+ const container = containerRef.current;
62
71
  if (!container) return { x: 250, y: 100 };
63
72
  const { width, height } = container.getBoundingClientRect();
64
73
  const { x, y, zoom } = getViewport();
@@ -190,7 +199,7 @@ const CanvasArea = ({
190
199
  // - display:none removes layout boxes, so nodes never measure and the fit can't compute.
191
200
  // opacity applies to the whole subtree as a group (children can't override it) yet keeps
192
201
  // layout intact, so measurement/fitView work while it's invisible.
193
- <div className="w-full h-full" style={{ opacity: ready ? 1 : 0 }}>
202
+ <div ref={containerRef} className="w-full h-full" style={{ opacity: ready ? 1 : 0 }}>
194
203
  <ReactFlow
195
204
  nodes={nodes}
196
205
  edges={edges}
@@ -1,3 +1,7 @@
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
+
1
5
  import type { Dispatch, MutableRefObject, SetStateAction } from "react";
2
6
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
7
  import type { Connection, Node, OnSelectionChangeFunc } from "@xyflow/react";
@@ -1,3 +1,7 @@
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
+
1
5
  import { ScrollArea } from "./components/ui/scroll-area";
2
6
  import { cn } from "./cn";
3
7
  import { FunctionSquare, Workflow, X } from "lucide-react";
@@ -1,3 +1,7 @@
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
+
1
5
  import { useCallback, useMemo, type ReactNode } from "react";
2
6
  import type { NodeData, NodeDefinition } from "@foresthubai/workflow-core/node";
3
7
  import type { EdgeData, EdgeType } from "@foresthubai/workflow-core/edge";
@@ -1,3 +1,7 @@
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
+
1
5
  import type { ApiWorkflow } from "@foresthubai/workflow-core/workflow";
2
6
  import type { ModelInfo } from "@foresthubai/workflow-core/model";
3
7
  import {
@@ -33,18 +37,7 @@ import {
33
37
  import { useDebugStore, type DebugSessionPhase } from "./stores/debugStore";
34
38
  import { useEditorStore } from "./stores/editorStore";
35
39
 
36
- /** BuilderMode steers the overall behavior of the workflow builder. */
37
- export type BuilderMode = { type: "edit" } | { type: "preview" } | { type: "debug" };
38
-
39
- /** True when canvas mutations should be blocked (preview or debug). */
40
- export function isReadOnly(mode: BuilderMode): boolean {
41
- return mode.type !== "edit";
42
- }
43
-
44
- /** Type guard for preview mode. */
45
- export function isPreview(mode: BuilderMode): boolean {
46
- return mode.type === "preview";
47
- }
40
+ import type { BuilderMode } from "./mode";
48
41
 
49
42
  // ============================================================================
50
43
  // Public contract
@@ -74,7 +67,12 @@ export interface WorkflowBuilderProps {
74
67
  onDebugStep?: (nodeId?: string) => void;
75
68
 
76
69
  // ── Lifecycle events ──
77
- /** Fires after any domain-state mutation. Pull current state via handle.exportWorkflow(). */
70
+ /**
71
+ * Fires after any USER domain-state mutation. Pull current state via
72
+ * handle.exportWorkflow(). Programmatic loads (handle.loadWorkflow /
73
+ * handle.clear) do NOT fire this — hosts can reset their dirty flag right
74
+ * after those calls without guarding against echoes.
75
+ */
78
76
  onChange?: () => void;
79
77
  /**
80
78
  * Undo/redo availability for the ACTIVE canvas changed — on history mutation,
@@ -239,6 +237,13 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
239
237
  onChangeRef.current = onChange;
240
238
  onHistoryChangeRef.current = onHistoryChange;
241
239
 
240
+ // onChange means "the USER changed the document". Programmatic loads
241
+ // (handle.loadWorkflow / handle.clear) mutate the same stores, so their
242
+ // subscription callbacks — which zustand runs synchronously inside the
243
+ // store writes — are muted while this flag is up. Without it every host
244
+ // sees phantom onChange events on load and has to guard its dirty flag.
245
+ const suppressChangeRef = useRef(false);
246
+
242
247
  // onChange fires on any domain change. For canvas content we watch the
243
248
  // history middleware's `mutationCount`, which bumps on checkpoints AND
244
249
  // undo/redo but never on selection/drag (those go through setNodes without a
@@ -256,8 +261,8 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
256
261
  let prev = store.getState().mutationCount;
257
262
  const unsub = store.subscribe((state) => {
258
263
  if (state.mutationCount !== prev) {
259
- prev = state.mutationCount;
260
- onChangeRef.current?.();
264
+ prev = state.mutationCount; // always track, even when muted
265
+ if (!suppressChangeRef.current) onChangeRef.current?.();
261
266
  }
262
267
  });
263
268
  subs.push(unsub);
@@ -285,8 +290,8 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
285
290
  let prevEditorCount = useEditorStore.getState().mutationCount;
286
291
  const unsubEditor = useEditorStore.subscribe((state) => {
287
292
  if (state.mutationCount !== prevEditorCount) {
288
- prevEditorCount = state.mutationCount;
289
- onChangeRef.current?.();
293
+ prevEditorCount = state.mutationCount; // always track, even when muted
294
+ if (!suppressChangeRef.current) onChangeRef.current?.();
290
295
  }
291
296
  });
292
297
  subs.push(unsubEditor);
@@ -347,14 +352,24 @@ export const WorkflowBuilder = forwardRef<WorkflowBuilderHandle, WorkflowBuilder
347
352
  ref,
348
353
  (): WorkflowBuilderHandle => ({
349
354
  loadWorkflow: (workflow) => {
355
+ suppressChangeRef.current = true;
350
356
  try {
351
357
  importProject(workflow);
352
358
  } catch (e) {
353
359
  onError?.(e instanceof Error ? e : new Error(String(e)));
360
+ } finally {
361
+ suppressChangeRef.current = false;
354
362
  }
355
363
  },
356
364
  exportWorkflow: () => exportProject(),
357
- clear: () => clearProject(),
365
+ clear: () => {
366
+ suppressChangeRef.current = true;
367
+ try {
368
+ clearProject();
369
+ } finally {
370
+ suppressChangeRef.current = false;
371
+ }
372
+ },
358
373
  setMode: (mode) => useEditorStore.getState().setBuilderMode(mode),
359
374
  getMode: () => useEditorStore.getState().builderMode,
360
375
  validate: runValidate,
package/src/cn.ts CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  import { clsx, type ClassValue } from "clsx"
2
6
  import { twMerge } from "tailwind-merge"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react";
2
6
  import { Plus, type LucideIcon } from "lucide-react";
3
7
  import { Button, type ButtonProps } from "./button";
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { cva, type VariantProps } from "class-variance-authority"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { cva, type VariantProps } from "class-variance-authority"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { Slot } from "@radix-ui/react-slot"
3
7
  import { cva, type VariantProps } from "class-variance-authority"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
 
3
7
  import { cn } from "../../cn"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
3
7
  import { Check } from "lucide-react"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
2
6
 
3
7
  const Collapsible = CollapsiblePrimitive.Root
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { type DialogProps } from "@radix-ui/react-dialog"
3
7
  import { Command as CommandPrimitive } from "cmdk"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react";
2
6
  import { Trash2 } from "lucide-react";
3
7
  import { Button, type ButtonProps } from "./button";
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as DialogPrimitive from "@radix-ui/react-dialog"
3
7
  import { X } from "lucide-react"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
3
7
  import { Check, ChevronRight, Circle } from "lucide-react"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { cva, type VariantProps } from "class-variance-authority"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as LabelPrimitive from "@radix-ui/react-label"
3
7
  import { cva, type VariantProps } from "class-variance-authority"
@@ -1,3 +1,7 @@
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
+
1
5
  import { useTranslation } from "react-i18next";
2
6
 
3
7
  /**
@@ -1,3 +1,7 @@
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
+
1
5
  import { GripVertical } from "lucide-react"
2
6
  import * as ResizablePrimitive from "react-resizable-panels"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react";
2
6
  import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as SelectPrimitive from "@radix-ui/react-select"
3
7
  import { Check, ChevronDown, ChevronUp } from "lucide-react"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as SeparatorPrimitive from "@radix-ui/react-separator"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as SwitchPrimitives from "@radix-ui/react-switch"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import { cva, type VariantProps } from "class-variance-authority"
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as ToastPrimitives from "@radix-ui/react-toast"
3
7
  import { cva, type VariantProps } from "class-variance-authority"
@@ -1,3 +1,7 @@
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
+
1
5
  import { useToast } from "../../hooks/use-toast"
2
6
  import {
3
7
  Toast,
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
3
7
  import { type VariantProps } from "class-variance-authority"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react"
2
6
  import * as TogglePrimitive from "@radix-ui/react-toggle"
3
7
  import { cva, type VariantProps } from "class-variance-authority"
@@ -1,3 +1,7 @@
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
+
1
5
  import * as React from "react";
2
6
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
3
7
 
@@ -1,3 +1,7 @@
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
+
1
5
  import { useEffect, useState } from "react";
2
6
  import { useTranslation } from "react-i18next";
3
7
  import { NodeDefinition, NodeCategory } from "@foresthubai/workflow-core/node";
@@ -1,3 +1,7 @@
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
+
1
5
  import { useState } from "react";
2
6
  import { useTranslation } from "react-i18next";
3
7
  import { AlertCircle, AlertTriangle, ChevronDown } from "lucide-react";
@@ -1,3 +1,7 @@
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
+
1
5
  import { Badge } from "../components/ui/badge";
2
6
  import { Tooltip, TooltipContent, TooltipTrigger } from "../components/ui/tooltip";
3
7
  import {
@@ -16,7 +20,7 @@ import { getOrCreateCanvasStore } from "../stores/canvasStore";
16
20
  import { useDebugStore } from "../stores/debugStore";
17
21
  import { useDiagnosticsStore } from "../stores/diagnosticsStore";
18
22
  import { useEditorStore } from "../stores/editorStore";
19
- import { isReadOnly } from "../WorkflowBuilder";
23
+ import { isReadOnly } from "../mode";
20
24
  import { categoryIcons } from "../utils/categoryConstants";
21
25
  import { computeNodeDiagnostics } from "@foresthubai/workflow-core/diagnostics";
22
26
  import {
@@ -63,10 +67,12 @@ export const BaseNode = memo(
63
67
  // Get active canvas ID with imperative access (no subscription), since it doesn't change for a node
64
68
  const activeCanvasId = useEditorStore.getState().activeCanvasId;
65
69
 
66
- // Get necessary canvas store data
70
+ // Get necessary canvas store data.
71
+ // BaseNode must NEVER subscribe to s.nodes: dragging replaces the nodes array
72
+ // every frame, so a nodes subscription re-renders all N nodes per frame (O(N²)).
73
+ // Subscribing to s.edges is fine — edges keep their identity during a drag.
67
74
  const canvasStore = getOrCreateCanvasStore(activeCanvasId);
68
75
  const edges = canvasStore((s) => s.edges);
69
- const nodes = canvasStore((s) => s.nodes);
70
76
  const channels = useEditorStore((s) => s.channels);
71
77
  const memory = useEditorStore((s) => s.memory);
72
78
  const models = useEditorStore((s) => s.models);
@@ -103,7 +109,7 @@ export const BaseNode = memo(
103
109
  }, [availableModels, models]);
104
110
 
105
111
  // Get port definitions using centralized dispatcher
106
- const portDefinitions = getPorts(nodeData);
112
+ const portDefinitions = useMemo(() => getPorts(nodeData), [nodeData]);
107
113
 
108
114
  // Separate ports by type for positioning
109
115
  const { executionInputs, toolInputs, executionOutputs, toolOutputs } = useMemo(() => {
@@ -508,7 +514,7 @@ export const BaseNode = memo(
508
514
 
509
515
  {/* Execution output handles (right) — disabled when tool input is connected */}
510
516
  {executionOutputs.map((port, index) => {
511
- const canAccept = !isPreview && !usedAsToolInput && canPortAcceptEdge(id, port.id, nodes, edges);
517
+ const canAccept = !isPreview && !usedAsToolInput && canPortAcceptEdge(nodeData, port.id, edges);
512
518
  return (
513
519
  <PortHandle
514
520
  key={port.id}
@@ -533,7 +539,7 @@ export const BaseNode = memo(
533
539
 
534
540
  {/* Tool output handles (bottom) */}
535
541
  {toolOutputs.map((port, index) => {
536
- const canAccept = !isPreview && canPortAcceptEdge(id, port.id, nodes, edges);
542
+ const canAccept = !isPreview && canPortAcceptEdge(nodeData, port.id, edges);
537
543
  return (
538
544
  <PortHandle
539
545
  key={port.id}