@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,653 @@
1
+ import { type StoreApi } from 'zustand';
2
+ import { createStore } from 'zustand/vanilla';
3
+ import type { Edge, Node, Viewport } from 'reactflow';
4
+ import { tabStoreFactory, type TabStore } from '@/store/tabs';
5
+ import { TabLoader } from './tabLoader';
6
+ import {
7
+ systemSettingsFactory,
8
+ PERSISTED_SETTING_KEYS,
9
+ type SystemSettingsStore
10
+ } from '../store/settings.js';
11
+ import {
12
+ settingsSchemaStoreFactory,
13
+ type SettingsSchemaStore,
14
+ type SettingDescriptor
15
+ } from '../store/settingsSchema.js';
16
+ import { legendStoreFactory, type LegendStore } from '@/store/legend';
17
+ import { menubarStoreFactory, type MenuBarStore } from '@/store/menubar';
18
+ import { hotKeyStoreFactory, type HotkeyStore } from '@/store/hotKeys';
19
+ import { PubSub } from './pubsub';
20
+ import { controlsStoreFactory, type ControlsStore } from '@/store/controls';
21
+ import { selectionStoreFactory, type SelectionStore } from '@/store/selection';
22
+ import { registryStoreFactory, type RegistryStore } from '@/store/registry';
23
+ import { specsStoreFactory, type SpecsStore } from '@/store/specs';
24
+ import { specificStoreFactory, type SpecificStore } from '@/store/specific';
25
+ import {
26
+ socketGeneratorStoreFactory,
27
+ type SocketGeneratorStore
28
+ } from '@/store/socketGenerator';
29
+ import {
30
+ documentationStoreFactory,
31
+ type DocumentationStore
32
+ } from '@/store/documentation';
33
+ import { toolbarStoreFactory, type ToolbarStore } from '@/store/toolbar';
34
+ import { Notifications, type NotificationData } from './notifications';
35
+ import {
36
+ conversionStoreFactory,
37
+ type ConversionStore,
38
+ type ConversionRule
39
+ } from '@/store/conversions';
40
+ import {
41
+ commandStoreFactory,
42
+ registerDefaultCommands,
43
+ type CommandStore,
44
+ type CommandContext
45
+ } from '@/store/commands';
46
+ import {
47
+ contextMenuStoreFactory,
48
+ registerDefaultContextMenu,
49
+ type ContextMenuStore
50
+ } from '@/store/contextMenu';
51
+ import { GraphSession } from './graphSession';
52
+ import { installPersistence, type PersistenceAdapter } from './persistence';
53
+ import { v4 as uuidv4 } from 'uuid';
54
+ import { tabIdForSession } from '@/components/layoutController/utils';
55
+ import type { Renderable } from 'react-hot-toast';
56
+ import type { INodeRegistry } from '@/types/NodeMetadata';
57
+ import type { LoadablePlugin, SessionExtension } from './plugin';
58
+ import type { UIGraphJSON } from '@/types/graph';
59
+ import type { GraphJSON } from '@kiberon-labs/behave-graph';
60
+ import type { LayoutBase } from 'rc-dock';
61
+ import type { FlowStore, NodeStore, EdgeStore } from '@/store/flow';
62
+ import type { VariableStore } from '@/store/variables';
63
+ import type { RefStore } from '@/store/refs';
64
+ import type { ActionStore } from '@/store/actions';
65
+ import type { TraceStore } from '@/store/traces';
66
+ import type { EventsStore } from '@/store/events';
67
+ import type { LogStore } from '@/store/logs';
68
+ import type { LayerStore } from '@/store/layers';
69
+ import type { UndoManager } from './undoRedo';
70
+ import type { Graph } from './graph';
71
+
72
+ /**
73
+ * Editor-level pubsub topics. These are global to the editor and shared across
74
+ * every open graph. Augment this interface (not {@link GraphPubSys}) for events
75
+ * that are not tied to a specific graph.
76
+ */
77
+ export interface EditorPubSys {
78
+ notification: NotificationData;
79
+ 'notification:dismiss': {
80
+ toastId?: string;
81
+ };
82
+ 'layout:saved': LayoutBase;
83
+ 'graph:saved': UIGraphJSON;
84
+ 'graph:inner:saved': GraphJSON;
85
+ }
86
+
87
+ /**
88
+ * Per-graph pubsub topics. Each {@link GraphSession} owns its own bus typed with
89
+ * this interface, so events stay isolated to the graph that produced them.
90
+ * Augment this interface for events that belong to a single graph.
91
+ */
92
+ export interface GraphPubSys {
93
+ 'edge:added': Edge;
94
+ 'node:added': Node;
95
+ 'edge:removed': Edge;
96
+ 'node:removed': Node;
97
+ graphAnnotationsChanged: {
98
+ [key: string]: any;
99
+ };
100
+ saveViewport: {
101
+ index: number;
102
+ viewport: Viewport;
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Combined pubsub surface kept for backwards compatibility. Prefer the split
108
+ * {@link EditorPubSys} / {@link GraphPubSys} interfaces.
109
+ */
110
+ export interface PubSys extends EditorPubSys, GraphPubSys {}
111
+
112
+ /**
113
+ * Use this to extend the System interface when adding plugins
114
+ */
115
+ export interface ISystem {}
116
+
117
+ /** Minimal storage adapter for persisting editor settings. */
118
+ export type SettingsStorage = {
119
+ getItem: (key: string) => string | null;
120
+ setItem: (key: string, value: string) => void;
121
+ };
122
+
123
+ /** Serialized editor-level settings (UI toggles + custom type conversions). */
124
+ export type EditorSettingsJSON = {
125
+ settings?: Record<string, any>;
126
+ conversions?: ConversionRule[];
127
+ };
128
+
129
+ const SETTINGS_STORAGE_KEY = 'behave-graph:editor-settings';
130
+
131
+ const settingSetterName = (key: string): string =>
132
+ `set${key.charAt(0).toUpperCase()}${key.slice(1)}`;
133
+
134
+ const defaultSettingsStorage = (): SettingsStorage | undefined => {
135
+ try {
136
+ if (typeof localStorage !== 'undefined') return localStorage;
137
+ } catch {
138
+ // localStorage access can throw in sandboxed contexts
139
+ }
140
+ return undefined;
141
+ };
142
+
143
+ /**
144
+ * Observable registry of open graph sessions plus the currently focused one.
145
+ * Backed by a zustand store so panels rendered outside of a graph tab can
146
+ * subscribe and re-render when the active graph changes.
147
+ */
148
+ export type ActiveGraphStore = {
149
+ activeGraphId: string | null;
150
+ sessions: Record<string, GraphSession>;
151
+ setActiveGraph: (id: string | null) => void;
152
+ addSession: (session: GraphSession) => void;
153
+ removeSession: (id: string) => void;
154
+ getActive: () => GraphSession | undefined;
155
+ };
156
+
157
+ const activeGraphStoreFactory = () =>
158
+ createStore<ActiveGraphStore>((set, get) => ({
159
+ activeGraphId: null,
160
+ sessions: {},
161
+ setActiveGraph: (activeGraphId) => set(() => ({ activeGraphId })),
162
+ addSession: (session) =>
163
+ set((state) => ({
164
+ sessions: { ...state.sessions, [session.id]: session }
165
+ })),
166
+ removeSession: (id) =>
167
+ set((state) => {
168
+ const next = { ...state.sessions };
169
+ delete next[id];
170
+ const activeGraphId =
171
+ state.activeGraphId === id ? null : state.activeGraphId;
172
+ return { sessions: next, activeGraphId };
173
+ }),
174
+ getActive: () => {
175
+ const state = get();
176
+ return state.activeGraphId
177
+ ? state.sessions[state.activeGraphId]
178
+ : undefined;
179
+ }
180
+ }));
181
+
182
+ /**
183
+ * The editor-level system. Holds state that is shared across every open graph
184
+ * (settings, registry, specs, menubar, tabs, ...) plus an observable registry of
185
+ * per-graph {@link GraphSession} instances. Per-graph state itself lives on the
186
+ * sessions, not here.
187
+ *
188
+ * The class is intentionally still named `System` so existing `declare module`
189
+ * augmentations (`interface System { ... }`) keep merging and the public API is
190
+ * stable; `EditorSystem` is exported as an alias for new code.
191
+ */
192
+ export class System implements ISystem {
193
+ public readonly pubsub = new PubSub<EditorPubSys>();
194
+ public readonly tabStore: StoreApi<TabStore>;
195
+ public readonly tabLoader: TabLoader;
196
+ public readonly systemSettings: StoreApi<SystemSettingsStore>;
197
+ /** Registry of setting descriptors driving the auto-generated Settings panel. */
198
+ public readonly settingsSchema: StoreApi<SettingsSchemaStore>;
199
+ public readonly legendStore: StoreApi<LegendStore>;
200
+ public readonly menubarStore: StoreApi<MenuBarStore>;
201
+ public readonly hotKeyStore: StoreApi<HotkeyStore>;
202
+ public readonly registry: StoreApi<RegistryStore>;
203
+ public readonly specStore: StoreApi<SpecsStore>;
204
+ public readonly specificStore: StoreApi<SpecificStore>;
205
+ public readonly socketGeneratorStore: StoreApi<SocketGeneratorStore>;
206
+ public readonly documentationStore: StoreApi<DocumentationStore>;
207
+ public readonly toolbarStore: StoreApi<ToolbarStore>;
208
+ public readonly controlStore: StoreApi<ControlsStore>;
209
+ /** User/plugin-defined automatic type conversions for auto-convert. */
210
+ public readonly conversionStore: StoreApi<ConversionStore>;
211
+ /** Named, dispatchable commands shared across hotkeys/menus/toolbar. */
212
+ public readonly commandStore: StoreApi<CommandStore>;
213
+ /** Per-target context-menu item registry (node/edge/selection/pane). */
214
+ public readonly contextMenuStore: StoreApi<ContextMenuStore>;
215
+ public readonly notifications: Notifications = new Notifications(this);
216
+
217
+ /** Observable registry of open graph sessions + the focused one. */
218
+ public readonly activeGraph: StoreApi<ActiveGraphStore>;
219
+
220
+ /** Editor-level extensions applied to every graph session on creation. */
221
+ private readonly sessionExtensions = new Set<SessionExtension>();
222
+
223
+ /** Disposer for the currently installed graph/layout save handlers. */
224
+ private persistenceDisposer: () => void = () => {};
225
+
226
+ protected deps: Record<string, unknown> = {};
227
+
228
+ /**
229
+ * Create a new editor System instance
230
+ * @param registry - INodeRegistry containing nodes and values metadata
231
+ */
232
+ constructor(registry?: INodeRegistry) {
233
+ this.activeGraph = activeGraphStoreFactory();
234
+ this.tabStore = tabStoreFactory();
235
+ this.controlStore = controlsStoreFactory();
236
+ this.systemSettings = systemSettingsFactory();
237
+ // Seeded with the built-in setting descriptors (DEFAULT_SETTINGS). Plugins
238
+ // append their own via registerSetting(...).
239
+ this.settingsSchema = settingsSchemaStoreFactory();
240
+ this.legendStore = legendStoreFactory();
241
+ this.registry = registryStoreFactory();
242
+ this.specStore = specsStoreFactory(this);
243
+ this.socketGeneratorStore = socketGeneratorStoreFactory();
244
+ this.specificStore = specificStoreFactory();
245
+ this.documentationStore = documentationStoreFactory();
246
+ this.toolbarStore = toolbarStoreFactory();
247
+ this.conversionStore = conversionStoreFactory();
248
+ this.commandStore = commandStoreFactory();
249
+ this.contextMenuStore = contextMenuStoreFactory();
250
+ this.hotKeyStore = hotKeyStoreFactory(this);
251
+
252
+ // Seed the built-in commands + context-menu items. Hosts can override by id
253
+ // or add their own via these registries.
254
+ registerDefaultCommands(this.commandStore);
255
+ registerDefaultContextMenu(this.contextMenuStore);
256
+
257
+ // Handle registry initialization
258
+ if (registry) {
259
+ this.registry.getState().updateRegistry(registry);
260
+ this.specStore.getState().setSpecs(registry.specs);
261
+ }
262
+
263
+ this.menubarStore = menubarStoreFactory();
264
+ this.tabLoader = new TabLoader(this);
265
+
266
+ // Wire the default graph/layout save handlers (download-to-file) so a fresh
267
+ // editor has working Save actions out of the box. Hosts that persist
268
+ // elsewhere override via enablePersistence(...) or opt out with
269
+ // disablePersistence().
270
+ this.persistenceDisposer = installPersistence(this);
271
+ }
272
+
273
+ /**
274
+ * The currently focused graph session, if any.
275
+ */
276
+ get session(): GraphSession | undefined {
277
+ return this.activeGraph.getState().getActive();
278
+ }
279
+
280
+ /**
281
+ * Create a new graph session, register it and (by default) make it active.
282
+ */
283
+ createSession(
284
+ id = 'graph',
285
+ options?: { activate?: boolean; name?: string }
286
+ ): GraphSession {
287
+ const session = new GraphSession(this, id, options?.name ?? 'Graph');
288
+ this.activeGraph.getState().addSession(session);
289
+ // Let editor plugins extend the fully-constructed session before it becomes
290
+ // active, so panels reacting to the active graph see a complete instance.
291
+ for (const extension of this.sessionExtensions) {
292
+ this.applySessionExtension(session, extension);
293
+ }
294
+ if (options?.activate ?? true) {
295
+ this.activeGraph.getState().setActiveGraph(id);
296
+ }
297
+ return session;
298
+ }
299
+
300
+ /**
301
+ * Look up an existing session by id, creating an empty one if missing.
302
+ */
303
+ getOrCreateSession(id: string): GraphSession {
304
+ return (
305
+ this.activeGraph.getState().sessions[id] ??
306
+ this.createSession(id, { activate: false })
307
+ );
308
+ }
309
+
310
+ /**
311
+ * Create a brand new, empty graph in its own tab and focus it.
312
+ */
313
+ newGraph(name?: string): GraphSession {
314
+ const id = uuidv4();
315
+ const count = Object.keys(this.activeGraph.getState().sessions).length;
316
+ const session = this.createSession(id, {
317
+ activate: true,
318
+ name: name ?? `Untitled ${count}`
319
+ });
320
+ this.tabStore.getState().openTab(tabIdForSession(id));
321
+ return session;
322
+ }
323
+
324
+ /**
325
+ * Dispose a graph session and remove it from the registry. Called when its
326
+ * tab is closed.
327
+ */
328
+ disposeSession(id: string): void {
329
+ const session = this.activeGraph.getState().sessions[id];
330
+ if (!session) return;
331
+ session.dispose();
332
+ this.activeGraph.getState().removeSession(id);
333
+ }
334
+
335
+ // ---------------------------------------------------------------------------
336
+ // Focused-graph accessors.
337
+ //
338
+ // All graph-canvas and dock-panel consumers read per-graph state through
339
+ // useGraph()/useActiveGraph(), and execution is per-session via each
340
+ // GraphSession's run controller. These getters remain only for editor-level
341
+ // surfaces that imperatively act on the *focused* graph , the hotkey handlers,
342
+ // layout utilities, the menubar, and server-metadata fan-out. They resolve to
343
+ // the focused session, consistent with the "panels follow focus" model; they
344
+ // are a convenience, not a single-open-graph limit (multiple graphs are open
345
+ // and independently runnable at once).
346
+ // ---------------------------------------------------------------------------
347
+ get flowStore(): StoreApi<FlowStore> {
348
+ return this.session!.flowStore;
349
+ }
350
+ get nodeStore(): StoreApi<NodeStore> {
351
+ return this.session!.nodeStore;
352
+ }
353
+ get edgeStore(): StoreApi<EdgeStore> {
354
+ return this.session!.edgeStore;
355
+ }
356
+ get variableStore(): StoreApi<VariableStore> {
357
+ return this.session!.variableStore;
358
+ }
359
+ get selectionStore(): StoreApi<SelectionStore> {
360
+ return this.session!.selectionStore;
361
+ }
362
+ get refStore(): StoreApi<RefStore> {
363
+ return this.session!.refStore;
364
+ }
365
+ get actionStore(): StoreApi<ActionStore> {
366
+ return this.session!.actionStore;
367
+ }
368
+ get traceStore(): StoreApi<TraceStore> {
369
+ return this.session!.traceStore;
370
+ }
371
+ get eventsStore(): StoreApi<EventsStore> {
372
+ return this.session!.eventsStore;
373
+ }
374
+ get logsStore(): StoreApi<LogStore> {
375
+ return this.session!.logsStore;
376
+ }
377
+ get layerStore(): StoreApi<LayerStore> {
378
+ return this.session!.layerStore;
379
+ }
380
+ get graph(): Graph {
381
+ return this.session!.graph;
382
+ }
383
+ get undoManager(): UndoManager {
384
+ return this.session!.undoManager;
385
+ }
386
+
387
+ /**
388
+ * Adds a new dependency to the system
389
+ * @param name
390
+ * @param val
391
+ */
392
+ decorate(name: keyof System, val: any) {
393
+ //@ts-ignore
394
+ this[name] = val;
395
+ }
396
+
397
+ /**
398
+ * Load a plugin into the system
399
+ * @param pluginInit - Plugin initialization function
400
+ * @param options - Optional configuration options for the plugin
401
+ * @template TOptions - Type of options object
402
+ *
403
+ * @example
404
+ * // Plugin without options
405
+ * system.registerPlugin(docsPlugin);
406
+ *
407
+ * @example
408
+ * // Plugin with typed options
409
+ * interface MyPluginOptions {
410
+ * enabled: boolean;
411
+ * apiKey: string;
412
+ * }
413
+ *
414
+ * const myPlugin: Plugin<MyPluginOptions> = (system, options) => {
415
+ * console.log('Plugin enabled:', options.enabled);
416
+ * };
417
+ *
418
+ * system.registerPlugin(myPlugin, { enabled: true, apiKey: 'secret' });
419
+ */
420
+ async registerPlugin<TOptions = void>(
421
+ plugin: LoadablePlugin<TOptions>,
422
+ options?: TOptions
423
+ ): Promise<void> {
424
+ await plugin.loader(this, options as TOptions);
425
+ console.log(`Plugin loaded: ${plugin.opts.name}`);
426
+ }
427
+
428
+ /**
429
+ * Register an extension applied to every {@link GraphSession}. It runs against
430
+ * each graph already open at registration time and against every graph created
431
+ * afterwards (via {@link createSession}), so a plugin can attach per-graph
432
+ * state to new graph instances from a single editor-level registration.
433
+ *
434
+ * If the extension returns a cleanup it is wired to the session's
435
+ * {@link GraphSession.onDispose} and runs when that graph's tab is closed.
436
+ *
437
+ * @returns an unregister function that stops the extension from applying to
438
+ * sessions created later. It does not retroactively tear down sessions already
439
+ * extended (those clean up on their own dispose).
440
+ */
441
+ registerSessionExtension(extension: SessionExtension): () => void {
442
+ this.sessionExtensions.add(extension);
443
+ // Apply to graphs that already exist so registration order doesn't matter.
444
+ for (const session of Object.values(this.activeGraph.getState().sessions)) {
445
+ this.applySessionExtension(session, extension);
446
+ }
447
+ return () => {
448
+ this.sessionExtensions.delete(extension);
449
+ };
450
+ }
451
+
452
+ /** Run a single session extension, wiring any returned cleanup to dispose. */
453
+ private applySessionExtension(
454
+ session: GraphSession,
455
+ extension: SessionExtension
456
+ ): void {
457
+ try {
458
+ const cleanup = extension(session);
459
+ if (typeof cleanup === 'function') session.onDispose(cleanup);
460
+ } catch (err) {
461
+ console.error('Session extension failed', err);
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Register a custom automatic type conversion (e.g. from a profile plugin) so
467
+ * auto-convert can splice in the given node for that type pair.
468
+ */
469
+ registerConversion(rule: ConversionRule): void {
470
+ this.conversionStore.getState().registerConversion(rule);
471
+ }
472
+
473
+ /**
474
+ * Contribute a setting to the schema-driven Settings panel. The panel
475
+ * auto-generates a row for it (grouped under `descriptor.section`), and its
476
+ * default value is seeded into the settings store if not already present.
477
+ * Built-in settings are registered the same way at construction.
478
+ *
479
+ * @example
480
+ * system.registerSetting({
481
+ * key: 'graphRunner.autoStart', section: 'Graph Runner', type: 'boolean',
482
+ * default: false, title: 'Auto-start runner'
483
+ * });
484
+ */
485
+ registerSetting(descriptor: SettingDescriptor): void {
486
+ if (
487
+ descriptor.type !== 'custom' &&
488
+ !(descriptor.key in this.systemSettings.getState())
489
+ ) {
490
+ this.systemSettings
491
+ .getState()
492
+ .setSetting(descriptor.key, descriptor.default);
493
+ }
494
+ this.settingsSchema.getState().registerSetting(descriptor);
495
+ }
496
+
497
+ /** Contribute several settings at once. */
498
+ registerSettings(descriptors: SettingDescriptor[]): void {
499
+ for (const descriptor of descriptors) this.registerSetting(descriptor);
500
+ }
501
+
502
+ /** Read a setting value by key (built-in or plugin-contributed). */
503
+ getSetting<T = unknown>(key: string): T {
504
+ return this.systemSettings.getState()[key] as T;
505
+ }
506
+
507
+ /** Set a setting value by key. Persists if the descriptor allows it. */
508
+ setSetting(key: string, value: unknown): void {
509
+ this.systemSettings.getState().setSetting(key, value);
510
+ }
511
+
512
+ /**
513
+ * Dispatch a registered command against the focused graph (or a supplied
514
+ * session). Convenience used by hotkeys, the menubar and the toolbar so they
515
+ * share one dispatch path. No-ops if there is no graph to act on.
516
+ */
517
+ runCommand(id: string, ctx?: Partial<CommandContext>): void | Promise<void> {
518
+ const session = ctx?.session ?? this.session;
519
+ if (!session) return;
520
+ return this.commandStore
521
+ .getState()
522
+ .run(id, { editor: this, session, ...ctx });
523
+ }
524
+
525
+ /**
526
+ * Override where the editor's Save actions send their data. The editor
527
+ * publishes `graph:saved`, `graph:inner:saved` and `layout:saved`; by default
528
+ * each triggers a JSON file download. Pass an adapter to redirect any subset
529
+ * of those to your own sink (write to disk, POST to a backend, ...); topics
530
+ * you omit keep the file-download default. Replaces any previously installed
531
+ * handlers and returns a disposer.
532
+ *
533
+ * This governs graph/layout saving only; editor *settings* persistence is
534
+ * separate , see {@link enableSettingsPersistence}.
535
+ *
536
+ * @example
537
+ * // Persist graphs to a backend, keep the default layout download.
538
+ * system.enablePersistence({
539
+ * saveGraph: (graph) => api.put('/graphs/current', graph)
540
+ * });
541
+ */
542
+ enablePersistence(adapter?: Partial<PersistenceAdapter>): () => void {
543
+ this.persistenceDisposer();
544
+ this.persistenceDisposer = installPersistence(this, adapter);
545
+ return this.persistenceDisposer;
546
+ }
547
+
548
+ /**
549
+ * Remove the default (or custom) graph/layout save handlers entirely. Use this
550
+ * when the host handles saving through a channel of its own and the built-in
551
+ * file download would be redundant (e.g. the VS Code extension).
552
+ */
553
+ disablePersistence(): void {
554
+ this.persistenceDisposer();
555
+ this.persistenceDisposer = () => {};
556
+ }
557
+
558
+ /**
559
+ * Serialize the persistable editor settings , the UI toggles plus any custom
560
+ * type conversions , to a plain JSON object.
561
+ */
562
+ serializeSettings(): EditorSettingsJSON {
563
+ const state = this.systemSettings.getState() as Record<string, any>;
564
+ const settings: Record<string, any> = {};
565
+ for (const key of this.persistedSettingKeys()) settings[key] = state[key];
566
+ return {
567
+ settings,
568
+ conversions: this.conversionStore.getState().conversions
569
+ };
570
+ }
571
+
572
+ /**
573
+ * Keys round-tripped to persisted storage: the built-in persisted keys plus
574
+ * every plugin-contributed descriptor that opts in (`persist !== false`).
575
+ * Custom descriptors carry no backing value, so they are excluded.
576
+ */
577
+ private persistedSettingKeys(): string[] {
578
+ const keys = new Set<string>(PERSISTED_SETTING_KEYS as string[]);
579
+ for (const descriptor of this.settingsSchema.getState().settings) {
580
+ if (descriptor.type === 'custom' || descriptor.persist === false)
581
+ continue;
582
+ keys.add(descriptor.key);
583
+ }
584
+ return [...keys];
585
+ }
586
+
587
+ /**
588
+ * Apply previously-serialized editor settings (toggles + conversions).
589
+ * Unknown keys are ignored.
590
+ */
591
+ applySettings(json: EditorSettingsJSON | undefined): void {
592
+ if (!json) return;
593
+ const state = this.systemSettings.getState() as Record<string, any>;
594
+ for (const [key, value] of Object.entries(json.settings ?? {})) {
595
+ if (value === undefined) continue;
596
+ const setter = state[settingSetterName(key)];
597
+ if (typeof setter === 'function') setter(value);
598
+ // Plugin-contributed keys have no typed setter: write generically.
599
+ else if (typeof state.setSetting === 'function')
600
+ state.setSetting(key, value);
601
+ }
602
+ if (Array.isArray(json.conversions)) {
603
+ this.conversionStore.getState().setConversions(json.conversions);
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Persist editor settings + conversions to a storage adapter (localStorage by
609
+ * default). Applies any saved state immediately, then saves (debounced) on
610
+ * change. Returns a disposer. A host can pass its own storage adapter (e.g.
611
+ * one backed by VS Code workspace state) instead of localStorage.
612
+ */
613
+ enableSettingsPersistence(
614
+ storage: SettingsStorage | undefined = defaultSettingsStorage()
615
+ ): () => void {
616
+ if (!storage) return () => {};
617
+
618
+ try {
619
+ const raw = storage.getItem(SETTINGS_STORAGE_KEY);
620
+ if (raw) this.applySettings(JSON.parse(raw));
621
+ } catch {
622
+ // ignore malformed saved state
623
+ }
624
+
625
+ let timer: ReturnType<typeof setTimeout> | undefined;
626
+ const save = () => {
627
+ if (timer) clearTimeout(timer);
628
+ timer = setTimeout(() => {
629
+ try {
630
+ storage.setItem(
631
+ SETTINGS_STORAGE_KEY,
632
+ JSON.stringify(this.serializeSettings())
633
+ );
634
+ } catch {
635
+ // ignore quota / serialization errors
636
+ }
637
+ }, 300);
638
+ };
639
+
640
+ const unsubSettings = this.systemSettings.subscribe(save);
641
+ const unsubConversions = this.conversionStore.subscribe(save);
642
+ return () => {
643
+ if (timer) clearTimeout(timer);
644
+ unsubSettings();
645
+ unsubConversions();
646
+ };
647
+ }
648
+ }
649
+
650
+ /**
651
+ * Alias for {@link System} expressing its role as the shared editor-level system.
652
+ */
653
+ export { System as EditorSystem };