@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
|
@@ -26,29 +26,43 @@ import type {
|
|
|
26
26
|
RemoveLinkMessage,
|
|
27
27
|
DirectExecuteNodeMessage
|
|
28
28
|
} from '../graphrunner/types.js';
|
|
29
|
-
import type {
|
|
29
|
+
import type {
|
|
30
|
+
ITransport,
|
|
31
|
+
IExecutionControl,
|
|
32
|
+
TransportState
|
|
33
|
+
} from '../graphrunner/transport.js';
|
|
30
34
|
import {
|
|
31
35
|
Engine,
|
|
32
36
|
type GraphInstance,
|
|
33
37
|
type ILifecycleEventEmitter,
|
|
34
38
|
readGraphFromJSON,
|
|
35
39
|
validateGraph,
|
|
36
|
-
ManualLifecycleEventEmitter,
|
|
37
40
|
DefaultLogger,
|
|
38
41
|
type ILogger,
|
|
39
42
|
Link,
|
|
40
|
-
makeGraphApi
|
|
43
|
+
makeGraphApi,
|
|
44
|
+
runSubgraph,
|
|
45
|
+
DEFAULT_SUBGRAPH_MAX_DEPTH
|
|
46
|
+
} from '@kiberon-labs/behave-graph';
|
|
47
|
+
import type {
|
|
48
|
+
IRegistry,
|
|
49
|
+
IGraphApi,
|
|
50
|
+
GraphJSON
|
|
41
51
|
} from '@kiberon-labs/behave-graph';
|
|
42
|
-
import type { IRegistry } from '@kiberon-labs/behave-graph';
|
|
43
52
|
import type { StoreApi } from 'zustand';
|
|
44
53
|
import type { LocalGraphRunnerStore } from './store.js';
|
|
45
54
|
import { sleep } from '@kiberon-labs/behave-graph';
|
|
46
55
|
import {
|
|
56
|
+
setupTracing,
|
|
47
57
|
setupVariableChangeTracking,
|
|
58
|
+
prepareRegistryWithDependencies,
|
|
48
59
|
handleGetServerVariables,
|
|
49
60
|
handleGetServerEvents,
|
|
50
61
|
handleGetSocketConstraints,
|
|
51
|
-
handleGetNodeTypes
|
|
62
|
+
handleGetNodeTypes,
|
|
63
|
+
executeGraphLifecycle,
|
|
64
|
+
type ActiveRun as BaseActiveRun,
|
|
65
|
+
type MessageContext
|
|
52
66
|
} from './execution-utils.js';
|
|
53
67
|
import {
|
|
54
68
|
SessionManager,
|
|
@@ -58,31 +72,22 @@ import {
|
|
|
58
72
|
} from '../graphrunner/session.js';
|
|
59
73
|
import { createNode } from '@kiberon-labs/behave-graph';
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Local run record. Extends the shared {@link BaseActiveRun} (used by both the
|
|
77
|
+
* local and worker runners) with the session + tick bookkeeping that only the
|
|
78
|
+
* local, interactively-controllable transport needs.
|
|
79
|
+
*/
|
|
80
|
+
interface ActiveRun extends BaseActiveRun {
|
|
64
81
|
sessionId: string;
|
|
65
|
-
engine: Engine;
|
|
66
|
-
graphInstance: GraphInstance;
|
|
67
|
-
registry: IRegistry;
|
|
68
|
-
status: RunStatus;
|
|
69
|
-
startedAt: number;
|
|
70
|
-
performance: {
|
|
71
|
-
nodesExecuted: number;
|
|
72
|
-
eventsEmitted: number;
|
|
73
|
-
variableChanges: number;
|
|
74
|
-
};
|
|
75
|
-
// Step execution control
|
|
76
|
-
isPaused: boolean;
|
|
77
|
-
executionPhase: 'start' | 'tick' | 'end' | 'completed';
|
|
78
|
-
currentTick: number;
|
|
79
82
|
maxTicks: number;
|
|
83
|
+
/** Whether this run finalizes when its flows drain (default: stay alive). */
|
|
84
|
+
autoEnd: boolean;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
/**
|
|
83
88
|
* Local transport that executes graphs in the browser using the Engine
|
|
84
89
|
*/
|
|
85
|
-
export class LocalTransport implements ITransport {
|
|
90
|
+
export class LocalTransport implements ITransport, IExecutionControl {
|
|
86
91
|
private state: TransportState = 'disconnected';
|
|
87
92
|
private messageHandlers: Array<(message: ServerGraphRunnerMessage) => void> =
|
|
88
93
|
[];
|
|
@@ -94,6 +99,7 @@ export class LocalTransport implements ITransport {
|
|
|
94
99
|
private store: StoreApi<LocalGraphRunnerStore> | null = null;
|
|
95
100
|
private variables: ServerVariable[];
|
|
96
101
|
private serverEvents: ServerEvent[];
|
|
102
|
+
private resolveGraph?: (id: string) => GraphJSON | undefined;
|
|
97
103
|
|
|
98
104
|
constructor(
|
|
99
105
|
registry: IRegistry,
|
|
@@ -102,6 +108,10 @@ export class LocalTransport implements ITransport {
|
|
|
102
108
|
variables?: ServerVariable[];
|
|
103
109
|
serverEvents?: ServerEvent[];
|
|
104
110
|
sessionFactory?: SessionFactory;
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a referenced graph's JSON by id, enabling Call Subgraph nodes.
|
|
113
|
+
*/
|
|
114
|
+
resolveGraph?: (id: string) => GraphJSON | undefined;
|
|
105
115
|
}
|
|
106
116
|
) {
|
|
107
117
|
this.registry = registry;
|
|
@@ -109,6 +119,7 @@ export class LocalTransport implements ITransport {
|
|
|
109
119
|
this.variables = options?.variables ?? [];
|
|
110
120
|
this.serverEvents = options?.serverEvents ?? [];
|
|
111
121
|
this.sessionManager = new SessionManager(options?.sessionFactory);
|
|
122
|
+
this.resolveGraph = options?.resolveGraph;
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
/**
|
|
@@ -438,29 +449,40 @@ export class LocalTransport implements ITransport {
|
|
|
438
449
|
};
|
|
439
450
|
}
|
|
440
451
|
|
|
441
|
-
if
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
// Inject the lifecycle event emitter (if absent) and the forwarding logger
|
|
453
|
+
// , shared with the worker runner.
|
|
454
|
+
registryToUse = prepareRegistryWithDependencies(
|
|
455
|
+
registryToUse,
|
|
456
|
+
transportLogger
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
// Inject the subgraph resolver so Call Subgraph nodes can run referenced
|
|
460
|
+
// graphs. runSubgraph builds a cycle/depth-guarded IGraphApi for nested
|
|
461
|
+
// calls; the active graph id seeds the call stack so a graph calling back
|
|
462
|
+
// to the active graph (or itself) is detected as a cycle and refused.
|
|
463
|
+
if (this.resolveGraph) {
|
|
464
|
+
const resolveGraph = this.resolveGraph;
|
|
465
|
+
const activeGraphId = message.graphId;
|
|
466
|
+
const graphApi: IGraphApi = {
|
|
467
|
+
getGraph: (id) => resolveGraph(id),
|
|
468
|
+
runGraph: (id, inputs) => {
|
|
469
|
+
const childGraph = resolveGraph(id);
|
|
470
|
+
return childGraph
|
|
471
|
+
? runSubgraph({
|
|
472
|
+
graphJson: childGraph,
|
|
473
|
+
registry: registryToUse,
|
|
474
|
+
inputs,
|
|
475
|
+
resolveGraph,
|
|
476
|
+
graphId: id,
|
|
477
|
+
stack: [activeGraphId],
|
|
478
|
+
maxDepth: DEFAULT_SUBGRAPH_MAX_DEPTH
|
|
479
|
+
})
|
|
480
|
+
: Promise.resolve({});
|
|
454
481
|
}
|
|
455
482
|
};
|
|
456
|
-
} else {
|
|
457
|
-
// Replace the existing logger with the transport logger
|
|
458
483
|
registryToUse = {
|
|
459
484
|
...registryToUse,
|
|
460
|
-
dependencies: {
|
|
461
|
-
...registryToUse.dependencies,
|
|
462
|
-
ILogger: transportLogger
|
|
463
|
-
}
|
|
485
|
+
dependencies: { ...registryToUse.dependencies, IGraphApi: graphApi }
|
|
464
486
|
};
|
|
465
487
|
}
|
|
466
488
|
|
|
@@ -508,7 +530,11 @@ export class LocalTransport implements ITransport {
|
|
|
508
530
|
isPaused: false,
|
|
509
531
|
executionPhase: 'start',
|
|
510
532
|
currentTick: 0,
|
|
511
|
-
maxTicks: Infinity // No limit - tick events run until stopped
|
|
533
|
+
maxTicks: Infinity, // No limit - tick events run until stopped
|
|
534
|
+
// Runs stay alive by default so event subscriptions (ai/onToolCall
|
|
535
|
+
// etc.) keep firing after the start flow drains; opting into autoEnd
|
|
536
|
+
// restores fire-and-forget completion.
|
|
537
|
+
autoEnd: executionOptions.autoEnd ?? false
|
|
512
538
|
};
|
|
513
539
|
|
|
514
540
|
this.activeRuns.set(runId, run);
|
|
@@ -541,37 +567,17 @@ export class LocalTransport implements ITransport {
|
|
|
541
567
|
sendError: (code, msg, details) => this.sendError(code, msg, details)
|
|
542
568
|
});
|
|
543
569
|
|
|
544
|
-
// Set up tracing
|
|
570
|
+
// Set up tracing , shared with the worker runner so the trace event shape
|
|
571
|
+
// and timestamps stay consistent across runners.
|
|
545
572
|
if (executionOptions.trace) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
this.
|
|
549
|
-
type: 'trace',
|
|
550
|
-
runId,
|
|
551
|
-
graphId: message.graphId,
|
|
552
|
-
nodeId: node.id,
|
|
553
|
-
event: 'start',
|
|
554
|
-
data: { typeName: node.description.typeName },
|
|
555
|
-
timestamp: Date.now() - run.startedAt
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
engine.onNodeExecutionEnd.addListener((node) => {
|
|
560
|
-
this.notifyMessage({
|
|
561
|
-
type: 'trace',
|
|
562
|
-
runId,
|
|
563
|
-
graphId: message.graphId,
|
|
564
|
-
nodeId: node.id,
|
|
565
|
-
event: 'end',
|
|
566
|
-
data: { typeName: node.description.typeName },
|
|
567
|
-
timestamp: Date.now() - run.startedAt
|
|
568
|
-
});
|
|
573
|
+
setupTracing(run, message.graphId, {
|
|
574
|
+
sendMessage: this.notifyMessage.bind(this),
|
|
575
|
+
sendError: (code, msg, details) => this.sendError(code, msg, details)
|
|
569
576
|
});
|
|
570
577
|
}
|
|
571
578
|
|
|
572
579
|
// Execute graph asynchronously
|
|
573
|
-
|
|
574
|
-
this.executeGraph(run, message.graphId, autoEnd, session);
|
|
580
|
+
this.executeGraph(run, message.graphId, run.autoEnd, session);
|
|
575
581
|
} catch (error) {
|
|
576
582
|
const errorMessage =
|
|
577
583
|
error instanceof Error ? error.message : String(error);
|
|
@@ -600,124 +606,64 @@ export class LocalTransport implements ITransport {
|
|
|
600
606
|
autoEnd: boolean,
|
|
601
607
|
session: Session
|
|
602
608
|
): Promise<void> {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
// Execute start event
|
|
610
|
-
if (run.executionPhase === 'start') {
|
|
611
|
-
if (
|
|
612
|
-
eventEmitter?.startEvent &&
|
|
613
|
-
eventEmitter.startEvent.listenerCount > 0
|
|
614
|
-
) {
|
|
615
|
-
eventEmitter.startEvent.emit();
|
|
616
|
-
await this.executeWithPauseSupport(run);
|
|
617
|
-
}
|
|
618
|
-
run.executionPhase = 'tick';
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// Execute tick events (runs indefinitely until stopped)
|
|
622
|
-
if (run.executionPhase === 'tick') {
|
|
623
|
-
if (
|
|
624
|
-
eventEmitter?.tickEvent &&
|
|
625
|
-
eventEmitter.tickEvent.listenerCount > 0
|
|
626
|
-
) {
|
|
627
|
-
// Get tick strategy hook or create default
|
|
628
|
-
const tickStrategy =
|
|
629
|
-
session.config.tickStrategy ||
|
|
630
|
-
this.createSleepTickStrategy(
|
|
631
|
-
session.config.executionSettings?.tickInterval ??
|
|
632
|
-
this.getTickInterval()
|
|
633
|
-
);
|
|
634
|
-
|
|
635
|
-
while (!run.isPaused && run.status === 'running') {
|
|
636
|
-
eventEmitter.tickEvent.emit();
|
|
637
|
-
await this.executeWithPauseSupport(run);
|
|
638
|
-
run.currentTick++;
|
|
639
|
-
|
|
640
|
-
if (run.isPaused || run.status !== 'running') {
|
|
641
|
-
return; // Exit early if paused or stopped
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// Call the tick strategy hook to handle timing
|
|
645
|
-
await tickStrategy();
|
|
646
|
-
}
|
|
647
|
-
} else {
|
|
648
|
-
// No tick event listeners, move to end phase
|
|
649
|
-
run.executionPhase = 'end';
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Execute end event
|
|
654
|
-
if (run.executionPhase === 'end' && !run.isPaused) {
|
|
655
|
-
if (eventEmitter?.endEvent && eventEmitter.endEvent.listenerCount > 0) {
|
|
656
|
-
eventEmitter.endEvent.emit();
|
|
657
|
-
await this.executeWithPauseSupport(run);
|
|
658
|
-
}
|
|
659
|
-
run.executionPhase = 'completed';
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// Only complete if not paused
|
|
663
|
-
if (!run.isPaused && !autoEnd) {
|
|
664
|
-
// Run completed successfully
|
|
665
|
-
run.status = 'completed';
|
|
666
|
-
const elapsedMs = Date.now() - run.startedAt;
|
|
667
|
-
const result = null; // Placeholder for result
|
|
668
|
-
|
|
669
|
-
// Call session hook for run completed
|
|
670
|
-
if (session.config.hooks?.onRunCompleted) {
|
|
671
|
-
await session.config.hooks.onRunCompleted(
|
|
672
|
-
session,
|
|
673
|
-
run.runId,
|
|
674
|
-
graphId,
|
|
675
|
-
result
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
this.notifyMessage({
|
|
680
|
-
type: 'completed',
|
|
681
|
-
runId: run.runId,
|
|
682
|
-
graphId,
|
|
683
|
-
completedAt: Date.now(),
|
|
684
|
-
elapsedMs,
|
|
685
|
-
result,
|
|
686
|
-
performance: run.performance
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// Cleanup
|
|
690
|
-
run.engine.dispose();
|
|
691
|
-
this.activeRuns.delete(run.runId);
|
|
692
|
-
this.sessionManager.removeRunFromSession(run.sessionId, run.runId);
|
|
693
|
-
this.updateStoreActiveRuns();
|
|
694
|
-
this.updateStoreExecutionState(false, false);
|
|
695
|
-
}
|
|
696
|
-
} catch (error) {
|
|
697
|
-
run.status = 'error';
|
|
698
|
-
const errorMessage =
|
|
699
|
-
error instanceof Error ? error.message : String(error);
|
|
700
|
-
|
|
701
|
-
// Call session hook for run error
|
|
702
|
-
if (session.config.hooks?.onRunError) {
|
|
703
|
-
await session.config.hooks.onRunError(
|
|
704
|
-
session,
|
|
705
|
-
run.runId,
|
|
706
|
-
graphId,
|
|
707
|
-
error instanceof Error ? error : new Error(errorMessage)
|
|
708
|
-
);
|
|
709
|
-
}
|
|
609
|
+
const ctx: MessageContext = {
|
|
610
|
+
sendMessage: this.notifyMessage.bind(this),
|
|
611
|
+
sendError: (code, message, details) =>
|
|
612
|
+
this.sendError(code, message, details)
|
|
613
|
+
};
|
|
710
614
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
615
|
+
// Tick timing: the session's custom strategy if provided, else a sleep based
|
|
616
|
+
// on the configured tick interval.
|
|
617
|
+
const tickStrategy =
|
|
618
|
+
session.config.tickStrategy ||
|
|
619
|
+
this.createSleepTickStrategy(
|
|
620
|
+
session.config.executionSettings?.tickInterval ?? this.getTickInterval()
|
|
621
|
+
);
|
|
715
622
|
|
|
716
|
-
|
|
623
|
+
// Tear the run down and re-sync the panel's running / active-runs state.
|
|
624
|
+
const cleanup = (): void => {
|
|
717
625
|
this.activeRuns.delete(run.runId);
|
|
718
626
|
this.sessionManager.removeRunFromSession(run.sessionId, run.runId);
|
|
719
627
|
this.updateStoreActiveRuns();
|
|
720
628
|
this.updateStoreExecutionState(false, false);
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
try {
|
|
632
|
+
await executeGraphLifecycle(run, graphId, ctx, {
|
|
633
|
+
autoEnd,
|
|
634
|
+
// Pause-aware executor that also honours the local step-delay / speed.
|
|
635
|
+
executeStep: () => this.executeWithPauseSupport(run),
|
|
636
|
+
tickStrategy,
|
|
637
|
+
onComplete: async () => {
|
|
638
|
+
if (session.config.hooks?.onRunCompleted) {
|
|
639
|
+
await session.config.hooks.onRunCompleted(
|
|
640
|
+
session,
|
|
641
|
+
run.runId,
|
|
642
|
+
graphId,
|
|
643
|
+
null
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
cleanup();
|
|
647
|
+
},
|
|
648
|
+
onError: async (error) => {
|
|
649
|
+
if (session.config.hooks?.onRunError) {
|
|
650
|
+
await session.config.hooks.onRunError(
|
|
651
|
+
session,
|
|
652
|
+
run.runId,
|
|
653
|
+
graphId,
|
|
654
|
+
error
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
this.sendError('NODE_EXECUTION_ERROR', error.message, {
|
|
658
|
+
runId: run.runId,
|
|
659
|
+
graphId
|
|
660
|
+
});
|
|
661
|
+
cleanup();
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
} catch {
|
|
665
|
+
// The error was already reported + cleaned up by the onError hook; this
|
|
666
|
+
// method is fire-and-forget, so swallow the lifecycle's rethrow.
|
|
721
667
|
}
|
|
722
668
|
}
|
|
723
669
|
|
|
@@ -737,11 +683,26 @@ export class LocalTransport implements ITransport {
|
|
|
737
683
|
this.store?.getState().executionSpeed ??
|
|
738
684
|
1.0;
|
|
739
685
|
|
|
740
|
-
const stepLimit = this.getExecutionStepLimit();
|
|
741
686
|
const delay =
|
|
742
687
|
stepDelay + (executionSpeed < 1.0 ? (1.0 - executionSpeed) * 100 : 0);
|
|
743
688
|
|
|
744
|
-
//
|
|
689
|
+
// Full-speed path: no artificial delay requested, so drain in large chunks.
|
|
690
|
+
// The single-step limit below exists only to interleave the step delay; at
|
|
691
|
+
// full speed it forced an executeAllAsync call (time checks, promise churn)
|
|
692
|
+
// per node, which dominated the per-tick cost. Chunking keeps pause
|
|
693
|
+
// responsive (checked between chunks) while the engine's sync fast path
|
|
694
|
+
// runs unhindered within a chunk.
|
|
695
|
+
if (delay <= 0) {
|
|
696
|
+
const CHUNK_STEPS = 8192;
|
|
697
|
+
while (run.engine.hasPending() && !run.isPaused) {
|
|
698
|
+
await run.engine.executeAllAsync(5, CHUNK_STEPS);
|
|
699
|
+
}
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const stepLimit = this.getExecutionStepLimit();
|
|
704
|
+
|
|
705
|
+
// Slow-motion path: execute step by step, sleeping between steps.
|
|
745
706
|
while (run.engine.hasPending() && !run.isPaused) {
|
|
746
707
|
// Execute limited number of steps
|
|
747
708
|
await run.engine.executeAllAsync(5, stepLimit);
|
|
@@ -783,7 +744,7 @@ export class LocalTransport implements ITransport {
|
|
|
783
744
|
|
|
784
745
|
run.isPaused = false;
|
|
785
746
|
// Continue execution from where we left off
|
|
786
|
-
await this.executeGraph(run, run.graphId,
|
|
747
|
+
await this.executeGraph(run, run.graphId, run.autoEnd, session);
|
|
787
748
|
}
|
|
788
749
|
|
|
789
750
|
/**
|
|
@@ -853,6 +814,7 @@ export class LocalTransport implements ITransport {
|
|
|
853
814
|
|
|
854
815
|
// Run completed successfully
|
|
855
816
|
run.status = 'completed';
|
|
817
|
+
run.flushTracing?.();
|
|
856
818
|
const elapsedMs = Date.now() - run.startedAt;
|
|
857
819
|
const result = null;
|
|
858
820
|
|
|
@@ -881,6 +843,10 @@ export class LocalTransport implements ITransport {
|
|
|
881
843
|
run.engine.dispose();
|
|
882
844
|
this.activeRuns.delete(run.runId);
|
|
883
845
|
this.sessionManager.removeRunFromSession(run.sessionId, run.runId);
|
|
846
|
+
// Keep the panel's running/active-runs state in sync when stepping
|
|
847
|
+
// reaches the end of the graph.
|
|
848
|
+
this.updateStoreActiveRuns();
|
|
849
|
+
this.updateStoreExecutionState(false, false);
|
|
884
850
|
}
|
|
885
851
|
}
|
|
886
852
|
}
|
|
@@ -903,6 +869,7 @@ export class LocalTransport implements ITransport {
|
|
|
903
869
|
}
|
|
904
870
|
|
|
905
871
|
run.status = 'stopped';
|
|
872
|
+
run.flushTracing?.();
|
|
906
873
|
this.updateStoreActiveRuns();
|
|
907
874
|
this.updateStoreExecutionState(false, false);
|
|
908
875
|
run.engine.dispose();
|
|
@@ -411,6 +411,7 @@ export function initializeGraphWorker(options: GraphWorkerOptions): void {
|
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
run.status = 'stopped';
|
|
414
|
+
run.flushTracing?.();
|
|
414
415
|
run.engine.dispose();
|
|
415
416
|
activeRuns.delete(message.runId);
|
|
416
417
|
|
|
@@ -570,6 +571,7 @@ export function initializeGraphWorker(options: GraphWorkerOptions): void {
|
|
|
570
571
|
}
|
|
571
572
|
|
|
572
573
|
run.status = 'stopped';
|
|
574
|
+
run.flushTracing?.();
|
|
573
575
|
run.engine.dispose();
|
|
574
576
|
activeRuns.delete(message.runId);
|
|
575
577
|
|
|
@@ -12,7 +12,6 @@ import { webWorkerGraphRunnerStoreFactory } from './store.js';
|
|
|
12
12
|
import { WebWorkerGraphRunnerPanel } from './panel.js';
|
|
13
13
|
import { MenuItemElement } from '../../components/menubar/menuItem.js';
|
|
14
14
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
15
|
-
import { setupClientEventListeners } from '../graphrunner/actions.js';
|
|
16
15
|
|
|
17
16
|
export * from './worker-transport.js';
|
|
18
17
|
export * from './store.js';
|
|
@@ -76,20 +75,15 @@ export async function webWorkerGraphRunnerPluginLoader(
|
|
|
76
75
|
}
|
|
77
76
|
});
|
|
78
77
|
|
|
79
|
-
// Register the graph runner client plugin
|
|
80
|
-
//
|
|
78
|
+
// Register the graph runner client plugin. With skipAutoConnect:false the
|
|
79
|
+
// plugin calls runner.connect(), which wires the persistent client event
|
|
80
|
+
// listeners (trace/logs/lifecycle) on this same client , so we must NOT wire
|
|
81
|
+
// them again here, or every trace span would be recorded twice.
|
|
81
82
|
await system.registerPlugin(graphRunnerClientPlugin, {
|
|
82
83
|
client,
|
|
83
84
|
skipAutoConnect: false
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
// Setup persistent event listeners for trace, logs, and run completion
|
|
87
|
-
// Access the store from the system after it's been registered by the plugin
|
|
88
|
-
const graphRunnerStore = system.runner.store;
|
|
89
|
-
if (graphRunnerStore) {
|
|
90
|
-
setupClientEventListeners(client, system, graphRunnerStore);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
87
|
// Register the web worker graph runner panel
|
|
94
88
|
system.tabLoader.register('webWorkerGraphRunner', () => {
|
|
95
89
|
return {
|
|
@@ -87,3 +87,12 @@ export const webWorkerGraphRunnerStoreFactory =
|
|
|
87
87
|
setStepDelay: (delay) => set({ stepDelay: delay }),
|
|
88
88
|
setExecutionSpeed: (speed) => set({ executionSpeed: speed })
|
|
89
89
|
}));
|
|
90
|
+
|
|
91
|
+
declare module '@/system/system' {
|
|
92
|
+
interface System {
|
|
93
|
+
/**
|
|
94
|
+
* Web Worker Graph Runner store
|
|
95
|
+
*/
|
|
96
|
+
webWorkerGraphRunnerStore: StoreApi<WebWorkerGraphRunnerStore>;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { plugin } from '@/system/plugin';
|
|
2
|
+
import type { System } from '@/system/system';
|
|
3
|
+
import { docsPlugin } from '@/plugin/docs';
|
|
4
|
+
import { alignmentPlugin } from '@/plugin/alignment';
|
|
5
|
+
import { layoutPlugin } from '@/plugin/layout';
|
|
6
|
+
import { notesPlugin } from '@/plugin/notes';
|
|
7
|
+
import { autosavePlugin } from '@/plugin/autosave';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Batteries-included bundle of the standard editor plugins. Register this once
|
|
11
|
+
* instead of wiring each plugin by hand:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const system = new System(registry);
|
|
15
|
+
* system.registerPlugin(kitchenSinkPlugin);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* It currently pulls in:
|
|
19
|
+
* - {@link docsPlugin} — the in-editor node documentation browser;
|
|
20
|
+
* - {@link alignmentPlugin} — node alignment + distribution;
|
|
21
|
+
* - {@link layoutPlugin} — Dagre/ELK auto-layout (heavy deps, opt-in);
|
|
22
|
+
* - {@link notesPlugin} — markdown note nodes (tiptap/prosemirror, opt-in);
|
|
23
|
+
* - {@link autosavePlugin} — client-side local backups of open graphs.
|
|
24
|
+
*
|
|
25
|
+
* It intentionally does **not** register a graph runner: runners
|
|
26
|
+
* ({@link localGraphRunnerPlugin}, the remote client, ...) need host-specific
|
|
27
|
+
* options (a node registry, transport, ...) so hosts wire those themselves.
|
|
28
|
+
*/
|
|
29
|
+
export const kitchenSinkPlugin = plugin(
|
|
30
|
+
async (system: System) => {
|
|
31
|
+
await system.registerPlugin(docsPlugin);
|
|
32
|
+
await system.registerPlugin(alignmentPlugin);
|
|
33
|
+
await system.registerPlugin(layoutPlugin);
|
|
34
|
+
await system.registerPlugin(notesPlugin);
|
|
35
|
+
await system.registerPlugin(autosavePlugin);
|
|
36
|
+
},
|
|
37
|
+
{ name: 'kitchen-sink' }
|
|
38
|
+
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Position } from 'reactflow';
|
|
2
2
|
import type { Edge, Node } from 'reactflow';
|
|
3
|
-
import
|
|
3
|
+
import type dagreNS from 'dagre';
|
|
4
4
|
import type { System } from '@/system';
|
|
5
5
|
import { pinned } from '@/annotations';
|
|
6
6
|
|
|
@@ -11,6 +11,18 @@ export type Options = {
|
|
|
11
11
|
direction: Direction;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* dagre is only needed when the user actually runs a Dagre layout, so load it
|
|
16
|
+
* lazily (a dynamic import the bundler code-splits into a separate chunk). This
|
|
17
|
+
* keeps it out of the initial load even for hosts that register the layout
|
|
18
|
+
* plugin. The module is fetched once and reused.
|
|
19
|
+
*/
|
|
20
|
+
let dagrePromise: Promise<typeof dagreNS> | undefined;
|
|
21
|
+
const getDagre = () => {
|
|
22
|
+
dagrePromise ??= import('dagre').then((m) => m.default ?? m);
|
|
23
|
+
return dagrePromise;
|
|
24
|
+
};
|
|
25
|
+
|
|
14
26
|
const getDimensions = (node: Node) => {
|
|
15
27
|
return {
|
|
16
28
|
width: node.style?.width ?? node.width ?? 300,
|
|
@@ -18,9 +30,6 @@ const getDimensions = (node: Node) => {
|
|
|
18
30
|
};
|
|
19
31
|
};
|
|
20
32
|
|
|
21
|
-
const dagreGraph = new dagre.graphlib!.Graph();
|
|
22
|
-
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
23
|
-
|
|
24
33
|
const positionMap: Record<string, Position> = {
|
|
25
34
|
T: Position.Top,
|
|
26
35
|
L: Position.Left,
|
|
@@ -28,7 +37,7 @@ const positionMap: Record<string, Position> = {
|
|
|
28
37
|
B: Position.Bottom
|
|
29
38
|
};
|
|
30
39
|
|
|
31
|
-
export function applyDagreLayout(
|
|
40
|
+
export async function applyDagreLayout(
|
|
32
41
|
system: System,
|
|
33
42
|
options: Options | undefined = { direction: 'LR' }
|
|
34
43
|
) {
|
|
@@ -40,6 +49,9 @@ export function applyDagreLayout(
|
|
|
40
49
|
return;
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
const dagre = await getDagre();
|
|
53
|
+
const dagreGraph = new dagre.graphlib!.Graph();
|
|
54
|
+
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
43
55
|
dagreGraph.setGraph({ rankdir: direction });
|
|
44
56
|
|
|
45
57
|
// Add nodes to layout: exclude pinned nodes and child nodes inside groups
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import type { System } from '@/system';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import type {
|
|
3
|
+
ElkExtendedEdge,
|
|
4
|
+
ElkNode,
|
|
5
|
+
ElkPort
|
|
6
6
|
} from 'elkjs/lib/elk.bundled.js';
|
|
7
7
|
import type { Edge, Node } from 'reactflow';
|
|
8
8
|
import { pinned } from '@/annotations';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* elkjs is ~1.4 MB — by far the largest dependency the editor can pull in, yet
|
|
12
|
+
* it is only used when the user explicitly runs an ELK layout. Load it lazily (a
|
|
13
|
+
* dynamic import the bundler code-splits into a separate chunk) so it stays out
|
|
14
|
+
* of the initial load even for consumers of this layout plugin. The instance is
|
|
15
|
+
* created once and reused.
|
|
16
|
+
*/
|
|
17
|
+
let elkPromise:
|
|
18
|
+
| Promise<{ layout: (graph: ElkNode) => Promise<ElkNode> }>
|
|
19
|
+
| undefined;
|
|
20
|
+
const getElk = () => {
|
|
21
|
+
elkPromise ??= import('elkjs/lib/elk.bundled.js').then(
|
|
22
|
+
(m) => new m.default()
|
|
23
|
+
);
|
|
24
|
+
return elkPromise;
|
|
25
|
+
};
|
|
26
|
+
|
|
10
27
|
const layoutOptions = {
|
|
11
28
|
// 'elk.algorithm': 'layered',
|
|
12
29
|
'elk.direction': 'RIGHT',
|
|
@@ -20,8 +37,6 @@ export type LayoutAlgorithm =
|
|
|
20
37
|
| 'org.eclipse.elk.force'
|
|
21
38
|
| 'org.eclipse.elk.rectpacking';
|
|
22
39
|
|
|
23
|
-
const elk = new ELK();
|
|
24
|
-
|
|
25
40
|
const getLayoutedNodes = async (
|
|
26
41
|
nodes: Node[],
|
|
27
42
|
edges: Edge[],
|
|
@@ -107,6 +122,7 @@ const getLayoutedNodes = async (
|
|
|
107
122
|
)
|
|
108
123
|
};
|
|
109
124
|
|
|
125
|
+
const elk = await getElk();
|
|
110
126
|
const layoutedGraph = await elk.layout(graph);
|
|
111
127
|
|
|
112
128
|
const layoutedNodes = nodes.map((node) => {
|