@kiberon-labs/behave-graph-flow 1.0.0 → 2.0.0

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 (314) hide show
  1. package/.fallowrc.json +16 -0
  2. package/.storybook/main.ts +32 -0
  3. package/.storybook/preview.ts +16 -0
  4. package/.storybook/styles.css +10 -0
  5. package/.storybook/vscode.css +814 -0
  6. package/.turbo/turbo-build.log +7 -0
  7. package/LICENSE +6 -0
  8. package/README.md +2 -2
  9. package/data/Polynomial.json +510 -0
  10. package/data/sequence.json +337 -0
  11. package/data/trigger-event.json +241 -0
  12. package/data/variable-change.json +210 -0
  13. package/dist/entry.css +4 -0
  14. package/dist/index.css +39 -0
  15. package/dist/index.css.map +1 -0
  16. package/dist/index.d.ts +2282 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +14873 -0
  19. package/dist/index.js.map +1 -0
  20. package/docs/notifications.md +246 -0
  21. package/docs/protocol.md +679 -0
  22. package/docs/specifics.md +191 -0
  23. package/package.json +85 -21
  24. package/postcss.config.ts +3 -4
  25. package/src/annotations/index.ts +32 -0
  26. package/src/components/FloatingToolbar/index.module.css +45 -0
  27. package/src/components/FloatingToolbar/index.tsx +256 -0
  28. package/src/components/Flow.tsx +276 -75
  29. package/src/components/contextMenus/NodePicker.module.css +274 -0
  30. package/src/components/contextMenus/NodePicker.tsx +481 -0
  31. package/src/components/contextMenus/edge.tsx +108 -0
  32. package/src/components/contextMenus/node.tsx +155 -0
  33. package/src/components/contextMenus/selection.tsx +77 -0
  34. package/src/components/controls/any/index.tsx +8 -0
  35. package/src/components/controls/boolean/index.tsx +13 -0
  36. package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
  37. package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
  38. package/src/components/controls/colorPicker/index.module.css +18 -0
  39. package/src/components/controls/colorPicker/index.tsx +61 -0
  40. package/src/components/controls/number/index.tsx +35 -0
  41. package/src/components/controls/string/index.tsx +16 -0
  42. package/src/components/edges/index.tsx +469 -0
  43. package/src/components/edges/offsetBezier.ts +134 -0
  44. package/src/components/hotKeys.tsx +20 -0
  45. package/src/components/layoutController/index.module.css +10 -0
  46. package/src/components/layoutController/index.tsx +117 -0
  47. package/src/components/layoutController/utils.ts +205 -0
  48. package/src/components/menubar/defaults.tsx +480 -0
  49. package/src/components/menubar/index.tsx +49 -0
  50. package/src/components/menubar/menuItem.module.css +16 -0
  51. package/src/components/menubar/menuItem.tsx +32 -0
  52. package/src/components/nodes/behave/Node.module.css +23 -0
  53. package/src/components/nodes/behave/Node.tsx +176 -0
  54. package/src/components/nodes/behave/NodeContainer.module.css +87 -0
  55. package/src/components/nodes/behave/NodeContainer.tsx +46 -0
  56. package/src/components/nodes/behave/index.tsx +14 -0
  57. package/src/components/nodes/comment/FormatToolbar.tsx +118 -0
  58. package/src/components/nodes/comment/comment.tsx +103 -0
  59. package/src/components/nodes/comment/styles.module.css +150 -0
  60. package/src/components/nodes/group/index.tsx +109 -0
  61. package/src/components/nodes/wrapper/index.tsx +73 -0
  62. package/src/components/nodes/wrapper/styles.module.css +113 -0
  63. package/src/components/notifications/NotificationProvider.tsx +81 -0
  64. package/src/components/notifications/index.ts +2 -0
  65. package/src/components/notifications/utils.ts +71 -0
  66. package/src/components/panels/alignment/index.module.css +20 -0
  67. package/src/components/panels/alignment/index.tsx +244 -0
  68. package/src/components/panels/base/index.tsx +5 -0
  69. package/src/components/panels/base/styles.module.css +12 -0
  70. package/src/components/panels/conversation/index.module.css +151 -0
  71. package/src/components/panels/conversation/index.tsx +162 -0
  72. package/src/components/panels/events/CustomEventsEditor.tsx +384 -0
  73. package/src/components/panels/events/EditEventPanel.tsx +315 -0
  74. package/src/components/panels/events/ManageEventsPanel.tsx +98 -0
  75. package/src/components/panels/events/index.tsx +23 -0
  76. package/src/components/panels/events/styles.module.css +236 -0
  77. package/src/components/panels/history/index.tsx +92 -0
  78. package/src/components/panels/history/styles.module.css +106 -0
  79. package/src/components/panels/keymaps/index.module.css +78 -0
  80. package/src/components/panels/keymaps/index.tsx +167 -0
  81. package/src/components/panels/layers/index.tsx +240 -0
  82. package/src/components/panels/layers/styles.module.css +110 -0
  83. package/src/components/panels/legend/index.module.css +6 -0
  84. package/src/components/panels/legend/index.tsx +76 -0
  85. package/src/components/panels/logs/index.module.css +212 -0
  86. package/src/components/panels/logs/index.tsx +288 -0
  87. package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
  88. package/src/components/panels/nodeInputs/InputsGroup.tsx +64 -0
  89. package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
  90. package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
  91. package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
  92. package/src/components/panels/nodeInputs/OutputsGroup.tsx +65 -0
  93. package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
  94. package/src/components/panels/nodeInputs/index.module.css +284 -0
  95. package/src/components/panels/nodeInputs/index.tsx +339 -0
  96. package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
  97. package/src/components/panels/nodeInputs/useNodeInputsData.ts +173 -0
  98. package/src/components/panels/nodePicker/index.tsx +115 -0
  99. package/src/components/panels/panel/index.module.css +66 -0
  100. package/src/components/panels/panel/index.tsx +88 -0
  101. package/src/components/panels/search/index.module.css +66 -0
  102. package/src/components/panels/search/index.tsx +215 -0
  103. package/src/components/panels/systemSettings/index.tsx +206 -0
  104. package/src/components/panels/systemSettings/styles.module.css +11 -0
  105. package/src/components/panels/traces/GridLines.tsx +38 -0
  106. package/src/components/panels/traces/TimeGrid.tsx +48 -0
  107. package/src/components/panels/traces/TraceLane.tsx +62 -0
  108. package/src/components/panels/traces/TraceTooltip.tsx +22 -0
  109. package/src/components/panels/traces/TracesHeader.tsx +56 -0
  110. package/src/components/panels/traces/index.module.css +166 -0
  111. package/src/components/panels/traces/index.tsx +294 -0
  112. package/src/components/panels/traces/types.ts +48 -0
  113. package/src/components/panels/traces/useDerivedSpans.ts +212 -0
  114. package/src/components/panels/traces/utils.ts +25 -0
  115. package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
  116. package/src/components/panels/variables/ManageVariablesScreen.tsx +144 -0
  117. package/src/components/panels/variables/index.tsx +125 -0
  118. package/src/components/panels/variables/styles.module.css +236 -0
  119. package/src/components/primitives/icon.module.css +45 -0
  120. package/src/components/primitives/icon.tsx +38 -0
  121. package/src/components/sockets/input/index.tsx +76 -0
  122. package/src/components/sockets/input/styles.module.css +27 -0
  123. package/src/components/sockets/output/index.tsx +61 -0
  124. package/src/components/sockets/output/styles.module.css +27 -0
  125. package/src/css/prosemirror.css +57 -0
  126. package/src/css/rc-dock.css +112 -0
  127. package/src/css/rc-menu.css +100 -0
  128. package/src/css/vars.css +14 -0
  129. package/src/css/vscode.css +13 -0
  130. package/src/entry.css +4 -0
  131. package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
  132. package/src/generators/SequenceGenerator.tsx +104 -0
  133. package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
  134. package/src/generators/SwitchOnStringGenerator.tsx +263 -0
  135. package/src/generators/registerDefaultGenerators.ts +34 -0
  136. package/src/hooks/useBehaveGraphFlow.ts +17 -16
  137. package/src/hooks/useDetachNodes.ts +39 -0
  138. package/src/hooks/useFlowHandlers.ts +115 -29
  139. package/src/hooks/useWasdPan.ts +188 -0
  140. package/src/index.css +146 -0
  141. package/src/index.ts +36 -18
  142. package/src/layout/dagre.tsx +119 -0
  143. package/src/layout/elk.ts +200 -0
  144. package/src/plugin/alignment/index.ts +81 -0
  145. package/src/plugin/docs/index.tsx +299 -0
  146. package/src/plugin/docs/panel/index.tsx +200 -0
  147. package/src/plugin/docs/panel/styles.module.css +174 -0
  148. package/src/plugin/graphrunner/actions.ts +253 -0
  149. package/src/plugin/graphrunner/buttons.tsx +87 -0
  150. package/src/plugin/graphrunner/client.ts +704 -0
  151. package/src/plugin/graphrunner/index.tsx +255 -0
  152. package/src/plugin/graphrunner/panel.tsx +386 -0
  153. package/src/plugin/graphrunner/runner.ts +358 -0
  154. package/src/plugin/graphrunner/session.ts +243 -0
  155. package/src/plugin/graphrunner/store.ts +206 -0
  156. package/src/plugin/graphrunner/styles.module.css +211 -0
  157. package/src/plugin/graphrunner/transport.ts +224 -0
  158. package/src/plugin/graphrunner/types.ts +672 -0
  159. package/src/plugin/graphrunner-local/execution-utils.ts +457 -0
  160. package/src/plugin/graphrunner-local/index.tsx +166 -0
  161. package/src/plugin/graphrunner-local/panel.tsx +231 -0
  162. package/src/plugin/graphrunner-local/store.ts +41 -0
  163. package/src/plugin/graphrunner-local/styles.module.css +101 -0
  164. package/src/plugin/graphrunner-local/transport.ts +1372 -0
  165. package/src/plugin/graphrunner-local/types.ts +10 -0
  166. package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +633 -0
  167. package/src/plugin/graphrunner-webworker/index.tsx +146 -0
  168. package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
  169. package/src/plugin/graphrunner-webworker/store.ts +89 -0
  170. package/src/plugin/graphrunner-webworker/types.ts +17 -0
  171. package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
  172. package/src/plugin/realtime/realtimeRunner.ts +570 -0
  173. package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
  174. package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
  175. package/src/specifics/VariableGetSpecific.tsx +110 -0
  176. package/src/specifics/VariableSetSpecific.tsx +110 -0
  177. package/src/specifics/registerDefaultSpecifics.ts +5 -0
  178. package/src/store/actions.tsx +698 -0
  179. package/src/store/chat.ts +73 -0
  180. package/src/store/controls.tsx +62 -0
  181. package/src/store/documentation.tsx +69 -0
  182. package/src/store/events.tsx +116 -0
  183. package/src/store/flow.tsx +245 -0
  184. package/src/store/graphRunnerClient.ts +110 -0
  185. package/src/store/hotKeys.tsx +323 -0
  186. package/src/store/layers.ts +259 -0
  187. package/src/store/legend.tsx +76 -0
  188. package/src/store/logs.ts +28 -0
  189. package/src/store/menubar.ts +41 -0
  190. package/src/store/refs.ts +84 -0
  191. package/src/store/registry.ts +43 -0
  192. package/src/store/selection.ts +22 -0
  193. package/src/store/settings.ts +99 -0
  194. package/src/store/socketGenerator.tsx +54 -0
  195. package/src/store/specific.tsx +75 -0
  196. package/src/store/specs.tsx +35 -0
  197. package/src/store/tabs.ts +278 -0
  198. package/src/store/toolbar.tsx +45 -0
  199. package/src/store/traces.ts +240 -0
  200. package/src/store/variables.ts +37 -0
  201. package/src/system/graph.ts +134 -0
  202. package/src/system/index.ts +3 -0
  203. package/src/system/notifications.ts +98 -0
  204. package/src/system/plugin.ts +27 -0
  205. package/src/system/provider.tsx +22 -0
  206. package/src/system/pubsub.ts +323 -0
  207. package/src/system/system.ts +223 -0
  208. package/src/system/tabLoader.tsx +265 -0
  209. package/src/system/undoRedo.ts +103 -0
  210. package/src/transformers/Uigraph.ts +60 -0
  211. package/src/transformers/behaveToFlow.ts +16 -4
  212. package/src/transformers/flowToBehave.ts +32 -12
  213. package/src/types/NodeMetadata.ts +27 -0
  214. package/src/types/graph.ts +49 -0
  215. package/src/types/nodes.ts +45 -0
  216. package/src/types.ts +16 -0
  217. package/src/util/colors.ts +1 -29
  218. package/src/util/downloadJson.ts +18 -0
  219. package/src/util/extractNodeMetadata.ts +16 -0
  220. package/src/util/getPickerFilters.ts +1 -1
  221. package/src/util/isBehaveNode.ts +6 -0
  222. package/src/util/isValidConnection.ts +28 -15
  223. package/src/util/mergeSockets.ts +29 -0
  224. package/src/util/serializeVariables.ts +66 -0
  225. package/src/util/sockets.ts +43 -0
  226. package/stories/apex/layoutController/example-graph.worker.ts +39 -0
  227. package/stories/apex/layoutController/index.stories.tsx +48 -0
  228. package/stories/apex/layoutController/webworker.stories.tsx +103 -0
  229. package/stories/apex/menubar/menubar.stories.tsx +19 -0
  230. package/stories/components/colorpicker/index.stories.tsx +20 -0
  231. package/stories/components/contextMenus/edge.stories.tsx +32 -0
  232. package/stories/components/contextMenus/node.stories.tsx +26 -0
  233. package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
  234. package/stories/components/controls/any/index.stories.tsx +19 -0
  235. package/stories/components/controls/boolean/index.stories.tsx +19 -0
  236. package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
  237. package/stories/components/controls/number/index.stories.tsx +19 -0
  238. package/stories/components/controls/string/index.stories.tsx +19 -0
  239. package/stories/components/nodes/behaveNode.stories.tsx +108 -0
  240. package/stories/components/nodes/comment.stories.tsx +106 -0
  241. package/stories/components/panels/alignment.stories.tsx +24 -0
  242. package/stories/components/panels/events.stories.tsx +38 -0
  243. package/stories/components/panels/graphRunner.stories.tsx +317 -0
  244. package/stories/components/panels/history.stories.tsx +37 -0
  245. package/stories/components/panels/keymaps.stories.tsx +21 -0
  246. package/stories/components/panels/legend.stories.tsx +37 -0
  247. package/stories/components/panels/logs.stories.tsx +24 -0
  248. package/stories/components/panels/nodeInputs.stories.tsx +21 -0
  249. package/stories/components/panels/nodePicker.stories.tsx +37 -0
  250. package/stories/components/panels/panel.stories.tsx +39 -0
  251. package/stories/components/panels/search.stories.tsx +24 -0
  252. package/stories/components/panels/systemSettings.stories.tsx +26 -0
  253. package/stories/components/panels/traces.stories.tsx +225 -0
  254. package/stories/components/panels/variables.stories.tsx +24 -0
  255. package/stories/defaults/defaultStoryProvider.tsx +167 -0
  256. package/stories/defaults/systemGenerator.ts +38 -0
  257. package/tests/components/edges/offsetBezier.test.ts +51 -0
  258. package/tests/components/layoutController/utils.test.ts +68 -0
  259. package/tests/components/panels/traces/utils.test.ts +52 -0
  260. package/tests/flowToBehave.test.ts +26 -4
  261. package/tests/notifications.test.ts +87 -0
  262. package/tests/saveLoad.test.ts +372 -0
  263. package/tests/util/calculateNewEdge.test.ts +98 -0
  264. package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
  265. package/tests/util/hasPositionMetaData.test.ts +33 -0
  266. package/tests/util/isBehaveNode.test.ts +22 -0
  267. package/tests/util/isHandleConnected.test.ts +37 -0
  268. package/tests/util/mergeSockets.test.ts +43 -0
  269. package/tests/visual/README.md +64 -0
  270. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
  271. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
  272. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
  273. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
  274. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
  275. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
  276. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
  277. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
  278. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
  279. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
  280. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
  281. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
  282. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
  283. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
  284. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
  285. package/tests/visual/panels.visual.test.tsx +76 -0
  286. package/tsconfig.base.json +39 -0
  287. package/tsconfig.json +18 -59
  288. package/tsconfig.prod.json +23 -0
  289. package/tsdown.config.ts +15 -3
  290. package/typedoc.json +7 -7
  291. package/vite.config.js +7 -0
  292. package/vitest.config.ts +5 -2
  293. package/vitest.visual.config.ts +48 -0
  294. package/src/components/AutoSizeInput.tsx +0 -65
  295. package/src/components/Controls.tsx +0 -87
  296. package/src/components/InputSocket.tsx +0 -142
  297. package/src/components/Node.tsx +0 -68
  298. package/src/components/NodeContainer.tsx +0 -46
  299. package/src/components/NodePicker.tsx +0 -77
  300. package/src/components/OutputSocket.tsx +0 -58
  301. package/src/components/modals/ClearModal.tsx +0 -40
  302. package/src/components/modals/HelpModal.tsx +0 -36
  303. package/src/components/modals/LoadModal.tsx +0 -96
  304. package/src/components/modals/Modal.tsx +0 -64
  305. package/src/components/modals/SaveModal.tsx +0 -60
  306. package/src/hooks/useCustomNodeTypes.tsx +0 -31
  307. package/src/hooks/useGraphRunner.ts +0 -104
  308. package/src/hooks/useMergeMap.ts +0 -14
  309. package/src/hooks/useNodeSpecJson.ts +0 -20
  310. package/src/hooks/useQueriableDefinitions.ts +0 -22
  311. package/src/styles.css +0 -8
  312. package/tailwind.config.ts +0 -19
  313. package/tests/tsconfig.json +0 -10
  314. /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
@@ -0,0 +1,263 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import {
3
+ VscodeButton,
4
+ VscodeTextfield,
5
+ VscodeSingleSelect,
6
+ VscodeOption
7
+ } from '@vscode-elements/react-elements';
8
+ import { Plus, Minus } from 'iconoir-react';
9
+
10
+ import { useSystem } from '@/system/provider';
11
+ import type { SocketGeneratorRenderProps } from '@/store/socketGenerator';
12
+ import type { NodeSpecJSON } from '@kiberon-labs/behave-graph/Graphs/IO/NodeSpecJSON';
13
+
14
+ const NAME = 'flow/switch/string.socketGenerator';
15
+
16
+ export function getSwitchOnStringGenerator() {
17
+ return {
18
+ name: NAME,
19
+ check: (spec: NodeSpecJSON) => spec?.type === 'flow/switch/string',
20
+ render: SwitchOnStringGenerator
21
+ };
22
+ }
23
+
24
+ const SwitchOnStringGenerator: React.FC<SocketGeneratorRenderProps> = ({
25
+ node
26
+ }) => {
27
+ const system = useSystem();
28
+
29
+ const numCases = node.data.configuration?.numCases ?? 0;
30
+ const caseLabels = node.data.configuration?.caseLabels ?? {};
31
+ const caseValues = node.data.configuration?.cases ?? {};
32
+
33
+ const [selectedCase, setSelectedCase] = useState<number>(1);
34
+
35
+ const updateNumCases = useCallback(
36
+ (newNumCases: number) => {
37
+ if (newNumCases < 0) return;
38
+
39
+ system.nodeStore.getState().setNodes((prev) =>
40
+ prev.map((n: any) => {
41
+ if (n.id !== node.id) return n;
42
+
43
+ // Preserve existing case values when changing numCases
44
+ const existingCases = n.data?.configuration?.cases ?? {};
45
+ const newCases: Record<string, string> = {};
46
+ for (let i = 1; i <= newNumCases; i++) {
47
+ const key = `${i}`;
48
+ newCases[key] = existingCases[key] ?? `case${i}`;
49
+ }
50
+
51
+ // Generate input sockets for each case
52
+ const inputs = [];
53
+ for (let i = 1; i <= newNumCases; i++) {
54
+ const key = `${i}`;
55
+ inputs.push({
56
+ name: n.data?.configuration?.caseLabels?.[key] || `Case ${i}`,
57
+ key,
58
+ valueType: 'string'
59
+ });
60
+ }
61
+
62
+ // Generate output sockets for each case
63
+ const outputs = [];
64
+ for (let i = 1; i <= newNumCases; i++) {
65
+ const label =
66
+ n.data?.configuration?.caseLabels?.[`${i}`] || `Case ${i}`;
67
+ outputs.push({
68
+ name: label,
69
+ key: `${i}`,
70
+ valueType: 'flow'
71
+ });
72
+ }
73
+ // Add default output
74
+ outputs.push({
75
+ name: 'default',
76
+ key: 'default',
77
+ valueType: 'flow'
78
+ });
79
+
80
+ return {
81
+ ...n,
82
+ data: {
83
+ ...n.data,
84
+ configuration: {
85
+ ...n.data?.configuration,
86
+ numCases: newNumCases,
87
+ cases: newCases
88
+ },
89
+ dynamicPorts: {
90
+ ...n.data?.dynamicPorts,
91
+ inputs,
92
+ outputs
93
+ }
94
+ }
95
+ };
96
+ })
97
+ );
98
+
99
+ if (selectedCase > newNumCases && newNumCases > 0) {
100
+ setSelectedCase(newNumCases);
101
+ }
102
+ },
103
+ [node.id, system, selectedCase]
104
+ );
105
+
106
+ const updateCaseValue = useCallback(
107
+ (caseIndex: number, value: string) => {
108
+ system.nodeStore.getState().setNodes((prev) =>
109
+ prev.map((n: any) => {
110
+ if (n.id !== node.id) return n;
111
+
112
+ const updatedCases = {
113
+ ...n.data?.configuration?.cases,
114
+ [`${caseIndex}`]: value
115
+ };
116
+
117
+ const updatedLabels = {
118
+ ...n.data?.configuration?.caseLabels,
119
+ [`${caseIndex}`]: value || `Case ${caseIndex}`
120
+ };
121
+
122
+ // Update input sockets
123
+ const numCases = n.data?.configuration?.numCases ?? 0;
124
+ const inputs = [];
125
+ for (let i = 1; i <= numCases; i++) {
126
+ const key = `${i}`;
127
+ inputs.push({
128
+ name: updatedLabels[key] || `Case ${i}`,
129
+ key,
130
+ valueType: 'string'
131
+ });
132
+ }
133
+
134
+ // Regenerate output sockets with updated labels
135
+ const outputs = [];
136
+ for (let i = 1; i <= numCases; i++) {
137
+ const label = updatedLabels[`${i}`] || `Case ${i}`;
138
+ outputs.push({
139
+ name: label,
140
+ key: `${i}`,
141
+ valueType: 'flow'
142
+ });
143
+ }
144
+ outputs.push({
145
+ name: 'default',
146
+ key: 'default',
147
+ valueType: 'flow'
148
+ });
149
+
150
+ return {
151
+ ...n,
152
+ data: {
153
+ ...n.data,
154
+ dynamicPorts: {
155
+ ...n.data?.dynamicPorts,
156
+ inputs,
157
+ outputs
158
+ },
159
+ configuration: {
160
+ ...n.data?.configuration,
161
+ cases: updatedCases,
162
+ caseLabels: updatedLabels
163
+ }
164
+ }
165
+ };
166
+ })
167
+ );
168
+ },
169
+ [node.id, system]
170
+ );
171
+
172
+ const addCase = useCallback(() => {
173
+ const newCount = numCases + 1;
174
+ updateNumCases(newCount);
175
+ setSelectedCase(newCount);
176
+ }, [numCases, updateNumCases]);
177
+
178
+ const removeCase = useCallback(() => {
179
+ updateNumCases(Math.max(0, numCases - 1));
180
+ }, [numCases, updateNumCases]);
181
+
182
+ return (
183
+ <div
184
+ style={{
185
+ display: 'flex',
186
+ flexDirection: 'column',
187
+ gap: 8
188
+ }}
189
+ >
190
+ <div
191
+ style={{
192
+ display: 'flex',
193
+ alignItems: 'center',
194
+ justifyContent: 'space-between',
195
+ marginBottom: 4
196
+ }}
197
+ >
198
+ <div style={{ fontSize: 12, opacity: 0.9 }}>Cases: {numCases}</div>
199
+ </div>
200
+
201
+ {numCases > 0 && (
202
+ <>
203
+ <div style={{ fontSize: 11, opacity: 0.8, marginBottom: -4 }}>
204
+ Edit Case
205
+ </div>
206
+ <VscodeSingleSelect
207
+ value={String(selectedCase)}
208
+ onChange={(e: any) => setSelectedCase(Number(e.target.value))}
209
+ >
210
+ {Array.from({ length: numCases }, (_, i) => i + 1).map(
211
+ (caseIndex) => (
212
+ <VscodeOption key={caseIndex} value={String(caseIndex)}>
213
+ {caseLabels[`${caseIndex}`] || `Case ${caseIndex}`}
214
+ </VscodeOption>
215
+ )
216
+ )}
217
+ </VscodeSingleSelect>
218
+
219
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
220
+ <label style={{ fontSize: 11, opacity: 0.8 }}>Match Value</label>
221
+ <VscodeTextfield
222
+ value={caseValues[`${selectedCase}`] ?? ''}
223
+ onInput={(e: any) =>
224
+ updateCaseValue(selectedCase, e.target.value)
225
+ }
226
+ placeholder={`String value for case ${selectedCase}`}
227
+ />
228
+ </div>
229
+ </>
230
+ )}
231
+
232
+ <div style={{ display: 'flex', gap: 4, marginTop: 4 }}>
233
+ <VscodeButton
234
+ onClick={addCase}
235
+ style={{
236
+ flex: 1,
237
+ display: 'flex',
238
+ alignItems: 'center',
239
+ justifyContent: 'center',
240
+ gap: 4
241
+ }}
242
+ >
243
+ <Plus width={16} height={16} />
244
+ Add Case
245
+ </VscodeButton>
246
+ <VscodeButton
247
+ onClick={removeCase}
248
+ disabled={numCases === 0}
249
+ style={{
250
+ flex: 1,
251
+ display: 'flex',
252
+ alignItems: 'center',
253
+ justifyContent: 'center',
254
+ gap: 4
255
+ }}
256
+ >
257
+ <Minus width={16} height={16} />
258
+ Remove
259
+ </VscodeButton>
260
+ </div>
261
+ </div>
262
+ );
263
+ };
@@ -0,0 +1,34 @@
1
+ import type { System } from '@/system/system';
2
+ import { getSwitchOnStringGenerator } from '../generators/SwitchOnStringGenerator';
3
+ import { getSwitchOnIntegerGenerator } from '../generators/SwitchOnIntegerGenerator';
4
+ import { getCustomEventOnTriggeredGenerator } from '../generators/CustomEventOnTriggeredGenerator';
5
+ import { getSequenceGenerator } from '../generators/SequenceGenerator';
6
+
7
+ export function registerDefaultSocketGenerators(system: System): () => void {
8
+ const store = system.socketGeneratorStore.getState();
9
+
10
+ const switchOnString = getSwitchOnStringGenerator();
11
+ store.registerGenerator(switchOnString);
12
+
13
+ const switchOnInteger = getSwitchOnIntegerGenerator();
14
+ store.registerGenerator(switchOnInteger);
15
+
16
+ const customEventOnTriggered = getCustomEventOnTriggeredGenerator();
17
+ store.registerGenerator(customEventOnTriggered);
18
+
19
+ const sequence = getSequenceGenerator();
20
+ store.registerGenerator(sequence);
21
+
22
+ return () => {
23
+ system.socketGeneratorStore
24
+ .getState()
25
+ .unregisterGenerator(switchOnString.name);
26
+ system.socketGeneratorStore
27
+ .getState()
28
+ .unregisterGenerator(switchOnInteger.name);
29
+ system.socketGeneratorStore
30
+ .getState()
31
+ .unregisterGenerator(customEventOnTriggered.name);
32
+ system.socketGeneratorStore.getState().unregisterGenerator(sequence.name);
33
+ };
34
+ }
@@ -1,16 +1,11 @@
1
1
  import type { GraphJSON, NodeSpecJSON } from '@kiberon-labs/behave-graph';
2
2
  import { useCallback, useEffect, useState } from 'react';
3
- import { useEdgesState, useNodesState } from 'reactflow';
4
-
5
3
  import { behaveToFlow } from '../transformers/behaveToFlow.js';
6
4
  import { flowToBehave } from '../transformers/flowToBehave.js';
7
5
  import { autoLayout } from '../util/autoLayout.js';
8
6
  import { hasPositionMetaData } from '../util/hasPositionMetaData.js';
9
- import { useCustomNodeTypes } from './useCustomNodeTypes.js';
10
-
11
- export const fetchBehaviorGraphJson = async (url: string) =>
12
- // eslint-disable-next-line unicorn/no-await-expression-member
13
- (await (await fetch(url)).json()) as GraphJSON;
7
+ import { useSystem } from '@/system/provider.js';
8
+ import { useStore } from 'zustand';
14
9
 
15
10
  /**
16
11
  * Hook that returns the nodes and edges for react-flow, and the graphJson for the behave-graph.
@@ -27,8 +22,19 @@ export const useBehaveGraphFlow = ({
27
22
  specJson: NodeSpecJSON[] | undefined;
28
23
  }) => {
29
24
  const [graphJson, setStoredGraphJson] = useState<GraphJSON | undefined>();
30
- const [nodes, setNodes, onNodesChange] = useNodesState([]);
31
- const [edges, setEdges, onEdgesChange] = useEdgesState([]);
25
+ const sys = useSystem();
26
+
27
+ const {
28
+ nodes,
29
+ setNodes,
30
+ applyNodeChanges: onNodesChange
31
+ } = useStore(sys.nodeStore);
32
+
33
+ const {
34
+ edges,
35
+ setEdges,
36
+ applyEdgeChanges: onEdgesChange
37
+ } = useStore(sys.edgeStore);
32
38
 
33
39
  const setGraphJson = useCallback(
34
40
  (graphJson: GraphJSON) => {
@@ -55,21 +61,16 @@ export const useBehaveGraphFlow = ({
55
61
  useEffect(() => {
56
62
  if (!specJson) return;
57
63
  // when nodes and edges are updated, update the graph json with the flow to behave behavior
58
- const graphJson = flowToBehave(nodes, edges, specJson);
64
+ const graphJson = flowToBehave(sys, nodes, edges, specJson);
59
65
  setStoredGraphJson(graphJson);
60
66
  }, [nodes, edges, specJson]);
61
67
 
62
- const nodeTypes = useCustomNodeTypes({
63
- specJson
64
- });
65
-
66
68
  return {
67
69
  nodes,
68
70
  edges,
69
71
  onEdgesChange,
70
72
  onNodesChange,
71
73
  setGraphJson,
72
- graphJson,
73
- nodeTypes
74
+ graphJson
74
75
  };
75
76
  };
@@ -0,0 +1,39 @@
1
+ import { useSystem } from '@/system';
2
+ import { useCallback } from 'react';
3
+ import { useStoreApi } from 'reactflow';
4
+ import { useStore } from 'zustand';
5
+
6
+ export function useDetachNodes() {
7
+ const sys = useSystem();
8
+ const setNodes = useStore(sys.nodeStore, (x) => x.setNodes);
9
+ const store = useStoreApi();
10
+
11
+ const detachNodes = useCallback(
12
+ (ids: string[], removeParentId?: string) => {
13
+ const { nodeInternals } = store.getState();
14
+ const nextNodes = Array.from(nodeInternals.values()).map((n) => {
15
+ if (ids.includes(n.id) && n.parentId) {
16
+ const parentNode = nodeInternals.get(n.parentId);
17
+
18
+ //Remove parent reference and recalculate in global space
19
+ return {
20
+ ...n,
21
+ position: {
22
+ x: n.position.x + (parentNode?.positionAbsolute?.x ?? 0),
23
+ y: n.position.y + (parentNode?.positionAbsolute?.y ?? 0)
24
+ },
25
+ extent: undefined,
26
+ parentId: undefined
27
+ };
28
+ }
29
+ return n;
30
+ });
31
+ setNodes(
32
+ nextNodes.filter((n) => !removeParentId || n.id !== removeParentId)
33
+ );
34
+ },
35
+ [setNodes, store]
36
+ );
37
+
38
+ return detachNodes;
39
+ }
@@ -7,6 +7,7 @@ import {
7
7
  } from 'react';
8
8
  import type {
9
9
  Connection,
10
+ ConnectionStatus,
10
11
  Node,
11
12
  OnConnectStartParams,
12
13
  XYPosition
@@ -16,6 +17,13 @@ import { v4 as uuidv4 } from 'uuid';
16
17
  import { calculateNewEdge } from '../util/calculateNewEdge.js';
17
18
  import { getNodePickerFilters } from '../util/getPickerFilters.js';
18
19
  import { useBehaveGraphFlow } from './useBehaveGraphFlow.js';
20
+ import { useSystem } from '@/system/provider.js';
21
+ import type { ExtendedNodeSpecJSON } from '@/components/contextMenus/NodePicker.js';
22
+ import {
23
+ addFloatingTab,
24
+ findTabInLayout,
25
+ removeTabFromLayout
26
+ } from '@/components/layoutController/utils.js';
19
27
 
20
28
  type BehaveGraphFlow = ReturnType<typeof useBehaveGraphFlow>;
21
29
 
@@ -48,11 +56,11 @@ export const useFlowHandlers = ({
48
56
  nodes: Node[];
49
57
  specJSON: NodeSpecJSON[] | undefined;
50
58
  }) => {
59
+ const sys = useSystem();
51
60
  const [lastConnectStart, setLastConnectStart] =
52
61
  useState<OnConnectStartParams>();
53
62
  const [nodePickerVisibility, setNodePickerVisibility] =
54
63
  useState<XYPosition>();
55
-
56
64
  const onConnect = useCallback(
57
65
  (connection: Connection) => {
58
66
  if (connection.source === null) return;
@@ -65,6 +73,7 @@ export const useFlowHandlers = ({
65
73
  sourceHandle: connection.sourceHandle,
66
74
  targetHandle: connection.targetHandle
67
75
  };
76
+ sys.pubsub.publish('edge:added', newEdge);
68
77
  onEdgesChange([
69
78
  {
70
79
  type: 'add',
@@ -78,23 +87,40 @@ export const useFlowHandlers = ({
78
87
  const closeNodePicker = useCallback(() => {
79
88
  setLastConnectStart(undefined);
80
89
  setNodePickerVisibility(undefined);
81
- }, []);
90
+
91
+ // Close the nodepicker panel from rc-dock
92
+ const currentLayout = sys.tabStore.getState().layout;
93
+ const newLayout = removeTabFromLayout(currentLayout, 'nodepicker');
94
+ sys.tabStore.getState().setLayout(newLayout);
95
+ }, [sys]);
82
96
 
83
97
  const handleAddNode = useCallback(
84
- (nodeType: string, position: XYPosition) => {
98
+ (spec: ExtendedNodeSpecJSON, position: XYPosition) => {
85
99
  closeNodePicker();
86
100
  const newNode = {
87
101
  id: uuidv4(),
88
- type: nodeType,
102
+ type: spec.nodeType ?? 'behaveNode',
89
103
  position,
90
- data: {}
104
+ data: {
105
+ configuration: {},
106
+ type: spec.type,
107
+ ports: {}
108
+ }
91
109
  };
92
- onNodesChange([
93
- {
94
- type: 'add',
95
- item: newNode
110
+
111
+ sys.undoManager.execute({
112
+ name: `Add node (${spec.type})`,
113
+ execute: () => {
114
+ sys.nodeStore.getState().addNode(newNode);
115
+ },
116
+ undo: () => {
117
+ sys.nodeStore
118
+ .getState()
119
+ .setNodes((existing) =>
120
+ existing.filter((n) => n.id !== newNode.id)
121
+ );
96
122
  }
97
- ]);
123
+ });
98
124
 
99
125
  if (lastConnectStart === undefined) return;
100
126
 
@@ -109,7 +135,7 @@ export const useFlowHandlers = ({
109
135
  type: 'add',
110
136
  item: calculateNewEdge(
111
137
  originNode,
112
- nodeType,
138
+ spec.type,
113
139
  newNode.id,
114
140
  lastConnectStart,
115
141
  specJSON
@@ -134,30 +160,90 @@ export const useFlowHandlers = ({
134
160
  []
135
161
  );
136
162
 
137
- const handleStopConnect = useCallback((e: MouseEvent) => {
138
- const element = e.target as HTMLElement;
139
- console.log(
140
- 'here',
141
- element.classList,
142
- element.classList.contains('react-flow__pane')
143
- );
144
- if (element.classList.contains('react-flow__pane')) {
145
- console.log('setting node picker');
146
- setNodePickerVisibility({ x: e.clientX, y: e.clientY });
147
- } else {
148
- setLastConnectStart(undefined);
149
- }
150
- }, []);
163
+ const handleStopConnect = useCallback(
164
+ (e: MouseEvent, _connectionState: ConnectionStatus) => {
165
+ const element = e.target as HTMLElement;
166
+ if (element.classList.contains('react-flow__pane')) {
167
+ const screenPos = { x: e.clientX, y: e.clientY };
168
+ setNodePickerVisibility(screenPos);
169
+
170
+ // Store screen position for NodePickerPanel to use
171
+ sys.refStore.getState().setRef('nodePickerPosition', screenPos);
172
+
173
+ // Open as floating rc-dock panel
174
+ const currentLayout = sys.tabStore.getState().layout;
175
+
176
+ // Close existing nodepicker if open
177
+ const existingPanel = findTabInLayout(currentLayout, 'nodepicker');
178
+ let layoutToUse = currentLayout;
179
+ if (existingPanel) {
180
+ layoutToUse = removeTabFromLayout(currentLayout, 'nodepicker');
181
+ }
182
+
183
+ // Create new floating panel with minimal tab data
184
+ // The actual content will be loaded by tabLoader
185
+ const tabData = {
186
+ id: 'nodepicker',
187
+ group: 'headless'
188
+ };
189
+
190
+ const newLayout = addFloatingTab(layoutToUse, tabData, {
191
+ left: e.clientX,
192
+ top: e.clientY,
193
+ width: 600,
194
+ height: 500
195
+ });
196
+
197
+ sys.tabStore.getState().setLayout(newLayout);
198
+ } else {
199
+ setLastConnectStart(undefined);
200
+ }
201
+ },
202
+ [sys]
203
+ );
151
204
 
152
205
  const handlePaneClick = useCallback(
153
206
  () => closeNodePicker(),
154
207
  [closeNodePicker]
155
208
  );
156
209
 
157
- const handlePaneContextMenu = useCallback((e: ReactMouseEvent) => {
158
- e.preventDefault();
159
- setNodePickerVisibility({ x: e.clientX, y: e.clientY });
160
- }, []);
210
+ const handlePaneContextMenu = useCallback(
211
+ (e: ReactMouseEvent) => {
212
+ e.preventDefault();
213
+ const screenPos = { x: e.clientX, y: e.clientY };
214
+ setNodePickerVisibility(screenPos);
215
+
216
+ // Store screen position for NodePickerPanel to use
217
+ sys.refStore.getState().setRef('nodePickerPosition', screenPos);
218
+
219
+ // Open as floating rc-dock panel
220
+ const currentLayout = sys.tabStore.getState().layout;
221
+
222
+ // Close existing nodepicker if open
223
+ const existingPanel = findTabInLayout(currentLayout, 'nodepicker');
224
+ let layoutToUse = currentLayout;
225
+ if (existingPanel) {
226
+ layoutToUse = removeTabFromLayout(currentLayout, 'nodepicker');
227
+ }
228
+
229
+ // Create new floating panel with minimal tab data
230
+ // The actual content will be loaded by tabLoader
231
+ const tabData = {
232
+ id: 'nodepicker',
233
+ group: 'headless'
234
+ };
235
+
236
+ const newLayout = addFloatingTab(layoutToUse, tabData, {
237
+ left: e.clientX,
238
+ top: e.clientY,
239
+ width: 600,
240
+ height: 500
241
+ });
242
+
243
+ sys.tabStore.getState().setLayout(newLayout);
244
+ },
245
+ [sys]
246
+ );
161
247
 
162
248
  const nodePickFilters = useNodePickFilters({
163
249
  nodes,