@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.
- package/.fallowrc.json +16 -0
- package/.storybook/main.ts +32 -0
- package/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +64 -0
- package/.storybook/styles.css +16 -0
- package/.turbo/turbo-build.log +7 -0
- package/CHANGELOG.md +368 -0
- package/LICENSE +6 -0
- package/README.md +2 -2
- package/data/Polynomial.json +510 -0
- package/data/sequence.json +337 -0
- package/data/trigger-event.json +241 -0
- package/data/variable-change.json +210 -0
- package/dist/AnyControlImpl-Ds-CShIB.js +20 -0
- package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
- package/dist/entry.css +4 -0
- package/dist/index.css +42 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +3597 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18009 -0
- package/dist/index.js.map +1 -0
- package/dist/noteImpl-KkrrWgJd.js +242 -0
- package/dist/noteImpl-KkrrWgJd.js.map +1 -0
- package/dist/styles.module-CvmpDkZj.css +3 -0
- package/dist/styles.module-CvmpDkZj.css.map +1 -0
- package/dist/styles.module-DZxg8aW9.js +271 -0
- package/dist/styles.module-DZxg8aW9.js.map +1 -0
- package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
- package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
- package/docs/notifications.md +246 -0
- package/docs/protocol.md +702 -0
- package/docs/specifics.md +191 -0
- package/package.json +82 -22
- package/postcss.config.ts +3 -4
- package/src/annotations/index.ts +32 -0
- package/src/components/FloatingToolbar/index.module.css +37 -0
- package/src/components/FloatingToolbar/index.tsx +256 -0
- package/src/components/Flow.tsx +287 -75
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +274 -0
- package/src/components/contextMenus/NodePicker.tsx +481 -0
- package/src/components/contextMenus/edge.tsx +22 -0
- package/src/components/contextMenus/node.tsx +15 -0
- package/src/components/contextMenus/selection.tsx +11 -0
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +19 -0
- package/src/components/controls/boolean/index.tsx +13 -0
- package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
- package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
- package/src/components/controls/colorPicker/index.module.css +18 -0
- package/src/components/controls/colorPicker/index.tsx +61 -0
- package/src/components/controls/number/index.tsx +35 -0
- package/src/components/controls/string/index.tsx +16 -0
- package/src/components/edges/index.tsx +475 -0
- package/src/components/edges/offsetBezier.ts +134 -0
- package/src/components/hotKeys.tsx +20 -0
- package/src/components/layoutController/index.module.css +13 -0
- package/src/components/layoutController/index.tsx +140 -0
- package/src/components/layoutController/utils.ts +248 -0
- package/src/components/menubar/defaults.tsx +516 -0
- package/src/components/menubar/index.tsx +49 -0
- package/src/components/menubar/menuItem.module.css +31 -0
- package/src/components/menubar/menuItem.tsx +65 -0
- package/src/components/nodes/behave/Node.module.css +23 -0
- package/src/components/nodes/behave/Node.tsx +176 -0
- package/src/components/nodes/behave/NodeContainer.module.css +88 -0
- package/src/components/nodes/behave/NodeContainer.tsx +46 -0
- package/src/components/nodes/behave/index.tsx +14 -0
- package/src/components/nodes/group/index.tsx +109 -0
- package/src/components/nodes/wrapper/index.tsx +73 -0
- package/src/components/nodes/wrapper/styles.module.css +87 -0
- package/src/components/notifications/NotificationProvider.tsx +81 -0
- package/src/components/notifications/index.ts +2 -0
- package/src/components/notifications/utils.ts +71 -0
- package/src/components/panels/alignment/index.module.css +10 -0
- package/src/components/panels/alignment/index.tsx +244 -0
- package/src/components/panels/base/index.tsx +5 -0
- package/src/components/panels/base/styles.module.css +12 -0
- package/src/components/panels/common/PanelHeader.module.css +24 -0
- package/src/components/panels/common/PanelHeader.tsx +22 -0
- package/src/components/panels/common/SectionTitle.module.css +13 -0
- package/src/components/panels/common/SectionTitle.tsx +10 -0
- package/src/components/panels/events/EditEventPanel.tsx +324 -0
- package/src/components/panels/events/ManageEventsPanel.tsx +101 -0
- package/src/components/panels/events/index.tsx +23 -0
- package/src/components/panels/events/styles.module.css +178 -0
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +92 -0
- package/src/components/panels/history/styles.module.css +97 -0
- package/src/components/panels/keymaps/index.module.css +68 -0
- package/src/components/panels/keymaps/index.tsx +166 -0
- package/src/components/panels/layers/index.tsx +245 -0
- package/src/components/panels/layers/styles.module.css +107 -0
- package/src/components/panels/legend/index.module.css +6 -0
- package/src/components/panels/legend/index.tsx +76 -0
- package/src/components/panels/logs/index.module.css +218 -0
- package/src/components/panels/logs/index.tsx +288 -0
- package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
- package/src/components/panels/nodeInputs/InputsGroup.tsx +65 -0
- package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +55 -0
- package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
- package/src/components/panels/nodeInputs/index.module.css +308 -0
- package/src/components/panels/nodeInputs/index.tsx +349 -0
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +153 -0
- package/src/components/panels/nodePicker/index.tsx +115 -0
- package/src/components/panels/panel/index.module.css +66 -0
- package/src/components/panels/panel/index.tsx +88 -0
- package/src/components/panels/search/index.module.css +16 -0
- package/src/components/panels/search/index.tsx +215 -0
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +251 -0
- package/src/components/panels/systemSettings/styles.module.css +138 -0
- package/src/components/panels/traces/GridLines.tsx +38 -0
- package/src/components/panels/traces/TimeGrid.tsx +48 -0
- package/src/components/panels/traces/TraceLane.tsx +62 -0
- package/src/components/panels/traces/TraceTooltip.tsx +22 -0
- package/src/components/panels/traces/TracesHeader.tsx +56 -0
- package/src/components/panels/traces/index.module.css +159 -0
- package/src/components/panels/traces/index.tsx +298 -0
- package/src/components/panels/traces/types.ts +48 -0
- package/src/components/panels/traces/useDerivedSpans.ts +307 -0
- package/src/components/panels/traces/utils.ts +33 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
- package/src/components/panels/variables/ManageVariablesScreen.tsx +147 -0
- package/src/components/panels/variables/index.tsx +125 -0
- package/src/components/panels/variables/styles.module.css +149 -0
- package/src/components/primitives/icon.module.css +45 -0
- package/src/components/primitives/icon.tsx +38 -0
- package/src/components/sockets/input/index.tsx +83 -0
- package/src/components/sockets/input/styles.module.css +26 -0
- package/src/components/sockets/output/index.tsx +68 -0
- package/src/components/sockets/output/styles.module.css +22 -0
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +57 -0
- package/src/css/rc-dock.css +212 -0
- package/src/css/rc-menu.css +101 -0
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +198 -0
- package/src/css/vscode-elements.css +124 -0
- package/src/entry.css +4 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -0
- package/src/generators/SequenceGenerator.tsx +104 -0
- package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
- package/src/generators/SwitchOnStringGenerator.tsx +263 -0
- package/src/generators/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +55 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +17 -16
- package/src/hooks/useFlowHandlers.ts +154 -30
- package/src/hooks/useWasdPan.ts +210 -0
- package/src/index.css +134 -0
- package/src/index.ts +53 -18
- package/src/manifest/contributionRegistry.ts +93 -0
- package/src/manifest/index.ts +4 -0
- package/src/manifest/loadManifest.ts +82 -0
- package/src/manifest/manifestPlugin.ts +29 -0
- package/src/manifest/passthroughValueType.ts +40 -0
- package/src/plugin/alignment/index.ts +91 -0
- package/src/plugin/autosave/controller.ts +366 -0
- package/src/plugin/autosave/index.tsx +114 -0
- package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
- package/src/plugin/autosave/panel/index.tsx +1 -0
- package/src/plugin/autosave/panel/styles.module.css +56 -0
- package/src/plugin/autosave/settings.ts +65 -0
- package/src/plugin/autosave/storage.ts +147 -0
- package/src/plugin/docs/index.tsx +297 -0
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +21 -0
- package/src/plugin/docs/panel/styles.module.css +174 -0
- package/src/plugin/graphrunner/actions.ts +326 -0
- package/src/plugin/graphrunner/buttons.tsx +95 -0
- package/src/plugin/graphrunner/client.ts +707 -0
- package/src/plugin/graphrunner/index.tsx +184 -0
- package/src/plugin/graphrunner/panel.tsx +386 -0
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +187 -0
- package/src/plugin/graphrunner/session.ts +243 -0
- package/src/plugin/graphrunner/store.ts +196 -0
- package/src/plugin/graphrunner/styles.module.css +171 -0
- package/src/plugin/graphrunner/transport.ts +250 -0
- package/src/plugin/graphrunner/types.ts +693 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +637 -0
- package/src/plugin/graphrunner-local/index.tsx +172 -0
- package/src/plugin/graphrunner-local/panel.tsx +187 -0
- package/src/plugin/graphrunner-local/store.ts +41 -0
- package/src/plugin/graphrunner-local/styles.module.css +82 -0
- package/src/plugin/graphrunner-local/transport.ts +1339 -0
- package/src/plugin/graphrunner-local/types.ts +10 -0
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +635 -0
- package/src/plugin/graphrunner-webworker/index.tsx +140 -0
- package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
- package/src/plugin/graphrunner-webworker/store.ts +98 -0
- package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/plugin/layout/dagre.ts +131 -0
- package/src/plugin/layout/elk.ts +216 -0
- package/src/plugin/layout/index.ts +80 -0
- package/src/plugin/notes/FormatToolbar.tsx +200 -0
- package/src/plugin/notes/index.tsx +191 -0
- package/src/plugin/notes/nodeActions.ts +100 -0
- package/src/plugin/notes/note.tsx +20 -0
- package/src/plugin/notes/noteImpl.tsx +89 -0
- package/src/plugin/realtime/realtimeRunner.ts +624 -0
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
- package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
- package/src/specifics/VariableGetSpecific.tsx +110 -0
- package/src/specifics/VariableSetSpecific.tsx +110 -0
- package/src/store/actions.tsx +698 -0
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/controls.tsx +62 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/documentation.tsx +69 -0
- package/src/store/events.tsx +116 -0
- package/src/store/flow.tsx +230 -0
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +364 -0
- package/src/store/layers.ts +259 -0
- package/src/store/legend.tsx +76 -0
- package/src/store/logs.ts +28 -0
- package/src/store/menubar.ts +41 -0
- package/src/store/refs.ts +84 -0
- package/src/store/registry.ts +51 -0
- package/src/store/selection.ts +22 -0
- package/src/store/settings.ts +99 -0
- package/src/store/settingsSchema.ts +210 -0
- package/src/store/socketGenerator.tsx +54 -0
- package/src/store/specific.tsx +75 -0
- package/src/store/specs.tsx +35 -0
- package/src/store/tabs.ts +282 -0
- package/src/store/toolbar.tsx +45 -0
- package/src/store/traces.ts +240 -0
- package/src/store/variables.ts +37 -0
- package/src/system/graph.ts +131 -0
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +6 -0
- package/src/system/notifications.ts +111 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +55 -0
- package/src/system/provider.tsx +86 -0
- package/src/system/pubsub.ts +323 -0
- package/src/system/system.ts +653 -0
- package/src/system/tabLoader.tsx +303 -0
- package/src/system/undoRedo.ts +103 -0
- package/src/transformers/Uigraph.ts +61 -0
- package/src/transformers/behaveToFlow.ts +16 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +40 -12
- package/src/types/NodeMetadata.ts +27 -0
- package/src/types/graph.ts +49 -0
- package/src/types/nodes.ts +50 -0
- package/src/types.ts +18 -0
- package/src/util/autoConvert.ts +200 -0
- package/src/util/colors.ts +1 -29
- package/src/util/downloadJson.ts +18 -0
- package/src/util/extractNodeMetadata.ts +16 -0
- package/src/util/getPickerFilters.ts +1 -1
- package/src/util/isBehaveNode.ts +6 -0
- package/src/util/isValidConnection.ts +51 -17
- package/src/util/mergeSockets.ts +29 -0
- package/src/util/serializeVariables.ts +66 -0
- package/src/util/sockets.ts +43 -0
- package/stories/apex/layoutController/example-graph.worker.ts +39 -0
- package/stories/apex/layoutController/index.stories.tsx +48 -0
- package/stories/apex/layoutController/webworker.stories.tsx +103 -0
- package/stories/apex/menubar/menubar.stories.tsx +19 -0
- package/stories/components/colorpicker/index.stories.tsx +20 -0
- package/stories/components/contextMenus/edge.stories.tsx +32 -0
- package/stories/components/contextMenus/node.stories.tsx +26 -0
- package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
- package/stories/components/controls/any/index.stories.tsx +19 -0
- package/stories/components/controls/boolean/index.stories.tsx +19 -0
- package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
- package/stories/components/controls/number/index.stories.tsx +19 -0
- package/stories/components/controls/string/index.stories.tsx +19 -0
- package/stories/components/nodes/behaveNode.stories.tsx +108 -0
- package/stories/components/panels/alignment.stories.tsx +24 -0
- package/stories/components/panels/events.stories.tsx +38 -0
- package/stories/components/panels/graphRunner.stories.tsx +317 -0
- package/stories/components/panels/history.stories.tsx +37 -0
- package/stories/components/panels/keymaps.stories.tsx +21 -0
- package/stories/components/panels/legend.stories.tsx +37 -0
- package/stories/components/panels/logs.stories.tsx +24 -0
- package/stories/components/panels/nodeInputs.stories.tsx +21 -0
- package/stories/components/panels/nodePicker.stories.tsx +37 -0
- package/stories/components/panels/panel.stories.tsx +39 -0
- package/stories/components/panels/search.stories.tsx +24 -0
- package/stories/components/panels/systemSettings.stories.tsx +26 -0
- package/stories/components/panels/traces.stories.tsx +225 -0
- package/stories/components/panels/variables.stories.tsx +24 -0
- package/stories/defaults/defaultStoryProvider.tsx +170 -0
- package/stories/defaults/systemGenerator.ts +43 -0
- package/stories/plugins/notes.stories.tsx +100 -0
- package/tests/autoConvert.test.ts +329 -0
- package/tests/autosavePlugin.test.ts +204 -0
- package/tests/callSubgraphSync.test.ts +148 -0
- package/tests/commandRegistry.test.ts +137 -0
- package/tests/components/edges/offsetBezier.test.ts +51 -0
- package/tests/components/layoutController/utils.test.ts +68 -0
- package/tests/components/panels/traces/utils.test.ts +52 -0
- package/tests/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +27 -4
- package/tests/hotkeys.test.ts +79 -0
- package/tests/keepAliveLifecycle.test.ts +167 -0
- package/tests/loadManifest.test.ts +113 -0
- package/tests/noteMarkdown.test.ts +65 -0
- package/tests/notesPlugin.test.ts +162 -0
- package/tests/notifications.test.ts +87 -0
- package/tests/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +373 -0
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -0
- package/tests/util/calculateNewEdge.test.ts +98 -0
- package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
- package/tests/util/hasPositionMetaData.test.ts +33 -0
- package/tests/util/isBehaveNode.test.ts +22 -0
- package/tests/util/isHandleConnected.test.ts +37 -0
- package/tests/util/mergeSockets.test.ts +43 -0
- package/tests/visual/README.md +64 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-localGraphRunner-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
- package/tests/visual/panels.visual.test.tsx +76 -0
- package/tests/wasdPan.test.ts +71 -0
- package/tsconfig.base.json +39 -0
- package/tsconfig.json +18 -59
- package/tsconfig.prod.json +23 -0
- package/tsdown.config.ts +15 -3
- package/typedoc.json +7 -7
- package/vite.config.js +7 -0
- package/vitest.config.ts +5 -2
- package/vitest.visual.config.ts +55 -0
- package/src/components/AutoSizeInput.tsx +0 -65
- package/src/components/Controls.tsx +0 -87
- package/src/components/InputSocket.tsx +0 -142
- package/src/components/Node.tsx +0 -68
- package/src/components/NodeContainer.tsx +0 -46
- package/src/components/NodePicker.tsx +0 -77
- package/src/components/OutputSocket.tsx +0 -58
- package/src/components/modals/ClearModal.tsx +0 -40
- package/src/components/modals/HelpModal.tsx +0 -36
- package/src/components/modals/LoadModal.tsx +0 -96
- package/src/components/modals/Modal.tsx +0 -64
- package/src/components/modals/SaveModal.tsx +0 -60
- package/src/hooks/useCustomNodeTypes.tsx +0 -31
- package/src/hooks/useGraphRunner.ts +0 -104
- package/src/hooks/useMergeMap.ts +0 -14
- package/src/hooks/useNodeSpecJson.ts +0 -20
- package/src/hooks/useQueriableDefinitions.ts +0 -22
- package/src/styles.css +0 -8
- package/tailwind.config.ts +0 -19
- package/tests/tsconfig.json +0 -10
- /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import type { SpanCollector } from '@/store/traces';
|
|
3
|
+
import type {
|
|
4
|
+
DerivedData,
|
|
5
|
+
LaneData,
|
|
6
|
+
Tick,
|
|
7
|
+
ViewState,
|
|
8
|
+
VisualSpan
|
|
9
|
+
} from './types';
|
|
10
|
+
import {
|
|
11
|
+
calculateTimeInterval,
|
|
12
|
+
clamp,
|
|
13
|
+
hashToHue,
|
|
14
|
+
MAX_ZOOM_OUT_FACTOR
|
|
15
|
+
} from './utils';
|
|
16
|
+
|
|
17
|
+
const MIN_WIDTH_PCT = 0.5;
|
|
18
|
+
const OVERLAP_EPSILON_PCT = 0.5;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Minimum *visual* span length (ms). Instant nodes record start === end (0ms),
|
|
22
|
+
* which otherwise collapses the range (maxEnd <= minStart) and renders nothing.
|
|
23
|
+
* The true duration is still reported to the tooltip.
|
|
24
|
+
*/
|
|
25
|
+
const MIN_SPAN_MS = 1;
|
|
26
|
+
|
|
27
|
+
/** Effective end used for layout: clamps instant/zero-length spans to MIN_SPAN_MS. */
|
|
28
|
+
const visualEnd = (start: number, rawEnd: number): number =>
|
|
29
|
+
Math.max(rawEnd, start + MIN_SPAN_MS);
|
|
30
|
+
|
|
31
|
+
const EMPTY: DerivedData = {
|
|
32
|
+
now: 0,
|
|
33
|
+
size: 0,
|
|
34
|
+
lanes: 0,
|
|
35
|
+
minStart: 0,
|
|
36
|
+
maxEnd: 0,
|
|
37
|
+
range: 1,
|
|
38
|
+
viewStart: 0,
|
|
39
|
+
viewEnd: 0,
|
|
40
|
+
viewRange: 1,
|
|
41
|
+
buckets: [],
|
|
42
|
+
laneData: [],
|
|
43
|
+
ticks: []
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function computeTicks(
|
|
47
|
+
viewStart: number,
|
|
48
|
+
viewEnd: number,
|
|
49
|
+
viewRange: number,
|
|
50
|
+
origin: number
|
|
51
|
+
): Tick[] {
|
|
52
|
+
const interval = calculateTimeInterval(viewRange);
|
|
53
|
+
const firstTick = Math.ceil(viewStart / interval) * interval;
|
|
54
|
+
const ticks: Tick[] = [];
|
|
55
|
+
for (let t = firstTick; t <= viewEnd; t += interval) {
|
|
56
|
+
const leftPct = ((t - viewStart) / viewRange) * 100;
|
|
57
|
+
if (leftPct >= 0 && leftPct <= 100) {
|
|
58
|
+
// Position uses absolute time; the label is relative to the trace origin
|
|
59
|
+
// (first span) so it reads 0ms… instead of the raw clock value.
|
|
60
|
+
ticks.push({ time: t - origin, leftPct });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return ticks;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function useDerivedSpans(
|
|
67
|
+
collector: SpanCollector,
|
|
68
|
+
version: number,
|
|
69
|
+
windowMs: number,
|
|
70
|
+
view: ViewState
|
|
71
|
+
): DerivedData {
|
|
72
|
+
return useMemo(
|
|
73
|
+
() => computeDerivedSpans(collector, windowMs, view),
|
|
74
|
+
[collector, version, windowMs, view.follow, view.range, view.start]
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Pure span → layout derivation used by {@link useDerivedSpans}. Exported so the
|
|
80
|
+
* layout math (lane assignment, min-span visibility, ticks, zoom clamping) can be
|
|
81
|
+
* unit-tested without a React render.
|
|
82
|
+
*/
|
|
83
|
+
type TraceSpan = import('@/store/traces').TraceSpan;
|
|
84
|
+
|
|
85
|
+
interface SpanScan {
|
|
86
|
+
minStart: number;
|
|
87
|
+
maxEnd: number;
|
|
88
|
+
maxLane: number;
|
|
89
|
+
buckets: TraceSpan[][];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Walk the ring buffer newest-window-first, bucketing spans by lane and
|
|
93
|
+
* tracking the overall time/lane bounds. */
|
|
94
|
+
function scanSpans(collector: SpanCollector, now: number): SpanScan {
|
|
95
|
+
const { capacity, size, writeIndex, spans } = collector;
|
|
96
|
+
let minStart = Number.POSITIVE_INFINITY;
|
|
97
|
+
let maxEnd = Number.NEGATIVE_INFINITY;
|
|
98
|
+
let maxLane = -1;
|
|
99
|
+
const buckets: TraceSpan[][] = [];
|
|
100
|
+
|
|
101
|
+
const startIndex = (writeIndex - size + capacity) % capacity;
|
|
102
|
+
for (let i = 0; i < size; i++) {
|
|
103
|
+
const idx = (startIndex + i) % capacity;
|
|
104
|
+
const s = spans[idx];
|
|
105
|
+
if (!s) continue;
|
|
106
|
+
|
|
107
|
+
const rawEnd = Number.isNaN(s.end) ? now : s.end;
|
|
108
|
+
const end = visualEnd(s.start, rawEnd);
|
|
109
|
+
minStart = Math.min(minStart, s.start);
|
|
110
|
+
maxEnd = Math.max(maxEnd, end);
|
|
111
|
+
maxLane = Math.max(maxLane, s.lane);
|
|
112
|
+
|
|
113
|
+
(buckets[s.lane] ??= []).push(s);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { minStart, maxEnd, maxLane, buckets };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface ViewWindow {
|
|
120
|
+
viewStart: number;
|
|
121
|
+
viewEnd: number;
|
|
122
|
+
viewRange: number;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Resolve the visible time window from the data bounds, the follow/zoom mode,
|
|
126
|
+
* and the requested window size. */
|
|
127
|
+
function resolveViewWindow(
|
|
128
|
+
scan: SpanScan,
|
|
129
|
+
fullRange: number,
|
|
130
|
+
windowMs: number,
|
|
131
|
+
view: ViewState
|
|
132
|
+
): ViewWindow {
|
|
133
|
+
const { minStart, maxEnd } = scan;
|
|
134
|
+
const desiredRange = windowMs <= 0 ? fullRange : Math.max(1, windowMs);
|
|
135
|
+
const desiredStart = Math.max(minStart, maxEnd - desiredRange);
|
|
136
|
+
|
|
137
|
+
// Following snaps to the data range; manual zoom may pull back past the data
|
|
138
|
+
// extent (up to MAX_ZOOM_OUT_FACTOR×) so short traces aren't un-zoom-out-able.
|
|
139
|
+
const viewRange = clamp(
|
|
140
|
+
view.follow ? desiredRange : view.range,
|
|
141
|
+
1,
|
|
142
|
+
view.follow ? fullRange : fullRange * MAX_ZOOM_OUT_FACTOR
|
|
143
|
+
);
|
|
144
|
+
const viewStart = clamp(
|
|
145
|
+
view.follow ? desiredStart : view.start,
|
|
146
|
+
minStart,
|
|
147
|
+
maxEnd - viewRange
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return { viewStart, viewEnd: viewStart + viewRange, viewRange };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
type PlacedSpan = {
|
|
154
|
+
span: TraceSpan;
|
|
155
|
+
leftPct: number;
|
|
156
|
+
widthPct: number;
|
|
157
|
+
rightPct: number;
|
|
158
|
+
durationMs: number;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Project a single span onto the view window, or null if it falls outside. */
|
|
162
|
+
function placeSpan(
|
|
163
|
+
s: TraceSpan,
|
|
164
|
+
now: number,
|
|
165
|
+
win: ViewWindow
|
|
166
|
+
): PlacedSpan | null {
|
|
167
|
+
const { viewStart, viewEnd, viewRange } = win;
|
|
168
|
+
const rawEnd = Number.isNaN(s.end) ? now : s.end;
|
|
169
|
+
const end = visualEnd(s.start, rawEnd);
|
|
170
|
+
if (end < viewStart || s.start > viewEnd) return null;
|
|
171
|
+
|
|
172
|
+
const visibleStart = Math.max(s.start, viewStart);
|
|
173
|
+
const visibleEnd = Math.min(end, viewEnd);
|
|
174
|
+
if (visibleEnd <= visibleStart) return null;
|
|
175
|
+
|
|
176
|
+
const rawLeft = ((visibleStart - viewStart) / viewRange) * 100;
|
|
177
|
+
const rawWidth = ((visibleEnd - visibleStart) / viewRange) * 100;
|
|
178
|
+
const leftPct = clamp(rawLeft, 0, 100);
|
|
179
|
+
const widthPct = clamp(Math.max(MIN_WIDTH_PCT, rawWidth), 0, 100 - leftPct);
|
|
180
|
+
const rightPct = leftPct + widthPct;
|
|
181
|
+
// Report the true duration (0ms for instant nodes), not the padded one.
|
|
182
|
+
const durationMs = Math.max(0, rawEnd - s.start);
|
|
183
|
+
|
|
184
|
+
return { span: s, leftPct, widthPct, rightPct, durationMs };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Pack a placed span into the lowest stack row that has cleared its left edge,
|
|
188
|
+
* returning the chosen stack index and updating `stackRight` in place. */
|
|
189
|
+
function assignStack(placed: PlacedSpan, stackRight: number[]): number {
|
|
190
|
+
let stack = 0;
|
|
191
|
+
for (; stack < stackRight.length; stack++) {
|
|
192
|
+
if (placed.leftPct >= stackRight[stack]! + OVERLAP_EPSILON_PCT) break;
|
|
193
|
+
}
|
|
194
|
+
if (stack === stackRight.length) stackRight.push(placed.rightPct);
|
|
195
|
+
else stackRight[stack] = Math.max(stackRight[stack]!, placed.rightPct);
|
|
196
|
+
return stack;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Build the fill/border colours for a span at a given stack depth. */
|
|
200
|
+
function spanColors(
|
|
201
|
+
span: TraceSpan,
|
|
202
|
+
stack: number
|
|
203
|
+
): { bg: string; border: string } {
|
|
204
|
+
const hue = hashToHue(span.nodeId);
|
|
205
|
+
const isOpen = Number.isNaN(span.end);
|
|
206
|
+
const lightness = clamp(56 - stack * 7, 30, 60);
|
|
207
|
+
const bg = isOpen
|
|
208
|
+
? `hsla(${hue}, 80%, ${clamp(lightness + 6, 30, 65)}%, 0.35)`
|
|
209
|
+
: `hsla(${hue}, 80%, ${lightness}%, 0.6)`;
|
|
210
|
+
const border = `hsla(${hue}, 90%, ${clamp(lightness + 22, 45, 80)}%, 0.95)`;
|
|
211
|
+
return { bg, border };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Lay out one lane's spans into stacked, coloured visual spans. */
|
|
215
|
+
function buildLaneData(
|
|
216
|
+
laneSpans: TraceSpan[] | undefined,
|
|
217
|
+
now: number,
|
|
218
|
+
win: ViewWindow
|
|
219
|
+
): LaneData {
|
|
220
|
+
if (!laneSpans || laneSpans.length === 0) {
|
|
221
|
+
return { stackCount: 1, visualSpans: [] };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const placed: PlacedSpan[] = [];
|
|
225
|
+
for (const s of laneSpans) {
|
|
226
|
+
const p = placeSpan(s, now, win);
|
|
227
|
+
if (p) placed.push(p);
|
|
228
|
+
}
|
|
229
|
+
placed.sort((a, b) => a.leftPct - b.leftPct);
|
|
230
|
+
|
|
231
|
+
const stackRight: number[] = [];
|
|
232
|
+
const visualSpans: VisualSpan[] = [];
|
|
233
|
+
for (const v of placed) {
|
|
234
|
+
const stack = assignStack(v, stackRight);
|
|
235
|
+
const { bg, border } = spanColors(v.span, stack);
|
|
236
|
+
visualSpans.push({
|
|
237
|
+
span: v.span,
|
|
238
|
+
leftPct: v.leftPct,
|
|
239
|
+
widthPct: v.widthPct,
|
|
240
|
+
rightPct: v.rightPct,
|
|
241
|
+
durationMs: v.durationMs,
|
|
242
|
+
stack,
|
|
243
|
+
bg,
|
|
244
|
+
border
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return { stackCount: Math.max(1, stackRight.length), visualSpans };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function computeDerivedSpans(
|
|
252
|
+
collector: SpanCollector,
|
|
253
|
+
windowMs: number,
|
|
254
|
+
view: ViewState
|
|
255
|
+
): DerivedData {
|
|
256
|
+
const now = performance.now();
|
|
257
|
+
|
|
258
|
+
if (collector.size <= 0) return { ...EMPTY, now };
|
|
259
|
+
|
|
260
|
+
const scan = scanSpans(collector, now);
|
|
261
|
+
const { minStart, maxEnd, maxLane, buckets } = scan;
|
|
262
|
+
const { size } = collector;
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
!Number.isFinite(minStart) ||
|
|
266
|
+
!Number.isFinite(maxEnd) ||
|
|
267
|
+
maxEnd <= minStart
|
|
268
|
+
) {
|
|
269
|
+
return {
|
|
270
|
+
...EMPTY,
|
|
271
|
+
now,
|
|
272
|
+
size,
|
|
273
|
+
lanes: maxLane + 1,
|
|
274
|
+
buckets,
|
|
275
|
+
laneData: []
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const fullRange = Math.max(1, maxEnd - minStart);
|
|
280
|
+
const win = resolveViewWindow(scan, fullRange, windowMs, view);
|
|
281
|
+
|
|
282
|
+
const laneData: LaneData[] = Array.from({ length: maxLane + 1 }, (_, lane) =>
|
|
283
|
+
buildLaneData(buckets[lane], now, win)
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const ticks = computeTicks(
|
|
287
|
+
win.viewStart,
|
|
288
|
+
win.viewEnd,
|
|
289
|
+
win.viewRange,
|
|
290
|
+
minStart
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
now,
|
|
295
|
+
size,
|
|
296
|
+
lanes: maxLane + 1,
|
|
297
|
+
minStart,
|
|
298
|
+
maxEnd,
|
|
299
|
+
range: fullRange,
|
|
300
|
+
viewStart: win.viewStart,
|
|
301
|
+
viewEnd: win.viewEnd,
|
|
302
|
+
viewRange: win.viewRange,
|
|
303
|
+
buckets,
|
|
304
|
+
laneData,
|
|
305
|
+
ticks
|
|
306
|
+
};
|
|
307
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const hashToHue = (str: string): number => {
|
|
2
|
+
let hash = 0;
|
|
3
|
+
for (let i = 0; i < str.length; i++)
|
|
4
|
+
hash = (hash * 31 + str.charCodeAt(i)) | 0;
|
|
5
|
+
return Math.abs(hash) % 360;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const clamp = (v: number, min: number, max: number): number =>
|
|
9
|
+
Math.max(min, Math.min(max, v));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* How far past the captured data extent the user may zoom out. Without headroom
|
|
13
|
+
* the view range is clamped to the data range, so you hit a wall and "can't zoom
|
|
14
|
+
* out" , especially for very short traces. This lets the trace be pulled back
|
|
15
|
+
* into a smaller cluster with surrounding space.
|
|
16
|
+
*/
|
|
17
|
+
export const MAX_ZOOM_OUT_FACTOR = 8;
|
|
18
|
+
|
|
19
|
+
const NICE_INTERVALS = [
|
|
20
|
+
1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Picks a "nice" round interval for time-axis grid lines.
|
|
25
|
+
* Targets roughly 8 ticks across the visible range.
|
|
26
|
+
*/
|
|
27
|
+
export const calculateTimeInterval = (rangeMs: number): number => {
|
|
28
|
+
const rawInterval = rangeMs / 8;
|
|
29
|
+
for (const mag of NICE_INTERVALS) {
|
|
30
|
+
if (mag >= rawInterval) return mag;
|
|
31
|
+
}
|
|
32
|
+
return NICE_INTERVALS[NICE_INTERVALS.length - 1]!;
|
|
33
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { useStore } from 'zustand';
|
|
3
|
+
import { Variable } from '@kiberon-labs/behave-graph';
|
|
4
|
+
import { useActiveGraph } from '@/system/provider';
|
|
5
|
+
import {
|
|
6
|
+
VscodeButton,
|
|
7
|
+
VscodeTextfield,
|
|
8
|
+
VscodeSingleSelect,
|
|
9
|
+
VscodeOption,
|
|
10
|
+
VscodeLabel
|
|
11
|
+
} from '@vscode-elements/react-elements';
|
|
12
|
+
import styles from './styles.module.css';
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
onBack: () => void;
|
|
16
|
+
onCancel: () => void;
|
|
17
|
+
onCreated?: (variableKey: string) => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function CreateVariableScreen({ onBack, onCancel, onCreated }: Props) {
|
|
21
|
+
const system = useActiveGraph()!;
|
|
22
|
+
const registry = useStore(system.editor.registry);
|
|
23
|
+
const setVariable = useStore(system.variableStore, (x) => x.setVariable);
|
|
24
|
+
const controls = useStore(system.controlStore, (x) => x.controls);
|
|
25
|
+
const defaultControl = useStore(system.controlStore, (x) => x.defaultControl);
|
|
26
|
+
|
|
27
|
+
const [newVarName, setNewVarName] = useState('');
|
|
28
|
+
const [newVarType, setNewVarType] = useState('string');
|
|
29
|
+
const [newVarInitialValue, setNewVarInitialValue] = useState<any>('');
|
|
30
|
+
|
|
31
|
+
const availableTypes = useMemo(() => {
|
|
32
|
+
return Object.keys(registry.values).sort();
|
|
33
|
+
}, [registry.values]);
|
|
34
|
+
|
|
35
|
+
const getControlComponent = useCallback(
|
|
36
|
+
(valueType: string) => {
|
|
37
|
+
return controls[valueType] || defaultControl;
|
|
38
|
+
},
|
|
39
|
+
[controls, defaultControl]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const NewVarControlComponent = useMemo(() => {
|
|
43
|
+
return getControlComponent(newVarType);
|
|
44
|
+
}, [getControlComponent, newVarType]);
|
|
45
|
+
|
|
46
|
+
const resetForm = useCallback(() => {
|
|
47
|
+
setNewVarName('');
|
|
48
|
+
const valueType = registry.values[newVarType];
|
|
49
|
+
if (valueType) {
|
|
50
|
+
setNewVarInitialValue(valueType.creator());
|
|
51
|
+
}
|
|
52
|
+
}, [registry.values, newVarType]);
|
|
53
|
+
|
|
54
|
+
const handleCreate = useCallback(() => {
|
|
55
|
+
if (!newVarName.trim()) return;
|
|
56
|
+
|
|
57
|
+
const valueType = registry.values[newVarType];
|
|
58
|
+
if (!valueType) {
|
|
59
|
+
console.error(`Value type ${newVarType} not found in registry`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const newVariable = new Variable(
|
|
64
|
+
newVarName,
|
|
65
|
+
newVarName,
|
|
66
|
+
newVarType,
|
|
67
|
+
newVarInitialValue
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
setVariable(newVarName, newVariable);
|
|
71
|
+
onCreated?.(newVarName);
|
|
72
|
+
resetForm();
|
|
73
|
+
onBack();
|
|
74
|
+
}, [
|
|
75
|
+
newVarName,
|
|
76
|
+
newVarType,
|
|
77
|
+
newVarInitialValue,
|
|
78
|
+
registry.values,
|
|
79
|
+
setVariable,
|
|
80
|
+
onCreated,
|
|
81
|
+
resetForm,
|
|
82
|
+
onBack
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const handleCancel = useCallback(() => {
|
|
86
|
+
resetForm();
|
|
87
|
+
onCancel();
|
|
88
|
+
}, [resetForm, onCancel]);
|
|
89
|
+
|
|
90
|
+
const handleBack = useCallback(() => {
|
|
91
|
+
resetForm();
|
|
92
|
+
onBack();
|
|
93
|
+
}, [resetForm, onBack]);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<>
|
|
97
|
+
<div className={styles.headerRow}>
|
|
98
|
+
<h3
|
|
99
|
+
style={{
|
|
100
|
+
fontSize: '1.1em',
|
|
101
|
+
fontWeight: 'bold'
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
Create Variable
|
|
105
|
+
</h3>
|
|
106
|
+
|
|
107
|
+
<VscodeButton secondary onClick={handleBack}>
|
|
108
|
+
Back
|
|
109
|
+
</VscodeButton>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div className={`${styles.section} ${styles.sectionPadded}`}>
|
|
113
|
+
<div className={styles.field}>
|
|
114
|
+
<VscodeLabel>Name:</VscodeLabel>
|
|
115
|
+
<VscodeTextfield
|
|
116
|
+
value={newVarName}
|
|
117
|
+
placeholder="Variable name..."
|
|
118
|
+
onChange={(e: any) => setNewVarName(e.target.value)}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className={styles.field}>
|
|
123
|
+
<VscodeLabel>Type:</VscodeLabel>
|
|
124
|
+
<VscodeSingleSelect
|
|
125
|
+
value={newVarType}
|
|
126
|
+
onChange={(e: any) => {
|
|
127
|
+
const valueType = registry.values[e.target.value];
|
|
128
|
+
if (valueType?.serialize && valueType?.creator) {
|
|
129
|
+
setNewVarInitialValue(valueType.serialize(valueType.creator()));
|
|
130
|
+
}
|
|
131
|
+
setNewVarType(e.target.value);
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{availableTypes.map((type) => (
|
|
135
|
+
<VscodeOption key={type} value={type}>
|
|
136
|
+
{type}
|
|
137
|
+
</VscodeOption>
|
|
138
|
+
))}
|
|
139
|
+
</VscodeSingleSelect>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div className={styles.field}>
|
|
143
|
+
<VscodeLabel>Initial Value:</VscodeLabel>
|
|
144
|
+
<NewVarControlComponent
|
|
145
|
+
value={newVarInitialValue}
|
|
146
|
+
onChange={setNewVarInitialValue}
|
|
147
|
+
valueType={newVarType}
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div className={styles.actionsRow}>
|
|
152
|
+
<VscodeButton onClick={handleCreate} disabled={!newVarName.trim()}>
|
|
153
|
+
Save
|
|
154
|
+
</VscodeButton>
|
|
155
|
+
<VscodeButton secondary onClick={handleCancel}>
|
|
156
|
+
Cancel
|
|
157
|
+
</VscodeButton>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { VariableJSON } from '@kiberon-labs/behave-graph';
|
|
3
|
+
import {
|
|
4
|
+
VscodeButton,
|
|
5
|
+
VscodeDivider,
|
|
6
|
+
VscodeTree,
|
|
7
|
+
VscodeTreeItem
|
|
8
|
+
} from '@vscode-elements/react-elements';
|
|
9
|
+
import { Plus, TrashSolid } from 'iconoir-react';
|
|
10
|
+
import styles from './styles.module.css';
|
|
11
|
+
import { useActiveGraph } from '@/system';
|
|
12
|
+
import { useStore } from 'zustand';
|
|
13
|
+
import { Icon } from '@/components/primitives/icon';
|
|
14
|
+
import { PanelHeader } from '../common/PanelHeader';
|
|
15
|
+
|
|
16
|
+
type ValueControlProps = {
|
|
17
|
+
value: any;
|
|
18
|
+
onChange: (value: any) => void;
|
|
19
|
+
valueType: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
variables: Record<string, VariableJSON>;
|
|
24
|
+
selectedVarKey: string;
|
|
25
|
+
onSelectVariable: (key: string) => void;
|
|
26
|
+
onDeleteVariable: (key: string) => void;
|
|
27
|
+
onNewVariable: () => void;
|
|
28
|
+
|
|
29
|
+
selectedVariable: VariableJSON | null;
|
|
30
|
+
editValue: any;
|
|
31
|
+
onChangeEditValue: (value: any) => void;
|
|
32
|
+
SelectedVarControlComponent: React.ComponentType<ValueControlProps>;
|
|
33
|
+
onUpdateVariable: () => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function ManageVariablesScreen({
|
|
37
|
+
selectedVarKey: _selectedVarKey,
|
|
38
|
+
onSelectVariable,
|
|
39
|
+
onDeleteVariable,
|
|
40
|
+
onNewVariable,
|
|
41
|
+
selectedVariable,
|
|
42
|
+
editValue,
|
|
43
|
+
onChangeEditValue,
|
|
44
|
+
SelectedVarControlComponent,
|
|
45
|
+
onUpdateVariable
|
|
46
|
+
}: Props) {
|
|
47
|
+
const system = useActiveGraph()!;
|
|
48
|
+
const icons = useStore(system.editor.legendStore, (x) => x.icons);
|
|
49
|
+
const variables = useStore(system.variableStore, (x) => x.variables);
|
|
50
|
+
|
|
51
|
+
const handleTreeSelect = (ev: unknown) => {
|
|
52
|
+
const detail = (ev as CustomEvent<any>)?.detail;
|
|
53
|
+
const selectedItems = Array.isArray(detail)
|
|
54
|
+
? detail
|
|
55
|
+
: detail?.selectedItems;
|
|
56
|
+
const firstSelected = selectedItems?.[0] as HTMLElement | undefined;
|
|
57
|
+
const key = firstSelected?.dataset?.varKey;
|
|
58
|
+
|
|
59
|
+
if (key) {
|
|
60
|
+
onSelectVariable(key);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className={styles.root}>
|
|
66
|
+
<PanelHeader
|
|
67
|
+
title="Variables"
|
|
68
|
+
actions={
|
|
69
|
+
<Icon title="New variable" onClick={onNewVariable}>
|
|
70
|
+
<Plus />
|
|
71
|
+
</Icon>
|
|
72
|
+
}
|
|
73
|
+
/>
|
|
74
|
+
<VscodeDivider />
|
|
75
|
+
|
|
76
|
+
<div className={styles.variableList}>
|
|
77
|
+
{Object.keys(variables).length === 0 ? (
|
|
78
|
+
<div className={styles.emptyState}>
|
|
79
|
+
<div className={styles.emptyStateText}>No variables defined</div>
|
|
80
|
+
<div className={styles.emptyStateHint}>
|
|
81
|
+
Click the "+" button to create your first variable
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
) : (
|
|
85
|
+
<VscodeTree
|
|
86
|
+
className={styles.tree}
|
|
87
|
+
onVscTreeSelect={handleTreeSelect}
|
|
88
|
+
>
|
|
89
|
+
{Object.entries(variables).map(([name, variable]) => (
|
|
90
|
+
<VscodeTreeItem
|
|
91
|
+
key={name}
|
|
92
|
+
data-var-key={name}
|
|
93
|
+
active={variable.id == selectedVariable?.id}
|
|
94
|
+
>
|
|
95
|
+
<div className={styles.treeItemContent}>
|
|
96
|
+
<div className={styles.treeItemIcon}>
|
|
97
|
+
{icons[variable.valueTypeName] &&
|
|
98
|
+
React.createElement(icons[variable.valueTypeName]!)}
|
|
99
|
+
</div>
|
|
100
|
+
<div className={styles.treeItemInfo} title={variable.id}>
|
|
101
|
+
{variable.name}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</VscodeTreeItem>
|
|
105
|
+
))}
|
|
106
|
+
</VscodeTree>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{selectedVariable && (
|
|
111
|
+
<div className={styles.selectedEditor}>
|
|
112
|
+
<div className={styles.editorHeader}>
|
|
113
|
+
<div className={styles.editorTitle}>Edit Variable</div>
|
|
114
|
+
<div className={styles.editorVariableName}>
|
|
115
|
+
{selectedVariable.name}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className={styles.editorFields}>
|
|
120
|
+
<label className={styles.fieldLabel}>Current Value</label>
|
|
121
|
+
<div className={styles.fieldControl}>
|
|
122
|
+
<SelectedVarControlComponent
|
|
123
|
+
value={editValue}
|
|
124
|
+
onChange={onChangeEditValue}
|
|
125
|
+
valueType={selectedVariable.valueTypeName}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div className={styles.editorActions}>
|
|
131
|
+
<VscodeButton
|
|
132
|
+
secondary
|
|
133
|
+
iconOnly
|
|
134
|
+
title="Delete variable"
|
|
135
|
+
onClick={() => {
|
|
136
|
+
onDeleteVariable(selectedVariable.name);
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<TrashSolid />
|
|
140
|
+
</VscodeButton>
|
|
141
|
+
<VscodeButton onClick={onUpdateVariable}>Update Value</VscodeButton>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|