@d34dman/flowdrop 0.0.26 → 0.0.28
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/App.svelte +3 -6
- package/dist/components/ConfigForm.svelte +496 -8
- package/dist/components/ConfigForm.svelte.d.ts +2 -0
- package/dist/components/EdgeRefresher.svelte +0 -1
- package/dist/components/SchemaForm.svelte +492 -0
- package/dist/components/SchemaForm.svelte.d.ts +65 -0
- package/dist/components/WorkflowEditor.svelte +1 -4
- package/dist/components/form/FormCodeEditor.svelte +0 -1
- package/dist/components/form/FormMarkdownEditor.svelte +0 -2
- package/dist/components/form/types.d.ts +96 -0
- package/dist/components/nodes/SimpleNode.svelte +92 -142
- package/dist/components/nodes/SquareNode.svelte +75 -58
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/services/dynamicSchemaService.d.ts +108 -0
- package/dist/services/dynamicSchemaService.js +445 -0
- package/dist/types/index.d.ts +213 -0
- package/package.json +1 -1
|
@@ -42,27 +42,6 @@
|
|
|
42
42
|
return instanceOverride ?? typeDefault;
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
* Check if a trigger port is connected
|
|
47
|
-
* @param portId - The port ID to check
|
|
48
|
-
* @param type - Whether this is an 'input' or 'output' port
|
|
49
|
-
*/
|
|
50
|
-
function isTriggerPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
51
|
-
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
52
|
-
return $connectedHandles.has(handleId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if a trigger port should be visible
|
|
57
|
-
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
58
|
-
*/
|
|
59
|
-
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
60
|
-
if (!hideUnconnectedHandles()) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return isTriggerPortConnected(portId, type);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
45
|
// Removed local config state - now using global ConfigSidebar
|
|
67
46
|
|
|
68
47
|
// Prioritize metadata icon over config icon for simple nodes (metadata is the node definition)
|
|
@@ -72,28 +51,6 @@
|
|
|
72
51
|
let nodeColor = $derived(
|
|
73
52
|
(props.data.metadata?.color as string) || (props.data.config?.color as string) || '#6366f1'
|
|
74
53
|
);
|
|
75
|
-
let nodeLayout = $derived((props.data.config?.layout as string) || 'normal');
|
|
76
|
-
|
|
77
|
-
// Layout configurations
|
|
78
|
-
const layoutConfig = {
|
|
79
|
-
compact: {
|
|
80
|
-
width: '80px',
|
|
81
|
-
height: '80px',
|
|
82
|
-
iconSize: '2rem',
|
|
83
|
-
showHeader: false
|
|
84
|
-
},
|
|
85
|
-
normal: {
|
|
86
|
-
width: '18rem',
|
|
87
|
-
height: 'auto',
|
|
88
|
-
iconSize: '1rem',
|
|
89
|
-
showHeader: true
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
let currentLayout = $derived(
|
|
94
|
-
layoutConfig[nodeLayout as keyof typeof layoutConfig] || layoutConfig.normal
|
|
95
|
-
);
|
|
96
|
-
let isCompact = $derived(nodeLayout === 'compact');
|
|
97
54
|
|
|
98
55
|
// Handle configuration sidebar - now using global ConfigSidebar
|
|
99
56
|
function openConfigSidebar(): void {
|
|
@@ -126,6 +83,28 @@
|
|
|
126
83
|
}
|
|
127
84
|
}
|
|
128
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Check if a port is connected
|
|
88
|
+
* @param portId - The port ID to check
|
|
89
|
+
* @param type - Whether this is an 'input' or 'output' port
|
|
90
|
+
* @returns true if the port is connected
|
|
91
|
+
*/
|
|
92
|
+
function isPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
93
|
+
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
94
|
+
return $connectedHandles.has(handleId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a trigger port should be visible
|
|
99
|
+
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
100
|
+
*/
|
|
101
|
+
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
102
|
+
if (!hideUnconnectedHandles()) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return isPortConnected(portId, type);
|
|
106
|
+
}
|
|
107
|
+
|
|
129
108
|
// Get first input/output ports for simple node representation
|
|
130
109
|
// Special handling for trigger ports - they should always be shown if present
|
|
131
110
|
let triggerInputPort = $derived(
|
|
@@ -136,47 +115,71 @@
|
|
|
136
115
|
);
|
|
137
116
|
|
|
138
117
|
// Get first non-trigger ports for data connections
|
|
118
|
+
let firstConnectedDataInputPort = $derived(
|
|
119
|
+
props.data.metadata?.inputs?.find(
|
|
120
|
+
(port) => port.dataType !== 'trigger' && isPortConnected(port.id, 'input')
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
|
|
139
124
|
let firstDataInputPort = $derived(
|
|
140
125
|
props.data.metadata?.inputs?.find((port) => port.dataType !== 'trigger')
|
|
141
126
|
);
|
|
127
|
+
|
|
128
|
+
let firstConnectedDataOutputPort = $derived(
|
|
129
|
+
props.data.metadata?.outputs?.find(
|
|
130
|
+
(port) => port.dataType !== 'trigger' && isPortConnected(port.id, 'output')
|
|
131
|
+
)
|
|
132
|
+
);
|
|
142
133
|
let firstDataOutputPort = $derived(
|
|
143
134
|
props.data.metadata?.outputs?.find((port) => port.dataType !== 'trigger')
|
|
144
135
|
);
|
|
145
136
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
137
|
+
let inputPorts = $derived.by(() => {
|
|
138
|
+
return [
|
|
139
|
+
...(firstConnectedDataInputPort
|
|
140
|
+
? [firstConnectedDataInputPort]
|
|
141
|
+
: firstDataInputPort
|
|
142
|
+
? [firstDataInputPort]
|
|
143
|
+
: []),
|
|
144
|
+
...(triggerInputPort && shouldShowTriggerPort(triggerInputPort.id, 'input')
|
|
145
|
+
? [triggerInputPort]
|
|
146
|
+
: [])
|
|
147
|
+
];
|
|
148
|
+
});
|
|
149
|
+
let outputPorts = $derived.by(() => {
|
|
150
|
+
return [
|
|
151
|
+
...(firstConnectedDataOutputPort
|
|
152
|
+
? [firstConnectedDataOutputPort]
|
|
153
|
+
: firstDataOutputPort
|
|
154
|
+
? [firstDataOutputPort]
|
|
155
|
+
: []),
|
|
156
|
+
...(triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')
|
|
157
|
+
? [triggerOutputPort]
|
|
158
|
+
: [])
|
|
159
|
+
];
|
|
160
|
+
});
|
|
149
161
|
</script>
|
|
150
162
|
|
|
151
163
|
<!-- Input Handles -->
|
|
152
|
-
{#
|
|
164
|
+
{#each inputPorts as port, index}
|
|
153
165
|
<!-- Data Input - positioned at top-left if both types exist, otherwise center -->
|
|
154
166
|
<Handle
|
|
155
167
|
type="target"
|
|
156
168
|
position={Position.Left}
|
|
157
169
|
style="background-color: {getDataTypeColor(
|
|
158
|
-
|
|
159
|
-
)}; border-color: '#ffffff'; top: {
|
|
160
|
-
|
|
170
|
+
port.dataType
|
|
171
|
+
)}; border-color: '#ffffff'; top: {inputPorts.length > 1
|
|
172
|
+
? index === 0
|
|
173
|
+
? '25%'
|
|
174
|
+
: '75%'
|
|
175
|
+
: '50%'}; z-index: 30;"
|
|
176
|
+
id={`${props.data.nodeId}-input-${port.id}`}
|
|
161
177
|
/>
|
|
162
|
-
{/
|
|
163
|
-
{#if triggerInputPort && shouldShowTriggerPort(triggerInputPort.id, 'input')}
|
|
164
|
-
<!-- Trigger Input - positioned at bottom-left (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
165
|
-
<Handle
|
|
166
|
-
type="target"
|
|
167
|
-
position={Position.Left}
|
|
168
|
-
style="background-color: {getDataTypeColor(
|
|
169
|
-
triggerInputPort.dataType
|
|
170
|
-
)}; border-color: '#ffffff'; top: {hasBothInputTypes ? '75%' : '50%'}; z-index: 30;"
|
|
171
|
-
id={`${props.data.nodeId}-input-${triggerInputPort.id}`}
|
|
172
|
-
/>
|
|
173
|
-
{/if}
|
|
178
|
+
{/each}
|
|
174
179
|
|
|
175
180
|
<!-- Simple Node -->
|
|
176
181
|
<div
|
|
177
|
-
class="flowdrop-simple-node"
|
|
178
|
-
class:flowdrop-simple-node--compact={isCompact}
|
|
179
|
-
class:flowdrop-simple-node--normal={!isCompact}
|
|
182
|
+
class="flowdrop-simple-node flowdrop-simple-node--normal"
|
|
180
183
|
class:flowdrop-simple-node--selected={props.selected}
|
|
181
184
|
class:flowdrop-simple-node--processing={props.isProcessing}
|
|
182
185
|
class:flowdrop-simple-node--error={props.isError}
|
|
@@ -186,36 +189,24 @@
|
|
|
186
189
|
role="button"
|
|
187
190
|
tabindex="0"
|
|
188
191
|
>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
<
|
|
193
|
-
icon={nodeIcon}
|
|
194
|
-
class="flowdrop-simple-node__compact-icon"
|
|
195
|
-
style="color: {nodeColor}; font-size: {currentLayout.iconSize};"
|
|
196
|
-
/>
|
|
197
|
-
</div>
|
|
198
|
-
{:else}
|
|
199
|
-
<!-- Normal Layout: Header with title and description -->
|
|
200
|
-
<div class="flowdrop-simple-node__header">
|
|
201
|
-
<div class="flowdrop-simple-node__header-content">
|
|
202
|
-
<!-- Node Icon -->
|
|
203
|
-
<div class="flowdrop-simple-node__icon-container" style="background-color: {nodeColor}">
|
|
204
|
-
<Icon icon={nodeIcon} class="flowdrop-simple-node__icon" />
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<!-- Node Title -->
|
|
208
|
-
<h3 class="flowdrop-simple-node__title">
|
|
209
|
-
{props.data.label}
|
|
210
|
-
</h3>
|
|
192
|
+
<div class="flowdrop-simple-node__header">
|
|
193
|
+
<div class="flowdrop-simple-node__header-content">
|
|
194
|
+
<!-- Node Icon -->
|
|
195
|
+
<div class="flowdrop-simple-node__icon-container" style="background-color: {nodeColor}">
|
|
196
|
+
<Icon icon={nodeIcon} class="flowdrop-simple-node__icon" />
|
|
211
197
|
</div>
|
|
212
198
|
|
|
213
|
-
<!-- Node
|
|
214
|
-
<
|
|
215
|
-
{props.data.
|
|
216
|
-
</
|
|
199
|
+
<!-- Node Title -->
|
|
200
|
+
<h3 class="flowdrop-simple-node__title">
|
|
201
|
+
{props.data.label}
|
|
202
|
+
</h3>
|
|
217
203
|
</div>
|
|
218
|
-
|
|
204
|
+
|
|
205
|
+
<!-- Node Description -->
|
|
206
|
+
<p class="flowdrop-simple-node__description">
|
|
207
|
+
{props.data.metadata?.description || 'A configurable simple node'}
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
219
210
|
|
|
220
211
|
<!-- Processing indicator -->
|
|
221
212
|
{#if props.isProcessing}
|
|
@@ -242,28 +233,21 @@
|
|
|
242
233
|
</div>
|
|
243
234
|
|
|
244
235
|
<!-- Output Handles -->
|
|
245
|
-
{#
|
|
236
|
+
{#each outputPorts as port, index}
|
|
246
237
|
<!-- Data Output - positioned at top-right if both types exist, otherwise center -->
|
|
247
238
|
<Handle
|
|
248
239
|
type="source"
|
|
249
240
|
position={Position.Right}
|
|
250
|
-
id={`${props.data.nodeId}-output-${firstDataOutputPort.id}`}
|
|
251
241
|
style="background-color: {getDataTypeColor(
|
|
252
|
-
|
|
253
|
-
)}; border-color: '#ffffff'; top: {
|
|
242
|
+
port.dataType
|
|
243
|
+
)}; border-color: '#ffffff'; top: {outputPorts.length > 1
|
|
244
|
+
? index === 0
|
|
245
|
+
? '25%'
|
|
246
|
+
: '75%'
|
|
247
|
+
: '50%'}; z-index: 30;"
|
|
248
|
+
id={`${props.data.nodeId}-output-${port.id}`}
|
|
254
249
|
/>
|
|
255
|
-
{/
|
|
256
|
-
{#if triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')}
|
|
257
|
-
<!-- Trigger Output - positioned at bottom-right (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
258
|
-
<Handle
|
|
259
|
-
type="source"
|
|
260
|
-
position={Position.Right}
|
|
261
|
-
id={`${props.data.nodeId}-output-${triggerOutputPort.id}`}
|
|
262
|
-
style="background-color: {getDataTypeColor(
|
|
263
|
-
triggerOutputPort.dataType
|
|
264
|
-
)}; border-color: '#ffffff'; top: {hasBothOutputTypes ? '75%' : '50%'}; z-index: 30;"
|
|
265
|
-
/>
|
|
266
|
-
{/if}
|
|
250
|
+
{/each}
|
|
267
251
|
|
|
268
252
|
<!-- ConfigSidebar removed - now using global ConfigSidebar in WorkflowEditor -->
|
|
269
253
|
|
|
@@ -287,14 +271,6 @@
|
|
|
287
271
|
width: 18rem;
|
|
288
272
|
}
|
|
289
273
|
|
|
290
|
-
/* Compact layout */
|
|
291
|
-
.flowdrop-simple-node--compact {
|
|
292
|
-
width: 80px;
|
|
293
|
-
height: 80px;
|
|
294
|
-
justify-content: center;
|
|
295
|
-
align-items: center;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
274
|
.flowdrop-simple-node:hover {
|
|
299
275
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
300
276
|
}
|
|
@@ -335,19 +311,6 @@
|
|
|
335
311
|
flex-shrink: 0;
|
|
336
312
|
}
|
|
337
313
|
|
|
338
|
-
/* Compact layout styles */
|
|
339
|
-
.flowdrop-simple-node__compact-content {
|
|
340
|
-
display: flex;
|
|
341
|
-
align-items: center;
|
|
342
|
-
justify-content: center;
|
|
343
|
-
width: 100%;
|
|
344
|
-
height: 100%;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
:global(.flowdrop-simple-node__compact-icon) {
|
|
348
|
-
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
|
|
349
|
-
}
|
|
350
|
-
|
|
351
314
|
.flowdrop-simple-node__title {
|
|
352
315
|
font-size: 0.875rem;
|
|
353
316
|
font-weight: 500;
|
|
@@ -365,19 +328,6 @@
|
|
|
365
328
|
line-height: 1.3;
|
|
366
329
|
}
|
|
367
330
|
|
|
368
|
-
/* Compact layout text constraints */
|
|
369
|
-
.flowdrop-simple-node--compact .flowdrop-simple-node__title {
|
|
370
|
-
overflow: hidden;
|
|
371
|
-
text-overflow: ellipsis;
|
|
372
|
-
white-space: nowrap;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.flowdrop-simple-node--compact .flowdrop-simple-node__description {
|
|
376
|
-
overflow: hidden;
|
|
377
|
-
text-overflow: ellipsis;
|
|
378
|
-
white-space: nowrap;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
331
|
:global(.flowdrop-simple-node__icon) {
|
|
382
332
|
color: white;
|
|
383
333
|
font-size: 1rem;
|
|
@@ -42,27 +42,6 @@
|
|
|
42
42
|
return instanceOverride ?? typeDefault;
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
* Check if a trigger port is connected
|
|
47
|
-
* @param portId - The port ID to check
|
|
48
|
-
* @param type - Whether this is an 'input' or 'output' port
|
|
49
|
-
*/
|
|
50
|
-
function isTriggerPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
51
|
-
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
52
|
-
return $connectedHandles.has(handleId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if a trigger port should be visible
|
|
57
|
-
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
58
|
-
*/
|
|
59
|
-
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
60
|
-
if (!hideUnconnectedHandles()) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return isTriggerPortConnected(portId, type);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
45
|
// Removed local config state - now using global ConfigSidebar
|
|
67
46
|
|
|
68
47
|
// Prioritize metadata icon over config icon for square nodes (metadata is the node definition)
|
|
@@ -111,6 +90,27 @@
|
|
|
111
90
|
openConfigSidebar();
|
|
112
91
|
}
|
|
113
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if a port is connected
|
|
95
|
+
* @param portId - The port ID to check
|
|
96
|
+
* @param type - Whether this is an 'input' or 'output' port
|
|
97
|
+
* @returns true if the port is connected
|
|
98
|
+
*/
|
|
99
|
+
function isPortConnected(portId: string, type: 'input' | 'output'): boolean {
|
|
100
|
+
const handleId = `${props.data.nodeId}-${type}-${portId}`;
|
|
101
|
+
return $connectedHandles.has(handleId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if a trigger port should be visible
|
|
106
|
+
* Always shows if hideUnconnectedHandles is disabled or if port is connected
|
|
107
|
+
*/
|
|
108
|
+
function shouldShowTriggerPort(portId: string, type: 'input' | 'output'): boolean {
|
|
109
|
+
if (!hideUnconnectedHandles()) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return isPortConnected(portId, type);
|
|
113
|
+
}
|
|
114
114
|
|
|
115
115
|
// Get first input/output ports for square node representation
|
|
116
116
|
// Special handling for trigger ports - they should always be shown if present
|
|
@@ -122,41 +122,67 @@
|
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
// Get first non-trigger ports for data connections
|
|
125
|
+
let firstConnectedDataInputPort = $derived(
|
|
126
|
+
props.data.metadata?.inputs?.find(
|
|
127
|
+
(port) => port.dataType !== 'trigger' && isPortConnected(port.id, 'input')
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
|
|
125
131
|
let firstDataInputPort = $derived(
|
|
126
132
|
props.data.metadata?.inputs?.find((port) => port.dataType !== 'trigger')
|
|
127
133
|
);
|
|
134
|
+
|
|
135
|
+
let firstConnectedDataOutputPort = $derived(
|
|
136
|
+
props.data.metadata?.outputs?.find(
|
|
137
|
+
(port) => port.dataType !== 'trigger' && isPortConnected(port.id, 'output')
|
|
138
|
+
)
|
|
139
|
+
);
|
|
128
140
|
let firstDataOutputPort = $derived(
|
|
129
141
|
props.data.metadata?.outputs?.find((port) => port.dataType !== 'trigger')
|
|
130
142
|
);
|
|
131
143
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
let inputPorts = $derived.by(() => {
|
|
145
|
+
return [
|
|
146
|
+
...(firstConnectedDataInputPort
|
|
147
|
+
? [firstConnectedDataInputPort]
|
|
148
|
+
: firstDataInputPort
|
|
149
|
+
? [firstDataInputPort]
|
|
150
|
+
: []),
|
|
151
|
+
...(triggerInputPort && shouldShowTriggerPort(triggerInputPort.id, 'input')
|
|
152
|
+
? [triggerInputPort]
|
|
153
|
+
: [])
|
|
154
|
+
];
|
|
155
|
+
});
|
|
156
|
+
let outputPorts = $derived.by(() => {
|
|
157
|
+
return [
|
|
158
|
+
...(firstConnectedDataOutputPort
|
|
159
|
+
? [firstConnectedDataOutputPort]
|
|
160
|
+
: firstDataOutputPort
|
|
161
|
+
? [firstDataOutputPort]
|
|
162
|
+
: []),
|
|
163
|
+
...(triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')
|
|
164
|
+
? [triggerOutputPort]
|
|
165
|
+
: [])
|
|
166
|
+
];
|
|
167
|
+
});
|
|
135
168
|
</script>
|
|
136
169
|
|
|
137
170
|
<!-- Input Handles -->
|
|
138
|
-
{#
|
|
171
|
+
{#each inputPorts as port, index}
|
|
139
172
|
<!-- Data Input - positioned at top-left if both types exist, otherwise center -->
|
|
140
173
|
<Handle
|
|
141
174
|
type="target"
|
|
142
175
|
position={Position.Left}
|
|
143
176
|
style="background-color: {getDataTypeColor(
|
|
144
|
-
|
|
145
|
-
)}; border-color: '#ffffff'; top: {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<Handle
|
|
152
|
-
type="target"
|
|
153
|
-
position={Position.Left}
|
|
154
|
-
style="background-color: {getDataTypeColor(
|
|
155
|
-
triggerInputPort.dataType
|
|
156
|
-
)}; border-color: '#ffffff'; top: {hasBothInputTypes ? '75%' : '50%'}; z-index: 30;"
|
|
157
|
-
id={`${props.data.nodeId}-input-${triggerInputPort.id}`}
|
|
177
|
+
port.dataType
|
|
178
|
+
)}; border-color: '#ffffff'; top: {inputPorts.length > 1
|
|
179
|
+
? index === 0
|
|
180
|
+
? '25%'
|
|
181
|
+
: '75%'
|
|
182
|
+
: '50%'}; z-index: 30;"
|
|
183
|
+
id={`${props.data.nodeId}-input-${port.id}`}
|
|
158
184
|
/>
|
|
159
|
-
{/
|
|
185
|
+
{/each}
|
|
160
186
|
|
|
161
187
|
<!-- Square Node -->
|
|
162
188
|
<div
|
|
@@ -204,30 +230,21 @@
|
|
|
204
230
|
</div>
|
|
205
231
|
|
|
206
232
|
<!-- Output Handles -->
|
|
207
|
-
{#
|
|
233
|
+
{#each outputPorts as port, index}
|
|
208
234
|
<!-- Data Output - positioned at top-right if both types exist, otherwise center -->
|
|
209
235
|
<Handle
|
|
210
236
|
type="source"
|
|
211
237
|
position={Position.Right}
|
|
212
|
-
id={`${props.data.nodeId}-output-${firstDataOutputPort.id}`}
|
|
213
238
|
style="background-color: {getDataTypeColor(
|
|
214
|
-
|
|
215
|
-
)}; border-color: '#ffffff'; top: {
|
|
239
|
+
port.dataType
|
|
240
|
+
)}; border-color: '#ffffff'; top: {outputPorts.length > 1
|
|
241
|
+
? index === 0
|
|
242
|
+
? '25%'
|
|
243
|
+
: '75%'
|
|
244
|
+
: '50%'}; z-index: 30;"
|
|
245
|
+
id={`${props.data.nodeId}-output-${port.id}`}
|
|
216
246
|
/>
|
|
217
|
-
{/
|
|
218
|
-
{#if triggerOutputPort && shouldShowTriggerPort(triggerOutputPort.id, 'output')}
|
|
219
|
-
<!-- Trigger Output - positioned at bottom-right (hidden if hideUnconnectedHandles enabled and not connected) -->
|
|
220
|
-
<Handle
|
|
221
|
-
type="source"
|
|
222
|
-
position={Position.Right}
|
|
223
|
-
id={`${props.data.nodeId}-output-${triggerOutputPort.id}`}
|
|
224
|
-
style="background-color: {getDataTypeColor(
|
|
225
|
-
triggerOutputPort.dataType
|
|
226
|
-
)}; border-color: '#ffffff'; top: {hasBothOutputTypes ? '75%' : '50%'}; z-index: 30;"
|
|
227
|
-
/>
|
|
228
|
-
{/if}
|
|
229
|
-
|
|
230
|
-
<!-- ConfigSidebar removed - now using global ConfigSidebar in WorkflowEditor -->
|
|
247
|
+
{/each}
|
|
231
248
|
|
|
232
249
|
<style>
|
|
233
250
|
.flowdrop-square-node {
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import './styles/base.css';
|
|
6
6
|
import './registry/builtinNodes.js';
|
|
7
|
-
export type { NodeCategory, NodeDataType, NodePort, DynamicPort, Branch, NodeMetadata, NodeExtensions, NodeUIExtensions, ConfigValues, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents, BuiltinNodeType } from './types/index.js';
|
|
7
|
+
export type { NodeCategory, NodeDataType, NodePort, DynamicPort, Branch, NodeMetadata, NodeExtensions, NodeUIExtensions, ConfigValues, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents, BuiltinNodeType, HttpMethod, DynamicSchemaEndpoint, ExternalEditLink, ConfigEditOptions } from './types/index.js';
|
|
8
8
|
export type { WorkflowEditorConfig, EditorFeatures, UIConfig, APIConfig, ExecutionConfig, StorageConfig } from './types/config.js';
|
|
9
9
|
export type { AuthProvider, StaticAuthConfig, CallbackAuthConfig } from './types/auth.js';
|
|
10
10
|
export { StaticAuthProvider, CallbackAuthProvider, NoAuthProvider } from './types/auth.js';
|
|
@@ -30,6 +30,8 @@ export { default as MarkdownDisplay } from './components/MarkdownDisplay.svelte'
|
|
|
30
30
|
export { default as ConfigForm } from './components/ConfigForm.svelte';
|
|
31
31
|
export { default as ConfigModal } from './components/ConfigModal.svelte';
|
|
32
32
|
export { default as ConfigPanel } from './components/ConfigPanel.svelte';
|
|
33
|
+
export { default as SchemaForm } from './components/SchemaForm.svelte';
|
|
34
|
+
export type { SchemaFormProps, FieldSchema, FieldType, FieldFormat, FieldOption } from './components/form/types.js';
|
|
33
35
|
export { default as ReadOnlyDetails } from './components/ReadOnlyDetails.svelte';
|
|
34
36
|
export { default as ConnectionLine } from './components/ConnectionLine.svelte';
|
|
35
37
|
export { default as LogsSidebar } from './components/LogsSidebar.svelte';
|
|
@@ -53,6 +55,8 @@ export { NodeExecutionService, nodeExecutionService } from './services/nodeExecu
|
|
|
53
55
|
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
54
56
|
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
55
57
|
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
58
|
+
export { fetchDynamicSchema, resolveExternalEditUrl, getEffectiveConfigEditOptions, clearSchemaCache, invalidateSchemaCache, hasConfigEditOptions, shouldShowExternalEdit, shouldUseDynamicSchema } from './services/dynamicSchemaService.js';
|
|
59
|
+
export type { DynamicSchemaResult } from './services/dynamicSchemaService.js';
|
|
56
60
|
export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from './services/draftStorage.js';
|
|
57
61
|
export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
|
|
58
62
|
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, connectedHandles, isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange } from './stores/workflowStore.js';
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export { default as MarkdownDisplay } from './components/MarkdownDisplay.svelte'
|
|
|
31
31
|
export { default as ConfigForm } from './components/ConfigForm.svelte';
|
|
32
32
|
export { default as ConfigModal } from './components/ConfigModal.svelte';
|
|
33
33
|
export { default as ConfigPanel } from './components/ConfigPanel.svelte';
|
|
34
|
+
export { default as SchemaForm } from './components/SchemaForm.svelte';
|
|
34
35
|
export { default as ReadOnlyDetails } from './components/ReadOnlyDetails.svelte';
|
|
35
36
|
export { default as ConnectionLine } from './components/ConnectionLine.svelte';
|
|
36
37
|
export { default as LogsSidebar } from './components/LogsSidebar.svelte';
|
|
@@ -60,6 +61,8 @@ export { NodeExecutionService, nodeExecutionService } from './services/nodeExecu
|
|
|
60
61
|
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
61
62
|
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
62
63
|
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
64
|
+
// Export dynamic schema service for config edit functionality
|
|
65
|
+
export { fetchDynamicSchema, resolveExternalEditUrl, getEffectiveConfigEditOptions, clearSchemaCache, invalidateSchemaCache, hasConfigEditOptions, shouldShowExternalEdit, shouldUseDynamicSchema } from './services/dynamicSchemaService.js';
|
|
63
66
|
// Export draft storage service
|
|
64
67
|
export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from './services/draftStorage.js';
|
|
65
68
|
// Export helpers
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Schema Service
|
|
3
|
+
* Handles fetching configuration schemas from REST endpoints at runtime.
|
|
4
|
+
* Used for nodes where the config schema cannot be determined at workflow load time.
|
|
5
|
+
*
|
|
6
|
+
* @module services/dynamicSchemaService
|
|
7
|
+
*/
|
|
8
|
+
import type { ConfigSchema, DynamicSchemaEndpoint, ExternalEditLink, ConfigEditOptions, WorkflowNode } from '../types/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* Result of a dynamic schema fetch operation
|
|
11
|
+
*/
|
|
12
|
+
export interface DynamicSchemaResult {
|
|
13
|
+
/** Whether the fetch was successful */
|
|
14
|
+
success: boolean;
|
|
15
|
+
/** The fetched config schema (if successful) */
|
|
16
|
+
schema?: ConfigSchema;
|
|
17
|
+
/** Error message (if failed) */
|
|
18
|
+
error?: string;
|
|
19
|
+
/** Whether the schema was loaded from cache */
|
|
20
|
+
fromCache?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fetches a dynamic configuration schema from a REST endpoint.
|
|
24
|
+
*
|
|
25
|
+
* @param endpoint - The dynamic schema endpoint configuration
|
|
26
|
+
* @param node - The workflow node instance
|
|
27
|
+
* @param workflowId - Optional workflow ID for context
|
|
28
|
+
* @returns A promise that resolves to the schema result
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const endpoint: DynamicSchemaEndpoint = {
|
|
33
|
+
* url: "/api/nodes/{nodeTypeId}/schema",
|
|
34
|
+
* method: "GET",
|
|
35
|
+
* parameterMapping: { nodeTypeId: "metadata.id" }
|
|
36
|
+
* };
|
|
37
|
+
*
|
|
38
|
+
* const result = await fetchDynamicSchema(endpoint, node);
|
|
39
|
+
* if (result.success && result.schema) {
|
|
40
|
+
* // Use the fetched schema
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function fetchDynamicSchema(endpoint: DynamicSchemaEndpoint, node: WorkflowNode, workflowId?: string): Promise<DynamicSchemaResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Resolves an external edit link URL with template variables.
|
|
47
|
+
*
|
|
48
|
+
* @param link - The external edit link configuration
|
|
49
|
+
* @param node - The workflow node instance
|
|
50
|
+
* @param workflowId - Optional workflow ID for context
|
|
51
|
+
* @param callbackUrl - Optional callback URL to append
|
|
52
|
+
* @returns The resolved URL string
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const link: ExternalEditLink = {
|
|
57
|
+
* url: "https://admin.example.com/nodes/{nodeTypeId}/edit/{instanceId}",
|
|
58
|
+
* parameterMapping: { nodeTypeId: "metadata.id", instanceId: "id" }
|
|
59
|
+
* };
|
|
60
|
+
*
|
|
61
|
+
* const url = resolveExternalEditUrl(link, node, workflowId);
|
|
62
|
+
* // Returns "https://admin.example.com/nodes/llm-node/edit/node-1"
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveExternalEditUrl(link: ExternalEditLink, node: WorkflowNode, workflowId?: string, callbackUrl?: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Gets the effective config edit options for a node.
|
|
68
|
+
* Merges node type defaults with instance-level overrides.
|
|
69
|
+
*
|
|
70
|
+
* @param node - The workflow node instance
|
|
71
|
+
* @returns The merged config edit options, or undefined if not configured
|
|
72
|
+
*/
|
|
73
|
+
export declare function getEffectiveConfigEditOptions(node: WorkflowNode): ConfigEditOptions | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Clears the schema cache.
|
|
76
|
+
* Can optionally clear only entries matching a specific pattern.
|
|
77
|
+
*
|
|
78
|
+
* @param pattern - Optional pattern to match cache keys (e.g., node type ID)
|
|
79
|
+
*/
|
|
80
|
+
export declare function clearSchemaCache(pattern?: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* Invalidates a specific schema cache entry for a node.
|
|
83
|
+
*
|
|
84
|
+
* @param node - The workflow node to invalidate cache for
|
|
85
|
+
* @param endpoint - The dynamic schema endpoint configuration
|
|
86
|
+
*/
|
|
87
|
+
export declare function invalidateSchemaCache(node: WorkflowNode, endpoint: DynamicSchemaEndpoint): void;
|
|
88
|
+
/**
|
|
89
|
+
* Checks if a node has config edit options configured.
|
|
90
|
+
*
|
|
91
|
+
* @param node - The workflow node to check
|
|
92
|
+
* @returns True if the node has config edit options configured
|
|
93
|
+
*/
|
|
94
|
+
export declare function hasConfigEditOptions(node: WorkflowNode): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Determines if external edit should be shown for a node.
|
|
97
|
+
*
|
|
98
|
+
* @param node - The workflow node to check
|
|
99
|
+
* @returns True if external edit link should be shown
|
|
100
|
+
*/
|
|
101
|
+
export declare function shouldShowExternalEdit(node: WorkflowNode): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Determines if dynamic schema should be used for a node.
|
|
104
|
+
*
|
|
105
|
+
* @param node - The workflow node to check
|
|
106
|
+
* @returns True if dynamic schema should be fetched
|
|
107
|
+
*/
|
|
108
|
+
export declare function shouldUseDynamicSchema(node: WorkflowNode): boolean;
|