@flowdrop/flowdrop 1.14.0 → 2.0.0-beta.1
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/CHANGELOG.md +475 -0
- package/MIGRATION-2.0.md +472 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +162 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +110 -66
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +16 -67
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +45 -40
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +118 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
- package/dist/components/chat/AIChatPanel.svelte +93 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +29 -13
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +4 -2
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +16 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +292 -0
- package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +65 -6
- package/dist/components/playground/PipelinePanel.svelte +11 -5
- package/dist/components/playground/PipelineTableView.svelte +186 -44
- package/dist/components/playground/Playground.svelte +95 -92
- package/dist/components/playground/Playground.svelte.d.ts +2 -0
- package/dist/components/playground/PlaygroundApp.svelte +6 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundModal.svelte +13 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundStudio.svelte +34 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
- package/dist/config/endpoints.d.ts +0 -7
- package/dist/config/endpoints.js +2 -10
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/display/index.d.ts +0 -2
- package/dist/display/index.js +0 -6
- package/dist/editor/index.d.ts +19 -20
- package/dist/editor/index.js +25 -35
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +17 -13
- package/dist/form/full.js +22 -27
- package/dist/form/index.d.ts +3 -3
- package/dist/form/index.js +3 -3
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +7 -3
- package/dist/helpers/proximityConnect.js +19 -6
- package/dist/helpers/workflowEditorHelper.d.ts +12 -5
- package/dist/helpers/workflowEditorHelper.js +27 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +227 -164
- package/dist/playground/index.d.ts +2 -3
- package/dist/playground/index.js +2 -30
- package/dist/playground/mount.d.ts +15 -0
- package/dist/playground/mount.js +46 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodes.d.ts +1 -26
- package/dist/registry/builtinNodes.js +14 -50
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +53 -6
- package/dist/services/agentSpecExecutionService.js +0 -1
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +5 -22
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/chatService.d.ts +8 -4
- package/dist/services/chatService.js +15 -15
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +14 -9
- package/dist/services/interruptService.js +27 -27
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +14 -9
- package/dist/services/playgroundService.js +31 -30
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +45 -0
- package/dist/stores/apiContext.js +65 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +70 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -216
- package/dist/stores/playgroundStore.svelte.js +515 -572
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +157 -53
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/formMerge.d.ts +36 -0
- package/dist/utils/formMerge.js +70 -0
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -19
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +6 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
package/dist/utils/icons.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Centralized icon management for FlowDrop
|
|
3
3
|
* Ensures consistent icon usage across all components
|
|
4
4
|
*/
|
|
5
|
-
import { getCategoryIcon as getCategoryIconFromStore } from '../stores/categoriesStore.svelte.js';
|
|
6
5
|
/**
|
|
7
6
|
* Default icons for different contexts
|
|
8
7
|
*/
|
|
@@ -103,18 +102,19 @@ export const CATEGORY_ICONS = {
|
|
|
103
102
|
};
|
|
104
103
|
/**
|
|
105
104
|
* Get the appropriate icon for a node
|
|
105
|
+
* @param categories - The instance's categories store (e.g. `fd.categories`)
|
|
106
106
|
* @param nodeIcon - The node's specific icon
|
|
107
107
|
* @param category - The node's category
|
|
108
108
|
* @returns The icon to use
|
|
109
109
|
*/
|
|
110
|
-
export function getNodeIcon(nodeIcon, category) {
|
|
110
|
+
export function getNodeIcon(categories, nodeIcon, category) {
|
|
111
111
|
// If node has a specific icon, use it
|
|
112
112
|
if (nodeIcon) {
|
|
113
113
|
return nodeIcon;
|
|
114
114
|
}
|
|
115
115
|
// If category is provided, use category icon from store (which includes API overrides)
|
|
116
116
|
if (category) {
|
|
117
|
-
return
|
|
117
|
+
return categories.getIcon(category);
|
|
118
118
|
}
|
|
119
119
|
// Fallback to default node icon
|
|
120
120
|
return DEFAULT_ICONS.NODE;
|
|
@@ -123,11 +123,12 @@ export function getNodeIcon(nodeIcon, category) {
|
|
|
123
123
|
* Get the appropriate icon for a category.
|
|
124
124
|
* Checks the categories store first (which includes API overrides),
|
|
125
125
|
* then falls back to the static CATEGORY_ICONS map, then to the default.
|
|
126
|
+
* @param categories - The instance's categories store (e.g. `fd.categories`)
|
|
126
127
|
* @param category - The category
|
|
127
128
|
* @returns The icon to use
|
|
128
129
|
*/
|
|
129
|
-
export function getCategoryIcon(category) {
|
|
130
|
-
return
|
|
130
|
+
export function getCategoryIcon(categories, category) {
|
|
131
|
+
return categories.getIcon(category);
|
|
131
132
|
}
|
|
132
133
|
/**
|
|
133
134
|
* Get a default icon by key
|
package/dist/utils/nodeSwap.d.ts
CHANGED
|
@@ -161,6 +161,10 @@ export declare function mapConfig(oldConfig: ConfigValues, newConfigSchema: Conf
|
|
|
161
161
|
*
|
|
162
162
|
* This does NOT mutate anything — it returns a preview that can be displayed
|
|
163
163
|
* to the user for confirmation before executing the swap.
|
|
164
|
+
*
|
|
165
|
+
* Thin wrapper over {@link computeSwapPreviewWithOptions} — with no strategies
|
|
166
|
+
* or overrides the cascade reduces to the built-in exact matching, so both
|
|
167
|
+
* entry points share one implementation.
|
|
164
168
|
*/
|
|
165
169
|
export declare function computeSwapPreview(oldNode: WorkflowNode, newMetadata: NodeMetadata, edges: WorkflowEdge[], allNodes: WorkflowNode[], checker?: PortCompatibilityChecker | null): SwapPreview;
|
|
166
170
|
/**
|
|
@@ -183,7 +187,7 @@ export declare function getVersionUpgrade(currentMetadata: NodeMetadata, allNode
|
|
|
183
187
|
*
|
|
184
188
|
* Resolution order:
|
|
185
189
|
* 1. Check strategies — first canHandle() match wins for mapPorts()/mapConfig()
|
|
186
|
-
* 2. Fall through to built-in
|
|
190
|
+
* 2. Fall through to built-in exact matching (ID, then name) for ports not covered by strategy
|
|
187
191
|
* 3. Apply portOverrides on top (highest priority — user's manual overrides)
|
|
188
192
|
* 4. Same cascade for config
|
|
189
193
|
*/
|
|
@@ -199,7 +203,7 @@ export declare function computeInteractiveState(oldNode: WorkflowNode, newMetada
|
|
|
199
203
|
* Convert user-edited InteractiveSwapState back into a SwapPreview
|
|
200
204
|
* for executeSwap(). Pure function, no side effects.
|
|
201
205
|
*/
|
|
202
|
-
export declare function buildSwapPreviewFromState(state: InteractiveSwapState,
|
|
206
|
+
export declare function buildSwapPreviewFromState(state: InteractiveSwapState, _allEdges: WorkflowEdge[]): SwapPreview;
|
|
203
207
|
/**
|
|
204
208
|
* Headless one-shot swap with full validation.
|
|
205
209
|
*
|
package/dist/utils/nodeSwap.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module utils/nodeSwap
|
|
8
8
|
*/
|
|
9
|
-
import { buildHandleId, extractPortId
|
|
9
|
+
import { buildHandleId, extractPortId } from './handleIds.js';
|
|
10
10
|
import { generateNodeId } from './nodeIds.js';
|
|
11
11
|
/** Error class for swap validation failures. */
|
|
12
12
|
export class SwapValidationError extends Error {
|
|
@@ -86,16 +86,17 @@ export function mapConfig(oldConfig, newConfigSchema, newDefaults = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
return { config, carriedOver, reset };
|
|
88
88
|
}
|
|
89
|
-
// =========================================================================
|
|
90
|
-
// Port matching
|
|
91
|
-
// =========================================================================
|
|
92
89
|
/**
|
|
93
90
|
* Find the best matching port on the new node for a given old port.
|
|
94
91
|
*
|
|
95
|
-
*
|
|
92
|
+
* Two-pass strategy — exact matches only:
|
|
96
93
|
* 1. Exact port ID match with compatible dataType
|
|
97
|
-
* 2.
|
|
98
|
-
*
|
|
94
|
+
* 2. Exact port name match (case-insensitive) with compatible dataType
|
|
95
|
+
*
|
|
96
|
+
* There is deliberately no fuzzy "first compatible dataType" fallback:
|
|
97
|
+
* it silently rewired edges to the wrong port (e.g. a `text` edge landing
|
|
98
|
+
* on `assistant_message`). If neither ID nor name matches, the edge is
|
|
99
|
+
* dropped and reported instead.
|
|
99
100
|
*/
|
|
100
101
|
function findMatchingPort(oldPort, newPorts, usedPortIds, checker) {
|
|
101
102
|
const available = newPorts.filter((p) => p.type === oldPort.type && !usedPortIds.has(p.id));
|
|
@@ -116,11 +117,7 @@ function findMatchingPort(oldPort, newPorts, usedPortIds, checker) {
|
|
|
116
117
|
// Pass 2: name match (case-insensitive)
|
|
117
118
|
const oldNameLower = oldPort.name.toLowerCase();
|
|
118
119
|
const nameMatch = available.find((p) => p.name.toLowerCase() === oldNameLower && isCompatible(oldPort, p));
|
|
119
|
-
|
|
120
|
-
return nameMatch;
|
|
121
|
-
// Pass 3: first compatible dataType
|
|
122
|
-
const typeMatch = available.find((p) => isCompatible(oldPort, p));
|
|
123
|
-
return typeMatch ?? null;
|
|
120
|
+
return nameMatch ?? null;
|
|
124
121
|
}
|
|
125
122
|
/**
|
|
126
123
|
* Resolve the port metadata for an edge endpoint on a given node.
|
|
@@ -142,65 +139,13 @@ function resolvePort(node, handleId, direction) {
|
|
|
142
139
|
*
|
|
143
140
|
* This does NOT mutate anything — it returns a preview that can be displayed
|
|
144
141
|
* to the user for confirmation before executing the swap.
|
|
142
|
+
*
|
|
143
|
+
* Thin wrapper over {@link computeSwapPreviewWithOptions} — with no strategies
|
|
144
|
+
* or overrides the cascade reduces to the built-in exact matching, so both
|
|
145
|
+
* entry points share one implementation.
|
|
145
146
|
*/
|
|
146
147
|
export function computeSwapPreview(oldNode, newMetadata, edges, allNodes, checker = null) {
|
|
147
|
-
|
|
148
|
-
const newNodeId = generateNodeId(newMetadata.id, allNodes);
|
|
149
|
-
// Collect all edges connected to the old node
|
|
150
|
-
const connectedEdges = edges.filter((e) => e.source === oldNodeId || e.target === oldNodeId);
|
|
151
|
-
// Track which ports on the new node have been claimed
|
|
152
|
-
const usedInputPortIds = new Set();
|
|
153
|
-
const usedOutputPortIds = new Set();
|
|
154
|
-
const keptEdges = [];
|
|
155
|
-
const droppedEdges = [];
|
|
156
|
-
for (const edge of connectedEdges) {
|
|
157
|
-
const isSource = edge.source === oldNodeId;
|
|
158
|
-
const direction = isSource ? 'output' : 'input';
|
|
159
|
-
const handleId = isSource ? edge.sourceHandle : edge.targetHandle;
|
|
160
|
-
const usedPorts = isSource ? usedOutputPortIds : usedInputPortIds;
|
|
161
|
-
// Resolve the old port
|
|
162
|
-
const oldPort = resolvePort(oldNode, handleId, direction);
|
|
163
|
-
if (!oldPort) {
|
|
164
|
-
droppedEdges.push({
|
|
165
|
-
edge,
|
|
166
|
-
reason: `Port not found on original node`
|
|
167
|
-
});
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
// Find matching port on new node
|
|
171
|
-
const newPorts = direction === 'input' ? newMetadata.inputs : newMetadata.outputs;
|
|
172
|
-
const match = findMatchingPort(oldPort, newPorts, usedPorts, checker);
|
|
173
|
-
if (!match) {
|
|
174
|
-
droppedEdges.push({
|
|
175
|
-
edge,
|
|
176
|
-
reason: `No compatible ${direction} port found on "${newMetadata.name}"`
|
|
177
|
-
});
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
usedPorts.add(match.id);
|
|
181
|
-
// Build the rewritten edge
|
|
182
|
-
const newHandleId = buildHandleId(newNodeId, direction, match.id);
|
|
183
|
-
const newEdge = { ...edge };
|
|
184
|
-
if (isSource) {
|
|
185
|
-
newEdge.source = newNodeId;
|
|
186
|
-
newEdge.sourceHandle = newHandleId;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
newEdge.target = newNodeId;
|
|
190
|
-
newEdge.targetHandle = newHandleId;
|
|
191
|
-
}
|
|
192
|
-
keptEdges.push({ edge, newEdge });
|
|
193
|
-
}
|
|
194
|
-
// Config mapping preview
|
|
195
|
-
const { carriedOver, reset } = mapConfig(oldNode.data.config, newMetadata.configSchema, newMetadata.config);
|
|
196
|
-
return {
|
|
197
|
-
keptEdges,
|
|
198
|
-
droppedEdges,
|
|
199
|
-
hasDataLoss: droppedEdges.length > 0,
|
|
200
|
-
newNodeId,
|
|
201
|
-
configCarriedOver: carriedOver,
|
|
202
|
-
configReset: reset
|
|
203
|
-
};
|
|
148
|
+
return computeSwapPreviewWithOptions(oldNode, newMetadata, edges, allNodes, { checker });
|
|
204
149
|
}
|
|
205
150
|
// =========================================================================
|
|
206
151
|
// Swap execution
|
|
@@ -231,6 +176,9 @@ export function executeSwap(oldNode, newMetadata, preview, allNodes, allEdges) {
|
|
|
231
176
|
label: newMetadata.name,
|
|
232
177
|
config: mappedConfig,
|
|
233
178
|
metadata: newMetadata,
|
|
179
|
+
// Node components derive their handle IDs from data.nodeId — without it
|
|
180
|
+
// every edge anchored to this node silently disappears from the canvas.
|
|
181
|
+
nodeId: newNodeId,
|
|
234
182
|
extensions
|
|
235
183
|
}
|
|
236
184
|
};
|
|
@@ -298,7 +246,7 @@ function classifyMatch(oldPort, matchedPort) {
|
|
|
298
246
|
*
|
|
299
247
|
* Resolution order:
|
|
300
248
|
* 1. Check strategies — first canHandle() match wins for mapPorts()/mapConfig()
|
|
301
|
-
* 2. Fall through to built-in
|
|
249
|
+
* 2. Fall through to built-in exact matching (ID, then name) for ports not covered by strategy
|
|
302
250
|
* 3. Apply portOverrides on top (highest priority — user's manual overrides)
|
|
303
251
|
* 4. Same cascade for config
|
|
304
252
|
*/
|
|
@@ -332,84 +280,73 @@ export function computeSwapPreviewWithOptions(oldNode, newMetadata, edges, allNo
|
|
|
332
280
|
for (const override of options.portOverrides ?? []) {
|
|
333
281
|
portOverrideLookup.set(`${override.direction}:${override.oldPortId}`, override.newPortId);
|
|
334
282
|
}
|
|
335
|
-
// Track
|
|
283
|
+
// Track ports on the new node claimed by a DIFFERENT old port
|
|
336
284
|
const usedInputPortIds = new Set();
|
|
337
285
|
const usedOutputPortIds = new Set();
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
const usedPorts = isSource ? usedOutputPortIds : usedInputPortIds;
|
|
345
|
-
const oldPort = resolvePort(oldNode, handleId, direction);
|
|
346
|
-
if (!oldPort) {
|
|
347
|
-
droppedEdges.push({ edge, reason: 'Port not found on original node' });
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
286
|
+
// Memoized old-port → new-port resolutions so every edge anchored on the
|
|
287
|
+
// same old port maps to the same new port (multi-connection ports, fan-outs).
|
|
288
|
+
const portResolutions = new Map();
|
|
289
|
+
/** Resolve a new port for an old port through the priority cascade. */
|
|
290
|
+
const resolveNewPort = (oldPort, direction, usedPorts) => {
|
|
291
|
+
const newPorts = direction === 'input' ? newMetadata.inputs : newMetadata.outputs;
|
|
350
292
|
// Priority 1: Manual port override
|
|
351
293
|
const overrideKey = `${direction}:${oldPort.id}`;
|
|
352
294
|
if (portOverrideLookup.has(overrideKey)) {
|
|
353
295
|
const overrideNewPortId = portOverrideLookup.get(overrideKey);
|
|
354
296
|
if (overrideNewPortId === null) {
|
|
355
|
-
|
|
356
|
-
continue;
|
|
297
|
+
return { port: null, reason: 'Manually dropped' };
|
|
357
298
|
}
|
|
358
|
-
const newPorts = direction === 'input' ? newMetadata.inputs : newMetadata.outputs;
|
|
359
299
|
const overridePort = newPorts.find((p) => p.id === overrideNewPortId);
|
|
360
300
|
if (overridePort) {
|
|
361
|
-
|
|
362
|
-
const newHandleId = buildHandleId(newNodeId, direction, overridePort.id);
|
|
363
|
-
const newEdge = { ...edge };
|
|
364
|
-
if (isSource) {
|
|
365
|
-
newEdge.source = newNodeId;
|
|
366
|
-
newEdge.sourceHandle = newHandleId;
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
newEdge.target = newNodeId;
|
|
370
|
-
newEdge.targetHandle = newHandleId;
|
|
371
|
-
}
|
|
372
|
-
keptEdges.push({ edge, newEdge });
|
|
373
|
-
continue;
|
|
301
|
+
return { port: overridePort };
|
|
374
302
|
}
|
|
375
303
|
}
|
|
376
304
|
// Priority 2: Strategy port mapping
|
|
377
305
|
if (strategyPortMap && oldPort.id in strategyPortMap) {
|
|
378
306
|
const strategyNewPortId = strategyPortMap[oldPort.id];
|
|
379
307
|
if (strategyNewPortId === null) {
|
|
380
|
-
|
|
381
|
-
continue;
|
|
308
|
+
return { port: null, reason: 'Dropped by strategy' };
|
|
382
309
|
}
|
|
383
|
-
const newPorts = direction === 'input' ? newMetadata.inputs : newMetadata.outputs;
|
|
384
310
|
const strategyPort = newPorts.find((p) => p.id === strategyNewPortId);
|
|
385
311
|
if (strategyPort && !usedPorts.has(strategyPort.id)) {
|
|
386
|
-
|
|
387
|
-
const newHandleId = buildHandleId(newNodeId, direction, strategyPort.id);
|
|
388
|
-
const newEdge = { ...edge };
|
|
389
|
-
if (isSource) {
|
|
390
|
-
newEdge.source = newNodeId;
|
|
391
|
-
newEdge.sourceHandle = newHandleId;
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
newEdge.target = newNodeId;
|
|
395
|
-
newEdge.targetHandle = newHandleId;
|
|
396
|
-
}
|
|
397
|
-
keptEdges.push({ edge, newEdge });
|
|
398
|
-
continue;
|
|
312
|
+
return { port: strategyPort };
|
|
399
313
|
}
|
|
400
314
|
}
|
|
401
|
-
// Priority 3: Built-in
|
|
402
|
-
const newPorts = direction === 'input' ? newMetadata.inputs : newMetadata.outputs;
|
|
315
|
+
// Priority 3: Built-in exact matching (ID, then name)
|
|
403
316
|
const match = findMatchingPort(oldPort, newPorts, usedPorts, checker);
|
|
404
317
|
if (!match) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
reason: `No
|
|
408
|
-
}
|
|
318
|
+
return {
|
|
319
|
+
port: null,
|
|
320
|
+
reason: `No matching ${direction} port for "${oldPort.id}" on "${newMetadata.name}"`
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return { port: match };
|
|
324
|
+
};
|
|
325
|
+
const keptEdges = [];
|
|
326
|
+
const droppedEdges = [];
|
|
327
|
+
for (const edge of connectedEdges) {
|
|
328
|
+
const isSource = edge.source === oldNodeId;
|
|
329
|
+
const direction = isSource ? 'output' : 'input';
|
|
330
|
+
const handleId = isSource ? edge.sourceHandle : edge.targetHandle;
|
|
331
|
+
const usedPorts = isSource ? usedOutputPortIds : usedInputPortIds;
|
|
332
|
+
const oldPort = resolvePort(oldNode, handleId, direction);
|
|
333
|
+
if (!oldPort) {
|
|
334
|
+
droppedEdges.push({ edge, reason: 'Port not found on original node' });
|
|
409
335
|
continue;
|
|
410
336
|
}
|
|
411
|
-
|
|
412
|
-
|
|
337
|
+
const matchKey = `${direction}:${oldPort.id}`;
|
|
338
|
+
let resolved = portResolutions.get(matchKey);
|
|
339
|
+
if (!resolved) {
|
|
340
|
+
resolved = resolveNewPort(oldPort, direction, usedPorts);
|
|
341
|
+
portResolutions.set(matchKey, resolved);
|
|
342
|
+
if (resolved.port)
|
|
343
|
+
usedPorts.add(resolved.port.id);
|
|
344
|
+
}
|
|
345
|
+
if (!resolved.port) {
|
|
346
|
+
droppedEdges.push({ edge, reason: resolved.reason });
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const newHandleId = buildHandleId(newNodeId, direction, resolved.port.id);
|
|
413
350
|
const newEdge = { ...edge };
|
|
414
351
|
if (isSource) {
|
|
415
352
|
newEdge.source = newNodeId;
|
|
@@ -422,7 +359,7 @@ export function computeSwapPreviewWithOptions(oldNode, newMetadata, edges, allNo
|
|
|
422
359
|
keptEdges.push({ edge, newEdge });
|
|
423
360
|
}
|
|
424
361
|
// Config mapping — apply strategy then overrides
|
|
425
|
-
const {
|
|
362
|
+
const { carriedOver, reset } = mapConfig(oldNode.data.config, newMetadata.configSchema, newMetadata.config);
|
|
426
363
|
// Apply strategy config overrides
|
|
427
364
|
if (strategyConfigMap) {
|
|
428
365
|
for (const [key, mapping] of Object.entries(strategyConfigMap)) {
|
|
@@ -482,7 +419,6 @@ export function computeSwapPreviewWithOptions(oldNode, newMetadata, edges, allNo
|
|
|
482
419
|
* with match quality annotations and isFlat flags.
|
|
483
420
|
*/
|
|
484
421
|
export function computeInteractiveState(oldNode, newMetadata, edges, allNodes, options = {}) {
|
|
485
|
-
const checker = options.checker ?? null;
|
|
486
422
|
const oldNodeId = oldNode.id;
|
|
487
423
|
const newNodeId = generateNodeId(newMetadata.id, allNodes);
|
|
488
424
|
const connectedEdges = edges.filter((e) => e.source === oldNodeId || e.target === oldNodeId);
|
|
@@ -567,7 +503,7 @@ function isPrimitive(value) {
|
|
|
567
503
|
* Convert user-edited InteractiveSwapState back into a SwapPreview
|
|
568
504
|
* for executeSwap(). Pure function, no side effects.
|
|
569
505
|
*/
|
|
570
|
-
export function buildSwapPreviewFromState(state,
|
|
506
|
+
export function buildSwapPreviewFromState(state, _allEdges) {
|
|
571
507
|
const keptEdges = [];
|
|
572
508
|
const droppedEdges = [];
|
|
573
509
|
for (const mapping of state.portMappings) {
|
|
@@ -10,14 +10,16 @@
|
|
|
10
10
|
* Works with both built-in types and custom registered types.
|
|
11
11
|
*/
|
|
12
12
|
import type { NodeType, NodeMetadata } from '../types/index.js';
|
|
13
|
+
import type { NodeComponentRegistry } from '../registry/nodeComponentRegistry.js';
|
|
13
14
|
/**
|
|
14
15
|
* Gets the SvelteFlow component name for a given NodeType.
|
|
15
16
|
* Uses the node component registry to resolve types.
|
|
16
17
|
*
|
|
18
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
17
19
|
* @param nodeType - The node type identifier
|
|
18
20
|
* @returns The component name to use
|
|
19
21
|
*/
|
|
20
|
-
export declare function getComponentNameForNodeType(nodeType: NodeType | string): string;
|
|
22
|
+
export declare function getComponentNameForNodeType(registry: NodeComponentRegistry, nodeType: NodeType | string): string;
|
|
21
23
|
/**
|
|
22
24
|
* Gets the available node types for a given NodeMetadata.
|
|
23
25
|
* Priority: supportedTypes > type > "default"
|
|
@@ -43,28 +45,31 @@ export declare function getPrimaryNodeType(metadata: NodeMetadata): NodeType | s
|
|
|
43
45
|
* 3. First supportedType
|
|
44
46
|
* 4. "default"
|
|
45
47
|
*
|
|
48
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
46
49
|
* @param metadata - The node metadata
|
|
47
50
|
* @param configNodeType - Optional type from user config
|
|
48
51
|
* @returns The resolved node type
|
|
49
52
|
*/
|
|
50
|
-
export declare function resolveNodeType(metadata: NodeMetadata, configNodeType?: string): NodeType | string;
|
|
53
|
+
export declare function resolveNodeType(registry: NodeComponentRegistry, metadata: NodeMetadata, configNodeType?: string): NodeType | string;
|
|
51
54
|
/**
|
|
52
55
|
* Gets the SvelteFlow component name for resolved node type.
|
|
53
56
|
* This is the main function used by UniversalNode to determine which component to render.
|
|
54
57
|
*
|
|
58
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
55
59
|
* @param metadata - The node metadata
|
|
56
60
|
* @param configNodeType - Optional type from user config
|
|
57
61
|
* @returns The component name to use
|
|
58
62
|
*/
|
|
59
|
-
export declare function resolveComponentName(metadata: NodeMetadata, configNodeType?: string): string;
|
|
63
|
+
export declare function resolveComponentName(registry: NodeComponentRegistry, metadata: NodeMetadata, configNodeType?: string): string;
|
|
60
64
|
/**
|
|
61
65
|
* Validates if a node type is supported by the given metadata.
|
|
62
66
|
*
|
|
67
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
63
68
|
* @param metadata - The node metadata
|
|
64
69
|
* @param nodeType - The type to check
|
|
65
70
|
* @returns true if the type is supported
|
|
66
71
|
*/
|
|
67
|
-
export declare function isNodeTypeSupported(metadata: NodeMetadata, nodeType: NodeType | string): boolean;
|
|
72
|
+
export declare function isNodeTypeSupported(registry: NodeComponentRegistry, metadata: NodeMetadata, nodeType: NodeType | string): boolean;
|
|
68
73
|
/**
|
|
69
74
|
* Gets oneOf options for node type configuration.
|
|
70
75
|
* Used in config schemas to show available options with labels.
|
|
@@ -73,11 +78,12 @@ export declare function isNodeTypeSupported(metadata: NodeMetadata, nodeType: No
|
|
|
73
78
|
* - Types specified in metadata.supportedTypes
|
|
74
79
|
* - Registered custom types (optionally filtered)
|
|
75
80
|
*
|
|
81
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
76
82
|
* @param metadata - The node metadata
|
|
77
83
|
* @param includeCustomTypes - Whether to include registered custom types
|
|
78
84
|
* @returns Array of oneOf items with const (type value) and title (display name)
|
|
79
85
|
*/
|
|
80
|
-
export declare function getNodeTypeOneOfOptions(metadata: NodeMetadata, includeCustomTypes?: boolean): Array<{
|
|
86
|
+
export declare function getNodeTypeOneOfOptions(registry: NodeComponentRegistry, metadata: NodeMetadata, includeCustomTypes?: boolean): Array<{
|
|
81
87
|
const: string;
|
|
82
88
|
title: string;
|
|
83
89
|
}>;
|
|
@@ -88,11 +94,12 @@ export declare function getNodeTypeOneOfOptions(metadata: NodeMetadata, includeC
|
|
|
88
94
|
* Uses JSON Schema `oneOf` pattern with `const`/`title` for labeled options,
|
|
89
95
|
* which is the standard approach supported by form components.
|
|
90
96
|
*
|
|
97
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
91
98
|
* @param metadata - The node metadata
|
|
92
99
|
* @param defaultType - Optional default type override
|
|
93
100
|
* @returns Config schema property object with oneOf for labeled options
|
|
94
101
|
*/
|
|
95
|
-
export declare function createNodeTypeConfigProperty(metadata: NodeMetadata, defaultType?: NodeType | string): {
|
|
102
|
+
export declare function createNodeTypeConfigProperty(registry: NodeComponentRegistry, metadata: NodeMetadata, defaultType?: NodeType | string): {
|
|
96
103
|
type: "string";
|
|
97
104
|
title: string;
|
|
98
105
|
description: string;
|
|
@@ -105,13 +112,15 @@ export declare function createNodeTypeConfigProperty(metadata: NodeMetadata, def
|
|
|
105
112
|
/**
|
|
106
113
|
* Check if a type string represents a valid registered or built-in type.
|
|
107
114
|
*
|
|
115
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
108
116
|
* @param type - The type to check
|
|
109
117
|
* @returns true if the type is valid
|
|
110
118
|
*/
|
|
111
|
-
export declare function isValidNodeType(type: string): boolean;
|
|
119
|
+
export declare function isValidNodeType(registry: NodeComponentRegistry, type: string): boolean;
|
|
112
120
|
/**
|
|
113
121
|
* Get all available node types (built-in + registered).
|
|
114
122
|
*
|
|
123
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
115
124
|
* @returns Array of all valid node type identifiers
|
|
116
125
|
*/
|
|
117
|
-
export declare function getAllNodeTypes(): string[];
|
|
126
|
+
export declare function getAllNodeTypes(registry: NodeComponentRegistry): string[];
|
package/dist/utils/nodeTypes.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Works with both built-in types and custom registered types.
|
|
11
11
|
*/
|
|
12
|
-
import { nodeComponentRegistry } from '../registry/nodeComponentRegistry.js';
|
|
13
12
|
import { resolveBuiltinAlias, isBuiltinType } from '../registry/builtinNodes.js';
|
|
14
13
|
/**
|
|
15
14
|
* Display names for built-in node types.
|
|
@@ -18,6 +17,7 @@ const TYPE_DISPLAY_NAMES = {
|
|
|
18
17
|
note: 'Note (sticky note style)',
|
|
19
18
|
simple: 'Simple (compact layout)',
|
|
20
19
|
square: 'Square (geometric layout)',
|
|
20
|
+
atom: 'Atom (minimal value/transform)',
|
|
21
21
|
tool: 'Tool (specialized for agent tools)',
|
|
22
22
|
gateway: 'Gateway (branching control flow)',
|
|
23
23
|
terminal: 'Terminal (start/end/exit)',
|
|
@@ -27,14 +27,15 @@ const TYPE_DISPLAY_NAMES = {
|
|
|
27
27
|
* Gets the SvelteFlow component name for a given NodeType.
|
|
28
28
|
* Uses the node component registry to resolve types.
|
|
29
29
|
*
|
|
30
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
30
31
|
* @param nodeType - The node type identifier
|
|
31
32
|
* @returns The component name to use
|
|
32
33
|
*/
|
|
33
|
-
export function getComponentNameForNodeType(nodeType) {
|
|
34
|
+
export function getComponentNameForNodeType(registry, nodeType) {
|
|
34
35
|
// Resolve aliases first (e.g., "default" -> "workflowNode")
|
|
35
36
|
const resolvedType = resolveBuiltinAlias(nodeType);
|
|
36
37
|
// Check if it's registered in the registry
|
|
37
|
-
if (
|
|
38
|
+
if (registry.has(resolvedType)) {
|
|
38
39
|
return resolvedType;
|
|
39
40
|
}
|
|
40
41
|
// Unknown type - return workflowNode as default
|
|
@@ -76,11 +77,12 @@ export function getPrimaryNodeType(metadata) {
|
|
|
76
77
|
* 3. First supportedType
|
|
77
78
|
* 4. "default"
|
|
78
79
|
*
|
|
80
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
79
81
|
* @param metadata - The node metadata
|
|
80
82
|
* @param configNodeType - Optional type from user config
|
|
81
83
|
* @returns The resolved node type
|
|
82
84
|
*/
|
|
83
|
-
export function resolveNodeType(metadata, configNodeType) {
|
|
85
|
+
export function resolveNodeType(registry, metadata, configNodeType) {
|
|
84
86
|
const availableTypes = getAvailableNodeTypes(metadata);
|
|
85
87
|
// Check if configNodeType is valid for this metadata
|
|
86
88
|
if (configNodeType) {
|
|
@@ -92,7 +94,7 @@ export function resolveNodeType(metadata, configNodeType) {
|
|
|
92
94
|
return configNodeType;
|
|
93
95
|
}
|
|
94
96
|
// Check if it's a registered custom type
|
|
95
|
-
if (
|
|
97
|
+
if (registry.has(configNodeType) || registry.has(resolvedConfig)) {
|
|
96
98
|
return configNodeType;
|
|
97
99
|
}
|
|
98
100
|
}
|
|
@@ -103,22 +105,24 @@ export function resolveNodeType(metadata, configNodeType) {
|
|
|
103
105
|
* Gets the SvelteFlow component name for resolved node type.
|
|
104
106
|
* This is the main function used by UniversalNode to determine which component to render.
|
|
105
107
|
*
|
|
108
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
106
109
|
* @param metadata - The node metadata
|
|
107
110
|
* @param configNodeType - Optional type from user config
|
|
108
111
|
* @returns The component name to use
|
|
109
112
|
*/
|
|
110
|
-
export function resolveComponentName(metadata, configNodeType) {
|
|
111
|
-
const nodeType = resolveNodeType(metadata, configNodeType);
|
|
112
|
-
return getComponentNameForNodeType(nodeType);
|
|
113
|
+
export function resolveComponentName(registry, metadata, configNodeType) {
|
|
114
|
+
const nodeType = resolveNodeType(registry, metadata, configNodeType);
|
|
115
|
+
return getComponentNameForNodeType(registry, nodeType);
|
|
113
116
|
}
|
|
114
117
|
/**
|
|
115
118
|
* Validates if a node type is supported by the given metadata.
|
|
116
119
|
*
|
|
120
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
117
121
|
* @param metadata - The node metadata
|
|
118
122
|
* @param nodeType - The type to check
|
|
119
123
|
* @returns true if the type is supported
|
|
120
124
|
*/
|
|
121
|
-
export function isNodeTypeSupported(metadata, nodeType) {
|
|
125
|
+
export function isNodeTypeSupported(registry, metadata, nodeType) {
|
|
122
126
|
const availableTypes = getAvailableNodeTypes(metadata);
|
|
123
127
|
// Check direct match
|
|
124
128
|
if (availableTypes.includes(nodeType)) {
|
|
@@ -130,7 +134,7 @@ export function isNodeTypeSupported(metadata, nodeType) {
|
|
|
130
134
|
return true;
|
|
131
135
|
}
|
|
132
136
|
// Check if it's a registered custom type that's in the available list
|
|
133
|
-
if (
|
|
137
|
+
if (registry.has(nodeType)) {
|
|
134
138
|
return availableTypes.some((t) => t === nodeType || resolveBuiltinAlias(t) === nodeType);
|
|
135
139
|
}
|
|
136
140
|
return false;
|
|
@@ -143,18 +147,19 @@ export function isNodeTypeSupported(metadata, nodeType) {
|
|
|
143
147
|
* - Types specified in metadata.supportedTypes
|
|
144
148
|
* - Registered custom types (optionally filtered)
|
|
145
149
|
*
|
|
150
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
146
151
|
* @param metadata - The node metadata
|
|
147
152
|
* @param includeCustomTypes - Whether to include registered custom types
|
|
148
153
|
* @returns Array of oneOf items with const (type value) and title (display name)
|
|
149
154
|
*/
|
|
150
|
-
export function getNodeTypeOneOfOptions(metadata, includeCustomTypes = false) {
|
|
155
|
+
export function getNodeTypeOneOfOptions(registry, metadata, includeCustomTypes = false) {
|
|
151
156
|
const availableTypes = getAvailableNodeTypes(metadata);
|
|
152
157
|
const options = [];
|
|
153
158
|
const includedTypes = new Set();
|
|
154
159
|
for (const type of availableTypes) {
|
|
155
160
|
includedTypes.add(type);
|
|
156
161
|
// Get display name from registry or fallback to built-in names
|
|
157
|
-
const registration =
|
|
162
|
+
const registration = registry.get(type);
|
|
158
163
|
let title;
|
|
159
164
|
if (registration) {
|
|
160
165
|
title = registration.displayName;
|
|
@@ -170,7 +175,7 @@ export function getNodeTypeOneOfOptions(metadata, includeCustomTypes = false) {
|
|
|
170
175
|
}
|
|
171
176
|
// Optionally include all registered custom types
|
|
172
177
|
if (includeCustomTypes) {
|
|
173
|
-
const registrations =
|
|
178
|
+
const registrations = registry.filter({
|
|
174
179
|
predicate: (reg) => !isBuiltinType(reg.type) && !includedTypes.has(reg.type)
|
|
175
180
|
});
|
|
176
181
|
for (const reg of registrations) {
|
|
@@ -186,12 +191,13 @@ export function getNodeTypeOneOfOptions(metadata, includeCustomTypes = false) {
|
|
|
186
191
|
* Uses JSON Schema `oneOf` pattern with `const`/`title` for labeled options,
|
|
187
192
|
* which is the standard approach supported by form components.
|
|
188
193
|
*
|
|
194
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
189
195
|
* @param metadata - The node metadata
|
|
190
196
|
* @param defaultType - Optional default type override
|
|
191
197
|
* @returns Config schema property object with oneOf for labeled options
|
|
192
198
|
*/
|
|
193
|
-
export function createNodeTypeConfigProperty(metadata, defaultType) {
|
|
194
|
-
const oneOf = getNodeTypeOneOfOptions(metadata);
|
|
199
|
+
export function createNodeTypeConfigProperty(registry, metadata, defaultType) {
|
|
200
|
+
const oneOf = getNodeTypeOneOfOptions(registry, metadata);
|
|
195
201
|
const primaryType = defaultType ?? getPrimaryNodeType(metadata);
|
|
196
202
|
return {
|
|
197
203
|
type: 'string',
|
|
@@ -204,19 +210,21 @@ export function createNodeTypeConfigProperty(metadata, defaultType) {
|
|
|
204
210
|
/**
|
|
205
211
|
* Check if a type string represents a valid registered or built-in type.
|
|
206
212
|
*
|
|
213
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
207
214
|
* @param type - The type to check
|
|
208
215
|
* @returns true if the type is valid
|
|
209
216
|
*/
|
|
210
|
-
export function isValidNodeType(type) {
|
|
211
|
-
return isBuiltinType(type) ||
|
|
217
|
+
export function isValidNodeType(registry, type) {
|
|
218
|
+
return isBuiltinType(type) || registry.has(type);
|
|
212
219
|
}
|
|
213
220
|
/**
|
|
214
221
|
* Get all available node types (built-in + registered).
|
|
215
222
|
*
|
|
223
|
+
* @param registry - The instance's node component registry (e.g. `fd.nodes`)
|
|
216
224
|
* @returns Array of all valid node type identifiers
|
|
217
225
|
*/
|
|
218
|
-
export function getAllNodeTypes() {
|
|
219
|
-
return
|
|
226
|
+
export function getAllNodeTypes(registry) {
|
|
227
|
+
return registry.getTypes();
|
|
220
228
|
}
|
|
221
229
|
/**
|
|
222
230
|
* Format a type name for display when no display name is registered.
|
|
@@ -51,6 +51,9 @@ export function areEdgeArraysEqual(edges1, edges2) {
|
|
|
51
51
|
* Throttle function execution to reduce frequency
|
|
52
52
|
* Uses requestAnimationFrame for smooth UI updates
|
|
53
53
|
*/
|
|
54
|
+
// `any[]` in a generic constraint is the variance-correct idiom (it's how the TS
|
|
55
|
+
// stdlib types Parameters<T>); `unknown[]` would reject every concrete callback.
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
57
|
export function throttle(func, wait) {
|
|
55
58
|
let timeout = null;
|
|
56
59
|
let lastRan = 0;
|
|
@@ -75,6 +78,8 @@ export function throttle(func, wait) {
|
|
|
75
78
|
* Debounce function execution to reduce frequency
|
|
76
79
|
* Waits for a pause in calls before executing
|
|
77
80
|
*/
|
|
81
|
+
// `any[]` constraint: see throttle() above.
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
83
|
export function debounce(func, wait) {
|
|
79
84
|
let timeout = null;
|
|
80
85
|
return function (...args) {
|
|
@@ -90,6 +95,8 @@ export function debounce(func, wait) {
|
|
|
90
95
|
* RequestAnimationFrame-based throttle for smooth animations
|
|
91
96
|
* Better for visual updates like node dragging
|
|
92
97
|
*/
|
|
98
|
+
// `any[]` constraint: see throttle() above.
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
100
|
export function rafThrottle(func) {
|
|
94
101
|
let rafId = null;
|
|
95
102
|
let lastArgs = null;
|