@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,307 @@
1
+ import { useMemo } from 'react';
2
+ import type { SpanCollector } from '@/store/traces';
3
+ import type {
4
+ DerivedData,
5
+ LaneData,
6
+ Tick,
7
+ ViewState,
8
+ VisualSpan
9
+ } from './types';
10
+ import {
11
+ calculateTimeInterval,
12
+ clamp,
13
+ hashToHue,
14
+ MAX_ZOOM_OUT_FACTOR
15
+ } from './utils';
16
+
17
+ const MIN_WIDTH_PCT = 0.5;
18
+ const OVERLAP_EPSILON_PCT = 0.5;
19
+
20
+ /**
21
+ * Minimum *visual* span length (ms). Instant nodes record start === end (0ms),
22
+ * which otherwise collapses the range (maxEnd <= minStart) and renders nothing.
23
+ * The true duration is still reported to the tooltip.
24
+ */
25
+ const MIN_SPAN_MS = 1;
26
+
27
+ /** Effective end used for layout: clamps instant/zero-length spans to MIN_SPAN_MS. */
28
+ const visualEnd = (start: number, rawEnd: number): number =>
29
+ Math.max(rawEnd, start + MIN_SPAN_MS);
30
+
31
+ const EMPTY: DerivedData = {
32
+ now: 0,
33
+ size: 0,
34
+ lanes: 0,
35
+ minStart: 0,
36
+ maxEnd: 0,
37
+ range: 1,
38
+ viewStart: 0,
39
+ viewEnd: 0,
40
+ viewRange: 1,
41
+ buckets: [],
42
+ laneData: [],
43
+ ticks: []
44
+ };
45
+
46
+ function computeTicks(
47
+ viewStart: number,
48
+ viewEnd: number,
49
+ viewRange: number,
50
+ origin: number
51
+ ): Tick[] {
52
+ const interval = calculateTimeInterval(viewRange);
53
+ const firstTick = Math.ceil(viewStart / interval) * interval;
54
+ const ticks: Tick[] = [];
55
+ for (let t = firstTick; t <= viewEnd; t += interval) {
56
+ const leftPct = ((t - viewStart) / viewRange) * 100;
57
+ if (leftPct >= 0 && leftPct <= 100) {
58
+ // Position uses absolute time; the label is relative to the trace origin
59
+ // (first span) so it reads 0ms… instead of the raw clock value.
60
+ ticks.push({ time: t - origin, leftPct });
61
+ }
62
+ }
63
+ return ticks;
64
+ }
65
+
66
+ export function useDerivedSpans(
67
+ collector: SpanCollector,
68
+ version: number,
69
+ windowMs: number,
70
+ view: ViewState
71
+ ): DerivedData {
72
+ return useMemo(
73
+ () => computeDerivedSpans(collector, windowMs, view),
74
+ [collector, version, windowMs, view.follow, view.range, view.start]
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Pure span → layout derivation used by {@link useDerivedSpans}. Exported so the
80
+ * layout math (lane assignment, min-span visibility, ticks, zoom clamping) can be
81
+ * unit-tested without a React render.
82
+ */
83
+ type TraceSpan = import('@/store/traces').TraceSpan;
84
+
85
+ interface SpanScan {
86
+ minStart: number;
87
+ maxEnd: number;
88
+ maxLane: number;
89
+ buckets: TraceSpan[][];
90
+ }
91
+
92
+ /** Walk the ring buffer newest-window-first, bucketing spans by lane and
93
+ * tracking the overall time/lane bounds. */
94
+ function scanSpans(collector: SpanCollector, now: number): SpanScan {
95
+ const { capacity, size, writeIndex, spans } = collector;
96
+ let minStart = Number.POSITIVE_INFINITY;
97
+ let maxEnd = Number.NEGATIVE_INFINITY;
98
+ let maxLane = -1;
99
+ const buckets: TraceSpan[][] = [];
100
+
101
+ const startIndex = (writeIndex - size + capacity) % capacity;
102
+ for (let i = 0; i < size; i++) {
103
+ const idx = (startIndex + i) % capacity;
104
+ const s = spans[idx];
105
+ if (!s) continue;
106
+
107
+ const rawEnd = Number.isNaN(s.end) ? now : s.end;
108
+ const end = visualEnd(s.start, rawEnd);
109
+ minStart = Math.min(minStart, s.start);
110
+ maxEnd = Math.max(maxEnd, end);
111
+ maxLane = Math.max(maxLane, s.lane);
112
+
113
+ (buckets[s.lane] ??= []).push(s);
114
+ }
115
+
116
+ return { minStart, maxEnd, maxLane, buckets };
117
+ }
118
+
119
+ interface ViewWindow {
120
+ viewStart: number;
121
+ viewEnd: number;
122
+ viewRange: number;
123
+ }
124
+
125
+ /** Resolve the visible time window from the data bounds, the follow/zoom mode,
126
+ * and the requested window size. */
127
+ function resolveViewWindow(
128
+ scan: SpanScan,
129
+ fullRange: number,
130
+ windowMs: number,
131
+ view: ViewState
132
+ ): ViewWindow {
133
+ const { minStart, maxEnd } = scan;
134
+ const desiredRange = windowMs <= 0 ? fullRange : Math.max(1, windowMs);
135
+ const desiredStart = Math.max(minStart, maxEnd - desiredRange);
136
+
137
+ // Following snaps to the data range; manual zoom may pull back past the data
138
+ // extent (up to MAX_ZOOM_OUT_FACTOR×) so short traces aren't un-zoom-out-able.
139
+ const viewRange = clamp(
140
+ view.follow ? desiredRange : view.range,
141
+ 1,
142
+ view.follow ? fullRange : fullRange * MAX_ZOOM_OUT_FACTOR
143
+ );
144
+ const viewStart = clamp(
145
+ view.follow ? desiredStart : view.start,
146
+ minStart,
147
+ maxEnd - viewRange
148
+ );
149
+
150
+ return { viewStart, viewEnd: viewStart + viewRange, viewRange };
151
+ }
152
+
153
+ type PlacedSpan = {
154
+ span: TraceSpan;
155
+ leftPct: number;
156
+ widthPct: number;
157
+ rightPct: number;
158
+ durationMs: number;
159
+ };
160
+
161
+ /** Project a single span onto the view window, or null if it falls outside. */
162
+ function placeSpan(
163
+ s: TraceSpan,
164
+ now: number,
165
+ win: ViewWindow
166
+ ): PlacedSpan | null {
167
+ const { viewStart, viewEnd, viewRange } = win;
168
+ const rawEnd = Number.isNaN(s.end) ? now : s.end;
169
+ const end = visualEnd(s.start, rawEnd);
170
+ if (end < viewStart || s.start > viewEnd) return null;
171
+
172
+ const visibleStart = Math.max(s.start, viewStart);
173
+ const visibleEnd = Math.min(end, viewEnd);
174
+ if (visibleEnd <= visibleStart) return null;
175
+
176
+ const rawLeft = ((visibleStart - viewStart) / viewRange) * 100;
177
+ const rawWidth = ((visibleEnd - visibleStart) / viewRange) * 100;
178
+ const leftPct = clamp(rawLeft, 0, 100);
179
+ const widthPct = clamp(Math.max(MIN_WIDTH_PCT, rawWidth), 0, 100 - leftPct);
180
+ const rightPct = leftPct + widthPct;
181
+ // Report the true duration (0ms for instant nodes), not the padded one.
182
+ const durationMs = Math.max(0, rawEnd - s.start);
183
+
184
+ return { span: s, leftPct, widthPct, rightPct, durationMs };
185
+ }
186
+
187
+ /** Pack a placed span into the lowest stack row that has cleared its left edge,
188
+ * returning the chosen stack index and updating `stackRight` in place. */
189
+ function assignStack(placed: PlacedSpan, stackRight: number[]): number {
190
+ let stack = 0;
191
+ for (; stack < stackRight.length; stack++) {
192
+ if (placed.leftPct >= stackRight[stack]! + OVERLAP_EPSILON_PCT) break;
193
+ }
194
+ if (stack === stackRight.length) stackRight.push(placed.rightPct);
195
+ else stackRight[stack] = Math.max(stackRight[stack]!, placed.rightPct);
196
+ return stack;
197
+ }
198
+
199
+ /** Build the fill/border colours for a span at a given stack depth. */
200
+ function spanColors(
201
+ span: TraceSpan,
202
+ stack: number
203
+ ): { bg: string; border: string } {
204
+ const hue = hashToHue(span.nodeId);
205
+ const isOpen = Number.isNaN(span.end);
206
+ const lightness = clamp(56 - stack * 7, 30, 60);
207
+ const bg = isOpen
208
+ ? `hsla(${hue}, 80%, ${clamp(lightness + 6, 30, 65)}%, 0.35)`
209
+ : `hsla(${hue}, 80%, ${lightness}%, 0.6)`;
210
+ const border = `hsla(${hue}, 90%, ${clamp(lightness + 22, 45, 80)}%, 0.95)`;
211
+ return { bg, border };
212
+ }
213
+
214
+ /** Lay out one lane's spans into stacked, coloured visual spans. */
215
+ function buildLaneData(
216
+ laneSpans: TraceSpan[] | undefined,
217
+ now: number,
218
+ win: ViewWindow
219
+ ): LaneData {
220
+ if (!laneSpans || laneSpans.length === 0) {
221
+ return { stackCount: 1, visualSpans: [] };
222
+ }
223
+
224
+ const placed: PlacedSpan[] = [];
225
+ for (const s of laneSpans) {
226
+ const p = placeSpan(s, now, win);
227
+ if (p) placed.push(p);
228
+ }
229
+ placed.sort((a, b) => a.leftPct - b.leftPct);
230
+
231
+ const stackRight: number[] = [];
232
+ const visualSpans: VisualSpan[] = [];
233
+ for (const v of placed) {
234
+ const stack = assignStack(v, stackRight);
235
+ const { bg, border } = spanColors(v.span, stack);
236
+ visualSpans.push({
237
+ span: v.span,
238
+ leftPct: v.leftPct,
239
+ widthPct: v.widthPct,
240
+ rightPct: v.rightPct,
241
+ durationMs: v.durationMs,
242
+ stack,
243
+ bg,
244
+ border
245
+ });
246
+ }
247
+
248
+ return { stackCount: Math.max(1, stackRight.length), visualSpans };
249
+ }
250
+
251
+ export function computeDerivedSpans(
252
+ collector: SpanCollector,
253
+ windowMs: number,
254
+ view: ViewState
255
+ ): DerivedData {
256
+ const now = performance.now();
257
+
258
+ if (collector.size <= 0) return { ...EMPTY, now };
259
+
260
+ const scan = scanSpans(collector, now);
261
+ const { minStart, maxEnd, maxLane, buckets } = scan;
262
+ const { size } = collector;
263
+
264
+ if (
265
+ !Number.isFinite(minStart) ||
266
+ !Number.isFinite(maxEnd) ||
267
+ maxEnd <= minStart
268
+ ) {
269
+ return {
270
+ ...EMPTY,
271
+ now,
272
+ size,
273
+ lanes: maxLane + 1,
274
+ buckets,
275
+ laneData: []
276
+ };
277
+ }
278
+
279
+ const fullRange = Math.max(1, maxEnd - minStart);
280
+ const win = resolveViewWindow(scan, fullRange, windowMs, view);
281
+
282
+ const laneData: LaneData[] = Array.from({ length: maxLane + 1 }, (_, lane) =>
283
+ buildLaneData(buckets[lane], now, win)
284
+ );
285
+
286
+ const ticks = computeTicks(
287
+ win.viewStart,
288
+ win.viewEnd,
289
+ win.viewRange,
290
+ minStart
291
+ );
292
+
293
+ return {
294
+ now,
295
+ size,
296
+ lanes: maxLane + 1,
297
+ minStart,
298
+ maxEnd,
299
+ range: fullRange,
300
+ viewStart: win.viewStart,
301
+ viewEnd: win.viewEnd,
302
+ viewRange: win.viewRange,
303
+ buckets,
304
+ laneData,
305
+ ticks
306
+ };
307
+ }
@@ -0,0 +1,33 @@
1
+ export const hashToHue = (str: string): number => {
2
+ let hash = 0;
3
+ for (let i = 0; i < str.length; i++)
4
+ hash = (hash * 31 + str.charCodeAt(i)) | 0;
5
+ return Math.abs(hash) % 360;
6
+ };
7
+
8
+ export const clamp = (v: number, min: number, max: number): number =>
9
+ Math.max(min, Math.min(max, v));
10
+
11
+ /**
12
+ * How far past the captured data extent the user may zoom out. Without headroom
13
+ * the view range is clamped to the data range, so you hit a wall and "can't zoom
14
+ * out" , especially for very short traces. This lets the trace be pulled back
15
+ * into a smaller cluster with surrounding space.
16
+ */
17
+ export const MAX_ZOOM_OUT_FACTOR = 8;
18
+
19
+ const NICE_INTERVALS = [
20
+ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000
21
+ ] as const;
22
+
23
+ /**
24
+ * Picks a "nice" round interval for time-axis grid lines.
25
+ * Targets roughly 8 ticks across the visible range.
26
+ */
27
+ export const calculateTimeInterval = (rangeMs: number): number => {
28
+ const rawInterval = rangeMs / 8;
29
+ for (const mag of NICE_INTERVALS) {
30
+ if (mag >= rawInterval) return mag;
31
+ }
32
+ return NICE_INTERVALS[NICE_INTERVALS.length - 1]!;
33
+ };
@@ -0,0 +1,162 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { useStore } from 'zustand';
3
+ import { Variable } from '@kiberon-labs/behave-graph';
4
+ import { useActiveGraph } from '@/system/provider';
5
+ import {
6
+ VscodeButton,
7
+ VscodeTextfield,
8
+ VscodeSingleSelect,
9
+ VscodeOption,
10
+ VscodeLabel
11
+ } from '@vscode-elements/react-elements';
12
+ import styles from './styles.module.css';
13
+
14
+ type Props = {
15
+ onBack: () => void;
16
+ onCancel: () => void;
17
+ onCreated?: (variableKey: string) => void;
18
+ };
19
+
20
+ export function CreateVariableScreen({ onBack, onCancel, onCreated }: Props) {
21
+ const system = useActiveGraph()!;
22
+ const registry = useStore(system.editor.registry);
23
+ const setVariable = useStore(system.variableStore, (x) => x.setVariable);
24
+ const controls = useStore(system.controlStore, (x) => x.controls);
25
+ const defaultControl = useStore(system.controlStore, (x) => x.defaultControl);
26
+
27
+ const [newVarName, setNewVarName] = useState('');
28
+ const [newVarType, setNewVarType] = useState('string');
29
+ const [newVarInitialValue, setNewVarInitialValue] = useState<any>('');
30
+
31
+ const availableTypes = useMemo(() => {
32
+ return Object.keys(registry.values).sort();
33
+ }, [registry.values]);
34
+
35
+ const getControlComponent = useCallback(
36
+ (valueType: string) => {
37
+ return controls[valueType] || defaultControl;
38
+ },
39
+ [controls, defaultControl]
40
+ );
41
+
42
+ const NewVarControlComponent = useMemo(() => {
43
+ return getControlComponent(newVarType);
44
+ }, [getControlComponent, newVarType]);
45
+
46
+ const resetForm = useCallback(() => {
47
+ setNewVarName('');
48
+ const valueType = registry.values[newVarType];
49
+ if (valueType) {
50
+ setNewVarInitialValue(valueType.creator());
51
+ }
52
+ }, [registry.values, newVarType]);
53
+
54
+ const handleCreate = useCallback(() => {
55
+ if (!newVarName.trim()) return;
56
+
57
+ const valueType = registry.values[newVarType];
58
+ if (!valueType) {
59
+ console.error(`Value type ${newVarType} not found in registry`);
60
+ return;
61
+ }
62
+
63
+ const newVariable = new Variable(
64
+ newVarName,
65
+ newVarName,
66
+ newVarType,
67
+ newVarInitialValue
68
+ );
69
+
70
+ setVariable(newVarName, newVariable);
71
+ onCreated?.(newVarName);
72
+ resetForm();
73
+ onBack();
74
+ }, [
75
+ newVarName,
76
+ newVarType,
77
+ newVarInitialValue,
78
+ registry.values,
79
+ setVariable,
80
+ onCreated,
81
+ resetForm,
82
+ onBack
83
+ ]);
84
+
85
+ const handleCancel = useCallback(() => {
86
+ resetForm();
87
+ onCancel();
88
+ }, [resetForm, onCancel]);
89
+
90
+ const handleBack = useCallback(() => {
91
+ resetForm();
92
+ onBack();
93
+ }, [resetForm, onBack]);
94
+
95
+ return (
96
+ <>
97
+ <div className={styles.headerRow}>
98
+ <h3
99
+ style={{
100
+ fontSize: '1.1em',
101
+ fontWeight: 'bold'
102
+ }}
103
+ >
104
+ Create Variable
105
+ </h3>
106
+
107
+ <VscodeButton secondary onClick={handleBack}>
108
+ Back
109
+ </VscodeButton>
110
+ </div>
111
+
112
+ <div className={`${styles.section} ${styles.sectionPadded}`}>
113
+ <div className={styles.field}>
114
+ <VscodeLabel>Name:</VscodeLabel>
115
+ <VscodeTextfield
116
+ value={newVarName}
117
+ placeholder="Variable name..."
118
+ onChange={(e: any) => setNewVarName(e.target.value)}
119
+ />
120
+ </div>
121
+
122
+ <div className={styles.field}>
123
+ <VscodeLabel>Type:</VscodeLabel>
124
+ <VscodeSingleSelect
125
+ value={newVarType}
126
+ onChange={(e: any) => {
127
+ const valueType = registry.values[e.target.value];
128
+ if (valueType?.serialize && valueType?.creator) {
129
+ setNewVarInitialValue(valueType.serialize(valueType.creator()));
130
+ }
131
+ setNewVarType(e.target.value);
132
+ }}
133
+ >
134
+ {availableTypes.map((type) => (
135
+ <VscodeOption key={type} value={type}>
136
+ {type}
137
+ </VscodeOption>
138
+ ))}
139
+ </VscodeSingleSelect>
140
+ </div>
141
+
142
+ <div className={styles.field}>
143
+ <VscodeLabel>Initial Value:</VscodeLabel>
144
+ <NewVarControlComponent
145
+ value={newVarInitialValue}
146
+ onChange={setNewVarInitialValue}
147
+ valueType={newVarType}
148
+ />
149
+ </div>
150
+
151
+ <div className={styles.actionsRow}>
152
+ <VscodeButton onClick={handleCreate} disabled={!newVarName.trim()}>
153
+ Save
154
+ </VscodeButton>
155
+ <VscodeButton secondary onClick={handleCancel}>
156
+ Cancel
157
+ </VscodeButton>
158
+ </div>
159
+ </div>
160
+ </>
161
+ );
162
+ }
@@ -0,0 +1,147 @@
1
+ import React from 'react';
2
+ import type { VariableJSON } from '@kiberon-labs/behave-graph';
3
+ import {
4
+ VscodeButton,
5
+ VscodeDivider,
6
+ VscodeTree,
7
+ VscodeTreeItem
8
+ } from '@vscode-elements/react-elements';
9
+ import { Plus, TrashSolid } from 'iconoir-react';
10
+ import styles from './styles.module.css';
11
+ import { useActiveGraph } from '@/system';
12
+ import { useStore } from 'zustand';
13
+ import { Icon } from '@/components/primitives/icon';
14
+ import { PanelHeader } from '../common/PanelHeader';
15
+
16
+ type ValueControlProps = {
17
+ value: any;
18
+ onChange: (value: any) => void;
19
+ valueType: string;
20
+ };
21
+
22
+ type Props = {
23
+ variables: Record<string, VariableJSON>;
24
+ selectedVarKey: string;
25
+ onSelectVariable: (key: string) => void;
26
+ onDeleteVariable: (key: string) => void;
27
+ onNewVariable: () => void;
28
+
29
+ selectedVariable: VariableJSON | null;
30
+ editValue: any;
31
+ onChangeEditValue: (value: any) => void;
32
+ SelectedVarControlComponent: React.ComponentType<ValueControlProps>;
33
+ onUpdateVariable: () => void;
34
+ };
35
+
36
+ export function ManageVariablesScreen({
37
+ selectedVarKey: _selectedVarKey,
38
+ onSelectVariable,
39
+ onDeleteVariable,
40
+ onNewVariable,
41
+ selectedVariable,
42
+ editValue,
43
+ onChangeEditValue,
44
+ SelectedVarControlComponent,
45
+ onUpdateVariable
46
+ }: Props) {
47
+ const system = useActiveGraph()!;
48
+ const icons = useStore(system.editor.legendStore, (x) => x.icons);
49
+ const variables = useStore(system.variableStore, (x) => x.variables);
50
+
51
+ const handleTreeSelect = (ev: unknown) => {
52
+ const detail = (ev as CustomEvent<any>)?.detail;
53
+ const selectedItems = Array.isArray(detail)
54
+ ? detail
55
+ : detail?.selectedItems;
56
+ const firstSelected = selectedItems?.[0] as HTMLElement | undefined;
57
+ const key = firstSelected?.dataset?.varKey;
58
+
59
+ if (key) {
60
+ onSelectVariable(key);
61
+ }
62
+ };
63
+
64
+ return (
65
+ <div className={styles.root}>
66
+ <PanelHeader
67
+ title="Variables"
68
+ actions={
69
+ <Icon title="New variable" onClick={onNewVariable}>
70
+ <Plus />
71
+ </Icon>
72
+ }
73
+ />
74
+ <VscodeDivider />
75
+
76
+ <div className={styles.variableList}>
77
+ {Object.keys(variables).length === 0 ? (
78
+ <div className={styles.emptyState}>
79
+ <div className={styles.emptyStateText}>No variables defined</div>
80
+ <div className={styles.emptyStateHint}>
81
+ Click the "+" button to create your first variable
82
+ </div>
83
+ </div>
84
+ ) : (
85
+ <VscodeTree
86
+ className={styles.tree}
87
+ onVscTreeSelect={handleTreeSelect}
88
+ >
89
+ {Object.entries(variables).map(([name, variable]) => (
90
+ <VscodeTreeItem
91
+ key={name}
92
+ data-var-key={name}
93
+ active={variable.id == selectedVariable?.id}
94
+ >
95
+ <div className={styles.treeItemContent}>
96
+ <div className={styles.treeItemIcon}>
97
+ {icons[variable.valueTypeName] &&
98
+ React.createElement(icons[variable.valueTypeName]!)}
99
+ </div>
100
+ <div className={styles.treeItemInfo} title={variable.id}>
101
+ {variable.name}
102
+ </div>
103
+ </div>
104
+ </VscodeTreeItem>
105
+ ))}
106
+ </VscodeTree>
107
+ )}
108
+ </div>
109
+
110
+ {selectedVariable && (
111
+ <div className={styles.selectedEditor}>
112
+ <div className={styles.editorHeader}>
113
+ <div className={styles.editorTitle}>Edit Variable</div>
114
+ <div className={styles.editorVariableName}>
115
+ {selectedVariable.name}
116
+ </div>
117
+ </div>
118
+
119
+ <div className={styles.editorFields}>
120
+ <label className={styles.fieldLabel}>Current Value</label>
121
+ <div className={styles.fieldControl}>
122
+ <SelectedVarControlComponent
123
+ value={editValue}
124
+ onChange={onChangeEditValue}
125
+ valueType={selectedVariable.valueTypeName}
126
+ />
127
+ </div>
128
+ </div>
129
+
130
+ <div className={styles.editorActions}>
131
+ <VscodeButton
132
+ secondary
133
+ iconOnly
134
+ title="Delete variable"
135
+ onClick={() => {
136
+ onDeleteVariable(selectedVariable.name);
137
+ }}
138
+ >
139
+ <TrashSolid />
140
+ </VscodeButton>
141
+ <VscodeButton onClick={onUpdateVariable}>Update Value</VscodeButton>
142
+ </div>
143
+ </div>
144
+ )}
145
+ </div>
146
+ );
147
+ }