@flowdrop/flowdrop 1.0.0 → 1.0.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/dist/components/FlowDropEdge.stories.svelte +161 -0
- package/dist/components/FlowDropEdge.stories.svelte.d.ts +26 -0
- package/dist/components/FlowDropEdge.svelte +168 -0
- package/dist/components/FlowDropEdge.svelte.d.ts +4 -0
- package/dist/components/WorkflowEditor.svelte +7 -0
- package/dist/config/constants.d.ts +8 -0
- package/dist/config/constants.js +8 -0
- package/dist/services/api.d.ts +16 -2
- package/dist/services/api.js +9 -6
- package/dist/services/globalSave.js +12 -4
- package/dist/stories/EdgeDecorator.svelte +122 -0
- package/dist/stories/EdgeDecorator.svelte.d.ts +17 -0
- package/package.json +296 -291
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import EdgeDecorator from '../../lib/stories/EdgeDecorator.svelte';
|
|
4
|
+
import { createSampleNodeData } from '../../lib/stories/utils.js';
|
|
5
|
+
import { EDGE_MARKER_SIZES } from '../../lib/config/constants.js';
|
|
6
|
+
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: 'Edges/FlowDropEdge',
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const sourceNode = createSampleNodeData({
|
|
16
|
+
label: 'Text Input',
|
|
17
|
+
metadata: {
|
|
18
|
+
id: 'text_input',
|
|
19
|
+
name: 'Text Input',
|
|
20
|
+
description: 'Simple text input',
|
|
21
|
+
category: 'inputs',
|
|
22
|
+
version: '1.0.0',
|
|
23
|
+
type: 'simple',
|
|
24
|
+
icon: 'mdi:text',
|
|
25
|
+
color: '#22c55e',
|
|
26
|
+
inputs: [],
|
|
27
|
+
outputs: [
|
|
28
|
+
{ id: 'output', name: 'Output', type: 'output', dataType: 'string', required: false }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const targetNode = createSampleNodeData({
|
|
34
|
+
label: 'Text Output',
|
|
35
|
+
metadata: {
|
|
36
|
+
id: 'text_output',
|
|
37
|
+
name: 'Text Output',
|
|
38
|
+
description: 'Display text output',
|
|
39
|
+
category: 'outputs',
|
|
40
|
+
version: '1.0.0',
|
|
41
|
+
type: 'simple',
|
|
42
|
+
icon: 'mdi:text-box',
|
|
43
|
+
color: '#ef4444',
|
|
44
|
+
inputs: [
|
|
45
|
+
{ id: 'input', name: 'Input', type: 'input', dataType: 'string', required: false }
|
|
46
|
+
],
|
|
47
|
+
outputs: []
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const triggerSource = createSampleNodeData({
|
|
52
|
+
label: 'Trigger Source',
|
|
53
|
+
metadata: {
|
|
54
|
+
id: 'trigger_source',
|
|
55
|
+
name: 'Trigger Source',
|
|
56
|
+
description: 'Emits a trigger signal',
|
|
57
|
+
category: 'processing',
|
|
58
|
+
version: '1.0.0',
|
|
59
|
+
type: 'simple',
|
|
60
|
+
icon: 'mdi:play',
|
|
61
|
+
color: '#3b82f6',
|
|
62
|
+
inputs: [],
|
|
63
|
+
outputs: [
|
|
64
|
+
{ id: 'trigger', name: 'Trigger', type: 'output', dataType: 'trigger', required: false }
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const triggerTarget = createSampleNodeData({
|
|
70
|
+
label: 'Trigger Target',
|
|
71
|
+
metadata: {
|
|
72
|
+
id: 'trigger_target',
|
|
73
|
+
name: 'Trigger Target',
|
|
74
|
+
description: 'Receives a trigger signal',
|
|
75
|
+
category: 'processing',
|
|
76
|
+
version: '1.0.0',
|
|
77
|
+
type: 'simple',
|
|
78
|
+
icon: 'mdi:lightning-bolt',
|
|
79
|
+
color: '#8b5cf6',
|
|
80
|
+
inputs: [
|
|
81
|
+
{ id: 'trigger', name: 'Trigger', type: 'input', dataType: 'trigger', required: false }
|
|
82
|
+
],
|
|
83
|
+
outputs: []
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const toolSource = createSampleNodeData({
|
|
88
|
+
label: 'Tool Provider',
|
|
89
|
+
metadata: {
|
|
90
|
+
id: 'tool_provider',
|
|
91
|
+
name: 'Tool Provider',
|
|
92
|
+
description: 'Provides a tool',
|
|
93
|
+
category: 'processing',
|
|
94
|
+
version: '1.0.0',
|
|
95
|
+
type: 'simple',
|
|
96
|
+
icon: 'mdi:wrench',
|
|
97
|
+
color: '#f59e0b',
|
|
98
|
+
inputs: [],
|
|
99
|
+
outputs: [
|
|
100
|
+
{ id: 'tool', name: 'Tool', type: 'output', dataType: 'tool', required: false }
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const toolTarget = createSampleNodeData({
|
|
106
|
+
label: 'Tool Consumer',
|
|
107
|
+
metadata: {
|
|
108
|
+
id: 'tool_consumer',
|
|
109
|
+
name: 'Tool Consumer',
|
|
110
|
+
description: 'Consumes a tool',
|
|
111
|
+
category: 'processing',
|
|
112
|
+
version: '1.0.0',
|
|
113
|
+
type: 'simple',
|
|
114
|
+
icon: 'mdi:cog',
|
|
115
|
+
color: '#f59e0b',
|
|
116
|
+
inputs: [
|
|
117
|
+
{ id: 'tool', name: 'Tool', type: 'input', dataType: 'tool', required: false }
|
|
118
|
+
],
|
|
119
|
+
outputs: []
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<Story name="Data Edge">
|
|
125
|
+
<EdgeDecorator
|
|
126
|
+
sourceData={sourceNode}
|
|
127
|
+
targetData={targetNode}
|
|
128
|
+
edgeStyle="stroke: var(--fd-edge-data, #64748b);"
|
|
129
|
+
edgeClass="flowdrop--edge--data"
|
|
130
|
+
edgeMarkerColor="#64748b"
|
|
131
|
+
edgeMarkerSize={EDGE_MARKER_SIZES.data}
|
|
132
|
+
sourceHandleId="output"
|
|
133
|
+
targetHandleId="input"
|
|
134
|
+
/>
|
|
135
|
+
</Story>
|
|
136
|
+
|
|
137
|
+
<Story name="Trigger Edge">
|
|
138
|
+
<EdgeDecorator
|
|
139
|
+
sourceData={triggerSource}
|
|
140
|
+
targetData={triggerTarget}
|
|
141
|
+
edgeStyle="stroke: var(--fd-edge-trigger, #1e293b); stroke-width: 2px;"
|
|
142
|
+
edgeClass="flowdrop--edge--trigger"
|
|
143
|
+
edgeMarkerColor="#1e293b"
|
|
144
|
+
edgeMarkerSize={EDGE_MARKER_SIZES.trigger}
|
|
145
|
+
sourceHandleId="trigger"
|
|
146
|
+
targetHandleId="trigger"
|
|
147
|
+
/>
|
|
148
|
+
</Story>
|
|
149
|
+
|
|
150
|
+
<Story name="Tool Edge">
|
|
151
|
+
<EdgeDecorator
|
|
152
|
+
sourceData={toolSource}
|
|
153
|
+
targetData={toolTarget}
|
|
154
|
+
edgeStyle="stroke: var(--fd-edge-tool, #f59e0b); stroke-dasharray: 5 3;"
|
|
155
|
+
edgeClass="flowdrop--edge--tool"
|
|
156
|
+
edgeMarkerColor="#f59e0b"
|
|
157
|
+
edgeMarkerSize={EDGE_MARKER_SIZES.tool}
|
|
158
|
+
sourceHandleId="tool"
|
|
159
|
+
targetHandleId="tool"
|
|
160
|
+
/>
|
|
161
|
+
</Story>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default FlowDropEdge;
|
|
2
|
+
type FlowDropEdge = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const FlowDropEdge: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
15
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
16
|
+
$$bindings?: Bindings;
|
|
17
|
+
} & Exports;
|
|
18
|
+
(internal: unknown, props: {
|
|
19
|
+
$$events?: Events;
|
|
20
|
+
$$slots?: Slots;
|
|
21
|
+
}): Exports & {
|
|
22
|
+
$set?: any;
|
|
23
|
+
$on?: any;
|
|
24
|
+
};
|
|
25
|
+
z_$$bindings?: Bindings;
|
|
26
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
FlowDropEdge Component
|
|
3
|
+
Custom bezier edge that draws its own arrowhead so the line stroke ends
|
|
4
|
+
at the arrow base instead of poking through to the tip.
|
|
5
|
+
|
|
6
|
+
Approach:
|
|
7
|
+
1. Compute the full bezier path (xyflow's getBezierPath)
|
|
8
|
+
2. Parse the SVG path to extract the cubic bezier control points
|
|
9
|
+
3. Evaluate the curve near the end to get the true visual tangent
|
|
10
|
+
4. Shorten the path along that tangent and draw the arrowhead at the target
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { getBezierPath } from '@xyflow/svelte';
|
|
15
|
+
import { BaseEdge } from '@xyflow/svelte';
|
|
16
|
+
import type { BezierEdgeProps } from '@xyflow/svelte';
|
|
17
|
+
import { ARROW_LENGTH_PX, ARROW_HALF_WIDTH_PX } from '../config/constants.js';
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
id,
|
|
21
|
+
interactionWidth,
|
|
22
|
+
label,
|
|
23
|
+
labelStyle,
|
|
24
|
+
markerEnd: _markerEnd,
|
|
25
|
+
markerStart,
|
|
26
|
+
pathOptions,
|
|
27
|
+
sourcePosition,
|
|
28
|
+
sourceX,
|
|
29
|
+
sourceY,
|
|
30
|
+
style,
|
|
31
|
+
targetPosition,
|
|
32
|
+
targetX,
|
|
33
|
+
targetY
|
|
34
|
+
}: BezierEdgeProps = $props();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Extract stroke color from the edge's inline style for the arrowhead fill.
|
|
38
|
+
*/
|
|
39
|
+
let strokeColor = $derived.by(() => {
|
|
40
|
+
if (!style) return 'var(--fd-edge-data, #64748b)';
|
|
41
|
+
const match = style.match(/stroke:\s*([^;]+)/);
|
|
42
|
+
return match ? match[1].trim() : 'var(--fd-edge-data, #64748b)';
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Evaluate a cubic bezier at parameter t.
|
|
47
|
+
* P(t) = (1-t)^3 * P0 + 3(1-t)^2 * t * P1 + 3(1-t) * t^2 * P2 + t^3 * P3
|
|
48
|
+
*/
|
|
49
|
+
function bezierAt(
|
|
50
|
+
p0x: number,
|
|
51
|
+
p0y: number,
|
|
52
|
+
p1x: number,
|
|
53
|
+
p1y: number,
|
|
54
|
+
p2x: number,
|
|
55
|
+
p2y: number,
|
|
56
|
+
p3x: number,
|
|
57
|
+
p3y: number,
|
|
58
|
+
t: number
|
|
59
|
+
): { x: number; y: number } {
|
|
60
|
+
const u = 1 - t;
|
|
61
|
+
const uu = u * u;
|
|
62
|
+
const uuu = uu * u;
|
|
63
|
+
const tt = t * t;
|
|
64
|
+
const ttt = tt * t;
|
|
65
|
+
return {
|
|
66
|
+
x: uuu * p0x + 3 * uu * t * p1x + 3 * u * tt * p2x + ttt * p3x,
|
|
67
|
+
y: uuu * p0y + 3 * uu * t * p1y + 3 * u * tt * p2y + ttt * p3y
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse the SVG cubic bezier path "M x0,y0 C x1,y1 x2,y2 x3,y3"
|
|
73
|
+
* into the four control points.
|
|
74
|
+
*/
|
|
75
|
+
function parseCubicBezier(d: string) {
|
|
76
|
+
const nums = d.match(/-?[\d.]+/g)?.map(Number);
|
|
77
|
+
if (!nums || nums.length < 8) return null;
|
|
78
|
+
return {
|
|
79
|
+
p0x: nums[0],
|
|
80
|
+
p0y: nums[1],
|
|
81
|
+
p1x: nums[2],
|
|
82
|
+
p1y: nums[3],
|
|
83
|
+
p2x: nums[4],
|
|
84
|
+
p2y: nums[5],
|
|
85
|
+
p3x: nums[6],
|
|
86
|
+
p3y: nums[7]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Parameter near the end of the curve for tangent sampling
|
|
91
|
+
const T_SAMPLE = 0.9;
|
|
92
|
+
|
|
93
|
+
let computed = $derived.by(() => {
|
|
94
|
+
// 1. Get the full bezier path from xyflow
|
|
95
|
+
const [fullPath, lx, ly] = getBezierPath({
|
|
96
|
+
sourceX,
|
|
97
|
+
sourceY,
|
|
98
|
+
targetX,
|
|
99
|
+
targetY,
|
|
100
|
+
sourcePosition,
|
|
101
|
+
targetPosition,
|
|
102
|
+
curvature: pathOptions?.curvature
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// 2. Parse control points from SVG path
|
|
106
|
+
const cp = parseCubicBezier(fullPath);
|
|
107
|
+
if (!cp) {
|
|
108
|
+
return { path: fullPath, labelX: lx, labelY: ly, angleDeg: 0 };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 3. Evaluate the curve at T_SAMPLE to get a reference point
|
|
112
|
+
const ref = bezierAt(
|
|
113
|
+
cp.p0x,
|
|
114
|
+
cp.p0y,
|
|
115
|
+
cp.p1x,
|
|
116
|
+
cp.p1y,
|
|
117
|
+
cp.p2x,
|
|
118
|
+
cp.p2y,
|
|
119
|
+
cp.p3x,
|
|
120
|
+
cp.p3y,
|
|
121
|
+
T_SAMPLE
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// 4. Tangent direction: from reference point to the target
|
|
125
|
+
const dx = targetX - ref.x;
|
|
126
|
+
const dy = targetY - ref.y;
|
|
127
|
+
const angleDeg = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
128
|
+
const angleRad = Math.atan2(dy, dx);
|
|
129
|
+
|
|
130
|
+
// 5. Shorten: move the endpoint back along the tangent
|
|
131
|
+
const adjX = targetX - Math.cos(angleRad) * ARROW_LENGTH_PX;
|
|
132
|
+
const adjY = targetY - Math.sin(angleRad) * ARROW_LENGTH_PX;
|
|
133
|
+
|
|
134
|
+
// 6. Recompute the bezier path with the shortened target
|
|
135
|
+
const [shortenedPath] = getBezierPath({
|
|
136
|
+
sourceX,
|
|
137
|
+
sourceY,
|
|
138
|
+
targetX: adjX,
|
|
139
|
+
targetY: adjY,
|
|
140
|
+
sourcePosition,
|
|
141
|
+
targetPosition,
|
|
142
|
+
curvature: pathOptions?.curvature
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return { path: shortenedPath, labelX: lx, labelY: ly, angleDeg };
|
|
146
|
+
});
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<BaseEdge
|
|
150
|
+
{id}
|
|
151
|
+
path={computed.path}
|
|
152
|
+
labelX={computed.labelX}
|
|
153
|
+
labelY={computed.labelY}
|
|
154
|
+
{label}
|
|
155
|
+
{labelStyle}
|
|
156
|
+
{markerStart}
|
|
157
|
+
{interactionWidth}
|
|
158
|
+
{style}
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
<!-- Manual arrowhead: tip at origin pointing right, rotated to the bezier tangent -->
|
|
162
|
+
<polygon
|
|
163
|
+
points="0,0 {-ARROW_LENGTH_PX},{-ARROW_HALF_WIDTH_PX} {-ARROW_LENGTH_PX},{ARROW_HALF_WIDTH_PX}"
|
|
164
|
+
fill={strokeColor}
|
|
165
|
+
stroke="none"
|
|
166
|
+
transform="translate({targetX},{targetY}) rotate({computed.angleDeg})"
|
|
167
|
+
class="flowdrop-edge-arrow"
|
|
168
|
+
/>
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
import { tick, untrack } from 'svelte';
|
|
34
34
|
import type { EndpointConfig } from '../config/endpoints.js';
|
|
35
35
|
import ConnectionLine from './ConnectionLine.svelte';
|
|
36
|
+
import FlowDropEdge from './FlowDropEdge.svelte';
|
|
36
37
|
import { getWorkflowStore, workflowActions } from '../stores/workflowStore.svelte.js';
|
|
37
38
|
import { historyActions, setOnRestoreCallback } from '../stores/historyStore.svelte.js';
|
|
38
39
|
import UniversalNode from './UniversalNode.svelte';
|
|
@@ -331,6 +332,11 @@
|
|
|
331
332
|
universalNode: UniversalNode
|
|
332
333
|
};
|
|
333
334
|
|
|
335
|
+
// Use custom edge that shortens the path so the stroke ends at the arrow base
|
|
336
|
+
const edgeTypes = {
|
|
337
|
+
default: FlowDropEdge
|
|
338
|
+
};
|
|
339
|
+
|
|
334
340
|
// Handle arrows in our custom connection handler
|
|
335
341
|
const defaultEdgeOptions = {};
|
|
336
342
|
|
|
@@ -759,6 +765,7 @@
|
|
|
759
765
|
bind:nodes={flowNodes}
|
|
760
766
|
bind:edges={flowEdges}
|
|
761
767
|
{nodeTypes}
|
|
768
|
+
{edgeTypes}
|
|
762
769
|
{defaultEdgeOptions}
|
|
763
770
|
onconnect={(connection) =>
|
|
764
771
|
void handleConnect({
|
|
@@ -14,6 +14,14 @@ export declare const NODE_EXECUTION_CACHE_TIMEOUT_MS = 30000;
|
|
|
14
14
|
export declare const PIPELINE_API_UNAVAILABLE_DURATION_MS: number;
|
|
15
15
|
/** Default cache TTL for schema and variable data in milliseconds (5 minutes) */
|
|
16
16
|
export declare const DEFAULT_CACHE_TTL_MS: number;
|
|
17
|
+
/**
|
|
18
|
+
* Custom arrowhead dimensions (in pixels) drawn by FlowDropEdge.
|
|
19
|
+
* The edge path is shortened by ARROW_LENGTH_PX so the stroke ends
|
|
20
|
+
* at the arrow base, and the arrowhead polygon is rendered separately
|
|
21
|
+
* at the target handle position.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ARROW_LENGTH_PX = 8;
|
|
24
|
+
export declare const ARROW_HALF_WIDTH_PX = 4;
|
|
17
25
|
/** Edge marker arrow sizes by category */
|
|
18
26
|
export declare const EDGE_MARKER_SIZES: {
|
|
19
27
|
readonly loopback: {
|
package/dist/config/constants.js
CHANGED
|
@@ -14,6 +14,14 @@ export const NODE_EXECUTION_CACHE_TIMEOUT_MS = 30_000;
|
|
|
14
14
|
export const PIPELINE_API_UNAVAILABLE_DURATION_MS = 5 * 60 * 1000;
|
|
15
15
|
/** Default cache TTL for schema and variable data in milliseconds (5 minutes) */
|
|
16
16
|
export const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
17
|
+
/**
|
|
18
|
+
* Custom arrowhead dimensions (in pixels) drawn by FlowDropEdge.
|
|
19
|
+
* The edge path is shortened by ARROW_LENGTH_PX so the stroke ends
|
|
20
|
+
* at the arrow base, and the arrowhead polygon is rendered separately
|
|
21
|
+
* at the target handle position.
|
|
22
|
+
*/
|
|
23
|
+
export const ARROW_LENGTH_PX = 8;
|
|
24
|
+
export const ARROW_HALF_WIDTH_PX = 4;
|
|
17
25
|
/** Edge marker arrow sizes by category */
|
|
18
26
|
export const EDGE_MARKER_SIZES = {
|
|
19
27
|
loopback: { width: 14, height: 14 },
|
package/dist/services/api.d.ts
CHANGED
|
@@ -59,7 +59,14 @@ export declare const workflowApi: {
|
|
|
59
59
|
*/
|
|
60
60
|
deleteWorkflow(id: string): Promise<void>;
|
|
61
61
|
/**
|
|
62
|
-
* Save workflow (create or update)
|
|
62
|
+
* Save workflow (create or update).
|
|
63
|
+
*
|
|
64
|
+
* A workflow is considered existing when it already has an id (any format —
|
|
65
|
+
* integer, UUID, slug). Only a missing/empty id means "truly new".
|
|
66
|
+
*
|
|
67
|
+
* Note: globalSave.ts bypasses this method and calls createWorkflow /
|
|
68
|
+
* updateWorkflow directly so it can capture the new/existing decision before
|
|
69
|
+
* the uuidv4() fallback. This method is kept for external callers.
|
|
63
70
|
*/
|
|
64
71
|
saveWorkflow(workflow: Workflow): Promise<Workflow>;
|
|
65
72
|
};
|
|
@@ -108,7 +115,14 @@ export declare const api: {
|
|
|
108
115
|
*/
|
|
109
116
|
deleteWorkflow(id: string): Promise<void>;
|
|
110
117
|
/**
|
|
111
|
-
* Save workflow (create or update)
|
|
118
|
+
* Save workflow (create or update).
|
|
119
|
+
*
|
|
120
|
+
* A workflow is considered existing when it already has an id (any format —
|
|
121
|
+
* integer, UUID, slug). Only a missing/empty id means "truly new".
|
|
122
|
+
*
|
|
123
|
+
* Note: globalSave.ts bypasses this method and calls createWorkflow /
|
|
124
|
+
* updateWorkflow directly so it can capture the new/existing decision before
|
|
125
|
+
* the uuidv4() fallback. This method is kept for external callers.
|
|
112
126
|
*/
|
|
113
127
|
saveWorkflow(workflow: Workflow): Promise<Workflow>;
|
|
114
128
|
};
|
package/dist/services/api.js
CHANGED
|
@@ -185,14 +185,17 @@ export const workflowApi = {
|
|
|
185
185
|
await apiRequest('workflows.delete', endpointConfig.endpoints.workflows.delete, { id }, { method: 'DELETE' });
|
|
186
186
|
},
|
|
187
187
|
/**
|
|
188
|
-
* Save workflow (create or update)
|
|
188
|
+
* Save workflow (create or update).
|
|
189
|
+
*
|
|
190
|
+
* A workflow is considered existing when it already has an id (any format —
|
|
191
|
+
* integer, UUID, slug). Only a missing/empty id means "truly new".
|
|
192
|
+
*
|
|
193
|
+
* Note: globalSave.ts bypasses this method and calls createWorkflow /
|
|
194
|
+
* updateWorkflow directly so it can capture the new/existing decision before
|
|
195
|
+
* the uuidv4() fallback. This method is kept for external callers.
|
|
189
196
|
*/
|
|
190
197
|
async saveWorkflow(workflow) {
|
|
191
|
-
|
|
192
|
-
// Valid IDs should not be a UUID (which indicates a new workflow)
|
|
193
|
-
const isExistingWorkflow = workflow.id &&
|
|
194
|
-
workflow.id.length > 0 &&
|
|
195
|
-
!workflow.id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
|
|
198
|
+
const isExistingWorkflow = !!(workflow.id && workflow.id.length > 0);
|
|
196
199
|
if (isExistingWorkflow) {
|
|
197
200
|
// Update existing workflow
|
|
198
201
|
return this.updateWorkflow(workflow.id, workflow);
|
|
@@ -110,6 +110,11 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
110
110
|
await ensureApiConfiguration();
|
|
111
111
|
// Step 3 — Build the canonical workflow object.
|
|
112
112
|
// Preserve all existing metadata fields (format, tags, etc.) so nothing is dropped.
|
|
113
|
+
//
|
|
114
|
+
// Determine new vs existing BEFORE the uuidv4() fallback: if the store already
|
|
115
|
+
// has an id (any format — integer, UUID, slug), the workflow came from a backend
|
|
116
|
+
// and must be updated. Only a missing id means "truly new".
|
|
117
|
+
const isExistingWorkflow = !!currentWorkflow.id;
|
|
113
118
|
const workflowId = currentWorkflow.id || uuidv4();
|
|
114
119
|
const finalWorkflow = {
|
|
115
120
|
id: workflowId,
|
|
@@ -128,9 +133,6 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
128
133
|
// Step 4 — Persist
|
|
129
134
|
let savedWorkflow;
|
|
130
135
|
if (apiClient) {
|
|
131
|
-
// Enhanced client path — detects existing workflows by non-UUID ID
|
|
132
|
-
const isExistingWorkflow = finalWorkflow.id.length > 0 &&
|
|
133
|
-
!finalWorkflow.id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
|
|
134
136
|
if (isExistingWorkflow) {
|
|
135
137
|
savedWorkflow = await apiClient.updateWorkflow(finalWorkflow.id, finalWorkflow);
|
|
136
138
|
}
|
|
@@ -140,7 +142,13 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
140
142
|
}
|
|
141
143
|
else {
|
|
142
144
|
// Legacy path
|
|
143
|
-
|
|
145
|
+
if (isExistingWorkflow) {
|
|
146
|
+
savedWorkflow = await workflowApi.updateWorkflow(finalWorkflow.id, finalWorkflow);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const { id: _id, ...workflowData } = finalWorkflow;
|
|
150
|
+
savedWorkflow = await workflowApi.createWorkflow(workflowData);
|
|
151
|
+
}
|
|
144
152
|
}
|
|
145
153
|
// Step 5 — If the server assigned a new ID, sync the store
|
|
146
154
|
if (savedWorkflow.id && savedWorkflow.id !== finalWorkflow.id) {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
EdgeDecorator: Renders two connected nodes inside a SvelteFlow canvas
|
|
3
|
+
to showcase edge rendering with arrowhead markers.
|
|
4
|
+
-->
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { SvelteFlow, Controls, MarkerType } from '@xyflow/svelte';
|
|
7
|
+
import type { Node, Edge, ColorMode } from '@xyflow/svelte';
|
|
8
|
+
import '@xyflow/svelte/dist/style.css';
|
|
9
|
+
import UniversalNode from '../components/UniversalNode.svelte';
|
|
10
|
+
import FlowDropEdge from '../components/FlowDropEdge.svelte';
|
|
11
|
+
import { registerBuiltinNodes } from '../registry/builtinNodes.js';
|
|
12
|
+
import { EDGE_MARKER_SIZES } from '../config/constants.js';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
sourceData: Record<string, unknown>;
|
|
16
|
+
targetData: Record<string, unknown>;
|
|
17
|
+
edgeStyle?: string;
|
|
18
|
+
edgeClass?: string;
|
|
19
|
+
edgeMarkerColor?: string;
|
|
20
|
+
edgeMarkerSize?: { width: number; height: number };
|
|
21
|
+
sourceHandleId?: string;
|
|
22
|
+
targetHandleId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
sourceData,
|
|
27
|
+
targetData,
|
|
28
|
+
edgeStyle = 'stroke: #64748b;',
|
|
29
|
+
edgeClass = 'flowdrop--edge--data',
|
|
30
|
+
edgeMarkerColor = '#64748b',
|
|
31
|
+
edgeMarkerSize = EDGE_MARKER_SIZES.data,
|
|
32
|
+
sourceHandleId = 'output',
|
|
33
|
+
targetHandleId = 'input'
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
registerBuiltinNodes();
|
|
37
|
+
|
|
38
|
+
const nodeTypes = {
|
|
39
|
+
universalNode: UniversalNode
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const edgeTypes = {
|
|
43
|
+
default: FlowDropEdge
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const SOURCE_ID = 'source-node';
|
|
47
|
+
const TARGET_ID = 'target-node';
|
|
48
|
+
|
|
49
|
+
let nodes = $state<Node[]>([
|
|
50
|
+
{
|
|
51
|
+
id: SOURCE_ID,
|
|
52
|
+
type: 'universalNode',
|
|
53
|
+
position: { x: 0, y: 0 },
|
|
54
|
+
data: { ...sourceData, nodeId: SOURCE_ID }
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: TARGET_ID,
|
|
58
|
+
type: 'universalNode',
|
|
59
|
+
position: { x: 350, y: 0 },
|
|
60
|
+
data: { ...targetData, nodeId: TARGET_ID }
|
|
61
|
+
}
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
// Handle IDs follow the format: {nodeId}-{input|output}-{portId}
|
|
65
|
+
let edges = $state<Edge[]>([
|
|
66
|
+
{
|
|
67
|
+
id: 'edge-1',
|
|
68
|
+
source: SOURCE_ID,
|
|
69
|
+
target: TARGET_ID,
|
|
70
|
+
sourceHandle: `${SOURCE_ID}-output-${sourceHandleId}`,
|
|
71
|
+
targetHandle: `${TARGET_ID}-input-${targetHandleId}`,
|
|
72
|
+
style: edgeStyle,
|
|
73
|
+
class: edgeClass,
|
|
74
|
+
markerEnd: {
|
|
75
|
+
type: MarkerType.ArrowClosed,
|
|
76
|
+
...edgeMarkerSize,
|
|
77
|
+
color: edgeMarkerColor
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
let colorMode = $state<ColorMode>(
|
|
83
|
+
(document.documentElement.getAttribute('data-theme') as ColorMode) || 'light'
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
$effect(() => {
|
|
87
|
+
const observer = new MutationObserver(() => {
|
|
88
|
+
colorMode = (document.documentElement.getAttribute('data-theme') as ColorMode) || 'light';
|
|
89
|
+
});
|
|
90
|
+
observer.observe(document.documentElement, {
|
|
91
|
+
attributes: true,
|
|
92
|
+
attributeFilter: ['data-theme']
|
|
93
|
+
});
|
|
94
|
+
return () => observer.disconnect();
|
|
95
|
+
});
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<div class="edge-decorator-wrapper">
|
|
99
|
+
<SvelteFlow
|
|
100
|
+
bind:nodes
|
|
101
|
+
bind:edges
|
|
102
|
+
{nodeTypes}
|
|
103
|
+
{edgeTypes}
|
|
104
|
+
fitView
|
|
105
|
+
fitViewOptions={{ maxZoom: 0.85, padding: 0.3 }}
|
|
106
|
+
{colorMode}
|
|
107
|
+
>
|
|
108
|
+
<Controls />
|
|
109
|
+
</SvelteFlow>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<style>
|
|
113
|
+
.edge-decorator-wrapper {
|
|
114
|
+
width: 900px;
|
|
115
|
+
height: 400px;
|
|
116
|
+
position: relative;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.edge-decorator-wrapper :global(.svelte-flow.dark) {
|
|
120
|
+
--background-color-default: var(--xy-background-color-default);
|
|
121
|
+
}
|
|
122
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import '@xyflow/svelte/dist/style.css';
|
|
2
|
+
interface Props {
|
|
3
|
+
sourceData: Record<string, unknown>;
|
|
4
|
+
targetData: Record<string, unknown>;
|
|
5
|
+
edgeStyle?: string;
|
|
6
|
+
edgeClass?: string;
|
|
7
|
+
edgeMarkerColor?: string;
|
|
8
|
+
edgeMarkerSize?: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
sourceHandleId?: string;
|
|
13
|
+
targetHandleId?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const EdgeDecorator: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type EdgeDecorator = ReturnType<typeof EdgeDecorator>;
|
|
17
|
+
export default EdgeDecorator;
|
package/package.json
CHANGED
|
@@ -1,292 +1,297 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
2
|
+
"name": "@flowdrop/flowdrop",
|
|
3
|
+
"description": "A drop-in visual workflow editor for any web application. You own the backend. You own the data. You own the orchestration.",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"private": false,
|
|
6
|
+
"version": "1.0.1",
|
|
7
|
+
"author": "Shibin Das (D34dMan)",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/flowdrop-io/flowdrop/issues"
|
|
10
|
+
},
|
|
11
|
+
"watch": {
|
|
12
|
+
"build": {
|
|
13
|
+
"ignore": "build",
|
|
14
|
+
"patterns": [
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"extensions": "js,ts,svelte,html,css,svg",
|
|
18
|
+
"quiet": true,
|
|
19
|
+
"legacyWatch": true,
|
|
20
|
+
"delay": 2500,
|
|
21
|
+
"runOnChangeOnly": false
|
|
22
|
+
},
|
|
23
|
+
"build:drupal": {
|
|
24
|
+
"ignore": "build",
|
|
25
|
+
"patterns": [
|
|
26
|
+
"src"
|
|
27
|
+
],
|
|
28
|
+
"extensions": "js,ts,svelte,html,css,svg",
|
|
29
|
+
"quiet": true,
|
|
30
|
+
"legacyWatch": true,
|
|
31
|
+
"delay": 2500,
|
|
32
|
+
"runOnChangeOnly": false
|
|
33
|
+
},
|
|
34
|
+
"build:production": {
|
|
35
|
+
"ignore": "build",
|
|
36
|
+
"patterns": [
|
|
37
|
+
"src"
|
|
38
|
+
],
|
|
39
|
+
"extensions": "js,ts,svelte,html,css,svg",
|
|
40
|
+
"quiet": true,
|
|
41
|
+
"legacyWatch": true,
|
|
42
|
+
"delay": 2500,
|
|
43
|
+
"runOnChangeOnly": false
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"!dist/**/*.test.*",
|
|
49
|
+
"!dist/**/*.spec.*"
|
|
50
|
+
],
|
|
51
|
+
"sideEffects": [
|
|
52
|
+
"**/*.css",
|
|
53
|
+
"./dist/styles/base.css",
|
|
54
|
+
"./dist/editor/index.js"
|
|
55
|
+
],
|
|
56
|
+
"svelte": "./dist/index.js",
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"type": "module",
|
|
59
|
+
"exports": {
|
|
60
|
+
".": {
|
|
61
|
+
"types": "./dist/index.d.ts",
|
|
62
|
+
"svelte": "./dist/index.js",
|
|
63
|
+
"default": "./dist/index.js"
|
|
64
|
+
},
|
|
65
|
+
"./core": {
|
|
66
|
+
"types": "./dist/core/index.d.ts",
|
|
67
|
+
"svelte": "./dist/core/index.js",
|
|
68
|
+
"default": "./dist/core/index.js"
|
|
69
|
+
},
|
|
70
|
+
"./editor": {
|
|
71
|
+
"types": "./dist/editor/index.d.ts",
|
|
72
|
+
"svelte": "./dist/editor/index.js",
|
|
73
|
+
"default": "./dist/editor/index.js"
|
|
74
|
+
},
|
|
75
|
+
"./form": {
|
|
76
|
+
"types": "./dist/form/index.d.ts",
|
|
77
|
+
"svelte": "./dist/form/index.js",
|
|
78
|
+
"default": "./dist/form/index.js"
|
|
79
|
+
},
|
|
80
|
+
"./form/code": {
|
|
81
|
+
"types": "./dist/form/code.d.ts",
|
|
82
|
+
"svelte": "./dist/form/code.js",
|
|
83
|
+
"default": "./dist/form/code.js"
|
|
84
|
+
},
|
|
85
|
+
"./form/markdown": {
|
|
86
|
+
"types": "./dist/form/markdown.d.ts",
|
|
87
|
+
"svelte": "./dist/form/markdown.js",
|
|
88
|
+
"default": "./dist/form/markdown.js"
|
|
89
|
+
},
|
|
90
|
+
"./form/full": {
|
|
91
|
+
"types": "./dist/form/full.d.ts",
|
|
92
|
+
"svelte": "./dist/form/full.js",
|
|
93
|
+
"default": "./dist/form/full.js"
|
|
94
|
+
},
|
|
95
|
+
"./display": {
|
|
96
|
+
"types": "./dist/display/index.d.ts",
|
|
97
|
+
"svelte": "./dist/display/index.js",
|
|
98
|
+
"default": "./dist/display/index.js"
|
|
99
|
+
},
|
|
100
|
+
"./playground": {
|
|
101
|
+
"types": "./dist/playground/index.d.ts",
|
|
102
|
+
"svelte": "./dist/playground/index.js",
|
|
103
|
+
"default": "./dist/playground/index.js"
|
|
104
|
+
},
|
|
105
|
+
"./settings": {
|
|
106
|
+
"types": "./dist/settings/index.d.ts",
|
|
107
|
+
"svelte": "./dist/settings/index.js",
|
|
108
|
+
"default": "./dist/settings/index.js"
|
|
109
|
+
},
|
|
110
|
+
"./styles": "./dist/styles/base.css",
|
|
111
|
+
"./styles/*": "./dist/styles/*",
|
|
112
|
+
"./schema": {
|
|
113
|
+
"default": "./dist/schemas/v1/workflow.schema.json"
|
|
114
|
+
},
|
|
115
|
+
"./schema/v1": {
|
|
116
|
+
"default": "./dist/schemas/v1/workflow.schema.json"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"peerDependencies": {
|
|
120
|
+
"@codemirror/autocomplete": "^6.20.0",
|
|
121
|
+
"@codemirror/commands": "^6.10.2",
|
|
122
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
123
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
124
|
+
"@codemirror/language": "^6.12.1",
|
|
125
|
+
"@codemirror/lint": "^6.9.2",
|
|
126
|
+
"@codemirror/state": "^6.5.4",
|
|
127
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
128
|
+
"@codemirror/view": "^6.39.14",
|
|
129
|
+
"@iconify/svelte": "^5.0.0",
|
|
130
|
+
"@xyflow/svelte": "^1.2",
|
|
131
|
+
"codemirror": "^6.0.2",
|
|
132
|
+
"svelte": "^5.0.0"
|
|
133
|
+
},
|
|
134
|
+
"peerDependenciesMeta": {
|
|
135
|
+
"@codemirror/autocomplete": {
|
|
136
|
+
"optional": true
|
|
137
|
+
},
|
|
138
|
+
"@codemirror/commands": {
|
|
139
|
+
"optional": true
|
|
140
|
+
},
|
|
141
|
+
"@codemirror/lang-json": {
|
|
142
|
+
"optional": true
|
|
143
|
+
},
|
|
144
|
+
"@codemirror/lang-markdown": {
|
|
145
|
+
"optional": true
|
|
146
|
+
},
|
|
147
|
+
"@codemirror/language": {
|
|
148
|
+
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"@codemirror/lint": {
|
|
151
|
+
"optional": true
|
|
152
|
+
},
|
|
153
|
+
"@codemirror/state": {
|
|
154
|
+
"optional": true
|
|
155
|
+
},
|
|
156
|
+
"@codemirror/theme-one-dark": {
|
|
157
|
+
"optional": true
|
|
158
|
+
},
|
|
159
|
+
"@codemirror/view": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"@iconify/svelte": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"codemirror": {
|
|
166
|
+
"optional": true
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"repository": {
|
|
170
|
+
"type": "git",
|
|
171
|
+
"url": "git+https://github.com/flowdrop-io/flowdrop.git"
|
|
172
|
+
},
|
|
173
|
+
"devDependencies": {
|
|
174
|
+
"@chromatic-com/storybook": "^5.0.1",
|
|
175
|
+
"@xyflow/svelte": "^1.2",
|
|
176
|
+
"@codemirror/autocomplete": "^6.20.0",
|
|
177
|
+
"@codemirror/commands": "^6.10.2",
|
|
178
|
+
"@codemirror/lang-json": "^6.0.2",
|
|
179
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
180
|
+
"@codemirror/language": "^6.12.1",
|
|
181
|
+
"@codemirror/lint": "^6.9.2",
|
|
182
|
+
"@codemirror/state": "^6.5.4",
|
|
183
|
+
"@codemirror/theme-one-dark": "^6.1.3",
|
|
184
|
+
"@codemirror/view": "^6.39.14",
|
|
185
|
+
"codemirror": "^6.0.2",
|
|
186
|
+
"@eslint/compat": "^1.2.5",
|
|
187
|
+
"@eslint/js": "^9.18.0",
|
|
188
|
+
"@iconify/svelte": "^5.0.0",
|
|
189
|
+
"@playwright/test": "^1.49.1",
|
|
190
|
+
"@storybook/addon-docs": "^10.2.15",
|
|
191
|
+
"@storybook/addon-svelte-csf": "^5.0.11",
|
|
192
|
+
"@storybook/addon-themes": "^10.2.15",
|
|
193
|
+
"@storybook/addon-vitest": "^10.2.15",
|
|
194
|
+
"@storybook/sveltekit": "^10.2.15",
|
|
195
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
196
|
+
"@sveltejs/adapter-node": "^5.4.0",
|
|
197
|
+
"@sveltejs/kit": "^2.49.2",
|
|
198
|
+
"@sveltejs/package": "^2.0.0",
|
|
199
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
200
|
+
"@types/dompurify": "^3.2.0",
|
|
201
|
+
"@types/marked": "^6.0.0",
|
|
202
|
+
"@types/node": "^20",
|
|
203
|
+
"@types/uuid": "^10.0.0",
|
|
204
|
+
"@vitest/browser": "^3.2.3",
|
|
205
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
206
|
+
"@vitest/ui": "^3.2.4",
|
|
207
|
+
"eslint": "^9.18.0",
|
|
208
|
+
"eslint-config-prettier": "^10.0.1",
|
|
209
|
+
"eslint-plugin-storybook": "^10.2.15",
|
|
210
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
211
|
+
"globals": "^16.0.0",
|
|
212
|
+
"happy-dom": "^20.0.11",
|
|
213
|
+
"msw": "^2.12.7",
|
|
214
|
+
"npm-watch": "^0.13.0",
|
|
215
|
+
"picomatch": "^4.0.3",
|
|
216
|
+
"playwright": "^1.53.0",
|
|
217
|
+
"prettier": "^3.4.2",
|
|
218
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
219
|
+
"publint": "^0.3.2",
|
|
220
|
+
"storybook": "^10.2.15",
|
|
221
|
+
"storybook-addon-tag-badges": "^3.0.6",
|
|
222
|
+
"svelte": "^5.0.0",
|
|
223
|
+
"svelte-check": "^4.0.0",
|
|
224
|
+
"terser": "^5.43.1",
|
|
225
|
+
"typescript": "^5.0.0",
|
|
226
|
+
"typescript-eslint": "^8.20.0",
|
|
227
|
+
"vite": "^6.2.6",
|
|
228
|
+
"vite-plugin-devtools-json": "^0.2.1",
|
|
229
|
+
"vitest": "^3.2.3",
|
|
230
|
+
"vitest-browser-svelte": "^0.1.0",
|
|
231
|
+
"yaml": "^2.8.2"
|
|
232
|
+
},
|
|
233
|
+
"overrides": {
|
|
234
|
+
"@sveltejs/kit": {
|
|
235
|
+
"cookie": "0.7.2"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"engines": {
|
|
239
|
+
"node": ">=18"
|
|
240
|
+
},
|
|
241
|
+
"keywords": [
|
|
242
|
+
"svelte",
|
|
243
|
+
"svelte5",
|
|
244
|
+
"workflow",
|
|
245
|
+
"workflow-editor",
|
|
246
|
+
"editor",
|
|
247
|
+
"node-editor",
|
|
248
|
+
"graph-editor",
|
|
249
|
+
"visual-programming",
|
|
250
|
+
"xyflow",
|
|
251
|
+
"flowchart",
|
|
252
|
+
"dag",
|
|
253
|
+
"agent",
|
|
254
|
+
"ai-workflow",
|
|
255
|
+
"embeddable"
|
|
256
|
+
],
|
|
257
|
+
"dependencies": {
|
|
258
|
+
"diff": "^8.0.3",
|
|
259
|
+
"dompurify": "^3.3.1",
|
|
260
|
+
"marked": "^16.1.1",
|
|
261
|
+
"svelte-5-french-toast": "^2.0.6",
|
|
262
|
+
"uuid": "^11.1.0"
|
|
263
|
+
},
|
|
264
|
+
"msw": {
|
|
265
|
+
"workerDirectory": [
|
|
266
|
+
"static"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"scripts": {
|
|
270
|
+
"dev": "vite dev",
|
|
271
|
+
"build": "vite build && pnpm run prepack",
|
|
272
|
+
"build:drupal": "vite build --config vite.config.drupal.ts",
|
|
273
|
+
"build:production": "vite build --config vite.config.production.ts",
|
|
274
|
+
"watch:build:drupal": "npm-watch build:drupal",
|
|
275
|
+
"watch:build:production": "npm-watch build:production",
|
|
276
|
+
"watch:build": "npm-watch build",
|
|
277
|
+
"preview": "vite preview",
|
|
278
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
279
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
280
|
+
"lint": "eslint . && prettier --check .",
|
|
281
|
+
"test": "vitest run",
|
|
282
|
+
"test:watch": "vitest",
|
|
283
|
+
"test:ui": "vitest --ui",
|
|
284
|
+
"test:coverage": "vitest run --coverage",
|
|
285
|
+
"test:e2e": "playwright test",
|
|
286
|
+
"test:e2e:ui": "playwright test --ui",
|
|
287
|
+
"test:e2e:debug": "playwright test --debug",
|
|
288
|
+
"test:all": "pnpm run test && pnpm run test:e2e",
|
|
289
|
+
"format": "prettier --write .",
|
|
290
|
+
"storybook": "storybook dev -p 6006",
|
|
291
|
+
"storybook:build": "storybook build",
|
|
292
|
+
"schema:generate": "node scripts/generate-schema.mjs",
|
|
293
|
+
"schema:check": "node scripts/generate-schema.mjs --check",
|
|
294
|
+
"api:lint": "pnpm --dir ../../apps/api-docs run lint",
|
|
295
|
+
"api:bundle": "pnpm --dir ../../apps/api-docs run bundle"
|
|
296
|
+
}
|
|
297
|
+
}
|