@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,191 @@
|
|
|
1
|
+
# Creating Custom Specifics for Nodes
|
|
2
|
+
|
|
3
|
+
Specifics are UI extensions that add custom controls and functionality to specific node types. They allow you to create dynamic interfaces for nodes with configurable sockets, special controls, or custom rendering.
|
|
4
|
+
|
|
5
|
+
## What are Specifics?
|
|
6
|
+
|
|
7
|
+
A "specific" is a React component that gets rendered inside a node when certain conditions are met. It's perfect for:
|
|
8
|
+
- Adding/removing dynamic sockets (like the Switch nodes)
|
|
9
|
+
- Custom dropdown controls (like Custom Event selection)
|
|
10
|
+
- Preview panels (like image previews)
|
|
11
|
+
- Any node-specific UI controls
|
|
12
|
+
|
|
13
|
+
## Creating a Specific
|
|
14
|
+
|
|
15
|
+
A specific consists of three parts:
|
|
16
|
+
|
|
17
|
+
1. **Check function** - Determines if this specific applies to a node
|
|
18
|
+
2. **Render component** - The React component to render
|
|
19
|
+
3. **Unique name** - An identifier for the specific
|
|
20
|
+
|
|
21
|
+
### Example: Dynamic Socket Management
|
|
22
|
+
|
|
23
|
+
Here's how the SwitchOnString specific works:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import React, { useCallback } from 'react';
|
|
27
|
+
import { VscodeButton } from '@vscode-elements/react-elements';
|
|
28
|
+
import { Plus, Minus } from 'iconoir-react';
|
|
29
|
+
import { useSystem } from '@/system/provider';
|
|
30
|
+
import type { SpecificRenderProps } from '@/store/specific';
|
|
31
|
+
|
|
32
|
+
const NAME = 'flow/switch/string.dynamicSockets';
|
|
33
|
+
|
|
34
|
+
export function getSwitchOnStringSpecific() {
|
|
35
|
+
return {
|
|
36
|
+
name: NAME,
|
|
37
|
+
check: (spec: any) => spec?.type === 'flow/switch/string',
|
|
38
|
+
render: SwitchOnStringSpecific
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const SwitchOnStringSpecific: React.FC<SpecificRenderProps> = ({ node }) => {
|
|
43
|
+
const system = useSystem();
|
|
44
|
+
const numCases = node.data?.configuration?.numCases ?? 0;
|
|
45
|
+
|
|
46
|
+
const updateNumCases = useCallback(
|
|
47
|
+
(newNumCases: number) => {
|
|
48
|
+
if (newNumCases < 0) return;
|
|
49
|
+
|
|
50
|
+
// Update the node configuration
|
|
51
|
+
system.nodeStore.getState().setNodes((prev) =>
|
|
52
|
+
prev.map((n: any) => {
|
|
53
|
+
if (n.id !== node.id) return n;
|
|
54
|
+
return {
|
|
55
|
+
...n,
|
|
56
|
+
data: {
|
|
57
|
+
...n.data,
|
|
58
|
+
configuration: {
|
|
59
|
+
...n.data?.configuration,
|
|
60
|
+
numCases: newNumCases
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Invalidate cache to trigger socket recalculation
|
|
68
|
+
system.flowStore.getState().invalidateCache();
|
|
69
|
+
},
|
|
70
|
+
[node.id, system]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const addCase = useCallback(() => {
|
|
74
|
+
updateNumCases(numCases + 1);
|
|
75
|
+
}, [numCases, updateNumCases]);
|
|
76
|
+
|
|
77
|
+
const removeCase = useCallback(() => {
|
|
78
|
+
updateNumCases(Math.max(0, numCases - 1));
|
|
79
|
+
}, [numCases, updateNumCases]);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div style={{ padding: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
83
|
+
<div style={{ fontSize: 12, opacity: 0.9 }}>Cases: {numCases}</div>
|
|
84
|
+
<div style={{ display: 'flex', gap: 4 }}>
|
|
85
|
+
<VscodeButton onClick={addCase}>
|
|
86
|
+
<Plus width={16} height={16} /> Add Case
|
|
87
|
+
</VscodeButton>
|
|
88
|
+
<VscodeButton onClick={removeCase} disabled={numCases === 0}>
|
|
89
|
+
<Minus width={16} height={16} /> Remove
|
|
90
|
+
</VscodeButton>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Registering Specifics
|
|
98
|
+
|
|
99
|
+
### Built-in Registration
|
|
100
|
+
|
|
101
|
+
Default specifics are automatically registered in `registerDefaultSpecifics`. To add yours:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// In registerDefaultSpecifics.ts
|
|
105
|
+
import { getYourCustomSpecific } from './YourCustomSpecific';
|
|
106
|
+
|
|
107
|
+
export function registerDefaultSpecifics(system: System): () => void {
|
|
108
|
+
const store = system.specificStore.getState();
|
|
109
|
+
|
|
110
|
+
const yourCustom = getYourCustomSpecific();
|
|
111
|
+
store.registerSpecific(yourCustom);
|
|
112
|
+
|
|
113
|
+
return () => {
|
|
114
|
+
system.specificStore.getState().unregisterSpecific(yourCustom.name);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### External Registration
|
|
120
|
+
|
|
121
|
+
You can also register specifics from external packages:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { System } from '@kiberon-labs/behave-graph-flow';
|
|
125
|
+
import { getSwitchOnStringSpecific } from '@kiberon-labs/behave-graph-flow';
|
|
126
|
+
|
|
127
|
+
export function registerMyPlugin(system: System) {
|
|
128
|
+
const switchSpecific = getSwitchOnStringSpecific();
|
|
129
|
+
system.specificStore.getState().registerSpecific(switchSpecific);
|
|
130
|
+
|
|
131
|
+
// Or create your own
|
|
132
|
+
const mySpecific = {
|
|
133
|
+
name: 'my-custom-specific',
|
|
134
|
+
check: (spec) => spec.type === 'my/custom/node',
|
|
135
|
+
render: MyCustomComponent
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
system.specificStore.getState().registerSpecific(mySpecific);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Key Patterns
|
|
143
|
+
|
|
144
|
+
### Updating Node Configuration
|
|
145
|
+
|
|
146
|
+
When your specific needs to update node configuration (like changing socket counts):
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
system.nodeStore.getState().setNodes((prev) =>
|
|
150
|
+
prev.map((n: any) => {
|
|
151
|
+
if (n.id !== node.id) return n;
|
|
152
|
+
return {
|
|
153
|
+
...n,
|
|
154
|
+
data: {
|
|
155
|
+
...n.data,
|
|
156
|
+
configuration: {
|
|
157
|
+
...n.data?.configuration,
|
|
158
|
+
yourConfigKey: newValue
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Important: Invalidate cache to recalculate sockets
|
|
166
|
+
system.flowStore.getState().invalidateCache();
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Accessing Node Data
|
|
170
|
+
|
|
171
|
+
The `node` prop contains:
|
|
172
|
+
- `id` - Node ID
|
|
173
|
+
- `data` - Node data (configuration, ports, annotations)
|
|
174
|
+
- `spec` - Node specification
|
|
175
|
+
- `selected` - Whether node is selected
|
|
176
|
+
|
|
177
|
+
## Available Specifics
|
|
178
|
+
|
|
179
|
+
Currently registered specifics:
|
|
180
|
+
|
|
181
|
+
1. **CustomEventOnTriggered** - Dropdown for selecting custom events
|
|
182
|
+
2. **SwitchOnString** - Add/remove string case sockets
|
|
183
|
+
3. **SwitchOnInteger** - Add/remove integer case sockets
|
|
184
|
+
|
|
185
|
+
## Tips
|
|
186
|
+
|
|
187
|
+
- Keep specifics focused on a single node type
|
|
188
|
+
- Use unique names to avoid conflicts
|
|
189
|
+
- Always invalidate the flow cache when changing node configuration that affects sockets
|
|
190
|
+
- Use inline styles or CSS modules (not Tailwind per AI instructions)
|
|
191
|
+
- Leverage the `useSystem` hook to access all system stores
|
package/package.json
CHANGED
|
@@ -1,42 +1,106 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiberon-labs/behave-graph-flow",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./entry.css": "./dist/entry.css",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
6
14
|
"main": "./dist/index.js",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"test": "vitest",
|
|
12
|
-
"check": "oxfmt --check",
|
|
13
|
-
"format": "oxfmt",
|
|
14
|
-
"lint": "oxlint",
|
|
15
|
-
"typecheck": "tsc"
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"provenance": true,
|
|
18
|
+
"access": "public"
|
|
16
19
|
},
|
|
17
20
|
"devDependencies": {
|
|
18
|
-
"@
|
|
19
|
-
"@
|
|
21
|
+
"@bosh-code/tsdown-plugin-inject-css": "^2.0.0",
|
|
22
|
+
"@bosh-code/tsdown-plugin-tailwindcss": "^1.0.1",
|
|
23
|
+
"@storybook/addon-a11y": "^10.0.8",
|
|
24
|
+
"@storybook/addon-docs": "^10.0.8",
|
|
25
|
+
"@storybook/addon-vitest": "^10.0.8",
|
|
26
|
+
"@storybook/react-vite": "^10.0.8",
|
|
27
|
+
"@types/dagre": "^0.7.53",
|
|
28
|
+
"@types/graphlib": "^2.1.12",
|
|
20
29
|
"@types/react": "^19.2.6",
|
|
21
30
|
"@types/uuid": "^8.3.4",
|
|
31
|
+
"@vitest/browser": "^4.0.10",
|
|
32
|
+
"@vitest/browser-playwright": "4.0.10",
|
|
22
33
|
"autoprefixer": "^10.4.22",
|
|
34
|
+
"fallow": "^2.89.0",
|
|
35
|
+
"happy-dom": "^20.4.0",
|
|
36
|
+
"jsdom": "^27.3.0",
|
|
37
|
+
"oxfmt": "^0.14.0",
|
|
23
38
|
"oxlint": "^1.29.0",
|
|
24
|
-
"
|
|
39
|
+
"playwright": "^1.60.0",
|
|
40
|
+
"react": "19.2.3",
|
|
41
|
+
"react-dom": "19.2.3",
|
|
42
|
+
"reactflow": "^11.11.4",
|
|
43
|
+
"storybook": "^10.0.8",
|
|
25
44
|
"tailwindcss": "^4.1.17",
|
|
26
45
|
"tsdown": "^0.16.5",
|
|
46
|
+
"unplugin-lightningcss": "^0.4.3",
|
|
47
|
+
"vite": "^7.2.4",
|
|
48
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
27
49
|
"vitest": "^4.0.10",
|
|
28
|
-
"
|
|
50
|
+
"vitest-browser-react": "^2.2.0",
|
|
51
|
+
"@kiberon-labs/behave-graph": "1.1.0"
|
|
29
52
|
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/Kiberon-Labs/behave-graph",
|
|
56
|
+
"directory": "packages/flow"
|
|
57
|
+
},
|
|
58
|
+
"author": "kiberonlabsdev",
|
|
59
|
+
"bugs": "https://github.com/Kiberon-Labs/behave-graph/issues",
|
|
30
60
|
"dependencies": {
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
61
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
62
|
+
"@reactflow/node-resizer": "^2.2.14",
|
|
63
|
+
"@tiptap/react": "^3.19.0",
|
|
64
|
+
"@tiptap/starter-kit": "^3.19.0",
|
|
65
|
+
"@vscode-elements/react-elements": "^2.4.0",
|
|
34
66
|
"classnames": "^2.3.1",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
67
|
+
"clsx": "^2.1.1",
|
|
68
|
+
"copy-to-clipboard": "^3.3.3",
|
|
69
|
+
"dagre": "^0.8.5",
|
|
70
|
+
"dockview": "^4.11.0",
|
|
71
|
+
"elkjs": "^0.11.0",
|
|
72
|
+
"fastify": "^5.7.4",
|
|
73
|
+
"fuse.js": "^7.1.0",
|
|
74
|
+
"graphlib": "^2.1.8",
|
|
75
|
+
"iconoir-react": "^7.11.0",
|
|
76
|
+
"json-edit-react": "^1.29.0",
|
|
77
|
+
"rc-dock": "4.0.0-alpha.2",
|
|
78
|
+
"rc-menu": "^9.16.1",
|
|
79
|
+
"react-colorful": "^5.6.1",
|
|
80
|
+
"react-error-boundary": "^6.0.0",
|
|
81
|
+
"react-hot-toast": "^2.6.0",
|
|
82
|
+
"react-hotkeys": "^2.0.0",
|
|
83
|
+
"tiptap-markdown": "^0.9.0",
|
|
84
|
+
"uuid": "^8.3.2",
|
|
85
|
+
"zod": "^4.1.13",
|
|
86
|
+
"zustand": "^5.0.8"
|
|
37
87
|
},
|
|
38
88
|
"peerDependencies": {
|
|
39
89
|
"reactflow": "^11.1.1",
|
|
40
|
-
"@kiberon-labs/behave-graph": "
|
|
90
|
+
"@kiberon-labs/behave-graph": "1.1.0"
|
|
91
|
+
},
|
|
92
|
+
"scripts": {
|
|
93
|
+
"audit": "fallow",
|
|
94
|
+
"dev": "tsdown --watch src",
|
|
95
|
+
"build": "tsdown",
|
|
96
|
+
"test": "vitest",
|
|
97
|
+
"test:visual": "vitest run --config vitest.visual.config.ts",
|
|
98
|
+
"test:visual:update": "vitest run --config vitest.visual.config.ts --update",
|
|
99
|
+
"check": "oxfmt --check",
|
|
100
|
+
"format": "oxfmt",
|
|
101
|
+
"lint": "oxlint",
|
|
102
|
+
"storybook": "storybook dev -p 6006",
|
|
103
|
+
"build-storybook": "storybook build",
|
|
104
|
+
"typecheck": "tsc --noEmit -p tsconfig.prod.json"
|
|
41
105
|
}
|
|
42
106
|
}
|
package/postcss.config.ts
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//Used on ports to indicate meta from a ui
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Indicates that the port can be completely deleted
|
|
5
|
+
*/
|
|
6
|
+
export const nonDeletable = 'ui.nonDeletable';
|
|
7
|
+
/**
|
|
8
|
+
* Indicates that the port can be reset to its default value / type
|
|
9
|
+
*/
|
|
10
|
+
export const resetable = 'ui.resetable';
|
|
11
|
+
/**
|
|
12
|
+
* Indicates that the port cannot be edited
|
|
13
|
+
*/
|
|
14
|
+
export const readonly = 'ui.readonly';
|
|
15
|
+
/**
|
|
16
|
+
* Indicates that the port is hidden from the user
|
|
17
|
+
*/
|
|
18
|
+
export const hidden = 'ui.hidden';
|
|
19
|
+
|
|
20
|
+
//Used on nodes and graph
|
|
21
|
+
export const annotatedTitle = 'ui.title';
|
|
22
|
+
export const description = 'ui.description';
|
|
23
|
+
export const executing = 'ui.executing';
|
|
24
|
+
export const pinned = 'ui.pinned';
|
|
25
|
+
export const layerId = 'ui.layerId';
|
|
26
|
+
|
|
27
|
+
//Used exclusively on graph
|
|
28
|
+
export const uiVersion = 'ui.version';
|
|
29
|
+
|
|
30
|
+
export const realtime = 'ui.realtime';
|
|
31
|
+
//UI annotation to detect realtime output nodes, which are used to display outputs in the UI without affecting the actual graph outputs
|
|
32
|
+
export const AnnotatedOutput = 'ui.annotatedOutput';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.panel {
|
|
2
|
+
margin: 0.5rem;
|
|
3
|
+
pointer-events: auto;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.toolbar {
|
|
7
|
+
display: flex;
|
|
8
|
+
gap: 0.5rem;
|
|
9
|
+
padding: 0.25rem;
|
|
10
|
+
background: var(--colors-bgSurface);
|
|
11
|
+
border: 1px solid var(--colors-borderSubtle);
|
|
12
|
+
border-radius: 0.375rem;
|
|
13
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.buttonGroup {
|
|
17
|
+
display: flex;
|
|
18
|
+
gap: 0.125rem;
|
|
19
|
+
padding: 0.125rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.buttonGroup vscode-button {
|
|
23
|
+
min-width: 28px;
|
|
24
|
+
height: 28px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.zoomLevel {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
padding: 0 0.5rem;
|
|
31
|
+
font-size: 0.85rem;
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
color: var(--colors-fg);
|
|
34
|
+
user-select: none;
|
|
35
|
+
min-width: 3rem;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.contextMenu {
|
|
40
|
+
position: absolute;
|
|
41
|
+
top: 100%;
|
|
42
|
+
left: 0;
|
|
43
|
+
margin-top: 0.25rem;
|
|
44
|
+
z-index: 1000;
|
|
45
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import React, { useCallback, useState, useRef } from 'react';
|
|
2
|
+
import { Panel, useViewport } from 'reactflow';
|
|
3
|
+
import {
|
|
4
|
+
VscodeButton,
|
|
5
|
+
VscodeContextMenu
|
|
6
|
+
} from '@vscode-elements/react-elements';
|
|
7
|
+
import { ZoomIn, ZoomOut, AlignLeft, LayoutRight } from 'iconoir-react';
|
|
8
|
+
import { useSystem } from '@/system/provider';
|
|
9
|
+
import { useStore } from 'zustand';
|
|
10
|
+
import styles from './index.module.css';
|
|
11
|
+
import { useRefFromStore } from '@/system';
|
|
12
|
+
import type { ToolbarButton } from '@/store/toolbar';
|
|
13
|
+
import type { AlignmentAxis, AlignmentType } from '@/plugin/alignment';
|
|
14
|
+
|
|
15
|
+
export const FloatingToolbar: React.FC = () => {
|
|
16
|
+
const system = useSystem();
|
|
17
|
+
const visible = useStore(system.toolbarStore, (x) => x.visible);
|
|
18
|
+
const customGroups = useStore(system.toolbarStore, (x) => x.groups);
|
|
19
|
+
const reactFlowInstance = useRefFromStore(system.refStore, 'reactflow');
|
|
20
|
+
const { zoom } = useViewport();
|
|
21
|
+
const currentZoom = Math.round(zoom * 100);
|
|
22
|
+
const [alignMenuOpen, setAlignMenuOpen] = useState(false);
|
|
23
|
+
const [distributeMenuOpen, setDistributeMenuOpen] = useState(false);
|
|
24
|
+
const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
|
|
25
|
+
const alignBtnRef = useRef<any>(null);
|
|
26
|
+
const distributeBtnRef = useRef<any>(null);
|
|
27
|
+
|
|
28
|
+
const publishAlignment = useCallback(
|
|
29
|
+
(type: AlignmentType, axis: AlignmentAxis) => {
|
|
30
|
+
system.pubsub.publishSync('alignment:align', { type, axis });
|
|
31
|
+
},
|
|
32
|
+
[system]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const publishDistribution = useCallback(
|
|
36
|
+
(type: AlignmentType, axis: AlignmentAxis) => {
|
|
37
|
+
system.pubsub.publishSync('alignment:distribute', { type, axis });
|
|
38
|
+
},
|
|
39
|
+
[system]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const handleZoomIn = useCallback(() => {
|
|
43
|
+
reactFlowInstance?.zoomIn();
|
|
44
|
+
}, [reactFlowInstance]);
|
|
45
|
+
|
|
46
|
+
const handleZoomOut = useCallback(() => {
|
|
47
|
+
reactFlowInstance?.zoomOut();
|
|
48
|
+
}, [reactFlowInstance]);
|
|
49
|
+
|
|
50
|
+
const handleAlignSelect = useCallback(
|
|
51
|
+
(e: any) => {
|
|
52
|
+
setAlignMenuOpen(false);
|
|
53
|
+
const value = e.detail?.value;
|
|
54
|
+
switch (value) {
|
|
55
|
+
case 'left':
|
|
56
|
+
publishAlignment('start', 'x');
|
|
57
|
+
break;
|
|
58
|
+
case 'center-h':
|
|
59
|
+
publishAlignment('center', 'x');
|
|
60
|
+
break;
|
|
61
|
+
case 'right':
|
|
62
|
+
publishAlignment('end', 'x');
|
|
63
|
+
break;
|
|
64
|
+
case 'top':
|
|
65
|
+
publishAlignment('start', 'y');
|
|
66
|
+
break;
|
|
67
|
+
case 'center-v':
|
|
68
|
+
publishAlignment('center', 'y');
|
|
69
|
+
break;
|
|
70
|
+
case 'bottom':
|
|
71
|
+
publishAlignment('end', 'y');
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
[publishAlignment]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const handleDistributeSelect = useCallback(
|
|
79
|
+
(e: any) => {
|
|
80
|
+
setDistributeMenuOpen(false);
|
|
81
|
+
const value = e.detail?.value;
|
|
82
|
+
switch (value) {
|
|
83
|
+
case 'left':
|
|
84
|
+
publishDistribution('start', 'x');
|
|
85
|
+
break;
|
|
86
|
+
case 'center-h':
|
|
87
|
+
publishDistribution('center', 'x');
|
|
88
|
+
break;
|
|
89
|
+
case 'right':
|
|
90
|
+
publishDistribution('end', 'x');
|
|
91
|
+
break;
|
|
92
|
+
case 'top':
|
|
93
|
+
publishDistribution('start', 'y');
|
|
94
|
+
break;
|
|
95
|
+
case 'center-v':
|
|
96
|
+
publishDistribution('center', 'y');
|
|
97
|
+
break;
|
|
98
|
+
case 'bottom':
|
|
99
|
+
publishDistribution('end', 'y');
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
[publishDistribution]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (!visible) return null;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<>
|
|
110
|
+
<Panel position="top-center" className={styles.panel}>
|
|
111
|
+
<div className={styles.toolbar}>
|
|
112
|
+
{/* Zoom Controls */}
|
|
113
|
+
<div className={styles.buttonGroup}>
|
|
114
|
+
<VscodeButton
|
|
115
|
+
secondary
|
|
116
|
+
iconOnly
|
|
117
|
+
title="Zoom In"
|
|
118
|
+
onClick={handleZoomIn}
|
|
119
|
+
>
|
|
120
|
+
<ZoomIn />
|
|
121
|
+
</VscodeButton>
|
|
122
|
+
<span className={styles.zoomLevel}>{currentZoom}%</span>
|
|
123
|
+
<VscodeButton
|
|
124
|
+
secondary
|
|
125
|
+
iconOnly
|
|
126
|
+
title="Zoom Out"
|
|
127
|
+
onClick={handleZoomOut}
|
|
128
|
+
>
|
|
129
|
+
<ZoomOut />
|
|
130
|
+
</VscodeButton>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
{/* Align Button */}
|
|
134
|
+
<div className={styles.buttonGroup}>
|
|
135
|
+
<VscodeButton
|
|
136
|
+
ref={alignBtnRef}
|
|
137
|
+
secondary
|
|
138
|
+
iconOnly
|
|
139
|
+
title="Align"
|
|
140
|
+
onClick={() => {
|
|
141
|
+
const rect = alignBtnRef.current?.getBoundingClientRect();
|
|
142
|
+
if (rect) {
|
|
143
|
+
setMenuPosition({ top: rect.bottom, left: rect.left });
|
|
144
|
+
setAlignMenuOpen(!alignMenuOpen);
|
|
145
|
+
}
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
<AlignLeft />
|
|
149
|
+
</VscodeButton>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Distribute Button */}
|
|
153
|
+
<div className={styles.buttonGroup}>
|
|
154
|
+
<VscodeButton
|
|
155
|
+
ref={distributeBtnRef}
|
|
156
|
+
secondary
|
|
157
|
+
iconOnly
|
|
158
|
+
title="Distribute"
|
|
159
|
+
onClick={() => {
|
|
160
|
+
const rect = distributeBtnRef.current?.getBoundingClientRect();
|
|
161
|
+
if (rect) {
|
|
162
|
+
setMenuPosition({ top: rect.bottom, left: rect.left });
|
|
163
|
+
setDistributeMenuOpen(!distributeMenuOpen);
|
|
164
|
+
}
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
<LayoutRight />
|
|
168
|
+
</VscodeButton>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
{/* Custom Groups */}
|
|
172
|
+
{customGroups.map((group) => (
|
|
173
|
+
<div key={group.id} className={styles.buttonGroup}>
|
|
174
|
+
{(Array.isArray(group.buttons) ? group.buttons : []).map(
|
|
175
|
+
(button: ToolbarButton | React.ReactNode, index: number) => {
|
|
176
|
+
// Support custom ReactNode buttons
|
|
177
|
+
if (React.isValidElement(button)) {
|
|
178
|
+
return (
|
|
179
|
+
<React.Fragment key={index}>{button}</React.Fragment>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Standard button definition
|
|
184
|
+
const typedButton = button as ToolbarButton;
|
|
185
|
+
const isDisabled =
|
|
186
|
+
typeof typedButton.disabled === 'function'
|
|
187
|
+
? typedButton.disabled()
|
|
188
|
+
: typedButton.disabled;
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<VscodeButton
|
|
192
|
+
key={typedButton.id}
|
|
193
|
+
secondary
|
|
194
|
+
iconOnly
|
|
195
|
+
title={typedButton.label}
|
|
196
|
+
onClick={typedButton.onClick}
|
|
197
|
+
disabled={isDisabled}
|
|
198
|
+
>
|
|
199
|
+
{typedButton.icon}
|
|
200
|
+
</VscodeButton>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
207
|
+
</Panel>
|
|
208
|
+
|
|
209
|
+
{/* Align Context Menu */}
|
|
210
|
+
{alignMenuOpen && (
|
|
211
|
+
<VscodeContextMenu
|
|
212
|
+
show
|
|
213
|
+
onVscContextMenuSelect={handleAlignSelect}
|
|
214
|
+
style={{
|
|
215
|
+
position: 'fixed',
|
|
216
|
+
top: `${menuPosition.top}px`,
|
|
217
|
+
left: `${menuPosition.left}px`,
|
|
218
|
+
zIndex: 2000
|
|
219
|
+
}}
|
|
220
|
+
data={[
|
|
221
|
+
{ label: 'Align Left', value: 'left' },
|
|
222
|
+
{ label: 'Align Center Horizontal', value: 'center-h' },
|
|
223
|
+
{ label: 'Align Right', value: 'right' },
|
|
224
|
+
{ separator: true },
|
|
225
|
+
{ label: 'Align Top', value: 'top' },
|
|
226
|
+
{ label: 'Align Center Vertical', value: 'center-v' },
|
|
227
|
+
{ label: 'Align Bottom', value: 'bottom' }
|
|
228
|
+
]}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{/* Distribute Context Menu */}
|
|
233
|
+
{distributeMenuOpen && (
|
|
234
|
+
<VscodeContextMenu
|
|
235
|
+
show
|
|
236
|
+
onVscContextMenuSelect={handleDistributeSelect}
|
|
237
|
+
style={{
|
|
238
|
+
position: 'fixed',
|
|
239
|
+
top: `${menuPosition.top}px`,
|
|
240
|
+
left: `${menuPosition.left}px`,
|
|
241
|
+
zIndex: 2000
|
|
242
|
+
}}
|
|
243
|
+
data={[
|
|
244
|
+
{ label: 'Distribute Horizontal Left', value: 'left' },
|
|
245
|
+
{ label: 'Distribute Horizontal Center', value: 'center-h' },
|
|
246
|
+
{ label: 'Distribute Horizontal Right', value: 'right' },
|
|
247
|
+
{ separator: true },
|
|
248
|
+
{ label: 'Distribute Vertical Top', value: 'top' },
|
|
249
|
+
{ label: 'Distribute Vertical Center', value: 'center-v' },
|
|
250
|
+
{ label: 'Distribute Vertical Bottom', value: 'bottom' }
|
|
251
|
+
]}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
254
|
+
</>
|
|
255
|
+
);
|
|
256
|
+
};
|