@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
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { System } from '../src/system/system.js';
|
|
3
|
+
import { registerDefaults } from '../src/generators/registerDefaults.js';
|
|
4
|
+
|
|
5
|
+
describe('editor settings serialization', () => {
|
|
6
|
+
it('round-trips toggles and custom conversions', () => {
|
|
7
|
+
const a = new System();
|
|
8
|
+
a.systemSettings.getState().setAutoConvert(false);
|
|
9
|
+
a.systemSettings.getState().setGridSize(42);
|
|
10
|
+
a.registerConversion({ from: 'integer', to: 'string', nodeType: 'x/conv' });
|
|
11
|
+
|
|
12
|
+
const json = a.serializeSettings();
|
|
13
|
+
|
|
14
|
+
const b = new System();
|
|
15
|
+
b.applySettings(json);
|
|
16
|
+
|
|
17
|
+
expect(b.systemSettings.getState().autoConvert).toBe(false);
|
|
18
|
+
expect(b.systemSettings.getState().gridSize).toBe(42);
|
|
19
|
+
expect(b.conversionStore.getState().conversions).toEqual([
|
|
20
|
+
{ from: 'integer', to: 'string', nodeType: 'x/conv' }
|
|
21
|
+
]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('round-trips settings that the old hand-written store dropped', () => {
|
|
25
|
+
// inlineValues lacked a setter (so applySettings could not restore it) and
|
|
26
|
+
// setShowMenu was untyped , both fixed by the declarative schema.
|
|
27
|
+
const a = new System();
|
|
28
|
+
a.systemSettings.getState().setInlineValues(true);
|
|
29
|
+
a.systemSettings.getState().setShowMenu(false);
|
|
30
|
+
|
|
31
|
+
const b = new System();
|
|
32
|
+
b.applySettings(a.serializeSettings());
|
|
33
|
+
|
|
34
|
+
expect(b.systemSettings.getState().inlineValues).toBe(true);
|
|
35
|
+
expect(b.systemSettings.getState().showMenu).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('does not persist transient settings (showSearch)', () => {
|
|
39
|
+
const a = new System();
|
|
40
|
+
a.systemSettings.getState().setShowSearch(true);
|
|
41
|
+
expect(a.serializeSettings().settings).not.toHaveProperty('showSearch');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('plugin-contributed settings (schema registry)', () => {
|
|
46
|
+
it('seeds the built-in descriptors so the panel auto-generates', () => {
|
|
47
|
+
const sys = new System();
|
|
48
|
+
const keys = sys.settingsSchema.getState().settings.map((s) => s.key);
|
|
49
|
+
expect(keys).toContain('edgeType');
|
|
50
|
+
expect(keys).toContain('autoConvert');
|
|
51
|
+
expect(sys.settingsSchema.getState().sectionOrder).toContain('Layout');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('registerSetting adds a descriptor and seeds its default value', () => {
|
|
55
|
+
const sys = new System();
|
|
56
|
+
sys.registerSetting({
|
|
57
|
+
key: 'myPlugin.apiUrl',
|
|
58
|
+
section: 'My Plugin',
|
|
59
|
+
type: 'string',
|
|
60
|
+
default: 'https://example.com',
|
|
61
|
+
title: 'API URL'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(sys.getSetting('myPlugin.apiUrl')).toBe('https://example.com');
|
|
65
|
+
expect(
|
|
66
|
+
sys.settingsSchema
|
|
67
|
+
.getState()
|
|
68
|
+
.settings.some((s) => s.key === 'myPlugin.apiUrl')
|
|
69
|
+
).toBe(true);
|
|
70
|
+
expect(sys.settingsSchema.getState().sectionOrder).toContain('My Plugin');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('ignores a duplicate key without clobbering the original', () => {
|
|
74
|
+
const sys = new System();
|
|
75
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
76
|
+
try {
|
|
77
|
+
// edgeType is a built-in; a plugin must not be able to redefine it.
|
|
78
|
+
sys.registerSetting({
|
|
79
|
+
key: 'edgeType',
|
|
80
|
+
section: 'Hijack',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
default: true
|
|
83
|
+
});
|
|
84
|
+
expect(warn).toHaveBeenCalled();
|
|
85
|
+
const edge = sys.settingsSchema
|
|
86
|
+
.getState()
|
|
87
|
+
.settings.find((s) => s.key === 'edgeType');
|
|
88
|
+
expect(edge?.section).toBe('Layout');
|
|
89
|
+
} finally {
|
|
90
|
+
warn.mockRestore();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('round-trips plugin setting values through persistence', () => {
|
|
95
|
+
const a = new System();
|
|
96
|
+
a.registerSetting({
|
|
97
|
+
key: 'myPlugin.autoStart',
|
|
98
|
+
section: 'My Plugin',
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
default: false
|
|
101
|
+
});
|
|
102
|
+
a.setSetting('myPlugin.autoStart', true);
|
|
103
|
+
|
|
104
|
+
const json = a.serializeSettings();
|
|
105
|
+
expect(json.settings).toHaveProperty('myPlugin.autoStart', true);
|
|
106
|
+
|
|
107
|
+
const b = new System();
|
|
108
|
+
b.registerSetting({
|
|
109
|
+
key: 'myPlugin.autoStart',
|
|
110
|
+
section: 'My Plugin',
|
|
111
|
+
type: 'boolean',
|
|
112
|
+
default: false
|
|
113
|
+
});
|
|
114
|
+
b.applySettings(json);
|
|
115
|
+
expect(b.getSetting('myPlugin.autoStart')).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('excludes persist:false plugin settings from serialization', () => {
|
|
119
|
+
const sys = new System();
|
|
120
|
+
sys.registerSetting({
|
|
121
|
+
key: 'myPlugin.scratch',
|
|
122
|
+
section: 'My Plugin',
|
|
123
|
+
type: 'boolean',
|
|
124
|
+
default: false,
|
|
125
|
+
persist: false
|
|
126
|
+
});
|
|
127
|
+
sys.setSetting('myPlugin.scratch', true);
|
|
128
|
+
expect(sys.serializeSettings().settings).not.toHaveProperty(
|
|
129
|
+
'myPlugin.scratch'
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('registerDefaults', () => {
|
|
135
|
+
it('registers built-in generators and is idempotent per editor', () => {
|
|
136
|
+
const sys = new System();
|
|
137
|
+
registerDefaults(sys);
|
|
138
|
+
const first = sys.socketGeneratorStore.getState().generators.length;
|
|
139
|
+
expect(first).toBeGreaterThan(0);
|
|
140
|
+
|
|
141
|
+
// Second call (e.g. another graph tab mounting) is a no-op.
|
|
142
|
+
expect(() => registerDefaults(sys)).not.toThrow();
|
|
143
|
+
expect(sys.socketGeneratorStore.getState().generators.length).toBe(first);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('persists to and loads from a storage adapter', () => {
|
|
147
|
+
vi.useFakeTimers();
|
|
148
|
+
try {
|
|
149
|
+
const backing: Record<string, string> = {};
|
|
150
|
+
const storage = {
|
|
151
|
+
getItem: (k: string) => backing[k] ?? null,
|
|
152
|
+
setItem: (k: string, v: string) => {
|
|
153
|
+
backing[k] = v;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const a = new System();
|
|
158
|
+
a.enableSettingsPersistence(storage);
|
|
159
|
+
a.registerConversion({ from: 'float', to: 'string', nodeType: 'f/conv' });
|
|
160
|
+
a.systemSettings.getState().setShowGrid(false);
|
|
161
|
+
vi.advanceTimersByTime(400); // flush the debounced save
|
|
162
|
+
|
|
163
|
+
expect(Object.keys(backing).length).toBe(1);
|
|
164
|
+
|
|
165
|
+
// A fresh system loads the saved state on init.
|
|
166
|
+
const b = new System();
|
|
167
|
+
b.enableSettingsPersistence(storage);
|
|
168
|
+
expect(b.systemSettings.getState().showGrid).toBe(false);
|
|
169
|
+
expect(b.conversionStore.getState().conversions).toContainEqual({
|
|
170
|
+
from: 'float',
|
|
171
|
+
to: 'string',
|
|
172
|
+
nodeType: 'f/conv'
|
|
173
|
+
});
|
|
174
|
+
} finally {
|
|
175
|
+
vi.useRealTimers();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { traceStoreFactory } from '../src/store/traces.js';
|
|
3
|
+
import type { TraceSpan } from '../src/store/traces.js';
|
|
4
|
+
|
|
5
|
+
const newStore = () => traceStoreFactory({} as any).getState();
|
|
6
|
+
|
|
7
|
+
const usedLanes = (spans: TraceSpan[], size: number): Set<number> =>
|
|
8
|
+
new Set(spans.slice(0, size).map((s) => s.lane));
|
|
9
|
+
|
|
10
|
+
describe('trace store lane allocation', () => {
|
|
11
|
+
it('reuses a lane for sequential spans (closed before the next opens)', () => {
|
|
12
|
+
const s = newStore();
|
|
13
|
+
// A opens and closes, then B opens and closes , never overlapping.
|
|
14
|
+
s.addSpan({ nodeId: 'a', name: 'a', start: 0, end: Number.NaN });
|
|
15
|
+
s.updateSpan('a', { end: 1 });
|
|
16
|
+
s.addSpan({ nodeId: 'b', name: 'b', start: 2, end: Number.NaN });
|
|
17
|
+
s.updateSpan('b', { end: 3 });
|
|
18
|
+
|
|
19
|
+
const c = s.collector;
|
|
20
|
+
expect(c.size).toBe(2);
|
|
21
|
+
// Both land in lane 0 → the panel shows a single lane.
|
|
22
|
+
expect(usedLanes(c.spans, c.size)).toEqual(new Set([0]));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('allocates separate lanes for concurrent (overlapping) spans', () => {
|
|
26
|
+
const s = newStore();
|
|
27
|
+
// Both open at once (no end between them) → must not share a lane.
|
|
28
|
+
s.addSpan({ nodeId: 'a', name: 'a', start: 0, end: Number.NaN });
|
|
29
|
+
s.addSpan({ nodeId: 'b', name: 'b', start: 0, end: Number.NaN });
|
|
30
|
+
|
|
31
|
+
const c = s.collector;
|
|
32
|
+
expect(c.size).toBe(2);
|
|
33
|
+
expect(usedLanes(c.spans, c.size)).toEqual(new Set([0, 1]));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('keeps an open span as open (NaN end) until updateSpan closes it', () => {
|
|
37
|
+
const s = newStore();
|
|
38
|
+
s.addSpan({ nodeId: 'a', name: 'a', start: 5, end: Number.NaN });
|
|
39
|
+
|
|
40
|
+
const open = s.collector.spans[0]!;
|
|
41
|
+
expect(Number.isNaN(open.end)).toBe(true);
|
|
42
|
+
|
|
43
|
+
s.updateSpan('a', { end: 9 });
|
|
44
|
+
expect(s.collector.spans[0]!.end).toBe(9);
|
|
45
|
+
});
|
|
46
|
+
});
|
package/tests/visual/README.md
CHANGED
|
@@ -32,7 +32,7 @@ pnpm test:visual:update
|
|
|
32
32
|
|
|
33
33
|
## How it works
|
|
34
34
|
|
|
35
|
-
- Config: [`vitest.visual.config.ts`](../../vitest.visual.config.ts)
|
|
35
|
+
- Config: [`vitest.visual.config.ts`](../../vitest.visual.config.ts) , browser
|
|
36
36
|
mode, Playwright/Chromium, headless, fixed `900x700` viewport.
|
|
37
37
|
- Test files: `tests/visual/**/*.visual.test.tsx`.
|
|
38
38
|
- Each panel is rendered inside a fixed-size `640x480` frame on the editor
|
|
@@ -60,5 +60,5 @@ When a new panel is added under `src/components/panels`, add one entry to the
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Then run `pnpm test:visual:update` to create the baseline, eyeball the generated
|
|
63
|
-
PNG, and commit it. Keep it to a single representative snapshot per panel
|
|
63
|
+
PNG, and commit it. Keep it to a single representative snapshot per panel , the
|
|
64
64
|
unit tests under `tests/util` and `tests/components` cover finer-grained logic.
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png
CHANGED
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png
CHANGED
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png
CHANGED
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png
CHANGED
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png
CHANGED
|
Binary file
|
|
@@ -9,7 +9,6 @@ import { cleanup, render } from 'vitest-browser-react';
|
|
|
9
9
|
import { DefaultSystemProvider } from '../../stories/defaults/defaultStoryProvider';
|
|
10
10
|
|
|
11
11
|
import { AlignmentPanel } from '@/components/panels/alignment';
|
|
12
|
-
import { ConversationPanel } from '@/components/panels/conversation';
|
|
13
12
|
import { EventsPanel } from '@/components/panels/events';
|
|
14
13
|
import { HistoryPanel } from '@/components/panels/history';
|
|
15
14
|
import { KeymapsPanel } from '@/components/panels/keymaps';
|
|
@@ -23,6 +22,7 @@ import { SearchPanel } from '@/components/panels/search';
|
|
|
23
22
|
import { Settings } from '@/components/panels/systemSettings';
|
|
24
23
|
import { TracesPanel } from '@/components/panels/traces';
|
|
25
24
|
import { VariablesPanel } from '@/components/panels/variables';
|
|
25
|
+
import { LocalGraphRunnerPanel } from '@/plugin/graphrunner-local';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* One pixel-snapshot per panel. The shared {@link DefaultSystemProvider} (the
|
|
@@ -31,7 +31,6 @@ import { VariablesPanel } from '@/components/panels/variables';
|
|
|
31
31
|
*/
|
|
32
32
|
const panels: ReadonlyArray<readonly [name: string, element: ReactElement]> = [
|
|
33
33
|
['alignment', <AlignmentPanel />],
|
|
34
|
-
['conversation', <ConversationPanel />],
|
|
35
34
|
['events', <EventsPanel />],
|
|
36
35
|
['history', <HistoryPanel />],
|
|
37
36
|
['keymaps', <KeymapsPanel />],
|
|
@@ -44,7 +43,8 @@ const panels: ReadonlyArray<readonly [name: string, element: ReactElement]> = [
|
|
|
44
43
|
['search', <SearchPanel />],
|
|
45
44
|
['systemSettings', <Settings />],
|
|
46
45
|
['traces', <TracesPanel />],
|
|
47
|
-
['variables', <VariablesPanel />]
|
|
46
|
+
['variables', <VariablesPanel />],
|
|
47
|
+
['localGraphRunner', <LocalGraphRunnerPanel />]
|
|
48
48
|
];
|
|
49
49
|
|
|
50
50
|
afterEach(() => {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { isEventFromEditable } from '../src/hooks/useWasdPan.js';
|
|
3
|
+
|
|
4
|
+
const keydownOn = (element: HTMLElement): KeyboardEvent => {
|
|
5
|
+
document.body.appendChild(element);
|
|
6
|
+
let captured: KeyboardEvent | undefined;
|
|
7
|
+
element.addEventListener('keydown', (e) => {
|
|
8
|
+
captured = e;
|
|
9
|
+
});
|
|
10
|
+
element.dispatchEvent(
|
|
11
|
+
new KeyboardEvent('keydown', { key: 'w', bubbles: true })
|
|
12
|
+
);
|
|
13
|
+
element.remove();
|
|
14
|
+
if (!captured) throw new Error('keydown was not captured');
|
|
15
|
+
return captured;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('WASD pan editable guard', () => {
|
|
19
|
+
it('treats form fields as editable (no pan)', () => {
|
|
20
|
+
expect(
|
|
21
|
+
isEventFromEditable(keydownOn(document.createElement('input')))
|
|
22
|
+
).toBe(true);
|
|
23
|
+
expect(
|
|
24
|
+
isEventFromEditable(keydownOn(document.createElement('textarea')))
|
|
25
|
+
).toBe(true);
|
|
26
|
+
expect(
|
|
27
|
+
isEventFromEditable(keydownOn(document.createElement('select')))
|
|
28
|
+
).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('treats contenteditable divs (e.g. the note editor) as editable', () => {
|
|
32
|
+
const editor = document.createElement('div');
|
|
33
|
+
editor.contentEditable = 'true';
|
|
34
|
+
expect(isEventFromEditable(keydownOn(editor))).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('treats inputs inside shadow DOM (e.g. vscode-textfield) as editable', () => {
|
|
38
|
+
// The conversation panel's chat input is a web component; at a window-level
|
|
39
|
+
// listener the event is retargeted to the host element, so the guard must
|
|
40
|
+
// look through composedPath. Simulate that with a shadow-DOM input and a
|
|
41
|
+
// document-level capture listener (composedPath is only populated during
|
|
42
|
+
// dispatch).
|
|
43
|
+
const host = document.createElement('x-textfield');
|
|
44
|
+
const shadow = host.attachShadow({ mode: 'open' });
|
|
45
|
+
const inner = document.createElement('input');
|
|
46
|
+
shadow.appendChild(inner);
|
|
47
|
+
document.body.appendChild(host);
|
|
48
|
+
|
|
49
|
+
let result: boolean | undefined;
|
|
50
|
+
const listener = (e: Event) => {
|
|
51
|
+
result = isEventFromEditable(e as KeyboardEvent);
|
|
52
|
+
};
|
|
53
|
+
document.addEventListener('keydown', listener, { capture: true });
|
|
54
|
+
inner.dispatchEvent(
|
|
55
|
+
new KeyboardEvent('keydown', { key: 'w', bubbles: true, composed: true })
|
|
56
|
+
);
|
|
57
|
+
document.removeEventListener('keydown', listener, { capture: true });
|
|
58
|
+
host.remove();
|
|
59
|
+
|
|
60
|
+
expect(result).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not treat plain elements as editable (pan proceeds)', () => {
|
|
64
|
+
expect(isEventFromEditable(keydownOn(document.createElement('div')))).toBe(
|
|
65
|
+
false
|
|
66
|
+
);
|
|
67
|
+
expect(isEventFromEditable(keydownOn(document.createElement('span')))).toBe(
|
|
68
|
+
false
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
});
|
package/vitest.config.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { configDefaults, defineConfig } from 'vitest/config';
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
test: {
|
|
6
6
|
// Visual/pixel regression tests run in a real browser via the dedicated
|
|
7
|
-
// vitest.visual.config.ts
|
|
7
|
+
// vitest.visual.config.ts , keep them out of the happy-dom unit run.
|
|
8
8
|
exclude: [...configDefaults.exclude, '**/*.visual.test.{ts,tsx}'],
|
|
9
9
|
watch: false,
|
|
10
10
|
environment: 'happy-dom'
|
package/vitest.visual.config.ts
CHANGED
|
@@ -40,6 +40,13 @@ export default defineConfig({
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
resolve: {
|
|
43
|
+
// The browser runner renders through vitest-browser-react, which can pull
|
|
44
|
+
// its own React/React-DOM copy (a different patch than the app's pinned
|
|
45
|
+
// 19.2.3). Two React instances means the renderer sets the hook dispatcher
|
|
46
|
+
// on one copy while the panels call hooks on the other, producing "Invalid
|
|
47
|
+
// hook call / more than one copy of React" and a null dispatcher. Force a
|
|
48
|
+
// single instance so the renderer and the components share it.
|
|
49
|
+
dedupe: ['react', 'react-dom'],
|
|
43
50
|
alias: {
|
|
44
51
|
'~': path.resolve(__dirname, './src'),
|
|
45
52
|
'@': path.resolve(__dirname, './src')
|