@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,212 @@
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 { calculateTimeInterval, clamp, hashToHue } from './utils';
11
+
12
+ const MIN_WIDTH_PCT = 0.5;
13
+ const OVERLAP_EPSILON_PCT = 0.5;
14
+
15
+ const EMPTY: DerivedData = {
16
+ now: 0,
17
+ size: 0,
18
+ lanes: 0,
19
+ minStart: 0,
20
+ maxEnd: 0,
21
+ range: 1,
22
+ viewStart: 0,
23
+ viewEnd: 0,
24
+ viewRange: 1,
25
+ buckets: [],
26
+ laneData: [],
27
+ ticks: []
28
+ };
29
+
30
+ function computeTicks(
31
+ viewStart: number,
32
+ viewEnd: number,
33
+ viewRange: number
34
+ ): Tick[] {
35
+ const interval = calculateTimeInterval(viewRange);
36
+ const firstTick = Math.ceil(viewStart / interval) * interval;
37
+ const ticks: Tick[] = [];
38
+ for (let t = firstTick; t <= viewEnd; t += interval) {
39
+ const leftPct = ((t - viewStart) / viewRange) * 100;
40
+ if (leftPct >= 0 && leftPct <= 100) {
41
+ ticks.push({ time: t, leftPct });
42
+ }
43
+ }
44
+ return ticks;
45
+ }
46
+
47
+ export function useDerivedSpans(
48
+ collector: SpanCollector,
49
+ version: number,
50
+ windowMs: number,
51
+ view: ViewState
52
+ ): DerivedData {
53
+ return useMemo(() => {
54
+ const now = performance.now();
55
+ const { capacity, size, writeIndex, spans } = collector;
56
+
57
+ if (size <= 0) return { ...EMPTY, now };
58
+
59
+ let minStart = Number.POSITIVE_INFINITY;
60
+ let maxEnd = Number.NEGATIVE_INFINITY;
61
+ let maxLane = -1;
62
+
63
+ const buckets: Array<import('@/store/traces').TraceSpan[]> = [];
64
+
65
+ const startIndex = (writeIndex - size + capacity) % capacity;
66
+ for (let i = 0; i < size; i++) {
67
+ const idx = (startIndex + i) % capacity;
68
+ const s = spans[idx];
69
+ if (!s) continue;
70
+
71
+ const end = Number.isNaN(s.end) ? now : s.end;
72
+ minStart = Math.min(minStart, s.start);
73
+ maxEnd = Math.max(maxEnd, end);
74
+ maxLane = Math.max(maxLane, s.lane);
75
+
76
+ (buckets[s.lane] ??= []).push(s);
77
+ }
78
+
79
+ if (
80
+ !Number.isFinite(minStart) ||
81
+ !Number.isFinite(maxEnd) ||
82
+ maxEnd <= minStart
83
+ ) {
84
+ return {
85
+ ...EMPTY,
86
+ now,
87
+ size,
88
+ lanes: maxLane + 1,
89
+ buckets,
90
+ laneData: []
91
+ };
92
+ }
93
+
94
+ const fullRange = Math.max(1, maxEnd - minStart);
95
+
96
+ const desiredRange = windowMs <= 0 ? fullRange : Math.max(1, windowMs);
97
+ const desiredStart = Math.max(minStart, maxEnd - desiredRange);
98
+
99
+ const effectiveRange = clamp(
100
+ view.follow ? desiredRange : view.range,
101
+ 1,
102
+ fullRange
103
+ );
104
+ const effectiveStart = clamp(
105
+ view.follow ? desiredStart : view.start,
106
+ minStart,
107
+ maxEnd - effectiveRange
108
+ );
109
+
110
+ const viewStart = effectiveStart;
111
+ const viewEnd = viewStart + effectiveRange;
112
+ const viewRange = effectiveRange;
113
+
114
+ const laneData: LaneData[] = Array.from({ length: maxLane + 1 }, () => ({
115
+ stackCount: 1,
116
+ visualSpans: [] as VisualSpan[]
117
+ }));
118
+
119
+ for (let lane = 0; lane <= maxLane; lane++) {
120
+ const laneSpans = buckets[lane];
121
+ if (!laneSpans || laneSpans.length === 0) {
122
+ laneData[lane] = { stackCount: 1, visualSpans: [] };
123
+ continue;
124
+ }
125
+
126
+ const tmp: Array<{
127
+ span: import('@/store/traces').TraceSpan;
128
+ leftPct: number;
129
+ widthPct: number;
130
+ rightPct: number;
131
+ durationMs: number;
132
+ }> = [];
133
+
134
+ for (const s of laneSpans) {
135
+ const end = Number.isNaN(s.end) ? now : s.end;
136
+ if (end < viewStart || s.start > viewEnd) continue;
137
+
138
+ const visibleStart = Math.max(s.start, viewStart);
139
+ const visibleEnd = Math.min(end, viewEnd);
140
+ if (visibleEnd <= visibleStart) continue;
141
+
142
+ const rawLeft = ((visibleStart - viewStart) / viewRange) * 100;
143
+ const rawWidth = ((visibleEnd - visibleStart) / viewRange) * 100;
144
+ const leftPct = clamp(rawLeft, 0, 100);
145
+ const widthPct = clamp(
146
+ Math.max(MIN_WIDTH_PCT, rawWidth),
147
+ 0,
148
+ 100 - leftPct
149
+ );
150
+ const rightPct = leftPct + widthPct;
151
+ const durationMs = Math.max(0, end - s.start);
152
+
153
+ tmp.push({ span: s, leftPct, widthPct, rightPct, durationMs });
154
+ }
155
+
156
+ tmp.sort((a, b) => a.leftPct - b.leftPct);
157
+
158
+ const stackRight: number[] = [];
159
+ const visualSpans: VisualSpan[] = [];
160
+
161
+ for (const v of tmp) {
162
+ let stack = 0;
163
+ for (; stack < stackRight.length; stack++) {
164
+ if (v.leftPct >= stackRight[stack]! + OVERLAP_EPSILON_PCT) break;
165
+ }
166
+ if (stack === stackRight.length) stackRight.push(v.rightPct);
167
+ else stackRight[stack] = Math.max(stackRight[stack]!, v.rightPct);
168
+
169
+ const hue = hashToHue(v.span.nodeId);
170
+ const isOpen = Number.isNaN(v.span.end);
171
+ const lightness = clamp(56 - stack * 7, 30, 60);
172
+ const bg = isOpen
173
+ ? `hsla(${hue}, 80%, ${clamp(lightness + 6, 30, 65)}%, 0.35)`
174
+ : `hsla(${hue}, 80%, ${lightness}%, 0.6)`;
175
+ const border = `hsla(${hue}, 90%, ${clamp(lightness + 22, 45, 80)}%, 0.95)`;
176
+
177
+ visualSpans.push({
178
+ span: v.span,
179
+ leftPct: v.leftPct,
180
+ widthPct: v.widthPct,
181
+ rightPct: v.rightPct,
182
+ durationMs: v.durationMs,
183
+ stack,
184
+ bg,
185
+ border
186
+ });
187
+ }
188
+
189
+ laneData[lane] = {
190
+ stackCount: Math.max(1, stackRight.length),
191
+ visualSpans
192
+ };
193
+ }
194
+
195
+ const ticks = computeTicks(viewStart, viewEnd, viewRange);
196
+
197
+ return {
198
+ now,
199
+ size,
200
+ lanes: maxLane + 1,
201
+ minStart,
202
+ maxEnd,
203
+ range: fullRange,
204
+ viewStart,
205
+ viewEnd,
206
+ viewRange,
207
+ buckets,
208
+ laneData,
209
+ ticks
210
+ };
211
+ }, [collector, version, windowMs, view.follow, view.range, view.start]);
212
+ }
@@ -0,0 +1,25 @@
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
+ const NICE_INTERVALS = [
12
+ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000
13
+ ] as const;
14
+
15
+ /**
16
+ * Picks a "nice" round interval for time-axis grid lines.
17
+ * Targets roughly 8 ticks across the visible range.
18
+ */
19
+ export const calculateTimeInterval = (rangeMs: number): number => {
20
+ const rawInterval = rangeMs / 8;
21
+ for (const mag of NICE_INTERVALS) {
22
+ if (mag >= rawInterval) return mag;
23
+ }
24
+ return NICE_INTERVALS[NICE_INTERVALS.length - 1]!;
25
+ };
@@ -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 { useSystem } 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 = useSystem();
22
+ const registry = useStore(system.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,144 @@
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 { useSystem } from '@/system';
12
+ import { useStore } from 'zustand';
13
+ import { Icon } from '@/components/primitives/icon';
14
+
15
+ type ValueControlProps = {
16
+ value: any;
17
+ onChange: (value: any) => void;
18
+ valueType: string;
19
+ };
20
+
21
+ type Props = {
22
+ variables: Record<string, VariableJSON>;
23
+ selectedVarKey: string;
24
+ onSelectVariable: (key: string) => void;
25
+ onDeleteVariable: (key: string) => void;
26
+ onNewVariable: () => void;
27
+
28
+ selectedVariable: VariableJSON | null;
29
+ editValue: any;
30
+ onChangeEditValue: (value: any) => void;
31
+ SelectedVarControlComponent: React.ComponentType<ValueControlProps>;
32
+ onUpdateVariable: () => void;
33
+ };
34
+
35
+ export function ManageVariablesScreen({
36
+ selectedVarKey: _selectedVarKey,
37
+ onSelectVariable,
38
+ onDeleteVariable,
39
+ onNewVariable,
40
+ selectedVariable,
41
+ editValue,
42
+ onChangeEditValue,
43
+ SelectedVarControlComponent,
44
+ onUpdateVariable
45
+ }: Props) {
46
+ const system = useSystem();
47
+ const icons = useStore(system.legendStore, (x) => x.icons);
48
+ const variables = useStore(system.variableStore, (x) => x.variables);
49
+
50
+ const handleTreeSelect = (ev: unknown) => {
51
+ const detail = (ev as CustomEvent<any>)?.detail;
52
+ const selectedItems = Array.isArray(detail)
53
+ ? detail
54
+ : detail?.selectedItems;
55
+ const firstSelected = selectedItems?.[0] as HTMLElement | undefined;
56
+ const key = firstSelected?.dataset?.varKey;
57
+
58
+ if (key) {
59
+ onSelectVariable(key);
60
+ }
61
+ };
62
+
63
+ return (
64
+ <div className={styles.root}>
65
+ <div className={styles.header}>
66
+ <div className={styles.headerTitle}>Variables</div>
67
+ <Icon title="New variable" onClick={onNewVariable}>
68
+ <Plus />
69
+ </Icon>
70
+ </div>
71
+ <VscodeDivider />
72
+
73
+ <div className={styles.variableList}>
74
+ {Object.keys(variables).length === 0 ? (
75
+ <div className={styles.emptyState}>
76
+ <div className={styles.emptyStateText}>No variables defined</div>
77
+ <div className={styles.emptyStateHint}>
78
+ Click the "+" button to create your first variable
79
+ </div>
80
+ </div>
81
+ ) : (
82
+ <VscodeTree
83
+ className={styles.tree}
84
+ onVscTreeSelect={handleTreeSelect}
85
+ >
86
+ {Object.entries(variables).map(([name, variable]) => (
87
+ <VscodeTreeItem
88
+ key={name}
89
+ data-var-key={name}
90
+ active={variable.id == selectedVariable?.id}
91
+ >
92
+ <div className={styles.treeItemContent}>
93
+ <div className={styles.treeItemIcon}>
94
+ {icons[variable.valueTypeName] &&
95
+ React.createElement(icons[variable.valueTypeName]!)}
96
+ </div>
97
+ <div className={styles.treeItemInfo} title={variable.id}>
98
+ {variable.name}
99
+ </div>
100
+ </div>
101
+ </VscodeTreeItem>
102
+ ))}
103
+ </VscodeTree>
104
+ )}
105
+ </div>
106
+
107
+ {selectedVariable && (
108
+ <div className={styles.selectedEditor}>
109
+ <div className={styles.editorHeader}>
110
+ <div className={styles.editorTitle}>Edit Variable</div>
111
+ <div className={styles.editorVariableName}>
112
+ {selectedVariable.name}
113
+ </div>
114
+ </div>
115
+
116
+ <div className={styles.editorFields}>
117
+ <label className={styles.fieldLabel}>Current Value</label>
118
+ <div className={styles.fieldControl}>
119
+ <SelectedVarControlComponent
120
+ value={editValue}
121
+ onChange={onChangeEditValue}
122
+ valueType={selectedVariable.valueTypeName}
123
+ />
124
+ </div>
125
+ </div>
126
+
127
+ <div className={styles.editorActions}>
128
+ <VscodeButton
129
+ secondary
130
+ iconOnly
131
+ title="Delete variable"
132
+ onClick={() => {
133
+ onDeleteVariable(selectedVariable.name);
134
+ }}
135
+ >
136
+ <TrashSolid />
137
+ </VscodeButton>
138
+ <VscodeButton onClick={onUpdateVariable}>Update Value</VscodeButton>
139
+ </div>
140
+ </div>
141
+ )}
142
+ </div>
143
+ );
144
+ }
@@ -0,0 +1,125 @@
1
+ import React, { useState, useCallback } from 'react';
2
+ import { useSystem } from '@/system/provider';
3
+ import { useStore } from 'zustand';
4
+ import { CreateVariableScreen } from './CreateVariableScreen';
5
+ import { ManageVariablesScreen } from './ManageVariablesScreen';
6
+ import { BasePanel } from '../base';
7
+
8
+ type VariablesPanelScreen = 'manage' | 'create';
9
+
10
+ export function VariablesPanel() {
11
+ const system = useSystem();
12
+ const variables = useStore(system.variableStore, (x) => x.variables);
13
+ const setVariable = useStore(system.variableStore, (x) => x.setVariable);
14
+ const removeVariable = useStore(
15
+ system.variableStore,
16
+ (x) => x.removeVariable
17
+ );
18
+ const controls = useStore(system.controlStore, (x) => x.controls);
19
+ const defaultControl = useStore(system.controlStore, (x) => x.defaultControl);
20
+
21
+ // Get the appropriate control component for a value type
22
+ const getControlComponent = useCallback(
23
+ (valueType: string) => {
24
+ return controls[valueType] || defaultControl;
25
+ },
26
+ [controls, defaultControl]
27
+ );
28
+
29
+ const [screen, setScreen] = useState<VariablesPanelScreen>('manage');
30
+
31
+ // Section 2 & 3: Select and edit existing variable
32
+ const [selectedVarId, setSelectedVarId] = useState<string>('');
33
+ const [editValue, setEditValue] = useState<any>('');
34
+
35
+ // Handle selecting a variable
36
+ const handleSelectVariable = useCallback(
37
+ (varKey: string) => {
38
+ setSelectedVarId(varKey);
39
+ const variable = variables[varKey];
40
+
41
+ if (variable) {
42
+ setEditValue(variable.initialValue);
43
+ }
44
+ },
45
+ [variables]
46
+ );
47
+
48
+ // Handle updating selected variable
49
+ const handleUpdateVariable = useCallback(() => {
50
+ if (!selectedVarId || !variables[selectedVarId]) {
51
+ return;
52
+ }
53
+
54
+ const variable = variables[selectedVarId];
55
+ // Force re-render by updating the store
56
+ setVariable(selectedVarId, {
57
+ ...variable,
58
+ initialValue: editValue
59
+ });
60
+ }, [selectedVarId, editValue, variables, setVariable]);
61
+
62
+ // Handle deleting a variable
63
+ const handleDeleteVariable = useCallback(
64
+ (varName: string) => {
65
+ removeVariable(varName);
66
+ if (varName === selectedVarId) {
67
+ setSelectedVarId('');
68
+ setEditValue('');
69
+ }
70
+ },
71
+ [removeVariable, selectedVarId, variables]
72
+ );
73
+
74
+ const selectedVariable = selectedVarId
75
+ ? (variables[selectedVarId] ?? null)
76
+ : null;
77
+ const SelectedVarControlComponent = selectedVariable
78
+ ? getControlComponent(selectedVariable.valueTypeName)
79
+ : defaultControl;
80
+
81
+ const goToCreate = useCallback(() => {
82
+ setScreen('create');
83
+ }, []);
84
+
85
+ const goToManage = useCallback(() => {
86
+ setScreen('manage');
87
+ }, []);
88
+
89
+ const handleCreatedVariable = useCallback(
90
+ (variableKey: string) => {
91
+ setSelectedVarId(variableKey);
92
+ const created = system.variableStore.getState().variables[variableKey];
93
+ if (created) {
94
+ setEditValue(created.initialValue);
95
+ }
96
+ },
97
+ [system.variableStore]
98
+ );
99
+
100
+ return (
101
+ <BasePanel>
102
+ {screen === 'create' && (
103
+ <CreateVariableScreen
104
+ onBack={goToManage}
105
+ onCancel={goToManage}
106
+ onCreated={handleCreatedVariable}
107
+ />
108
+ )}
109
+ {screen === 'manage' && (
110
+ <ManageVariablesScreen
111
+ variables={variables}
112
+ selectedVarKey={selectedVarId}
113
+ onSelectVariable={handleSelectVariable}
114
+ onDeleteVariable={handleDeleteVariable}
115
+ onNewVariable={goToCreate}
116
+ selectedVariable={selectedVariable}
117
+ editValue={editValue}
118
+ onChangeEditValue={setEditValue}
119
+ SelectedVarControlComponent={SelectedVarControlComponent}
120
+ onUpdateVariable={handleUpdateVariable}
121
+ />
122
+ )}
123
+ </BasePanel>
124
+ );
125
+ }