@kiberon-labs/behave-graph-flow 1.0.0 → 3.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 (378) hide show
  1. package/.fallowrc.json +16 -0
  2. package/.storybook/main.ts +32 -0
  3. package/.storybook/manager.ts +6 -0
  4. package/.storybook/preview.ts +64 -0
  5. package/.storybook/styles.css +16 -0
  6. package/.turbo/turbo-build.log +7 -0
  7. package/CHANGELOG.md +368 -0
  8. package/LICENSE +6 -0
  9. package/README.md +2 -2
  10. package/data/Polynomial.json +510 -0
  11. package/data/sequence.json +337 -0
  12. package/data/trigger-event.json +241 -0
  13. package/data/variable-change.json +210 -0
  14. package/dist/AnyControlImpl-Ds-CShIB.js +20 -0
  15. package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
  16. package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
  17. package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
  18. package/dist/entry.css +4 -0
  19. package/dist/index.css +42 -0
  20. package/dist/index.css.map +1 -0
  21. package/dist/index.d.ts +3597 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +18009 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/noteImpl-KkrrWgJd.js +242 -0
  26. package/dist/noteImpl-KkrrWgJd.js.map +1 -0
  27. package/dist/styles.module-CvmpDkZj.css +3 -0
  28. package/dist/styles.module-CvmpDkZj.css.map +1 -0
  29. package/dist/styles.module-DZxg8aW9.js +271 -0
  30. package/dist/styles.module-DZxg8aW9.js.map +1 -0
  31. package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
  32. package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
  33. package/docs/notifications.md +246 -0
  34. package/docs/protocol.md +702 -0
  35. package/docs/specifics.md +191 -0
  36. package/package.json +82 -22
  37. package/postcss.config.ts +3 -4
  38. package/src/annotations/index.ts +32 -0
  39. package/src/components/FloatingToolbar/index.module.css +37 -0
  40. package/src/components/FloatingToolbar/index.tsx +256 -0
  41. package/src/components/Flow.tsx +287 -75
  42. package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
  43. package/src/components/contextMenus/NodePicker.module.css +274 -0
  44. package/src/components/contextMenus/NodePicker.tsx +481 -0
  45. package/src/components/contextMenus/edge.tsx +22 -0
  46. package/src/components/contextMenus/node.tsx +15 -0
  47. package/src/components/contextMenus/selection.tsx +11 -0
  48. package/src/components/controls/any/AnyControlImpl.tsx +14 -0
  49. package/src/components/controls/any/index.tsx +19 -0
  50. package/src/components/controls/boolean/index.tsx +13 -0
  51. package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
  52. package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
  53. package/src/components/controls/colorPicker/index.module.css +18 -0
  54. package/src/components/controls/colorPicker/index.tsx +61 -0
  55. package/src/components/controls/number/index.tsx +35 -0
  56. package/src/components/controls/string/index.tsx +16 -0
  57. package/src/components/edges/index.tsx +475 -0
  58. package/src/components/edges/offsetBezier.ts +134 -0
  59. package/src/components/hotKeys.tsx +20 -0
  60. package/src/components/layoutController/index.module.css +13 -0
  61. package/src/components/layoutController/index.tsx +140 -0
  62. package/src/components/layoutController/utils.ts +248 -0
  63. package/src/components/menubar/defaults.tsx +516 -0
  64. package/src/components/menubar/index.tsx +49 -0
  65. package/src/components/menubar/menuItem.module.css +31 -0
  66. package/src/components/menubar/menuItem.tsx +65 -0
  67. package/src/components/nodes/behave/Node.module.css +23 -0
  68. package/src/components/nodes/behave/Node.tsx +176 -0
  69. package/src/components/nodes/behave/NodeContainer.module.css +88 -0
  70. package/src/components/nodes/behave/NodeContainer.tsx +46 -0
  71. package/src/components/nodes/behave/index.tsx +14 -0
  72. package/src/components/nodes/group/index.tsx +109 -0
  73. package/src/components/nodes/wrapper/index.tsx +73 -0
  74. package/src/components/nodes/wrapper/styles.module.css +87 -0
  75. package/src/components/notifications/NotificationProvider.tsx +81 -0
  76. package/src/components/notifications/index.ts +2 -0
  77. package/src/components/notifications/utils.ts +71 -0
  78. package/src/components/panels/alignment/index.module.css +10 -0
  79. package/src/components/panels/alignment/index.tsx +244 -0
  80. package/src/components/panels/base/index.tsx +5 -0
  81. package/src/components/panels/base/styles.module.css +12 -0
  82. package/src/components/panels/common/PanelHeader.module.css +24 -0
  83. package/src/components/panels/common/PanelHeader.tsx +22 -0
  84. package/src/components/panels/common/SectionTitle.module.css +13 -0
  85. package/src/components/panels/common/SectionTitle.tsx +10 -0
  86. package/src/components/panels/events/EditEventPanel.tsx +324 -0
  87. package/src/components/panels/events/ManageEventsPanel.tsx +101 -0
  88. package/src/components/panels/events/index.tsx +23 -0
  89. package/src/components/panels/events/styles.module.css +178 -0
  90. package/src/components/panels/graphProperties/index.tsx +125 -0
  91. package/src/components/panels/history/index.tsx +92 -0
  92. package/src/components/panels/history/styles.module.css +97 -0
  93. package/src/components/panels/keymaps/index.module.css +68 -0
  94. package/src/components/panels/keymaps/index.tsx +166 -0
  95. package/src/components/panels/layers/index.tsx +245 -0
  96. package/src/components/panels/layers/styles.module.css +107 -0
  97. package/src/components/panels/legend/index.module.css +6 -0
  98. package/src/components/panels/legend/index.tsx +76 -0
  99. package/src/components/panels/logs/index.module.css +218 -0
  100. package/src/components/panels/logs/index.tsx +288 -0
  101. package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
  102. package/src/components/panels/nodeInputs/InputsGroup.tsx +65 -0
  103. package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
  104. package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
  105. package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
  106. package/src/components/panels/nodeInputs/OutputsGroup.tsx +55 -0
  107. package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
  108. package/src/components/panels/nodeInputs/index.module.css +308 -0
  109. package/src/components/panels/nodeInputs/index.tsx +349 -0
  110. package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
  111. package/src/components/panels/nodeInputs/useNodeInputsData.ts +153 -0
  112. package/src/components/panels/nodePicker/index.tsx +115 -0
  113. package/src/components/panels/panel/index.module.css +66 -0
  114. package/src/components/panels/panel/index.tsx +88 -0
  115. package/src/components/panels/search/index.module.css +16 -0
  116. package/src/components/panels/search/index.tsx +215 -0
  117. package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
  118. package/src/components/panels/systemSettings/index.tsx +251 -0
  119. package/src/components/panels/systemSettings/styles.module.css +138 -0
  120. package/src/components/panels/traces/GridLines.tsx +38 -0
  121. package/src/components/panels/traces/TimeGrid.tsx +48 -0
  122. package/src/components/panels/traces/TraceLane.tsx +62 -0
  123. package/src/components/panels/traces/TraceTooltip.tsx +22 -0
  124. package/src/components/panels/traces/TracesHeader.tsx +56 -0
  125. package/src/components/panels/traces/index.module.css +159 -0
  126. package/src/components/panels/traces/index.tsx +298 -0
  127. package/src/components/panels/traces/types.ts +48 -0
  128. package/src/components/panels/traces/useDerivedSpans.ts +307 -0
  129. package/src/components/panels/traces/utils.ts +33 -0
  130. package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
  131. package/src/components/panels/variables/ManageVariablesScreen.tsx +147 -0
  132. package/src/components/panels/variables/index.tsx +125 -0
  133. package/src/components/panels/variables/styles.module.css +149 -0
  134. package/src/components/primitives/icon.module.css +45 -0
  135. package/src/components/primitives/icon.tsx +38 -0
  136. package/src/components/sockets/input/index.tsx +83 -0
  137. package/src/components/sockets/input/styles.module.css +26 -0
  138. package/src/components/sockets/output/index.tsx +68 -0
  139. package/src/components/sockets/output/styles.module.css +22 -0
  140. package/src/css/notes.css +135 -0
  141. package/src/css/prosemirror.css +57 -0
  142. package/src/css/rc-dock.css +212 -0
  143. package/src/css/rc-menu.css +101 -0
  144. package/src/css/themes/kiberon.css +127 -0
  145. package/src/css/vars.css +198 -0
  146. package/src/css/vscode-elements.css +124 -0
  147. package/src/entry.css +4 -0
  148. package/src/generators/CallSubgraphGenerator.tsx +136 -0
  149. package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
  150. package/src/generators/GraphBoundaryGenerator.module.css +32 -0
  151. package/src/generators/GraphBoundaryGenerator.tsx +193 -0
  152. package/src/generators/SequenceGenerator.tsx +104 -0
  153. package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
  154. package/src/generators/SwitchOnStringGenerator.tsx +263 -0
  155. package/src/generators/callSubgraphSync.ts +126 -0
  156. package/src/generators/registerDefaultGenerators.ts +55 -0
  157. package/src/generators/registerDefaults.ts +26 -0
  158. package/src/hooks/useBehaveGraphFlow.ts +17 -16
  159. package/src/hooks/useFlowHandlers.ts +154 -30
  160. package/src/hooks/useWasdPan.ts +210 -0
  161. package/src/index.css +134 -0
  162. package/src/index.ts +53 -18
  163. package/src/manifest/contributionRegistry.ts +93 -0
  164. package/src/manifest/index.ts +4 -0
  165. package/src/manifest/loadManifest.ts +82 -0
  166. package/src/manifest/manifestPlugin.ts +29 -0
  167. package/src/manifest/passthroughValueType.ts +40 -0
  168. package/src/plugin/alignment/index.ts +91 -0
  169. package/src/plugin/autosave/controller.ts +366 -0
  170. package/src/plugin/autosave/index.tsx +114 -0
  171. package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
  172. package/src/plugin/autosave/panel/index.tsx +1 -0
  173. package/src/plugin/autosave/panel/styles.module.css +56 -0
  174. package/src/plugin/autosave/settings.ts +65 -0
  175. package/src/plugin/autosave/storage.ts +147 -0
  176. package/src/plugin/docs/index.tsx +297 -0
  177. package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
  178. package/src/plugin/docs/panel/index.tsx +21 -0
  179. package/src/plugin/docs/panel/styles.module.css +174 -0
  180. package/src/plugin/graphrunner/actions.ts +326 -0
  181. package/src/plugin/graphrunner/buttons.tsx +95 -0
  182. package/src/plugin/graphrunner/client.ts +707 -0
  183. package/src/plugin/graphrunner/index.tsx +184 -0
  184. package/src/plugin/graphrunner/panel.tsx +386 -0
  185. package/src/plugin/graphrunner/runController.ts +283 -0
  186. package/src/plugin/graphrunner/runner.ts +187 -0
  187. package/src/plugin/graphrunner/session.ts +243 -0
  188. package/src/plugin/graphrunner/store.ts +196 -0
  189. package/src/plugin/graphrunner/styles.module.css +171 -0
  190. package/src/plugin/graphrunner/transport.ts +250 -0
  191. package/src/plugin/graphrunner/types.ts +693 -0
  192. package/src/plugin/graphrunner-local/execution-utils.ts +637 -0
  193. package/src/plugin/graphrunner-local/index.tsx +172 -0
  194. package/src/plugin/graphrunner-local/panel.tsx +187 -0
  195. package/src/plugin/graphrunner-local/store.ts +41 -0
  196. package/src/plugin/graphrunner-local/styles.module.css +82 -0
  197. package/src/plugin/graphrunner-local/transport.ts +1339 -0
  198. package/src/plugin/graphrunner-local/types.ts +10 -0
  199. package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +635 -0
  200. package/src/plugin/graphrunner-webworker/index.tsx +140 -0
  201. package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
  202. package/src/plugin/graphrunner-webworker/store.ts +98 -0
  203. package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
  204. package/src/plugin/kitchen-sink/index.ts +38 -0
  205. package/src/plugin/layout/dagre.ts +131 -0
  206. package/src/plugin/layout/elk.ts +216 -0
  207. package/src/plugin/layout/index.ts +80 -0
  208. package/src/plugin/notes/FormatToolbar.tsx +200 -0
  209. package/src/plugin/notes/index.tsx +191 -0
  210. package/src/plugin/notes/nodeActions.ts +100 -0
  211. package/src/plugin/notes/note.tsx +20 -0
  212. package/src/plugin/notes/noteImpl.tsx +89 -0
  213. package/src/plugin/realtime/realtimeRunner.ts +624 -0
  214. package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
  215. package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
  216. package/src/specifics/VariableGetSpecific.tsx +110 -0
  217. package/src/specifics/VariableSetSpecific.tsx +110 -0
  218. package/src/store/actions.tsx +698 -0
  219. package/src/store/commands.ts +278 -0
  220. package/src/store/contextMenu.ts +192 -0
  221. package/src/store/controls.tsx +62 -0
  222. package/src/store/conversions.ts +47 -0
  223. package/src/store/documentation.tsx +69 -0
  224. package/src/store/events.tsx +116 -0
  225. package/src/store/flow.tsx +230 -0
  226. package/src/store/graphMeta.ts +39 -0
  227. package/src/store/hotKeys.tsx +364 -0
  228. package/src/store/layers.ts +259 -0
  229. package/src/store/legend.tsx +76 -0
  230. package/src/store/logs.ts +28 -0
  231. package/src/store/menubar.ts +41 -0
  232. package/src/store/refs.ts +84 -0
  233. package/src/store/registry.ts +51 -0
  234. package/src/store/selection.ts +22 -0
  235. package/src/store/settings.ts +99 -0
  236. package/src/store/settingsSchema.ts +210 -0
  237. package/src/store/socketGenerator.tsx +54 -0
  238. package/src/store/specific.tsx +75 -0
  239. package/src/store/specs.tsx +35 -0
  240. package/src/store/tabs.ts +282 -0
  241. package/src/store/toolbar.tsx +45 -0
  242. package/src/store/traces.ts +240 -0
  243. package/src/store/variables.ts +37 -0
  244. package/src/system/graph.ts +131 -0
  245. package/src/system/graphSession.ts +172 -0
  246. package/src/system/index.ts +6 -0
  247. package/src/system/notifications.ts +111 -0
  248. package/src/system/persistence.ts +82 -0
  249. package/src/system/plugin.ts +55 -0
  250. package/src/system/provider.tsx +86 -0
  251. package/src/system/pubsub.ts +323 -0
  252. package/src/system/system.ts +653 -0
  253. package/src/system/tabLoader.tsx +303 -0
  254. package/src/system/undoRedo.ts +103 -0
  255. package/src/transformers/Uigraph.ts +61 -0
  256. package/src/transformers/behaveToFlow.ts +16 -4
  257. package/src/transformers/contract.ts +87 -0
  258. package/src/transformers/flowToBehave.ts +40 -12
  259. package/src/types/NodeMetadata.ts +27 -0
  260. package/src/types/graph.ts +49 -0
  261. package/src/types/nodes.ts +50 -0
  262. package/src/types.ts +18 -0
  263. package/src/util/autoConvert.ts +200 -0
  264. package/src/util/colors.ts +1 -29
  265. package/src/util/downloadJson.ts +18 -0
  266. package/src/util/extractNodeMetadata.ts +16 -0
  267. package/src/util/getPickerFilters.ts +1 -1
  268. package/src/util/isBehaveNode.ts +6 -0
  269. package/src/util/isValidConnection.ts +51 -17
  270. package/src/util/mergeSockets.ts +29 -0
  271. package/src/util/serializeVariables.ts +66 -0
  272. package/src/util/sockets.ts +43 -0
  273. package/stories/apex/layoutController/example-graph.worker.ts +39 -0
  274. package/stories/apex/layoutController/index.stories.tsx +48 -0
  275. package/stories/apex/layoutController/webworker.stories.tsx +103 -0
  276. package/stories/apex/menubar/menubar.stories.tsx +19 -0
  277. package/stories/components/colorpicker/index.stories.tsx +20 -0
  278. package/stories/components/contextMenus/edge.stories.tsx +32 -0
  279. package/stories/components/contextMenus/node.stories.tsx +26 -0
  280. package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
  281. package/stories/components/controls/any/index.stories.tsx +19 -0
  282. package/stories/components/controls/boolean/index.stories.tsx +19 -0
  283. package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
  284. package/stories/components/controls/number/index.stories.tsx +19 -0
  285. package/stories/components/controls/string/index.stories.tsx +19 -0
  286. package/stories/components/nodes/behaveNode.stories.tsx +108 -0
  287. package/stories/components/panels/alignment.stories.tsx +24 -0
  288. package/stories/components/panels/events.stories.tsx +38 -0
  289. package/stories/components/panels/graphRunner.stories.tsx +317 -0
  290. package/stories/components/panels/history.stories.tsx +37 -0
  291. package/stories/components/panels/keymaps.stories.tsx +21 -0
  292. package/stories/components/panels/legend.stories.tsx +37 -0
  293. package/stories/components/panels/logs.stories.tsx +24 -0
  294. package/stories/components/panels/nodeInputs.stories.tsx +21 -0
  295. package/stories/components/panels/nodePicker.stories.tsx +37 -0
  296. package/stories/components/panels/panel.stories.tsx +39 -0
  297. package/stories/components/panels/search.stories.tsx +24 -0
  298. package/stories/components/panels/systemSettings.stories.tsx +26 -0
  299. package/stories/components/panels/traces.stories.tsx +225 -0
  300. package/stories/components/panels/variables.stories.tsx +24 -0
  301. package/stories/defaults/defaultStoryProvider.tsx +170 -0
  302. package/stories/defaults/systemGenerator.ts +43 -0
  303. package/stories/plugins/notes.stories.tsx +100 -0
  304. package/tests/autoConvert.test.ts +329 -0
  305. package/tests/autosavePlugin.test.ts +204 -0
  306. package/tests/callSubgraphSync.test.ts +148 -0
  307. package/tests/commandRegistry.test.ts +137 -0
  308. package/tests/components/edges/offsetBezier.test.ts +51 -0
  309. package/tests/components/layoutController/utils.test.ts +68 -0
  310. package/tests/components/panels/traces/utils.test.ts +52 -0
  311. package/tests/contract.test.ts +51 -0
  312. package/tests/contractSerialize.test.ts +62 -0
  313. package/tests/deriveSpans.test.ts +71 -0
  314. package/tests/flowToBehave.test.ts +27 -4
  315. package/tests/hotkeys.test.ts +79 -0
  316. package/tests/keepAliveLifecycle.test.ts +167 -0
  317. package/tests/loadManifest.test.ts +113 -0
  318. package/tests/noteMarkdown.test.ts +65 -0
  319. package/tests/notesPlugin.test.ts +162 -0
  320. package/tests/notifications.test.ts +87 -0
  321. package/tests/persistence.test.ts +51 -0
  322. package/tests/saveLoad.test.ts +373 -0
  323. package/tests/settings.test.ts +178 -0
  324. package/tests/traceStore.test.ts +46 -0
  325. package/tests/util/calculateNewEdge.test.ts +98 -0
  326. package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
  327. package/tests/util/hasPositionMetaData.test.ts +33 -0
  328. package/tests/util/isBehaveNode.test.ts +22 -0
  329. package/tests/util/isHandleConnected.test.ts +37 -0
  330. package/tests/util/mergeSockets.test.ts +43 -0
  331. package/tests/visual/README.md +64 -0
  332. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
  333. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
  334. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
  335. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
  336. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
  337. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
  338. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
  339. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-localGraphRunner-chromium-win32.png +0 -0
  340. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
  341. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
  342. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
  343. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
  344. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
  345. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
  346. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
  347. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
  348. package/tests/visual/panels.visual.test.tsx +76 -0
  349. package/tests/wasdPan.test.ts +71 -0
  350. package/tsconfig.base.json +39 -0
  351. package/tsconfig.json +18 -59
  352. package/tsconfig.prod.json +23 -0
  353. package/tsdown.config.ts +15 -3
  354. package/typedoc.json +7 -7
  355. package/vite.config.js +7 -0
  356. package/vitest.config.ts +5 -2
  357. package/vitest.visual.config.ts +55 -0
  358. package/src/components/AutoSizeInput.tsx +0 -65
  359. package/src/components/Controls.tsx +0 -87
  360. package/src/components/InputSocket.tsx +0 -142
  361. package/src/components/Node.tsx +0 -68
  362. package/src/components/NodeContainer.tsx +0 -46
  363. package/src/components/NodePicker.tsx +0 -77
  364. package/src/components/OutputSocket.tsx +0 -58
  365. package/src/components/modals/ClearModal.tsx +0 -40
  366. package/src/components/modals/HelpModal.tsx +0 -36
  367. package/src/components/modals/LoadModal.tsx +0 -96
  368. package/src/components/modals/Modal.tsx +0 -64
  369. package/src/components/modals/SaveModal.tsx +0 -60
  370. package/src/hooks/useCustomNodeTypes.tsx +0 -31
  371. package/src/hooks/useGraphRunner.ts +0 -104
  372. package/src/hooks/useMergeMap.ts +0 -14
  373. package/src/hooks/useNodeSpecJson.ts +0 -20
  374. package/src/hooks/useQueriableDefinitions.ts +0 -22
  375. package/src/styles.css +0 -8
  376. package/tailwind.config.ts +0 -19
  377. package/tests/tsconfig.json +0 -10
  378. /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
@@ -0,0 +1,624 @@
1
+ import {
2
+ Engine,
3
+ type GraphInstance,
4
+ type ILifecycleEventEmitter,
5
+ type IRegistry,
6
+ isFunctionNode,
7
+ readGraphFromJSON
8
+ } from '@kiberon-labs/behave-graph';
9
+ import { create, type StoreApi } from 'zustand';
10
+
11
+ import type { System } from '@/system/system.js';
12
+ import { flowToBehave } from '@/transformers/flowToBehave.js';
13
+ import { realtime } from '@/annotations';
14
+
15
+ export type RealtimeRunnerStore = {
16
+ engine?: Engine;
17
+ /** Live output values published for UI consumers (keyed by nodeId -> outputName) */
18
+ outputs: Record<string, Record<string, unknown>>;
19
+ setEngine: (engine?: Engine) => void;
20
+ setOutputs: (
21
+ updates: Array<{ nodeId: string; outputName: string; value: unknown }>
22
+ ) => void;
23
+ };
24
+
25
+ const realtimeRunnerStoreFactory = () =>
26
+ create<RealtimeRunnerStore>((set) => ({
27
+ engine: undefined,
28
+ outputs: {},
29
+ setEngine: (engine) => set({ engine }),
30
+ setOutputs: (updates) =>
31
+ set((state) => {
32
+ if (updates.length === 0) return state;
33
+
34
+ let changed = false;
35
+ const nextOutputs: Record<string, Record<string, unknown>> = {
36
+ ...state.outputs
37
+ };
38
+
39
+ for (const { nodeId, outputName, value } of updates) {
40
+ const prevNode = nextOutputs[nodeId] ?? state.outputs[nodeId];
41
+ const prevVal = prevNode?.[outputName];
42
+ if (Object.is(prevVal, value)) continue;
43
+
44
+ const base = prevNode ?? {};
45
+ // Copy-on-write per node
46
+ const nextNode = base === prevNode ? { ...base } : { ...base };
47
+ nextNode[outputName] = value;
48
+ nextOutputs[nodeId] = nextNode;
49
+ changed = true;
50
+ }
51
+
52
+ if (!changed) return state;
53
+ return { outputs: nextOutputs };
54
+ })
55
+ }));
56
+
57
+ /**
58
+ * RealtimeRunner keeps a small preview Engine up-to-date with the current graph.
59
+ * It is designed for UI previews (e.g. live node output thumbnails) and does not
60
+ * replace the existing `GraphRunner`.
61
+ */
62
+ export class RealtimeRunner {
63
+ private system: System;
64
+ private executionRegistry?: IRegistry;
65
+ private graphInstance?: GraphInstance;
66
+ private engine?: Engine;
67
+ private scheduled = false;
68
+ private tickRafHandle?: number;
69
+ private tickTimeoutHandle?: number;
70
+ private tickLoopActive = false;
71
+ private tickInProgress = false;
72
+ private lastTickAtMs = 0;
73
+ private readonly tickIntervalMs = 50;
74
+ private unsubscribers: Array<() => void> = [];
75
+ private watched = new Map<string, Set<string>>();
76
+ private lastPublished = new Map<string, Map<string, unknown>>();
77
+ private lastGraphSignature?: string;
78
+ private annotatedOutputNodeIds: string[] = [];
79
+
80
+ public readonly store: StoreApi<RealtimeRunnerStore>;
81
+
82
+ constructor(system: System, executionRegistry?: IRegistry) {
83
+ this.system = system;
84
+ this.executionRegistry = executionRegistry;
85
+ this.store = realtimeRunnerStoreFactory();
86
+
87
+ // Start in a low-impact mode; actual ticking only happens when something is watched.
88
+ this.start();
89
+ }
90
+
91
+ getEngine(): Engine | undefined {
92
+ return this.engine;
93
+ }
94
+
95
+ /**
96
+ * Get a value from a node's output socket, if available.
97
+ */
98
+ getNodeOutputValue(nodeId: string, outputName: string): unknown {
99
+ const node = this.engine?.nodes?.[nodeId];
100
+ const socket = node?.outputs?.find((s: any) => s.name === outputName);
101
+ return socket?.value;
102
+ }
103
+
104
+ /**
105
+ * Register interest in a particular node output. Used to keep the preview runner efficient.
106
+ */
107
+ watchNodeOutput(nodeId: string, outputName: string): () => void {
108
+ const existing = this.watched.get(nodeId);
109
+ if (existing) {
110
+ existing.add(outputName);
111
+ } else {
112
+ this.watched.set(nodeId, new Set([outputName]));
113
+ }
114
+
115
+ this.ensureTicking();
116
+ this.scheduleEvaluate();
117
+
118
+ return () => {
119
+ const set = this.watched.get(nodeId);
120
+ if (!set) return;
121
+ set.delete(outputName);
122
+ if (set.size === 0) this.watched.delete(nodeId);
123
+ this.ensureTicking();
124
+ };
125
+ }
126
+
127
+ start(): void {
128
+ this.stop();
129
+
130
+ // Rebuild/re-evaluate when graph inputs change.
131
+ this.unsubscribers.push(
132
+ this.system.nodeStore.subscribe(() => this.scheduleEvaluate())
133
+ );
134
+ this.unsubscribers.push(
135
+ this.system.edgeStore.subscribe(() => this.scheduleEvaluate())
136
+ );
137
+ this.unsubscribers.push(
138
+ this.system.variableStore.subscribe(() => this.scheduleEvaluate())
139
+ );
140
+ this.unsubscribers.push(
141
+ this.system.specStore.subscribe(() => this.scheduleEvaluate())
142
+ );
143
+ this.unsubscribers.push(
144
+ this.system.registry.subscribe(() => this.scheduleEvaluate())
145
+ );
146
+
147
+ this.scheduleEvaluate();
148
+ }
149
+
150
+ stop(): void {
151
+ this.unsubscribers.forEach((u) => u());
152
+ this.unsubscribers = [];
153
+
154
+ this.stopTickLoop();
155
+
156
+ if (this.engine) {
157
+ this.engine.dispose();
158
+ this.engine = undefined;
159
+ this.graphInstance = undefined;
160
+ this.store.getState().setEngine(undefined);
161
+ }
162
+ }
163
+
164
+ triggerNode(nodeId: string): void {
165
+ const node = this.engine?.nodes?.[nodeId];
166
+ if (!node) {
167
+ throw new Error(`node not found: ${nodeId}`);
168
+ }
169
+ this.engine?.trigger(node, 'flow');
170
+ // this.engine?.executeAllSync(100,1);
171
+ // this.startTickLoop();
172
+ }
173
+
174
+ private ensureTicking(): void {
175
+ const hasWatchers = this.watched.size > 0;
176
+
177
+ if (hasWatchers) {
178
+ this.startTickLoop();
179
+ return;
180
+ }
181
+
182
+ this.stopTickLoop();
183
+ }
184
+
185
+ private nowMs(): number {
186
+ return typeof performance !== 'undefined' && performance.now
187
+ ? performance.now()
188
+ : Date.now();
189
+ }
190
+
191
+ private startTickLoop(): void {
192
+ if (
193
+ this.tickRafHandle !== undefined ||
194
+ this.tickTimeoutHandle !== undefined
195
+ ) {
196
+ return;
197
+ }
198
+
199
+ this.tickLoopActive = true;
200
+
201
+ this.lastTickAtMs = this.nowMs();
202
+ this.scheduleNextTick();
203
+ }
204
+
205
+ private stopTickLoop(): void {
206
+ this.tickLoopActive = false;
207
+ if (this.tickRafHandle !== undefined) {
208
+ window.cancelAnimationFrame(this.tickRafHandle);
209
+ this.tickRafHandle = undefined;
210
+ }
211
+ if (this.tickTimeoutHandle !== undefined) {
212
+ window.clearTimeout(this.tickTimeoutHandle);
213
+ this.tickTimeoutHandle = undefined;
214
+ }
215
+ }
216
+
217
+ private scheduleNextTick(): void {
218
+ if (!this.tickLoopActive) return;
219
+ //TODO REadd
220
+ // if (this.watched.size === 0) return;
221
+ if (
222
+ this.tickRafHandle !== undefined ||
223
+ this.tickTimeoutHandle !== undefined
224
+ )
225
+ return;
226
+
227
+ const raf = window.requestAnimationFrame;
228
+ if (typeof raf === 'function') {
229
+ this.tickRafHandle = raf((timestampMs) => {
230
+ this.tickRafHandle = undefined;
231
+
232
+ if (!this.tickLoopActive) return;
233
+ // if (this.watched.size === 0) return;
234
+
235
+ const now =
236
+ typeof timestampMs === 'number' ? timestampMs : this.nowMs();
237
+ if (now - this.lastTickAtMs >= this.tickIntervalMs) {
238
+ if (this.tickInProgress) return;
239
+
240
+ this.tickInProgress = true;
241
+ this.lastTickAtMs = now;
242
+ void this.tick().finally(() => {
243
+ this.tickInProgress = false;
244
+ this.scheduleNextTick();
245
+ });
246
+ return;
247
+ }
248
+
249
+ this.scheduleNextTick();
250
+ });
251
+ return;
252
+ }
253
+
254
+ // Fallback (non-browser/test environments).
255
+ this.tickTimeoutHandle = window.setTimeout(() => {
256
+ this.tickTimeoutHandle = undefined;
257
+ if (!this.tickLoopActive) return;
258
+ // if (this.watched.size === 0) return;
259
+ if (this.tickInProgress) return;
260
+
261
+ this.tickInProgress = true;
262
+ this.lastTickAtMs = this.nowMs();
263
+ void this.tick().finally(() => {
264
+ this.tickInProgress = false;
265
+ this.scheduleNextTick();
266
+ });
267
+ }, this.tickIntervalMs);
268
+ }
269
+
270
+ private scheduleEvaluate(): void {
271
+ if (this.scheduled) return;
272
+ this.scheduled = true;
273
+
274
+ // Batch rapid changes (dragging, typing) into a single eval.
275
+ window.setTimeout(() => {
276
+ this.scheduled = false;
277
+ void this.evaluate();
278
+ }, 50);
279
+ }
280
+
281
+ private stripNonSemanticMetadata(graphJson: any): any {
282
+ // We want previews to react to semantic changes (types/ports/edges/vars),
283
+ // not editor-only movement/selection changes.
284
+ if (!graphJson?.nodes) return graphJson;
285
+ const nodes = graphJson.nodes.map((n: any) => {
286
+ if (!n || typeof n !== 'object') return n;
287
+ if (!n.metadata) return n;
288
+
289
+ const meta = { ...n.metadata };
290
+ delete meta.positionX;
291
+ delete meta.positionY;
292
+
293
+ const hasMeta = Object.keys(meta).length > 0;
294
+ return hasMeta ? { ...n, metadata: meta } : { ...n, metadata: undefined };
295
+ });
296
+ return { ...graphJson, nodes };
297
+ }
298
+
299
+ private computeGraphSignature(graphJson: any): string {
300
+ // Stable enough for our preview use-case.
301
+ return JSON.stringify(graphJson);
302
+ }
303
+
304
+ private mergeNodeAnnotationsIntoMetadata(
305
+ graphJson: any,
306
+ flowNodes: any[]
307
+ ): any {
308
+ // The editor stores node annotations in ReactFlow `node.data.annotations`.
309
+ // The execution engine only sees behave-graph `node.metadata` (string map),
310
+ // so we merge annotations into metadata here.
311
+ if (!graphJson?.nodes) return graphJson;
312
+
313
+ const byId = new Map<string, any>();
314
+ for (const n of flowNodes) {
315
+ if (n?.id) byId.set(n.id, n);
316
+ }
317
+
318
+ const nodes = graphJson.nodes.map((nodeJson: any) => {
319
+ const flowNode = byId.get(nodeJson?.id);
320
+ const annotations = flowNode?.data?.annotations;
321
+ if (!annotations || typeof annotations !== 'object') return nodeJson;
322
+
323
+ const meta: Record<string, string> = {
324
+ ...(nodeJson.metadata as Record<string, string> | undefined)
325
+ };
326
+ for (const [key, value] of Object.entries(annotations)) {
327
+ if (value === undefined) continue;
328
+ if (typeof value === 'string') {
329
+ meta[key] = value;
330
+ continue;
331
+ }
332
+ if (typeof value === 'number' || typeof value === 'boolean') {
333
+ meta[key] = String(value);
334
+ continue;
335
+ }
336
+ try {
337
+ meta[key] = JSON.stringify(value);
338
+ } catch {
339
+ meta[key] = String(value);
340
+ }
341
+ }
342
+
343
+ return { ...nodeJson, metadata: meta };
344
+ });
345
+
346
+ return { ...graphJson, nodes };
347
+ }
348
+
349
+ private isRealtimeOutputNode(node: any): boolean {
350
+ const meta = node?.metadata;
351
+ if (!meta || typeof meta !== 'object') return false;
352
+
353
+ const rawFlag = meta[realtime];
354
+
355
+ return !(rawFlag === undefined || rawFlag === null);
356
+ }
357
+
358
+ private rebuildAnnotatedOutputNodeCache(): void {
359
+ if (!this.engine?.nodes) {
360
+ this.annotatedOutputNodeIds = [];
361
+ return;
362
+ }
363
+
364
+ const ids: string[] = [];
365
+ for (const node of Object.values(this.engine.nodes)) {
366
+ if (this.isRealtimeOutputNode(node)) {
367
+ ids.push(node.id);
368
+ }
369
+ }
370
+ this.annotatedOutputNodeIds = ids;
371
+ }
372
+
373
+ private async resolveSocketValueForPreview(
374
+ inputSocket: any
375
+ ): Promise<number> {
376
+ if (!this.engine) return 0;
377
+ if (!inputSocket?.links || inputSocket.links.length === 0) return 0;
378
+
379
+ const nodes = this.engine.nodes;
380
+
381
+ // Safe because links.length > 0
382
+ const upstreamLink = inputSocket.links[0]!;
383
+
384
+ if (
385
+ upstreamLink._targetNode === undefined ||
386
+ upstreamLink._targetSocket === undefined
387
+ ) {
388
+ upstreamLink._targetNode = nodes[upstreamLink.nodeId]!;
389
+ upstreamLink._targetSocket = upstreamLink._targetNode.outputs.find(
390
+ (socket: any) => socket.name === upstreamLink.socketName
391
+ );
392
+ if (upstreamLink._targetSocket === undefined) {
393
+ throw new Error(
394
+ `can not find socket with the name ${upstreamLink.socketName}`
395
+ );
396
+ }
397
+ }
398
+
399
+ const upstreamNode = upstreamLink._targetNode;
400
+ const upstreamOutputSocket = upstreamLink._targetSocket;
401
+
402
+ // If upstream is a flow/event/async node, use its existing output value.
403
+ if (!isFunctionNode(upstreamNode)) {
404
+ inputSocket.value = upstreamOutputSocket.value;
405
+ return 0;
406
+ }
407
+
408
+ let executionSteps = 0;
409
+ for (const upstreamInputSocket of upstreamNode.inputs) {
410
+ executionSteps +=
411
+ await this.resolveSocketValueForPreview(upstreamInputSocket);
412
+ }
413
+
414
+ this.engine.onNodeExecutionStart.emit(upstreamNode);
415
+ await upstreamNode.exec(upstreamNode);
416
+ executionSteps++;
417
+ this.engine.onNodeExecutionEnd.emit(upstreamNode);
418
+
419
+ inputSocket.value = upstreamOutputSocket.value;
420
+ return executionSteps;
421
+ }
422
+
423
+ private async recalculateAnnotatedOutputNodes(): Promise<void> {
424
+ if (!this.engine) return;
425
+ if (this.annotatedOutputNodeIds.length === 0) return;
426
+
427
+ for (const nodeId of this.annotatedOutputNodeIds) {
428
+ await this.evaluateNodeForPreview(nodeId);
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Evaluate every node that a UI consumer is actively watching.
434
+ *
435
+ * Pure function graphs (e.g. the image nodes) never run during
436
+ * `executeAllSync()` because nothing pushes a flow fiber through them. They
437
+ * are normally pulled lazily when a downstream flow/event node reads their
438
+ * value. In the editor preview there is no such consumer, so the act of a
439
+ * component calling `watchNodeOutput()` is what drives evaluation: we resolve
440
+ * the node's upstream function graph and execute the node itself so its
441
+ * sockets hold a fresh value for `publishWatchedOutputs()` to read.
442
+ *
443
+ * This intentionally does not depend on the `ui.realtime` annotation , any
444
+ * watched output is evaluated.
445
+ */
446
+ private async recalculateWatchedOutputs(): Promise<void> {
447
+ if (!this.engine) return;
448
+ if (this.watched.size === 0) return;
449
+
450
+ for (const nodeId of this.watched.keys()) {
451
+ await this.evaluateNodeForPreview(nodeId);
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Resolve a single function node's inputs (recursively executing its upstream
457
+ * function graph) and execute it. Non-function nodes keep whatever value their
458
+ * last real execution produced. Resilient: a failure on one node is logged and
459
+ * does not abort evaluation of the others.
460
+ */
461
+ private async evaluateNodeForPreview(nodeId: string): Promise<void> {
462
+ if (!this.engine) return;
463
+ const node = this.engine.nodes?.[nodeId];
464
+ if (!node) return;
465
+ if (!isFunctionNode(node)) return;
466
+
467
+ try {
468
+ let executionSteps = 0;
469
+ for (const inputSocket of node.inputs) {
470
+ executionSteps += await this.resolveSocketValueForPreview(inputSocket);
471
+ }
472
+
473
+ this.engine.onNodeExecutionStart.emit(node);
474
+ await node.exec(node);
475
+ executionSteps++;
476
+ this.engine.onNodeExecutionEnd.emit(node);
477
+
478
+ this.engine.executionSteps += executionSteps;
479
+ } catch (err) {
480
+ console.error(
481
+ `RealtimeRunner: failed to evaluate watched node ${nodeId}:`,
482
+ err
483
+ );
484
+ }
485
+ }
486
+
487
+ private rebuildEngine(): void {
488
+ const specJson = this.system.specStore.getState().specs;
489
+ if (!specJson || specJson.length === 0) return;
490
+
491
+ const nodes = this.system.nodeStore.getState().nodes;
492
+ const edges = this.system.edgeStore.getState().edges;
493
+
494
+ const rawGraphJson = flowToBehave(
495
+ this.system.session!,
496
+ nodes,
497
+ edges,
498
+ specJson
499
+ );
500
+ const graphWithAnnotations = this.mergeNodeAnnotationsIntoMetadata(
501
+ rawGraphJson,
502
+ nodes
503
+ );
504
+ const graphJson = this.stripNonSemanticMetadata(graphWithAnnotations);
505
+
506
+ const signature = this.computeGraphSignature(graphJson);
507
+ if (this.lastGraphSignature === signature && this.engine) {
508
+ return;
509
+ }
510
+ this.lastGraphSignature = signature;
511
+
512
+ if (!this.executionRegistry) {
513
+ return;
514
+ }
515
+
516
+ this.graphInstance = readGraphFromJSON({
517
+ graphJson,
518
+ registry: this.executionRegistry
519
+ });
520
+
521
+ if (this.engine) {
522
+ this.engine.dispose();
523
+ }
524
+
525
+ this.engine = new Engine(this.graphInstance, this.executionRegistry);
526
+
527
+ this.engine.onNodeExecutionError.addListener(({ error }) => {
528
+ console.error(error);
529
+ });
530
+
531
+ this.store.getState().setEngine(this.engine);
532
+
533
+ this.rebuildAnnotatedOutputNodeCache();
534
+ }
535
+
536
+ private publishWatchedOutputs(): void {
537
+ if (!this.engine) return;
538
+ if (this.watched.size === 0) return;
539
+
540
+ const updates: Array<{
541
+ nodeId: string;
542
+ outputName: string;
543
+ value: unknown;
544
+ }> = [];
545
+
546
+ for (const [nodeId, outputs] of this.watched.entries()) {
547
+ const node = this.engine.nodes?.[nodeId];
548
+ if (!node) continue;
549
+
550
+ let lastForNode = this.lastPublished.get(nodeId);
551
+ if (!lastForNode) {
552
+ lastForNode = new Map<string, unknown>();
553
+ this.lastPublished.set(nodeId, lastForNode);
554
+ }
555
+
556
+ for (const outputName of outputs.values()) {
557
+ const outputSocket = node.outputs?.find(
558
+ (s: any) => s.name === outputName
559
+ );
560
+ const inputSocket = outputSocket
561
+ ? undefined
562
+ : node.inputs?.find((s: any) => s.name === outputName);
563
+
564
+ const value = (outputSocket ?? inputSocket)?.value;
565
+ const prev = lastForNode.get(outputName);
566
+ if (Object.is(prev, value)) continue;
567
+
568
+ lastForNode.set(outputName, value);
569
+ updates.push({ nodeId, outputName, value });
570
+ }
571
+ }
572
+
573
+ if (updates.length > 0) {
574
+ this.store.getState().setOutputs(updates);
575
+ }
576
+ }
577
+
578
+ private async evaluate(): Promise<void> {
579
+ try {
580
+ this.rebuildEngine();
581
+ if (!this.engine) return;
582
+
583
+ // Compute function/flow nodes immediately.
584
+ await this.engine.executeAllSync();
585
+
586
+ // Recalculate annotated output nodes to force evaluation of upstream function graphs.
587
+ await this.recalculateAnnotatedOutputNodes();
588
+
589
+ // Pull every watched output (e.g. live image node previews) so pure
590
+ // function graphs are evaluated even without a flow consumer.
591
+ await this.recalculateWatchedOutputs();
592
+
593
+ this.publishWatchedOutputs();
594
+ } catch (err) {
595
+ // Keep preview runner resilient; don't crash the editor.
596
+ console.error('RealtimeRunner evaluate failed:', err);
597
+ }
598
+ }
599
+
600
+ private async tick(): Promise<void> {
601
+ if (!this.engine) return;
602
+ if (this.watched.size === 0) return;
603
+
604
+ try {
605
+ const eventEmitter = this.executionRegistry?.dependencies
606
+ ?.ILifecycleEventEmitter as ILifecycleEventEmitter | undefined;
607
+
608
+ // Tick time-based graphs.
609
+ eventEmitter?.tickEvent?.emit();
610
+
611
+ await this.engine.executeAllSync();
612
+ // Recalculate annotated output nodes to force evaluation of upstream function graphs.
613
+ await this.recalculateAnnotatedOutputNodes();
614
+
615
+ // Pull every watched output (e.g. live image node previews) so pure
616
+ // function graphs are evaluated even without a flow consumer.
617
+ await this.recalculateWatchedOutputs();
618
+
619
+ this.publishWatchedOutputs();
620
+ } catch {
621
+ // ignore
622
+ }
623
+ }
624
+ }
@@ -0,0 +1,92 @@
1
+ import React, { useEffect, useMemo } from 'react';
2
+ import { useStore } from 'zustand';
3
+ import {
4
+ VscodeOption,
5
+ VscodeSingleSelect
6
+ } from '@vscode-elements/react-elements';
7
+
8
+ import { useGraph } from '@/system/provider';
9
+ import type { SpecificRenderProps } from '@/store/specific';
10
+
11
+ const NAME = 'customEvent/onTriggered.customEventId';
12
+
13
+ export function getCustomEventOnTriggeredSpecific() {
14
+ return {
15
+ name: NAME,
16
+ check: (spec: any) => spec?.type === 'customEvent/onTriggered',
17
+ render: CustomEventOnTriggeredSpecific
18
+ };
19
+ }
20
+
21
+ const CustomEventOnTriggeredSpecific: React.FC<SpecificRenderProps> = ({
22
+ node
23
+ }) => {
24
+ const system = useGraph();
25
+ const customEvents = useStore(system.eventsStore, (s) => s.customEvents);
26
+
27
+ const options = useMemo(() => {
28
+ return Object.values(customEvents)
29
+ .map((evt) => ({
30
+ id: evt.id === undefined || evt.id === null ? '' : String(evt.id),
31
+ name:
32
+ evt.name === undefined || evt.name === null ? '' : String(evt.name)
33
+ }))
34
+ .filter((x) => x.id);
35
+ }, [customEvents]);
36
+
37
+ const value = useMemo(() => {
38
+ const v = node.data?.configuration?.customEventId;
39
+ return v === undefined || v === null ? '' : String(v);
40
+ }, [node.data]);
41
+
42
+ const setNodeConfigValue = (nextValue: string) => {
43
+ system.nodeStore.getState().setNodes((prev) =>
44
+ prev.map((n) => {
45
+ if (n.id !== node.id) return n;
46
+ return {
47
+ ...n,
48
+ data: {
49
+ ...n.data,
50
+ configuration: {
51
+ ...n.data?.configuration,
52
+ customEventId: nextValue
53
+ }
54
+ }
55
+ };
56
+ })
57
+ );
58
+ };
59
+
60
+ useEffect(() => {
61
+ if (value) return;
62
+ const first = options[0];
63
+ if (!first) return;
64
+ setNodeConfigValue(first.id);
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [options.length, node.id]);
67
+
68
+ return (
69
+ <div style={{ paddingLeft: 8, paddingRight: 8, paddingBottom: 8 }}>
70
+ <div style={{ fontSize: 12, opacity: 0.9, marginBottom: 4 }}>
71
+ customEventId
72
+ </div>
73
+ <VscodeSingleSelect
74
+ value={value}
75
+ onChange={(e: any) =>
76
+ setNodeConfigValue(String(e?.target?.value ?? ''))
77
+ }
78
+ disabled={options.length === 0}
79
+ >
80
+ {options.length === 0 ? (
81
+ <VscodeOption value="">No custom events</VscodeOption>
82
+ ) : (
83
+ options.map((opt) => (
84
+ <VscodeOption key={opt.id} value={opt.id}>
85
+ {opt.name ? `${opt.name} (${opt.id})` : opt.id}
86
+ </VscodeOption>
87
+ ))
88
+ )}
89
+ </VscodeSingleSelect>
90
+ </div>
91
+ );
92
+ };