@kiberon-labs/behave-graph-flow 1.0.0 → 2.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 (314) hide show
  1. package/.fallowrc.json +16 -0
  2. package/.storybook/main.ts +32 -0
  3. package/.storybook/preview.ts +16 -0
  4. package/.storybook/styles.css +10 -0
  5. package/.storybook/vscode.css +814 -0
  6. package/.turbo/turbo-build.log +7 -0
  7. package/LICENSE +6 -0
  8. package/README.md +2 -2
  9. package/data/Polynomial.json +510 -0
  10. package/data/sequence.json +337 -0
  11. package/data/trigger-event.json +241 -0
  12. package/data/variable-change.json +210 -0
  13. package/dist/entry.css +4 -0
  14. package/dist/index.css +39 -0
  15. package/dist/index.css.map +1 -0
  16. package/dist/index.d.ts +2282 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +14873 -0
  19. package/dist/index.js.map +1 -0
  20. package/docs/notifications.md +246 -0
  21. package/docs/protocol.md +679 -0
  22. package/docs/specifics.md +191 -0
  23. package/package.json +85 -21
  24. package/postcss.config.ts +3 -4
  25. package/src/annotations/index.ts +32 -0
  26. package/src/components/FloatingToolbar/index.module.css +45 -0
  27. package/src/components/FloatingToolbar/index.tsx +256 -0
  28. package/src/components/Flow.tsx +276 -75
  29. package/src/components/contextMenus/NodePicker.module.css +274 -0
  30. package/src/components/contextMenus/NodePicker.tsx +481 -0
  31. package/src/components/contextMenus/edge.tsx +108 -0
  32. package/src/components/contextMenus/node.tsx +155 -0
  33. package/src/components/contextMenus/selection.tsx +77 -0
  34. package/src/components/controls/any/index.tsx +8 -0
  35. package/src/components/controls/boolean/index.tsx +13 -0
  36. package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
  37. package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
  38. package/src/components/controls/colorPicker/index.module.css +18 -0
  39. package/src/components/controls/colorPicker/index.tsx +61 -0
  40. package/src/components/controls/number/index.tsx +35 -0
  41. package/src/components/controls/string/index.tsx +16 -0
  42. package/src/components/edges/index.tsx +469 -0
  43. package/src/components/edges/offsetBezier.ts +134 -0
  44. package/src/components/hotKeys.tsx +20 -0
  45. package/src/components/layoutController/index.module.css +10 -0
  46. package/src/components/layoutController/index.tsx +117 -0
  47. package/src/components/layoutController/utils.ts +205 -0
  48. package/src/components/menubar/defaults.tsx +480 -0
  49. package/src/components/menubar/index.tsx +49 -0
  50. package/src/components/menubar/menuItem.module.css +16 -0
  51. package/src/components/menubar/menuItem.tsx +32 -0
  52. package/src/components/nodes/behave/Node.module.css +23 -0
  53. package/src/components/nodes/behave/Node.tsx +176 -0
  54. package/src/components/nodes/behave/NodeContainer.module.css +87 -0
  55. package/src/components/nodes/behave/NodeContainer.tsx +46 -0
  56. package/src/components/nodes/behave/index.tsx +14 -0
  57. package/src/components/nodes/comment/FormatToolbar.tsx +118 -0
  58. package/src/components/nodes/comment/comment.tsx +103 -0
  59. package/src/components/nodes/comment/styles.module.css +150 -0
  60. package/src/components/nodes/group/index.tsx +109 -0
  61. package/src/components/nodes/wrapper/index.tsx +73 -0
  62. package/src/components/nodes/wrapper/styles.module.css +113 -0
  63. package/src/components/notifications/NotificationProvider.tsx +81 -0
  64. package/src/components/notifications/index.ts +2 -0
  65. package/src/components/notifications/utils.ts +71 -0
  66. package/src/components/panels/alignment/index.module.css +20 -0
  67. package/src/components/panels/alignment/index.tsx +244 -0
  68. package/src/components/panels/base/index.tsx +5 -0
  69. package/src/components/panels/base/styles.module.css +12 -0
  70. package/src/components/panels/conversation/index.module.css +151 -0
  71. package/src/components/panels/conversation/index.tsx +162 -0
  72. package/src/components/panels/events/CustomEventsEditor.tsx +384 -0
  73. package/src/components/panels/events/EditEventPanel.tsx +315 -0
  74. package/src/components/panels/events/ManageEventsPanel.tsx +98 -0
  75. package/src/components/panels/events/index.tsx +23 -0
  76. package/src/components/panels/events/styles.module.css +236 -0
  77. package/src/components/panels/history/index.tsx +92 -0
  78. package/src/components/panels/history/styles.module.css +106 -0
  79. package/src/components/panels/keymaps/index.module.css +78 -0
  80. package/src/components/panels/keymaps/index.tsx +167 -0
  81. package/src/components/panels/layers/index.tsx +240 -0
  82. package/src/components/panels/layers/styles.module.css +110 -0
  83. package/src/components/panels/legend/index.module.css +6 -0
  84. package/src/components/panels/legend/index.tsx +76 -0
  85. package/src/components/panels/logs/index.module.css +212 -0
  86. package/src/components/panels/logs/index.tsx +288 -0
  87. package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
  88. package/src/components/panels/nodeInputs/InputsGroup.tsx +64 -0
  89. package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
  90. package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
  91. package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
  92. package/src/components/panels/nodeInputs/OutputsGroup.tsx +65 -0
  93. package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
  94. package/src/components/panels/nodeInputs/index.module.css +284 -0
  95. package/src/components/panels/nodeInputs/index.tsx +339 -0
  96. package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
  97. package/src/components/panels/nodeInputs/useNodeInputsData.ts +173 -0
  98. package/src/components/panels/nodePicker/index.tsx +115 -0
  99. package/src/components/panels/panel/index.module.css +66 -0
  100. package/src/components/panels/panel/index.tsx +88 -0
  101. package/src/components/panels/search/index.module.css +66 -0
  102. package/src/components/panels/search/index.tsx +215 -0
  103. package/src/components/panels/systemSettings/index.tsx +206 -0
  104. package/src/components/panels/systemSettings/styles.module.css +11 -0
  105. package/src/components/panels/traces/GridLines.tsx +38 -0
  106. package/src/components/panels/traces/TimeGrid.tsx +48 -0
  107. package/src/components/panels/traces/TraceLane.tsx +62 -0
  108. package/src/components/panels/traces/TraceTooltip.tsx +22 -0
  109. package/src/components/panels/traces/TracesHeader.tsx +56 -0
  110. package/src/components/panels/traces/index.module.css +166 -0
  111. package/src/components/panels/traces/index.tsx +294 -0
  112. package/src/components/panels/traces/types.ts +48 -0
  113. package/src/components/panels/traces/useDerivedSpans.ts +212 -0
  114. package/src/components/panels/traces/utils.ts +25 -0
  115. package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
  116. package/src/components/panels/variables/ManageVariablesScreen.tsx +144 -0
  117. package/src/components/panels/variables/index.tsx +125 -0
  118. package/src/components/panels/variables/styles.module.css +236 -0
  119. package/src/components/primitives/icon.module.css +45 -0
  120. package/src/components/primitives/icon.tsx +38 -0
  121. package/src/components/sockets/input/index.tsx +76 -0
  122. package/src/components/sockets/input/styles.module.css +27 -0
  123. package/src/components/sockets/output/index.tsx +61 -0
  124. package/src/components/sockets/output/styles.module.css +27 -0
  125. package/src/css/prosemirror.css +57 -0
  126. package/src/css/rc-dock.css +112 -0
  127. package/src/css/rc-menu.css +100 -0
  128. package/src/css/vars.css +14 -0
  129. package/src/css/vscode.css +13 -0
  130. package/src/entry.css +4 -0
  131. package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
  132. package/src/generators/SequenceGenerator.tsx +104 -0
  133. package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
  134. package/src/generators/SwitchOnStringGenerator.tsx +263 -0
  135. package/src/generators/registerDefaultGenerators.ts +34 -0
  136. package/src/hooks/useBehaveGraphFlow.ts +17 -16
  137. package/src/hooks/useDetachNodes.ts +39 -0
  138. package/src/hooks/useFlowHandlers.ts +115 -29
  139. package/src/hooks/useWasdPan.ts +188 -0
  140. package/src/index.css +146 -0
  141. package/src/index.ts +36 -18
  142. package/src/layout/dagre.tsx +119 -0
  143. package/src/layout/elk.ts +200 -0
  144. package/src/plugin/alignment/index.ts +81 -0
  145. package/src/plugin/docs/index.tsx +299 -0
  146. package/src/plugin/docs/panel/index.tsx +200 -0
  147. package/src/plugin/docs/panel/styles.module.css +174 -0
  148. package/src/plugin/graphrunner/actions.ts +253 -0
  149. package/src/plugin/graphrunner/buttons.tsx +87 -0
  150. package/src/plugin/graphrunner/client.ts +704 -0
  151. package/src/plugin/graphrunner/index.tsx +255 -0
  152. package/src/plugin/graphrunner/panel.tsx +386 -0
  153. package/src/plugin/graphrunner/runner.ts +358 -0
  154. package/src/plugin/graphrunner/session.ts +243 -0
  155. package/src/plugin/graphrunner/store.ts +206 -0
  156. package/src/plugin/graphrunner/styles.module.css +211 -0
  157. package/src/plugin/graphrunner/transport.ts +224 -0
  158. package/src/plugin/graphrunner/types.ts +672 -0
  159. package/src/plugin/graphrunner-local/execution-utils.ts +457 -0
  160. package/src/plugin/graphrunner-local/index.tsx +166 -0
  161. package/src/plugin/graphrunner-local/panel.tsx +231 -0
  162. package/src/plugin/graphrunner-local/store.ts +41 -0
  163. package/src/plugin/graphrunner-local/styles.module.css +101 -0
  164. package/src/plugin/graphrunner-local/transport.ts +1372 -0
  165. package/src/plugin/graphrunner-local/types.ts +10 -0
  166. package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +633 -0
  167. package/src/plugin/graphrunner-webworker/index.tsx +146 -0
  168. package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
  169. package/src/plugin/graphrunner-webworker/store.ts +89 -0
  170. package/src/plugin/graphrunner-webworker/types.ts +17 -0
  171. package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
  172. package/src/plugin/realtime/realtimeRunner.ts +570 -0
  173. package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
  174. package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
  175. package/src/specifics/VariableGetSpecific.tsx +110 -0
  176. package/src/specifics/VariableSetSpecific.tsx +110 -0
  177. package/src/specifics/registerDefaultSpecifics.ts +5 -0
  178. package/src/store/actions.tsx +698 -0
  179. package/src/store/chat.ts +73 -0
  180. package/src/store/controls.tsx +62 -0
  181. package/src/store/documentation.tsx +69 -0
  182. package/src/store/events.tsx +116 -0
  183. package/src/store/flow.tsx +245 -0
  184. package/src/store/graphRunnerClient.ts +110 -0
  185. package/src/store/hotKeys.tsx +323 -0
  186. package/src/store/layers.ts +259 -0
  187. package/src/store/legend.tsx +76 -0
  188. package/src/store/logs.ts +28 -0
  189. package/src/store/menubar.ts +41 -0
  190. package/src/store/refs.ts +84 -0
  191. package/src/store/registry.ts +43 -0
  192. package/src/store/selection.ts +22 -0
  193. package/src/store/settings.ts +99 -0
  194. package/src/store/socketGenerator.tsx +54 -0
  195. package/src/store/specific.tsx +75 -0
  196. package/src/store/specs.tsx +35 -0
  197. package/src/store/tabs.ts +278 -0
  198. package/src/store/toolbar.tsx +45 -0
  199. package/src/store/traces.ts +240 -0
  200. package/src/store/variables.ts +37 -0
  201. package/src/system/graph.ts +134 -0
  202. package/src/system/index.ts +3 -0
  203. package/src/system/notifications.ts +98 -0
  204. package/src/system/plugin.ts +27 -0
  205. package/src/system/provider.tsx +22 -0
  206. package/src/system/pubsub.ts +323 -0
  207. package/src/system/system.ts +223 -0
  208. package/src/system/tabLoader.tsx +265 -0
  209. package/src/system/undoRedo.ts +103 -0
  210. package/src/transformers/Uigraph.ts +60 -0
  211. package/src/transformers/behaveToFlow.ts +16 -4
  212. package/src/transformers/flowToBehave.ts +32 -12
  213. package/src/types/NodeMetadata.ts +27 -0
  214. package/src/types/graph.ts +49 -0
  215. package/src/types/nodes.ts +45 -0
  216. package/src/types.ts +16 -0
  217. package/src/util/colors.ts +1 -29
  218. package/src/util/downloadJson.ts +18 -0
  219. package/src/util/extractNodeMetadata.ts +16 -0
  220. package/src/util/getPickerFilters.ts +1 -1
  221. package/src/util/isBehaveNode.ts +6 -0
  222. package/src/util/isValidConnection.ts +28 -15
  223. package/src/util/mergeSockets.ts +29 -0
  224. package/src/util/serializeVariables.ts +66 -0
  225. package/src/util/sockets.ts +43 -0
  226. package/stories/apex/layoutController/example-graph.worker.ts +39 -0
  227. package/stories/apex/layoutController/index.stories.tsx +48 -0
  228. package/stories/apex/layoutController/webworker.stories.tsx +103 -0
  229. package/stories/apex/menubar/menubar.stories.tsx +19 -0
  230. package/stories/components/colorpicker/index.stories.tsx +20 -0
  231. package/stories/components/contextMenus/edge.stories.tsx +32 -0
  232. package/stories/components/contextMenus/node.stories.tsx +26 -0
  233. package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
  234. package/stories/components/controls/any/index.stories.tsx +19 -0
  235. package/stories/components/controls/boolean/index.stories.tsx +19 -0
  236. package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
  237. package/stories/components/controls/number/index.stories.tsx +19 -0
  238. package/stories/components/controls/string/index.stories.tsx +19 -0
  239. package/stories/components/nodes/behaveNode.stories.tsx +108 -0
  240. package/stories/components/nodes/comment.stories.tsx +106 -0
  241. package/stories/components/panels/alignment.stories.tsx +24 -0
  242. package/stories/components/panels/events.stories.tsx +38 -0
  243. package/stories/components/panels/graphRunner.stories.tsx +317 -0
  244. package/stories/components/panels/history.stories.tsx +37 -0
  245. package/stories/components/panels/keymaps.stories.tsx +21 -0
  246. package/stories/components/panels/legend.stories.tsx +37 -0
  247. package/stories/components/panels/logs.stories.tsx +24 -0
  248. package/stories/components/panels/nodeInputs.stories.tsx +21 -0
  249. package/stories/components/panels/nodePicker.stories.tsx +37 -0
  250. package/stories/components/panels/panel.stories.tsx +39 -0
  251. package/stories/components/panels/search.stories.tsx +24 -0
  252. package/stories/components/panels/systemSettings.stories.tsx +26 -0
  253. package/stories/components/panels/traces.stories.tsx +225 -0
  254. package/stories/components/panels/variables.stories.tsx +24 -0
  255. package/stories/defaults/defaultStoryProvider.tsx +167 -0
  256. package/stories/defaults/systemGenerator.ts +38 -0
  257. package/tests/components/edges/offsetBezier.test.ts +51 -0
  258. package/tests/components/layoutController/utils.test.ts +68 -0
  259. package/tests/components/panels/traces/utils.test.ts +52 -0
  260. package/tests/flowToBehave.test.ts +26 -4
  261. package/tests/notifications.test.ts +87 -0
  262. package/tests/saveLoad.test.ts +372 -0
  263. package/tests/util/calculateNewEdge.test.ts +98 -0
  264. package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
  265. package/tests/util/hasPositionMetaData.test.ts +33 -0
  266. package/tests/util/isBehaveNode.test.ts +22 -0
  267. package/tests/util/isHandleConnected.test.ts +37 -0
  268. package/tests/util/mergeSockets.test.ts +43 -0
  269. package/tests/visual/README.md +64 -0
  270. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
  271. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
  272. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
  273. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
  274. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
  275. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
  276. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
  277. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
  278. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
  279. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
  280. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
  281. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
  282. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
  283. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
  284. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
  285. package/tests/visual/panels.visual.test.tsx +76 -0
  286. package/tsconfig.base.json +39 -0
  287. package/tsconfig.json +18 -59
  288. package/tsconfig.prod.json +23 -0
  289. package/tsdown.config.ts +15 -3
  290. package/typedoc.json +7 -7
  291. package/vite.config.js +7 -0
  292. package/vitest.config.ts +5 -2
  293. package/vitest.visual.config.ts +48 -0
  294. package/src/components/AutoSizeInput.tsx +0 -65
  295. package/src/components/Controls.tsx +0 -87
  296. package/src/components/InputSocket.tsx +0 -142
  297. package/src/components/Node.tsx +0 -68
  298. package/src/components/NodeContainer.tsx +0 -46
  299. package/src/components/NodePicker.tsx +0 -77
  300. package/src/components/OutputSocket.tsx +0 -58
  301. package/src/components/modals/ClearModal.tsx +0 -40
  302. package/src/components/modals/HelpModal.tsx +0 -36
  303. package/src/components/modals/LoadModal.tsx +0 -96
  304. package/src/components/modals/Modal.tsx +0 -64
  305. package/src/components/modals/SaveModal.tsx +0 -60
  306. package/src/hooks/useCustomNodeTypes.tsx +0 -31
  307. package/src/hooks/useGraphRunner.ts +0 -104
  308. package/src/hooks/useMergeMap.ts +0 -14
  309. package/src/hooks/useNodeSpecJson.ts +0 -20
  310. package/src/hooks/useQueriableDefinitions.ts +0 -22
  311. package/src/styles.css +0 -8
  312. package/tailwind.config.ts +0 -19
  313. package/tests/tsconfig.json +0 -10
  314. /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mergeSockets } from '@/util/mergeSockets';
3
+
4
+ const specSocket = (name: string, valueType: string) =>
5
+ ({ name, valueType }) as any;
6
+
7
+ describe('mergeSockets', () => {
8
+ it('maps spec sockets to socket-base objects keyed by name', () => {
9
+ const result = mergeSockets([
10
+ specSocket('a', 'flow'),
11
+ specSocket('b', 'string')
12
+ ]);
13
+
14
+ expect(result).toHaveLength(2);
15
+ expect(result.map((s) => s.key)).toEqual(['a', 'b']);
16
+ expect(result[0]).toMatchObject({ key: 'a', name: 'a', valueType: 'flow' });
17
+ });
18
+
19
+ it('returns spec sockets unchanged when no dynamic ports are given', () => {
20
+ const result = mergeSockets([specSocket('a', 'flow')], undefined);
21
+ expect(result).toEqual([{ key: 'a', name: 'a', valueType: 'flow' }]);
22
+ });
23
+
24
+ it('lets a dynamic port override a spec socket with the same key', () => {
25
+ const result = mergeSockets(
26
+ [specSocket('a', 'flow'), specSocket('b', 'string')],
27
+ [{ key: 'b', name: 'b', valueType: 'integer' } as any]
28
+ );
29
+
30
+ expect(result).toHaveLength(2);
31
+ const b = result.find((s) => s.key === 'b');
32
+ expect(b?.valueType).toBe('integer');
33
+ });
34
+
35
+ it('appends dynamic ports that do not match any spec socket', () => {
36
+ const result = mergeSockets(
37
+ [specSocket('a', 'flow')],
38
+ [{ key: 'extra', name: 'extra', valueType: 'boolean' } as any]
39
+ );
40
+
41
+ expect(result.map((s) => s.key)).toEqual(['a', 'extra']);
42
+ });
43
+ });
@@ -0,0 +1,64 @@
1
+ # Visual (pixel) regression tests
2
+
3
+ These tests render React components in a **real browser** (Chromium via Playwright)
4
+ and compare a screenshot against a committed baseline using Vitest's built-in
5
+ [`toMatchScreenshot`](https://vitest.dev/guide/browser/visual-regression-testing)
6
+ assertion. They catch unintended rendering regressions that DOM-only unit tests
7
+ miss.
8
+
9
+ ## Scope
10
+
11
+ Coverage is deliberately minimal and panel-focused: **one snapshot per editor
12
+ panel** (see [`panels.visual.test.tsx`](./panels.visual.test.tsx)), since the
13
+ panels are the composed, regression-prone surfaces. Each panel is rendered
14
+ through the same [`DefaultSystemProvider`](../../stories/defaults/defaultStoryProvider.tsx)
15
+ the Storybook stories use, so it shows representative content.
16
+
17
+ They are intentionally **separate** from the default `pnpm test` run (which uses
18
+ happy-dom) so the heavier browser runner only starts when explicitly requested.
19
+
20
+ ## Running
21
+
22
+ ```bash
23
+ # one-time: install the Chromium binary Playwright drives
24
+ npx playwright install chromium
25
+
26
+ # verify components against the committed baselines
27
+ pnpm test:visual
28
+
29
+ # create/refresh baselines (after an intentional visual change)
30
+ pnpm test:visual:update
31
+ ```
32
+
33
+ ## How it works
34
+
35
+ - Config: [`vitest.visual.config.ts`](../../vitest.visual.config.ts) — browser
36
+ mode, Playwright/Chromium, headless, fixed `900x700` viewport.
37
+ - Test files: `tests/visual/**/*.visual.test.tsx`.
38
+ - Each panel is rendered inside a fixed-size `640x480` frame on the editor
39
+ background so screenshots are bounded and stable. The full app stylesheet
40
+ (`@/index.css`) is imported so `--vscode-*` theme tokens resolve.
41
+ - Baselines live in `__screenshots__/` and are committed. Vitest names them per
42
+ platform (e.g. `*-chromium-win32.png`), because font rendering and
43
+ anti-aliasing differ across operating systems.
44
+
45
+ ## Important: baselines are platform-specific
46
+
47
+ Generate and verify baselines on the **same OS / CI image**. A baseline captured
48
+ on Windows will not match one rendered on Linux. For CI, run
49
+ `pnpm test:visual:update` on the CI platform once (or in a container that matches
50
+ CI) and commit those baselines. The comparator is configured with a small
51
+ `allowedMismatchedPixelRatio` to tolerate negligible anti-aliasing noise.
52
+
53
+ ## Adding a panel
54
+
55
+ When a new panel is added under `src/components/panels`, add one entry to the
56
+ `panels` array in [`panels.visual.test.tsx`](./panels.visual.test.tsx):
57
+
58
+ ```tsx
59
+ ['myPanel', <MyPanel />],
60
+ ```
61
+
62
+ Then run `pnpm test:visual:update` to create the baseline, eyeball the generated
63
+ PNG, and commit it. Keep it to a single representative snapshot per panel — the
64
+ unit tests under `tests/util` and `tests/components` cover finer-grained logic.
@@ -0,0 +1,76 @@
1
+ // Full editor stylesheet (vscode theme vars, rc-dock, reactflow, etc.) so the
2
+ // panels render the way they do in the real app.
3
+ import '@/index.css';
4
+
5
+ import type { ReactElement } from 'react';
6
+ import { afterEach, describe, expect, it } from 'vitest';
7
+ import { page } from 'vitest/browser';
8
+ import { cleanup, render } from 'vitest-browser-react';
9
+ import { DefaultSystemProvider } from '../../stories/defaults/defaultStoryProvider';
10
+
11
+ import { AlignmentPanel } from '@/components/panels/alignment';
12
+ import { ConversationPanel } from '@/components/panels/conversation';
13
+ import { EventsPanel } from '@/components/panels/events';
14
+ import { HistoryPanel } from '@/components/panels/history';
15
+ import { KeymapsPanel } from '@/components/panels/keymaps';
16
+ import { LayersPanel } from '@/components/panels/layers';
17
+ import { LegendPanel } from '@/components/panels/legend';
18
+ import { LogsPanel } from '@/components/panels/logs';
19
+ import { NodeInputsPanel } from '@/components/panels/nodeInputs';
20
+ import { NodePickerPanel } from '@/components/panels/nodePicker';
21
+ import { PanelPanel } from '@/components/panels/panel';
22
+ import { SearchPanel } from '@/components/panels/search';
23
+ import { Settings } from '@/components/panels/systemSettings';
24
+ import { TracesPanel } from '@/components/panels/traces';
25
+ import { VariablesPanel } from '@/components/panels/variables';
26
+
27
+ /**
28
+ * One pixel-snapshot per panel. The shared {@link DefaultSystemProvider} (the
29
+ * same provider the Storybook stories use) supplies a populated System so each
30
+ * panel renders representative content.
31
+ */
32
+ const panels: ReadonlyArray<readonly [name: string, element: ReactElement]> = [
33
+ ['alignment', <AlignmentPanel />],
34
+ ['conversation', <ConversationPanel />],
35
+ ['events', <EventsPanel />],
36
+ ['history', <HistoryPanel />],
37
+ ['keymaps', <KeymapsPanel />],
38
+ ['layers', <LayersPanel />],
39
+ ['legend', <LegendPanel />],
40
+ ['logs', <LogsPanel />],
41
+ ['nodeInputs', <NodeInputsPanel />],
42
+ ['nodePicker', <NodePickerPanel />],
43
+ ['panel', <PanelPanel />],
44
+ ['search', <SearchPanel />],
45
+ ['systemSettings', <Settings />],
46
+ ['traces', <TracesPanel />],
47
+ ['variables', <VariablesPanel />]
48
+ ];
49
+
50
+ afterEach(() => {
51
+ cleanup();
52
+ });
53
+
54
+ describe('panels (visual)', () => {
55
+ it.each(panels)('renders the %s panel', async (name, element) => {
56
+ render(
57
+ <DefaultSystemProvider>
58
+ <div
59
+ data-testid="panel-frame"
60
+ style={{
61
+ width: 640,
62
+ height: 480,
63
+ overflow: 'auto',
64
+ background: 'var(--vscode-editor-background, #1f1f1f)'
65
+ }}
66
+ >
67
+ {element}
68
+ </div>
69
+ </DefaultSystemProvider>
70
+ );
71
+
72
+ await expect(page.getByTestId('panel-frame')).toMatchScreenshot(
73
+ `panel-${name}`
74
+ );
75
+ });
76
+ });
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Monorepo shared library – base (no references)",
4
+ "compilerOptions": {
5
+ /* Base Options: */
6
+ "esModuleInterop": true,
7
+ "skipLibCheck": true,
8
+ "target": "esnext",
9
+ "allowJs": true,
10
+ "resolveJsonModule": true,
11
+ "moduleDetection": "force",
12
+ "isolatedModules": true,
13
+ "verbatimModuleSyntax": true,
14
+ /* Strictness */
15
+ "strict": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noImplicitOverride": true,
18
+ "erasableSyntaxOnly": true,
19
+ /* Opinion */
20
+ "module": "preserve",
21
+ "outDir": "./dist",
22
+ "paths": {
23
+ "@/*": [
24
+ "./src/*"
25
+ ],
26
+ "~/*": [
27
+ "./stories/*"
28
+ ]
29
+ },
30
+ "declaration": true,
31
+ "declarationMap": true,
32
+ "jsx": "react-jsx",
33
+ "lib": [
34
+ "dom",
35
+ "dom.iterable",
36
+ "esnext"
37
+ ],
38
+ },
39
+ }
package/tsconfig.json CHANGED
@@ -1,60 +1,19 @@
1
- {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "display": "Monorepo shared library",
4
- "compilerOptions": {
5
- /* Base Options: */
6
- "esModuleInterop": true,
7
- "skipLibCheck": true,
8
- "target": "esnext",
9
- "allowJs": true,
10
- "resolveJsonModule": true,
11
- "moduleDetection": "force",
12
- "isolatedModules": true,
13
- "verbatimModuleSyntax": true,
14
- /* Strictness */
15
- "strict": true,
16
- "noUncheckedIndexedAccess": true,
17
- "noImplicitOverride": true,
18
- "erasableSyntaxOnly": true,
19
- /* Opinion */
20
- "incremental": true,
21
- "tsBuildInfoFile": "./tsconfig.tsbuildinfo",
22
- "module": "preserve",
23
- "outDir": "./dist",
24
- "baseUrl": ".",
25
- "rootDir": "./src",
26
- "paths": {
27
- "~/*": [
28
- "./src/*"
29
- ],
30
- "@/*": [
31
- "./src/*"
32
- ]
33
- },
34
- /* Required for project references, which provide go-to-definition in your
35
- IDE without first having to build the module, which is essential during development. */
36
- "composite": true,
37
- /* Assuming your bundler will output everything, but we can not have noEmit
38
- enabled because it is not compatible with composite / project references. Also declaration might be required for references to work fully. Not sure yet... */
39
- "declaration": true,
40
- "declarationMap": true,
41
- "jsx": "react-jsx",
42
- "lib": [
43
- "dom",
44
- "dom.iterable",
45
- "esnext"
46
- ],
47
- },
48
- "include": [
49
- "./src",
50
- "./src/**/*.json",
51
- ],
52
- "exclude": [
53
- "!./src/**"
54
- ],
55
- "references": [
56
- {
57
- "path": "../core"
58
- }
59
- ],
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Monorepo shared library",
4
+ "extends": "./tsconfig.base.json",
5
+ "compilerOptions": {
6
+ "rootDir": ".",
7
+ /* Required for project references, which provide go-to-definition in your
8
+ IDE without first having to build the module, which is essential during development. */
9
+ "composite": true,
10
+ "incremental": true,
11
+ "tsBuildInfoFile": "./tsconfig.tsbuildinfo",
12
+ },
13
+ "include": [
14
+ "./**/*.ts",
15
+ "./**/*.tsx",
16
+ "./**/*.json",
17
+ ],
18
+ "exclude": []
60
19
  }
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "./tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "composite": false,
6
+ "paths": {
7
+ "@/*": [
8
+ "./src/*"
9
+ ],
10
+ "~/*": [
11
+ "./stories/*"
12
+ ],
13
+ "@kiberon-labs/behave-graph": [
14
+ "../core/dist/index.d.ts"
15
+ ]
16
+ },
17
+ },
18
+ "include": [
19
+ "./src",
20
+ "./src/**/*.json",
21
+ ],
22
+ "exclude": [],
23
+ }
package/tsdown.config.ts CHANGED
@@ -1,15 +1,27 @@
1
1
  import { defineConfig } from 'tsdown';
2
+ import LightningCSS from 'unplugin-lightningcss/rolldown';
2
3
 
3
4
  export default defineConfig({
4
- entry: ['./src/index.ts'],
5
+ entry: {
6
+ index: './src/index.ts'
7
+ },
8
+
5
9
  outDir: 'dist',
6
10
  target: 'es2022',
7
11
  sourcemap: true,
12
+ tsconfig: 'tsconfig.prod.json',
8
13
  skipNodeModulesBundle: true,
9
- external: ['@kiberon-labs/behave-graph'],
14
+ copy: [{ from: 'src/entry.css', to: 'dist/entry.css' }],
15
+ plugins: [LightningCSS({ options: { minify: true } })],
16
+ external: [
17
+ '@kiberon-labs/behave-graph',
18
+ '@vscode-elements/react-elements',
19
+ 'rc-dock',
20
+ 'rc-menu'
21
+ ],
10
22
  format: ['esm'],
11
23
  dts: true,
24
+ // unbundle: true,
12
25
  logLevel: 'warn',
13
- unbundle: true,
14
26
  platform: 'neutral'
15
27
  });
package/typedoc.json CHANGED
@@ -1,8 +1,8 @@
1
- {
2
- "extends": [
3
- "../../typedoc.base.jsonc"
4
- ],
5
- "entryPoints": [
6
- "src/index.ts"
7
- ]
1
+ {
2
+ "extends": [
3
+ "../../typedoc.base.jsonc"
4
+ ],
5
+ "entryPoints": [
6
+ "src/index.ts"
7
+ ]
8
8
  }
package/vite.config.js ADDED
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vite';
2
+ import tsconfigPaths from 'vite-tsconfig-paths';
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [tsconfigPaths()]
7
+ });
package/vitest.config.ts CHANGED
@@ -3,8 +3,11 @@ import { configDefaults, defineConfig } from 'vitest/config';
3
3
 
4
4
  export default defineConfig({
5
5
  test: {
6
- exclude: [...configDefaults.exclude],
7
- watch: false
6
+ // Visual/pixel regression tests run in a real browser via the dedicated
7
+ // vitest.visual.config.ts — keep them out of the happy-dom unit run.
8
+ exclude: [...configDefaults.exclude, '**/*.visual.test.{ts,tsx}'],
9
+ watch: false,
10
+ environment: 'happy-dom'
8
11
  },
9
12
  resolve: {
10
13
  alias: {
@@ -0,0 +1,48 @@
1
+ import path from 'node:path';
2
+ import { playwright } from '@vitest/browser-playwright';
3
+ import { defineConfig } from 'vitest/config';
4
+
5
+ /**
6
+ * Dedicated config for visual (pixel) regression tests.
7
+ *
8
+ * These run in a real browser via Vitest browser mode + Playwright and use the
9
+ * built-in `toMatchScreenshot` assertion to detect rendering regressions.
10
+ *
11
+ * Run with `pnpm test:visual` (and `pnpm test:visual:update` to refresh
12
+ * baselines). It is intentionally separate from the default happy-dom unit-test
13
+ * config so the heavy browser runner is only spun up when explicitly requested.
14
+ *
15
+ * Screenshots are platform-sensitive (fonts/anti-aliasing differ across OSes),
16
+ * so Vitest stores baselines under per-platform folders. Generate/refresh
17
+ * baselines on the same platform/CI image that will verify them.
18
+ */
19
+ export default defineConfig({
20
+ test: {
21
+ include: ['tests/visual/**/*.visual.test.{ts,tsx}'],
22
+ watch: false,
23
+ browser: {
24
+ enabled: true,
25
+ provider: playwright(),
26
+ headless: true,
27
+ instances: [{ browser: 'chromium' }],
28
+ // Deterministic viewport, large enough to contain a panel frame without
29
+ // introducing page scrollbars that would shift layout.
30
+ viewport: { width: 900, height: 700 },
31
+ expect: {
32
+ toMatchScreenshot: {
33
+ // Allow a tiny amount of anti-aliasing noise without failing.
34
+ comparatorName: 'pixelmatch',
35
+ comparatorOptions: {
36
+ allowedMismatchedPixelRatio: 0.02
37
+ }
38
+ }
39
+ }
40
+ }
41
+ },
42
+ resolve: {
43
+ alias: {
44
+ '~': path.resolve(__dirname, './src'),
45
+ '@': path.resolve(__dirname, './src')
46
+ }
47
+ }
48
+ });
@@ -1,65 +0,0 @@
1
- import React from 'react';
2
- import {
3
- type CSSProperties,
4
- type HTMLProps,
5
- useCallback,
6
- useEffect,
7
- useRef,
8
- useState
9
- } from 'react';
10
-
11
- export type AutoSizeInputProps = HTMLProps<HTMLInputElement> & {
12
- minWidth?: number;
13
- };
14
-
15
- const baseStyles: CSSProperties = {
16
- position: 'absolute',
17
- top: 0,
18
- left: 0,
19
- visibility: 'hidden',
20
- height: 0,
21
- width: 'auto',
22
- whiteSpace: 'pre'
23
- };
24
-
25
- export const AutoSizeInput: React.FC<AutoSizeInputProps> = ({
26
- minWidth = 30,
27
- ...props
28
- }) => {
29
- const inputRef = useRef<HTMLInputElement | null>(null);
30
- const measureRef = useRef<HTMLSpanElement | null>(null);
31
- const [styles, setStyles] = useState<CSSProperties>({});
32
-
33
- // grab the font size of the input on ref mount
34
- const setRef = useCallback((input: HTMLInputElement | null) => {
35
- if (input) {
36
- const styles = window.getComputedStyle(input);
37
- setStyles({
38
- fontSize: styles.getPropertyValue('font-size'),
39
- paddingLeft: styles.getPropertyValue('padding-left'),
40
- paddingRight: styles.getPropertyValue('padding-right')
41
- });
42
- }
43
- inputRef.current = input;
44
- }, []);
45
-
46
- // measure the text on change and update input
47
- useEffect(() => {
48
- if (measureRef.current === null) return;
49
- if (inputRef.current === null) return;
50
-
51
- const padding = props.type === 'number' || props.type === 'float' ? 20 : 0;
52
-
53
- const width = measureRef.current.clientWidth + padding;
54
- inputRef.current.style.width = Math.max(minWidth, width) + 'px';
55
- }, [props.value, minWidth, styles, props.type]);
56
-
57
- return (
58
- <>
59
- <input ref={setRef} {...props} />
60
- <span ref={measureRef} style={{ ...baseStyles, ...styles }}>
61
- {props.value}
62
- </span>
63
- </>
64
- );
65
- };
@@ -1,87 +0,0 @@
1
- import type { GraphJSON, NodeSpecJSON } from '@kiberon-labs/behave-graph';
2
- import {
3
- faDownload,
4
- faPause,
5
- faPlay,
6
- faQuestion,
7
- faTrash,
8
- faUpload
9
- } from '@fortawesome/free-solid-svg-icons';
10
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
11
- import { useState } from 'react';
12
- import React from 'react';
13
- import { ControlButton, Controls } from 'reactflow';
14
-
15
- import { ClearModal } from './modals/ClearModal.js';
16
- import { HelpModal } from './modals/HelpModal.js';
17
- import { type Examples, LoadModal } from './modals/LoadModal.js';
18
- import { SaveModal } from './modals/SaveModal.js';
19
-
20
- export type CustomControlsProps = {
21
- playing: boolean;
22
- togglePlay: () => void;
23
- setBehaviorGraph: (value: GraphJSON) => void;
24
- examples: Examples;
25
- specJson: NodeSpecJSON[] | undefined;
26
- };
27
-
28
- export const CustomControls: React.FC<CustomControlsProps> = ({
29
- playing,
30
- togglePlay,
31
- setBehaviorGraph,
32
- examples,
33
- specJson
34
- }: {
35
- playing: boolean;
36
- togglePlay: () => void;
37
- setBehaviorGraph: (value: GraphJSON) => void;
38
- examples: Examples;
39
- specJson: NodeSpecJSON[] | undefined;
40
- }) => {
41
- const [loadModalOpen, setLoadModalOpen] = useState(false);
42
- const [saveModalOpen, setSaveModalOpen] = useState(false);
43
- const [helpModalOpen, setHelpModalOpen] = useState(false);
44
- const [clearModalOpen, setClearModalOpen] = useState(false);
45
-
46
- return (
47
- <>
48
- <Controls>
49
- <ControlButton title="Help" onClick={() => setHelpModalOpen(true)}>
50
- <FontAwesomeIcon icon={faQuestion} />
51
- </ControlButton>
52
- <ControlButton title="Load" onClick={() => setLoadModalOpen(true)}>
53
- <FontAwesomeIcon icon={faUpload} />
54
- </ControlButton>
55
- <ControlButton title="Save" onClick={() => setSaveModalOpen(true)}>
56
- <FontAwesomeIcon icon={faDownload} />
57
- </ControlButton>
58
- <ControlButton title="Clear" onClick={() => setClearModalOpen(true)}>
59
- <FontAwesomeIcon icon={faTrash} />
60
- </ControlButton>
61
- <ControlButton title="Run" onClick={togglePlay}>
62
- <FontAwesomeIcon icon={playing ? faPause : faPlay} />
63
- </ControlButton>
64
- </Controls>
65
- <LoadModal
66
- open={loadModalOpen}
67
- onClose={() => setLoadModalOpen(false)}
68
- setBehaviorGraph={setBehaviorGraph}
69
- examples={examples}
70
- />
71
- {specJson && (
72
- <SaveModal
73
- open={saveModalOpen}
74
- specJson={specJson}
75
- onClose={() => setSaveModalOpen(false)}
76
- />
77
- )}
78
- <HelpModal open={helpModalOpen} onClose={() => setHelpModalOpen(false)} />
79
- <ClearModal
80
- open={clearModalOpen}
81
- onClose={() => setClearModalOpen(false)}
82
- />
83
- </>
84
- );
85
- };
86
-
87
- export default CustomControls;