@kiberon-labs/behave-graph-flow 2.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/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +49 -1
- package/.storybook/styles.css +9 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +368 -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/index.css +36 -33
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1865 -550
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14357 -11221
- package/dist/index.js.map +1 -1
- 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/protocol.md +43 -20
- package/package.json +5 -9
- package/src/components/FloatingToolbar/index.module.css +5 -13
- package/src/components/FloatingToolbar/index.tsx +9 -9
- package/src/components/Flow.tsx +34 -23
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +13 -13
- package/src/components/contextMenus/edge.tsx +9 -95
- package/src/components/contextMenus/node.tsx +9 -149
- package/src/components/contextMenus/selection.tsx +5 -71
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +13 -2
- package/src/components/edges/index.tsx +75 -69
- package/src/components/layoutController/index.module.css +3 -0
- package/src/components/layoutController/index.tsx +24 -1
- package/src/components/layoutController/utils.ts +46 -3
- package/src/components/menubar/defaults.tsx +55 -19
- package/src/components/menubar/menuItem.module.css +18 -3
- package/src/components/menubar/menuItem.tsx +34 -1
- package/src/components/nodes/behave/NodeContainer.module.css +26 -25
- package/src/components/nodes/group/index.tsx +3 -3
- package/src/components/nodes/wrapper/styles.module.css +6 -32
- package/src/components/panels/alignment/index.module.css +0 -10
- package/src/components/panels/alignment/index.tsx +4 -4
- package/src/components/panels/base/styles.module.css +2 -2
- 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 +14 -5
- package/src/components/panels/events/ManageEventsPanel.tsx +11 -8
- package/src/components/panels/events/styles.module.css +6 -64
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +2 -2
- package/src/components/panels/history/styles.module.css +0 -9
- package/src/components/panels/keymaps/index.module.css +3 -13
- package/src/components/panels/keymaps/index.tsx +1 -2
- package/src/components/panels/layers/index.tsx +20 -15
- package/src/components/panels/layers/styles.module.css +9 -12
- package/src/components/panels/legend/index.tsx +1 -1
- package/src/components/panels/logs/index.module.css +25 -19
- package/src/components/panels/logs/index.tsx +7 -7
- package/src/components/panels/nodeInputs/InputsGroup.tsx +1 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +2 -2
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +1 -1
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +2 -12
- package/src/components/panels/nodeInputs/index.module.css +99 -75
- package/src/components/panels/nodeInputs/index.tsx +21 -11
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +2 -2
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +23 -43
- package/src/components/panels/nodePicker/index.tsx +8 -8
- package/src/components/panels/panel/index.module.css +7 -7
- package/src/components/panels/search/index.module.css +0 -50
- package/src/components/panels/search/index.tsx +2 -2
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +221 -176
- package/src/components/panels/systemSettings/styles.module.css +135 -8
- package/src/components/panels/traces/GridLines.tsx +1 -1
- package/src/components/panels/traces/TimeGrid.tsx +3 -3
- package/src/components/panels/traces/TraceLane.tsx +1 -1
- package/src/components/panels/traces/index.module.css +1 -8
- package/src/components/panels/traces/index.tsx +8 -4
- package/src/components/panels/traces/useDerivedSpans.ts +241 -146
- package/src/components/panels/traces/utils.ts +8 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +3 -3
- package/src/components/panels/variables/ManageVariablesScreen.tsx +12 -9
- package/src/components/panels/variables/index.tsx +2 -2
- package/src/components/panels/variables/styles.module.css +4 -91
- package/src/components/primitives/icon.module.css +4 -4
- package/src/components/sockets/input/index.tsx +9 -2
- package/src/components/sockets/input/styles.module.css +2 -3
- package/src/components/sockets/output/index.tsx +10 -3
- package/src/components/sockets/output/styles.module.css +1 -6
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +3 -3
- package/src/css/rc-dock.css +143 -43
- package/src/css/rc-menu.css +56 -55
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +197 -13
- package/src/css/vscode-elements.css +124 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +2 -2
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -0
- package/src/generators/SequenceGenerator.tsx +2 -2
- package/src/generators/SwitchOnIntegerGenerator.tsx +2 -2
- package/src/generators/SwitchOnStringGenerator.tsx +2 -2
- package/src/generators/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +21 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +2 -2
- package/src/hooks/useFlowHandlers.ts +47 -9
- package/src/hooks/useWasdPan.ts +26 -4
- package/src/index.css +4 -16
- package/src/index.ts +17 -0
- 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 +22 -12
- 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 +2 -4
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +15 -194
- package/src/plugin/docs/panel/styles.module.css +8 -8
- package/src/plugin/graphrunner/actions.ts +258 -185
- package/src/plugin/graphrunner/buttons.tsx +34 -26
- package/src/plugin/graphrunner/client.ts +4 -1
- package/src/plugin/graphrunner/index.tsx +29 -100
- package/src/plugin/graphrunner/panel.tsx +2 -2
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +21 -192
- package/src/plugin/graphrunner/store.ts +14 -24
- package/src/plugin/graphrunner/styles.module.css +17 -57
- package/src/plugin/graphrunner/transport.ts +26 -0
- package/src/plugin/graphrunner/types.ts +21 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +260 -80
- package/src/plugin/graphrunner-local/index.tsx +8 -2
- package/src/plugin/graphrunner-local/panel.tsx +131 -175
- package/src/plugin/graphrunner-local/styles.module.css +57 -76
- package/src/plugin/graphrunner-local/transport.ts +151 -184
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +2 -0
- package/src/plugin/graphrunner-webworker/index.tsx +4 -10
- package/src/plugin/graphrunner-webworker/store.ts +9 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/{layout/dagre.tsx → plugin/layout/dagre.ts} +17 -5
- package/src/{layout → plugin/layout}/elk.ts +22 -6
- 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 +58 -4
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +2 -2
- package/src/specifics/CustomEventTriggerSpecific.tsx +2 -2
- package/src/specifics/VariableGetSpecific.tsx +2 -2
- package/src/specifics/VariableSetSpecific.tsx +2 -2
- package/src/store/actions.tsx +5 -5
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/flow.tsx +23 -38
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +301 -260
- package/src/store/layers.ts +3 -3
- package/src/store/registry.ts +12 -4
- package/src/store/selection.ts +3 -3
- package/src/store/settings.ts +82 -82
- package/src/store/settingsSchema.ts +210 -0
- package/src/store/tabs.ts +5 -1
- package/src/store/traces.ts +3 -3
- package/src/system/graph.ts +11 -14
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +3 -0
- package/src/system/notifications.ts +13 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +28 -0
- package/src/system/provider.tsx +64 -0
- package/src/system/system.ts +518 -88
- package/src/system/tabLoader.tsx +70 -32
- package/src/system/undoRedo.ts +1 -1
- package/src/transformers/Uigraph.ts +5 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +13 -5
- package/src/types/nodes.ts +8 -3
- package/src/types.ts +2 -0
- package/src/util/autoConvert.ts +200 -0
- package/src/util/isValidConnection.ts +23 -2
- package/stories/defaults/defaultStoryProvider.tsx +17 -14
- package/stories/defaults/systemGenerator.ts +6 -1
- package/stories/{components/nodes/comment.stories.tsx → plugins/notes.stories.tsx} +24 -30
- 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/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +2 -1
- 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/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +7 -6
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -0
- package/tests/visual/README.md +2 -2
- 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-variables-chromium-win32.png +0 -0
- package/tests/visual/panels.visual.test.tsx +3 -3
- package/tests/wasdPan.test.ts +71 -0
- package/vitest.config.ts +1 -1
- package/vitest.visual.config.ts +7 -0
- package/.storybook/vscode.css +0 -814
- package/src/components/nodes/comment/FormatToolbar.tsx +0 -118
- package/src/components/nodes/comment/comment.tsx +0 -103
- package/src/components/nodes/comment/styles.module.css +0 -150
- package/src/components/panels/conversation/index.module.css +0 -151
- package/src/components/panels/conversation/index.tsx +0 -162
- package/src/components/panels/events/CustomEventsEditor.tsx +0 -384
- package/src/css/vscode.css +0 -13
- package/src/hooks/useDetachNodes.ts +0 -39
- package/src/plugin/graphrunner-webworker/types.ts +0 -17
- package/src/specifics/registerDefaultSpecifics.ts +0 -5
- package/src/store/chat.ts +0 -73
- package/src/store/graphRunnerClient.ts +0 -110
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
.header {
|
|
12
12
|
padding: 1rem;
|
|
13
|
-
border-bottom: 1px solid var(--
|
|
13
|
+
border-bottom: 1px solid var(--ds-input-border, var(--colors-borderSubtle));
|
|
14
14
|
background: var(--colors-bgPanel);
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
align-items: center;
|
|
29
29
|
justify-content: center;
|
|
30
30
|
border-radius: 0.5rem;
|
|
31
|
-
border: 1px solid var(--
|
|
31
|
+
border: 1px solid var(--ds-input-border, var(--colors-borderSubtle));
|
|
32
32
|
background: var(--colors-bgCanvas);
|
|
33
33
|
flex-shrink: 0;
|
|
34
34
|
}
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
font-size: 0.75rem;
|
|
63
63
|
padding: 0.25rem 0.5rem;
|
|
64
64
|
border-radius: 0.25rem;
|
|
65
|
-
background: var(--
|
|
66
|
-
color: var(--
|
|
65
|
+
background: var(--ds-badge-bg, #4d4d4d);
|
|
66
|
+
color: var(--ds-badge-fg, #ffffff);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.content {
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
line-height: 1.5;
|
|
78
78
|
margin-bottom: 1.5rem;
|
|
79
79
|
padding: 0.75rem;
|
|
80
|
-
background: var(--
|
|
81
|
-
border-left: 4px solid var(--
|
|
80
|
+
background: var(--ds-blockquote-bg, rgba(127, 127, 127, 0.1));
|
|
81
|
+
border-left: 4px solid var(--ds-blockquote-border, #007acc);
|
|
82
82
|
border-radius: 0.25rem;
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
font-weight: 600;
|
|
99
99
|
margin-bottom: 0.75rem;
|
|
100
100
|
padding-bottom: 0.25rem;
|
|
101
|
-
border-bottom: 1px solid var(--
|
|
101
|
+
border-bottom: 1px solid var(--ds-input-border, var(--colors-borderSubtle));
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
.socketList {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
padding: 0.5rem;
|
|
115
115
|
background: var(--colors-bgPanel);
|
|
116
116
|
border-radius: 0.25rem;
|
|
117
|
-
border: 1px solid var(--
|
|
117
|
+
border: 1px solid var(--ds-input-border, var(--colors-borderSubtle));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
.socketIcon {
|
|
@@ -1,17 +1,88 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { StoreApi } from 'zustand';
|
|
3
|
-
import type { GraphRunnerClientStore } from './store';
|
|
1
|
+
import type { GraphSession } from '@/system/graphSession';
|
|
4
2
|
import type { GraphRunnerClient } from './client';
|
|
3
|
+
import type { GraphRunner } from './runner';
|
|
5
4
|
import { executing } from '@/annotations';
|
|
6
|
-
import { sleep } from '@/util/sleep';
|
|
7
5
|
import { type ValueJSON } from '@kiberon-labs/behave-graph';
|
|
6
|
+
import type { TraceBatchEvent } from './types';
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Per-session batcher for the `executing` node annotation.
|
|
10
|
+
*
|
|
11
|
+
* Trace events arrive per node execution (start + end), which at display-rate
|
|
12
|
+
* ticking means hundreds of events per frame. Writing the annotation straight
|
|
13
|
+
* through did a full O(nodes) copy-map of the node array per event; instead,
|
|
14
|
+
* accumulate the net executing state here and apply it once per animation
|
|
15
|
+
* frame with a single identity-preserving setNodes pass.
|
|
16
|
+
*/
|
|
17
|
+
type ExecutingBatch = {
|
|
18
|
+
pending: Map<string, boolean>;
|
|
19
|
+
scheduled: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const executingBatches = new WeakMap<GraphSession, ExecutingBatch>();
|
|
23
|
+
|
|
24
|
+
const scheduleFrame = (cb: () => void): void => {
|
|
25
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
26
|
+
requestAnimationFrame(cb);
|
|
27
|
+
} else {
|
|
28
|
+
setTimeout(cb, 16);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function flushExecutingState(session: GraphSession, batch: ExecutingBatch) {
|
|
33
|
+
batch.scheduled = false;
|
|
34
|
+
if (batch.pending.size === 0) return;
|
|
35
|
+
const updates = batch.pending;
|
|
36
|
+
batch.pending = new Map();
|
|
37
|
+
|
|
38
|
+
session.nodeStore.getState().setNodes((nodes) => {
|
|
39
|
+
let changed = false;
|
|
40
|
+
const next = nodes.map((node) => {
|
|
41
|
+
if (!('data' in node)) return node;
|
|
42
|
+
const target = updates.get(node.id);
|
|
43
|
+
if (target === undefined) return node;
|
|
44
|
+
if (Boolean(node.data.annotations?.[executing]) === target) return node;
|
|
45
|
+
changed = true;
|
|
46
|
+
return {
|
|
47
|
+
...node,
|
|
48
|
+
data: {
|
|
49
|
+
...node.data,
|
|
50
|
+
annotations: {
|
|
51
|
+
...node.data.annotations,
|
|
52
|
+
[executing]: target
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
// Keep the original array identity when nothing changed so selector-based
|
|
58
|
+
// subscribers (the React Flow canvas) skip the re-render entirely.
|
|
59
|
+
return changed ? next : nodes;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function markExecuting(session: GraphSession, nodeId: string, state: boolean) {
|
|
64
|
+
let batch = executingBatches.get(session);
|
|
65
|
+
if (!batch) {
|
|
66
|
+
batch = { pending: new Map(), scheduled: false };
|
|
67
|
+
executingBatches.set(session, batch);
|
|
68
|
+
}
|
|
69
|
+
batch.pending.set(nodeId, state);
|
|
70
|
+
if (!batch.scheduled) {
|
|
71
|
+
batch.scheduled = true;
|
|
72
|
+
scheduleFrame(() => flushExecutingState(session, batch));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Clear executing state from all nodes of a session, dropping any queued
|
|
77
|
+
// per-node updates so a pending flush can't re-highlight after the run ended.
|
|
78
|
+
function clearAllExecutingStates(session: GraphSession) {
|
|
79
|
+
const batch = executingBatches.get(session);
|
|
80
|
+
if (batch) batch.pending.clear();
|
|
81
|
+
session.nodeStore.getState().setNodes((nodes) => {
|
|
82
|
+
let changed = false;
|
|
83
|
+
const next = nodes.map((node) => {
|
|
14
84
|
if ('data' in node && node.data.annotations?.[executing]) {
|
|
85
|
+
changed = true;
|
|
15
86
|
return {
|
|
16
87
|
...node,
|
|
17
88
|
data: {
|
|
@@ -24,95 +95,92 @@ async function clearAllExecutingStates(system: System) {
|
|
|
24
95
|
};
|
|
25
96
|
}
|
|
26
97
|
return node;
|
|
27
|
-
})
|
|
28
|
-
|
|
98
|
+
});
|
|
99
|
+
return changed ? next : nodes;
|
|
100
|
+
});
|
|
29
101
|
}
|
|
30
102
|
|
|
103
|
+
/** Apply one trace event to the session's trace spans + executing annotation. */
|
|
104
|
+
function processTraceEvent(session: GraphSession, ev: TraceBatchEvent) {
|
|
105
|
+
const traceStore = session.traceStore.getState();
|
|
106
|
+
if (ev.event === 'start') {
|
|
107
|
+
let name = ev.nodeId;
|
|
108
|
+
if (ev.data && typeof ev.data === 'object' && 'typeName' in ev.data) {
|
|
109
|
+
const typeName = (ev.data as { typeName?: unknown }).typeName;
|
|
110
|
+
if (typeof typeName === 'string') {
|
|
111
|
+
name = typeName;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
traceStore.addSpan({
|
|
115
|
+
nodeId: ev.nodeId,
|
|
116
|
+
name,
|
|
117
|
+
// `??` not `||`: the worker sends run-relative ms, so 0 is a valid (and
|
|
118
|
+
// common, for the first node) start , `||` fell back to the main thread's
|
|
119
|
+
// performance.now(), producing huge, wrong-clock timestamps.
|
|
120
|
+
start: ev.timestamp ?? performance.now(),
|
|
121
|
+
// Open span: NaN until the matching `end` event arrives. The store/render
|
|
122
|
+
// treat NaN as "still running"; a literal end let it render mis-sized.
|
|
123
|
+
end: Number.NaN
|
|
124
|
+
// lane omitted: let the store allocate/free lanes so concurrent spans
|
|
125
|
+
// stack instead of all piling into lane 0.
|
|
126
|
+
});
|
|
127
|
+
markExecuting(session, ev.nodeId, true);
|
|
128
|
+
} else if (ev.event === 'end') {
|
|
129
|
+
traceStore.updateSpan(ev.nodeId, {
|
|
130
|
+
end: ev.timestamp ?? performance.now()
|
|
131
|
+
});
|
|
132
|
+
markExecuting(session, ev.nodeId, false);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Clients that already have listeners attached. Guards against double-wiring when
|
|
138
|
+
* both the plugin's `runner.connect()` and a host (e.g. the webworker runner)
|
|
139
|
+
* call {@link setupClientEventListeners} on the same client , which would record
|
|
140
|
+
* every trace span, log and event twice.
|
|
141
|
+
*/
|
|
142
|
+
const wiredClients = new WeakSet<GraphRunnerClient>();
|
|
143
|
+
|
|
31
144
|
/**
|
|
32
|
-
* Setup persistent event listeners on the client
|
|
33
|
-
*
|
|
145
|
+
* Setup persistent event listeners on the shared client. Registered once when
|
|
146
|
+
* the client connects; every message is routed to the session that started its
|
|
147
|
+
* run (via {@link GraphRunner.runIndex}), so concurrent graphs stay isolated.
|
|
148
|
+
*
|
|
149
|
+
* Idempotent per client: calling it again with the same client is a no-op.
|
|
34
150
|
*/
|
|
35
151
|
export function setupClientEventListeners(
|
|
36
152
|
client: GraphRunnerClient,
|
|
37
|
-
|
|
38
|
-
store: StoreApi<GraphRunnerClientStore>
|
|
153
|
+
runner: GraphRunner
|
|
39
154
|
) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
const typeName = (message.data as { typeName?: unknown }).typeName;
|
|
54
|
-
if (typeof typeName === 'string') {
|
|
55
|
-
name = typeName;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
traceStore.addSpan({
|
|
59
|
-
nodeId: message.nodeId,
|
|
60
|
-
name,
|
|
61
|
-
start: message.timestamp || performance.now(),
|
|
62
|
-
end: 1,
|
|
63
|
-
lane: 0
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Mark node as executing
|
|
67
|
-
system.nodeStore.getState().setNodes((nodes) =>
|
|
68
|
-
nodes.map((node) =>
|
|
69
|
-
node.id === message.nodeId && 'data' in node
|
|
70
|
-
? {
|
|
71
|
-
...node,
|
|
72
|
-
data: {
|
|
73
|
-
...node.data,
|
|
74
|
-
annotations: {
|
|
75
|
-
...node.data.annotations,
|
|
76
|
-
[executing]: true
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
: node
|
|
81
|
-
)
|
|
82
|
-
);
|
|
83
|
-
} else if (message.event === 'end') {
|
|
84
|
-
traceStore.updateSpan(message.nodeId, {
|
|
85
|
-
end: message.timestamp || performance.now()
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
//Delay to allow UI to show executing state
|
|
89
|
-
await sleep(1);
|
|
90
|
-
|
|
91
|
-
// Mark node as no longer executing
|
|
92
|
-
system.nodeStore.getState().setNodes((nodes) =>
|
|
93
|
-
nodes.map((node) =>
|
|
94
|
-
node.id === message.nodeId && 'data' in node
|
|
95
|
-
? {
|
|
96
|
-
...node,
|
|
97
|
-
data: {
|
|
98
|
-
...node.data,
|
|
99
|
-
annotations: {
|
|
100
|
-
...node.data.annotations,
|
|
101
|
-
[executing]: false
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
: node
|
|
106
|
-
)
|
|
107
|
-
);
|
|
155
|
+
if (wiredClients.has(client)) return;
|
|
156
|
+
wiredClients.add(client);
|
|
157
|
+
|
|
158
|
+
// Resolve the session that owns a given run id, or null if unknown.
|
|
159
|
+
const sessionFor = (runId: string): GraphSession | null =>
|
|
160
|
+
runner.runIndex.get(runId)?.session ?? null;
|
|
161
|
+
|
|
162
|
+
// Batched trace events (one message per flush window, many events inside).
|
|
163
|
+
client.on('traceBatch', (message) => {
|
|
164
|
+
const session = sessionFor(message.runId);
|
|
165
|
+
if (!session) return;
|
|
166
|
+
for (const ev of message.events) {
|
|
167
|
+
processTraceEvent(session, ev);
|
|
108
168
|
}
|
|
109
169
|
});
|
|
110
170
|
|
|
171
|
+
// Single trace events , kept for remote servers that predate `traceBatch`.
|
|
172
|
+
client.on('trace', (message) => {
|
|
173
|
+
const session = sessionFor(message.runId);
|
|
174
|
+
if (!session) return;
|
|
175
|
+
processTraceEvent(session, message);
|
|
176
|
+
});
|
|
177
|
+
|
|
111
178
|
// Listen for log messages
|
|
112
179
|
client.on('log', (message) => {
|
|
113
|
-
const
|
|
180
|
+
const session = sessionFor(message.runId);
|
|
181
|
+
if (!session) return;
|
|
114
182
|
const formattedMessage = `[${message.runId}/${message.graphId}] ${message.message}${message.data !== undefined ? ` ${JSON.stringify(message.data)}` : ''}`;
|
|
115
|
-
|
|
183
|
+
session.logsStore.getState().append({
|
|
116
184
|
time: new Date(),
|
|
117
185
|
data: {
|
|
118
186
|
message: formattedMessage
|
|
@@ -121,133 +189,138 @@ export function setupClientEventListeners(
|
|
|
121
189
|
});
|
|
122
190
|
});
|
|
123
191
|
|
|
124
|
-
// Listen for variable change events from server
|
|
192
|
+
// Listen for variable change events from server. A tick-driven graph writes
|
|
193
|
+
// variables every frame, so coalesce to the latest value per variable and
|
|
194
|
+
// apply once per animation frame instead of one store write per change.
|
|
195
|
+
const pendingVariableUpdates = new Map<GraphSession, Map<string, unknown>>();
|
|
196
|
+
let variableFlushScheduled = false;
|
|
197
|
+
|
|
198
|
+
const flushVariableUpdates = () => {
|
|
199
|
+
variableFlushScheduled = false;
|
|
200
|
+
for (const [session, updates] of pendingVariableUpdates) {
|
|
201
|
+
const variableStore = session.variableStore.getState();
|
|
202
|
+
for (const [id, newValue] of updates) {
|
|
203
|
+
const existingVariable = variableStore.variables[id];
|
|
204
|
+
if (existingVariable) {
|
|
205
|
+
variableStore.setVariable(id, {
|
|
206
|
+
...existingVariable,
|
|
207
|
+
initialValue: newValue as ValueJSON
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
const inferredType = typeof newValue;
|
|
211
|
+
variableStore.setVariable(id, {
|
|
212
|
+
id,
|
|
213
|
+
name: id,
|
|
214
|
+
valueTypeName: inferredType === 'object' ? 'string' : inferredType,
|
|
215
|
+
initialValue: newValue as ValueJSON
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
pendingVariableUpdates.clear();
|
|
221
|
+
};
|
|
222
|
+
|
|
125
223
|
client.on('variableChanged', (message) => {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
});
|
|
138
|
-
} else {
|
|
139
|
-
// Create new variable if it doesn't exist
|
|
140
|
-
const inferredType = typeof message.newValue;
|
|
141
|
-
const newVariable = {
|
|
142
|
-
id,
|
|
143
|
-
name: message.variableName,
|
|
144
|
-
valueTypeName: inferredType === 'object' ? 'string' : inferredType,
|
|
145
|
-
initialValue: message.newValue as ValueJSON
|
|
146
|
-
};
|
|
147
|
-
variableStore.setVariable(id, newVariable);
|
|
224
|
+
const session = sessionFor(message.runId);
|
|
225
|
+
if (!session) return;
|
|
226
|
+
let updates = pendingVariableUpdates.get(session);
|
|
227
|
+
if (!updates) {
|
|
228
|
+
updates = new Map();
|
|
229
|
+
pendingVariableUpdates.set(session, updates);
|
|
230
|
+
}
|
|
231
|
+
updates.set(message.variableName, message.newValue);
|
|
232
|
+
if (!variableFlushScheduled) {
|
|
233
|
+
variableFlushScheduled = true;
|
|
234
|
+
scheduleFrame(flushVariableUpdates);
|
|
148
235
|
}
|
|
149
236
|
});
|
|
150
237
|
|
|
151
|
-
//
|
|
238
|
+
// Run lifecycle events
|
|
152
239
|
client.on('completed', (message) => {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
clearAllExecutingStates(system);
|
|
161
|
-
}
|
|
240
|
+
const controller = runner.runIndex.get(message.runId);
|
|
241
|
+
if (!controller) return;
|
|
242
|
+
controller.session.editor.notifications.success(
|
|
243
|
+
`Graph completed: ${message.graphId}`
|
|
244
|
+
);
|
|
245
|
+
controller.finishRun();
|
|
246
|
+
clearAllExecutingStates(controller.session);
|
|
162
247
|
});
|
|
163
248
|
|
|
164
249
|
client.on('error', (message) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
250
|
+
if (!message.runId) return;
|
|
251
|
+
const controller = runner.runIndex.get(message.runId);
|
|
252
|
+
if (!controller) return;
|
|
253
|
+
controller.session.editor.notifications.error(
|
|
254
|
+
`Graph failed: ${message.graphId}`
|
|
255
|
+
);
|
|
256
|
+
controller.finishRun();
|
|
257
|
+
clearAllExecutingStates(controller.session);
|
|
174
258
|
});
|
|
175
259
|
|
|
176
260
|
client.on('stopped', (message) => {
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
clearAllExecutingStates(system);
|
|
185
|
-
}
|
|
261
|
+
const controller = runner.runIndex.get(message.runId);
|
|
262
|
+
if (!controller) return;
|
|
263
|
+
controller.session.editor.notifications.info(
|
|
264
|
+
`Graph stopped: ${message.graphId}`
|
|
265
|
+
);
|
|
266
|
+
controller.finishRun();
|
|
267
|
+
clearAllExecutingStates(controller.session);
|
|
186
268
|
});
|
|
187
269
|
|
|
188
270
|
// Realtime state change listeners
|
|
189
271
|
client.on('nodeRemoved', (message) => {
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
nodes.filter((node) => node.id !== message.nodeId)
|
|
197
|
-
);
|
|
198
|
-
system.notifications.info(`Node removed: ${message.nodeId}`);
|
|
199
|
-
}
|
|
272
|
+
const session = sessionFor(message.runId);
|
|
273
|
+
if (!session) return;
|
|
274
|
+
session.nodeStore
|
|
275
|
+
.getState()
|
|
276
|
+
.setNodes((nodes) => nodes.filter((node) => node.id !== message.nodeId));
|
|
277
|
+
session.editor.notifications.info(`Node removed: ${message.nodeId}`);
|
|
200
278
|
});
|
|
201
279
|
|
|
202
280
|
client.on('linkCreated', (message) => {
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
281
|
+
const session = sessionFor(message.runId);
|
|
282
|
+
if (!session) return;
|
|
283
|
+
session.editor.notifications.info(
|
|
284
|
+
`Link created: ${message.fromNodeId}/${message.fromSocket} -> ${message.toNodeId}/${message.toSocket}`
|
|
285
|
+
);
|
|
209
286
|
});
|
|
210
287
|
|
|
211
288
|
client.on('linkRemoved', (message) => {
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
289
|
+
const session = sessionFor(message.runId);
|
|
290
|
+
if (!session) return;
|
|
291
|
+
session.editor.notifications.info(
|
|
292
|
+
`Link removed: ${message.fromNodeId}/${message.fromSocket} -> ${message.toNodeId}/${message.toSocket}`
|
|
293
|
+
);
|
|
218
294
|
});
|
|
219
295
|
|
|
220
296
|
client.on('nodeParamUpdated', (message) => {
|
|
221
|
-
const
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
}
|
|
297
|
+
const session = sessionFor(message.runId);
|
|
298
|
+
if (!session) return;
|
|
299
|
+
session.editor.notifications.info(`Parameter updated on ${message.nodeId}`);
|
|
225
300
|
});
|
|
226
301
|
|
|
227
302
|
client.on('affectedNodes', (message) => {
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
data
|
|
237
|
-
|
|
238
|
-
annotations
|
|
239
|
-
|
|
240
|
-
[executing]: true
|
|
241
|
-
}
|
|
303
|
+
const session = sessionFor(message.runId);
|
|
304
|
+
if (!session) return;
|
|
305
|
+
session.nodeStore.getState().setNodes((nodes) =>
|
|
306
|
+
nodes.map((node) => {
|
|
307
|
+
if (message.nodeIds.includes(node.id)) {
|
|
308
|
+
return {
|
|
309
|
+
...node,
|
|
310
|
+
data: {
|
|
311
|
+
...node.data,
|
|
312
|
+
annotations: {
|
|
313
|
+
...node.data?.annotations,
|
|
314
|
+
[executing]: true
|
|
242
315
|
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
)
|
|
251
|
-
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return node;
|
|
320
|
+
})
|
|
321
|
+
);
|
|
322
|
+
session.editor.notifications.info(
|
|
323
|
+
`Executing ${message.reason}: ${message.nodeIds.length} node(s)`
|
|
324
|
+
);
|
|
252
325
|
});
|
|
253
326
|
}
|
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
import { VscodeButton } from '@vscode-elements/react-elements';
|
|
2
|
-
import { useStore } from 'zustand';
|
|
2
|
+
import { useStore, type StoreApi } from 'zustand';
|
|
3
3
|
import { Play, PauseWindow, Square, ArrowRight } from 'iconoir-react';
|
|
4
|
-
import
|
|
4
|
+
import { useGraph } from '@/system/provider';
|
|
5
5
|
import type { GraphRunnerClientStore } from './store';
|
|
6
|
+
import type { GraphRunController } from './runController';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Execution controls for the graph in the surrounding tab. Connection state
|
|
10
|
+
* comes from the shared runner connection; run state comes from this graph's own
|
|
11
|
+
* run controller, so each open graph has independent controls.
|
|
12
|
+
*/
|
|
13
|
+
export const GraphRunnerButtons = () => {
|
|
14
|
+
const session = useGraph();
|
|
15
|
+
const controller = session.runController;
|
|
16
|
+
if (!controller) return null;
|
|
17
|
+
return (
|
|
18
|
+
<Buttons
|
|
19
|
+
controller={controller}
|
|
20
|
+
connectionStore={session.editor.runner.store}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const isPaused = useStore(store, (state) => state.isPaused);
|
|
25
|
+
const Buttons = ({
|
|
26
|
+
controller,
|
|
27
|
+
connectionStore
|
|
28
|
+
}: {
|
|
29
|
+
controller: GraphRunController;
|
|
30
|
+
connectionStore: StoreApi<GraphRunnerClientStore>;
|
|
31
|
+
}) => {
|
|
32
|
+
const connectionState = useStore(connectionStore, (s) => s.connectionState);
|
|
33
|
+
const isExecuting = useStore(controller.store, (s) => s.isExecuting);
|
|
34
|
+
const isPaused = useStore(controller.store, (s) => s.isPaused);
|
|
27
35
|
|
|
28
36
|
const isConnected = connectionState === 'connected';
|
|
29
37
|
|
|
@@ -34,7 +42,7 @@ export const GraphRunnerButtons = ({
|
|
|
34
42
|
secondary
|
|
35
43
|
iconOnly
|
|
36
44
|
title="Play Graph"
|
|
37
|
-
onClick={
|
|
45
|
+
onClick={() => controller.play()}
|
|
38
46
|
disabled={!isConnected}
|
|
39
47
|
>
|
|
40
48
|
<Play />
|
|
@@ -45,7 +53,7 @@ export const GraphRunnerButtons = ({
|
|
|
45
53
|
secondary
|
|
46
54
|
iconOnly
|
|
47
55
|
title="Pause Graph"
|
|
48
|
-
onClick={
|
|
56
|
+
onClick={() => controller.pause()}
|
|
49
57
|
disabled={!isConnected}
|
|
50
58
|
>
|
|
51
59
|
<PauseWindow />
|
|
@@ -57,7 +65,7 @@ export const GraphRunnerButtons = ({
|
|
|
57
65
|
secondary
|
|
58
66
|
iconOnly
|
|
59
67
|
title="Resume Graph"
|
|
60
|
-
onClick={
|
|
68
|
+
onClick={() => controller.resume()}
|
|
61
69
|
disabled={!isConnected}
|
|
62
70
|
>
|
|
63
71
|
<Play />
|
|
@@ -66,7 +74,7 @@ export const GraphRunnerButtons = ({
|
|
|
66
74
|
secondary
|
|
67
75
|
iconOnly
|
|
68
76
|
title="Step Forward"
|
|
69
|
-
onClick={
|
|
77
|
+
onClick={() => controller.step()}
|
|
70
78
|
disabled={!isConnected}
|
|
71
79
|
>
|
|
72
80
|
<ArrowRight />
|
|
@@ -77,7 +85,7 @@ export const GraphRunnerButtons = ({
|
|
|
77
85
|
secondary
|
|
78
86
|
iconOnly
|
|
79
87
|
title="Stop Graph"
|
|
80
|
-
onClick={
|
|
88
|
+
onClick={() => controller.stop()}
|
|
81
89
|
disabled={!isExecuting}
|
|
82
90
|
>
|
|
83
91
|
<Square />
|
|
@@ -301,7 +301,10 @@ export class GraphRunnerClient {
|
|
|
301
301
|
graph: options?.graph as GraphJSON,
|
|
302
302
|
inputs: options?.inputs,
|
|
303
303
|
options: {
|
|
304
|
-
|
|
304
|
+
// Default off , tracing is a debugging aid with a real per-node cost;
|
|
305
|
+
// callers opt in explicitly (the run controller passes the panel's
|
|
306
|
+
// "Enable execution tracing" preference).
|
|
307
|
+
trace: options?.trace ?? false
|
|
305
308
|
}
|
|
306
309
|
});
|
|
307
310
|
|