@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
package/src/system/system.ts
CHANGED
|
@@ -1,73 +1,95 @@
|
|
|
1
|
-
import { UndoManager } from './undoRedo';
|
|
2
1
|
import { type StoreApi } from 'zustand';
|
|
2
|
+
import { createStore } from 'zustand/vanilla';
|
|
3
3
|
import type { Edge, Node, Viewport } from 'reactflow';
|
|
4
4
|
import { tabStoreFactory, type TabStore } from '@/store/tabs';
|
|
5
5
|
import { TabLoader } from './tabLoader';
|
|
6
6
|
import {
|
|
7
7
|
systemSettingsFactory,
|
|
8
|
+
PERSISTED_SETTING_KEYS,
|
|
8
9
|
type SystemSettingsStore
|
|
9
10
|
} from '../store/settings.js';
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
settingsSchemaStoreFactory,
|
|
13
|
+
type SettingsSchemaStore,
|
|
14
|
+
type SettingDescriptor
|
|
15
|
+
} from '../store/settingsSchema.js';
|
|
11
16
|
import { legendStoreFactory, type LegendStore } from '@/store/legend';
|
|
12
17
|
import { menubarStoreFactory, type MenuBarStore } from '@/store/menubar';
|
|
13
18
|
import { hotKeyStoreFactory, type HotkeyStore } from '@/store/hotKeys';
|
|
14
19
|
import { PubSub } from './pubsub';
|
|
15
|
-
import {
|
|
16
|
-
edgeStoreFactory,
|
|
17
|
-
flowStoreFactory,
|
|
18
|
-
nodeStoreFactory,
|
|
19
|
-
type EdgeStore,
|
|
20
|
-
type FlowStore,
|
|
21
|
-
type NodeStore
|
|
22
|
-
} from '@/store/flow';
|
|
23
20
|
import { controlsStoreFactory, type ControlsStore } from '@/store/controls';
|
|
24
|
-
import { variableStoreFactory, type VariableStore } from '@/store/variables';
|
|
25
21
|
import { selectionStoreFactory, type SelectionStore } from '@/store/selection';
|
|
26
|
-
import { refStoreFactory, type RefStore } from '@/store/refs';
|
|
27
|
-
import { Graph } from './graph';
|
|
28
|
-
import { actionStoreFactory, type ActionStore } from '@/store/actions';
|
|
29
22
|
import { registryStoreFactory, type RegistryStore } from '@/store/registry';
|
|
30
23
|
import { specsStoreFactory, type SpecsStore } from '@/store/specs';
|
|
31
|
-
import { traceStoreFactory, type TraceStore } from '@/store/traces';
|
|
32
24
|
import { specificStoreFactory, type SpecificStore } from '@/store/specific';
|
|
33
25
|
import {
|
|
34
26
|
socketGeneratorStoreFactory,
|
|
35
27
|
type SocketGeneratorStore
|
|
36
28
|
} from '@/store/socketGenerator';
|
|
37
|
-
import { eventsStoreFactory, type EventsStore } from '@/store/events';
|
|
38
29
|
import {
|
|
39
30
|
documentationStoreFactory,
|
|
40
31
|
type DocumentationStore
|
|
41
32
|
} from '@/store/documentation';
|
|
42
33
|
import { toolbarStoreFactory, type ToolbarStore } from '@/store/toolbar';
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
import { Notifications, type NotificationData } from './notifications';
|
|
35
|
+
import {
|
|
36
|
+
conversionStoreFactory,
|
|
37
|
+
type ConversionStore,
|
|
38
|
+
type ConversionRule
|
|
39
|
+
} from '@/store/conversions';
|
|
40
|
+
import {
|
|
41
|
+
commandStoreFactory,
|
|
42
|
+
registerDefaultCommands,
|
|
43
|
+
type CommandStore,
|
|
44
|
+
type CommandContext
|
|
45
|
+
} from '@/store/commands';
|
|
46
|
+
import {
|
|
47
|
+
contextMenuStoreFactory,
|
|
48
|
+
registerDefaultContextMenu,
|
|
49
|
+
type ContextMenuStore
|
|
50
|
+
} from '@/store/contextMenu';
|
|
51
|
+
import { GraphSession } from './graphSession';
|
|
52
|
+
import { installPersistence, type PersistenceAdapter } from './persistence';
|
|
53
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
54
|
+
import { tabIdForSession } from '@/components/layoutController/utils';
|
|
47
55
|
import type { Renderable } from 'react-hot-toast';
|
|
48
56
|
import type { INodeRegistry } from '@/types/NodeMetadata';
|
|
49
|
-
import type { LoadablePlugin } from './plugin';
|
|
57
|
+
import type { LoadablePlugin, SessionExtension } from './plugin';
|
|
50
58
|
import type { UIGraphJSON } from '@/types/graph';
|
|
51
59
|
import type { GraphJSON } from '@kiberon-labs/behave-graph';
|
|
52
60
|
import type { LayoutBase } from 'rc-dock';
|
|
61
|
+
import type { FlowStore, NodeStore, EdgeStore } from '@/store/flow';
|
|
62
|
+
import type { VariableStore } from '@/store/variables';
|
|
63
|
+
import type { RefStore } from '@/store/refs';
|
|
64
|
+
import type { ActionStore } from '@/store/actions';
|
|
65
|
+
import type { TraceStore } from '@/store/traces';
|
|
66
|
+
import type { EventsStore } from '@/store/events';
|
|
67
|
+
import type { LogStore } from '@/store/logs';
|
|
68
|
+
import type { LayerStore } from '@/store/layers';
|
|
69
|
+
import type { UndoManager } from './undoRedo';
|
|
70
|
+
import type { Graph } from './graph';
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
icon?: Renderable;
|
|
64
|
-
style?: React.CSSProperties;
|
|
65
|
-
className?: string;
|
|
66
|
-
ariaLive?: any;
|
|
72
|
+
/**
|
|
73
|
+
* Editor-level pubsub topics. These are global to the editor and shared across
|
|
74
|
+
* every open graph. Augment this interface (not {@link GraphPubSys}) for events
|
|
75
|
+
* that are not tied to a specific graph.
|
|
76
|
+
*/
|
|
77
|
+
export interface EditorPubSys {
|
|
78
|
+
notification: NotificationData;
|
|
79
|
+
'notification:dismiss': {
|
|
80
|
+
toastId?: string;
|
|
67
81
|
};
|
|
82
|
+
'layout:saved': LayoutBase;
|
|
83
|
+
'graph:saved': UIGraphJSON;
|
|
84
|
+
'graph:inner:saved': GraphJSON;
|
|
68
85
|
}
|
|
69
86
|
|
|
70
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Per-graph pubsub topics. Each {@link GraphSession} owns its own bus typed with
|
|
89
|
+
* this interface, so events stay isolated to the graph that produced them.
|
|
90
|
+
* Augment this interface for events that belong to a single graph.
|
|
91
|
+
*/
|
|
92
|
+
export interface GraphPubSys {
|
|
71
93
|
'edge:added': Edge;
|
|
72
94
|
'node:added': Node;
|
|
73
95
|
'edge:removed': Edge;
|
|
@@ -75,95 +97,162 @@ export interface PubSys {
|
|
|
75
97
|
graphAnnotationsChanged: {
|
|
76
98
|
[key: string]: any;
|
|
77
99
|
};
|
|
78
|
-
'aiNode:trigger': {
|
|
79
|
-
nodeId: string;
|
|
80
|
-
};
|
|
81
100
|
saveViewport: {
|
|
82
101
|
index: number;
|
|
83
102
|
viewport: Viewport;
|
|
84
103
|
};
|
|
85
|
-
missingViewPort: {
|
|
86
|
-
index: number;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
notification: NotificationData;
|
|
90
|
-
'notification:dismiss': {
|
|
91
|
-
toastId?: string;
|
|
92
|
-
};
|
|
93
|
-
'layout:saved': LayoutBase;
|
|
94
|
-
'graph:saved': UIGraphJSON;
|
|
95
|
-
'graph:inner:saved': GraphJSON;
|
|
96
|
-
'chat:userMessage': {
|
|
97
|
-
content: string;
|
|
98
|
-
};
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Combined pubsub surface kept for backwards compatibility. Prefer the split
|
|
108
|
+
* {@link EditorPubSys} / {@link GraphPubSys} interfaces.
|
|
109
|
+
*/
|
|
110
|
+
export interface PubSys extends EditorPubSys, GraphPubSys {}
|
|
111
|
+
|
|
101
112
|
/**
|
|
102
113
|
* Use this to extend the System interface when adding plugins
|
|
103
114
|
*/
|
|
104
115
|
export interface ISystem {}
|
|
105
116
|
|
|
117
|
+
/** Minimal storage adapter for persisting editor settings. */
|
|
118
|
+
export type SettingsStorage = {
|
|
119
|
+
getItem: (key: string) => string | null;
|
|
120
|
+
setItem: (key: string, value: string) => void;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** Serialized editor-level settings (UI toggles + custom type conversions). */
|
|
124
|
+
export type EditorSettingsJSON = {
|
|
125
|
+
settings?: Record<string, any>;
|
|
126
|
+
conversions?: ConversionRule[];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const SETTINGS_STORAGE_KEY = 'behave-graph:editor-settings';
|
|
130
|
+
|
|
131
|
+
const settingSetterName = (key: string): string =>
|
|
132
|
+
`set${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
133
|
+
|
|
134
|
+
const defaultSettingsStorage = (): SettingsStorage | undefined => {
|
|
135
|
+
try {
|
|
136
|
+
if (typeof localStorage !== 'undefined') return localStorage;
|
|
137
|
+
} catch {
|
|
138
|
+
// localStorage access can throw in sandboxed contexts
|
|
139
|
+
}
|
|
140
|
+
return undefined;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Observable registry of open graph sessions plus the currently focused one.
|
|
145
|
+
* Backed by a zustand store so panels rendered outside of a graph tab can
|
|
146
|
+
* subscribe and re-render when the active graph changes.
|
|
147
|
+
*/
|
|
148
|
+
export type ActiveGraphStore = {
|
|
149
|
+
activeGraphId: string | null;
|
|
150
|
+
sessions: Record<string, GraphSession>;
|
|
151
|
+
setActiveGraph: (id: string | null) => void;
|
|
152
|
+
addSession: (session: GraphSession) => void;
|
|
153
|
+
removeSession: (id: string) => void;
|
|
154
|
+
getActive: () => GraphSession | undefined;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const activeGraphStoreFactory = () =>
|
|
158
|
+
createStore<ActiveGraphStore>((set, get) => ({
|
|
159
|
+
activeGraphId: null,
|
|
160
|
+
sessions: {},
|
|
161
|
+
setActiveGraph: (activeGraphId) => set(() => ({ activeGraphId })),
|
|
162
|
+
addSession: (session) =>
|
|
163
|
+
set((state) => ({
|
|
164
|
+
sessions: { ...state.sessions, [session.id]: session }
|
|
165
|
+
})),
|
|
166
|
+
removeSession: (id) =>
|
|
167
|
+
set((state) => {
|
|
168
|
+
const next = { ...state.sessions };
|
|
169
|
+
delete next[id];
|
|
170
|
+
const activeGraphId =
|
|
171
|
+
state.activeGraphId === id ? null : state.activeGraphId;
|
|
172
|
+
return { sessions: next, activeGraphId };
|
|
173
|
+
}),
|
|
174
|
+
getActive: () => {
|
|
175
|
+
const state = get();
|
|
176
|
+
return state.activeGraphId
|
|
177
|
+
? state.sessions[state.activeGraphId]
|
|
178
|
+
: undefined;
|
|
179
|
+
}
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The editor-level system. Holds state that is shared across every open graph
|
|
184
|
+
* (settings, registry, specs, menubar, tabs, ...) plus an observable registry of
|
|
185
|
+
* per-graph {@link GraphSession} instances. Per-graph state itself lives on the
|
|
186
|
+
* sessions, not here.
|
|
187
|
+
*
|
|
188
|
+
* The class is intentionally still named `System` so existing `declare module`
|
|
189
|
+
* augmentations (`interface System { ... }`) keep merging and the public API is
|
|
190
|
+
* stable; `EditorSystem` is exported as an alias for new code.
|
|
191
|
+
*/
|
|
106
192
|
export class System implements ISystem {
|
|
107
|
-
public readonly
|
|
108
|
-
public readonly pubsub = new PubSub<PubSys>();
|
|
109
|
-
public readonly undoManager = new UndoManager();
|
|
110
|
-
public readonly flowStore: StoreApi<FlowStore>;
|
|
111
|
-
public readonly controlStore: StoreApi<ControlsStore>;
|
|
112
|
-
public readonly variableStore: StoreApi<VariableStore>;
|
|
113
|
-
public readonly selectionStore: StoreApi<SelectionStore>;
|
|
114
|
-
public readonly refStore: StoreApi<RefStore>;
|
|
193
|
+
public readonly pubsub = new PubSub<EditorPubSys>();
|
|
115
194
|
public readonly tabStore: StoreApi<TabStore>;
|
|
116
|
-
protected deps: Record<string, unknown> = {};
|
|
117
|
-
public readonly registry: StoreApi<RegistryStore>;
|
|
118
195
|
public readonly tabLoader: TabLoader;
|
|
119
196
|
public readonly systemSettings: StoreApi<SystemSettingsStore>;
|
|
120
|
-
|
|
197
|
+
/** Registry of setting descriptors driving the auto-generated Settings panel. */
|
|
198
|
+
public readonly settingsSchema: StoreApi<SettingsSchemaStore>;
|
|
121
199
|
public readonly legendStore: StoreApi<LegendStore>;
|
|
200
|
+
public readonly menubarStore: StoreApi<MenuBarStore>;
|
|
122
201
|
public readonly hotKeyStore: StoreApi<HotkeyStore>;
|
|
123
|
-
public readonly
|
|
124
|
-
public readonly nodeStore: StoreApi<NodeStore>;
|
|
202
|
+
public readonly registry: StoreApi<RegistryStore>;
|
|
125
203
|
public readonly specStore: StoreApi<SpecsStore>;
|
|
126
204
|
public readonly specificStore: StoreApi<SpecificStore>;
|
|
127
205
|
public readonly socketGeneratorStore: StoreApi<SocketGeneratorStore>;
|
|
128
|
-
public readonly eventsStore: StoreApi<EventsStore>;
|
|
129
206
|
public readonly documentationStore: StoreApi<DocumentationStore>;
|
|
130
207
|
public readonly toolbarStore: StoreApi<ToolbarStore>;
|
|
131
|
-
public readonly
|
|
132
|
-
|
|
133
|
-
public readonly
|
|
134
|
-
|
|
135
|
-
public readonly
|
|
208
|
+
public readonly controlStore: StoreApi<ControlsStore>;
|
|
209
|
+
/** User/plugin-defined automatic type conversions for auto-convert. */
|
|
210
|
+
public readonly conversionStore: StoreApi<ConversionStore>;
|
|
211
|
+
/** Named, dispatchable commands shared across hotkeys/menus/toolbar. */
|
|
212
|
+
public readonly commandStore: StoreApi<CommandStore>;
|
|
213
|
+
/** Per-target context-menu item registry (node/edge/selection/pane). */
|
|
214
|
+
public readonly contextMenuStore: StoreApi<ContextMenuStore>;
|
|
136
215
|
public readonly notifications: Notifications = new Notifications(this);
|
|
137
216
|
|
|
217
|
+
/** Observable registry of open graph sessions + the focused one. */
|
|
218
|
+
public readonly activeGraph: StoreApi<ActiveGraphStore>;
|
|
219
|
+
|
|
220
|
+
/** Editor-level extensions applied to every graph session on creation. */
|
|
221
|
+
private readonly sessionExtensions = new Set<SessionExtension>();
|
|
222
|
+
|
|
223
|
+
/** Disposer for the currently installed graph/layout save handlers. */
|
|
224
|
+
private persistenceDisposer: () => void = () => {};
|
|
225
|
+
|
|
226
|
+
protected deps: Record<string, unknown> = {};
|
|
227
|
+
|
|
138
228
|
/**
|
|
139
|
-
* Create a new System instance
|
|
229
|
+
* Create a new editor System instance
|
|
140
230
|
* @param registry - INodeRegistry containing nodes and values metadata
|
|
141
231
|
*/
|
|
142
232
|
constructor(registry?: INodeRegistry) {
|
|
233
|
+
this.activeGraph = activeGraphStoreFactory();
|
|
143
234
|
this.tabStore = tabStoreFactory();
|
|
144
235
|
this.controlStore = controlsStoreFactory();
|
|
145
|
-
this.variableStore = variableStoreFactory();
|
|
146
|
-
this.refStore = refStoreFactory();
|
|
147
236
|
this.systemSettings = systemSettingsFactory();
|
|
148
|
-
|
|
237
|
+
// Seeded with the built-in setting descriptors (DEFAULT_SETTINGS). Plugins
|
|
238
|
+
// append their own via registerSetting(...).
|
|
239
|
+
this.settingsSchema = settingsSchemaStoreFactory();
|
|
149
240
|
this.legendStore = legendStoreFactory();
|
|
150
|
-
this.eventsStore = eventsStoreFactory();
|
|
151
|
-
this.nodeStore = nodeStoreFactory(this);
|
|
152
|
-
this.edgeStore = edgeStoreFactory(this);
|
|
153
|
-
this.flowStore = flowStoreFactory(this);
|
|
154
|
-
this.selectionStore = selectionStoreFactory(this);
|
|
155
|
-
this.hotKeyStore = hotKeyStoreFactory(this);
|
|
156
|
-
this.actionStore = actionStoreFactory(this);
|
|
157
241
|
this.registry = registryStoreFactory();
|
|
158
242
|
this.specStore = specsStoreFactory(this);
|
|
159
243
|
this.socketGeneratorStore = socketGeneratorStoreFactory();
|
|
160
244
|
this.specificStore = specificStoreFactory();
|
|
161
245
|
this.documentationStore = documentationStoreFactory();
|
|
162
246
|
this.toolbarStore = toolbarStoreFactory();
|
|
163
|
-
this.
|
|
247
|
+
this.conversionStore = conversionStoreFactory();
|
|
248
|
+
this.commandStore = commandStoreFactory();
|
|
249
|
+
this.contextMenuStore = contextMenuStoreFactory();
|
|
250
|
+
this.hotKeyStore = hotKeyStoreFactory(this);
|
|
164
251
|
|
|
165
|
-
|
|
166
|
-
|
|
252
|
+
// Seed the built-in commands + context-menu items. Hosts can override by id
|
|
253
|
+
// or add their own via these registries.
|
|
254
|
+
registerDefaultCommands(this.commandStore);
|
|
255
|
+
registerDefaultContextMenu(this.contextMenuStore);
|
|
167
256
|
|
|
168
257
|
// Handle registry initialization
|
|
169
258
|
if (registry) {
|
|
@@ -174,10 +263,125 @@ export class System implements ISystem {
|
|
|
174
263
|
this.menubarStore = menubarStoreFactory();
|
|
175
264
|
this.tabLoader = new TabLoader(this);
|
|
176
265
|
|
|
177
|
-
|
|
266
|
+
// Wire the default graph/layout save handlers (download-to-file) so a fresh
|
|
267
|
+
// editor has working Save actions out of the box. Hosts that persist
|
|
268
|
+
// elsewhere override via enablePersistence(...) or opt out with
|
|
269
|
+
// disablePersistence().
|
|
270
|
+
this.persistenceDisposer = installPersistence(this);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* The currently focused graph session, if any.
|
|
275
|
+
*/
|
|
276
|
+
get session(): GraphSession | undefined {
|
|
277
|
+
return this.activeGraph.getState().getActive();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Create a new graph session, register it and (by default) make it active.
|
|
282
|
+
*/
|
|
283
|
+
createSession(
|
|
284
|
+
id = 'graph',
|
|
285
|
+
options?: { activate?: boolean; name?: string }
|
|
286
|
+
): GraphSession {
|
|
287
|
+
const session = new GraphSession(this, id, options?.name ?? 'Graph');
|
|
288
|
+
this.activeGraph.getState().addSession(session);
|
|
289
|
+
// Let editor plugins extend the fully-constructed session before it becomes
|
|
290
|
+
// active, so panels reacting to the active graph see a complete instance.
|
|
291
|
+
for (const extension of this.sessionExtensions) {
|
|
292
|
+
this.applySessionExtension(session, extension);
|
|
293
|
+
}
|
|
294
|
+
if (options?.activate ?? true) {
|
|
295
|
+
this.activeGraph.getState().setActiveGraph(id);
|
|
296
|
+
}
|
|
297
|
+
return session;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Look up an existing session by id, creating an empty one if missing.
|
|
302
|
+
*/
|
|
303
|
+
getOrCreateSession(id: string): GraphSession {
|
|
304
|
+
return (
|
|
305
|
+
this.activeGraph.getState().sessions[id] ??
|
|
306
|
+
this.createSession(id, { activate: false })
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Create a brand new, empty graph in its own tab and focus it.
|
|
312
|
+
*/
|
|
313
|
+
newGraph(name?: string): GraphSession {
|
|
314
|
+
const id = uuidv4();
|
|
315
|
+
const count = Object.keys(this.activeGraph.getState().sessions).length;
|
|
316
|
+
const session = this.createSession(id, {
|
|
317
|
+
activate: true,
|
|
318
|
+
name: name ?? `Untitled ${count}`
|
|
319
|
+
});
|
|
320
|
+
this.tabStore.getState().openTab(tabIdForSession(id));
|
|
321
|
+
return session;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Dispose a graph session and remove it from the registry. Called when its
|
|
326
|
+
* tab is closed.
|
|
327
|
+
*/
|
|
328
|
+
disposeSession(id: string): void {
|
|
329
|
+
const session = this.activeGraph.getState().sessions[id];
|
|
330
|
+
if (!session) return;
|
|
331
|
+
session.dispose();
|
|
332
|
+
this.activeGraph.getState().removeSession(id);
|
|
333
|
+
}
|
|
178
334
|
|
|
179
|
-
|
|
180
|
-
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// Focused-graph accessors.
|
|
337
|
+
//
|
|
338
|
+
// All graph-canvas and dock-panel consumers read per-graph state through
|
|
339
|
+
// useGraph()/useActiveGraph(), and execution is per-session via each
|
|
340
|
+
// GraphSession's run controller. These getters remain only for editor-level
|
|
341
|
+
// surfaces that imperatively act on the *focused* graph , the hotkey handlers,
|
|
342
|
+
// layout utilities, the menubar, and server-metadata fan-out. They resolve to
|
|
343
|
+
// the focused session, consistent with the "panels follow focus" model; they
|
|
344
|
+
// are a convenience, not a single-open-graph limit (multiple graphs are open
|
|
345
|
+
// and independently runnable at once).
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
get flowStore(): StoreApi<FlowStore> {
|
|
348
|
+
return this.session!.flowStore;
|
|
349
|
+
}
|
|
350
|
+
get nodeStore(): StoreApi<NodeStore> {
|
|
351
|
+
return this.session!.nodeStore;
|
|
352
|
+
}
|
|
353
|
+
get edgeStore(): StoreApi<EdgeStore> {
|
|
354
|
+
return this.session!.edgeStore;
|
|
355
|
+
}
|
|
356
|
+
get variableStore(): StoreApi<VariableStore> {
|
|
357
|
+
return this.session!.variableStore;
|
|
358
|
+
}
|
|
359
|
+
get selectionStore(): StoreApi<SelectionStore> {
|
|
360
|
+
return this.session!.selectionStore;
|
|
361
|
+
}
|
|
362
|
+
get refStore(): StoreApi<RefStore> {
|
|
363
|
+
return this.session!.refStore;
|
|
364
|
+
}
|
|
365
|
+
get actionStore(): StoreApi<ActionStore> {
|
|
366
|
+
return this.session!.actionStore;
|
|
367
|
+
}
|
|
368
|
+
get traceStore(): StoreApi<TraceStore> {
|
|
369
|
+
return this.session!.traceStore;
|
|
370
|
+
}
|
|
371
|
+
get eventsStore(): StoreApi<EventsStore> {
|
|
372
|
+
return this.session!.eventsStore;
|
|
373
|
+
}
|
|
374
|
+
get logsStore(): StoreApi<LogStore> {
|
|
375
|
+
return this.session!.logsStore;
|
|
376
|
+
}
|
|
377
|
+
get layerStore(): StoreApi<LayerStore> {
|
|
378
|
+
return this.session!.layerStore;
|
|
379
|
+
}
|
|
380
|
+
get graph(): Graph {
|
|
381
|
+
return this.session!.graph;
|
|
382
|
+
}
|
|
383
|
+
get undoManager(): UndoManager {
|
|
384
|
+
return this.session!.undoManager;
|
|
181
385
|
}
|
|
182
386
|
|
|
183
387
|
/**
|
|
@@ -220,4 +424,230 @@ export class System implements ISystem {
|
|
|
220
424
|
await plugin.loader(this, options as TOptions);
|
|
221
425
|
console.log(`Plugin loaded: ${plugin.opts.name}`);
|
|
222
426
|
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Register an extension applied to every {@link GraphSession}. It runs against
|
|
430
|
+
* each graph already open at registration time and against every graph created
|
|
431
|
+
* afterwards (via {@link createSession}), so a plugin can attach per-graph
|
|
432
|
+
* state to new graph instances from a single editor-level registration.
|
|
433
|
+
*
|
|
434
|
+
* If the extension returns a cleanup it is wired to the session's
|
|
435
|
+
* {@link GraphSession.onDispose} and runs when that graph's tab is closed.
|
|
436
|
+
*
|
|
437
|
+
* @returns an unregister function that stops the extension from applying to
|
|
438
|
+
* sessions created later. It does not retroactively tear down sessions already
|
|
439
|
+
* extended (those clean up on their own dispose).
|
|
440
|
+
*/
|
|
441
|
+
registerSessionExtension(extension: SessionExtension): () => void {
|
|
442
|
+
this.sessionExtensions.add(extension);
|
|
443
|
+
// Apply to graphs that already exist so registration order doesn't matter.
|
|
444
|
+
for (const session of Object.values(this.activeGraph.getState().sessions)) {
|
|
445
|
+
this.applySessionExtension(session, extension);
|
|
446
|
+
}
|
|
447
|
+
return () => {
|
|
448
|
+
this.sessionExtensions.delete(extension);
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** Run a single session extension, wiring any returned cleanup to dispose. */
|
|
453
|
+
private applySessionExtension(
|
|
454
|
+
session: GraphSession,
|
|
455
|
+
extension: SessionExtension
|
|
456
|
+
): void {
|
|
457
|
+
try {
|
|
458
|
+
const cleanup = extension(session);
|
|
459
|
+
if (typeof cleanup === 'function') session.onDispose(cleanup);
|
|
460
|
+
} catch (err) {
|
|
461
|
+
console.error('Session extension failed', err);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Register a custom automatic type conversion (e.g. from a profile plugin) so
|
|
467
|
+
* auto-convert can splice in the given node for that type pair.
|
|
468
|
+
*/
|
|
469
|
+
registerConversion(rule: ConversionRule): void {
|
|
470
|
+
this.conversionStore.getState().registerConversion(rule);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Contribute a setting to the schema-driven Settings panel. The panel
|
|
475
|
+
* auto-generates a row for it (grouped under `descriptor.section`), and its
|
|
476
|
+
* default value is seeded into the settings store if not already present.
|
|
477
|
+
* Built-in settings are registered the same way at construction.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* system.registerSetting({
|
|
481
|
+
* key: 'graphRunner.autoStart', section: 'Graph Runner', type: 'boolean',
|
|
482
|
+
* default: false, title: 'Auto-start runner'
|
|
483
|
+
* });
|
|
484
|
+
*/
|
|
485
|
+
registerSetting(descriptor: SettingDescriptor): void {
|
|
486
|
+
if (
|
|
487
|
+
descriptor.type !== 'custom' &&
|
|
488
|
+
!(descriptor.key in this.systemSettings.getState())
|
|
489
|
+
) {
|
|
490
|
+
this.systemSettings
|
|
491
|
+
.getState()
|
|
492
|
+
.setSetting(descriptor.key, descriptor.default);
|
|
493
|
+
}
|
|
494
|
+
this.settingsSchema.getState().registerSetting(descriptor);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Contribute several settings at once. */
|
|
498
|
+
registerSettings(descriptors: SettingDescriptor[]): void {
|
|
499
|
+
for (const descriptor of descriptors) this.registerSetting(descriptor);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/** Read a setting value by key (built-in or plugin-contributed). */
|
|
503
|
+
getSetting<T = unknown>(key: string): T {
|
|
504
|
+
return this.systemSettings.getState()[key] as T;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Set a setting value by key. Persists if the descriptor allows it. */
|
|
508
|
+
setSetting(key: string, value: unknown): void {
|
|
509
|
+
this.systemSettings.getState().setSetting(key, value);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Dispatch a registered command against the focused graph (or a supplied
|
|
514
|
+
* session). Convenience used by hotkeys, the menubar and the toolbar so they
|
|
515
|
+
* share one dispatch path. No-ops if there is no graph to act on.
|
|
516
|
+
*/
|
|
517
|
+
runCommand(id: string, ctx?: Partial<CommandContext>): void | Promise<void> {
|
|
518
|
+
const session = ctx?.session ?? this.session;
|
|
519
|
+
if (!session) return;
|
|
520
|
+
return this.commandStore
|
|
521
|
+
.getState()
|
|
522
|
+
.run(id, { editor: this, session, ...ctx });
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Override where the editor's Save actions send their data. The editor
|
|
527
|
+
* publishes `graph:saved`, `graph:inner:saved` and `layout:saved`; by default
|
|
528
|
+
* each triggers a JSON file download. Pass an adapter to redirect any subset
|
|
529
|
+
* of those to your own sink (write to disk, POST to a backend, ...); topics
|
|
530
|
+
* you omit keep the file-download default. Replaces any previously installed
|
|
531
|
+
* handlers and returns a disposer.
|
|
532
|
+
*
|
|
533
|
+
* This governs graph/layout saving only; editor *settings* persistence is
|
|
534
|
+
* separate , see {@link enableSettingsPersistence}.
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* // Persist graphs to a backend, keep the default layout download.
|
|
538
|
+
* system.enablePersistence({
|
|
539
|
+
* saveGraph: (graph) => api.put('/graphs/current', graph)
|
|
540
|
+
* });
|
|
541
|
+
*/
|
|
542
|
+
enablePersistence(adapter?: Partial<PersistenceAdapter>): () => void {
|
|
543
|
+
this.persistenceDisposer();
|
|
544
|
+
this.persistenceDisposer = installPersistence(this, adapter);
|
|
545
|
+
return this.persistenceDisposer;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Remove the default (or custom) graph/layout save handlers entirely. Use this
|
|
550
|
+
* when the host handles saving through a channel of its own and the built-in
|
|
551
|
+
* file download would be redundant (e.g. the VS Code extension).
|
|
552
|
+
*/
|
|
553
|
+
disablePersistence(): void {
|
|
554
|
+
this.persistenceDisposer();
|
|
555
|
+
this.persistenceDisposer = () => {};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Serialize the persistable editor settings , the UI toggles plus any custom
|
|
560
|
+
* type conversions , to a plain JSON object.
|
|
561
|
+
*/
|
|
562
|
+
serializeSettings(): EditorSettingsJSON {
|
|
563
|
+
const state = this.systemSettings.getState() as Record<string, any>;
|
|
564
|
+
const settings: Record<string, any> = {};
|
|
565
|
+
for (const key of this.persistedSettingKeys()) settings[key] = state[key];
|
|
566
|
+
return {
|
|
567
|
+
settings,
|
|
568
|
+
conversions: this.conversionStore.getState().conversions
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Keys round-tripped to persisted storage: the built-in persisted keys plus
|
|
574
|
+
* every plugin-contributed descriptor that opts in (`persist !== false`).
|
|
575
|
+
* Custom descriptors carry no backing value, so they are excluded.
|
|
576
|
+
*/
|
|
577
|
+
private persistedSettingKeys(): string[] {
|
|
578
|
+
const keys = new Set<string>(PERSISTED_SETTING_KEYS as string[]);
|
|
579
|
+
for (const descriptor of this.settingsSchema.getState().settings) {
|
|
580
|
+
if (descriptor.type === 'custom' || descriptor.persist === false)
|
|
581
|
+
continue;
|
|
582
|
+
keys.add(descriptor.key);
|
|
583
|
+
}
|
|
584
|
+
return [...keys];
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Apply previously-serialized editor settings (toggles + conversions).
|
|
589
|
+
* Unknown keys are ignored.
|
|
590
|
+
*/
|
|
591
|
+
applySettings(json: EditorSettingsJSON | undefined): void {
|
|
592
|
+
if (!json) return;
|
|
593
|
+
const state = this.systemSettings.getState() as Record<string, any>;
|
|
594
|
+
for (const [key, value] of Object.entries(json.settings ?? {})) {
|
|
595
|
+
if (value === undefined) continue;
|
|
596
|
+
const setter = state[settingSetterName(key)];
|
|
597
|
+
if (typeof setter === 'function') setter(value);
|
|
598
|
+
// Plugin-contributed keys have no typed setter: write generically.
|
|
599
|
+
else if (typeof state.setSetting === 'function')
|
|
600
|
+
state.setSetting(key, value);
|
|
601
|
+
}
|
|
602
|
+
if (Array.isArray(json.conversions)) {
|
|
603
|
+
this.conversionStore.getState().setConversions(json.conversions);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Persist editor settings + conversions to a storage adapter (localStorage by
|
|
609
|
+
* default). Applies any saved state immediately, then saves (debounced) on
|
|
610
|
+
* change. Returns a disposer. A host can pass its own storage adapter (e.g.
|
|
611
|
+
* one backed by VS Code workspace state) instead of localStorage.
|
|
612
|
+
*/
|
|
613
|
+
enableSettingsPersistence(
|
|
614
|
+
storage: SettingsStorage | undefined = defaultSettingsStorage()
|
|
615
|
+
): () => void {
|
|
616
|
+
if (!storage) return () => {};
|
|
617
|
+
|
|
618
|
+
try {
|
|
619
|
+
const raw = storage.getItem(SETTINGS_STORAGE_KEY);
|
|
620
|
+
if (raw) this.applySettings(JSON.parse(raw));
|
|
621
|
+
} catch {
|
|
622
|
+
// ignore malformed saved state
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
626
|
+
const save = () => {
|
|
627
|
+
if (timer) clearTimeout(timer);
|
|
628
|
+
timer = setTimeout(() => {
|
|
629
|
+
try {
|
|
630
|
+
storage.setItem(
|
|
631
|
+
SETTINGS_STORAGE_KEY,
|
|
632
|
+
JSON.stringify(this.serializeSettings())
|
|
633
|
+
);
|
|
634
|
+
} catch {
|
|
635
|
+
// ignore quota / serialization errors
|
|
636
|
+
}
|
|
637
|
+
}, 300);
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
const unsubSettings = this.systemSettings.subscribe(save);
|
|
641
|
+
const unsubConversions = this.conversionStore.subscribe(save);
|
|
642
|
+
return () => {
|
|
643
|
+
if (timer) clearTimeout(timer);
|
|
644
|
+
unsubSettings();
|
|
645
|
+
unsubConversions();
|
|
646
|
+
};
|
|
647
|
+
}
|
|
223
648
|
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Alias for {@link System} expressing its role as the shared editor-level system.
|
|
652
|
+
*/
|
|
653
|
+
export { System as EditorSystem };
|