@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,216 @@
1
+ import type { System } from '@/system';
2
+ import type {
3
+ ElkExtendedEdge,
4
+ ElkNode,
5
+ ElkPort
6
+ } from 'elkjs/lib/elk.bundled.js';
7
+ import type { Edge, Node } from 'reactflow';
8
+ import { pinned } from '@/annotations';
9
+
10
+ /**
11
+ * elkjs is ~1.4 MB — by far the largest dependency the editor can pull in, yet
12
+ * it is only used when the user explicitly runs an ELK layout. Load it lazily (a
13
+ * dynamic import the bundler code-splits into a separate chunk) so it stays out
14
+ * of the initial load even for consumers of this layout plugin. The instance is
15
+ * created once and reused.
16
+ */
17
+ let elkPromise:
18
+ | Promise<{ layout: (graph: ElkNode) => Promise<ElkNode> }>
19
+ | undefined;
20
+ const getElk = () => {
21
+ elkPromise ??= import('elkjs/lib/elk.bundled.js').then(
22
+ (m) => new m.default()
23
+ );
24
+ return elkPromise;
25
+ };
26
+
27
+ const layoutOptions = {
28
+ // 'elk.algorithm': 'layered',
29
+ 'elk.direction': 'RIGHT',
30
+ 'elk.layered.spacing.edgeNodeBetweenLayers': '40',
31
+ 'elk.spacing.nodeNode': '40',
32
+ 'elk.layered.nodePlacement.strategy': 'SIMPLE'
33
+ };
34
+
35
+ export type LayoutAlgorithm =
36
+ | 'org.eclipse.elk.layered'
37
+ | 'org.eclipse.elk.force'
38
+ | 'org.eclipse.elk.rectpacking';
39
+
40
+ const getLayoutedNodes = async (
41
+ nodes: Node[],
42
+ edges: Edge[],
43
+ algorithm: LayoutAlgorithm
44
+ ) => {
45
+ // Filter out pinned nodes, group nodes, and nodes inside groups
46
+ const layoutNodes = nodes.filter((node) => {
47
+ const isPinned = 'data' in node && node.data.annotations?.[pinned];
48
+ const isInGroup = !!node.parentId;
49
+ const isGroup = node.type === 'group';
50
+ return !isPinned && !isInGroup && !isGroup;
51
+ });
52
+
53
+ //Convert the edges to a lookup map
54
+ const edgeOut = new Map<string, Edge[]>();
55
+ const edgeIn = new Map<string, Edge[]>();
56
+
57
+ edges.forEach((edge) => {
58
+ if (!edgeOut.has(edge.source)) {
59
+ edgeOut.set(edge.source, []);
60
+ }
61
+ edgeOut.get(edge.source)!.push(edge);
62
+
63
+ if (!edgeIn.has(edge.target)) {
64
+ edgeIn.set(edge.target, []);
65
+ }
66
+ edgeIn.get(edge.target)!.push(edge);
67
+ });
68
+
69
+ const graph: ElkNode = {
70
+ id: 'root',
71
+ layoutOptions: { 'elk.algorithm': algorithm, ...layoutOptions },
72
+ children: layoutNodes.map((n) => {
73
+ //lookup the edges for this node
74
+ const outgoingEdges = edgeIn.get(n.id) || [];
75
+ const incomingEdges = edgeOut.get(n.id) || [];
76
+
77
+ // we need unique ids for the handles (called 'ports' in elkjs) for the layouting
78
+ // an id is structured like: nodeId-source/target-id
79
+
80
+ const targetPorts = outgoingEdges.map((e) => {
81
+ return {
82
+ id: e.sourceHandle as string,
83
+ width: 10,
84
+ height: 10,
85
+ properties: {
86
+ side: 'WEST'
87
+ }
88
+ } as ElkPort;
89
+ });
90
+
91
+ const sourcePorts = incomingEdges.map(
92
+ (e) =>
93
+ ({
94
+ id: e.targetHandle as string,
95
+ width: 10,
96
+ height: 10,
97
+ properties: {
98
+ side: 'EAST'
99
+ }
100
+ }) as ElkPort
101
+ );
102
+
103
+ return {
104
+ id: n.id,
105
+ width: n.width ?? 150,
106
+ height: n.height ?? 50,
107
+ // ⚠️ we need to tell elk that the ports are fixed, in order to reduce edge crossings
108
+ properties: {
109
+ 'org.eclipse.elk.portConstraints': 'FIXED_ORDER'
110
+ },
111
+ // we are also passing the id, so we can also handle edges without a sourceHandle or targetHandle option
112
+ ports: [...targetPorts, ...sourcePorts]
113
+ };
114
+ }),
115
+ edges: edges.map(
116
+ (e) =>
117
+ ({
118
+ id: e.id,
119
+ sources: [e.sourceHandle || e.source],
120
+ targets: [e.targetHandle || e.target]
121
+ }) as ElkExtendedEdge
122
+ )
123
+ };
124
+
125
+ const elk = await getElk();
126
+ const layoutedGraph = await elk.layout(graph);
127
+
128
+ const layoutedNodes = nodes.map((node) => {
129
+ // Skip pinned nodes - keep their current position
130
+ const isPinned = 'data' in node && node.data.annotations?.[pinned];
131
+ if (isPinned) {
132
+ return node;
133
+ }
134
+
135
+ // Skip group nodes for now - we'll position them based on children
136
+ if (node.type === 'group') {
137
+ return node;
138
+ }
139
+
140
+ // Skip nodes inside groups - they maintain relative positions
141
+ if (node.parentId) {
142
+ return node;
143
+ }
144
+
145
+ const layoutedNode = layoutedGraph.children?.find(
146
+ (lgNode) => lgNode.id === node.id
147
+ );
148
+
149
+ return {
150
+ ...node,
151
+ position: {
152
+ x: layoutedNode?.x ?? 0,
153
+ y: layoutedNode?.y ?? 0
154
+ }
155
+ };
156
+ });
157
+
158
+ // Update group positions and sizes based on their children
159
+ const padding = 25;
160
+ return layoutedNodes.map((node) => {
161
+ if (node.type !== 'group') return node;
162
+
163
+ const children = layoutedNodes.filter((n) => n.parentId === node.id);
164
+ if (children.length === 0) return node;
165
+
166
+ // Calculate bounding box of children (in absolute coordinates)
167
+ let minX = Infinity;
168
+ let minY = Infinity;
169
+ let maxX = -Infinity;
170
+ let maxY = -Infinity;
171
+
172
+ children.forEach((child) => {
173
+ const childX = child.positionAbsolute?.x ?? child.position.x;
174
+ const childY = child.positionAbsolute?.y ?? child.position.y;
175
+ const childWidth = child.width ?? 150;
176
+ const childHeight = child.height ?? 50;
177
+
178
+ minX = Math.min(minX, childX);
179
+ minY = Math.min(minY, childY);
180
+ maxX = Math.max(maxX, childX + childWidth);
181
+ maxY = Math.max(maxY, childY + childHeight);
182
+ });
183
+
184
+ // Position group with padding around children
185
+ return {
186
+ ...node,
187
+ position: {
188
+ x: minX - padding,
189
+ y: minY - padding
190
+ },
191
+ style: {
192
+ ...node.style,
193
+ width: maxX - minX + padding * 2,
194
+ height: maxY - minY + padding * 2
195
+ }
196
+ };
197
+ });
198
+ };
199
+
200
+ export const applyElkLayout = async (
201
+ sys: System,
202
+ algorithm: LayoutAlgorithm
203
+ ) => {
204
+ const nodeStore = sys.nodeStore.getState();
205
+ const nodes = Object.values(nodeStore.nodes);
206
+ const edges = Object.values(sys.edgeStore.getState().edges);
207
+ const reactflow = sys.refStore.getState().getRef('reactflow');
208
+
209
+ if (!reactflow) {
210
+ return;
211
+ }
212
+
213
+ const layoutedNodes = await getLayoutedNodes(nodes, edges, algorithm);
214
+
215
+ nodeStore.setNodes(layoutedNodes);
216
+ };
@@ -0,0 +1,80 @@
1
+ import { plugin } from '@/system/plugin';
2
+ import type { System } from '@/system/system';
3
+ import { applyDagreLayout } from './dagre';
4
+ import { applyElkLayout } from './elk';
5
+
6
+ export * from './dagre';
7
+ export * from './elk';
8
+
9
+ /**
10
+ * Available auto-layout engines. `Dagre` is a small, synchronous layered layout;
11
+ * the `Elk - *` options use elkjs (~1.4 MB, loaded lazily on first use) for
12
+ * higher-quality layouts.
13
+ */
14
+ export const LAYOUT_TYPE = {
15
+ dagre: 'Dagre',
16
+ elkForce: 'Elk - Force',
17
+ elkRect: 'Elk - Rect',
18
+ elkLayered: 'Elk - Layered'
19
+ } as const;
20
+
21
+ export type LayoutType = (typeof LAYOUT_TYPE)[keyof typeof LAYOUT_TYPE];
22
+
23
+ /**
24
+ * Run the currently-selected auto-layout engine against the focused graph.
25
+ * Reads the `layoutType` setting (registered by this plugin) to pick the engine.
26
+ */
27
+ export const applyAutoLayout = (system: System): void => {
28
+ switch (system.getSetting<LayoutType>('layoutType')) {
29
+ case LAYOUT_TYPE.dagre:
30
+ void applyDagreLayout(system);
31
+ break;
32
+ case LAYOUT_TYPE.elkLayered:
33
+ void applyElkLayout(system, 'org.eclipse.elk.layered');
34
+ break;
35
+ case LAYOUT_TYPE.elkForce:
36
+ void applyElkLayout(system, 'org.eclipse.elk.force');
37
+ break;
38
+ case LAYOUT_TYPE.elkRect:
39
+ void applyElkLayout(system, 'org.eclipse.elk.rectpacking');
40
+ break;
41
+ }
42
+ };
43
+
44
+ /**
45
+ * Adds graph auto-layout (Dagre + ELK) to the editor. elkjs and dagre are heavy
46
+ * dependencies that not every host needs, so they live here rather than in the
47
+ * core editor — register this plugin (directly or via the kitchen-sink plugin)
48
+ * to opt in.
49
+ *
50
+ * The plugin:
51
+ * - registers the `layoutType` setting (the engine picker in the Settings panel);
52
+ * - registers the `editor.autoLayout` command that the "Auto Layout" hotkey and
53
+ * menu dispatch to.
54
+ *
55
+ * Without it, `editor.autoLayout` is simply unregistered (the hotkey no-ops).
56
+ */
57
+ export const layoutPlugin = plugin(
58
+ (system: System) => {
59
+ system.registerSetting({
60
+ key: 'layoutType',
61
+ section: 'Layout',
62
+ type: 'enum',
63
+ default: LAYOUT_TYPE.dagre,
64
+ options: Object.values(LAYOUT_TYPE).map((value) => ({
65
+ value,
66
+ label: value
67
+ })),
68
+ title: 'Layout Type',
69
+ description:
70
+ 'Select the type of layout engine to use in the graph editor.'
71
+ });
72
+
73
+ system.commandStore.getState().register({
74
+ id: 'editor.autoLayout',
75
+ title: 'Auto Layout',
76
+ run: (ctx) => applyAutoLayout(ctx.editor)
77
+ });
78
+ },
79
+ { name: 'layout' }
80
+ );
@@ -0,0 +1,200 @@
1
+ import { useState } from 'react';
2
+ import { useEditorState, type Editor } from '@tiptap/react';
3
+ import { VscodeButton, VscodeTextfield } from '@vscode-elements/react-elements';
4
+ import {
5
+ Bold,
6
+ Code,
7
+ Italic,
8
+ List,
9
+ NumberedListLeft,
10
+ Quote,
11
+ Strikethrough,
12
+ Youtube
13
+ } from 'iconoir-react';
14
+
15
+ interface FormatToolbarProps {
16
+ editor: Editor;
17
+ }
18
+
19
+ /**
20
+ * Formatting actions for the note editor. Buttons are the same VscodeButton
21
+ * component the FloatingToolbar uses, so the note toolbar stays visually
22
+ * consistent with the rest of the editor chrome; the active format renders as
23
+ * a primary (accent) button.
24
+ *
25
+ * The button row swallows mousedown so clicking a formatting button keeps the
26
+ * editor's text selection instead of blurring it. The video row must NOT: its
27
+ * text field needs focus. Video embedding uses an inline URL field rather than
28
+ * `window.prompt`, which is blocked in VS Code webviews.
29
+ */
30
+ export const FormatToolbar = ({ editor }: FormatToolbarProps) => {
31
+ // null = embed row closed; a string = the URL being typed.
32
+ const [videoUrl, setVideoUrl] = useState<string | null>(null);
33
+
34
+ const active = useEditorState({
35
+ editor,
36
+ selector: ({ editor }) => ({
37
+ bold: editor.isActive('bold'),
38
+ italic: editor.isActive('italic'),
39
+ strike: editor.isActive('strike'),
40
+ code: editor.isActive('code'),
41
+ h1: editor.isActive('heading', { level: 1 }),
42
+ h2: editor.isActive('heading', { level: 2 }),
43
+ h3: editor.isActive('heading', { level: 3 }),
44
+ bulletList: editor.isActive('bulletList'),
45
+ orderedList: editor.isActive('orderedList'),
46
+ codeBlock: editor.isActive('codeBlock'),
47
+ blockquote: editor.isActive('blockquote')
48
+ })
49
+ });
50
+
51
+ const embedVideo = () => {
52
+ const src = videoUrl?.trim();
53
+ if (src) {
54
+ editor.chain().focus().setYoutubeVideo({ src }).run();
55
+ }
56
+ setVideoUrl(null);
57
+ };
58
+
59
+ return (
60
+ <div className="notes-toolbar nodrag nopan">
61
+ <div
62
+ className="notes-toolbar__row"
63
+ onMouseDown={(e) => e.preventDefault()}
64
+ >
65
+ <VscodeButton
66
+ secondary={!active.bold}
67
+ iconOnly
68
+ title="Bold"
69
+ onClick={() => editor.chain().focus().toggleBold().run()}
70
+ >
71
+ <Bold />
72
+ </VscodeButton>
73
+ <VscodeButton
74
+ secondary={!active.italic}
75
+ iconOnly
76
+ title="Italic"
77
+ onClick={() => editor.chain().focus().toggleItalic().run()}
78
+ >
79
+ <Italic />
80
+ </VscodeButton>
81
+ <VscodeButton
82
+ secondary={!active.strike}
83
+ iconOnly
84
+ title="Strikethrough"
85
+ onClick={() => editor.chain().focus().toggleStrike().run()}
86
+ >
87
+ <Strikethrough />
88
+ </VscodeButton>
89
+ <VscodeButton
90
+ secondary={!active.code}
91
+ iconOnly
92
+ title="Code"
93
+ onClick={() => editor.chain().focus().toggleCode().run()}
94
+ >
95
+ <Code />
96
+ </VscodeButton>
97
+ <div className="notes-toolbar__separator" />
98
+ <VscodeButton
99
+ secondary={!active.h1}
100
+ title="Heading 1"
101
+ onClick={() =>
102
+ editor.chain().focus().toggleHeading({ level: 1 }).run()
103
+ }
104
+ >
105
+ H1
106
+ </VscodeButton>
107
+ <VscodeButton
108
+ secondary={!active.h2}
109
+ title="Heading 2"
110
+ onClick={() =>
111
+ editor.chain().focus().toggleHeading({ level: 2 }).run()
112
+ }
113
+ >
114
+ H2
115
+ </VscodeButton>
116
+ <VscodeButton
117
+ secondary={!active.h3}
118
+ title="Heading 3"
119
+ onClick={() =>
120
+ editor.chain().focus().toggleHeading({ level: 3 }).run()
121
+ }
122
+ >
123
+ H3
124
+ </VscodeButton>
125
+ <div className="notes-toolbar__separator" />
126
+ <VscodeButton
127
+ secondary={!active.bulletList}
128
+ iconOnly
129
+ title="Bullet List"
130
+ onClick={() => editor.chain().focus().toggleBulletList().run()}
131
+ >
132
+ <List />
133
+ </VscodeButton>
134
+ <VscodeButton
135
+ secondary={!active.orderedList}
136
+ iconOnly
137
+ title="Numbered List"
138
+ onClick={() => editor.chain().focus().toggleOrderedList().run()}
139
+ >
140
+ <NumberedListLeft />
141
+ </VscodeButton>
142
+ <VscodeButton
143
+ secondary={!active.codeBlock}
144
+ iconOnly
145
+ title="Code Block"
146
+ onClick={() => editor.chain().focus().toggleCodeBlock().run()}
147
+ >
148
+ <Code />
149
+ </VscodeButton>
150
+ <VscodeButton
151
+ secondary={!active.blockquote}
152
+ iconOnly
153
+ title="Quote"
154
+ onClick={() => editor.chain().focus().toggleBlockquote().run()}
155
+ >
156
+ <Quote />
157
+ </VscodeButton>
158
+ <div className="notes-toolbar__separator" />
159
+ <VscodeButton
160
+ secondary={videoUrl === null}
161
+ iconOnly
162
+ title="Embed YouTube Video"
163
+ onClick={() => setVideoUrl(videoUrl === null ? '' : null)}
164
+ >
165
+ <Youtube />
166
+ </VscodeButton>
167
+ </div>
168
+
169
+ {videoUrl !== null && (
170
+ <div
171
+ className="notes-toolbar__embed"
172
+ // Keep keystrokes (e.g. Backspace) from reaching React Flow, which
173
+ // would delete the selected note while the URL is being typed.
174
+ onKeyDown={(e) => {
175
+ e.stopPropagation();
176
+ if (e.key === 'Enter') embedVideo();
177
+ if (e.key === 'Escape') setVideoUrl(null);
178
+ }}
179
+ >
180
+ <VscodeTextfield
181
+ type="text"
182
+ placeholder="YouTube video URL"
183
+ value={videoUrl}
184
+ onInput={(e: any) => setVideoUrl(e.target.value)}
185
+ />
186
+ <VscodeButton title="Embed" onClick={embedVideo}>
187
+ Embed
188
+ </VscodeButton>
189
+ <VscodeButton
190
+ secondary
191
+ title="Cancel"
192
+ onClick={() => setVideoUrl(null)}
193
+ >
194
+ Cancel
195
+ </VscodeButton>
196
+ </div>
197
+ )}
198
+ </div>
199
+ );
200
+ };
@@ -0,0 +1,191 @@
1
+ import { Notes } from 'iconoir-react';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { plugin } from '@/system/plugin';
4
+ import type { System } from '@/system/system';
5
+ import type { CommandContext } from '@/store/commands';
6
+ import type { INoteNode } from '@/types/nodes';
7
+ import { NoteNode } from './note';
8
+ import {
9
+ NOTE_NODE_TYPE,
10
+ LEGACY_COMMENT_NODE_TYPE,
11
+ noteAt,
12
+ duplicateNote,
13
+ deleteNote,
14
+ reorderNote
15
+ } from './nodeActions';
16
+
17
+ export { NoteNode } from './note';
18
+ export * from './nodeActions';
19
+
20
+ /**
21
+ * Create a note node on the graph the command targets. Placed at the given
22
+ * position (e.g. from a context menu) or, from surfaces without one (toolbar,
23
+ * hotkey), at the centre of the current viewport. Undo-aware.
24
+ */
25
+ const addNote = (ctx: CommandContext): void => {
26
+ const reactflow = ctx.session.refStore.getState().getRef('reactflow');
27
+ const position = ctx.position ??
28
+ reactflow?.screenToFlowPosition({
29
+ x: window.innerWidth / 2,
30
+ y: window.innerHeight / 2
31
+ }) ?? { x: 0, y: 0 };
32
+
33
+ const note: INoteNode = {
34
+ id: uuidv4(),
35
+ type: NOTE_NODE_TYPE,
36
+ position,
37
+ selected: true,
38
+ // Only the header drags the node; the body is a text surface.
39
+ dragHandle: '.notes-node__header',
40
+ style: { width: 280, height: 180 },
41
+ data: { text: '' }
42
+ };
43
+
44
+ ctx.session.undoManager.execute({
45
+ name: 'Add note',
46
+ execute: () => {
47
+ ctx.session.nodeStore.getState().addNode(note);
48
+ },
49
+ undo: () => {
50
+ ctx.session.nodeStore
51
+ .getState()
52
+ .setNodes((existing) => existing.filter((n) => n.id !== note.id));
53
+ }
54
+ });
55
+ };
56
+
57
+ /**
58
+ * Adds markdown note nodes to the editor. Notes are purely presentational:
59
+ * they never appear in the compiled behave graph (only behave nodes do), but
60
+ * they persist with the UI graph JSON like any other canvas node.
61
+ *
62
+ * The note editor embeds tiptap/prosemirror, a heavy dependency most hosts do
63
+ * not need, so notes live here rather than in the core editor — register this
64
+ * plugin (directly or via the kitchen-sink plugin) to opt in.
65
+ *
66
+ * The plugin:
67
+ * - registers the `noteNode` component on every graph session (plus the legacy
68
+ * `commentNode` alias for graphs saved before notes moved here);
69
+ * - registers the `notes.addNote` command, an "Add Note" button on the
70
+ * floating toolbar, and a `Shift+N` hotkey that dispatch it;
71
+ * - registers note-specific node commands + context-menu items
72
+ * (duplicate / delete / bring to front / send to back).
73
+ */
74
+ export const notesPlugin = plugin(
75
+ (system: System) => {
76
+ system.registerSessionExtension((session) => {
77
+ const { registerNodeType } = session.flowStore.getState();
78
+ registerNodeType(NOTE_NODE_TYPE, NoteNode);
79
+ registerNodeType(LEGACY_COMMENT_NODE_TYPE, NoteNode);
80
+ });
81
+
82
+ const commands = system.commandStore.getState();
83
+ commands.register({
84
+ id: 'notes.addNote',
85
+ title: 'Add Note',
86
+ run: addNote
87
+ });
88
+ commands.register({
89
+ id: 'note.duplicate',
90
+ title: 'Duplicate Note',
91
+ run: (ctx) => {
92
+ const note = noteAt(ctx);
93
+ if (note) duplicateNote(ctx.session, note);
94
+ }
95
+ });
96
+ commands.register({
97
+ id: 'note.delete',
98
+ title: 'Delete Note',
99
+ run: (ctx) => {
100
+ const note = noteAt(ctx);
101
+ if (note) deleteNote(ctx.session, note);
102
+ }
103
+ });
104
+ commands.register({
105
+ id: 'note.bringToFront',
106
+ title: 'Bring Note to Front',
107
+ run: (ctx) => {
108
+ const note = noteAt(ctx);
109
+ if (note) reorderNote(ctx.session, note, 'front');
110
+ }
111
+ });
112
+ commands.register({
113
+ id: 'note.sendToBack',
114
+ title: 'Send Note to Back',
115
+ run: (ctx) => {
116
+ const note = noteAt(ctx);
117
+ if (note) reorderNote(ctx.session, note, 'back');
118
+ }
119
+ });
120
+
121
+ // Notes get their own node context menu; the behave items (trace, pin,
122
+ // ...) are guarded by `when` in the core defaults and stay hidden here.
123
+ const menu = system.contextMenuStore.getState();
124
+ const noteOnly = (ctx: CommandContext) => Boolean(noteAt(ctx));
125
+ menu.register({
126
+ id: 'note.duplicate',
127
+ target: 'node',
128
+ label: 'Duplicate',
129
+ order: 10,
130
+ group: 'note',
131
+ when: noteOnly,
132
+ commandId: 'note.duplicate'
133
+ });
134
+ menu.register({
135
+ id: 'note.bringToFront',
136
+ target: 'node',
137
+ label: 'Bring to Front',
138
+ order: 20,
139
+ group: 'note-order',
140
+ when: noteOnly,
141
+ commandId: 'note.bringToFront'
142
+ });
143
+ menu.register({
144
+ id: 'note.sendToBack',
145
+ target: 'node',
146
+ label: 'Send to Back',
147
+ order: 21,
148
+ group: 'note-order',
149
+ when: noteOnly,
150
+ commandId: 'note.sendToBack'
151
+ });
152
+ menu.register({
153
+ id: 'note.delete',
154
+ target: 'node',
155
+ label: 'Delete',
156
+ order: 30,
157
+ group: 'note-danger',
158
+ when: noteOnly,
159
+ commandId: 'note.delete'
160
+ });
161
+
162
+ const dispatchAddNote = () => {
163
+ const session = system.session;
164
+ if (!session) return;
165
+ void system.commandStore
166
+ .getState()
167
+ .run('notes.addNote', { editor: system, session });
168
+ };
169
+
170
+ system.toolbarStore.getState().addGroup({
171
+ id: 'notes',
172
+ label: 'Notes',
173
+ buttons: [
174
+ {
175
+ id: 'notes.addNote',
176
+ icon: <Notes />,
177
+ label: 'Add Note',
178
+ onClick: dispatchAddNote
179
+ }
180
+ ]
181
+ });
182
+
183
+ system.hotKeyStore.getState().register({
184
+ action: 'ADD_NOTE',
185
+ description: 'Add a markdown note to the graph',
186
+ trigger: 'shift+n',
187
+ handler: dispatchAddNote
188
+ });
189
+ },
190
+ { name: 'notes' }
191
+ );