@kiberon-labs/behave-graph-flow 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.fallowrc.json +16 -0
- package/.storybook/main.ts +32 -0
- package/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +64 -0
- package/.storybook/styles.css +16 -0
- package/.turbo/turbo-build.log +7 -0
- package/CHANGELOG.md +368 -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/AnyControlImpl-Ds-CShIB.js +20 -0
- package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
- package/dist/entry.css +4 -0
- package/dist/index.css +42 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +3597 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18009 -0
- package/dist/index.js.map +1 -0
- package/dist/noteImpl-KkrrWgJd.js +242 -0
- package/dist/noteImpl-KkrrWgJd.js.map +1 -0
- package/dist/styles.module-CvmpDkZj.css +3 -0
- package/dist/styles.module-CvmpDkZj.css.map +1 -0
- package/dist/styles.module-DZxg8aW9.js +271 -0
- package/dist/styles.module-DZxg8aW9.js.map +1 -0
- package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
- package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
- package/docs/notifications.md +246 -0
- package/docs/protocol.md +702 -0
- package/docs/specifics.md +191 -0
- package/package.json +82 -22
- package/postcss.config.ts +3 -4
- package/src/annotations/index.ts +32 -0
- package/src/components/FloatingToolbar/index.module.css +37 -0
- package/src/components/FloatingToolbar/index.tsx +256 -0
- package/src/components/Flow.tsx +287 -75
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +274 -0
- package/src/components/contextMenus/NodePicker.tsx +481 -0
- package/src/components/contextMenus/edge.tsx +22 -0
- package/src/components/contextMenus/node.tsx +15 -0
- package/src/components/contextMenus/selection.tsx +11 -0
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +19 -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 +475 -0
- package/src/components/edges/offsetBezier.ts +134 -0
- package/src/components/hotKeys.tsx +20 -0
- package/src/components/layoutController/index.module.css +13 -0
- package/src/components/layoutController/index.tsx +140 -0
- package/src/components/layoutController/utils.ts +248 -0
- package/src/components/menubar/defaults.tsx +516 -0
- package/src/components/menubar/index.tsx +49 -0
- package/src/components/menubar/menuItem.module.css +31 -0
- package/src/components/menubar/menuItem.tsx +65 -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 +88 -0
- package/src/components/nodes/behave/NodeContainer.tsx +46 -0
- package/src/components/nodes/behave/index.tsx +14 -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 +87 -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 +10 -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/common/PanelHeader.module.css +24 -0
- package/src/components/panels/common/PanelHeader.tsx +22 -0
- package/src/components/panels/common/SectionTitle.module.css +13 -0
- package/src/components/panels/common/SectionTitle.tsx +10 -0
- package/src/components/panels/events/EditEventPanel.tsx +324 -0
- package/src/components/panels/events/ManageEventsPanel.tsx +101 -0
- package/src/components/panels/events/index.tsx +23 -0
- package/src/components/panels/events/styles.module.css +178 -0
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +92 -0
- package/src/components/panels/history/styles.module.css +97 -0
- package/src/components/panels/keymaps/index.module.css +68 -0
- package/src/components/panels/keymaps/index.tsx +166 -0
- package/src/components/panels/layers/index.tsx +245 -0
- package/src/components/panels/layers/styles.module.css +107 -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 +218 -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 +65 -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 +55 -0
- package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
- package/src/components/panels/nodeInputs/index.module.css +308 -0
- package/src/components/panels/nodeInputs/index.tsx +349 -0
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +153 -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 +16 -0
- package/src/components/panels/search/index.tsx +215 -0
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +251 -0
- package/src/components/panels/systemSettings/styles.module.css +138 -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 +159 -0
- package/src/components/panels/traces/index.tsx +298 -0
- package/src/components/panels/traces/types.ts +48 -0
- package/src/components/panels/traces/useDerivedSpans.ts +307 -0
- package/src/components/panels/traces/utils.ts +33 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
- package/src/components/panels/variables/ManageVariablesScreen.tsx +147 -0
- package/src/components/panels/variables/index.tsx +125 -0
- package/src/components/panels/variables/styles.module.css +149 -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 +83 -0
- package/src/components/sockets/input/styles.module.css +26 -0
- package/src/components/sockets/output/index.tsx +68 -0
- package/src/components/sockets/output/styles.module.css +22 -0
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +57 -0
- package/src/css/rc-dock.css +212 -0
- package/src/css/rc-menu.css +101 -0
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +198 -0
- package/src/css/vscode-elements.css +124 -0
- package/src/entry.css +4 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -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/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +55 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +17 -16
- package/src/hooks/useFlowHandlers.ts +154 -30
- package/src/hooks/useWasdPan.ts +210 -0
- package/src/index.css +134 -0
- package/src/index.ts +53 -18
- package/src/manifest/contributionRegistry.ts +93 -0
- package/src/manifest/index.ts +4 -0
- package/src/manifest/loadManifest.ts +82 -0
- package/src/manifest/manifestPlugin.ts +29 -0
- package/src/manifest/passthroughValueType.ts +40 -0
- package/src/plugin/alignment/index.ts +91 -0
- package/src/plugin/autosave/controller.ts +366 -0
- package/src/plugin/autosave/index.tsx +114 -0
- package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
- package/src/plugin/autosave/panel/index.tsx +1 -0
- package/src/plugin/autosave/panel/styles.module.css +56 -0
- package/src/plugin/autosave/settings.ts +65 -0
- package/src/plugin/autosave/storage.ts +147 -0
- package/src/plugin/docs/index.tsx +297 -0
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +21 -0
- package/src/plugin/docs/panel/styles.module.css +174 -0
- package/src/plugin/graphrunner/actions.ts +326 -0
- package/src/plugin/graphrunner/buttons.tsx +95 -0
- package/src/plugin/graphrunner/client.ts +707 -0
- package/src/plugin/graphrunner/index.tsx +184 -0
- package/src/plugin/graphrunner/panel.tsx +386 -0
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +187 -0
- package/src/plugin/graphrunner/session.ts +243 -0
- package/src/plugin/graphrunner/store.ts +196 -0
- package/src/plugin/graphrunner/styles.module.css +171 -0
- package/src/plugin/graphrunner/transport.ts +250 -0
- package/src/plugin/graphrunner/types.ts +693 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +637 -0
- package/src/plugin/graphrunner-local/index.tsx +172 -0
- package/src/plugin/graphrunner-local/panel.tsx +187 -0
- package/src/plugin/graphrunner-local/store.ts +41 -0
- package/src/plugin/graphrunner-local/styles.module.css +82 -0
- package/src/plugin/graphrunner-local/transport.ts +1339 -0
- package/src/plugin/graphrunner-local/types.ts +10 -0
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +635 -0
- package/src/plugin/graphrunner-webworker/index.tsx +140 -0
- package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
- package/src/plugin/graphrunner-webworker/store.ts +98 -0
- package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/plugin/layout/dagre.ts +131 -0
- package/src/plugin/layout/elk.ts +216 -0
- package/src/plugin/layout/index.ts +80 -0
- package/src/plugin/notes/FormatToolbar.tsx +200 -0
- package/src/plugin/notes/index.tsx +191 -0
- package/src/plugin/notes/nodeActions.ts +100 -0
- package/src/plugin/notes/note.tsx +20 -0
- package/src/plugin/notes/noteImpl.tsx +89 -0
- package/src/plugin/realtime/realtimeRunner.ts +624 -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/store/actions.tsx +698 -0
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/controls.tsx +62 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/documentation.tsx +69 -0
- package/src/store/events.tsx +116 -0
- package/src/store/flow.tsx +230 -0
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +364 -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 +51 -0
- package/src/store/selection.ts +22 -0
- package/src/store/settings.ts +99 -0
- package/src/store/settingsSchema.ts +210 -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 +282 -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 +131 -0
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +6 -0
- package/src/system/notifications.ts +111 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +55 -0
- package/src/system/provider.tsx +86 -0
- package/src/system/pubsub.ts +323 -0
- package/src/system/system.ts +653 -0
- package/src/system/tabLoader.tsx +303 -0
- package/src/system/undoRedo.ts +103 -0
- package/src/transformers/Uigraph.ts +61 -0
- package/src/transformers/behaveToFlow.ts +16 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +40 -12
- package/src/types/NodeMetadata.ts +27 -0
- package/src/types/graph.ts +49 -0
- package/src/types/nodes.ts +50 -0
- package/src/types.ts +18 -0
- package/src/util/autoConvert.ts +200 -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 +51 -17
- 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/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 +170 -0
- package/stories/defaults/systemGenerator.ts +43 -0
- package/stories/plugins/notes.stories.tsx +100 -0
- package/tests/autoConvert.test.ts +329 -0
- package/tests/autosavePlugin.test.ts +204 -0
- package/tests/callSubgraphSync.test.ts +148 -0
- package/tests/commandRegistry.test.ts +137 -0
- package/tests/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/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +27 -4
- package/tests/hotkeys.test.ts +79 -0
- package/tests/keepAliveLifecycle.test.ts +167 -0
- package/tests/loadManifest.test.ts +113 -0
- package/tests/noteMarkdown.test.ts +65 -0
- package/tests/notesPlugin.test.ts +162 -0
- package/tests/notifications.test.ts +87 -0
- package/tests/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +373 -0
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -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-localGraphRunner-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-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/tests/wasdPan.test.ts +71 -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 +55 -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,65 @@
|
|
|
1
|
+
import type { SettingDescriptor } from '@/store/settingsSchema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setting keys owned by the autosave plugin. Namespaced (`autosave.*`) so they
|
|
5
|
+
* never collide with the built-in flat setting keys.
|
|
6
|
+
*/
|
|
7
|
+
export const AUTOSAVE_ENABLED = 'autosave.enabled';
|
|
8
|
+
export const AUTOSAVE_INTERVAL_SECONDS = 'autosave.intervalSeconds';
|
|
9
|
+
export const AUTOSAVE_MAX_COPIES = 'autosave.maxCopies';
|
|
10
|
+
|
|
11
|
+
/** Shipped defaults, also used by the controller when a value is missing. */
|
|
12
|
+
export const AUTOSAVE_DEFAULTS = {
|
|
13
|
+
enabled: false,
|
|
14
|
+
intervalSeconds: 60,
|
|
15
|
+
maxCopies: 20
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
/** Lower bound on the timer so a typo can't spin the loop every frame. */
|
|
19
|
+
export const MIN_INTERVAL_SECONDS = 5;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The Settings-panel rows this plugin contributes. Registered via
|
|
23
|
+
* `system.registerSettings(...)`, which also seeds each default into the value
|
|
24
|
+
* store, so `system.getSetting(AUTOSAVE_*)` is populated the moment the plugin
|
|
25
|
+
* loads.
|
|
26
|
+
*/
|
|
27
|
+
export const AUTOSAVE_SETTINGS: SettingDescriptor[] = [
|
|
28
|
+
{
|
|
29
|
+
key: AUTOSAVE_ENABLED,
|
|
30
|
+
section: 'Autosave',
|
|
31
|
+
order: 0,
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
default: AUTOSAVE_DEFAULTS.enabled,
|
|
34
|
+
title: 'Enable local backups',
|
|
35
|
+
description:
|
|
36
|
+
'Periodically snapshot open graphs into local storage so a crash or a bad edit is recoverable. Purely client-side; nothing leaves the browser.'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: AUTOSAVE_INTERVAL_SECONDS,
|
|
40
|
+
section: 'Autosave',
|
|
41
|
+
order: 1,
|
|
42
|
+
type: 'number',
|
|
43
|
+
default: AUTOSAVE_DEFAULTS.intervalSeconds,
|
|
44
|
+
min: MIN_INTERVAL_SECONDS,
|
|
45
|
+
step: 5,
|
|
46
|
+
title: 'Backup frequency (seconds)',
|
|
47
|
+
description:
|
|
48
|
+
'How often a snapshot is taken. A copy is only written when the graph has actually changed and is in a consistent state.',
|
|
49
|
+
when: (values) => values[AUTOSAVE_ENABLED] === true
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: AUTOSAVE_MAX_COPIES,
|
|
53
|
+
section: 'Autosave',
|
|
54
|
+
order: 2,
|
|
55
|
+
type: 'number',
|
|
56
|
+
default: AUTOSAVE_DEFAULTS.maxCopies,
|
|
57
|
+
min: 1,
|
|
58
|
+
max: 200,
|
|
59
|
+
step: 1,
|
|
60
|
+
title: 'Copies to keep per graph',
|
|
61
|
+
description:
|
|
62
|
+
'The oldest snapshots are discarded once a graph has this many. Higher values use more local storage.',
|
|
63
|
+
when: (values) => values[AUTOSAVE_ENABLED] === true
|
|
64
|
+
}
|
|
65
|
+
];
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { UIGraphJSON } from '@/types/graph';
|
|
2
|
+
import type { SettingsStorage } from '@/system/system';
|
|
3
|
+
|
|
4
|
+
/** Single local-storage key holding every graph's backup ring. */
|
|
5
|
+
export const AUTOSAVE_STORAGE_KEY = 'behave-graph:autosave';
|
|
6
|
+
|
|
7
|
+
/** A single point-in-time copy of one graph. */
|
|
8
|
+
export type BackupSnapshot = {
|
|
9
|
+
/** Unique id for this snapshot (used by the panel to restore/delete). */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Session id of the graph this snapshot came from. */
|
|
12
|
+
graphId: string;
|
|
13
|
+
/** Graph display name captured at snapshot time (for the panel list). */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Epoch milliseconds when the snapshot was taken. */
|
|
16
|
+
timestamp: number;
|
|
17
|
+
/** Node count, shown in the panel without deserializing the whole graph. */
|
|
18
|
+
nodeCount: number;
|
|
19
|
+
/** The full, restorable graph document. */
|
|
20
|
+
graph: UIGraphJSON;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Per-graph ring buffer of snapshots, newest last. */
|
|
24
|
+
type GraphBackups = {
|
|
25
|
+
name: string;
|
|
26
|
+
snapshots: BackupSnapshot[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** The persisted shape: graph id -> its backups. */
|
|
30
|
+
type BackupStore = Record<string, GraphBackups>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* localStorage-or-nothing accessor. The whole backup set lives under a single
|
|
34
|
+
* key as a JSON object, so a plain get/set storage adapter (the same shape the
|
|
35
|
+
* settings persistence uses) is all we need , no `key(i)`/`length` scanning.
|
|
36
|
+
*/
|
|
37
|
+
const defaultStorage = (): SettingsStorage | undefined => {
|
|
38
|
+
try {
|
|
39
|
+
if (typeof localStorage !== 'undefined') return localStorage;
|
|
40
|
+
} catch {
|
|
41
|
+
// localStorage can throw in sandboxed / SSR contexts.
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const readStore = (storage: SettingsStorage): BackupStore => {
|
|
47
|
+
try {
|
|
48
|
+
const raw = storage.getItem(AUTOSAVE_STORAGE_KEY);
|
|
49
|
+
if (!raw) return {};
|
|
50
|
+
const parsed = JSON.parse(raw);
|
|
51
|
+
return parsed && typeof parsed === 'object' ? (parsed as BackupStore) : {};
|
|
52
|
+
} catch {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const writeStore = (storage: SettingsStorage, store: BackupStore): void => {
|
|
58
|
+
try {
|
|
59
|
+
storage.setItem(AUTOSAVE_STORAGE_KEY, JSON.stringify(store));
|
|
60
|
+
} catch {
|
|
61
|
+
// Quota exceeded / serialization error: drop this write rather than throw.
|
|
62
|
+
// The next successful snapshot will re-establish the ring.
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Thin persistence wrapper over the single backup key. Kept storage-agnostic
|
|
68
|
+
* (any get/set adapter) so hosts can back it with something other than
|
|
69
|
+
* localStorage and tests can inject an in-memory map.
|
|
70
|
+
*/
|
|
71
|
+
export class BackupStorage {
|
|
72
|
+
private readonly storage: SettingsStorage | undefined;
|
|
73
|
+
|
|
74
|
+
constructor(storage: SettingsStorage | undefined = defaultStorage()) {
|
|
75
|
+
this.storage = storage;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Whether a backing store is available (false in SSR / sandboxed contexts). */
|
|
79
|
+
get available(): boolean {
|
|
80
|
+
return this.storage !== undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** All snapshots across every graph, newest first. */
|
|
84
|
+
listAll(): BackupSnapshot[] {
|
|
85
|
+
if (!this.storage) return [];
|
|
86
|
+
const store = readStore(this.storage);
|
|
87
|
+
return Object.values(store)
|
|
88
|
+
.flatMap((g) => g.snapshots)
|
|
89
|
+
.sort((a, b) => b.timestamp - a.timestamp);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Find one snapshot by id (across all graphs). */
|
|
93
|
+
find(id: string): BackupSnapshot | undefined {
|
|
94
|
+
return this.listAll().find((s) => s.id === id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Append a snapshot to its graph's ring and trim to `maxCopies` (oldest
|
|
99
|
+
* dropped). `name` refreshes the stored graph label so the panel tracks
|
|
100
|
+
* renames.
|
|
101
|
+
*/
|
|
102
|
+
append(snapshot: BackupSnapshot, maxCopies: number): void {
|
|
103
|
+
if (!this.storage) return;
|
|
104
|
+
const store = readStore(this.storage);
|
|
105
|
+
const entry = store[snapshot.graphId] ?? {
|
|
106
|
+
name: snapshot.name,
|
|
107
|
+
snapshots: []
|
|
108
|
+
};
|
|
109
|
+
entry.name = snapshot.name;
|
|
110
|
+
entry.snapshots = [...entry.snapshots, snapshot];
|
|
111
|
+
const limit = Math.max(1, Math.floor(maxCopies));
|
|
112
|
+
if (entry.snapshots.length > limit) {
|
|
113
|
+
entry.snapshots = entry.snapshots.slice(entry.snapshots.length - limit);
|
|
114
|
+
}
|
|
115
|
+
store[snapshot.graphId] = entry;
|
|
116
|
+
writeStore(this.storage, store);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Remove a single snapshot; prunes the graph entry once it is empty. */
|
|
120
|
+
remove(id: string): void {
|
|
121
|
+
if (!this.storage) return;
|
|
122
|
+
const store = readStore(this.storage);
|
|
123
|
+
for (const [graphId, entry] of Object.entries(store)) {
|
|
124
|
+
const next = entry.snapshots.filter((s) => s.id !== id);
|
|
125
|
+
if (next.length === entry.snapshots.length) continue;
|
|
126
|
+
if (next.length === 0) delete store[graphId];
|
|
127
|
+
else store[graphId] = { ...entry, snapshots: next };
|
|
128
|
+
writeStore(this.storage, store);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Drop every snapshot for a single graph. */
|
|
134
|
+
removeGraph(graphId: string): void {
|
|
135
|
+
if (!this.storage) return;
|
|
136
|
+
const store = readStore(this.storage);
|
|
137
|
+
if (!(graphId in store)) return;
|
|
138
|
+
delete store[graphId];
|
|
139
|
+
writeStore(this.storage, store);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Wipe all backups. */
|
|
143
|
+
clear(): void {
|
|
144
|
+
if (!this.storage) return;
|
|
145
|
+
writeStore(this.storage, {});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { type System } from '@/system/system';
|
|
2
|
+
import type { NodeDocumentation } from '@/store/documentation';
|
|
3
|
+
import { BounceRight, Flash, GitFork, 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: <GitFork />,
|
|
88
|
+
type: 'flow/branch',
|
|
89
|
+
shortDescription: 'Execute different flows based on a boolean condition',
|
|
90
|
+
tags: ['flow', 'conditional', 'control'],
|
|
91
|
+
markdownDescription: `
|
|
92
|
+
Conditionally executes one of two flow paths based on a boolean input.
|
|
93
|
+
|
|
94
|
+
## Common Use Cases
|
|
95
|
+
- Implementing if/else logic
|
|
96
|
+
- State-based behavior
|
|
97
|
+
- Conditional event handling
|
|
98
|
+
`.trim()
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: 'flow/sequence',
|
|
102
|
+
shortDescription: 'Execute multiple flows in order',
|
|
103
|
+
tags: ['flow', 'control', 'sequential'],
|
|
104
|
+
markdownDescription: `
|
|
105
|
+
# Sequence
|
|
106
|
+
|
|
107
|
+
Executes multiple flow outputs in order, one after another.
|
|
108
|
+
|
|
109
|
+
Perfect for chaining multiple actions that need to happen sequentially.
|
|
110
|
+
`.trim()
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: 'flow/delay',
|
|
114
|
+
shortDescription: 'Delay execution for a specified duration',
|
|
115
|
+
tags: ['flow', 'time', 'async'],
|
|
116
|
+
markdownDescription: `
|
|
117
|
+
# Delay
|
|
118
|
+
|
|
119
|
+
Pauses execution for a specified number of seconds before continuing.
|
|
120
|
+
|
|
121
|
+
Useful for creating timed behaviors, animations, or cooldown mechanics.
|
|
122
|
+
`.trim()
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
// Math nodes
|
|
127
|
+
const mathDocs: NodeDocumentation[] = [
|
|
128
|
+
{
|
|
129
|
+
type: 'math/add',
|
|
130
|
+
shortDescription: 'Add two numbers',
|
|
131
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
132
|
+
markdownDescription: 'Calculates the sum of two numeric values: a + b'
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'math/subtract',
|
|
136
|
+
shortDescription: 'Subtract two numbers',
|
|
137
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
138
|
+
markdownDescription: 'Calculates the difference: a - b'
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: 'math/multiply',
|
|
142
|
+
shortDescription: 'Multiply two numbers',
|
|
143
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
144
|
+
markdownDescription: 'Calculates the product: a * b'
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'math/divide',
|
|
148
|
+
shortDescription: 'Divide two numbers',
|
|
149
|
+
tags: ['math', 'arithmetic', 'basic'],
|
|
150
|
+
markdownDescription:
|
|
151
|
+
'Calculates the quotient: a / b\\n\\n⚠️ Division by zero returns 0'
|
|
152
|
+
}
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
// Logic nodes
|
|
156
|
+
const logicDocs: NodeDocumentation[] = [
|
|
157
|
+
{
|
|
158
|
+
type: 'logic/and',
|
|
159
|
+
shortDescription: 'Logical AND of two boolean values',
|
|
160
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
161
|
+
markdownDescription: 'Returns true only if both inputs are true'
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
type: 'logic/or',
|
|
165
|
+
shortDescription: 'Logical OR of two boolean values',
|
|
166
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
167
|
+
markdownDescription: 'Returns true if at least one input is true'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'logic/not',
|
|
171
|
+
shortDescription: 'Logical NOT - inverts a boolean value',
|
|
172
|
+
tags: ['logic', 'boolean', 'gates'],
|
|
173
|
+
markdownDescription:
|
|
174
|
+
'Inverts the input: true becomes false, false becomes true'
|
|
175
|
+
}
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
// Variable nodes
|
|
179
|
+
const variableDocs: NodeDocumentation[] = [
|
|
180
|
+
{
|
|
181
|
+
type: 'variable/get',
|
|
182
|
+
shortDescription: 'Read a variable value',
|
|
183
|
+
tags: ['variable', 'state', 'getter'],
|
|
184
|
+
markdownDescription: `
|
|
185
|
+
Retrieves the current value of a named variable.
|
|
186
|
+
|
|
187
|
+
Variables are shared across the entire graph and persist between executions.
|
|
188
|
+
`.trim()
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'variable/set',
|
|
192
|
+
shortDescription: 'Write a variable value',
|
|
193
|
+
tags: ['variable', 'state', 'setter'],
|
|
194
|
+
markdownDescription: `
|
|
195
|
+
Sets the value of a named variable.
|
|
196
|
+
|
|
197
|
+
This executes as a flow and passes through to allow chaining.
|
|
198
|
+
`.trim()
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
// Event nodes
|
|
203
|
+
const eventDocs: NodeDocumentation[] = [
|
|
204
|
+
{
|
|
205
|
+
type: 'lifecycle/onStart',
|
|
206
|
+
shortDescription: 'Triggered when the graph starts',
|
|
207
|
+
tags: ['event', 'lifecycle', 'entry'],
|
|
208
|
+
markdownDescription: `
|
|
209
|
+
Fires once when the graph begins execution.
|
|
210
|
+
|
|
211
|
+
Perfect for initialization logic and setup tasks.
|
|
212
|
+
`.trim()
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
type: 'lifecycle/onEnd',
|
|
216
|
+
shortDescription: 'Triggered when the graph ends',
|
|
217
|
+
tags: ['event', 'lifecycle', 'cleanup'],
|
|
218
|
+
markdownDescription: `
|
|
219
|
+
# On End
|
|
220
|
+
|
|
221
|
+
Fires once when the graph stops execution.
|
|
222
|
+
|
|
223
|
+
Useful for cleanup, saving state, or final actions.
|
|
224
|
+
`.trim()
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
type: 'lifecycle/onTick',
|
|
228
|
+
shortDescription: 'Triggered every frame/update',
|
|
229
|
+
tags: ['event', 'lifecycle', 'update', 'loop'],
|
|
230
|
+
markdownDescription: `
|
|
231
|
+
# On Tick
|
|
232
|
+
|
|
233
|
+
Fires continuously on every update cycle.
|
|
234
|
+
|
|
235
|
+
⚠️ Use carefully - runs every frame! Great for animations and real-time updates.
|
|
236
|
+
`.trim()
|
|
237
|
+
}
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
// Combine and set all documentation
|
|
241
|
+
const allDocs = [
|
|
242
|
+
...flowDocs,
|
|
243
|
+
...mathDocs,
|
|
244
|
+
...logicDocs,
|
|
245
|
+
...variableDocs,
|
|
246
|
+
...eventDocs
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
docStore.setMultipleDocumentation(allDocs);
|
|
250
|
+
|
|
251
|
+
console.log(`✅ Loaded documentation for ${allDocs.length} nodes`);
|
|
252
|
+
|
|
253
|
+
// Register the documentation panel with TabLoader
|
|
254
|
+
sys.tabLoader.register('docs', () => {
|
|
255
|
+
return {
|
|
256
|
+
id: 'docs',
|
|
257
|
+
closable: true,
|
|
258
|
+
title: 'Documentation',
|
|
259
|
+
group: 'default',
|
|
260
|
+
content: () => (
|
|
261
|
+
<ErrorBoundary fallback={'Error loading Documentation panel'}>
|
|
262
|
+
<DocumentationBrowserPanel />
|
|
263
|
+
</ErrorBoundary>
|
|
264
|
+
)
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Add menu item to Window menu
|
|
269
|
+
const menuStore = sys.menubarStore;
|
|
270
|
+
const currentItems = menuStore.getState().items;
|
|
271
|
+
const windowMenu = currentItems.find((menu) => menu.name === 'window');
|
|
272
|
+
|
|
273
|
+
if (windowMenu) {
|
|
274
|
+
// Add the Documentation menu item to the Window menu
|
|
275
|
+
const newMenuItem = {
|
|
276
|
+
name: 'docs',
|
|
277
|
+
render: function DocsMenuItem() {
|
|
278
|
+
return (
|
|
279
|
+
<MenuItemElement
|
|
280
|
+
onClick={() => sys.tabStore.getState().openTab('docs')}
|
|
281
|
+
key="docs"
|
|
282
|
+
>
|
|
283
|
+
Documentation
|
|
284
|
+
</MenuItemElement>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
menuStore
|
|
290
|
+
.getState()
|
|
291
|
+
.setSubMenuItems('window', [...windowMenu.items, newMenuItem]);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const docsPlugin = plugin(docsPluginLoader, {
|
|
296
|
+
name: 'docs'
|
|
297
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { useMemo, useCallback, type ComponentType } from 'react';
|
|
2
|
+
import { useEditor, EditorContent } from '@tiptap/react';
|
|
3
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
4
|
+
import { useSystem } from '@/system/provider';
|
|
5
|
+
import { Markdown } from 'tiptap-markdown';
|
|
6
|
+
import { useStore } from 'zustand';
|
|
7
|
+
import type { ExtendedNodeSpecJSON } from '@/components/contextMenus/NodePicker';
|
|
8
|
+
import { useOnPressKey } from '@/hooks/useOnPressKey';
|
|
9
|
+
import { removeTabFromLayout } from '@/components/layoutController/utils';
|
|
10
|
+
import styles from './styles.module.css';
|
|
11
|
+
|
|
12
|
+
/** One named socket (name + value type) rendered in a documentation section. */
|
|
13
|
+
interface DocSocket {
|
|
14
|
+
name: string;
|
|
15
|
+
valueType: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SocketSectionProps {
|
|
19
|
+
title: string;
|
|
20
|
+
sockets: DocSocket[] | undefined;
|
|
21
|
+
getIconForType: (valueType: string) => ComponentType;
|
|
22
|
+
getColorForType: (valueType: string) => string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A titled list of node sockets (Inputs / Outputs / Configuration). Renders
|
|
27
|
+
* nothing when the socket list is empty so callers can drop it in
|
|
28
|
+
* unconditionally.
|
|
29
|
+
*/
|
|
30
|
+
function SocketSection({
|
|
31
|
+
title,
|
|
32
|
+
sockets,
|
|
33
|
+
getIconForType,
|
|
34
|
+
getColorForType
|
|
35
|
+
}: SocketSectionProps) {
|
|
36
|
+
if (!sockets || sockets.length === 0) return null;
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className={styles.section}>
|
|
40
|
+
<h3 className={styles.sectionTitle}>{title}</h3>
|
|
41
|
+
<div className={styles.socketList}>
|
|
42
|
+
{sockets.map((socket, index) => {
|
|
43
|
+
const IconComponent = getIconForType(socket.valueType);
|
|
44
|
+
const color = getColorForType(socket.valueType);
|
|
45
|
+
return (
|
|
46
|
+
<div key={index} className={styles.socketItem}>
|
|
47
|
+
<div className={styles.socketIcon} style={{ color }}>
|
|
48
|
+
<IconComponent />
|
|
49
|
+
</div>
|
|
50
|
+
<div className={styles.socketName}>{socket.name}</div>
|
|
51
|
+
<div className={styles.socketType} style={{ color }}>
|
|
52
|
+
{socket.valueType}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
})}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function DocumentationBrowserPanelImpl() {
|
|
63
|
+
const sys = useSystem();
|
|
64
|
+
const selectedNodeType = useStore(
|
|
65
|
+
sys.refStore,
|
|
66
|
+
(x) => x.refs.selectedDocumentationType
|
|
67
|
+
);
|
|
68
|
+
const documentation = useStore(sys.documentationStore, (x) => x.docs);
|
|
69
|
+
const specs = useStore(sys.specStore, (x) => x.specs);
|
|
70
|
+
const icons = useStore(sys.legendStore, (x) => x.icons);
|
|
71
|
+
const defaultIcon = useStore(sys.legendStore, (x) => x.defaultIcon);
|
|
72
|
+
const valueTypeColors = useStore(sys.legendStore, (x) => x.valueTypeColors);
|
|
73
|
+
|
|
74
|
+
const nodeSpec = useMemo((): ExtendedNodeSpecJSON | null => {
|
|
75
|
+
if (!selectedNodeType) return null;
|
|
76
|
+
return specs.find((s) => s.type === selectedNodeType) ?? null;
|
|
77
|
+
}, [selectedNodeType, specs]);
|
|
78
|
+
|
|
79
|
+
const getIconForType = useCallback(
|
|
80
|
+
(valueType: string): ComponentType => {
|
|
81
|
+
const IconComponent = icons[valueType] || defaultIcon;
|
|
82
|
+
return IconComponent;
|
|
83
|
+
},
|
|
84
|
+
[icons, defaultIcon]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const getColorForType = useCallback(
|
|
88
|
+
(valueType: string) => {
|
|
89
|
+
return valueTypeColors[valueType] || '#6c727e';
|
|
90
|
+
},
|
|
91
|
+
[valueTypeColors]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const nodeDoc = useMemo(() => {
|
|
95
|
+
if (!selectedNodeType) return null;
|
|
96
|
+
return documentation.get(selectedNodeType);
|
|
97
|
+
}, [selectedNodeType, documentation]);
|
|
98
|
+
|
|
99
|
+
const editor = useEditor(
|
|
100
|
+
{
|
|
101
|
+
extensions: [StarterKit, Markdown],
|
|
102
|
+
content: nodeDoc?.markdownDescription || '',
|
|
103
|
+
editable: false
|
|
104
|
+
},
|
|
105
|
+
[nodeDoc?.markdownDescription]
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const closePanel = useCallback(() => {
|
|
109
|
+
const currentLayout = sys.tabStore.getState().layout;
|
|
110
|
+
const newLayout = removeTabFromLayout(currentLayout, 'docbrowser');
|
|
111
|
+
sys.tabStore.getState().setLayout(newLayout);
|
|
112
|
+
}, [sys]);
|
|
113
|
+
|
|
114
|
+
useOnPressKey('Escape', closePanel);
|
|
115
|
+
|
|
116
|
+
if (!selectedNodeType) {
|
|
117
|
+
return (
|
|
118
|
+
<div className={styles.container}>
|
|
119
|
+
<div className={styles.empty}>
|
|
120
|
+
<div className={styles.emptyIcon}>📚</div>
|
|
121
|
+
<div className={styles.emptyText}>No documentation selected</div>
|
|
122
|
+
<div className={styles.emptySub}>
|
|
123
|
+
Click the info icon on a node to view its documentation
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const hasMarkdown = nodeDoc?.markdownDescription;
|
|
131
|
+
const hasShortDescription =
|
|
132
|
+
nodeDoc?.shortDescription || nodeSpec?.description;
|
|
133
|
+
const hasTags = nodeDoc?.tags && nodeDoc.tags.length > 0;
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className={styles.container}>
|
|
137
|
+
<div className={styles.header}>
|
|
138
|
+
<div className={styles.titleSection}>
|
|
139
|
+
{nodeDoc?.icon && (
|
|
140
|
+
<div className={styles.iconLarge}>{nodeDoc.icon}</div>
|
|
141
|
+
)}
|
|
142
|
+
<div>
|
|
143
|
+
<h2 className={styles.title}>
|
|
144
|
+
{nodeSpec?.label || selectedNodeType}
|
|
145
|
+
</h2>
|
|
146
|
+
<div className={styles.nodeType}>{selectedNodeType}</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
{hasTags && nodeDoc?.tags && (
|
|
150
|
+
<div className={styles.tags}>
|
|
151
|
+
{nodeDoc.tags.map((tag, index) => (
|
|
152
|
+
<span key={index} className={styles.tag}>
|
|
153
|
+
{tag}
|
|
154
|
+
</span>
|
|
155
|
+
))}
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div className={styles.content}>
|
|
161
|
+
{hasShortDescription && (
|
|
162
|
+
<div className={styles.description}>
|
|
163
|
+
{nodeDoc?.shortDescription || nodeSpec?.description}
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{hasMarkdown && nodeDoc?.markdownDescription ? (
|
|
168
|
+
<EditorContent editor={editor} />
|
|
169
|
+
) : (
|
|
170
|
+
<div className={styles.noContent}>
|
|
171
|
+
<p>No detailed documentation available for this node.</p>
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
{nodeSpec && (
|
|
176
|
+
<>
|
|
177
|
+
<SocketSection
|
|
178
|
+
title="Inputs"
|
|
179
|
+
sockets={nodeSpec.inputs}
|
|
180
|
+
getIconForType={getIconForType}
|
|
181
|
+
getColorForType={getColorForType}
|
|
182
|
+
/>
|
|
183
|
+
<SocketSection
|
|
184
|
+
title="Outputs"
|
|
185
|
+
sockets={nodeSpec.outputs}
|
|
186
|
+
getIconForType={getIconForType}
|
|
187
|
+
getColorForType={getColorForType}
|
|
188
|
+
/>
|
|
189
|
+
<SocketSection
|
|
190
|
+
title="Configuration"
|
|
191
|
+
sockets={nodeSpec.configuration}
|
|
192
|
+
getIconForType={getIconForType}
|
|
193
|
+
getColorForType={getColorForType}
|
|
194
|
+
/>
|
|
195
|
+
</>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
}
|