@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
|
@@ -425,10 +425,46 @@ export class RealtimeRunner {
|
|
|
425
425
|
if (this.annotatedOutputNodeIds.length === 0) return;
|
|
426
426
|
|
|
427
427
|
for (const nodeId of this.annotatedOutputNodeIds) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
428
|
+
await this.evaluateNodeForPreview(nodeId);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Evaluate every node that a UI consumer is actively watching.
|
|
434
|
+
*
|
|
435
|
+
* Pure function graphs (e.g. the image nodes) never run during
|
|
436
|
+
* `executeAllSync()` because nothing pushes a flow fiber through them. They
|
|
437
|
+
* are normally pulled lazily when a downstream flow/event node reads their
|
|
438
|
+
* value. In the editor preview there is no such consumer, so the act of a
|
|
439
|
+
* component calling `watchNodeOutput()` is what drives evaluation: we resolve
|
|
440
|
+
* the node's upstream function graph and execute the node itself so its
|
|
441
|
+
* sockets hold a fresh value for `publishWatchedOutputs()` to read.
|
|
442
|
+
*
|
|
443
|
+
* This intentionally does not depend on the `ui.realtime` annotation , any
|
|
444
|
+
* watched output is evaluated.
|
|
445
|
+
*/
|
|
446
|
+
private async recalculateWatchedOutputs(): Promise<void> {
|
|
447
|
+
if (!this.engine) return;
|
|
448
|
+
if (this.watched.size === 0) return;
|
|
449
|
+
|
|
450
|
+
for (const nodeId of this.watched.keys()) {
|
|
451
|
+
await this.evaluateNodeForPreview(nodeId);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Resolve a single function node's inputs (recursively executing its upstream
|
|
457
|
+
* function graph) and execute it. Non-function nodes keep whatever value their
|
|
458
|
+
* last real execution produced. Resilient: a failure on one node is logged and
|
|
459
|
+
* does not abort evaluation of the others.
|
|
460
|
+
*/
|
|
461
|
+
private async evaluateNodeForPreview(nodeId: string): Promise<void> {
|
|
462
|
+
if (!this.engine) return;
|
|
463
|
+
const node = this.engine.nodes?.[nodeId];
|
|
464
|
+
if (!node) return;
|
|
465
|
+
if (!isFunctionNode(node)) return;
|
|
431
466
|
|
|
467
|
+
try {
|
|
432
468
|
let executionSteps = 0;
|
|
433
469
|
for (const inputSocket of node.inputs) {
|
|
434
470
|
executionSteps += await this.resolveSocketValueForPreview(inputSocket);
|
|
@@ -440,6 +476,11 @@ export class RealtimeRunner {
|
|
|
440
476
|
this.engine.onNodeExecutionEnd.emit(node);
|
|
441
477
|
|
|
442
478
|
this.engine.executionSteps += executionSteps;
|
|
479
|
+
} catch (err) {
|
|
480
|
+
console.error(
|
|
481
|
+
`RealtimeRunner: failed to evaluate watched node ${nodeId}:`,
|
|
482
|
+
err
|
|
483
|
+
);
|
|
443
484
|
}
|
|
444
485
|
}
|
|
445
486
|
|
|
@@ -450,7 +491,12 @@ export class RealtimeRunner {
|
|
|
450
491
|
const nodes = this.system.nodeStore.getState().nodes;
|
|
451
492
|
const edges = this.system.edgeStore.getState().edges;
|
|
452
493
|
|
|
453
|
-
const rawGraphJson = flowToBehave(
|
|
494
|
+
const rawGraphJson = flowToBehave(
|
|
495
|
+
this.system.session!,
|
|
496
|
+
nodes,
|
|
497
|
+
edges,
|
|
498
|
+
specJson
|
|
499
|
+
);
|
|
454
500
|
const graphWithAnnotations = this.mergeNodeAnnotationsIntoMetadata(
|
|
455
501
|
rawGraphJson,
|
|
456
502
|
nodes
|
|
@@ -540,6 +586,10 @@ export class RealtimeRunner {
|
|
|
540
586
|
// Recalculate annotated output nodes to force evaluation of upstream function graphs.
|
|
541
587
|
await this.recalculateAnnotatedOutputNodes();
|
|
542
588
|
|
|
589
|
+
// Pull every watched output (e.g. live image node previews) so pure
|
|
590
|
+
// function graphs are evaluated even without a flow consumer.
|
|
591
|
+
await this.recalculateWatchedOutputs();
|
|
592
|
+
|
|
543
593
|
this.publishWatchedOutputs();
|
|
544
594
|
} catch (err) {
|
|
545
595
|
// Keep preview runner resilient; don't crash the editor.
|
|
@@ -562,6 +612,10 @@ export class RealtimeRunner {
|
|
|
562
612
|
// Recalculate annotated output nodes to force evaluation of upstream function graphs.
|
|
563
613
|
await this.recalculateAnnotatedOutputNodes();
|
|
564
614
|
|
|
615
|
+
// Pull every watched output (e.g. live image node previews) so pure
|
|
616
|
+
// function graphs are evaluated even without a flow consumer.
|
|
617
|
+
await this.recalculateWatchedOutputs();
|
|
618
|
+
|
|
565
619
|
this.publishWatchedOutputs();
|
|
566
620
|
} catch {
|
|
567
621
|
// ignore
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
VscodeSingleSelect
|
|
6
6
|
} from '@vscode-elements/react-elements';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { useGraph } from '@/system/provider';
|
|
9
9
|
import type { SpecificRenderProps } from '@/store/specific';
|
|
10
10
|
|
|
11
11
|
const NAME = 'customEvent/onTriggered.customEventId';
|
|
@@ -21,7 +21,7 @@ export function getCustomEventOnTriggeredSpecific() {
|
|
|
21
21
|
const CustomEventOnTriggeredSpecific: React.FC<SpecificRenderProps> = ({
|
|
22
22
|
node
|
|
23
23
|
}) => {
|
|
24
|
-
const system =
|
|
24
|
+
const system = useGraph();
|
|
25
25
|
const customEvents = useStore(system.eventsStore, (s) => s.customEvents);
|
|
26
26
|
|
|
27
27
|
const options = useMemo(() => {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
VscodeSingleSelect
|
|
6
6
|
} from '@vscode-elements/react-elements';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { useGraph } from '@/system/provider';
|
|
9
9
|
import type { SpecificRenderProps } from '@/store/specific';
|
|
10
10
|
import type { NodeSpecJSON } from '@kiberon-labs/behave-graph';
|
|
11
11
|
import type { Socket } from '@/types';
|
|
@@ -24,7 +24,7 @@ export function getCustomEventTriggerSpecific() {
|
|
|
24
24
|
const CustomEventTriggerSpecific: React.FC<SpecificRenderProps> = ({
|
|
25
25
|
node
|
|
26
26
|
}) => {
|
|
27
|
-
const system =
|
|
27
|
+
const system = useGraph();
|
|
28
28
|
const customEvents = useStore(system.eventsStore, (s) => s.customEvents);
|
|
29
29
|
|
|
30
30
|
const options = useMemo(() => {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
VscodeSingleSelect
|
|
6
6
|
} from '@vscode-elements/react-elements';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { useGraph } from '@/system/provider';
|
|
9
9
|
import type { SpecificRenderProps } from '@/store/specific';
|
|
10
10
|
import type { NodeSpecJSON } from '@kiberon-labs/behave-graph';
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@ export function getVariableGetSpecific() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const VariableGetSpecific: React.FC<SpecificRenderProps> = ({ node }) => {
|
|
23
|
-
const system =
|
|
23
|
+
const system = useGraph();
|
|
24
24
|
const variables = useStore(system.variableStore, (s) => s.variables);
|
|
25
25
|
|
|
26
26
|
const options = useMemo(() => {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
VscodeSingleSelect
|
|
6
6
|
} from '@vscode-elements/react-elements';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { useGraph } from '@/system/provider';
|
|
9
9
|
import type { SpecificRenderProps } from '@/store/specific';
|
|
10
10
|
import type { NodeSpecJSON } from '@kiberon-labs/behave-graph';
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@ export function getVariableSetSpecific() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const VariableSetSpecific: React.FC<SpecificRenderProps> = ({ node }) => {
|
|
23
|
-
const system =
|
|
23
|
+
const system = useGraph();
|
|
24
24
|
const variables = useStore(system.variableStore, (s) => s.variables);
|
|
25
25
|
|
|
26
26
|
const options = useMemo(() => {
|
package/src/store/actions.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GraphSession } from '@/system/graphSession';
|
|
2
2
|
import { Graph } from 'graphlib';
|
|
3
3
|
import { create } from 'zustand';
|
|
4
4
|
import copyToClipboard from 'copy-to-clipboard';
|
|
@@ -87,7 +87,7 @@ export type ActionStore = {
|
|
|
87
87
|
* @param sys
|
|
88
88
|
* @returns
|
|
89
89
|
*/
|
|
90
|
-
const convertToGraph = (sys:
|
|
90
|
+
const convertToGraph = (sys: GraphSession) => {
|
|
91
91
|
const nodes = sys.nodeStore.getState().nodes;
|
|
92
92
|
const edges = sys.edgeStore.getState().edges;
|
|
93
93
|
|
|
@@ -119,7 +119,7 @@ const createNodeLookup = (nodes: string[]) => {
|
|
|
119
119
|
);
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
-
const applyFilters = (sys:
|
|
122
|
+
const applyFilters = (sys: GraphSession, lookup: Record<string, boolean>) => {
|
|
123
123
|
sys.nodeStore.getState().setNodes((nodes) => {
|
|
124
124
|
const newNodes = nodes.map((x) => {
|
|
125
125
|
if (!lookup[x.id]) {
|
|
@@ -140,13 +140,13 @@ const applyFilters = (sys: System, lookup: Record<string, boolean>) => {
|
|
|
140
140
|
});
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export const actionStoreFactory = (sys:
|
|
143
|
+
export const actionStoreFactory = (sys: GraphSession) =>
|
|
144
144
|
create<ActionStore>((set, get) => ({
|
|
145
145
|
actions: {
|
|
146
146
|
save: async () => {
|
|
147
147
|
try {
|
|
148
148
|
const uiGraph = buildUIGraphJSON(sys);
|
|
149
|
-
sys.pubsub.publish('graph:saved', uiGraph);
|
|
149
|
+
sys.editor.pubsub.publish('graph:saved', uiGraph);
|
|
150
150
|
return uiGraph;
|
|
151
151
|
} catch (err) {
|
|
152
152
|
sys.notifications.error('Failed to save graph');
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createStore, type StoreApi } from 'zustand';
|
|
2
|
+
import type { EdgeChange, XYPosition } from 'reactflow';
|
|
3
|
+
import type { System } from '@/system/system';
|
|
4
|
+
import type { GraphSession } from '@/system/graphSession';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Context handed to a command when it runs. Carries the editor + the graph it
|
|
8
|
+
* acts on, plus optional targets so the same command works from a context menu,
|
|
9
|
+
* a hotkey, or a toolbar button.
|
|
10
|
+
*/
|
|
11
|
+
export type CommandContext = {
|
|
12
|
+
editor: System;
|
|
13
|
+
session: GraphSession;
|
|
14
|
+
nodeId?: string;
|
|
15
|
+
edgeId?: string;
|
|
16
|
+
sourceId?: string;
|
|
17
|
+
targetId?: string;
|
|
18
|
+
position?: XYPosition;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A named, dispatchable action. Decouples *what* (id) from *how* (run), so UI
|
|
23
|
+
* surfaces (context menus, hotkeys, menubar, toolbar) reference commands by id
|
|
24
|
+
* instead of reaching into concrete stores.
|
|
25
|
+
*/
|
|
26
|
+
export type Command = {
|
|
27
|
+
id: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
/** When present and false, the command is treated as unavailable. */
|
|
30
|
+
isEnabled?: (ctx: CommandContext) => boolean;
|
|
31
|
+
run: (ctx: CommandContext) => void | Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type CommandStore = {
|
|
35
|
+
commands: Map<string, Command>;
|
|
36
|
+
/** Register (or replace) a command. Returns an unregister disposer. */
|
|
37
|
+
register: (command: Command) => () => void;
|
|
38
|
+
unregister: (id: string) => void;
|
|
39
|
+
get: (id: string) => Command | undefined;
|
|
40
|
+
list: () => Command[];
|
|
41
|
+
/** Run a command by id; no-ops (with a warning) if unknown or disabled. */
|
|
42
|
+
run: (id: string, ctx: CommandContext) => void | Promise<void>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const commandStoreFactory = (): StoreApi<CommandStore> =>
|
|
46
|
+
createStore<CommandStore>((set, get) => ({
|
|
47
|
+
commands: new Map(),
|
|
48
|
+
register: (command) => {
|
|
49
|
+
set((s) => {
|
|
50
|
+
const next = new Map(s.commands);
|
|
51
|
+
next.set(command.id, command);
|
|
52
|
+
return { commands: next };
|
|
53
|
+
});
|
|
54
|
+
return () => get().unregister(command.id);
|
|
55
|
+
},
|
|
56
|
+
unregister: (id) =>
|
|
57
|
+
set((s) => {
|
|
58
|
+
if (!s.commands.has(id)) return s;
|
|
59
|
+
const next = new Map(s.commands);
|
|
60
|
+
next.delete(id);
|
|
61
|
+
return { commands: next };
|
|
62
|
+
}),
|
|
63
|
+
get: (id) => get().commands.get(id),
|
|
64
|
+
list: () => Array.from(get().commands.values()),
|
|
65
|
+
run: (id, ctx) => {
|
|
66
|
+
const command = get().commands.get(id);
|
|
67
|
+
if (!command) {
|
|
68
|
+
console.warn(`[commands] unknown command: ${id}`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (command.isEnabled && !command.isEnabled(ctx)) return;
|
|
72
|
+
return command.run(ctx);
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
// --- Default graph commands --------------------------------------------------
|
|
77
|
+
// Transitional: these delegate to the per-session action store. As actions are
|
|
78
|
+
// decomposed (a later slice), the logic can move into the command handlers and
|
|
79
|
+
// the action store can shrink.
|
|
80
|
+
|
|
81
|
+
const actionsOf = (ctx: CommandContext) =>
|
|
82
|
+
ctx.session.actionStore.getState().actions;
|
|
83
|
+
|
|
84
|
+
const centerOnNode = (session: GraphSession, nodeId: string): void => {
|
|
85
|
+
const node = session.nodeStore.getState().nodes.find((n) => n.id === nodeId);
|
|
86
|
+
if (!node) return;
|
|
87
|
+
const x = node.position.x + (node.width ?? 0) / 2;
|
|
88
|
+
const y = node.position.y + (node.height ?? 0) / 2;
|
|
89
|
+
session.refStore
|
|
90
|
+
.getState()
|
|
91
|
+
.getRef('reactflow')
|
|
92
|
+
?.setCenter(x, y, { duration: 200, zoom: 1 });
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const reactFlowOf = (session: GraphSession) =>
|
|
96
|
+
session.refStore.getState().getRef('reactflow');
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Register the built-in editor commands. Hosts may override any of them by
|
|
100
|
+
* re-registering with the same id, or add their own.
|
|
101
|
+
*/
|
|
102
|
+
export const registerDefaultCommands = (
|
|
103
|
+
store: StoreApi<CommandStore>
|
|
104
|
+
): void => {
|
|
105
|
+
const { register } = store.getState();
|
|
106
|
+
|
|
107
|
+
register({
|
|
108
|
+
id: 'node.focus',
|
|
109
|
+
title: 'Focus',
|
|
110
|
+
run: (ctx) => {
|
|
111
|
+
if (ctx.nodeId) actionsOf(ctx).focusNode(ctx.nodeId);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
register({
|
|
115
|
+
id: 'node.traceUpstream',
|
|
116
|
+
title: 'Trace Upstream',
|
|
117
|
+
run: (ctx) => {
|
|
118
|
+
if (ctx.nodeId) actionsOf(ctx).traceUpstream(ctx.nodeId);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
register({
|
|
122
|
+
id: 'node.traceDownstream',
|
|
123
|
+
title: 'Trace Downstream',
|
|
124
|
+
run: (ctx) => {
|
|
125
|
+
if (ctx.nodeId) actionsOf(ctx).traceDownstream(ctx.nodeId);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
register({
|
|
129
|
+
id: 'trace.reset',
|
|
130
|
+
title: 'Reset Trace',
|
|
131
|
+
run: (ctx) => actionsOf(ctx).resetTrace()
|
|
132
|
+
});
|
|
133
|
+
register({
|
|
134
|
+
id: 'node.togglePinned',
|
|
135
|
+
title: 'Pin / Unpin',
|
|
136
|
+
run: (ctx) => {
|
|
137
|
+
if (ctx.nodeId) actionsOf(ctx).toggleNodePinned(ctx.nodeId);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
register({
|
|
141
|
+
id: 'node.toggleHidden',
|
|
142
|
+
title: 'Hide / Show',
|
|
143
|
+
run: (ctx) => {
|
|
144
|
+
if (ctx.nodeId) actionsOf(ctx).toggleNodeHidden(ctx.nodeId);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
register({
|
|
149
|
+
id: 'edge.findSource',
|
|
150
|
+
title: 'Find Source',
|
|
151
|
+
run: (ctx) => {
|
|
152
|
+
if (ctx.sourceId) centerOnNode(ctx.session, ctx.sourceId);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
register({
|
|
156
|
+
id: 'edge.findTarget',
|
|
157
|
+
title: 'Find Target',
|
|
158
|
+
run: (ctx) => {
|
|
159
|
+
if (ctx.targetId) centerOnNode(ctx.session, ctx.targetId);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
register({
|
|
163
|
+
id: 'edge.delete',
|
|
164
|
+
title: 'Delete',
|
|
165
|
+
run: (ctx) => {
|
|
166
|
+
if (!ctx.edgeId) return;
|
|
167
|
+
const change: EdgeChange = { id: ctx.edgeId, type: 'remove' };
|
|
168
|
+
ctx.session.edgeStore.getState().applyEdgeChanges([change]);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
register({
|
|
173
|
+
id: 'selection.copy',
|
|
174
|
+
title: 'Copy',
|
|
175
|
+
run: (ctx) => actionsOf(ctx).copySelectionToClipboard()
|
|
176
|
+
});
|
|
177
|
+
register({
|
|
178
|
+
id: 'selection.paste',
|
|
179
|
+
title: 'Paste',
|
|
180
|
+
run: (ctx) => actionsOf(ctx).pasteFromClipboard()
|
|
181
|
+
});
|
|
182
|
+
register({
|
|
183
|
+
id: 'selection.group',
|
|
184
|
+
title: 'Group',
|
|
185
|
+
run: (ctx) => actionsOf(ctx).groupNodes()
|
|
186
|
+
});
|
|
187
|
+
register({
|
|
188
|
+
id: 'selection.selectAll',
|
|
189
|
+
title: 'Select All',
|
|
190
|
+
run: (ctx) =>
|
|
191
|
+
ctx.session.nodeStore
|
|
192
|
+
.getState()
|
|
193
|
+
.setNodes((nodes) => nodes.map((n) => ({ ...n, selected: true })))
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Editor-level
|
|
197
|
+
register({
|
|
198
|
+
id: 'editor.save',
|
|
199
|
+
title: 'Save Graph',
|
|
200
|
+
run: (ctx) => {
|
|
201
|
+
void actionsOf(ctx).save();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
register({
|
|
205
|
+
id: 'editor.undo',
|
|
206
|
+
title: 'Undo',
|
|
207
|
+
run: (ctx) => ctx.session.undoManager.undo()
|
|
208
|
+
});
|
|
209
|
+
register({
|
|
210
|
+
id: 'editor.redo',
|
|
211
|
+
title: 'Redo',
|
|
212
|
+
run: (ctx) => ctx.session.undoManager.redo()
|
|
213
|
+
});
|
|
214
|
+
register({
|
|
215
|
+
id: 'editor.find',
|
|
216
|
+
title: 'Find',
|
|
217
|
+
run: (ctx) => ctx.editor.tabStore.getState().openTab('find')
|
|
218
|
+
});
|
|
219
|
+
// `editor.autoLayout` is contributed by the optional layout plugin
|
|
220
|
+
// (`@/plugin/layout`), which owns the heavy elkjs/dagre dependencies. When
|
|
221
|
+
// that plugin is not registered the command is simply unavailable and the
|
|
222
|
+
// bound hotkey no-ops.
|
|
223
|
+
|
|
224
|
+
// View
|
|
225
|
+
register({
|
|
226
|
+
id: 'view.fit',
|
|
227
|
+
title: 'Fit View',
|
|
228
|
+
run: (ctx) => {
|
|
229
|
+
reactFlowOf(ctx.session)?.fitView({
|
|
230
|
+
padding: 0.2,
|
|
231
|
+
includeHiddenNodes: true
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
register({
|
|
236
|
+
id: 'view.zoomIn',
|
|
237
|
+
title: 'Zoom In',
|
|
238
|
+
run: (ctx) => reactFlowOf(ctx.session)?.zoomIn({ duration: 300 })
|
|
239
|
+
});
|
|
240
|
+
register({
|
|
241
|
+
id: 'view.zoomOut',
|
|
242
|
+
title: 'Zoom Out',
|
|
243
|
+
run: (ctx) => reactFlowOf(ctx.session)?.zoomOut({ duration: 300 })
|
|
244
|
+
});
|
|
245
|
+
register({
|
|
246
|
+
id: 'view.zoomReset',
|
|
247
|
+
title: 'Reset Zoom',
|
|
248
|
+
run: (ctx) => {
|
|
249
|
+
const rf = reactFlowOf(ctx.session);
|
|
250
|
+
if (!rf) return;
|
|
251
|
+
rf.setViewport({ ...rf.getViewport(), zoom: 1 });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
register({
|
|
255
|
+
id: 'view.toggleGrid',
|
|
256
|
+
title: 'Toggle Grid',
|
|
257
|
+
run: (ctx) => {
|
|
258
|
+
const s = ctx.editor.systemSettings.getState();
|
|
259
|
+
s.setShowGrid(!s.showGrid);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
register({
|
|
263
|
+
id: 'view.toggleMinimap',
|
|
264
|
+
title: 'Toggle Minimap',
|
|
265
|
+
run: (ctx) => {
|
|
266
|
+
const s = ctx.editor.systemSettings.getState();
|
|
267
|
+
s.setShowMinimap(!s.showMinimap);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
register({
|
|
271
|
+
id: 'view.toggleSnapGrid',
|
|
272
|
+
title: 'Toggle Snap to Grid',
|
|
273
|
+
run: (ctx) => {
|
|
274
|
+
const s = ctx.editor.systemSettings.getState();
|
|
275
|
+
s.setSnapGrid(!s.snapGrid);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createStore, type StoreApi } from 'zustand';
|
|
2
|
+
import { hidden, pinned } from '@/annotations';
|
|
3
|
+
import { isBehaveNode } from '@/util/isBehaveNode';
|
|
4
|
+
import type { CommandContext } from './commands';
|
|
5
|
+
|
|
6
|
+
/** Which canvas target a context-menu item applies to. */
|
|
7
|
+
export type ContextMenuTarget = 'node' | 'edge' | 'selection' | 'pane';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A registrable context-menu entry. Items dispatch either a registered command
|
|
11
|
+
* (`commandId`) or an inline `onSelect`. `group` controls separator placement;
|
|
12
|
+
* `order` controls position within a target. `when` hides the item dynamically.
|
|
13
|
+
*/
|
|
14
|
+
export type ContextMenuItem = {
|
|
15
|
+
id: string;
|
|
16
|
+
target: ContextMenuTarget;
|
|
17
|
+
/** Static text, or a function for state-dependent labels (e.g. Pin/Unpin). */
|
|
18
|
+
label: string | ((ctx: CommandContext) => string);
|
|
19
|
+
keybinding?: string;
|
|
20
|
+
/** Ascending sort within the target. */
|
|
21
|
+
order?: number;
|
|
22
|
+
/** Items with different adjacent groups get a separator between them. */
|
|
23
|
+
group?: string | number;
|
|
24
|
+
when?: (ctx: CommandContext) => boolean;
|
|
25
|
+
/** Dispatch a registered command by id. */
|
|
26
|
+
commandId?: string;
|
|
27
|
+
/** Or run inline (takes precedence over commandId). */
|
|
28
|
+
onSelect?: (ctx: CommandContext) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ContextMenuStore = {
|
|
32
|
+
items: ContextMenuItem[];
|
|
33
|
+
/** Register (or replace by id) an item. Returns an unregister disposer. */
|
|
34
|
+
register: (item: ContextMenuItem) => () => void;
|
|
35
|
+
unregister: (id: string) => void;
|
|
36
|
+
/** Items for a target, sorted by `order`. Filtering by `when` is the caller's. */
|
|
37
|
+
getItems: (target: ContextMenuTarget) => ContextMenuItem[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const contextMenuStoreFactory = (): StoreApi<ContextMenuStore> =>
|
|
41
|
+
createStore<ContextMenuStore>((set, get) => ({
|
|
42
|
+
items: [],
|
|
43
|
+
register: (item) => {
|
|
44
|
+
set((s) => ({
|
|
45
|
+
items: [...s.items.filter((i) => i.id !== item.id), item]
|
|
46
|
+
}));
|
|
47
|
+
return () => get().unregister(item.id);
|
|
48
|
+
},
|
|
49
|
+
unregister: (id) =>
|
|
50
|
+
set((s) => ({ items: s.items.filter((i) => i.id !== id) })),
|
|
51
|
+
getItems: (target) =>
|
|
52
|
+
get()
|
|
53
|
+
.items.filter((i) => i.target === target)
|
|
54
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
// --- Default context menus ---------------------------------------------------
|
|
58
|
+
|
|
59
|
+
const nodeAt = (ctx: CommandContext) =>
|
|
60
|
+
ctx.session.nodeStore.getState().nodes.find((n) => n.id === ctx.nodeId);
|
|
61
|
+
|
|
62
|
+
const nodeFlag = (ctx: CommandContext, key: string): boolean => {
|
|
63
|
+
const node = nodeAt(ctx);
|
|
64
|
+
return node && 'data' in node
|
|
65
|
+
? Boolean((node.data.annotations as Record<string, unknown>)?.[key])
|
|
66
|
+
: false;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// The default node items only make sense on behave nodes; presentational node
|
|
70
|
+
// types (notes, groups, ...) register their own `when`-scoped items instead.
|
|
71
|
+
const isBehaveTarget = (ctx: CommandContext): boolean => {
|
|
72
|
+
const node = nodeAt(ctx);
|
|
73
|
+
return node !== undefined && isBehaveNode(node);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Register the built-in context-menu items, dispatching the default commands.
|
|
78
|
+
* Hosts can add/remove/replace items by id without forking the menu components.
|
|
79
|
+
*/
|
|
80
|
+
export const registerDefaultContextMenu = (
|
|
81
|
+
store: StoreApi<ContextMenuStore>
|
|
82
|
+
): void => {
|
|
83
|
+
const { register } = store.getState();
|
|
84
|
+
|
|
85
|
+
// Node
|
|
86
|
+
register({
|
|
87
|
+
id: 'node.focus',
|
|
88
|
+
target: 'node',
|
|
89
|
+
label: 'Focus',
|
|
90
|
+
order: 10,
|
|
91
|
+
group: 'focus',
|
|
92
|
+
when: isBehaveTarget,
|
|
93
|
+
commandId: 'node.focus'
|
|
94
|
+
});
|
|
95
|
+
register({
|
|
96
|
+
id: 'node.traceUpstream',
|
|
97
|
+
target: 'node',
|
|
98
|
+
label: 'Trace Upstream',
|
|
99
|
+
order: 20,
|
|
100
|
+
group: 'trace',
|
|
101
|
+
when: isBehaveTarget,
|
|
102
|
+
commandId: 'node.traceUpstream'
|
|
103
|
+
});
|
|
104
|
+
register({
|
|
105
|
+
id: 'node.traceDownstream',
|
|
106
|
+
target: 'node',
|
|
107
|
+
label: 'Trace Downstream',
|
|
108
|
+
order: 21,
|
|
109
|
+
group: 'trace',
|
|
110
|
+
when: isBehaveTarget,
|
|
111
|
+
commandId: 'node.traceDownstream'
|
|
112
|
+
});
|
|
113
|
+
register({
|
|
114
|
+
id: 'node.resetTrace',
|
|
115
|
+
target: 'node',
|
|
116
|
+
label: 'Reset Trace',
|
|
117
|
+
order: 30,
|
|
118
|
+
group: 'reset',
|
|
119
|
+
when: isBehaveTarget,
|
|
120
|
+
commandId: 'trace.reset'
|
|
121
|
+
});
|
|
122
|
+
register({
|
|
123
|
+
id: 'node.togglePinned',
|
|
124
|
+
target: 'node',
|
|
125
|
+
label: (ctx) => (nodeFlag(ctx, pinned) ? 'Unpin' : 'Pin'),
|
|
126
|
+
order: 40,
|
|
127
|
+
group: 'visibility',
|
|
128
|
+
when: isBehaveTarget,
|
|
129
|
+
commandId: 'node.togglePinned'
|
|
130
|
+
});
|
|
131
|
+
register({
|
|
132
|
+
id: 'node.toggleHidden',
|
|
133
|
+
target: 'node',
|
|
134
|
+
label: (ctx) => (nodeFlag(ctx, hidden) ? 'Show' : 'Hide'),
|
|
135
|
+
order: 41,
|
|
136
|
+
group: 'visibility',
|
|
137
|
+
when: isBehaveTarget,
|
|
138
|
+
commandId: 'node.toggleHidden'
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Edge
|
|
142
|
+
register({
|
|
143
|
+
id: 'edge.findSource',
|
|
144
|
+
target: 'edge',
|
|
145
|
+
label: 'Find Source',
|
|
146
|
+
order: 10,
|
|
147
|
+
group: 'find',
|
|
148
|
+
commandId: 'edge.findSource'
|
|
149
|
+
});
|
|
150
|
+
register({
|
|
151
|
+
id: 'edge.findTarget',
|
|
152
|
+
target: 'edge',
|
|
153
|
+
label: 'Find Target',
|
|
154
|
+
order: 11,
|
|
155
|
+
group: 'find',
|
|
156
|
+
commandId: 'edge.findTarget'
|
|
157
|
+
});
|
|
158
|
+
register({
|
|
159
|
+
id: 'edge.delete',
|
|
160
|
+
target: 'edge',
|
|
161
|
+
label: 'Delete',
|
|
162
|
+
order: 20,
|
|
163
|
+
group: 'delete',
|
|
164
|
+
commandId: 'edge.delete'
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Selection
|
|
168
|
+
register({
|
|
169
|
+
id: 'selection.copy',
|
|
170
|
+
target: 'selection',
|
|
171
|
+
label: 'Copy',
|
|
172
|
+
order: 10,
|
|
173
|
+
group: 'clipboard',
|
|
174
|
+
commandId: 'selection.copy'
|
|
175
|
+
});
|
|
176
|
+
register({
|
|
177
|
+
id: 'selection.paste',
|
|
178
|
+
target: 'selection',
|
|
179
|
+
label: 'Paste',
|
|
180
|
+
order: 11,
|
|
181
|
+
group: 'clipboard',
|
|
182
|
+
commandId: 'selection.paste'
|
|
183
|
+
});
|
|
184
|
+
register({
|
|
185
|
+
id: 'selection.group',
|
|
186
|
+
target: 'selection',
|
|
187
|
+
label: 'Group',
|
|
188
|
+
order: 20,
|
|
189
|
+
group: 'group',
|
|
190
|
+
commandId: 'selection.group'
|
|
191
|
+
});
|
|
192
|
+
};
|