@flowdrop/flowdrop 2.0.0-beta.4 → 2.0.0-beta.5
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 +13 -0
- package/dist/adapters/WorkflowAdapter.js +4 -5
- package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -3
- package/dist/adapters/agentspec/defaultNodeTypes.js +9 -9
- package/dist/commands/executor.js +5 -6
- package/dist/commands/types.js +5 -5
- package/dist/components/App.svelte +4 -4
- package/dist/components/NodeSidebar.svelte +4 -4
- package/dist/components/NodeSwapPicker.svelte +2 -2
- package/dist/components/UniversalNode.svelte +5 -4
- package/dist/components/UniversalNode.svelte.d.ts +1 -1
- package/dist/components/console/ConsoleInput.svelte +3 -3
- package/dist/components/nodes/AtomNode.svelte +3 -3
- package/dist/components/nodes/AtomNode.svelte.d.ts +1 -1
- package/dist/components/nodes/GatewayNode.svelte +8 -11
- package/dist/components/nodes/GatewayNode.svelte.d.ts +1 -1
- package/dist/components/nodes/IdeaNode.svelte +10 -8
- package/dist/components/nodes/IdeaNode.svelte.d.ts +8 -4
- package/dist/components/nodes/NotesNode.svelte +6 -4
- package/dist/components/nodes/NotesNode.svelte.d.ts +8 -4
- package/dist/components/nodes/SimpleNode.svelte +10 -8
- package/dist/components/nodes/SimpleNode.svelte.d.ts +4 -4
- package/dist/components/nodes/SquareNode.svelte +10 -8
- package/dist/components/nodes/SquareNode.svelte.d.ts +4 -4
- package/dist/components/nodes/TerminalNode.svelte +10 -8
- package/dist/components/nodes/TerminalNode.svelte.d.ts +4 -4
- package/dist/components/nodes/ToolNode.svelte +10 -8
- package/dist/components/nodes/ToolNode.svelte.d.ts +6 -6
- package/dist/components/nodes/WorkflowNode.svelte +7 -10
- package/dist/components/nodes/WorkflowNode.svelte.d.ts +1 -1
- package/dist/components/playground/PipelineKanbanView.svelte +2 -2
- package/dist/components/playground/PipelineTableView.svelte +2 -2
- package/dist/helpers/workflowEditorHelper.js +4 -5
- package/dist/registry/nodeComponentRegistry.d.ts +2 -1
- package/dist/services/dynamicSchemaService.d.ts +2 -2
- package/dist/services/dynamicSchemaService.js +8 -8
- package/dist/stores/playgroundStore.svelte.js +1 -1
- package/dist/stores/workflowStore.svelte.js +0 -18
- package/dist/types/index.d.ts +6 -7
- package/dist/utils/connections.js +6 -6
- package/dist/utils/nodeIds.d.ts +1 -1
- package/dist/utils/nodeIds.js +1 -1
- package/dist/utils/nodeSwap.js +3 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0-beta.5] - 2026-06-20
|
|
11
|
+
|
|
12
|
+
Fifth 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. This release is a node-identity cleanup: node components now derive their instance id from the canonical `id` prop SvelteFlow already passes, dropping the duplicate `data.nodeId` copy and its load-time healing, and the node-_type_ entity id is renamed `metadata.id` → `metadata.node_type_id` to disambiguate it from the instance id. Six node components were also moved off the type-erasing `$props<T>()` onto a typed `interface Props`, which surfaced and fixed latent config-access errors in `ToolNode`.
|
|
13
|
+
|
|
14
|
+
### Breaking Changes
|
|
15
|
+
|
|
16
|
+
- **Node-type entity id renamed `metadata.id` → `metadata.node_type_id`.** The node-type entity id collided conceptually with the node _instance_ id; it is now `metadata.node_type_id` to disambiguate the two. It is a server-owned wire field, so the library preserves the snake_case name as a non-opinionated passthrough — adapters, the command type-map, connection validation, and the dynamic-schema paths were all updated to match. Consumers reading `metadata.id` for the node type must read `metadata.node_type_id`.
|
|
17
|
+
- **Dropped the duplicate `data.nodeId` field.** Node components now derive their id from the canonical `id` prop that SvelteFlow already passes to every node, instead of a copy stored in `data.nodeId`. Handle ids, config-open callbacks, and status overlays read `id` directly. The field and its load-time "healing" are gone, so an intact graph can no longer look corrupted from a missing copy.
|
|
18
|
+
|
|
19
|
+
### Changed (internal — no API change)
|
|
20
|
+
|
|
21
|
+
- **Six node components typed properly.** The node components that used `$props<T>()` — which returns `any` and silently disabled type-checking — now declare an `interface Props` with `let props: Props = $props()`. This surfaced and fixed latent config-access errors in `ToolNode`.
|
|
22
|
+
|
|
10
23
|
## [2.0.0-beta.4] - 2026-06-13
|
|
11
24
|
|
|
12
25
|
Fourth 2.0 beta, published under the npm `beta` dist-tag (`npm install @flowdrop/flowdrop@beta`). `latest` remains 1.15.0 until 2.0.0 GA. This release is mostly an internal design-system consolidation pass — new shared `Button` / `IconButton` / `Input` / `Select` / `Textarea` primitives route every control through the `base.css` class system as the single source of truth, so themes (including Drafter) style fields and buttons consistently. User-visible changes are a navbar `end` snippet for embedding custom trailing controls, a new `--fd-control-radius` theming token, a range-slider fill fix, and opaque Drafter buttons. The archived Astro docs site is retired in favor of the Mintlify docs at `flowdrop.mintlify.app`.
|
|
@@ -46,7 +46,7 @@ export class WorkflowAdapter {
|
|
|
46
46
|
* Add a node to a workflow
|
|
47
47
|
*/
|
|
48
48
|
addNode(workflow, nodeType, position, config) {
|
|
49
|
-
const metadata = this.nodeTypes.find((nt) => nt.
|
|
49
|
+
const metadata = this.nodeTypes.find((nt) => nt.node_type_id === nodeType);
|
|
50
50
|
if (!metadata) {
|
|
51
51
|
throw new Error(`Node type '${nodeType}' not found`);
|
|
52
52
|
}
|
|
@@ -248,7 +248,7 @@ export class WorkflowAdapter {
|
|
|
248
248
|
description: svelteFlowWorkflow.description,
|
|
249
249
|
nodes: svelteFlowWorkflow.nodes.map((node) => ({
|
|
250
250
|
id: node.id,
|
|
251
|
-
type: node.data.metadata.
|
|
251
|
+
type: node.data.metadata.node_type_id,
|
|
252
252
|
position: node.position,
|
|
253
253
|
data: {
|
|
254
254
|
label: node.data.label,
|
|
@@ -282,8 +282,7 @@ export class WorkflowAdapter {
|
|
|
282
282
|
data: {
|
|
283
283
|
label: node.data.label,
|
|
284
284
|
config: node.data.config,
|
|
285
|
-
metadata: node.data.metadata
|
|
286
|
-
nodeId: node.id
|
|
285
|
+
metadata: node.data.metadata
|
|
287
286
|
}
|
|
288
287
|
})),
|
|
289
288
|
edges: workflow.edges.map((edge) => ({
|
|
@@ -326,7 +325,7 @@ export class WorkflowAdapter {
|
|
|
326
325
|
const nodeTypeCounts = new Map();
|
|
327
326
|
cloned.nodes.forEach((node) => {
|
|
328
327
|
const oldId = node.id;
|
|
329
|
-
const nodeTypeId = node.data.metadata.
|
|
328
|
+
const nodeTypeId = node.data.metadata.node_type_id;
|
|
330
329
|
// Get the current count for this node type
|
|
331
330
|
const currentCount = nodeTypeCounts.get(nodeTypeId) || 0;
|
|
332
331
|
const newCount = currentCount + 1;
|
|
@@ -266,7 +266,7 @@ export class AgentSpecAdapter {
|
|
|
266
266
|
metadata: {
|
|
267
267
|
'flowdrop:position': node.position,
|
|
268
268
|
'flowdrop:node_id': node.id,
|
|
269
|
-
'flowdrop:node_type_id': node.data.metadata.
|
|
269
|
+
'flowdrop:node_type_id': node.data.metadata.node_type_id
|
|
270
270
|
}
|
|
271
271
|
};
|
|
272
272
|
// Add type-specific attributes from config
|
|
@@ -362,7 +362,7 @@ export class AgentSpecAdapter {
|
|
|
362
362
|
return ext;
|
|
363
363
|
}
|
|
364
364
|
// Infer from FlowDrop node type ID
|
|
365
|
-
const fromId = extractComponentType(node.data.metadata.
|
|
365
|
+
const fromId = extractComponentType(node.data.metadata.node_type_id);
|
|
366
366
|
if (fromId)
|
|
367
367
|
return fromId;
|
|
368
368
|
// Infer from FlowDrop visual type + category
|
|
@@ -404,7 +404,7 @@ export class AgentSpecAdapter {
|
|
|
404
404
|
const nodeTypeId = asNode.metadata?.['flowdrop:node_type_id'] ||
|
|
405
405
|
`${AGENTSPEC_NAMESPACE}.${asNode.component_type}`;
|
|
406
406
|
const metadata = {
|
|
407
|
-
|
|
407
|
+
node_type_id: nodeTypeId,
|
|
408
408
|
name: defaults.defaultName,
|
|
409
409
|
type: defaults.visualType,
|
|
410
410
|
description: asNode.description || defaults.defaultDescription,
|
|
@@ -30,7 +30,7 @@ function buildRegistry() {
|
|
|
30
30
|
registry.set('start_node', {
|
|
31
31
|
componentType: 'start_node',
|
|
32
32
|
metadata: {
|
|
33
|
-
|
|
33
|
+
node_type_id: `${AGENTSPEC_NS}.start_node`,
|
|
34
34
|
name: 'Start',
|
|
35
35
|
type: 'terminal',
|
|
36
36
|
description: 'Flow entry point. Defines the initial inputs for the flow.',
|
|
@@ -55,7 +55,7 @@ function buildRegistry() {
|
|
|
55
55
|
registry.set('end_node', {
|
|
56
56
|
componentType: 'end_node',
|
|
57
57
|
metadata: {
|
|
58
|
-
|
|
58
|
+
node_type_id: `${AGENTSPEC_NS}.end_node`,
|
|
59
59
|
name: 'End',
|
|
60
60
|
type: 'terminal',
|
|
61
61
|
description: 'Flow exit point. Defines the final outputs of the flow.',
|
|
@@ -80,7 +80,7 @@ function buildRegistry() {
|
|
|
80
80
|
registry.set('llm_node', {
|
|
81
81
|
componentType: 'llm_node',
|
|
82
82
|
metadata: {
|
|
83
|
-
|
|
83
|
+
node_type_id: `${AGENTSPEC_NS}.llm_node`,
|
|
84
84
|
name: 'LLM',
|
|
85
85
|
type: 'default',
|
|
86
86
|
description: 'Generate text using a large language model with configurable prompts.',
|
|
@@ -145,7 +145,7 @@ function buildRegistry() {
|
|
|
145
145
|
registry.set('branching_node', {
|
|
146
146
|
componentType: 'branching_node',
|
|
147
147
|
metadata: {
|
|
148
|
-
|
|
148
|
+
node_type_id: `${AGENTSPEC_NS}.branching_node`,
|
|
149
149
|
name: 'Branch',
|
|
150
150
|
type: 'gateway',
|
|
151
151
|
description: 'Route execution to different paths based on conditions.',
|
|
@@ -213,7 +213,7 @@ function buildRegistry() {
|
|
|
213
213
|
registry.set('tool_node', {
|
|
214
214
|
componentType: 'tool_node',
|
|
215
215
|
metadata: {
|
|
216
|
-
|
|
216
|
+
node_type_id: `${AGENTSPEC_NS}.tool_node`,
|
|
217
217
|
name: 'Tool',
|
|
218
218
|
type: 'tool',
|
|
219
219
|
description: 'Execute a tool function with inputs and receive outputs.',
|
|
@@ -271,7 +271,7 @@ function buildRegistry() {
|
|
|
271
271
|
registry.set('api_node', {
|
|
272
272
|
componentType: 'api_node',
|
|
273
273
|
metadata: {
|
|
274
|
-
|
|
274
|
+
node_type_id: `${AGENTSPEC_NS}.api_node`,
|
|
275
275
|
name: 'API Call',
|
|
276
276
|
type: 'default',
|
|
277
277
|
description: 'Make an HTTP API call with configurable endpoint, method, and headers.',
|
|
@@ -342,7 +342,7 @@ function buildRegistry() {
|
|
|
342
342
|
registry.set('agent_node', {
|
|
343
343
|
componentType: 'agent_node',
|
|
344
344
|
metadata: {
|
|
345
|
-
|
|
345
|
+
node_type_id: `${AGENTSPEC_NS}.agent_node`,
|
|
346
346
|
name: 'Agent',
|
|
347
347
|
type: 'default',
|
|
348
348
|
description: 'Run a multi-round agent conversation within the flow.',
|
|
@@ -400,7 +400,7 @@ function buildRegistry() {
|
|
|
400
400
|
registry.set('flow_node', {
|
|
401
401
|
componentType: 'flow_node',
|
|
402
402
|
metadata: {
|
|
403
|
-
|
|
403
|
+
node_type_id: `${AGENTSPEC_NS}.flow_node`,
|
|
404
404
|
name: 'Sub-Flow',
|
|
405
405
|
type: 'simple',
|
|
406
406
|
description: 'Execute another flow as a sub-routine within this flow.',
|
|
@@ -451,7 +451,7 @@ function buildRegistry() {
|
|
|
451
451
|
registry.set('map_node', {
|
|
452
452
|
componentType: 'map_node',
|
|
453
453
|
metadata: {
|
|
454
|
-
|
|
454
|
+
node_type_id: `${AGENTSPEC_NS}.map_node`,
|
|
455
455
|
name: 'Map',
|
|
456
456
|
type: 'default',
|
|
457
457
|
description: 'Apply a flow or operation to each item in a collection (map-reduce).',
|
|
@@ -76,7 +76,7 @@ function executeAddNode(command, context) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
const position = command.position ?? computeAutoPosition(workflow.nodes);
|
|
79
|
-
const nodeId = generateNodeId(metadata.
|
|
79
|
+
const nodeId = generateNodeId(metadata.node_type_id, workflow.nodes);
|
|
80
80
|
const config = extractConfigDefaults(metadata.configSchema);
|
|
81
81
|
const node = {
|
|
82
82
|
id: nodeId,
|
|
@@ -86,8 +86,7 @@ function executeAddNode(command, context) {
|
|
|
86
86
|
data: {
|
|
87
87
|
label: metadata.name,
|
|
88
88
|
config,
|
|
89
|
-
metadata
|
|
90
|
-
nodeId
|
|
89
|
+
metadata
|
|
91
90
|
}
|
|
92
91
|
};
|
|
93
92
|
context.dispatch.addNode(node);
|
|
@@ -357,7 +356,7 @@ function executeInfo(command, context) {
|
|
|
357
356
|
const resultData = {
|
|
358
357
|
nodeId: shortId,
|
|
359
358
|
label: node.data.label ?? metadata?.name ?? '',
|
|
360
|
-
type: metadata?.
|
|
359
|
+
type: metadata?.node_type_id ? toShortTypeId(metadata.node_type_id) : '',
|
|
361
360
|
position: node.position,
|
|
362
361
|
config: node.data.config ?? {},
|
|
363
362
|
inputs,
|
|
@@ -578,7 +577,7 @@ function executeListNodes(context) {
|
|
|
578
577
|
const nodes = workflow.nodes.map((n) => ({
|
|
579
578
|
nodeId: toShortId(n.id),
|
|
580
579
|
label: n.data.label ?? n.data.metadata?.name ?? '',
|
|
581
|
-
type: n.data.metadata?.
|
|
580
|
+
type: n.data.metadata?.node_type_id ? toShortTypeId(n.data.metadata.node_type_id) : ''
|
|
582
581
|
}));
|
|
583
582
|
const resultData = { nodes };
|
|
584
583
|
return {
|
|
@@ -610,7 +609,7 @@ function executeListEdges(context) {
|
|
|
610
609
|
}
|
|
611
610
|
function executeListTypes(context) {
|
|
612
611
|
const types = context.nodeTypes.map((m) => ({
|
|
613
|
-
typeId: toShortTypeId(m.
|
|
612
|
+
typeId: toShortTypeId(m.node_type_id),
|
|
614
613
|
name: m.name,
|
|
615
614
|
category: m.category
|
|
616
615
|
}));
|
package/dist/commands/types.js
CHANGED
|
@@ -24,18 +24,18 @@ export function buildTypeMap(nodeTypes) {
|
|
|
24
24
|
const shortIdCounts = new Map();
|
|
25
25
|
// First pass: register full IDs and count short ID occurrences
|
|
26
26
|
for (const metadata of nodeTypes) {
|
|
27
|
-
map.set(metadata.
|
|
28
|
-
const dotIndex = metadata.
|
|
27
|
+
map.set(metadata.node_type_id, metadata);
|
|
28
|
+
const dotIndex = metadata.node_type_id.indexOf('.');
|
|
29
29
|
if (dotIndex !== -1) {
|
|
30
|
-
const shortId = metadata.
|
|
30
|
+
const shortId = metadata.node_type_id.substring(dotIndex + 1);
|
|
31
31
|
shortIdCounts.set(shortId, (shortIdCounts.get(shortId) ?? 0) + 1);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
// Second pass: register unique short IDs
|
|
35
35
|
for (const metadata of nodeTypes) {
|
|
36
|
-
const dotIndex = metadata.
|
|
36
|
+
const dotIndex = metadata.node_type_id.indexOf('.');
|
|
37
37
|
if (dotIndex !== -1) {
|
|
38
|
-
const shortId = metadata.
|
|
38
|
+
const shortId = metadata.node_type_id.substring(dotIndex + 1);
|
|
39
39
|
if (shortIdCounts.get(shortId) === 1) {
|
|
40
40
|
map.set(shortId, metadata);
|
|
41
41
|
}
|
|
@@ -412,8 +412,8 @@
|
|
|
412
412
|
if (propNodes && propNodes.length > 0) {
|
|
413
413
|
// Merge format-provided nodes with prop nodes (deduplicate by ID, props take priority)
|
|
414
414
|
const formatNodes = fd.formats.getAllFormatNodes();
|
|
415
|
-
const existingIds = new Set(propNodes.map((n) => n.
|
|
416
|
-
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.
|
|
415
|
+
const existingIds = new Set(propNodes.map((n) => n.node_type_id));
|
|
416
|
+
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.node_type_id));
|
|
417
417
|
nodes = [...propNodes, ...uniqueFormatNodes];
|
|
418
418
|
nodeTypesLoading = false;
|
|
419
419
|
return;
|
|
@@ -429,8 +429,8 @@
|
|
|
429
429
|
|
|
430
430
|
// Merge format-provided nodes with API nodes (deduplicate by ID, API takes priority)
|
|
431
431
|
const formatNodes = fd.formats.getAllFormatNodes();
|
|
432
|
-
const existingIds = new Set(fetchedNodes.map((n) => n.
|
|
433
|
-
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.
|
|
432
|
+
const existingIds = new Set(fetchedNodes.map((n) => n.node_type_id));
|
|
433
|
+
const uniqueFormatNodes = formatNodes.filter((n) => !existingIds.has(n.node_type_id));
|
|
434
434
|
nodes = [...fetchedNodes, ...uniqueFormatNodes];
|
|
435
435
|
error = null;
|
|
436
436
|
nodeTypesLoading = false;
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
// Create a new node instance from the node type
|
|
122
122
|
const newNodeData = {
|
|
123
123
|
type: 'node',
|
|
124
|
-
nodeType: nodeType.
|
|
124
|
+
nodeType: nodeType.node_type_id,
|
|
125
125
|
nodeData: {
|
|
126
126
|
label: nodeType.name,
|
|
127
127
|
config: extractConfigDefaults(nodeType.configSchema),
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
</div>
|
|
247
247
|
{:else}
|
|
248
248
|
<div class="flowdrop-node-list">
|
|
249
|
-
{#each filteredNodes as nodeType (nodeType.
|
|
249
|
+
{#each filteredNodes as nodeType (nodeType.node_type_id)}
|
|
250
250
|
<div
|
|
251
251
|
class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
|
|
252
252
|
draggable="true"
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
{getCategoryDisplayName(category).toUpperCase()}
|
|
300
300
|
</div>
|
|
301
301
|
<div class="fd-sidebar-flat-list" role="list">
|
|
302
|
-
{#each categoryNodes as nodeType (nodeType.
|
|
302
|
+
{#each categoryNodes as nodeType (nodeType.node_type_id)}
|
|
303
303
|
<div
|
|
304
304
|
class="fd-sidebar-flat-item"
|
|
305
305
|
role="listitem"
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
</summary>
|
|
340
340
|
<div class="flowdrop-details__content">
|
|
341
341
|
<div class="flowdrop-node-list" role="list">
|
|
342
|
-
{#each categoryNodes as nodeType (nodeType.
|
|
342
|
+
{#each categoryNodes as nodeType (nodeType.node_type_id)}
|
|
343
343
|
<div
|
|
344
344
|
class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
|
|
345
345
|
role="listitem"
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
{fd.categories.getLabel(category).toUpperCase()}
|
|
146
146
|
</div>
|
|
147
147
|
<div class="swap-picker__flat-list">
|
|
148
|
-
{#each categoryNodes as nodeType (nodeType.
|
|
148
|
+
{#each categoryNodes as nodeType (nodeType.node_type_id)}
|
|
149
149
|
<button class="swap-picker__flat-item" onclick={() => onSelect(nodeType)}>
|
|
150
150
|
<span
|
|
151
151
|
class="swap-picker__flat-dot"
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
</span>
|
|
175
175
|
</div>
|
|
176
176
|
<div class="swap-picker__category-items">
|
|
177
|
-
{#each categoryNodes as nodeType (nodeType.
|
|
177
|
+
{#each categoryNodes as nodeType (nodeType.node_type_id)}
|
|
178
178
|
<button class="swap-picker__item" onclick={() => onSelect(nodeType)}>
|
|
179
179
|
<span
|
|
180
180
|
class="swap-picker__item-icon"
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
let universalNodeEl: HTMLDivElement;
|
|
22
22
|
|
|
23
23
|
let {
|
|
24
|
+
id,
|
|
24
25
|
data,
|
|
25
26
|
selected = false
|
|
26
27
|
}: {
|
|
28
|
+
id: string;
|
|
27
29
|
data: WorkflowNode['data'] & {
|
|
28
|
-
nodeId?: string;
|
|
29
30
|
onConfigOpen?: (node: { id: string; type: string; data: WorkflowNode['data'] }) => void;
|
|
30
31
|
};
|
|
31
32
|
selected?: boolean;
|
|
@@ -81,7 +82,7 @@
|
|
|
81
82
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
82
83
|
event.preventDefault();
|
|
83
84
|
data.onConfigOpen?.({
|
|
84
|
-
id
|
|
85
|
+
id,
|
|
85
86
|
type: resolvedComponentName,
|
|
86
87
|
data
|
|
87
88
|
});
|
|
@@ -165,13 +166,13 @@
|
|
|
165
166
|
{#if nodeComponent}
|
|
166
167
|
<!-- Svelte 5 dynamic component limitation; reactivity maintained via $derived -->
|
|
167
168
|
{@const NodeComponent = nodeComponent}
|
|
168
|
-
<NodeComponent {data} {selected} />
|
|
169
|
+
<NodeComponent {id} {data} {selected} />
|
|
169
170
|
{/if}
|
|
170
171
|
|
|
171
172
|
<!-- Status overlay - only show if there's meaningful status information -->
|
|
172
173
|
{#if shouldShowStatus}
|
|
173
174
|
<NodeStatusOverlay
|
|
174
|
-
nodeId={
|
|
175
|
+
nodeId={id}
|
|
175
176
|
{executionInfo}
|
|
176
177
|
position={getStatusPosition()}
|
|
177
178
|
size={getStatusSize()}
|
|
@@ -341,10 +341,10 @@
|
|
|
341
341
|
if (nodeTypeContext !== null) {
|
|
342
342
|
const prefix = nodeTypeContext.toLowerCase();
|
|
343
343
|
return nodeTypes
|
|
344
|
-
.filter((nt) => nt.
|
|
344
|
+
.filter((nt) => nt.node_type_id.toLowerCase().startsWith(prefix))
|
|
345
345
|
.map((nt) => ({
|
|
346
|
-
value: nt.
|
|
347
|
-
label: nt.
|
|
346
|
+
value: nt.node_type_id,
|
|
347
|
+
label: nt.node_type_id,
|
|
348
348
|
detail: `${nt.name} (${nt.category})`
|
|
349
349
|
}))
|
|
350
350
|
.slice(0, 50);
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
label: string;
|
|
29
29
|
config: ConfigValues;
|
|
30
30
|
metadata: NodeMetadata;
|
|
31
|
-
nodeId?: string;
|
|
32
31
|
extensions?: NodeExtensions;
|
|
33
32
|
onConfigOpen?: (node: {
|
|
34
33
|
id: string;
|
|
@@ -38,18 +37,19 @@
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
interface Props {
|
|
40
|
+
id: string;
|
|
41
41
|
data: AtomNodeData;
|
|
42
42
|
selected?: boolean;
|
|
43
43
|
isProcessing?: boolean;
|
|
44
44
|
isError?: boolean;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
let { data, selected, isProcessing, isError }: Props = $props();
|
|
47
|
+
let { id, data, selected, isProcessing, isError }: Props = $props();
|
|
48
48
|
|
|
49
49
|
const fd = getInstance();
|
|
50
50
|
const checker = fd.portCompatibility;
|
|
51
51
|
|
|
52
|
-
const nodeId = $derived(
|
|
52
|
+
const nodeId = $derived(id);
|
|
53
53
|
const nodeType = $derived(data.metadata?.type ?? 'atom');
|
|
54
54
|
|
|
55
55
|
// Instance extensions override node-type defaults.
|
|
@@ -3,7 +3,6 @@ interface AtomNodeData {
|
|
|
3
3
|
label: string;
|
|
4
4
|
config: ConfigValues;
|
|
5
5
|
metadata: NodeMetadata;
|
|
6
|
-
nodeId?: string;
|
|
7
6
|
extensions?: NodeExtensions;
|
|
8
7
|
onConfigOpen?: (node: {
|
|
9
8
|
id: string;
|
|
@@ -16,6 +15,7 @@ interface AtomNodeData {
|
|
|
16
15
|
}) => void;
|
|
17
16
|
}
|
|
18
17
|
interface Props {
|
|
18
|
+
id: string;
|
|
19
19
|
data: AtomNodeData;
|
|
20
20
|
selected?: boolean;
|
|
21
21
|
isProcessing?: boolean;
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
import { m } from '../../messages/index.js';
|
|
24
24
|
|
|
25
25
|
interface Props {
|
|
26
|
+
id: string;
|
|
26
27
|
data: WorkflowNode['data'] & {
|
|
27
|
-
nodeId?: string;
|
|
28
28
|
onConfigOpen?: (node: { id: string; type: string; data: WorkflowNode['data'] }) => void;
|
|
29
29
|
};
|
|
30
30
|
selected?: boolean;
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Check if port is connected
|
|
86
|
-
const handleId = `${props.
|
|
86
|
+
const handleId = `${props.id}-${type}-${port.id}`;
|
|
87
87
|
return fd.workflow.connectedHandles.has(handleId);
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// Check if branch output is connected
|
|
109
|
-
const handleId = `${props.
|
|
109
|
+
const handleId = `${props.id}-output-${branchName}`;
|
|
110
110
|
return fd.workflow.connectedHandles.has(handleId);
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
function handleNodeDoubleClick(): void {
|
|
128
128
|
if (props.data.onConfigOpen) {
|
|
129
129
|
props.data.onConfigOpen({
|
|
130
|
-
id: props.
|
|
130
|
+
id: props.id,
|
|
131
131
|
type: 'gateway',
|
|
132
132
|
data: props.data
|
|
133
133
|
});
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
class:flowdrop-workflow-node--selected={props.selected}
|
|
152
152
|
ondblclick={handleNodeDoubleClick}
|
|
153
153
|
aria-label={graph.gatewayNode({ title: displayTitle })}
|
|
154
|
-
aria-describedby="node-description-{props.
|
|
154
|
+
aria-describedby="node-description-{props.id}"
|
|
155
155
|
>
|
|
156
156
|
<!-- Node Header: expands in multiples of 10 (title row 40px + gap 10px + description 20px per line) -->
|
|
157
157
|
<div class="flowdrop-workflow-node__header">
|
|
@@ -173,10 +173,7 @@
|
|
|
173
173
|
</h3>
|
|
174
174
|
</div>
|
|
175
175
|
<!-- Node Description - line-height 20px so header grows in steps of 10 -->
|
|
176
|
-
<p
|
|
177
|
-
class="flowdrop-workflow-node__header-desc"
|
|
178
|
-
id="node-description-{props.data.nodeId || 'unknown'}"
|
|
179
|
-
>
|
|
176
|
+
<p class="flowdrop-workflow-node__header-desc" id="node-description-{props.id}">
|
|
180
177
|
{displayDescription}
|
|
181
178
|
</p>
|
|
182
179
|
</div>
|
|
@@ -191,7 +188,7 @@
|
|
|
191
188
|
<Handle
|
|
192
189
|
type="target"
|
|
193
190
|
position={Position.Left}
|
|
194
|
-
id={`${props.
|
|
191
|
+
id={`${props.id}-input-${port.id}`}
|
|
195
192
|
class="flowdrop-workflow-node__handle"
|
|
196
193
|
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: {getDataTypeColorToken(
|
|
197
194
|
checker,
|
|
@@ -280,7 +277,7 @@
|
|
|
280
277
|
<Handle
|
|
281
278
|
type="source"
|
|
282
279
|
position={Position.Right}
|
|
283
|
-
id={`${props.
|
|
280
|
+
id={`${props.id}-output-${branch.name}`}
|
|
284
281
|
class={`flowdrop-workflow-node__handle ${isActive ? 'flowdrop-workflow-node__handle--active' : ''}`}
|
|
285
282
|
style="top: var(--fd-node-port-row-height); transform: translateY(-50%); --fd-handle-fill: {isActive
|
|
286
283
|
? getDataTypeColorToken(checker, 'trigger')
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
* IdeaNode component props
|
|
20
20
|
* Displays a card-style node for conceptual flow diagrams
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
interface Props {
|
|
23
|
+
id: string;
|
|
23
24
|
data: {
|
|
24
25
|
label: string;
|
|
25
26
|
config: ConfigValues;
|
|
26
27
|
metadata: NodeMetadata;
|
|
27
|
-
nodeId?: string;
|
|
28
28
|
onConfigOpen?: (node: {
|
|
29
29
|
id: string;
|
|
30
30
|
type: string;
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
selected?: boolean;
|
|
35
35
|
isProcessing?: boolean;
|
|
36
36
|
isError?: boolean;
|
|
37
|
-
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let props: Props = $props();
|
|
38
40
|
|
|
39
41
|
const checker = getInstance().portCompatibility;
|
|
40
42
|
|
|
@@ -101,7 +103,7 @@
|
|
|
101
103
|
function openConfigSidebar(): void {
|
|
102
104
|
if (props.data.onConfigOpen) {
|
|
103
105
|
const nodeForConfig = {
|
|
104
|
-
id: props.
|
|
106
|
+
id: props.id,
|
|
105
107
|
type: 'idea',
|
|
106
108
|
data: props.data
|
|
107
109
|
};
|
|
@@ -139,7 +141,7 @@
|
|
|
139
141
|
checker,
|
|
140
142
|
IDEA_DATA_TYPE
|
|
141
143
|
)}; --fd-handle-border-color: var(--fd-handle-border); top: 40px; transform: translateY(-50%); z-index: 30;"
|
|
142
|
-
id={`${props.
|
|
144
|
+
id={`${props.id}-input-left`}
|
|
143
145
|
/>
|
|
144
146
|
{/if}
|
|
145
147
|
|
|
@@ -152,7 +154,7 @@
|
|
|
152
154
|
checker,
|
|
153
155
|
IDEA_DATA_TYPE
|
|
154
156
|
)}; --fd-handle-border-color: var(--fd-handle-border); left: 140px; transform: translateX(-50%); z-index: 30;"
|
|
155
|
-
id={`${props.
|
|
157
|
+
id={`${props.id}-input-top`}
|
|
156
158
|
/>
|
|
157
159
|
{/if}
|
|
158
160
|
|
|
@@ -203,7 +205,7 @@
|
|
|
203
205
|
checker,
|
|
204
206
|
IDEA_DATA_TYPE
|
|
205
207
|
)}; --fd-handle-border-color: var(--fd-handle-border); top: 40px; transform: translateY(-50%); z-index: 30;"
|
|
206
|
-
id={`${props.
|
|
208
|
+
id={`${props.id}-output-right`}
|
|
207
209
|
/>
|
|
208
210
|
{/if}
|
|
209
211
|
|
|
@@ -216,7 +218,7 @@
|
|
|
216
218
|
checker,
|
|
217
219
|
IDEA_DATA_TYPE
|
|
218
220
|
)}; --fd-handle-border-color: var(--fd-handle-border); left: 140px; transform: translateX(-50%); z-index: 30;"
|
|
219
|
-
id={`${props.
|
|
221
|
+
id={`${props.id}-output-bottom`}
|
|
220
222
|
/>
|
|
221
223
|
{/if}
|
|
222
224
|
</div>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* IdeaNode component props
|
|
4
|
+
* Displays a card-style node for conceptual flow diagrams
|
|
5
|
+
*/
|
|
6
|
+
interface Props {
|
|
7
|
+
id: string;
|
|
3
8
|
data: {
|
|
4
9
|
label: string;
|
|
5
10
|
config: ConfigValues;
|
|
6
11
|
metadata: NodeMetadata;
|
|
7
|
-
nodeId?: string;
|
|
8
12
|
onConfigOpen?: (node: {
|
|
9
13
|
id: string;
|
|
10
14
|
type: string;
|
|
@@ -18,7 +22,7 @@ type $$ComponentProps = {
|
|
|
18
22
|
selected?: boolean;
|
|
19
23
|
isProcessing?: boolean;
|
|
20
24
|
isError?: boolean;
|
|
21
|
-
}
|
|
22
|
-
declare const IdeaNode: import("svelte").Component
|
|
25
|
+
}
|
|
26
|
+
declare const IdeaNode: import("svelte").Component<Props, {}, "">;
|
|
23
27
|
type IdeaNode = ReturnType<typeof IdeaNode>;
|
|
24
28
|
export default IdeaNode;
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
* NotesNode component props
|
|
10
10
|
* Displays a styled note with markdown content
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
interface Props {
|
|
13
|
+
id: string;
|
|
13
14
|
data: {
|
|
14
15
|
label: string;
|
|
15
16
|
config: ConfigValues;
|
|
16
17
|
metadata: NodeMetadata;
|
|
17
|
-
nodeId?: string;
|
|
18
18
|
onConfigOpen?: (node: {
|
|
19
19
|
id: string;
|
|
20
20
|
type: string;
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
selected?: boolean;
|
|
25
25
|
isProcessing?: boolean;
|
|
26
26
|
isError?: boolean;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let props: Props = $props();
|
|
28
30
|
|
|
29
31
|
// Hoist the notes branch — read for placeholder, every type name, processing,
|
|
30
32
|
// error, and configure tooltip.
|
|
@@ -75,7 +77,7 @@
|
|
|
75
77
|
function openConfigSidebar(): void {
|
|
76
78
|
if (props.data.onConfigOpen) {
|
|
77
79
|
const nodeForConfig = {
|
|
78
|
-
id: props.
|
|
80
|
+
id: props.id,
|
|
79
81
|
type: 'note',
|
|
80
82
|
data: props.data
|
|
81
83
|
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* NotesNode component props
|
|
4
|
+
* Displays a styled note with markdown content
|
|
5
|
+
*/
|
|
6
|
+
interface Props {
|
|
7
|
+
id: string;
|
|
3
8
|
data: {
|
|
4
9
|
label: string;
|
|
5
10
|
config: ConfigValues;
|
|
6
11
|
metadata: NodeMetadata;
|
|
7
|
-
nodeId?: string;
|
|
8
12
|
onConfigOpen?: (node: {
|
|
9
13
|
id: string;
|
|
10
14
|
type: string;
|
|
@@ -18,7 +22,7 @@ type $$ComponentProps = {
|
|
|
18
22
|
selected?: boolean;
|
|
19
23
|
isProcessing?: boolean;
|
|
20
24
|
isError?: boolean;
|
|
21
|
-
}
|
|
22
|
-
declare const NotesNode: import("svelte").Component
|
|
25
|
+
}
|
|
26
|
+
declare const NotesNode: import("svelte").Component<Props, {}, "">;
|
|
23
27
|
type NotesNode = ReturnType<typeof NotesNode>;
|
|
24
28
|
export default NotesNode;
|