@kiberon-labs/behave-graph-flow 1.0.0 → 2.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/.fallowrc.json +16 -0
- package/.storybook/main.ts +32 -0
- package/.storybook/preview.ts +16 -0
- package/.storybook/styles.css +10 -0
- package/.storybook/vscode.css +814 -0
- package/.turbo/turbo-build.log +7 -0
- package/LICENSE +6 -0
- package/README.md +2 -2
- package/data/Polynomial.json +510 -0
- package/data/sequence.json +337 -0
- package/data/trigger-event.json +241 -0
- package/data/variable-change.json +210 -0
- package/dist/entry.css +4 -0
- package/dist/index.css +39 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +2282 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14873 -0
- package/dist/index.js.map +1 -0
- package/docs/notifications.md +246 -0
- package/docs/protocol.md +679 -0
- package/docs/specifics.md +191 -0
- package/package.json +85 -21
- package/postcss.config.ts +3 -4
- package/src/annotations/index.ts +32 -0
- package/src/components/FloatingToolbar/index.module.css +45 -0
- package/src/components/FloatingToolbar/index.tsx +256 -0
- package/src/components/Flow.tsx +276 -75
- package/src/components/contextMenus/NodePicker.module.css +274 -0
- package/src/components/contextMenus/NodePicker.tsx +481 -0
- package/src/components/contextMenus/edge.tsx +108 -0
- package/src/components/contextMenus/node.tsx +155 -0
- package/src/components/contextMenus/selection.tsx +77 -0
- package/src/components/controls/any/index.tsx +8 -0
- package/src/components/controls/boolean/index.tsx +13 -0
- package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
- package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
- package/src/components/controls/colorPicker/index.module.css +18 -0
- package/src/components/controls/colorPicker/index.tsx +61 -0
- package/src/components/controls/number/index.tsx +35 -0
- package/src/components/controls/string/index.tsx +16 -0
- package/src/components/edges/index.tsx +469 -0
- package/src/components/edges/offsetBezier.ts +134 -0
- package/src/components/hotKeys.tsx +20 -0
- package/src/components/layoutController/index.module.css +10 -0
- package/src/components/layoutController/index.tsx +117 -0
- package/src/components/layoutController/utils.ts +205 -0
- package/src/components/menubar/defaults.tsx +480 -0
- package/src/components/menubar/index.tsx +49 -0
- package/src/components/menubar/menuItem.module.css +16 -0
- package/src/components/menubar/menuItem.tsx +32 -0
- package/src/components/nodes/behave/Node.module.css +23 -0
- package/src/components/nodes/behave/Node.tsx +176 -0
- package/src/components/nodes/behave/NodeContainer.module.css +87 -0
- package/src/components/nodes/behave/NodeContainer.tsx +46 -0
- package/src/components/nodes/behave/index.tsx +14 -0
- package/src/components/nodes/comment/FormatToolbar.tsx +118 -0
- package/src/components/nodes/comment/comment.tsx +103 -0
- package/src/components/nodes/comment/styles.module.css +150 -0
- package/src/components/nodes/group/index.tsx +109 -0
- package/src/components/nodes/wrapper/index.tsx +73 -0
- package/src/components/nodes/wrapper/styles.module.css +113 -0
- package/src/components/notifications/NotificationProvider.tsx +81 -0
- package/src/components/notifications/index.ts +2 -0
- package/src/components/notifications/utils.ts +71 -0
- package/src/components/panels/alignment/index.module.css +20 -0
- package/src/components/panels/alignment/index.tsx +244 -0
- package/src/components/panels/base/index.tsx +5 -0
- package/src/components/panels/base/styles.module.css +12 -0
- package/src/components/panels/conversation/index.module.css +151 -0
- package/src/components/panels/conversation/index.tsx +162 -0
- package/src/components/panels/events/CustomEventsEditor.tsx +384 -0
- package/src/components/panels/events/EditEventPanel.tsx +315 -0
- package/src/components/panels/events/ManageEventsPanel.tsx +98 -0
- package/src/components/panels/events/index.tsx +23 -0
- package/src/components/panels/events/styles.module.css +236 -0
- package/src/components/panels/history/index.tsx +92 -0
- package/src/components/panels/history/styles.module.css +106 -0
- package/src/components/panels/keymaps/index.module.css +78 -0
- package/src/components/panels/keymaps/index.tsx +167 -0
- package/src/components/panels/layers/index.tsx +240 -0
- package/src/components/panels/layers/styles.module.css +110 -0
- package/src/components/panels/legend/index.module.css +6 -0
- package/src/components/panels/legend/index.tsx +76 -0
- package/src/components/panels/logs/index.module.css +212 -0
- package/src/components/panels/logs/index.tsx +288 -0
- package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
- package/src/components/panels/nodeInputs/InputsGroup.tsx +64 -0
- package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +65 -0
- package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
- package/src/components/panels/nodeInputs/index.module.css +284 -0
- package/src/components/panels/nodeInputs/index.tsx +339 -0
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +173 -0
- package/src/components/panels/nodePicker/index.tsx +115 -0
- package/src/components/panels/panel/index.module.css +66 -0
- package/src/components/panels/panel/index.tsx +88 -0
- package/src/components/panels/search/index.module.css +66 -0
- package/src/components/panels/search/index.tsx +215 -0
- package/src/components/panels/systemSettings/index.tsx +206 -0
- package/src/components/panels/systemSettings/styles.module.css +11 -0
- package/src/components/panels/traces/GridLines.tsx +38 -0
- package/src/components/panels/traces/TimeGrid.tsx +48 -0
- package/src/components/panels/traces/TraceLane.tsx +62 -0
- package/src/components/panels/traces/TraceTooltip.tsx +22 -0
- package/src/components/panels/traces/TracesHeader.tsx +56 -0
- package/src/components/panels/traces/index.module.css +166 -0
- package/src/components/panels/traces/index.tsx +294 -0
- package/src/components/panels/traces/types.ts +48 -0
- package/src/components/panels/traces/useDerivedSpans.ts +212 -0
- package/src/components/panels/traces/utils.ts +25 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
- package/src/components/panels/variables/ManageVariablesScreen.tsx +144 -0
- package/src/components/panels/variables/index.tsx +125 -0
- package/src/components/panels/variables/styles.module.css +236 -0
- package/src/components/primitives/icon.module.css +45 -0
- package/src/components/primitives/icon.tsx +38 -0
- package/src/components/sockets/input/index.tsx +76 -0
- package/src/components/sockets/input/styles.module.css +27 -0
- package/src/components/sockets/output/index.tsx +61 -0
- package/src/components/sockets/output/styles.module.css +27 -0
- package/src/css/prosemirror.css +57 -0
- package/src/css/rc-dock.css +112 -0
- package/src/css/rc-menu.css +100 -0
- package/src/css/vars.css +14 -0
- package/src/css/vscode.css +13 -0
- package/src/entry.css +4 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
- package/src/generators/SequenceGenerator.tsx +104 -0
- package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
- package/src/generators/SwitchOnStringGenerator.tsx +263 -0
- package/src/generators/registerDefaultGenerators.ts +34 -0
- package/src/hooks/useBehaveGraphFlow.ts +17 -16
- package/src/hooks/useDetachNodes.ts +39 -0
- package/src/hooks/useFlowHandlers.ts +115 -29
- package/src/hooks/useWasdPan.ts +188 -0
- package/src/index.css +146 -0
- package/src/index.ts +36 -18
- package/src/layout/dagre.tsx +119 -0
- package/src/layout/elk.ts +200 -0
- package/src/plugin/alignment/index.ts +81 -0
- package/src/plugin/docs/index.tsx +299 -0
- package/src/plugin/docs/panel/index.tsx +200 -0
- package/src/plugin/docs/panel/styles.module.css +174 -0
- package/src/plugin/graphrunner/actions.ts +253 -0
- package/src/plugin/graphrunner/buttons.tsx +87 -0
- package/src/plugin/graphrunner/client.ts +704 -0
- package/src/plugin/graphrunner/index.tsx +255 -0
- package/src/plugin/graphrunner/panel.tsx +386 -0
- package/src/plugin/graphrunner/runner.ts +358 -0
- package/src/plugin/graphrunner/session.ts +243 -0
- package/src/plugin/graphrunner/store.ts +206 -0
- package/src/plugin/graphrunner/styles.module.css +211 -0
- package/src/plugin/graphrunner/transport.ts +224 -0
- package/src/plugin/graphrunner/types.ts +672 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +457 -0
- package/src/plugin/graphrunner-local/index.tsx +166 -0
- package/src/plugin/graphrunner-local/panel.tsx +231 -0
- package/src/plugin/graphrunner-local/store.ts +41 -0
- package/src/plugin/graphrunner-local/styles.module.css +101 -0
- package/src/plugin/graphrunner-local/transport.ts +1372 -0
- package/src/plugin/graphrunner-local/types.ts +10 -0
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +633 -0
- package/src/plugin/graphrunner-webworker/index.tsx +146 -0
- package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
- package/src/plugin/graphrunner-webworker/store.ts +89 -0
- package/src/plugin/graphrunner-webworker/types.ts +17 -0
- package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
- package/src/plugin/realtime/realtimeRunner.ts +570 -0
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
- package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
- package/src/specifics/VariableGetSpecific.tsx +110 -0
- package/src/specifics/VariableSetSpecific.tsx +110 -0
- package/src/specifics/registerDefaultSpecifics.ts +5 -0
- package/src/store/actions.tsx +698 -0
- package/src/store/chat.ts +73 -0
- package/src/store/controls.tsx +62 -0
- package/src/store/documentation.tsx +69 -0
- package/src/store/events.tsx +116 -0
- package/src/store/flow.tsx +245 -0
- package/src/store/graphRunnerClient.ts +110 -0
- package/src/store/hotKeys.tsx +323 -0
- package/src/store/layers.ts +259 -0
- package/src/store/legend.tsx +76 -0
- package/src/store/logs.ts +28 -0
- package/src/store/menubar.ts +41 -0
- package/src/store/refs.ts +84 -0
- package/src/store/registry.ts +43 -0
- package/src/store/selection.ts +22 -0
- package/src/store/settings.ts +99 -0
- package/src/store/socketGenerator.tsx +54 -0
- package/src/store/specific.tsx +75 -0
- package/src/store/specs.tsx +35 -0
- package/src/store/tabs.ts +278 -0
- package/src/store/toolbar.tsx +45 -0
- package/src/store/traces.ts +240 -0
- package/src/store/variables.ts +37 -0
- package/src/system/graph.ts +134 -0
- package/src/system/index.ts +3 -0
- package/src/system/notifications.ts +98 -0
- package/src/system/plugin.ts +27 -0
- package/src/system/provider.tsx +22 -0
- package/src/system/pubsub.ts +323 -0
- package/src/system/system.ts +223 -0
- package/src/system/tabLoader.tsx +265 -0
- package/src/system/undoRedo.ts +103 -0
- package/src/transformers/Uigraph.ts +60 -0
- package/src/transformers/behaveToFlow.ts +16 -4
- package/src/transformers/flowToBehave.ts +32 -12
- package/src/types/NodeMetadata.ts +27 -0
- package/src/types/graph.ts +49 -0
- package/src/types/nodes.ts +45 -0
- package/src/types.ts +16 -0
- package/src/util/colors.ts +1 -29
- package/src/util/downloadJson.ts +18 -0
- package/src/util/extractNodeMetadata.ts +16 -0
- package/src/util/getPickerFilters.ts +1 -1
- package/src/util/isBehaveNode.ts +6 -0
- package/src/util/isValidConnection.ts +28 -15
- package/src/util/mergeSockets.ts +29 -0
- package/src/util/serializeVariables.ts +66 -0
- package/src/util/sockets.ts +43 -0
- package/stories/apex/layoutController/example-graph.worker.ts +39 -0
- package/stories/apex/layoutController/index.stories.tsx +48 -0
- package/stories/apex/layoutController/webworker.stories.tsx +103 -0
- package/stories/apex/menubar/menubar.stories.tsx +19 -0
- package/stories/components/colorpicker/index.stories.tsx +20 -0
- package/stories/components/contextMenus/edge.stories.tsx +32 -0
- package/stories/components/contextMenus/node.stories.tsx +26 -0
- package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
- package/stories/components/controls/any/index.stories.tsx +19 -0
- package/stories/components/controls/boolean/index.stories.tsx +19 -0
- package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
- package/stories/components/controls/number/index.stories.tsx +19 -0
- package/stories/components/controls/string/index.stories.tsx +19 -0
- package/stories/components/nodes/behaveNode.stories.tsx +108 -0
- package/stories/components/nodes/comment.stories.tsx +106 -0
- package/stories/components/panels/alignment.stories.tsx +24 -0
- package/stories/components/panels/events.stories.tsx +38 -0
- package/stories/components/panels/graphRunner.stories.tsx +317 -0
- package/stories/components/panels/history.stories.tsx +37 -0
- package/stories/components/panels/keymaps.stories.tsx +21 -0
- package/stories/components/panels/legend.stories.tsx +37 -0
- package/stories/components/panels/logs.stories.tsx +24 -0
- package/stories/components/panels/nodeInputs.stories.tsx +21 -0
- package/stories/components/panels/nodePicker.stories.tsx +37 -0
- package/stories/components/panels/panel.stories.tsx +39 -0
- package/stories/components/panels/search.stories.tsx +24 -0
- package/stories/components/panels/systemSettings.stories.tsx +26 -0
- package/stories/components/panels/traces.stories.tsx +225 -0
- package/stories/components/panels/variables.stories.tsx +24 -0
- package/stories/defaults/defaultStoryProvider.tsx +167 -0
- package/stories/defaults/systemGenerator.ts +38 -0
- package/tests/components/edges/offsetBezier.test.ts +51 -0
- package/tests/components/layoutController/utils.test.ts +68 -0
- package/tests/components/panels/traces/utils.test.ts +52 -0
- package/tests/flowToBehave.test.ts +26 -4
- package/tests/notifications.test.ts +87 -0
- package/tests/saveLoad.test.ts +372 -0
- package/tests/util/calculateNewEdge.test.ts +98 -0
- package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
- package/tests/util/hasPositionMetaData.test.ts +33 -0
- package/tests/util/isBehaveNode.test.ts +22 -0
- package/tests/util/isHandleConnected.test.ts +37 -0
- package/tests/util/mergeSockets.test.ts +43 -0
- package/tests/visual/README.md +64 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
- 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-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-traces-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 +76 -0
- package/tsconfig.base.json +39 -0
- package/tsconfig.json +18 -59
- package/tsconfig.prod.json +23 -0
- package/tsdown.config.ts +15 -3
- package/typedoc.json +7 -7
- package/vite.config.js +7 -0
- package/vitest.config.ts +5 -2
- package/vitest.visual.config.ts +48 -0
- package/src/components/AutoSizeInput.tsx +0 -65
- package/src/components/Controls.tsx +0 -87
- package/src/components/InputSocket.tsx +0 -142
- package/src/components/Node.tsx +0 -68
- package/src/components/NodeContainer.tsx +0 -46
- package/src/components/NodePicker.tsx +0 -77
- package/src/components/OutputSocket.tsx +0 -58
- package/src/components/modals/ClearModal.tsx +0 -40
- package/src/components/modals/HelpModal.tsx +0 -36
- package/src/components/modals/LoadModal.tsx +0 -96
- package/src/components/modals/Modal.tsx +0 -64
- package/src/components/modals/SaveModal.tsx +0 -60
- package/src/hooks/useCustomNodeTypes.tsx +0 -31
- package/src/hooks/useGraphRunner.ts +0 -104
- package/src/hooks/useMergeMap.ts +0 -14
- package/src/hooks/useNodeSpecJson.ts +0 -20
- package/src/hooks/useQueriableDefinitions.ts +0 -22
- package/src/styles.css +0 -8
- package/tailwind.config.ts +0 -19
- package/tests/tsconfig.json +0 -10
- /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { System } from '@/system';
|
|
2
|
+
import ELK, {
|
|
3
|
+
type ElkExtendedEdge,
|
|
4
|
+
type ElkNode,
|
|
5
|
+
type ElkPort
|
|
6
|
+
} from 'elkjs/lib/elk.bundled.js';
|
|
7
|
+
import type { Edge, Node } from 'reactflow';
|
|
8
|
+
import { pinned } from '@/annotations';
|
|
9
|
+
|
|
10
|
+
const layoutOptions = {
|
|
11
|
+
// 'elk.algorithm': 'layered',
|
|
12
|
+
'elk.direction': 'RIGHT',
|
|
13
|
+
'elk.layered.spacing.edgeNodeBetweenLayers': '40',
|
|
14
|
+
'elk.spacing.nodeNode': '40',
|
|
15
|
+
'elk.layered.nodePlacement.strategy': 'SIMPLE'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type LayoutAlgorithm =
|
|
19
|
+
| 'org.eclipse.elk.layered'
|
|
20
|
+
| 'org.eclipse.elk.force'
|
|
21
|
+
| 'org.eclipse.elk.rectpacking';
|
|
22
|
+
|
|
23
|
+
const elk = new ELK();
|
|
24
|
+
|
|
25
|
+
const getLayoutedNodes = async (
|
|
26
|
+
nodes: Node[],
|
|
27
|
+
edges: Edge[],
|
|
28
|
+
algorithm: LayoutAlgorithm
|
|
29
|
+
) => {
|
|
30
|
+
// Filter out pinned nodes, group nodes, and nodes inside groups
|
|
31
|
+
const layoutNodes = nodes.filter((node) => {
|
|
32
|
+
const isPinned = 'data' in node && node.data.annotations?.[pinned];
|
|
33
|
+
const isInGroup = !!node.parentId;
|
|
34
|
+
const isGroup = node.type === 'group';
|
|
35
|
+
return !isPinned && !isInGroup && !isGroup;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
//Convert the edges to a lookup map
|
|
39
|
+
const edgeOut = new Map<string, Edge[]>();
|
|
40
|
+
const edgeIn = new Map<string, Edge[]>();
|
|
41
|
+
|
|
42
|
+
edges.forEach((edge) => {
|
|
43
|
+
if (!edgeOut.has(edge.source)) {
|
|
44
|
+
edgeOut.set(edge.source, []);
|
|
45
|
+
}
|
|
46
|
+
edgeOut.get(edge.source)!.push(edge);
|
|
47
|
+
|
|
48
|
+
if (!edgeIn.has(edge.target)) {
|
|
49
|
+
edgeIn.set(edge.target, []);
|
|
50
|
+
}
|
|
51
|
+
edgeIn.get(edge.target)!.push(edge);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const graph: ElkNode = {
|
|
55
|
+
id: 'root',
|
|
56
|
+
layoutOptions: { 'elk.algorithm': algorithm, ...layoutOptions },
|
|
57
|
+
children: layoutNodes.map((n) => {
|
|
58
|
+
//lookup the edges for this node
|
|
59
|
+
const outgoingEdges = edgeIn.get(n.id) || [];
|
|
60
|
+
const incomingEdges = edgeOut.get(n.id) || [];
|
|
61
|
+
|
|
62
|
+
// we need unique ids for the handles (called 'ports' in elkjs) for the layouting
|
|
63
|
+
// an id is structured like: nodeId-source/target-id
|
|
64
|
+
|
|
65
|
+
const targetPorts = outgoingEdges.map((e) => {
|
|
66
|
+
return {
|
|
67
|
+
id: e.sourceHandle as string,
|
|
68
|
+
width: 10,
|
|
69
|
+
height: 10,
|
|
70
|
+
properties: {
|
|
71
|
+
side: 'WEST'
|
|
72
|
+
}
|
|
73
|
+
} as ElkPort;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const sourcePorts = incomingEdges.map(
|
|
77
|
+
(e) =>
|
|
78
|
+
({
|
|
79
|
+
id: e.targetHandle as string,
|
|
80
|
+
width: 10,
|
|
81
|
+
height: 10,
|
|
82
|
+
properties: {
|
|
83
|
+
side: 'EAST'
|
|
84
|
+
}
|
|
85
|
+
}) as ElkPort
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
id: n.id,
|
|
90
|
+
width: n.width ?? 150,
|
|
91
|
+
height: n.height ?? 50,
|
|
92
|
+
// ⚠️ we need to tell elk that the ports are fixed, in order to reduce edge crossings
|
|
93
|
+
properties: {
|
|
94
|
+
'org.eclipse.elk.portConstraints': 'FIXED_ORDER'
|
|
95
|
+
},
|
|
96
|
+
// we are also passing the id, so we can also handle edges without a sourceHandle or targetHandle option
|
|
97
|
+
ports: [...targetPorts, ...sourcePorts]
|
|
98
|
+
};
|
|
99
|
+
}),
|
|
100
|
+
edges: edges.map(
|
|
101
|
+
(e) =>
|
|
102
|
+
({
|
|
103
|
+
id: e.id,
|
|
104
|
+
sources: [e.sourceHandle || e.source],
|
|
105
|
+
targets: [e.targetHandle || e.target]
|
|
106
|
+
}) as ElkExtendedEdge
|
|
107
|
+
)
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const layoutedGraph = await elk.layout(graph);
|
|
111
|
+
|
|
112
|
+
const layoutedNodes = nodes.map((node) => {
|
|
113
|
+
// Skip pinned nodes - keep their current position
|
|
114
|
+
const isPinned = 'data' in node && node.data.annotations?.[pinned];
|
|
115
|
+
if (isPinned) {
|
|
116
|
+
return node;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Skip group nodes for now - we'll position them based on children
|
|
120
|
+
if (node.type === 'group') {
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Skip nodes inside groups - they maintain relative positions
|
|
125
|
+
if (node.parentId) {
|
|
126
|
+
return node;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const layoutedNode = layoutedGraph.children?.find(
|
|
130
|
+
(lgNode) => lgNode.id === node.id
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
...node,
|
|
135
|
+
position: {
|
|
136
|
+
x: layoutedNode?.x ?? 0,
|
|
137
|
+
y: layoutedNode?.y ?? 0
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Update group positions and sizes based on their children
|
|
143
|
+
const padding = 25;
|
|
144
|
+
return layoutedNodes.map((node) => {
|
|
145
|
+
if (node.type !== 'group') return node;
|
|
146
|
+
|
|
147
|
+
const children = layoutedNodes.filter((n) => n.parentId === node.id);
|
|
148
|
+
if (children.length === 0) return node;
|
|
149
|
+
|
|
150
|
+
// Calculate bounding box of children (in absolute coordinates)
|
|
151
|
+
let minX = Infinity;
|
|
152
|
+
let minY = Infinity;
|
|
153
|
+
let maxX = -Infinity;
|
|
154
|
+
let maxY = -Infinity;
|
|
155
|
+
|
|
156
|
+
children.forEach((child) => {
|
|
157
|
+
const childX = child.positionAbsolute?.x ?? child.position.x;
|
|
158
|
+
const childY = child.positionAbsolute?.y ?? child.position.y;
|
|
159
|
+
const childWidth = child.width ?? 150;
|
|
160
|
+
const childHeight = child.height ?? 50;
|
|
161
|
+
|
|
162
|
+
minX = Math.min(minX, childX);
|
|
163
|
+
minY = Math.min(minY, childY);
|
|
164
|
+
maxX = Math.max(maxX, childX + childWidth);
|
|
165
|
+
maxY = Math.max(maxY, childY + childHeight);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Position group with padding around children
|
|
169
|
+
return {
|
|
170
|
+
...node,
|
|
171
|
+
position: {
|
|
172
|
+
x: minX - padding,
|
|
173
|
+
y: minY - padding
|
|
174
|
+
},
|
|
175
|
+
style: {
|
|
176
|
+
...node.style,
|
|
177
|
+
width: maxX - minX + padding * 2,
|
|
178
|
+
height: maxY - minY + padding * 2
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const applyElkLayout = async (
|
|
185
|
+
sys: System,
|
|
186
|
+
algorithm: LayoutAlgorithm
|
|
187
|
+
) => {
|
|
188
|
+
const nodeStore = sys.nodeStore.getState();
|
|
189
|
+
const nodes = Object.values(nodeStore.nodes);
|
|
190
|
+
const edges = Object.values(sys.edgeStore.getState().edges);
|
|
191
|
+
const reactflow = sys.refStore.getState().getRef('reactflow');
|
|
192
|
+
|
|
193
|
+
if (!reactflow) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const layoutedNodes = await getLayoutedNodes(nodes, edges, algorithm);
|
|
198
|
+
|
|
199
|
+
nodeStore.setNodes(layoutedNodes);
|
|
200
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { align, distribute, ALIGNMENT } from '@/components/panels/alignment';
|
|
2
|
+
import { type System } from '../../system';
|
|
3
|
+
import type { Node } from 'reactflow';
|
|
4
|
+
import { plugin } from '@/system/plugin';
|
|
5
|
+
import { pinned } from '@/annotations';
|
|
6
|
+
|
|
7
|
+
const partitionSelectedNodes = (nodes: Node[]) => {
|
|
8
|
+
return nodes.reduce(
|
|
9
|
+
(acc, node) => {
|
|
10
|
+
if (node.selected && !node.data?.metadata?.[pinned]) {
|
|
11
|
+
acc.selectedNodes.push(node);
|
|
12
|
+
} else {
|
|
13
|
+
acc.unselectedNodes.push(node);
|
|
14
|
+
}
|
|
15
|
+
return acc;
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
selectedNodes: [] as Node[],
|
|
19
|
+
unselectedNodes: [] as Node[]
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type AlignmentAxis = 'x' | 'y';
|
|
25
|
+
export type AlignmentType = 'start' | 'center' | 'end';
|
|
26
|
+
|
|
27
|
+
declare module '@/system/system' {
|
|
28
|
+
interface PubSys {
|
|
29
|
+
'alignment:align': {
|
|
30
|
+
type: AlignmentType;
|
|
31
|
+
axis: AlignmentAxis;
|
|
32
|
+
};
|
|
33
|
+
'alignment:distribute': {
|
|
34
|
+
type: AlignmentType;
|
|
35
|
+
axis: AlignmentAxis;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const setupSystemActions = (system: System) => {
|
|
41
|
+
// Subscribe to alignment events
|
|
42
|
+
system.pubsub.subscribe('alignment:align', (_, data) => {
|
|
43
|
+
const { nodes, setNodes } = system.nodeStore.getState();
|
|
44
|
+
const { selectedNodes, unselectedNodes } = partitionSelectedNodes(nodes);
|
|
45
|
+
|
|
46
|
+
const alignmentType =
|
|
47
|
+
data.type === 'start'
|
|
48
|
+
? ALIGNMENT.START
|
|
49
|
+
: data.type === 'center'
|
|
50
|
+
? ALIGNMENT.CENTER
|
|
51
|
+
: ALIGNMENT.END;
|
|
52
|
+
|
|
53
|
+
align(alignmentType, data.axis)(selectedNodes);
|
|
54
|
+
setNodes([...unselectedNodes, ...selectedNodes]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Subscribe to distribution events
|
|
58
|
+
system.pubsub.subscribe('alignment:distribute', (_, data) => {
|
|
59
|
+
const { nodes, setNodes } = system.nodeStore.getState();
|
|
60
|
+
const { selectedNodes, unselectedNodes } = partitionSelectedNodes(nodes);
|
|
61
|
+
|
|
62
|
+
const alignmentType =
|
|
63
|
+
data.type === 'start'
|
|
64
|
+
? ALIGNMENT.START
|
|
65
|
+
: data.type === 'center'
|
|
66
|
+
? ALIGNMENT.CENTER
|
|
67
|
+
: ALIGNMENT.END;
|
|
68
|
+
|
|
69
|
+
distribute(alignmentType, data.axis)(selectedNodes);
|
|
70
|
+
setNodes([...unselectedNodes, ...selectedNodes]);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const alignmentPlugin = plugin(
|
|
75
|
+
(system) => {
|
|
76
|
+
setupSystemActions(system);
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'alignment'
|
|
80
|
+
}
|
|
81
|
+
);
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { type System } from '@/system/system';
|
|
2
|
+
import type { NodeDocumentation } from '@/store/documentation';
|
|
3
|
+
import { BounceRight, Flash, PauseWindow } from 'iconoir-react';
|
|
4
|
+
import { DocumentationBrowserPanel } from './panel';
|
|
5
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
6
|
+
import { MenuItemElement } from '../../components/menubar/menuItem';
|
|
7
|
+
import { plugin } from '@/system/plugin';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This is a plugin that sets up default documentation for core behave-graph nodes.
|
|
11
|
+
* It uses the documentation store to add rich markdown descriptions,
|
|
12
|
+
* short descriptions, and tags for various node types.
|
|
13
|
+
* @param sys
|
|
14
|
+
*/
|
|
15
|
+
export const docsPluginLoader = (sys: System, _options: void) => {
|
|
16
|
+
const docStore = sys.documentationStore.getState();
|
|
17
|
+
|
|
18
|
+
sys.tabLoader.register('docbrowser', () => {
|
|
19
|
+
return {
|
|
20
|
+
id: 'docbrowser',
|
|
21
|
+
closable: true,
|
|
22
|
+
cached: true,
|
|
23
|
+
title: 'Documentation',
|
|
24
|
+
group: 'headless',
|
|
25
|
+
content: () => (
|
|
26
|
+
<ErrorBoundary fallback={'whoops'}>
|
|
27
|
+
<DocumentationBrowserPanel />
|
|
28
|
+
</ErrorBoundary>
|
|
29
|
+
)
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Flow control nodes
|
|
34
|
+
const flowDocs: NodeDocumentation[] = [
|
|
35
|
+
{
|
|
36
|
+
type: 'flow/debounce',
|
|
37
|
+
shortDescription: 'Debounce rapid flow executions',
|
|
38
|
+
tags: ['flow', 'control', 'async'],
|
|
39
|
+
markdownDescription: `
|
|
40
|
+
Prevents a flow from executing too frequently by enforcing a minimum delay between executions.
|
|
41
|
+
`,
|
|
42
|
+
icon: <BounceRight />
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
type: 'customEvent/trigger',
|
|
47
|
+
shortDescription: 'Trigger a custom event by name',
|
|
48
|
+
tags: ['event', 'custom', 'trigger'],
|
|
49
|
+
markdownDescription: `
|
|
50
|
+
Triggers a custom event by name, allowing other parts of the graph to respond to it.
|
|
51
|
+
|
|
52
|
+
You will need to pass through the required parameters of the event when triggering it.
|
|
53
|
+
`.trim(),
|
|
54
|
+
icon: <Flash />
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
type: 'time/delay',
|
|
59
|
+
shortDescription: 'Delay execution for a specified duration',
|
|
60
|
+
tags: ['time', 'async', 'delay', 'control'],
|
|
61
|
+
markdownDescription: `
|
|
62
|
+
Pauses execution for a specified number of seconds before continuing.
|
|
63
|
+
|
|
64
|
+
Useful for creating timed behaviors, animations, or cooldown mechanics.
|
|
65
|
+
`.trim(),
|
|
66
|
+
icon: <PauseWindow />
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
{
|
|
70
|
+
type: 'debug/expectTrue',
|
|
71
|
+
shortDescription: 'Asserts that a condition is true during execution',
|
|
72
|
+
tags: ['debug', 'assertion', 'testing'],
|
|
73
|
+
markdownDescription: `
|
|
74
|
+
## Purpose
|
|
75
|
+
The Assert Expect True node is a debugging and validation tool used during graph execution to verify runtime conditions. It validates that a boolean condition evaluates to true at a specific point in the execution flow.
|
|
76
|
+
|
|
77
|
+
## Use Cases
|
|
78
|
+
Testing & Debugging: Validate that graph logic produces expected intermediate results
|
|
79
|
+
Runtime Invariants: Enforce constraints that must always hold true during execution
|
|
80
|
+
Quality Assurance: Catch logic errors early in development by asserting expected states
|
|
81
|
+
Documentation: The description field serves as inline documentation of assumptions
|
|
82
|
+
|
|
83
|
+
`.trim()
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
icon: (
|
|
88
|
+
<img src="https://imgs.search.brave.com/I24-hRYZL3tBnj1vmcr525tMOtlP-GFWPoS1qr7tMdo/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly9pbWcu/aWNvbnM4LmNvbS9l/eHRlcm5hbC1mbGF0/aWNvbnMtbGluZWFs/LWNvbG9yLWZsYXQt/aWNvbnMvMTIwMC9l/eHRlcm5hbC1kZWJ1/Zy1tb2JpbGUtYXBw/LWRldmVsb3BtZW50/LWZsYXRpY29ucy1s/aW5lYWwtY29sb3It/ZmxhdC1pY29ucy5q/cGc" />
|
|
89
|
+
),
|
|
90
|
+
type: 'flow/branch',
|
|
91
|
+
shortDescription: 'Execute different flows based on a boolean condition',
|
|
92
|
+
tags: ['flow', 'conditional', 'control'],
|
|
93
|
+
markdownDescription: `
|
|
94
|
+
Conditionally executes one of two flow paths based on a boolean input.
|
|
95
|
+
|
|
96
|
+
## Common Use Cases
|
|
97
|
+
- Implementing if/else logic
|
|
98
|
+
- State-based behavior
|
|
99
|
+
- Conditional event handling
|
|
100
|
+
`.trim()
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'flow/sequence',
|
|
104
|
+
shortDescription: 'Execute multiple flows in order',
|
|
105
|
+
tags: ['flow', 'control', 'sequential'],
|
|
106
|
+
markdownDescription: `
|
|
107
|
+
# Sequence
|
|
108
|
+
|
|
109
|
+
Executes multiple flow outputs in order, one after another.
|
|
110
|
+
|
|
111
|
+
Perfect for chaining multiple actions that need to happen sequentially.
|
|
112
|
+
`.trim()
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'flow/delay',
|
|
116
|
+
shortDescription: 'Delay execution for a specified duration',
|
|
117
|
+
tags: ['flow', 'time', 'async'],
|
|
118
|
+
markdownDescription: `
|
|
119
|
+
# Delay
|
|
120
|
+
|
|
121
|
+
Pauses execution for a specified number of seconds before continuing.
|
|
122
|
+
|
|
123
|
+
Useful for creating timed behaviors, animations, or cooldown mechanics.
|
|
124
|
+
`.trim()
|
|
125
|
+
}
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// Math nodes
|
|
129
|
+
const mathDocs: NodeDocumentation[] = [
|
|
130
|
+
{
|
|
131
|
+
type: 'math/add',
|
|
132
|
+
shortDescription: 'Add two numbers',
|
|
133
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
134
|
+
markdownDescription: 'Calculates the sum of two numeric values: a + b'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: 'math/subtract',
|
|
138
|
+
shortDescription: 'Subtract two numbers',
|
|
139
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
140
|
+
markdownDescription: 'Calculates the difference: a - b'
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: 'math/multiply',
|
|
144
|
+
shortDescription: 'Multiply two numbers',
|
|
145
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
146
|
+
markdownDescription: 'Calculates the product: a * b'
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: 'math/divide',
|
|
150
|
+
shortDescription: 'Divide two numbers',
|
|
151
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
152
|
+
markdownDescription:
|
|
153
|
+
'Calculates the quotient: a / b\\n\\n⚠️ Division by zero returns 0'
|
|
154
|
+
}
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
// Logic nodes
|
|
158
|
+
const logicDocs: NodeDocumentation[] = [
|
|
159
|
+
{
|
|
160
|
+
type: 'logic/and',
|
|
161
|
+
shortDescription: 'Logical AND of two boolean values',
|
|
162
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
163
|
+
markdownDescription: 'Returns true only if both inputs are true'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'logic/or',
|
|
167
|
+
shortDescription: 'Logical OR of two boolean values',
|
|
168
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
169
|
+
markdownDescription: 'Returns true if at least one input is true'
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'logic/not',
|
|
173
|
+
shortDescription: 'Logical NOT - inverts a boolean value',
|
|
174
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
175
|
+
markdownDescription:
|
|
176
|
+
'Inverts the input: true becomes false, false becomes true'
|
|
177
|
+
}
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
// Variable nodes
|
|
181
|
+
const variableDocs: NodeDocumentation[] = [
|
|
182
|
+
{
|
|
183
|
+
type: 'variable/get',
|
|
184
|
+
shortDescription: 'Read a variable value',
|
|
185
|
+
tags: ['variable', 'state', 'getter'],
|
|
186
|
+
markdownDescription: `
|
|
187
|
+
Retrieves the current value of a named variable.
|
|
188
|
+
|
|
189
|
+
Variables are shared across the entire graph and persist between executions.
|
|
190
|
+
`.trim()
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'variable/set',
|
|
194
|
+
shortDescription: 'Write a variable value',
|
|
195
|
+
tags: ['variable', 'state', 'setter'],
|
|
196
|
+
markdownDescription: `
|
|
197
|
+
Sets the value of a named variable.
|
|
198
|
+
|
|
199
|
+
This executes as a flow and passes through to allow chaining.
|
|
200
|
+
`.trim()
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// Event nodes
|
|
205
|
+
const eventDocs: NodeDocumentation[] = [
|
|
206
|
+
{
|
|
207
|
+
type: 'lifecycle/onStart',
|
|
208
|
+
shortDescription: 'Triggered when the graph starts',
|
|
209
|
+
tags: ['event', 'lifecycle', 'entry'],
|
|
210
|
+
markdownDescription: `
|
|
211
|
+
Fires once when the graph begins execution.
|
|
212
|
+
|
|
213
|
+
Perfect for initialization logic and setup tasks.
|
|
214
|
+
`.trim()
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'lifecycle/onEnd',
|
|
218
|
+
shortDescription: 'Triggered when the graph ends',
|
|
219
|
+
tags: ['event', 'lifecycle', 'cleanup'],
|
|
220
|
+
markdownDescription: `
|
|
221
|
+
# On End
|
|
222
|
+
|
|
223
|
+
Fires once when the graph stops execution.
|
|
224
|
+
|
|
225
|
+
Useful for cleanup, saving state, or final actions.
|
|
226
|
+
`.trim()
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: 'lifecycle/onTick',
|
|
230
|
+
shortDescription: 'Triggered every frame/update',
|
|
231
|
+
tags: ['event', 'lifecycle', 'update', 'loop'],
|
|
232
|
+
markdownDescription: `
|
|
233
|
+
# On Tick
|
|
234
|
+
|
|
235
|
+
Fires continuously on every update cycle.
|
|
236
|
+
|
|
237
|
+
⚠️ Use carefully - runs every frame! Great for animations and real-time updates.
|
|
238
|
+
`.trim()
|
|
239
|
+
}
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
// Combine and set all documentation
|
|
243
|
+
const allDocs = [
|
|
244
|
+
...flowDocs,
|
|
245
|
+
...mathDocs,
|
|
246
|
+
...logicDocs,
|
|
247
|
+
...variableDocs,
|
|
248
|
+
...eventDocs
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
docStore.setMultipleDocumentation(allDocs);
|
|
252
|
+
|
|
253
|
+
console.log(`✅ Loaded documentation for ${allDocs.length} nodes`);
|
|
254
|
+
|
|
255
|
+
// Register the documentation panel with TabLoader
|
|
256
|
+
sys.tabLoader.register('docs', () => {
|
|
257
|
+
return {
|
|
258
|
+
id: 'docs',
|
|
259
|
+
closable: true,
|
|
260
|
+
title: 'Documentation',
|
|
261
|
+
group: 'default',
|
|
262
|
+
content: () => (
|
|
263
|
+
<ErrorBoundary fallback={'Error loading Documentation panel'}>
|
|
264
|
+
<DocumentationBrowserPanel />
|
|
265
|
+
</ErrorBoundary>
|
|
266
|
+
)
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Add menu item to Window menu
|
|
271
|
+
const menuStore = sys.menubarStore;
|
|
272
|
+
const currentItems = menuStore.getState().items;
|
|
273
|
+
const windowMenu = currentItems.find((menu) => menu.name === 'window');
|
|
274
|
+
|
|
275
|
+
if (windowMenu) {
|
|
276
|
+
// Add the Documentation menu item to the Window menu
|
|
277
|
+
const newMenuItem = {
|
|
278
|
+
name: 'docs',
|
|
279
|
+
render: function DocsMenuItem() {
|
|
280
|
+
return (
|
|
281
|
+
<MenuItemElement
|
|
282
|
+
onClick={() => sys.tabStore.getState().openTab('docs')}
|
|
283
|
+
key="docs"
|
|
284
|
+
>
|
|
285
|
+
Documentation
|
|
286
|
+
</MenuItemElement>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
menuStore
|
|
292
|
+
.getState()
|
|
293
|
+
.setSubMenuItems('window', [...windowMenu.items, newMenuItem]);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export const docsPlugin = plugin(docsPluginLoader, {
|
|
298
|
+
name: 'docs'
|
|
299
|
+
});
|