@d34dman/flowdrop 0.0.25 → 0.0.27
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/README.md +52 -62
- package/dist/components/App.svelte +12 -2
- package/dist/components/ConfigForm.svelte +500 -9
- package/dist/components/ConfigForm.svelte.d.ts +2 -0
- package/dist/components/ConfigModal.svelte +4 -70
- package/dist/components/ConfigPanel.svelte +4 -9
- package/dist/components/EdgeRefresher.svelte +41 -0
- package/dist/components/EdgeRefresher.svelte.d.ts +9 -0
- package/dist/components/ReadOnlyDetails.svelte +3 -1
- package/dist/components/UniversalNode.svelte +6 -3
- package/dist/components/WorkflowEditor.svelte +30 -0
- package/dist/components/WorkflowEditor.svelte.d.ts +3 -1
- package/dist/components/form/FormCheckboxGroup.svelte +2 -9
- package/dist/components/form/FormField.svelte +1 -12
- package/dist/components/form/FormFieldWrapper.svelte +2 -10
- package/dist/components/form/FormFieldWrapper.svelte.d.ts +1 -1
- package/dist/components/form/FormMarkdownEditor.svelte +0 -2
- package/dist/components/form/FormNumberField.svelte +5 -6
- package/dist/components/form/FormRangeField.svelte +3 -13
- package/dist/components/form/FormSelect.svelte +4 -5
- package/dist/components/form/FormSelect.svelte.d.ts +1 -1
- package/dist/components/form/FormTextField.svelte +3 -4
- package/dist/components/form/FormTextarea.svelte +3 -4
- package/dist/components/form/FormToggle.svelte +2 -3
- package/dist/components/form/index.d.ts +14 -14
- package/dist/components/form/index.js +14 -14
- package/dist/components/form/types.d.ts +2 -2
- package/dist/components/form/types.js +1 -1
- package/dist/components/nodes/NotesNode.svelte +39 -45
- package/dist/components/nodes/NotesNode.svelte.d.ts +1 -1
- package/dist/components/nodes/SimpleNode.svelte +92 -142
- package/dist/components/nodes/SquareNode.svelte +75 -58
- package/dist/components/nodes/WorkflowNode.svelte +1 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/services/dynamicSchemaService.d.ts +108 -0
- package/dist/services/dynamicSchemaService.js +445 -0
- package/dist/styles/base.css +1 -1
- package/dist/types/index.d.ts +213 -0
- package/package.json +163 -155
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { ConfigValues, NodeMetadata } from
|
|
3
|
-
import Icon from
|
|
4
|
-
import MarkdownDisplay from
|
|
2
|
+
import type { ConfigValues, NodeMetadata } from '../../types/index.js';
|
|
3
|
+
import Icon from '@iconify/svelte';
|
|
4
|
+
import MarkdownDisplay from '../MarkdownDisplay.svelte';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* NotesNode component props
|
|
@@ -25,63 +25,57 @@
|
|
|
25
25
|
}>();
|
|
26
26
|
|
|
27
27
|
/** Note content derived from config */
|
|
28
|
-
const noteContent = $derived(
|
|
29
|
-
(props.data.config?.content as string) || "Add your notes here..."
|
|
30
|
-
);
|
|
28
|
+
const noteContent = $derived((props.data.config?.content as string) || 'Add your notes here...');
|
|
31
29
|
|
|
32
30
|
/** Note type derived from config */
|
|
33
|
-
const noteType = $derived(
|
|
34
|
-
(props.data.config?.noteType as string) || "info"
|
|
35
|
-
);
|
|
31
|
+
const noteType = $derived((props.data.config?.noteType as string) || 'info');
|
|
36
32
|
|
|
37
33
|
/** Note type configuration with styling for each type */
|
|
38
34
|
const noteTypes = {
|
|
39
35
|
info: {
|
|
40
|
-
name:
|
|
41
|
-
bgColor:
|
|
42
|
-
borderColor:
|
|
43
|
-
textColor:
|
|
44
|
-
iconColor:
|
|
45
|
-
icon:
|
|
36
|
+
name: 'Info',
|
|
37
|
+
bgColor: 'bg-blue-50',
|
|
38
|
+
borderColor: 'border-blue-200',
|
|
39
|
+
textColor: 'text-blue-800',
|
|
40
|
+
iconColor: 'text-blue-500',
|
|
41
|
+
icon: 'mdi:information'
|
|
46
42
|
},
|
|
47
43
|
warning: {
|
|
48
|
-
name:
|
|
49
|
-
bgColor:
|
|
50
|
-
borderColor:
|
|
51
|
-
textColor:
|
|
52
|
-
iconColor:
|
|
53
|
-
icon:
|
|
44
|
+
name: 'Warning',
|
|
45
|
+
bgColor: 'bg-yellow-50',
|
|
46
|
+
borderColor: 'border-yellow-200',
|
|
47
|
+
textColor: 'text-yellow-800',
|
|
48
|
+
iconColor: 'text-yellow-500',
|
|
49
|
+
icon: 'mdi:alert'
|
|
54
50
|
},
|
|
55
51
|
success: {
|
|
56
|
-
name:
|
|
57
|
-
bgColor:
|
|
58
|
-
borderColor:
|
|
59
|
-
textColor:
|
|
60
|
-
iconColor:
|
|
61
|
-
icon:
|
|
52
|
+
name: 'Success',
|
|
53
|
+
bgColor: 'bg-green-50',
|
|
54
|
+
borderColor: 'border-green-200',
|
|
55
|
+
textColor: 'text-green-800',
|
|
56
|
+
iconColor: 'text-green-500',
|
|
57
|
+
icon: 'mdi:check-circle'
|
|
62
58
|
},
|
|
63
59
|
error: {
|
|
64
|
-
name:
|
|
65
|
-
bgColor:
|
|
66
|
-
borderColor:
|
|
67
|
-
textColor:
|
|
68
|
-
iconColor:
|
|
69
|
-
icon:
|
|
60
|
+
name: 'Error',
|
|
61
|
+
bgColor: 'bg-red-50',
|
|
62
|
+
borderColor: 'border-red-200',
|
|
63
|
+
textColor: 'text-red-800',
|
|
64
|
+
iconColor: 'text-red-500',
|
|
65
|
+
icon: 'mdi:close-circle'
|
|
70
66
|
},
|
|
71
67
|
note: {
|
|
72
|
-
name:
|
|
73
|
-
bgColor:
|
|
74
|
-
borderColor:
|
|
75
|
-
textColor:
|
|
76
|
-
iconColor:
|
|
77
|
-
icon:
|
|
68
|
+
name: 'Note',
|
|
69
|
+
bgColor: 'bg-gray-50',
|
|
70
|
+
borderColor: 'border-gray-200',
|
|
71
|
+
textColor: 'text-gray-800',
|
|
72
|
+
iconColor: 'text-gray-500',
|
|
73
|
+
icon: 'mdi:note-text'
|
|
78
74
|
}
|
|
79
75
|
};
|
|
80
76
|
|
|
81
77
|
/** Current note type configuration based on selected type */
|
|
82
|
-
const currentType = $derived(
|
|
83
|
-
noteTypes[noteType as keyof typeof noteTypes] || noteTypes.info
|
|
84
|
-
);
|
|
78
|
+
const currentType = $derived(noteTypes[noteType as keyof typeof noteTypes] || noteTypes.info);
|
|
85
79
|
|
|
86
80
|
/**
|
|
87
81
|
* Opens the configuration sidebar for editing note properties
|
|
@@ -89,8 +83,8 @@
|
|
|
89
83
|
function openConfigSidebar(): void {
|
|
90
84
|
if (props.data.onConfigOpen) {
|
|
91
85
|
const nodeForConfig = {
|
|
92
|
-
id: props.data.nodeId ||
|
|
93
|
-
type:
|
|
86
|
+
id: props.data.nodeId || 'unknown',
|
|
87
|
+
type: 'note',
|
|
94
88
|
data: props.data
|
|
95
89
|
};
|
|
96
90
|
props.data.onConfigOpen(nodeForConfig);
|
|
@@ -109,7 +103,7 @@
|
|
|
109
103
|
* @param event - The keyboard event
|
|
110
104
|
*/
|
|
111
105
|
function handleKeydown(event: KeyboardEvent): void {
|
|
112
|
-
if (event.key ===
|
|
106
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
113
107
|
event.preventDefault();
|
|
114
108
|
handleDoubleClick();
|
|
115
109
|
}
|
|
@@ -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 {
|
|
@@ -94,9 +94,7 @@
|
|
|
94
94
|
* Derived list of visible input ports based on hideUnconnectedHandles setting
|
|
95
95
|
* Now includes both static and dynamic inputs
|
|
96
96
|
*/
|
|
97
|
-
const visibleInputPorts = $derived(
|
|
98
|
-
allInputPorts.filter((port) => isPortVisible(port, 'input'))
|
|
99
|
-
);
|
|
97
|
+
const visibleInputPorts = $derived(allInputPorts.filter((port) => isPortVisible(port, 'input')));
|
|
100
98
|
|
|
101
99
|
/**
|
|
102
100
|
* Derived list of visible output ports based on hideUnconnectedHandles setting
|
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';
|
|
@@ -53,6 +53,8 @@ export { NodeExecutionService, nodeExecutionService } from './services/nodeExecu
|
|
|
53
53
|
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
54
54
|
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
55
55
|
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
56
|
+
export { fetchDynamicSchema, resolveExternalEditUrl, getEffectiveConfigEditOptions, clearSchemaCache, invalidateSchemaCache, hasConfigEditOptions, shouldShowExternalEdit, shouldUseDynamicSchema } from './services/dynamicSchemaService.js';
|
|
57
|
+
export type { DynamicSchemaResult } from './services/dynamicSchemaService.js';
|
|
56
58
|
export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from './services/draftStorage.js';
|
|
57
59
|
export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
|
|
58
60
|
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
|
@@ -60,6 +60,8 @@ export { NodeExecutionService, nodeExecutionService } from './services/nodeExecu
|
|
|
60
60
|
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
61
61
|
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
62
62
|
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
63
|
+
// Export dynamic schema service for config edit functionality
|
|
64
|
+
export { fetchDynamicSchema, resolveExternalEditUrl, getEffectiveConfigEditOptions, clearSchemaCache, invalidateSchemaCache, hasConfigEditOptions, shouldShowExternalEdit, shouldUseDynamicSchema } from './services/dynamicSchemaService.js';
|
|
63
65
|
// Export draft storage service
|
|
64
66
|
export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from './services/draftStorage.js';
|
|
65
67
|
// Export helpers
|