@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,698 @@
1
+ import type { GraphSession } from '@/system/graphSession';
2
+ import { Graph } from 'graphlib';
3
+ import { create } from 'zustand';
4
+ import copyToClipboard from 'copy-to-clipboard';
5
+ import type { Edge, Node, XYPosition } from 'reactflow';
6
+ import { getNodesBounds } from 'reactflow';
7
+ import { v4 as uuidv4 } from 'uuid';
8
+ import { hidden, pinned } from '@/annotations';
9
+ import { buildUIGraphJSON } from '@/transformers/Uigraph';
10
+ import type { UIGraphJSON } from '../types/graph';
11
+ import type { IBehaveNode } from '@/types/nodes';
12
+
13
+ const FILTERED_CLASS_NAME = 'filtered';
14
+
15
+ type NodeClipboardPayloadV1 = {
16
+ type: 'behave-graph/flow-nodes';
17
+ v: 1;
18
+ nodes: Node[];
19
+ edges?: Edge[];
20
+ };
21
+
22
+ const NODE_CLIPBOARD_PREFIX = 'behave-graph:nodes:';
23
+
24
+ let inMemoryClipboard: string | null = null;
25
+ let pasteSequence = 0;
26
+
27
+ const encodeNodeClipboardPayload = (payload: NodeClipboardPayloadV1) =>
28
+ `${NODE_CLIPBOARD_PREFIX}${JSON.stringify(payload)}`;
29
+
30
+ const decodeNodeClipboardPayload = (
31
+ text: string
32
+ ): NodeClipboardPayloadV1 | null => {
33
+ try {
34
+ const raw = text.startsWith(NODE_CLIPBOARD_PREFIX)
35
+ ? text.slice(NODE_CLIPBOARD_PREFIX.length)
36
+ : text;
37
+ const parsed = JSON.parse(raw) as Partial<NodeClipboardPayloadV1>;
38
+ if (parsed?.type !== 'behave-graph/flow-nodes') return null;
39
+ if (parsed?.v !== 1) return null;
40
+ if (!Array.isArray(parsed?.nodes)) return null;
41
+ if (parsed?.edges && !Array.isArray(parsed.edges)) return null;
42
+ return parsed as NodeClipboardPayloadV1;
43
+ } catch {
44
+ return null;
45
+ }
46
+ };
47
+
48
+ const deepClone = <T,>(value: T): T => {
49
+ const sc = (globalThis as any).structuredClone as
50
+ | ((value: any) => any)
51
+ | undefined;
52
+ if (typeof sc === 'function') {
53
+ return sc(value) as T;
54
+ }
55
+ return JSON.parse(JSON.stringify(value)) as T;
56
+ };
57
+
58
+ /**
59
+ * Extend or modify this interface to add more actions as needed.
60
+ */
61
+ export interface Actions {
62
+ traceUpstream: (nodeId: string) => void;
63
+ traceDownstream: (nodeId: string) => void;
64
+ focusNode: (nodeId: string) => void;
65
+ resetTrace: () => void;
66
+ addBehaveNode: (nodeType: string, position: XYPosition) => void;
67
+ copySelectionToClipboard: () => void;
68
+ pasteFromClipboard: () => Promise<void>;
69
+ toggleNodeHidden: (nodeId: string) => void;
70
+ toggleNodePinned: (nodeId: string) => void;
71
+ groupNodes: () => void;
72
+ ungroupNodes: (groupId: string) => void;
73
+ deleteNodes: (nodeIds: string[]) => void;
74
+ updateGroupColor: (groupId: string, color: string) => void;
75
+ // Triggers the save graph
76
+ save: () => Promise<UIGraphJSON>;
77
+ }
78
+
79
+ export type ActionStore = {
80
+ actions: Actions;
81
+ setAction: <K extends keyof Actions>(key: K, action: Actions[K]) => void;
82
+ getAction: <K extends keyof Actions>(key: K) => Actions[K] | undefined;
83
+ };
84
+
85
+ /**
86
+ * Converts to a graph lib graph
87
+ * @param sys
88
+ * @returns
89
+ */
90
+ const convertToGraph = (sys: GraphSession) => {
91
+ const nodes = sys.nodeStore.getState().nodes;
92
+ const edges = sys.edgeStore.getState().edges;
93
+
94
+ const graph = new Graph({ multigraph: true });
95
+ nodes.forEach((node) => graph.setNode(node.id));
96
+ edges.forEach((edge) => graph.setEdge(edge.source, edge.target));
97
+ return graph;
98
+ };
99
+
100
+ const findAllUpstream = (id: string, graph: Graph): string[] => {
101
+ return (graph.predecessors(id) || []).flatMap((x) =>
102
+ [x].concat(findAllUpstream(x, graph))
103
+ );
104
+ };
105
+
106
+ const findAllDownstream = (id: string, graph: Graph): string[] => {
107
+ return (graph.successors(id) || []).flatMap((x) =>
108
+ [x].concat(findAllDownstream(x, graph))
109
+ );
110
+ };
111
+
112
+ const createNodeLookup = (nodes: string[]) => {
113
+ return nodes.reduce(
114
+ (acc, node) => {
115
+ acc[node] = true;
116
+ return acc;
117
+ },
118
+ {} as Record<string, boolean>
119
+ );
120
+ };
121
+
122
+ const applyFilters = (sys: GraphSession, lookup: Record<string, boolean>) => {
123
+ sys.nodeStore.getState().setNodes((nodes) => {
124
+ const newNodes = nodes.map((x) => {
125
+ if (!lookup[x.id]) {
126
+ return {
127
+ ...x,
128
+ className: FILTERED_CLASS_NAME // clsx(x.className ?? '', FILTERED_CLASS_NAME)
129
+ };
130
+ }
131
+ return {
132
+ ...x,
133
+ className: (x.className ?? '')
134
+ .split(' ')
135
+ .filter((c) => c !== FILTERED_CLASS_NAME)
136
+ .join(' ')
137
+ };
138
+ });
139
+ return newNodes;
140
+ });
141
+ };
142
+
143
+ export const actionStoreFactory = (sys: GraphSession) =>
144
+ create<ActionStore>((set, get) => ({
145
+ actions: {
146
+ save: async () => {
147
+ try {
148
+ const uiGraph = buildUIGraphJSON(sys);
149
+ sys.editor.pubsub.publish('graph:saved', uiGraph);
150
+ return uiGraph;
151
+ } catch (err) {
152
+ sys.notifications.error('Failed to save graph');
153
+ throw err;
154
+ }
155
+ },
156
+ traceUpstream: (nodeId: string) => {
157
+ const graph = convertToGraph(sys);
158
+ const foundNodes = createNodeLookup(
159
+ findAllUpstream(nodeId, graph).concat([nodeId])
160
+ );
161
+ applyFilters(sys, foundNodes);
162
+ },
163
+ traceDownstream: (nodeId: string) => {
164
+ const graph = convertToGraph(sys);
165
+ const foundNodes = createNodeLookup(
166
+ findAllDownstream(nodeId, graph).concat([nodeId])
167
+ );
168
+
169
+ applyFilters(sys, foundNodes);
170
+ },
171
+ resetTrace: () => {
172
+ sys.nodeStore.getState().setNodes((nodes) =>
173
+ nodes.map((x) => {
174
+ //Remove filtering
175
+ return {
176
+ ...x,
177
+ className: (x.className ?? '')
178
+ .split(' ')
179
+ .filter((c) => c !== FILTERED_CLASS_NAME)
180
+ .join(' ')
181
+ };
182
+ })
183
+ );
184
+ },
185
+ focusNode: (nodeId: string) => {
186
+ const reactflow = sys.refStore.getState().getRef('reactflow');
187
+ if (!reactflow) {
188
+ return;
189
+ }
190
+
191
+ const node = reactflow.getNodes().find((n) => n.id === nodeId);
192
+
193
+ if (node) {
194
+ reactflow.fitView({
195
+ padding: 0.2,
196
+ duration: 200,
197
+ includeHiddenNodes: true,
198
+ nodes: [node]
199
+ });
200
+ }
201
+ },
202
+ addBehaveNode: (nodeType: string, position: XYPosition) => {
203
+ const newNode: IBehaveNode = {
204
+ id: uuidv4(),
205
+ type: 'behaveNode',
206
+ position,
207
+ data: {
208
+ configuration: {},
209
+ type: nodeType,
210
+ ports: {},
211
+ dynamicPorts: {}
212
+ }
213
+ };
214
+
215
+ sys.undoManager.execute({
216
+ name: `Add node (${nodeType})`,
217
+ execute: () => {
218
+ sys.nodeStore.getState().addNode(newNode);
219
+ },
220
+ undo: () => {
221
+ sys.nodeStore
222
+ .getState()
223
+ .setNodes((existing) =>
224
+ existing.filter((n) => n.id !== newNode.id)
225
+ );
226
+ }
227
+ });
228
+ },
229
+ copySelectionToClipboard: () => {
230
+ const selected = sys.nodeStore
231
+ .getState()
232
+ .nodes.filter((n) => n.selected);
233
+
234
+ if (selected.length === 0) return;
235
+
236
+ const selectedIds = new Set(selected.map((n) => n.id));
237
+ const selectedEdges = sys.edgeStore
238
+ .getState()
239
+ .edges.filter(
240
+ (e) => selectedIds.has(e.source) && selectedIds.has(e.target)
241
+ );
242
+
243
+ pasteSequence = 0;
244
+ const payload: NodeClipboardPayloadV1 = {
245
+ type: 'behave-graph/flow-nodes',
246
+ v: 1,
247
+ nodes: selected,
248
+ edges: selectedEdges
249
+ };
250
+
251
+ const encoded = encodeNodeClipboardPayload(payload);
252
+ inMemoryClipboard = encoded;
253
+ copyToClipboard(encoded);
254
+ },
255
+ pasteFromClipboard: async () => {
256
+ let text: string | null = inMemoryClipboard;
257
+ if (typeof navigator !== 'undefined' && navigator.clipboard?.readText) {
258
+ try {
259
+ text = await navigator.clipboard.readText();
260
+ } catch {
261
+ // ignore and fall back to in-memory clipboard
262
+ }
263
+ }
264
+
265
+ if (!text) return;
266
+ const payload = decodeNodeClipboardPayload(text);
267
+ if (!payload) return;
268
+
269
+ const sourceNodes = payload.nodes;
270
+ if (!sourceNodes.length) return;
271
+
272
+ const sourceEdges = payload.edges ?? [];
273
+
274
+ const selectedIds = new Set(sourceNodes.map((n) => n.id));
275
+ const idMap = new Map<string, string>();
276
+ for (const node of sourceNodes) {
277
+ idMap.set(node.id, uuidv4());
278
+ }
279
+
280
+ const offset = 20 * (pasteSequence + 1);
281
+ const dx = offset;
282
+ const dy = offset;
283
+
284
+ const newNodes: Node[] = sourceNodes.map((n) => {
285
+ const cloned = deepClone(n);
286
+ const newId = idMap.get(cloned.id);
287
+ if (!newId) return cloned;
288
+
289
+ const hasValidParent =
290
+ !!cloned.parentId && selectedIds.has(cloned.parentId);
291
+
292
+ // If the parent isn't being copied too, detach so it pastes in global space
293
+ if (cloned.parentId && !hasValidParent) {
294
+ const posAbs = cloned.positionAbsolute as
295
+ | { x: number; y: number }
296
+ | undefined;
297
+ if (posAbs) {
298
+ cloned.position = { x: posAbs.x, y: posAbs.y };
299
+ }
300
+ cloned.parentId = undefined;
301
+ cloned.extent = undefined;
302
+ }
303
+
304
+ cloned.id = newId;
305
+ cloned.selected = true;
306
+
307
+ if (cloned.parentId && hasValidParent) {
308
+ cloned.parentId = idMap.get(cloned.parentId) ?? cloned.parentId;
309
+ }
310
+
311
+ cloned.position = {
312
+ x: (cloned.position?.x ?? 0) + dx,
313
+ y: (cloned.position?.y ?? 0) + dy
314
+ };
315
+
316
+ const abs = cloned.positionAbsolute as
317
+ | { x: number; y: number }
318
+ | undefined;
319
+ if (abs) {
320
+ cloned.positionAbsolute = { x: abs.x + dx, y: abs.y + dy };
321
+ }
322
+
323
+ return cloned;
324
+ });
325
+
326
+ const newEdges: Edge[] = sourceEdges
327
+ .filter((e) => idMap.has(e.source) && idMap.has(e.target))
328
+ .map((e) => {
329
+ const cloned = deepClone(e);
330
+ cloned.id = uuidv4();
331
+ cloned.source = idMap.get(e.source) ?? e.source;
332
+ cloned.target = idMap.get(e.target) ?? e.target;
333
+ return cloned;
334
+ });
335
+
336
+ const newIds = new Set(newNodes.map((n) => n.id));
337
+ const newEdgeIds = new Set(newEdges.map((e) => e.id));
338
+ const prevSelected: Record<string, boolean> = {};
339
+ for (const node of sys.nodeStore.getState().nodes) {
340
+ prevSelected[node.id] = !!node.selected;
341
+ }
342
+
343
+ sys.undoManager.execute({
344
+ name: 'Paste',
345
+ execute: () => {
346
+ sys.nodeStore.getState().setNodes((nodes) => {
347
+ const unselected = nodes.map((n) => ({ ...n, selected: false }));
348
+ return [...unselected, ...newNodes];
349
+ });
350
+ if (newEdges.length) {
351
+ const existingEdges = sys.edgeStore.getState().edges;
352
+ sys.edgeStore
353
+ .getState()
354
+ .setEdges([...existingEdges, ...newEdges]);
355
+ }
356
+ },
357
+ undo: () => {
358
+ sys.nodeStore
359
+ .getState()
360
+ .setNodes((nodes) =>
361
+ nodes
362
+ .filter((n) => !newIds.has(n.id))
363
+ .map((n) => ({ ...n, selected: prevSelected[n.id] ?? false }))
364
+ );
365
+ if (newEdgeIds.size) {
366
+ sys.edgeStore
367
+ .getState()
368
+ .setEdges(
369
+ sys.edgeStore
370
+ .getState()
371
+ .edges.filter((e) => !newEdgeIds.has(e.id))
372
+ );
373
+ }
374
+ }
375
+ });
376
+
377
+ pasteSequence += 1;
378
+ },
379
+ toggleNodeHidden: (nodeId: string) => {
380
+ const currentNode = sys.nodeStore
381
+ .getState()
382
+ .nodes.find((n) => n.id === nodeId);
383
+
384
+ if (!currentNode || !('data' in currentNode)) return;
385
+
386
+ const currentHiddenState =
387
+ currentNode.data.annotations?.[hidden] ?? false;
388
+ const newHiddenState = !currentHiddenState;
389
+
390
+ sys.undoManager.execute({
391
+ name: newHiddenState ? 'Hide node' : 'Show node',
392
+ execute: () => {
393
+ sys.nodeStore.getState().setNodes((nodes) =>
394
+ nodes.map((node) =>
395
+ node.id === nodeId && 'data' in node
396
+ ? {
397
+ ...node,
398
+ data: {
399
+ ...node.data,
400
+ annotations: {
401
+ ...node.data.annotations,
402
+ [hidden]: newHiddenState
403
+ }
404
+ }
405
+ }
406
+ : node
407
+ )
408
+ );
409
+ },
410
+ undo: () => {
411
+ sys.nodeStore.getState().setNodes((nodes) =>
412
+ nodes.map((node) =>
413
+ node.id === nodeId && 'data' in node
414
+ ? {
415
+ ...node,
416
+ data: {
417
+ ...node.data,
418
+ annotations: {
419
+ ...node.data.annotations,
420
+ [hidden]: currentHiddenState
421
+ }
422
+ }
423
+ }
424
+ : node
425
+ )
426
+ );
427
+ }
428
+ });
429
+ },
430
+ toggleNodePinned: (nodeId: string) => {
431
+ const currentNode = sys.nodeStore
432
+ .getState()
433
+ .nodes.find((n) => n.id === nodeId);
434
+
435
+ if (!currentNode || !('data' in currentNode)) return;
436
+
437
+ const currentPinnedState =
438
+ currentNode.data.annotations?.[pinned] ?? false;
439
+ const newPinnedState = !currentPinnedState;
440
+
441
+ sys.undoManager.execute({
442
+ name: newPinnedState ? 'Pin node' : 'Unpin node',
443
+ execute: () => {
444
+ sys.nodeStore.getState().setNodes((nodes) =>
445
+ nodes.map((node) =>
446
+ node.id === nodeId && 'data' in node
447
+ ? {
448
+ ...node,
449
+ draggable: !newPinnedState,
450
+ data: {
451
+ ...node.data,
452
+ annotations: {
453
+ ...node.data.annotations,
454
+ [pinned]: newPinnedState
455
+ }
456
+ }
457
+ }
458
+ : node
459
+ )
460
+ );
461
+ },
462
+ undo: () => {
463
+ sys.nodeStore.getState().setNodes((nodes) =>
464
+ nodes.map((node) =>
465
+ node.id === nodeId && 'data' in node
466
+ ? {
467
+ ...node,
468
+ draggable: currentPinnedState,
469
+ data: {
470
+ ...node.data,
471
+ annotations: {
472
+ ...node.data.annotations,
473
+ [pinned]: currentPinnedState
474
+ }
475
+ }
476
+ }
477
+ : node
478
+ )
479
+ );
480
+ }
481
+ });
482
+ },
483
+ groupNodes: () => {
484
+ const nodes = sys.nodeStore.getState().nodes;
485
+ // Only group nodes that don't already have a parent
486
+ const selectedNodes = nodes.filter((n) => n.selected && !n.parentId);
487
+
488
+ if (selectedNodes.length === 0) return;
489
+
490
+ // Calculate bounds of selected nodes
491
+ const rect = getNodesBounds(selectedNodes);
492
+ const groupId = uuidv4();
493
+
494
+ const padding = 25;
495
+ const groupNode: Node = {
496
+ id: groupId,
497
+ type: 'group',
498
+ position: { x: rect.x - padding, y: rect.y - padding },
499
+ data: { color: '#6366f1' },
500
+ style: {
501
+ width: rect.width + padding * 2,
502
+ height: rect.height + padding * 2
503
+ },
504
+ selected: false,
505
+ draggable: true
506
+ };
507
+
508
+ // Store original state for undo
509
+ const originalNodes = selectedNodes.map((n) => ({
510
+ id: n.id,
511
+ parentId: n.parentId,
512
+ position: { ...n.position },
513
+ extent: n.extent
514
+ }));
515
+
516
+ sys.undoManager.execute({
517
+ name: 'Group nodes',
518
+ execute: () => {
519
+ sys.nodeStore.getState().setNodes((nodes) => {
520
+ const updatedNodes = nodes.map((node) => {
521
+ const selectedNode = selectedNodes.find(
522
+ (sn) => sn.id === node.id
523
+ );
524
+ if (!selectedNode) return node;
525
+
526
+ return {
527
+ ...node,
528
+ parentId: groupId,
529
+ position: {
530
+ x: node.position.x - rect.x + padding,
531
+ y: node.position.y - rect.y + padding
532
+ },
533
+ extent: 'parent' as const
534
+ };
535
+ });
536
+
537
+ // Add group node at the head of the array
538
+ return [groupNode, ...updatedNodes];
539
+ });
540
+ },
541
+ undo: () => {
542
+ sys.nodeStore.getState().setNodes((nodes) => {
543
+ // Remove the group node and restore original state
544
+ const filteredNodes = nodes.filter((n) => n.id !== groupId);
545
+ return filteredNodes.map((node) => {
546
+ const original = originalNodes.find((on) => on.id === node.id);
547
+ if (!original) return node;
548
+
549
+ return {
550
+ ...node,
551
+ parentId: original.parentId,
552
+ position: original.position,
553
+ extent: original.extent
554
+ };
555
+ });
556
+ });
557
+ }
558
+ });
559
+ },
560
+ ungroupNodes: (groupId: string) => {
561
+ const nodes = sys.nodeStore.getState().nodes;
562
+ const childNodes = nodes.filter((n) => n.parentId === groupId);
563
+
564
+ if (childNodes.length === 0) return;
565
+
566
+ const groupNode = nodes.find((n) => n.id === groupId);
567
+ if (!groupNode) return;
568
+
569
+ // Store original state for undo
570
+ const originalChildNodes = childNodes.map((n) => ({
571
+ id: n.id,
572
+ parentId: n.parentId,
573
+ position: { ...n.position },
574
+ extent: n.extent
575
+ }));
576
+
577
+ sys.undoManager.execute({
578
+ name: 'Ungroup nodes',
579
+ execute: () => {
580
+ sys.nodeStore.getState().setNodes((nodes) => {
581
+ return nodes
582
+ .filter((n) => n.id !== groupId)
583
+ .map((node) => {
584
+ const childNode = childNodes.find((cn) => cn.id === node.id);
585
+ if (!childNode) return node;
586
+
587
+ const posAbs = node.positionAbsolute || node.position;
588
+ return {
589
+ ...node,
590
+ parentId: undefined,
591
+ position: {
592
+ x: posAbs.x,
593
+ y: posAbs.y
594
+ },
595
+ extent: undefined
596
+ };
597
+ });
598
+ });
599
+ },
600
+ undo: () => {
601
+ sys.nodeStore.getState().setNodes((nodes) => {
602
+ const restoredNodes = nodes.map((node) => {
603
+ const original = originalChildNodes.find(
604
+ (on) => on.id === node.id
605
+ );
606
+ if (!original) return node;
607
+
608
+ return {
609
+ ...node,
610
+ parentId: original.parentId,
611
+ position: original.position,
612
+ extent: original.extent
613
+ };
614
+ });
615
+
616
+ // Add back the group node at the head
617
+ return [groupNode, ...restoredNodes];
618
+ });
619
+ }
620
+ });
621
+ },
622
+ deleteNodes: (nodeIds: string[]) => {
623
+ const nodes = sys.nodeStore.getState().nodes;
624
+ const edges = sys.edgeStore.getState().edges;
625
+
626
+ // Get edges connected to these nodes
627
+ const edgesToDelete = edges.filter(
628
+ (e) => nodeIds.includes(e.source) || nodeIds.includes(e.target)
629
+ );
630
+
631
+ sys.undoManager.execute({
632
+ name:
633
+ nodeIds.length === 1
634
+ ? 'Delete node'
635
+ : `Delete ${nodeIds.length} nodes`,
636
+ execute: () => {
637
+ sys.edgeStore
638
+ .getState()
639
+ .setEdges(
640
+ edges.filter((e) => !edgesToDelete.some((ed) => ed.id === e.id))
641
+ );
642
+ sys.nodeStore
643
+ .getState()
644
+ .setNodes(nodes.filter((n) => !nodeIds.includes(n.id)));
645
+ },
646
+ undo: () => {
647
+ sys.nodeStore.getState().setNodes([...nodes]);
648
+ sys.edgeStore.getState().setEdges([...edges]);
649
+ }
650
+ });
651
+ },
652
+ updateGroupColor: (groupId: string, color: string) => {
653
+ const nodes = sys.nodeStore.getState().nodes;
654
+ const groupNode = nodes.find((n) => n.id === groupId);
655
+
656
+ if (!groupNode) return;
657
+
658
+ const oldColor = groupNode.data?.color || '#6366f1';
659
+
660
+ sys.undoManager.execute({
661
+ name: 'Change group color',
662
+ execute: () => {
663
+ sys.nodeStore
664
+ .getState()
665
+ .setNodes(
666
+ nodes.map((n) =>
667
+ n.id === groupId ? { ...n, data: { ...n.data, color } } : n
668
+ )
669
+ );
670
+ },
671
+ undo: () => {
672
+ sys.nodeStore
673
+ .getState()
674
+ .setNodes(
675
+ sys.nodeStore
676
+ .getState()
677
+ .nodes.map((n) =>
678
+ n.id === groupId
679
+ ? { ...n, data: { ...n.data, color: oldColor } }
680
+ : n
681
+ )
682
+ );
683
+ }
684
+ });
685
+ }
686
+ },
687
+ setAction: (key, action) =>
688
+ set((state) => ({
689
+ actions: {
690
+ ...state.actions,
691
+ [key]: action
692
+ }
693
+ })),
694
+ getAction: (key) => {
695
+ const state = get();
696
+ return state.actions[key];
697
+ }
698
+ }));