@kiberon-labs/behave-graph-flow 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +49 -1
- package/.storybook/styles.css +9 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +368 -0
- package/dist/AnyControlImpl-Ds-CShIB.js +20 -0
- package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
- package/dist/index.css +36 -33
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1865 -550
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14357 -11221
- package/dist/index.js.map +1 -1
- package/dist/noteImpl-KkrrWgJd.js +242 -0
- package/dist/noteImpl-KkrrWgJd.js.map +1 -0
- package/dist/styles.module-CvmpDkZj.css +3 -0
- package/dist/styles.module-CvmpDkZj.css.map +1 -0
- package/dist/styles.module-DZxg8aW9.js +271 -0
- package/dist/styles.module-DZxg8aW9.js.map +1 -0
- package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
- package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
- package/docs/protocol.md +43 -20
- package/package.json +5 -9
- package/src/components/FloatingToolbar/index.module.css +5 -13
- package/src/components/FloatingToolbar/index.tsx +9 -9
- package/src/components/Flow.tsx +34 -23
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +13 -13
- package/src/components/contextMenus/edge.tsx +9 -95
- package/src/components/contextMenus/node.tsx +9 -149
- package/src/components/contextMenus/selection.tsx +5 -71
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +13 -2
- package/src/components/edges/index.tsx +75 -69
- package/src/components/layoutController/index.module.css +3 -0
- package/src/components/layoutController/index.tsx +24 -1
- package/src/components/layoutController/utils.ts +46 -3
- package/src/components/menubar/defaults.tsx +55 -19
- package/src/components/menubar/menuItem.module.css +18 -3
- package/src/components/menubar/menuItem.tsx +34 -1
- package/src/components/nodes/behave/NodeContainer.module.css +26 -25
- package/src/components/nodes/group/index.tsx +3 -3
- package/src/components/nodes/wrapper/styles.module.css +6 -32
- package/src/components/panels/alignment/index.module.css +0 -10
- package/src/components/panels/alignment/index.tsx +4 -4
- package/src/components/panels/base/styles.module.css +2 -2
- package/src/components/panels/common/PanelHeader.module.css +24 -0
- package/src/components/panels/common/PanelHeader.tsx +22 -0
- package/src/components/panels/common/SectionTitle.module.css +13 -0
- package/src/components/panels/common/SectionTitle.tsx +10 -0
- package/src/components/panels/events/EditEventPanel.tsx +14 -5
- package/src/components/panels/events/ManageEventsPanel.tsx +11 -8
- package/src/components/panels/events/styles.module.css +6 -64
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +2 -2
- package/src/components/panels/history/styles.module.css +0 -9
- package/src/components/panels/keymaps/index.module.css +3 -13
- package/src/components/panels/keymaps/index.tsx +1 -2
- package/src/components/panels/layers/index.tsx +20 -15
- package/src/components/panels/layers/styles.module.css +9 -12
- package/src/components/panels/legend/index.tsx +1 -1
- package/src/components/panels/logs/index.module.css +25 -19
- package/src/components/panels/logs/index.tsx +7 -7
- package/src/components/panels/nodeInputs/InputsGroup.tsx +1 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +2 -2
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +1 -1
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +2 -12
- package/src/components/panels/nodeInputs/index.module.css +99 -75
- package/src/components/panels/nodeInputs/index.tsx +21 -11
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +2 -2
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +23 -43
- package/src/components/panels/nodePicker/index.tsx +8 -8
- package/src/components/panels/panel/index.module.css +7 -7
- package/src/components/panels/search/index.module.css +0 -50
- package/src/components/panels/search/index.tsx +2 -2
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +221 -176
- package/src/components/panels/systemSettings/styles.module.css +135 -8
- package/src/components/panels/traces/GridLines.tsx +1 -1
- package/src/components/panels/traces/TimeGrid.tsx +3 -3
- package/src/components/panels/traces/TraceLane.tsx +1 -1
- package/src/components/panels/traces/index.module.css +1 -8
- package/src/components/panels/traces/index.tsx +8 -4
- package/src/components/panels/traces/useDerivedSpans.ts +241 -146
- package/src/components/panels/traces/utils.ts +8 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +3 -3
- package/src/components/panels/variables/ManageVariablesScreen.tsx +12 -9
- package/src/components/panels/variables/index.tsx +2 -2
- package/src/components/panels/variables/styles.module.css +4 -91
- package/src/components/primitives/icon.module.css +4 -4
- package/src/components/sockets/input/index.tsx +9 -2
- package/src/components/sockets/input/styles.module.css +2 -3
- package/src/components/sockets/output/index.tsx +10 -3
- package/src/components/sockets/output/styles.module.css +1 -6
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +3 -3
- package/src/css/rc-dock.css +143 -43
- package/src/css/rc-menu.css +56 -55
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +197 -13
- package/src/css/vscode-elements.css +124 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +2 -2
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -0
- package/src/generators/SequenceGenerator.tsx +2 -2
- package/src/generators/SwitchOnIntegerGenerator.tsx +2 -2
- package/src/generators/SwitchOnStringGenerator.tsx +2 -2
- package/src/generators/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +21 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +2 -2
- package/src/hooks/useFlowHandlers.ts +47 -9
- package/src/hooks/useWasdPan.ts +26 -4
- package/src/index.css +4 -16
- package/src/index.ts +17 -0
- package/src/manifest/contributionRegistry.ts +93 -0
- package/src/manifest/index.ts +4 -0
- package/src/manifest/loadManifest.ts +82 -0
- package/src/manifest/manifestPlugin.ts +29 -0
- package/src/manifest/passthroughValueType.ts +40 -0
- package/src/plugin/alignment/index.ts +22 -12
- package/src/plugin/autosave/controller.ts +366 -0
- package/src/plugin/autosave/index.tsx +114 -0
- package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
- package/src/plugin/autosave/panel/index.tsx +1 -0
- package/src/plugin/autosave/panel/styles.module.css +56 -0
- package/src/plugin/autosave/settings.ts +65 -0
- package/src/plugin/autosave/storage.ts +147 -0
- package/src/plugin/docs/index.tsx +2 -4
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +15 -194
- package/src/plugin/docs/panel/styles.module.css +8 -8
- package/src/plugin/graphrunner/actions.ts +258 -185
- package/src/plugin/graphrunner/buttons.tsx +34 -26
- package/src/plugin/graphrunner/client.ts +4 -1
- package/src/plugin/graphrunner/index.tsx +29 -100
- package/src/plugin/graphrunner/panel.tsx +2 -2
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +21 -192
- package/src/plugin/graphrunner/store.ts +14 -24
- package/src/plugin/graphrunner/styles.module.css +17 -57
- package/src/plugin/graphrunner/transport.ts +26 -0
- package/src/plugin/graphrunner/types.ts +21 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +260 -80
- package/src/plugin/graphrunner-local/index.tsx +8 -2
- package/src/plugin/graphrunner-local/panel.tsx +131 -175
- package/src/plugin/graphrunner-local/styles.module.css +57 -76
- package/src/plugin/graphrunner-local/transport.ts +151 -184
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +2 -0
- package/src/plugin/graphrunner-webworker/index.tsx +4 -10
- package/src/plugin/graphrunner-webworker/store.ts +9 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/{layout/dagre.tsx → plugin/layout/dagre.ts} +17 -5
- package/src/{layout → plugin/layout}/elk.ts +22 -6
- package/src/plugin/layout/index.ts +80 -0
- package/src/plugin/notes/FormatToolbar.tsx +200 -0
- package/src/plugin/notes/index.tsx +191 -0
- package/src/plugin/notes/nodeActions.ts +100 -0
- package/src/plugin/notes/note.tsx +20 -0
- package/src/plugin/notes/noteImpl.tsx +89 -0
- package/src/plugin/realtime/realtimeRunner.ts +58 -4
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +2 -2
- package/src/specifics/CustomEventTriggerSpecific.tsx +2 -2
- package/src/specifics/VariableGetSpecific.tsx +2 -2
- package/src/specifics/VariableSetSpecific.tsx +2 -2
- package/src/store/actions.tsx +5 -5
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/flow.tsx +23 -38
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +301 -260
- package/src/store/layers.ts +3 -3
- package/src/store/registry.ts +12 -4
- package/src/store/selection.ts +3 -3
- package/src/store/settings.ts +82 -82
- package/src/store/settingsSchema.ts +210 -0
- package/src/store/tabs.ts +5 -1
- package/src/store/traces.ts +3 -3
- package/src/system/graph.ts +11 -14
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +3 -0
- package/src/system/notifications.ts +13 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +28 -0
- package/src/system/provider.tsx +64 -0
- package/src/system/system.ts +518 -88
- package/src/system/tabLoader.tsx +70 -32
- package/src/system/undoRedo.ts +1 -1
- package/src/transformers/Uigraph.ts +5 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +13 -5
- package/src/types/nodes.ts +8 -3
- package/src/types.ts +2 -0
- package/src/util/autoConvert.ts +200 -0
- package/src/util/isValidConnection.ts +23 -2
- package/stories/defaults/defaultStoryProvider.tsx +17 -14
- package/stories/defaults/systemGenerator.ts +6 -1
- package/stories/{components/nodes/comment.stories.tsx → plugins/notes.stories.tsx} +24 -30
- package/tests/autoConvert.test.ts +329 -0
- package/tests/autosavePlugin.test.ts +204 -0
- package/tests/callSubgraphSync.test.ts +148 -0
- package/tests/commandRegistry.test.ts +137 -0
- package/tests/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +2 -1
- package/tests/hotkeys.test.ts +79 -0
- package/tests/keepAliveLifecycle.test.ts +167 -0
- package/tests/loadManifest.test.ts +113 -0
- package/tests/noteMarkdown.test.ts +65 -0
- package/tests/notesPlugin.test.ts +162 -0
- package/tests/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +7 -6
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -0
- package/tests/visual/README.md +2 -2
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-localGraphRunner-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
- package/tests/visual/panels.visual.test.tsx +3 -3
- package/tests/wasdPan.test.ts +71 -0
- package/vitest.config.ts +1 -1
- package/vitest.visual.config.ts +7 -0
- package/.storybook/vscode.css +0 -814
- package/src/components/nodes/comment/FormatToolbar.tsx +0 -118
- package/src/components/nodes/comment/comment.tsx +0 -103
- package/src/components/nodes/comment/styles.module.css +0 -150
- package/src/components/panels/conversation/index.module.css +0 -151
- package/src/components/panels/conversation/index.tsx +0 -162
- package/src/components/panels/events/CustomEventsEditor.tsx +0 -384
- package/src/css/vscode.css +0 -13
- package/src/hooks/useDetachNodes.ts +0 -39
- package/src/plugin/graphrunner-webworker/types.ts +0 -17
- package/src/specifics/registerDefaultSpecifics.ts +0 -5
- package/src/store/chat.ts +0 -73
- package/src/store/graphRunnerClient.ts +0 -110
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { useStore } from 'zustand';
|
|
3
|
+
import {
|
|
4
|
+
VscodeButton,
|
|
5
|
+
VscodeOption,
|
|
6
|
+
VscodeSingleSelect
|
|
7
|
+
} from '@vscode-elements/react-elements';
|
|
8
|
+
import { Trash } from 'iconoir-react';
|
|
9
|
+
import { useSystem } from '@/system/provider';
|
|
10
|
+
import styles from './styles.module.css';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Editor for the custom automatic type conversions used by auto-convert. Rules
|
|
14
|
+
* defined here override the generic spec-derived defaults and are persisted with
|
|
15
|
+
* the editor settings.
|
|
16
|
+
*
|
|
17
|
+
* A rule pins the converter node AND the specific ports to wire: which input
|
|
18
|
+
* receives the `from` value and which output produces the `to` value. The port
|
|
19
|
+
* choices are filtered to type-compatible sockets, so converter nodes with more
|
|
20
|
+
* than one input/output resolve unambiguously.
|
|
21
|
+
*/
|
|
22
|
+
export const ConversionsSettings: React.FC = () => {
|
|
23
|
+
const sys = useSystem();
|
|
24
|
+
const values = useStore(sys.registry, (s) => s.values);
|
|
25
|
+
const specs = useStore(sys.specStore, (s) => s.specs);
|
|
26
|
+
const conversions = useStore(sys.conversionStore, (s) => s.conversions);
|
|
27
|
+
|
|
28
|
+
const valueTypes = useMemo(
|
|
29
|
+
() => Object.keys(values).filter((t) => t !== 'flow'),
|
|
30
|
+
[values]
|
|
31
|
+
);
|
|
32
|
+
const nodeTypes = useMemo(() => specs.map((s) => s.type).sort(), [specs]);
|
|
33
|
+
|
|
34
|
+
const [from, setFrom] = useState('');
|
|
35
|
+
const [to, setTo] = useState('');
|
|
36
|
+
const [nodeType, setNodeType] = useState('');
|
|
37
|
+
const [inputKey, setInputKey] = useState('');
|
|
38
|
+
const [outputKey, setOutputKey] = useState('');
|
|
39
|
+
|
|
40
|
+
const selectedSpec = useMemo(
|
|
41
|
+
() => specs.find((s) => s.type === nodeType),
|
|
42
|
+
[specs, nodeType]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Only ports whose type matches the conversion endpoints are valid targets:
|
|
46
|
+
// the spliced node must accept `from` and emit `to`.
|
|
47
|
+
const inputPorts = useMemo(
|
|
48
|
+
() =>
|
|
49
|
+
(selectedSpec?.inputs ?? []).filter(
|
|
50
|
+
(i) => i.valueType !== 'flow' && (!from || i.valueType === from)
|
|
51
|
+
),
|
|
52
|
+
[selectedSpec, from]
|
|
53
|
+
);
|
|
54
|
+
const outputPorts = useMemo(
|
|
55
|
+
() =>
|
|
56
|
+
(selectedSpec?.outputs ?? []).filter(
|
|
57
|
+
(o) => o.valueType !== 'flow' && (!to || o.valueType === to)
|
|
58
|
+
),
|
|
59
|
+
[selectedSpec, to]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Default the port selection to the first compatible socket whenever the node
|
|
63
|
+
// or endpoints change; the user can still override when several ports match.
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
setInputKey(inputPorts[0]?.name ?? '');
|
|
66
|
+
}, [inputPorts]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setOutputKey(outputPorts[0]?.name ?? '');
|
|
69
|
+
}, [outputPorts]);
|
|
70
|
+
|
|
71
|
+
const nodeChosen = Boolean(nodeType);
|
|
72
|
+
const portsMissing =
|
|
73
|
+
nodeChosen && (inputPorts.length === 0 || outputPorts.length === 0);
|
|
74
|
+
const canAdd =
|
|
75
|
+
Boolean(from && to && nodeType && inputKey && outputKey) && from !== to;
|
|
76
|
+
|
|
77
|
+
const add = () => {
|
|
78
|
+
if (!canAdd) return;
|
|
79
|
+
sys.conversionStore
|
|
80
|
+
.getState()
|
|
81
|
+
.registerConversion({ from, to, nodeType, inputKey, outputKey });
|
|
82
|
+
setFrom('');
|
|
83
|
+
setTo('');
|
|
84
|
+
setNodeType('');
|
|
85
|
+
setInputKey('');
|
|
86
|
+
setOutputKey('');
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className={styles.conversions}>
|
|
91
|
+
<span className={styles.conversionsLabel}>
|
|
92
|
+
Custom rules override the built-in defaults.
|
|
93
|
+
</span>
|
|
94
|
+
{conversions.length === 0 && (
|
|
95
|
+
<div className={styles.empty}>No custom conversions.</div>
|
|
96
|
+
)}
|
|
97
|
+
{conversions.map((rule) => (
|
|
98
|
+
<div key={`${rule.from}->${rule.to}`} className={styles.rule}>
|
|
99
|
+
<span className={styles.ruleText}>
|
|
100
|
+
{rule.from} → {rule.to}{' '}
|
|
101
|
+
<span className={styles.ruleNode}>
|
|
102
|
+
({rule.nodeType}
|
|
103
|
+
{rule.inputKey && rule.outputKey
|
|
104
|
+
? `: ${rule.inputKey} → ${rule.outputKey}`
|
|
105
|
+
: ''}
|
|
106
|
+
)
|
|
107
|
+
</span>
|
|
108
|
+
</span>
|
|
109
|
+
<VscodeButton
|
|
110
|
+
secondary
|
|
111
|
+
iconOnly
|
|
112
|
+
title="Remove"
|
|
113
|
+
onClick={() =>
|
|
114
|
+
sys.conversionStore
|
|
115
|
+
.getState()
|
|
116
|
+
.removeConversion(rule.from, rule.to)
|
|
117
|
+
}
|
|
118
|
+
>
|
|
119
|
+
<Trash />
|
|
120
|
+
</VscodeButton>
|
|
121
|
+
</div>
|
|
122
|
+
))}
|
|
123
|
+
|
|
124
|
+
<div className={styles.addForm}>
|
|
125
|
+
<VscodeSingleSelect
|
|
126
|
+
value={from}
|
|
127
|
+
onChange={(e: any) => setFrom(String(e?.target?.value ?? ''))}
|
|
128
|
+
>
|
|
129
|
+
<VscodeOption value="">from…</VscodeOption>
|
|
130
|
+
{valueTypes.map((t) => (
|
|
131
|
+
<VscodeOption key={t} value={t}>
|
|
132
|
+
{t}
|
|
133
|
+
</VscodeOption>
|
|
134
|
+
))}
|
|
135
|
+
</VscodeSingleSelect>
|
|
136
|
+
<VscodeSingleSelect
|
|
137
|
+
value={to}
|
|
138
|
+
onChange={(e: any) => setTo(String(e?.target?.value ?? ''))}
|
|
139
|
+
>
|
|
140
|
+
<VscodeOption value="">to…</VscodeOption>
|
|
141
|
+
{valueTypes.map((t) => (
|
|
142
|
+
<VscodeOption key={t} value={t}>
|
|
143
|
+
{t}
|
|
144
|
+
</VscodeOption>
|
|
145
|
+
))}
|
|
146
|
+
</VscodeSingleSelect>
|
|
147
|
+
<VscodeSingleSelect
|
|
148
|
+
value={nodeType}
|
|
149
|
+
onChange={(e: any) => setNodeType(String(e?.target?.value ?? ''))}
|
|
150
|
+
>
|
|
151
|
+
<VscodeOption value="">converter node…</VscodeOption>
|
|
152
|
+
{nodeTypes.map((t) => (
|
|
153
|
+
<VscodeOption key={t} value={t}>
|
|
154
|
+
{t}
|
|
155
|
+
</VscodeOption>
|
|
156
|
+
))}
|
|
157
|
+
</VscodeSingleSelect>
|
|
158
|
+
|
|
159
|
+
{nodeChosen && !portsMissing && (
|
|
160
|
+
<>
|
|
161
|
+
<VscodeSingleSelect
|
|
162
|
+
value={inputKey}
|
|
163
|
+
onChange={(e: any) => setInputKey(String(e?.target?.value ?? ''))}
|
|
164
|
+
>
|
|
165
|
+
<VscodeOption value="">input port…</VscodeOption>
|
|
166
|
+
{inputPorts.map((p) => (
|
|
167
|
+
<VscodeOption key={p.name} value={p.name}>
|
|
168
|
+
{p.name} ({p.valueType})
|
|
169
|
+
</VscodeOption>
|
|
170
|
+
))}
|
|
171
|
+
</VscodeSingleSelect>
|
|
172
|
+
<VscodeSingleSelect
|
|
173
|
+
value={outputKey}
|
|
174
|
+
onChange={(e: any) =>
|
|
175
|
+
setOutputKey(String(e?.target?.value ?? ''))
|
|
176
|
+
}
|
|
177
|
+
>
|
|
178
|
+
<VscodeOption value="">output port…</VscodeOption>
|
|
179
|
+
{outputPorts.map((p) => (
|
|
180
|
+
<VscodeOption key={p.name} value={p.name}>
|
|
181
|
+
{p.name} ({p.valueType})
|
|
182
|
+
</VscodeOption>
|
|
183
|
+
))}
|
|
184
|
+
</VscodeSingleSelect>
|
|
185
|
+
</>
|
|
186
|
+
)}
|
|
187
|
+
|
|
188
|
+
{portsMissing && (
|
|
189
|
+
<span className={styles.empty}>
|
|
190
|
+
“{nodeType}” has no {inputPorts.length === 0 ? `${from} input` : ''}
|
|
191
|
+
{inputPorts.length === 0 && outputPorts.length === 0 ? ' / ' : ''}
|
|
192
|
+
{outputPorts.length === 0 ? `${to} output` : ''} , not a valid
|
|
193
|
+
converter for {from} → {to}.
|
|
194
|
+
</span>
|
|
195
|
+
)}
|
|
196
|
+
|
|
197
|
+
<VscodeButton secondary disabled={!canAdd} onClick={add}>
|
|
198
|
+
+ Add conversion
|
|
199
|
+
</VscodeButton>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
@@ -1,206 +1,251 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
EDGE_TYPE,
|
|
4
|
-
LAYOUT_TYPE,
|
|
5
|
-
type EdgeType,
|
|
6
|
-
type LayoutType
|
|
7
|
-
} from '@/store/settings';
|
|
8
|
-
|
|
1
|
+
import { Fragment, useMemo, useState } from 'react';
|
|
2
|
+
import { useStore } from 'zustand';
|
|
9
3
|
import {
|
|
10
4
|
VscodeCheckbox,
|
|
11
5
|
VscodeDivider,
|
|
12
|
-
VscodeLabel,
|
|
13
6
|
VscodeOption,
|
|
14
|
-
VscodeSingleSelect
|
|
7
|
+
VscodeSingleSelect,
|
|
8
|
+
VscodeTextfield
|
|
15
9
|
} from '@vscode-elements/react-elements';
|
|
16
|
-
import {
|
|
10
|
+
import { Undo } from 'iconoir-react';
|
|
11
|
+
import { useSystem } from '@/system/provider';
|
|
12
|
+
import type { SettingDescriptor, SettingsValues } from '@/store/settingsSchema';
|
|
17
13
|
import styles from './styles.module.css';
|
|
18
14
|
import { BasePanel } from '../base';
|
|
15
|
+
import { SectionTitle } from '../common/SectionTitle';
|
|
16
|
+
import { ConversionsSettings } from './ConversionsSettings';
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return <div className={styles.title}>{children}</div>;
|
|
18
|
+
type RowProps = {
|
|
19
|
+
descriptor: SettingDescriptor;
|
|
20
|
+
value: unknown;
|
|
21
|
+
setValue: (value: unknown) => void;
|
|
25
22
|
};
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
/** The input control for a descriptor, chosen by its `type`. */
|
|
25
|
+
const SettingControl = ({ descriptor, value, setValue }: RowProps) => {
|
|
26
|
+
switch (descriptor.type) {
|
|
27
|
+
case 'boolean':
|
|
28
|
+
return (
|
|
29
|
+
<VscodeCheckbox
|
|
30
|
+
checked={Boolean(value)}
|
|
31
|
+
onChange={(e: any) => setValue(Boolean(e?.target?.checked))}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
case 'enum':
|
|
35
|
+
return (
|
|
36
|
+
<VscodeSingleSelect
|
|
37
|
+
value={String(value ?? '')}
|
|
38
|
+
onChange={(e: any) => {
|
|
39
|
+
const next = e?.target?.value as string | undefined;
|
|
40
|
+
if (next !== undefined) setValue(next);
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{descriptor.options.map((option) => (
|
|
44
|
+
<VscodeOption key={option.value} value={option.value}>
|
|
45
|
+
{option.label}
|
|
46
|
+
</VscodeOption>
|
|
47
|
+
))}
|
|
48
|
+
</VscodeSingleSelect>
|
|
49
|
+
);
|
|
50
|
+
case 'number':
|
|
51
|
+
return (
|
|
52
|
+
<VscodeTextfield
|
|
53
|
+
type="number"
|
|
54
|
+
value={String(value ?? '')}
|
|
55
|
+
min={descriptor.min}
|
|
56
|
+
max={descriptor.max}
|
|
57
|
+
step={descriptor.step}
|
|
58
|
+
style={{ width: '100%' }}
|
|
59
|
+
onChange={(e: any) => {
|
|
60
|
+
const next = Number(e?.target?.value);
|
|
61
|
+
if (!Number.isNaN(next)) setValue(next);
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
case 'string':
|
|
66
|
+
return (
|
|
67
|
+
<VscodeTextfield
|
|
68
|
+
value={String(value ?? '')}
|
|
69
|
+
placeholder={descriptor.placeholder}
|
|
70
|
+
style={{ width: '100%' }}
|
|
71
|
+
onChange={(e: any) => setValue(String(e?.target?.value ?? ''))}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
case 'custom': {
|
|
75
|
+
const Render = descriptor.render;
|
|
76
|
+
return (
|
|
77
|
+
<Render value={value} setValue={setValue} descriptor={descriptor} />
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
39
81
|
};
|
|
40
82
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
83
|
+
const ResetButton = ({ onClick }: { onClick: () => void }) => (
|
|
84
|
+
<button
|
|
85
|
+
type="button"
|
|
86
|
+
className={styles.reset}
|
|
87
|
+
title="Reset to default"
|
|
88
|
+
aria-label="Reset to default"
|
|
89
|
+
onClick={onClick}
|
|
90
|
+
>
|
|
91
|
+
<Undo width={13} height={13} />
|
|
92
|
+
</button>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const Label = ({
|
|
96
|
+
descriptor,
|
|
97
|
+
modified
|
|
47
98
|
}: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
descriptor: SettingDescriptor;
|
|
100
|
+
modified: boolean;
|
|
101
|
+
}) => (
|
|
102
|
+
<span className={styles.label}>
|
|
103
|
+
{descriptor.title}
|
|
104
|
+
{modified && <span className={styles.modified} title="Modified" />}
|
|
105
|
+
</span>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
/** A single auto-generated setting row. Booleans put the control inline with the
|
|
109
|
+
* label (toggle layout); other types stack the control under the label. */
|
|
110
|
+
const SettingRow = ({ descriptor, value, setValue }: RowProps) => {
|
|
111
|
+
const modified = descriptor.type !== 'custom' && value !== descriptor.default;
|
|
112
|
+
const reset = () => setValue(descriptor.default);
|
|
113
|
+
|
|
114
|
+
if (descriptor.type === 'custom') {
|
|
115
|
+
return (
|
|
116
|
+
<div className={styles.setting}>
|
|
117
|
+
{descriptor.title && (
|
|
118
|
+
<span className={styles.label}>{descriptor.title}</span>
|
|
119
|
+
)}
|
|
120
|
+
{descriptor.description && (
|
|
121
|
+
<span className={styles.description}>{descriptor.description}</span>
|
|
122
|
+
)}
|
|
123
|
+
<SettingControl
|
|
124
|
+
descriptor={descriptor}
|
|
125
|
+
value={value}
|
|
126
|
+
setValue={setValue}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (descriptor.type === 'boolean') {
|
|
133
|
+
return (
|
|
134
|
+
<div className={`${styles.setting} ${styles.toggle}`}>
|
|
135
|
+
<SettingControl
|
|
136
|
+
descriptor={descriptor}
|
|
137
|
+
value={value}
|
|
138
|
+
setValue={setValue}
|
|
139
|
+
/>
|
|
140
|
+
<div className={styles.body}>
|
|
141
|
+
<Label descriptor={descriptor} modified={modified} />
|
|
142
|
+
{descriptor.description && (
|
|
143
|
+
<span className={styles.description}>{descriptor.description}</span>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
{modified && <ResetButton onClick={reset} />}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
70
150
|
|
|
71
|
-
const ToggleSetting = ({
|
|
72
|
-
label,
|
|
73
|
-
description,
|
|
74
|
-
checked,
|
|
75
|
-
onChange
|
|
76
|
-
}: {
|
|
77
|
-
label: string;
|
|
78
|
-
description: React.ReactNode;
|
|
79
|
-
checked: boolean;
|
|
80
|
-
onChange: (checked: boolean) => void;
|
|
81
|
-
}) => {
|
|
82
151
|
return (
|
|
83
|
-
<div className=
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
onChange={(event: any) => onChange(Boolean(event?.target?.checked))}
|
|
88
|
-
/>
|
|
89
|
-
<div className="flex flex-col justify-start gap-0.5">
|
|
90
|
-
<VscodeLabel>{label}</VscodeLabel>
|
|
91
|
-
<Description>{description}</Description>
|
|
152
|
+
<div className={styles.setting}>
|
|
153
|
+
<div className={styles.labelRow}>
|
|
154
|
+
<Label descriptor={descriptor} modified={modified} />
|
|
155
|
+
{modified && <ResetButton onClick={reset} />}
|
|
92
156
|
</div>
|
|
157
|
+
{descriptor.description && (
|
|
158
|
+
<span className={styles.description}>{descriptor.description}</span>
|
|
159
|
+
)}
|
|
160
|
+
<SettingControl
|
|
161
|
+
descriptor={descriptor}
|
|
162
|
+
value={value}
|
|
163
|
+
setValue={setValue}
|
|
164
|
+
/>
|
|
93
165
|
</div>
|
|
94
166
|
);
|
|
95
167
|
};
|
|
96
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Schema-driven Settings panel. Rows are auto-generated from the descriptor
|
|
171
|
+
* registry (`system.settingsSchema`) — built-in and plugin-contributed settings
|
|
172
|
+
* render through the same path — grouped by section, filterable, with reset.
|
|
173
|
+
*/
|
|
97
174
|
export const Settings = () => {
|
|
98
175
|
const system = useSystem();
|
|
99
|
-
|
|
100
|
-
const
|
|
176
|
+
const descriptors = useStore(system.settingsSchema, (s) => s.settings);
|
|
177
|
+
const sectionOrder = useStore(system.settingsSchema, (s) => s.sectionOrder);
|
|
178
|
+
const values = useStore(system.systemSettings) as SettingsValues & {
|
|
179
|
+
setSetting: (key: string, value: unknown) => void;
|
|
180
|
+
};
|
|
181
|
+
const [query, setQuery] = useState('');
|
|
182
|
+
|
|
183
|
+
const bySection = useMemo(() => {
|
|
184
|
+
const q = query.trim().toLowerCase();
|
|
185
|
+
const matches = (d: SettingDescriptor) =>
|
|
186
|
+
!q ||
|
|
187
|
+
[d.title, d.description, d.section, d.key].some(
|
|
188
|
+
(field) => typeof field === 'string' && field.toLowerCase().includes(q)
|
|
189
|
+
);
|
|
190
|
+
const visible = (d: SettingDescriptor) => (d.when ? d.when(values) : true);
|
|
191
|
+
|
|
192
|
+
const map = new Map<string, SettingDescriptor[]>();
|
|
193
|
+
for (const descriptor of descriptors) {
|
|
194
|
+
if (!matches(descriptor) || !visible(descriptor)) continue;
|
|
195
|
+
const rows = map.get(descriptor.section) ?? [];
|
|
196
|
+
rows.push(descriptor);
|
|
197
|
+
map.set(descriptor.section, rows);
|
|
198
|
+
}
|
|
199
|
+
for (const rows of map.values()) {
|
|
200
|
+
rows.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
201
|
+
}
|
|
202
|
+
return map;
|
|
203
|
+
}, [descriptors, query, values]);
|
|
204
|
+
|
|
205
|
+
const visibleSections = sectionOrder.filter(
|
|
206
|
+
(section) => (bySection.get(section)?.length ?? 0) > 0
|
|
207
|
+
);
|
|
101
208
|
|
|
102
209
|
return (
|
|
103
210
|
<BasePanel>
|
|
104
|
-
<div className=
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
onChange={(value) => settings.setEdgeType(value as EdgeType)}
|
|
111
|
-
>
|
|
112
|
-
<VscodeOption value="">Select an edge type...</VscodeOption>
|
|
113
|
-
{EdgeValues.map((type) => (
|
|
114
|
-
<VscodeOption key={type} value={type}>
|
|
115
|
-
{type}
|
|
116
|
-
</VscodeOption>
|
|
117
|
-
))}
|
|
118
|
-
</SelectSetting>
|
|
119
|
-
|
|
120
|
-
<SelectSetting
|
|
121
|
-
label="Layout Type"
|
|
122
|
-
description="Select the type of layout engine to use in the graph editor."
|
|
123
|
-
value={settings.layoutType}
|
|
124
|
-
onChange={(value) => settings.setLayoutType(value as LayoutType)}
|
|
125
|
-
>
|
|
126
|
-
<VscodeOption value="">Select a layout type...</VscodeOption>
|
|
127
|
-
{LayoutValues.map((type) => (
|
|
128
|
-
<VscodeOption key={type} value={type}>
|
|
129
|
-
{type}
|
|
130
|
-
</VscodeOption>
|
|
131
|
-
))}
|
|
132
|
-
</SelectSetting>
|
|
133
|
-
|
|
134
|
-
<VscodeDivider />
|
|
135
|
-
|
|
136
|
-
<SectionTitle>Accessibility</SectionTitle>
|
|
137
|
-
<ToggleSetting
|
|
138
|
-
label="Show inline types"
|
|
139
|
-
description={
|
|
140
|
-
'Adds additional spans to help differentiate types for colorblind users.'
|
|
141
|
-
}
|
|
142
|
-
checked={settings.inlineTypes}
|
|
143
|
-
onChange={(value) => settings.setInlineTypes(value)}
|
|
144
|
-
/>
|
|
145
|
-
|
|
146
|
-
<VscodeDivider />
|
|
147
|
-
|
|
148
|
-
<SectionTitle>Interaction</SectionTitle>
|
|
149
|
-
<ToggleSetting
|
|
150
|
-
label="Use delayed interaction"
|
|
151
|
-
description={'Forces a user to click save to update port.'}
|
|
152
|
-
checked={settings.delayedUpdate}
|
|
153
|
-
onChange={(value) => settings.setDelayedUpdate(value)}
|
|
154
|
-
/>
|
|
155
|
-
<ToggleSetting
|
|
156
|
-
label="Click to connect"
|
|
157
|
-
description={
|
|
158
|
-
'Allows you to quick connect nodes by clicking on the 2 port.'
|
|
159
|
-
}
|
|
160
|
-
checked={settings.connectOnClick}
|
|
161
|
-
onChange={(value) => settings.setConnectOnClick(value)}
|
|
162
|
-
/>
|
|
163
|
-
|
|
164
|
-
<VscodeDivider />
|
|
165
|
-
|
|
166
|
-
<SectionTitle>Display</SectionTitle>
|
|
167
|
-
<ToggleSetting
|
|
168
|
-
label="Show inline values"
|
|
169
|
-
description={
|
|
170
|
-
'Shows values directly on the node. Useful for debugging but can be cluttered.'
|
|
171
|
-
}
|
|
172
|
-
checked={settings.inlineValues}
|
|
173
|
-
onChange={(value) => settings.setInlineValues(value)}
|
|
174
|
-
/>
|
|
175
|
-
<ToggleSetting
|
|
176
|
-
label="Show Minimap"
|
|
177
|
-
description={'Shows the minimap in the graph editing area.'}
|
|
178
|
-
checked={settings.showMinimap}
|
|
179
|
-
onChange={(value) => settings.setShowMinimap(value)}
|
|
180
|
-
/>
|
|
181
|
-
<ToggleSetting
|
|
182
|
-
label="Show Grid"
|
|
183
|
-
description={'Shows the grid in the graph editing area.'}
|
|
184
|
-
checked={settings.showGrid}
|
|
185
|
-
onChange={(value) => settings.setShowGrid(value)}
|
|
186
|
-
/>
|
|
187
|
-
<ToggleSetting
|
|
188
|
-
label="Snap to Grid"
|
|
189
|
-
description={'Snaps nodes to the grid while dragging.'}
|
|
190
|
-
checked={settings.snapGrid}
|
|
191
|
-
onChange={(value) => settings.setSnapGrid(value)}
|
|
192
|
-
/>
|
|
193
|
-
|
|
194
|
-
<VscodeDivider />
|
|
195
|
-
|
|
196
|
-
<SectionTitle>Performance</SectionTitle>
|
|
197
|
-
<ToggleSetting
|
|
198
|
-
label="Show execution time"
|
|
199
|
-
description={'Shows how long it takes for a node to process.'}
|
|
200
|
-
checked={settings.showTimings}
|
|
201
|
-
onChange={(value) => settings.setShowTimings(value)}
|
|
211
|
+
<div className={styles.searchRow}>
|
|
212
|
+
<VscodeTextfield
|
|
213
|
+
value={query}
|
|
214
|
+
placeholder="Search settings"
|
|
215
|
+
style={{ width: '100%' }}
|
|
216
|
+
onChange={(e: any) => setQuery(String(e?.target?.value ?? ''))}
|
|
202
217
|
/>
|
|
203
218
|
</div>
|
|
219
|
+
|
|
220
|
+
{visibleSections.length === 0 ? (
|
|
221
|
+
<div className={styles.empty}>No settings match “{query}”.</div>
|
|
222
|
+
) : (
|
|
223
|
+
<div className={styles.list}>
|
|
224
|
+
{visibleSections.map((section, index) => (
|
|
225
|
+
<Fragment key={section}>
|
|
226
|
+
{index > 0 && <VscodeDivider className={styles.divider} />}
|
|
227
|
+
<SectionTitle>{section}</SectionTitle>
|
|
228
|
+
{bySection.get(section)!.map((descriptor) => (
|
|
229
|
+
<Fragment key={descriptor.key}>
|
|
230
|
+
<SettingRow
|
|
231
|
+
descriptor={descriptor}
|
|
232
|
+
// Fall back to the descriptor default so plugin-registered
|
|
233
|
+
// settings (whose value is not seeded into the settings
|
|
234
|
+
// store until first changed) show their real initial state.
|
|
235
|
+
value={values[descriptor.key] ?? descriptor.default}
|
|
236
|
+
setValue={(next) => values.setSetting(descriptor.key, next)}
|
|
237
|
+
/>
|
|
238
|
+
{/* The conversions editor is a bespoke built-in control shown
|
|
239
|
+
under Auto-convert when it is enabled. */}
|
|
240
|
+
{descriptor.key === 'autoConvert' && values.autoConvert ? (
|
|
241
|
+
<ConversionsSettings />
|
|
242
|
+
) : null}
|
|
243
|
+
</Fragment>
|
|
244
|
+
))}
|
|
245
|
+
</Fragment>
|
|
246
|
+
))}
|
|
247
|
+
</div>
|
|
248
|
+
)}
|
|
204
249
|
</BasePanel>
|
|
205
250
|
);
|
|
206
251
|
};
|