@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,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mergeSockets } from '@/util/mergeSockets';
|
|
3
|
+
|
|
4
|
+
const specSocket = (name: string, valueType: string) =>
|
|
5
|
+
({ name, valueType }) as any;
|
|
6
|
+
|
|
7
|
+
describe('mergeSockets', () => {
|
|
8
|
+
it('maps spec sockets to socket-base objects keyed by name', () => {
|
|
9
|
+
const result = mergeSockets([
|
|
10
|
+
specSocket('a', 'flow'),
|
|
11
|
+
specSocket('b', 'string')
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
expect(result).toHaveLength(2);
|
|
15
|
+
expect(result.map((s) => s.key)).toEqual(['a', 'b']);
|
|
16
|
+
expect(result[0]).toMatchObject({ key: 'a', name: 'a', valueType: 'flow' });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns spec sockets unchanged when no dynamic ports are given', () => {
|
|
20
|
+
const result = mergeSockets([specSocket('a', 'flow')], undefined);
|
|
21
|
+
expect(result).toEqual([{ key: 'a', name: 'a', valueType: 'flow' }]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('lets a dynamic port override a spec socket with the same key', () => {
|
|
25
|
+
const result = mergeSockets(
|
|
26
|
+
[specSocket('a', 'flow'), specSocket('b', 'string')],
|
|
27
|
+
[{ key: 'b', name: 'b', valueType: 'integer' } as any]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(result).toHaveLength(2);
|
|
31
|
+
const b = result.find((s) => s.key === 'b');
|
|
32
|
+
expect(b?.valueType).toBe('integer');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('appends dynamic ports that do not match any spec socket', () => {
|
|
36
|
+
const result = mergeSockets(
|
|
37
|
+
[specSocket('a', 'flow')],
|
|
38
|
+
[{ key: 'extra', name: 'extra', valueType: 'boolean' } as any]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect(result.map((s) => s.key)).toEqual(['a', 'extra']);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Visual (pixel) regression tests
|
|
2
|
+
|
|
3
|
+
These tests render React components in a **real browser** (Chromium via Playwright)
|
|
4
|
+
and compare a screenshot against a committed baseline using Vitest's built-in
|
|
5
|
+
[`toMatchScreenshot`](https://vitest.dev/guide/browser/visual-regression-testing)
|
|
6
|
+
assertion. They catch unintended rendering regressions that DOM-only unit tests
|
|
7
|
+
miss.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
Coverage is deliberately minimal and panel-focused: **one snapshot per editor
|
|
12
|
+
panel** (see [`panels.visual.test.tsx`](./panels.visual.test.tsx)), since the
|
|
13
|
+
panels are the composed, regression-prone surfaces. Each panel is rendered
|
|
14
|
+
through the same [`DefaultSystemProvider`](../../stories/defaults/defaultStoryProvider.tsx)
|
|
15
|
+
the Storybook stories use, so it shows representative content.
|
|
16
|
+
|
|
17
|
+
They are intentionally **separate** from the default `pnpm test` run (which uses
|
|
18
|
+
happy-dom) so the heavier browser runner only starts when explicitly requested.
|
|
19
|
+
|
|
20
|
+
## Running
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# one-time: install the Chromium binary Playwright drives
|
|
24
|
+
npx playwright install chromium
|
|
25
|
+
|
|
26
|
+
# verify components against the committed baselines
|
|
27
|
+
pnpm test:visual
|
|
28
|
+
|
|
29
|
+
# create/refresh baselines (after an intentional visual change)
|
|
30
|
+
pnpm test:visual:update
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
- Config: [`vitest.visual.config.ts`](../../vitest.visual.config.ts) — browser
|
|
36
|
+
mode, Playwright/Chromium, headless, fixed `900x700` viewport.
|
|
37
|
+
- Test files: `tests/visual/**/*.visual.test.tsx`.
|
|
38
|
+
- Each panel is rendered inside a fixed-size `640x480` frame on the editor
|
|
39
|
+
background so screenshots are bounded and stable. The full app stylesheet
|
|
40
|
+
(`@/index.css`) is imported so `--vscode-*` theme tokens resolve.
|
|
41
|
+
- Baselines live in `__screenshots__/` and are committed. Vitest names them per
|
|
42
|
+
platform (e.g. `*-chromium-win32.png`), because font rendering and
|
|
43
|
+
anti-aliasing differ across operating systems.
|
|
44
|
+
|
|
45
|
+
## Important: baselines are platform-specific
|
|
46
|
+
|
|
47
|
+
Generate and verify baselines on the **same OS / CI image**. A baseline captured
|
|
48
|
+
on Windows will not match one rendered on Linux. For CI, run
|
|
49
|
+
`pnpm test:visual:update` on the CI platform once (or in a container that matches
|
|
50
|
+
CI) and commit those baselines. The comparator is configured with a small
|
|
51
|
+
`allowedMismatchedPixelRatio` to tolerate negligible anti-aliasing noise.
|
|
52
|
+
|
|
53
|
+
## Adding a panel
|
|
54
|
+
|
|
55
|
+
When a new panel is added under `src/components/panels`, add one entry to the
|
|
56
|
+
`panels` array in [`panels.visual.test.tsx`](./panels.visual.test.tsx):
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
['myPanel', <MyPanel />],
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then run `pnpm test:visual:update` to create the baseline, eyeball the generated
|
|
63
|
+
PNG, and commit it. Keep it to a single representative snapshot per panel — the
|
|
64
|
+
unit tests under `tests/util` and `tests/components` cover finer-grained logic.
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png
ADDED
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png
ADDED
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png
ADDED
|
Binary file
|
|
Binary file
|
package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Full editor stylesheet (vscode theme vars, rc-dock, reactflow, etc.) so the
|
|
2
|
+
// panels render the way they do in the real app.
|
|
3
|
+
import '@/index.css';
|
|
4
|
+
|
|
5
|
+
import type { ReactElement } from 'react';
|
|
6
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
7
|
+
import { page } from 'vitest/browser';
|
|
8
|
+
import { cleanup, render } from 'vitest-browser-react';
|
|
9
|
+
import { DefaultSystemProvider } from '../../stories/defaults/defaultStoryProvider';
|
|
10
|
+
|
|
11
|
+
import { AlignmentPanel } from '@/components/panels/alignment';
|
|
12
|
+
import { ConversationPanel } from '@/components/panels/conversation';
|
|
13
|
+
import { EventsPanel } from '@/components/panels/events';
|
|
14
|
+
import { HistoryPanel } from '@/components/panels/history';
|
|
15
|
+
import { KeymapsPanel } from '@/components/panels/keymaps';
|
|
16
|
+
import { LayersPanel } from '@/components/panels/layers';
|
|
17
|
+
import { LegendPanel } from '@/components/panels/legend';
|
|
18
|
+
import { LogsPanel } from '@/components/panels/logs';
|
|
19
|
+
import { NodeInputsPanel } from '@/components/panels/nodeInputs';
|
|
20
|
+
import { NodePickerPanel } from '@/components/panels/nodePicker';
|
|
21
|
+
import { PanelPanel } from '@/components/panels/panel';
|
|
22
|
+
import { SearchPanel } from '@/components/panels/search';
|
|
23
|
+
import { Settings } from '@/components/panels/systemSettings';
|
|
24
|
+
import { TracesPanel } from '@/components/panels/traces';
|
|
25
|
+
import { VariablesPanel } from '@/components/panels/variables';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* One pixel-snapshot per panel. The shared {@link DefaultSystemProvider} (the
|
|
29
|
+
* same provider the Storybook stories use) supplies a populated System so each
|
|
30
|
+
* panel renders representative content.
|
|
31
|
+
*/
|
|
32
|
+
const panels: ReadonlyArray<readonly [name: string, element: ReactElement]> = [
|
|
33
|
+
['alignment', <AlignmentPanel />],
|
|
34
|
+
['conversation', <ConversationPanel />],
|
|
35
|
+
['events', <EventsPanel />],
|
|
36
|
+
['history', <HistoryPanel />],
|
|
37
|
+
['keymaps', <KeymapsPanel />],
|
|
38
|
+
['layers', <LayersPanel />],
|
|
39
|
+
['legend', <LegendPanel />],
|
|
40
|
+
['logs', <LogsPanel />],
|
|
41
|
+
['nodeInputs', <NodeInputsPanel />],
|
|
42
|
+
['nodePicker', <NodePickerPanel />],
|
|
43
|
+
['panel', <PanelPanel />],
|
|
44
|
+
['search', <SearchPanel />],
|
|
45
|
+
['systemSettings', <Settings />],
|
|
46
|
+
['traces', <TracesPanel />],
|
|
47
|
+
['variables', <VariablesPanel />]
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
cleanup();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('panels (visual)', () => {
|
|
55
|
+
it.each(panels)('renders the %s panel', async (name, element) => {
|
|
56
|
+
render(
|
|
57
|
+
<DefaultSystemProvider>
|
|
58
|
+
<div
|
|
59
|
+
data-testid="panel-frame"
|
|
60
|
+
style={{
|
|
61
|
+
width: 640,
|
|
62
|
+
height: 480,
|
|
63
|
+
overflow: 'auto',
|
|
64
|
+
background: 'var(--vscode-editor-background, #1f1f1f)'
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
{element}
|
|
68
|
+
</div>
|
|
69
|
+
</DefaultSystemProvider>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
await expect(page.getByTestId('panel-frame')).toMatchScreenshot(
|
|
73
|
+
`panel-${name}`
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Monorepo shared library – base (no references)",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
/* Base Options: */
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"target": "esnext",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
/* Strictness */
|
|
15
|
+
"strict": true,
|
|
16
|
+
"noUncheckedIndexedAccess": true,
|
|
17
|
+
"noImplicitOverride": true,
|
|
18
|
+
"erasableSyntaxOnly": true,
|
|
19
|
+
/* Opinion */
|
|
20
|
+
"module": "preserve",
|
|
21
|
+
"outDir": "./dist",
|
|
22
|
+
"paths": {
|
|
23
|
+
"@/*": [
|
|
24
|
+
"./src/*"
|
|
25
|
+
],
|
|
26
|
+
"~/*": [
|
|
27
|
+
"./stories/*"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"declaration": true,
|
|
31
|
+
"declarationMap": true,
|
|
32
|
+
"jsx": "react-jsx",
|
|
33
|
+
"lib": [
|
|
34
|
+
"dom",
|
|
35
|
+
"dom.iterable",
|
|
36
|
+
"esnext"
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,60 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
-
"display": "Monorepo shared library",
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/* Opinion */
|
|
20
|
-
"incremental": true,
|
|
21
|
-
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
|
|
22
|
-
"module": "preserve",
|
|
23
|
-
"outDir": "./dist",
|
|
24
|
-
"baseUrl": ".",
|
|
25
|
-
"rootDir": "./src",
|
|
26
|
-
"paths": {
|
|
27
|
-
"~/*": [
|
|
28
|
-
"./src/*"
|
|
29
|
-
],
|
|
30
|
-
"@/*": [
|
|
31
|
-
"./src/*"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
/* Required for project references, which provide go-to-definition in your
|
|
35
|
-
IDE without first having to build the module, which is essential during development. */
|
|
36
|
-
"composite": true,
|
|
37
|
-
/* Assuming your bundler will output everything, but we can not have noEmit
|
|
38
|
-
enabled because it is not compatible with composite / project references. Also declaration might be required for references to work fully. Not sure yet... */
|
|
39
|
-
"declaration": true,
|
|
40
|
-
"declarationMap": true,
|
|
41
|
-
"jsx": "react-jsx",
|
|
42
|
-
"lib": [
|
|
43
|
-
"dom",
|
|
44
|
-
"dom.iterable",
|
|
45
|
-
"esnext"
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
"include": [
|
|
49
|
-
"./src",
|
|
50
|
-
"./src/**/*.json",
|
|
51
|
-
],
|
|
52
|
-
"exclude": [
|
|
53
|
-
"!./src/**"
|
|
54
|
-
],
|
|
55
|
-
"references": [
|
|
56
|
-
{
|
|
57
|
-
"path": "../core"
|
|
58
|
-
}
|
|
59
|
-
],
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Monorepo shared library",
|
|
4
|
+
"extends": "./tsconfig.base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"rootDir": ".",
|
|
7
|
+
/* Required for project references, which provide go-to-definition in your
|
|
8
|
+
IDE without first having to build the module, which is essential during development. */
|
|
9
|
+
"composite": true,
|
|
10
|
+
"incremental": true,
|
|
11
|
+
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"./**/*.ts",
|
|
15
|
+
"./**/*.tsx",
|
|
16
|
+
"./**/*.json",
|
|
17
|
+
],
|
|
18
|
+
"exclude": []
|
|
60
19
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"composite": false,
|
|
6
|
+
"paths": {
|
|
7
|
+
"@/*": [
|
|
8
|
+
"./src/*"
|
|
9
|
+
],
|
|
10
|
+
"~/*": [
|
|
11
|
+
"./stories/*"
|
|
12
|
+
],
|
|
13
|
+
"@kiberon-labs/behave-graph": [
|
|
14
|
+
"../core/dist/index.d.ts"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"./src",
|
|
20
|
+
"./src/**/*.json",
|
|
21
|
+
],
|
|
22
|
+
"exclude": [],
|
|
23
|
+
}
|
package/tsdown.config.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { defineConfig } from 'tsdown';
|
|
2
|
+
import LightningCSS from 'unplugin-lightningcss/rolldown';
|
|
2
3
|
|
|
3
4
|
export default defineConfig({
|
|
4
|
-
entry:
|
|
5
|
+
entry: {
|
|
6
|
+
index: './src/index.ts'
|
|
7
|
+
},
|
|
8
|
+
|
|
5
9
|
outDir: 'dist',
|
|
6
10
|
target: 'es2022',
|
|
7
11
|
sourcemap: true,
|
|
12
|
+
tsconfig: 'tsconfig.prod.json',
|
|
8
13
|
skipNodeModulesBundle: true,
|
|
9
|
-
|
|
14
|
+
copy: [{ from: 'src/entry.css', to: 'dist/entry.css' }],
|
|
15
|
+
plugins: [LightningCSS({ options: { minify: true } })],
|
|
16
|
+
external: [
|
|
17
|
+
'@kiberon-labs/behave-graph',
|
|
18
|
+
'@vscode-elements/react-elements',
|
|
19
|
+
'rc-dock',
|
|
20
|
+
'rc-menu'
|
|
21
|
+
],
|
|
10
22
|
format: ['esm'],
|
|
11
23
|
dts: true,
|
|
24
|
+
// unbundle: true,
|
|
12
25
|
logLevel: 'warn',
|
|
13
|
-
unbundle: true,
|
|
14
26
|
platform: 'neutral'
|
|
15
27
|
});
|
package/typedoc.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": [
|
|
3
|
-
"../../typedoc.base.jsonc"
|
|
4
|
-
],
|
|
5
|
-
"entryPoints": [
|
|
6
|
-
"src/index.ts"
|
|
7
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"../../typedoc.base.jsonc"
|
|
4
|
+
],
|
|
5
|
+
"entryPoints": [
|
|
6
|
+
"src/index.ts"
|
|
7
|
+
]
|
|
8
8
|
}
|
package/vite.config.js
ADDED
package/vitest.config.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { configDefaults, defineConfig } from 'vitest/config';
|
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
test: {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Visual/pixel regression tests run in a real browser via the dedicated
|
|
7
|
+
// vitest.visual.config.ts — keep them out of the happy-dom unit run.
|
|
8
|
+
exclude: [...configDefaults.exclude, '**/*.visual.test.{ts,tsx}'],
|
|
9
|
+
watch: false,
|
|
10
|
+
environment: 'happy-dom'
|
|
8
11
|
},
|
|
9
12
|
resolve: {
|
|
10
13
|
alias: {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { playwright } from '@vitest/browser-playwright';
|
|
3
|
+
import { defineConfig } from 'vitest/config';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Dedicated config for visual (pixel) regression tests.
|
|
7
|
+
*
|
|
8
|
+
* These run in a real browser via Vitest browser mode + Playwright and use the
|
|
9
|
+
* built-in `toMatchScreenshot` assertion to detect rendering regressions.
|
|
10
|
+
*
|
|
11
|
+
* Run with `pnpm test:visual` (and `pnpm test:visual:update` to refresh
|
|
12
|
+
* baselines). It is intentionally separate from the default happy-dom unit-test
|
|
13
|
+
* config so the heavy browser runner is only spun up when explicitly requested.
|
|
14
|
+
*
|
|
15
|
+
* Screenshots are platform-sensitive (fonts/anti-aliasing differ across OSes),
|
|
16
|
+
* so Vitest stores baselines under per-platform folders. Generate/refresh
|
|
17
|
+
* baselines on the same platform/CI image that will verify them.
|
|
18
|
+
*/
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
test: {
|
|
21
|
+
include: ['tests/visual/**/*.visual.test.{ts,tsx}'],
|
|
22
|
+
watch: false,
|
|
23
|
+
browser: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
provider: playwright(),
|
|
26
|
+
headless: true,
|
|
27
|
+
instances: [{ browser: 'chromium' }],
|
|
28
|
+
// Deterministic viewport, large enough to contain a panel frame without
|
|
29
|
+
// introducing page scrollbars that would shift layout.
|
|
30
|
+
viewport: { width: 900, height: 700 },
|
|
31
|
+
expect: {
|
|
32
|
+
toMatchScreenshot: {
|
|
33
|
+
// Allow a tiny amount of anti-aliasing noise without failing.
|
|
34
|
+
comparatorName: 'pixelmatch',
|
|
35
|
+
comparatorOptions: {
|
|
36
|
+
allowedMismatchedPixelRatio: 0.02
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
resolve: {
|
|
43
|
+
alias: {
|
|
44
|
+
'~': path.resolve(__dirname, './src'),
|
|
45
|
+
'@': path.resolve(__dirname, './src')
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
type CSSProperties,
|
|
4
|
-
type HTMLProps,
|
|
5
|
-
useCallback,
|
|
6
|
-
useEffect,
|
|
7
|
-
useRef,
|
|
8
|
-
useState
|
|
9
|
-
} from 'react';
|
|
10
|
-
|
|
11
|
-
export type AutoSizeInputProps = HTMLProps<HTMLInputElement> & {
|
|
12
|
-
minWidth?: number;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const baseStyles: CSSProperties = {
|
|
16
|
-
position: 'absolute',
|
|
17
|
-
top: 0,
|
|
18
|
-
left: 0,
|
|
19
|
-
visibility: 'hidden',
|
|
20
|
-
height: 0,
|
|
21
|
-
width: 'auto',
|
|
22
|
-
whiteSpace: 'pre'
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const AutoSizeInput: React.FC<AutoSizeInputProps> = ({
|
|
26
|
-
minWidth = 30,
|
|
27
|
-
...props
|
|
28
|
-
}) => {
|
|
29
|
-
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
30
|
-
const measureRef = useRef<HTMLSpanElement | null>(null);
|
|
31
|
-
const [styles, setStyles] = useState<CSSProperties>({});
|
|
32
|
-
|
|
33
|
-
// grab the font size of the input on ref mount
|
|
34
|
-
const setRef = useCallback((input: HTMLInputElement | null) => {
|
|
35
|
-
if (input) {
|
|
36
|
-
const styles = window.getComputedStyle(input);
|
|
37
|
-
setStyles({
|
|
38
|
-
fontSize: styles.getPropertyValue('font-size'),
|
|
39
|
-
paddingLeft: styles.getPropertyValue('padding-left'),
|
|
40
|
-
paddingRight: styles.getPropertyValue('padding-right')
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
inputRef.current = input;
|
|
44
|
-
}, []);
|
|
45
|
-
|
|
46
|
-
// measure the text on change and update input
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
if (measureRef.current === null) return;
|
|
49
|
-
if (inputRef.current === null) return;
|
|
50
|
-
|
|
51
|
-
const padding = props.type === 'number' || props.type === 'float' ? 20 : 0;
|
|
52
|
-
|
|
53
|
-
const width = measureRef.current.clientWidth + padding;
|
|
54
|
-
inputRef.current.style.width = Math.max(minWidth, width) + 'px';
|
|
55
|
-
}, [props.value, minWidth, styles, props.type]);
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<>
|
|
59
|
-
<input ref={setRef} {...props} />
|
|
60
|
-
<span ref={measureRef} style={{ ...baseStyles, ...styles }}>
|
|
61
|
-
{props.value}
|
|
62
|
-
</span>
|
|
63
|
-
</>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import type { GraphJSON, NodeSpecJSON } from '@kiberon-labs/behave-graph';
|
|
2
|
-
import {
|
|
3
|
-
faDownload,
|
|
4
|
-
faPause,
|
|
5
|
-
faPlay,
|
|
6
|
-
faQuestion,
|
|
7
|
-
faTrash,
|
|
8
|
-
faUpload
|
|
9
|
-
} from '@fortawesome/free-solid-svg-icons';
|
|
10
|
-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
11
|
-
import { useState } from 'react';
|
|
12
|
-
import React from 'react';
|
|
13
|
-
import { ControlButton, Controls } from 'reactflow';
|
|
14
|
-
|
|
15
|
-
import { ClearModal } from './modals/ClearModal.js';
|
|
16
|
-
import { HelpModal } from './modals/HelpModal.js';
|
|
17
|
-
import { type Examples, LoadModal } from './modals/LoadModal.js';
|
|
18
|
-
import { SaveModal } from './modals/SaveModal.js';
|
|
19
|
-
|
|
20
|
-
export type CustomControlsProps = {
|
|
21
|
-
playing: boolean;
|
|
22
|
-
togglePlay: () => void;
|
|
23
|
-
setBehaviorGraph: (value: GraphJSON) => void;
|
|
24
|
-
examples: Examples;
|
|
25
|
-
specJson: NodeSpecJSON[] | undefined;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const CustomControls: React.FC<CustomControlsProps> = ({
|
|
29
|
-
playing,
|
|
30
|
-
togglePlay,
|
|
31
|
-
setBehaviorGraph,
|
|
32
|
-
examples,
|
|
33
|
-
specJson
|
|
34
|
-
}: {
|
|
35
|
-
playing: boolean;
|
|
36
|
-
togglePlay: () => void;
|
|
37
|
-
setBehaviorGraph: (value: GraphJSON) => void;
|
|
38
|
-
examples: Examples;
|
|
39
|
-
specJson: NodeSpecJSON[] | undefined;
|
|
40
|
-
}) => {
|
|
41
|
-
const [loadModalOpen, setLoadModalOpen] = useState(false);
|
|
42
|
-
const [saveModalOpen, setSaveModalOpen] = useState(false);
|
|
43
|
-
const [helpModalOpen, setHelpModalOpen] = useState(false);
|
|
44
|
-
const [clearModalOpen, setClearModalOpen] = useState(false);
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<>
|
|
48
|
-
<Controls>
|
|
49
|
-
<ControlButton title="Help" onClick={() => setHelpModalOpen(true)}>
|
|
50
|
-
<FontAwesomeIcon icon={faQuestion} />
|
|
51
|
-
</ControlButton>
|
|
52
|
-
<ControlButton title="Load" onClick={() => setLoadModalOpen(true)}>
|
|
53
|
-
<FontAwesomeIcon icon={faUpload} />
|
|
54
|
-
</ControlButton>
|
|
55
|
-
<ControlButton title="Save" onClick={() => setSaveModalOpen(true)}>
|
|
56
|
-
<FontAwesomeIcon icon={faDownload} />
|
|
57
|
-
</ControlButton>
|
|
58
|
-
<ControlButton title="Clear" onClick={() => setClearModalOpen(true)}>
|
|
59
|
-
<FontAwesomeIcon icon={faTrash} />
|
|
60
|
-
</ControlButton>
|
|
61
|
-
<ControlButton title="Run" onClick={togglePlay}>
|
|
62
|
-
<FontAwesomeIcon icon={playing ? faPause : faPlay} />
|
|
63
|
-
</ControlButton>
|
|
64
|
-
</Controls>
|
|
65
|
-
<LoadModal
|
|
66
|
-
open={loadModalOpen}
|
|
67
|
-
onClose={() => setLoadModalOpen(false)}
|
|
68
|
-
setBehaviorGraph={setBehaviorGraph}
|
|
69
|
-
examples={examples}
|
|
70
|
-
/>
|
|
71
|
-
{specJson && (
|
|
72
|
-
<SaveModal
|
|
73
|
-
open={saveModalOpen}
|
|
74
|
-
specJson={specJson}
|
|
75
|
-
onClose={() => setSaveModalOpen(false)}
|
|
76
|
-
/>
|
|
77
|
-
)}
|
|
78
|
-
<HelpModal open={helpModalOpen} onClose={() => setHelpModalOpen(false)} />
|
|
79
|
-
<ClearModal
|
|
80
|
-
open={clearModalOpen}
|
|
81
|
-
onClose={() => setClearModalOpen(false)}
|
|
82
|
-
/>
|
|
83
|
-
</>
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
export default CustomControls;
|