@d34dman/flowdrop 0.0.17 → 0.0.19
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 +34 -47
- package/dist/api/enhanced-client.d.ts +2 -2
- package/dist/api/enhanced-client.js +5 -5
- package/dist/components/NotesNode.svelte +3 -3
- package/dist/components/NotesNode.svelte.d.ts +3 -3
- package/dist/components/SimpleNode.svelte +3 -3
- package/dist/components/SimpleNode.svelte.d.ts +3 -3
- package/dist/components/SquareNode.svelte +3 -3
- package/dist/components/SquareNode.svelte.d.ts +3 -3
- package/dist/components/TerminalNode.svelte +583 -0
- package/dist/components/TerminalNode.svelte.d.ts +24 -0
- package/dist/components/UniversalNode.svelte +7 -33
- package/dist/components/WorkflowEditor.svelte +1 -9
- package/dist/helpers/workflowEditorHelper.d.ts +3 -3
- package/dist/helpers/workflowEditorHelper.js +5 -7
- package/dist/index.d.ts +4 -7
- package/dist/index.js +2 -5
- package/dist/registry/builtinNodes.d.ts +3 -3
- package/dist/registry/builtinNodes.js +16 -3
- package/dist/services/api.d.ts +0 -5
- package/dist/services/api.js +0 -20
- package/dist/svelte-app.d.ts +0 -6
- package/dist/svelte-app.js +0 -8
- package/dist/types/auth.d.ts +0 -15
- package/dist/types/auth.js +0 -15
- package/dist/types/config.d.ts +11 -151
- package/dist/types/config.js +3 -0
- package/dist/types/index.d.ts +2 -8
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +3 -4
- package/dist/utils/config.d.ts +0 -8
- package/dist/utils/config.js +0 -14
- package/dist/utils/connections.d.ts +0 -5
- package/dist/utils/connections.js +10 -16
- package/dist/utils/icons.js +1 -1
- package/dist/utils/nodeTypes.d.ts +1 -1
- package/dist/utils/nodeTypes.js +4 -19
- package/package.json +144 -138
- package/dist/clients/ApiClient.d.ts +0 -199
- package/dist/clients/ApiClient.js +0 -214
- package/dist/config/apiConfig.d.ts +0 -34
- package/dist/config/apiConfig.js +0 -40
- package/dist/examples/adapter-usage.d.ts +0 -66
- package/dist/examples/adapter-usage.js +0 -133
- package/dist/examples/api-client-usage.d.ts +0 -32
- package/dist/examples/api-client-usage.js +0 -243
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Terminal Node Component
|
|
3
|
+
A circular node for workflow terminal points (start, end, exit/abort)
|
|
4
|
+
Configurable via metadata to display different variants:
|
|
5
|
+
- start: Green with play icon, output-only
|
|
6
|
+
- end: Gray with stop icon, input-only
|
|
7
|
+
- exit: Red with X icon, input-only (for abort/error exits)
|
|
8
|
+
Styled with BEM syntax
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { Position, Handle } from '@xyflow/svelte';
|
|
13
|
+
import type { ConfigValues, NodeMetadata } from '../types/index.js';
|
|
14
|
+
import Icon from '@iconify/svelte';
|
|
15
|
+
import { getDataTypeColor } from '../utils/colors.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Terminal node variant types
|
|
19
|
+
*/
|
|
20
|
+
type TerminalVariant = 'start' | 'end' | 'exit';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Configuration for each terminal variant
|
|
24
|
+
*/
|
|
25
|
+
interface VariantConfig {
|
|
26
|
+
/** Default icon for this variant */
|
|
27
|
+
icon: string;
|
|
28
|
+
/** Default color for this variant */
|
|
29
|
+
color: string;
|
|
30
|
+
/** Default label for this variant */
|
|
31
|
+
label: string;
|
|
32
|
+
/** Whether this variant has input handles */
|
|
33
|
+
hasInputs: boolean;
|
|
34
|
+
/** Whether this variant has output handles */
|
|
35
|
+
hasOutputs: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Variant configurations mapping
|
|
40
|
+
*/
|
|
41
|
+
const VARIANT_CONFIGS: Record<TerminalVariant, VariantConfig> = {
|
|
42
|
+
start: {
|
|
43
|
+
icon: 'mdi:play-circle',
|
|
44
|
+
color: '#10b981',
|
|
45
|
+
label: 'Start',
|
|
46
|
+
hasInputs: false,
|
|
47
|
+
hasOutputs: true
|
|
48
|
+
},
|
|
49
|
+
end: {
|
|
50
|
+
icon: 'mdi:stop-circle',
|
|
51
|
+
color: '#6b7280',
|
|
52
|
+
label: 'End',
|
|
53
|
+
hasInputs: true,
|
|
54
|
+
hasOutputs: false
|
|
55
|
+
},
|
|
56
|
+
exit: {
|
|
57
|
+
icon: 'mdi:close-circle',
|
|
58
|
+
color: '#ef4444',
|
|
59
|
+
label: 'Exit',
|
|
60
|
+
hasInputs: true,
|
|
61
|
+
hasOutputs: false
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const props = $props<{
|
|
66
|
+
data: {
|
|
67
|
+
label: string;
|
|
68
|
+
config: ConfigValues;
|
|
69
|
+
metadata: NodeMetadata;
|
|
70
|
+
nodeId?: string;
|
|
71
|
+
onConfigOpen?: (node: {
|
|
72
|
+
id: string;
|
|
73
|
+
type: string;
|
|
74
|
+
data: { label: string; config: ConfigValues; metadata: NodeMetadata };
|
|
75
|
+
}) => void;
|
|
76
|
+
};
|
|
77
|
+
selected?: boolean;
|
|
78
|
+
isProcessing?: boolean;
|
|
79
|
+
isError?: boolean;
|
|
80
|
+
}>();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Determine terminal variant from config or metadata
|
|
84
|
+
* Priority: config.variant > metadata tag detection > default to "start"
|
|
85
|
+
*/
|
|
86
|
+
function getVariant(): TerminalVariant {
|
|
87
|
+
// Check config first
|
|
88
|
+
const configVariant = props.data.config?.variant as string | undefined;
|
|
89
|
+
if (configVariant && configVariant in VARIANT_CONFIGS) {
|
|
90
|
+
return configVariant as TerminalVariant;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check metadata tags for variant hints
|
|
94
|
+
const tags = props.data.metadata?.tags || [];
|
|
95
|
+
if (tags.includes('start') || tags.includes('entry')) {
|
|
96
|
+
return 'start';
|
|
97
|
+
}
|
|
98
|
+
if (tags.includes('exit') || tags.includes('abort') || tags.includes('error')) {
|
|
99
|
+
return 'exit';
|
|
100
|
+
}
|
|
101
|
+
if (tags.includes('end') || tags.includes('finish') || tags.includes('complete')) {
|
|
102
|
+
return 'end';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check metadata id/name for hints
|
|
106
|
+
const idLower = (props.data.metadata?.id || '').toLowerCase();
|
|
107
|
+
const nameLower = (props.data.metadata?.name || '').toLowerCase();
|
|
108
|
+
if (idLower.includes('start') || nameLower.includes('start')) {
|
|
109
|
+
return 'start';
|
|
110
|
+
}
|
|
111
|
+
if (
|
|
112
|
+
idLower.includes('exit') ||
|
|
113
|
+
idLower.includes('abort') ||
|
|
114
|
+
nameLower.includes('exit') ||
|
|
115
|
+
nameLower.includes('abort')
|
|
116
|
+
) {
|
|
117
|
+
return 'exit';
|
|
118
|
+
}
|
|
119
|
+
if (idLower.includes('end') || nameLower.includes('end')) {
|
|
120
|
+
return 'end';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Default to start
|
|
124
|
+
return 'start';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let variant = $derived(getVariant());
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get current variant configuration
|
|
131
|
+
*/
|
|
132
|
+
let variantConfig = $derived(VARIANT_CONFIGS[variant]);
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get icon - prioritize metadata/config over variant default
|
|
136
|
+
*/
|
|
137
|
+
let terminalIcon = $derived(
|
|
138
|
+
(props.data.metadata?.icon as string) ||
|
|
139
|
+
(props.data.config?.icon as string) ||
|
|
140
|
+
variantConfig.icon
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get color - prioritize metadata/config over variant default
|
|
145
|
+
*/
|
|
146
|
+
let terminalColor = $derived(
|
|
147
|
+
(props.data.metadata?.color as string) ||
|
|
148
|
+
(props.data.config?.color as string) ||
|
|
149
|
+
variantConfig.color
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get display label
|
|
154
|
+
*/
|
|
155
|
+
let displayLabel = $derived(props.data.label || props.data.metadata?.name || variantConfig.label);
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if metadata explicitly defines inputs (including empty array)
|
|
159
|
+
* This allows API to control ports:
|
|
160
|
+
* - undefined: use variant default
|
|
161
|
+
* - []: explicitly no inputs
|
|
162
|
+
* - [{...}]: use these inputs
|
|
163
|
+
*/
|
|
164
|
+
let hasExplicitInputs = $derived(Array.isArray(props.data.metadata?.inputs));
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if metadata explicitly defines outputs (including empty array)
|
|
168
|
+
*/
|
|
169
|
+
let hasExplicitOutputs = $derived(Array.isArray(props.data.metadata?.outputs));
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Default trigger input port for end/exit nodes
|
|
173
|
+
*/
|
|
174
|
+
const DEFAULT_INPUT_PORT = {
|
|
175
|
+
id: 'trigger',
|
|
176
|
+
name: 'Trigger',
|
|
177
|
+
type: 'input' as const,
|
|
178
|
+
dataType: 'trigger',
|
|
179
|
+
description: 'Workflow trigger input'
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Default trigger output port for start nodes
|
|
184
|
+
*/
|
|
185
|
+
const DEFAULT_OUTPUT_PORT = {
|
|
186
|
+
id: 'trigger',
|
|
187
|
+
name: 'Trigger',
|
|
188
|
+
type: 'output' as const,
|
|
189
|
+
dataType: 'trigger',
|
|
190
|
+
description: 'Workflow trigger output'
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get input ports from metadata or create default trigger input
|
|
195
|
+
* Priority:
|
|
196
|
+
* 1. If metadata.inputs is defined (even empty array), use it exactly
|
|
197
|
+
* 2. Otherwise, use variant default (trigger port for end/exit)
|
|
198
|
+
*/
|
|
199
|
+
let inputPorts = $derived(
|
|
200
|
+
hasExplicitInputs
|
|
201
|
+
? props.data.metadata.inputs
|
|
202
|
+
: variantConfig.hasInputs
|
|
203
|
+
? [DEFAULT_INPUT_PORT]
|
|
204
|
+
: []
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get output ports from metadata or create default trigger output
|
|
209
|
+
* Priority:
|
|
210
|
+
* 1. If metadata.outputs is defined (even empty array), use it exactly
|
|
211
|
+
* 2. Otherwise, use variant default (trigger port for start)
|
|
212
|
+
*/
|
|
213
|
+
let outputPorts = $derived(
|
|
214
|
+
hasExplicitOutputs
|
|
215
|
+
? props.data.metadata.outputs
|
|
216
|
+
: variantConfig.hasOutputs
|
|
217
|
+
? [DEFAULT_OUTPUT_PORT]
|
|
218
|
+
: []
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Determine if we should show inputs based on ports
|
|
223
|
+
*/
|
|
224
|
+
let showInputs = $derived(inputPorts.length > 0);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Determine if we should show outputs based on ports
|
|
228
|
+
*/
|
|
229
|
+
let showOutputs = $derived(outputPorts.length > 0);
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Handle configuration sidebar - using global ConfigSidebar
|
|
233
|
+
*/
|
|
234
|
+
function openConfigSidebar(): void {
|
|
235
|
+
if (props.data.onConfigOpen) {
|
|
236
|
+
const nodeForConfig = {
|
|
237
|
+
id: props.data.nodeId || 'unknown',
|
|
238
|
+
type: 'terminal',
|
|
239
|
+
data: props.data
|
|
240
|
+
};
|
|
241
|
+
props.data.onConfigOpen(nodeForConfig);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Handle double-click to open config
|
|
247
|
+
*/
|
|
248
|
+
function handleDoubleClick(): void {
|
|
249
|
+
openConfigSidebar();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handle single click - only handle selection
|
|
254
|
+
*/
|
|
255
|
+
function handleClick(): void {
|
|
256
|
+
// Node selection is handled by Svelte Flow
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Handle keyboard events for accessibility
|
|
261
|
+
*/
|
|
262
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
263
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
handleDoubleClick();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
</script>
|
|
269
|
+
|
|
270
|
+
<!-- Terminal Node -->
|
|
271
|
+
<div
|
|
272
|
+
class="flowdrop-terminal-node"
|
|
273
|
+
class:flowdrop-terminal-node--selected={props.selected}
|
|
274
|
+
class:flowdrop-terminal-node--processing={props.isProcessing}
|
|
275
|
+
class:flowdrop-terminal-node--error={props.isError}
|
|
276
|
+
class:flowdrop-terminal-node--start={variant === 'start'}
|
|
277
|
+
class:flowdrop-terminal-node--end={variant === 'end'}
|
|
278
|
+
class:flowdrop-terminal-node--exit={variant === 'exit'}
|
|
279
|
+
style="--terminal-color: {terminalColor};"
|
|
280
|
+
onclick={handleClick}
|
|
281
|
+
ondblclick={handleDoubleClick}
|
|
282
|
+
onkeydown={handleKeydown}
|
|
283
|
+
role="button"
|
|
284
|
+
tabindex="0"
|
|
285
|
+
aria-label="{variant} node: {displayLabel}"
|
|
286
|
+
>
|
|
287
|
+
<!-- Config button at top -->
|
|
288
|
+
<button
|
|
289
|
+
class="flowdrop-terminal-node__config-btn"
|
|
290
|
+
onclick={openConfigSidebar}
|
|
291
|
+
title="Configure node"
|
|
292
|
+
>
|
|
293
|
+
<Icon icon="mdi:cog" />
|
|
294
|
+
</button>
|
|
295
|
+
|
|
296
|
+
<!-- Circle wrapper for proper handle positioning -->
|
|
297
|
+
<div class="flowdrop-terminal-node__circle-wrapper">
|
|
298
|
+
<!-- Input Handles (for end/exit variants) -->
|
|
299
|
+
{#if showInputs}
|
|
300
|
+
{#each inputPorts as port (port.id)}
|
|
301
|
+
<Handle
|
|
302
|
+
type="target"
|
|
303
|
+
position={Position.Left}
|
|
304
|
+
style="background-color: {getDataTypeColor(
|
|
305
|
+
port.dataType
|
|
306
|
+
)}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
|
|
307
|
+
id={`${props.data.nodeId}-input-${port.id}`}
|
|
308
|
+
/>
|
|
309
|
+
{/each}
|
|
310
|
+
{/if}
|
|
311
|
+
|
|
312
|
+
<!-- Circular content with icon -->
|
|
313
|
+
<div class="flowdrop-terminal-node__content">
|
|
314
|
+
<Icon
|
|
315
|
+
icon={terminalIcon}
|
|
316
|
+
class="flowdrop-terminal-node__icon"
|
|
317
|
+
style="color: {terminalColor}; font-size: 2.5rem;"
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<!-- Output Handles (for start variant) -->
|
|
322
|
+
{#if showOutputs}
|
|
323
|
+
{#each outputPorts as port (port.id)}
|
|
324
|
+
<Handle
|
|
325
|
+
type="source"
|
|
326
|
+
position={Position.Right}
|
|
327
|
+
id={`${props.data.nodeId}-output-${port.id}`}
|
|
328
|
+
style="background-color: {getDataTypeColor(
|
|
329
|
+
port.dataType
|
|
330
|
+
)}; border-color: #ffffff; top: 50%; transform: translateY(-50%); z-index: 30;"
|
|
331
|
+
/>
|
|
332
|
+
{/each}
|
|
333
|
+
{/if}
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<!-- Label below the circle -->
|
|
337
|
+
<div class="flowdrop-terminal-node__label">
|
|
338
|
+
{displayLabel}
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<!-- Processing indicator -->
|
|
342
|
+
{#if props.isProcessing}
|
|
343
|
+
<div class="flowdrop-terminal-node__processing">
|
|
344
|
+
<div class="flowdrop-terminal-node__spinner"></div>
|
|
345
|
+
</div>
|
|
346
|
+
{/if}
|
|
347
|
+
|
|
348
|
+
<!-- Error indicator -->
|
|
349
|
+
{#if props.isError}
|
|
350
|
+
<div class="flowdrop-terminal-node__error">
|
|
351
|
+
<Icon icon="mdi:alert-circle" class="flowdrop-terminal-node__error-icon" />
|
|
352
|
+
</div>
|
|
353
|
+
{/if}
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<style>
|
|
357
|
+
.flowdrop-terminal-node {
|
|
358
|
+
position: relative;
|
|
359
|
+
display: flex;
|
|
360
|
+
flex-direction: column;
|
|
361
|
+
align-items: center;
|
|
362
|
+
gap: 0.5rem;
|
|
363
|
+
cursor: pointer;
|
|
364
|
+
transition: all 0.2s ease-in-out;
|
|
365
|
+
z-index: 10;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Wrapper for circle and handles - ensures handles are vertically centered to circle */
|
|
369
|
+
.flowdrop-terminal-node__circle-wrapper {
|
|
370
|
+
position: relative;
|
|
371
|
+
display: flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.flowdrop-terminal-node__content {
|
|
377
|
+
width: 72px;
|
|
378
|
+
height: 72px;
|
|
379
|
+
background-color: #ffffff;
|
|
380
|
+
border: 3px solid var(--terminal-color, #6b7280);
|
|
381
|
+
border-radius: 50%;
|
|
382
|
+
display: flex;
|
|
383
|
+
align-items: center;
|
|
384
|
+
justify-content: center;
|
|
385
|
+
box-shadow:
|
|
386
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
387
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
388
|
+
transition: all 0.2s ease-in-out;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.flowdrop-terminal-node:hover .flowdrop-terminal-node__content {
|
|
392
|
+
box-shadow:
|
|
393
|
+
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
394
|
+
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
395
|
+
transform: scale(1.05);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.flowdrop-terminal-node--selected .flowdrop-terminal-node__content {
|
|
399
|
+
box-shadow:
|
|
400
|
+
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
401
|
+
0 0 0 3px rgba(59, 130, 246, 0.5);
|
|
402
|
+
border-color: #3b82f6;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.flowdrop-terminal-node--processing .flowdrop-terminal-node__content {
|
|
406
|
+
opacity: 0.7;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.flowdrop-terminal-node--error .flowdrop-terminal-node__content {
|
|
410
|
+
border-color: #ef4444 !important;
|
|
411
|
+
background-color: #fef2f2 !important;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* Variant-specific glow effects */
|
|
415
|
+
.flowdrop-terminal-node--start .flowdrop-terminal-node__content {
|
|
416
|
+
box-shadow:
|
|
417
|
+
0 4px 6px -1px rgba(16, 185, 129, 0.2),
|
|
418
|
+
0 2px 4px -1px rgba(16, 185, 129, 0.1);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.flowdrop-terminal-node--start:hover .flowdrop-terminal-node__content {
|
|
422
|
+
box-shadow:
|
|
423
|
+
0 10px 15px -3px rgba(16, 185, 129, 0.3),
|
|
424
|
+
0 4px 6px -2px rgba(16, 185, 129, 0.15);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.flowdrop-terminal-node--exit .flowdrop-terminal-node__content {
|
|
428
|
+
box-shadow:
|
|
429
|
+
0 4px 6px -1px rgba(239, 68, 68, 0.2),
|
|
430
|
+
0 2px 4px -1px rgba(239, 68, 68, 0.1);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.flowdrop-terminal-node--exit:hover .flowdrop-terminal-node__content {
|
|
434
|
+
box-shadow:
|
|
435
|
+
0 10px 15px -3px rgba(239, 68, 68, 0.3),
|
|
436
|
+
0 4px 6px -2px rgba(239, 68, 68, 0.15);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
:global(.flowdrop-terminal-node__icon) {
|
|
440
|
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.flowdrop-terminal-node__label {
|
|
444
|
+
font-size: 0.75rem;
|
|
445
|
+
font-weight: 500;
|
|
446
|
+
color: #374151;
|
|
447
|
+
text-align: center;
|
|
448
|
+
max-width: 100px;
|
|
449
|
+
overflow: hidden;
|
|
450
|
+
text-overflow: ellipsis;
|
|
451
|
+
white-space: nowrap;
|
|
452
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
453
|
+
padding: 0.125rem 0.5rem;
|
|
454
|
+
border-radius: 0.25rem;
|
|
455
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.flowdrop-terminal-node__processing {
|
|
459
|
+
position: absolute;
|
|
460
|
+
top: 1.5rem;
|
|
461
|
+
right: 0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.flowdrop-terminal-node__spinner {
|
|
465
|
+
width: 14px;
|
|
466
|
+
height: 14px;
|
|
467
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
468
|
+
border-top: 2px solid var(--terminal-color, #6b7280);
|
|
469
|
+
border-radius: 50%;
|
|
470
|
+
animation: spin 1s linear infinite;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.flowdrop-terminal-node__error {
|
|
474
|
+
position: absolute;
|
|
475
|
+
top: 1.5rem;
|
|
476
|
+
right: 0;
|
|
477
|
+
color: #ef4444;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
:global(.flowdrop-terminal-node__error-icon) {
|
|
481
|
+
width: 14px;
|
|
482
|
+
height: 14px;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* Config button positioned at top center */
|
|
486
|
+
.flowdrop-terminal-node__config-btn {
|
|
487
|
+
position: absolute;
|
|
488
|
+
top: -1.5rem;
|
|
489
|
+
left: 50%;
|
|
490
|
+
transform: translateX(-50%);
|
|
491
|
+
width: 1.5rem;
|
|
492
|
+
height: 1.5rem;
|
|
493
|
+
background-color: rgba(255, 255, 255, 0.95);
|
|
494
|
+
border: 1px solid #e5e7eb;
|
|
495
|
+
border-radius: 50%;
|
|
496
|
+
color: #6b7280;
|
|
497
|
+
cursor: pointer;
|
|
498
|
+
display: flex;
|
|
499
|
+
align-items: center;
|
|
500
|
+
justify-content: center;
|
|
501
|
+
opacity: 0;
|
|
502
|
+
transition: all 0.2s ease-in-out;
|
|
503
|
+
backdrop-filter: blur(4px);
|
|
504
|
+
z-index: 15;
|
|
505
|
+
font-size: 0.75rem;
|
|
506
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.flowdrop-terminal-node:hover .flowdrop-terminal-node__config-btn {
|
|
510
|
+
opacity: 1;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.flowdrop-terminal-node__config-btn:hover {
|
|
514
|
+
background-color: #f9fafb;
|
|
515
|
+
border-color: #d1d5db;
|
|
516
|
+
color: #374151;
|
|
517
|
+
transform: translateX(-50%) scale(1.1);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
@keyframes spin {
|
|
521
|
+
to {
|
|
522
|
+
transform: rotate(360deg);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/* Handle styles - positioned relative to circle wrapper */
|
|
527
|
+
:global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle) {
|
|
528
|
+
width: 16px !important;
|
|
529
|
+
height: 16px !important;
|
|
530
|
+
border-radius: 50% !important;
|
|
531
|
+
border: 2px solid #ffffff !important;
|
|
532
|
+
transition: all 0.2s ease-in-out !important;
|
|
533
|
+
cursor: pointer !important;
|
|
534
|
+
z-index: 20 !important;
|
|
535
|
+
pointer-events: auto !important;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
:global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle-left) {
|
|
539
|
+
left: -8px !important;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
:global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle-right) {
|
|
543
|
+
right: -8px !important;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
:global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:hover) {
|
|
547
|
+
transform: translateY(-50%) scale(1.2) !important;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
:global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:focus) {
|
|
551
|
+
outline: 2px solid #3b82f6 !important;
|
|
552
|
+
outline-offset: 2px !important;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* Also keep node-level handle styles for fallback */
|
|
556
|
+
:global(.svelte-flow__node-terminal .svelte-flow__handle) {
|
|
557
|
+
width: 16px !important;
|
|
558
|
+
height: 16px !important;
|
|
559
|
+
border-radius: 50% !important;
|
|
560
|
+
border: 2px solid #ffffff !important;
|
|
561
|
+
transition: all 0.2s ease-in-out !important;
|
|
562
|
+
cursor: pointer !important;
|
|
563
|
+
z-index: 20 !important;
|
|
564
|
+
pointer-events: auto !important;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
:global(.svelte-flow__node-terminal .svelte-flow__handle-left) {
|
|
568
|
+
left: -8px !important;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
:global(.svelte-flow__node-terminal .svelte-flow__handle-right) {
|
|
572
|
+
right: -8px !important;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
:global(.svelte-flow__node-terminal .svelte-flow__handle:hover) {
|
|
576
|
+
transform: translateY(-50%) scale(1.2) !important;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
:global(.svelte-flow__node-terminal .svelte-flow__handle:focus) {
|
|
580
|
+
outline: 2px solid #3b82f6 !important;
|
|
581
|
+
outline-offset: 2px !important;
|
|
582
|
+
}
|
|
583
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ConfigValues, NodeMetadata } from '../types/index.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: {
|
|
4
|
+
label: string;
|
|
5
|
+
config: ConfigValues;
|
|
6
|
+
metadata: NodeMetadata;
|
|
7
|
+
nodeId?: string;
|
|
8
|
+
onConfigOpen?: (node: {
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
data: {
|
|
12
|
+
label: string;
|
|
13
|
+
config: ConfigValues;
|
|
14
|
+
metadata: NodeMetadata;
|
|
15
|
+
};
|
|
16
|
+
}) => void;
|
|
17
|
+
};
|
|
18
|
+
selected?: boolean;
|
|
19
|
+
isProcessing?: boolean;
|
|
20
|
+
isError?: boolean;
|
|
21
|
+
};
|
|
22
|
+
declare const TerminalNode: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
23
|
+
type TerminalNode = ReturnType<typeof TerminalNode>;
|
|
24
|
+
export default TerminalNode;
|
|
@@ -15,15 +15,6 @@
|
|
|
15
15
|
import { shouldShowNodeStatus } from '../utils/nodeWrapper.js';
|
|
16
16
|
import { resolveComponentName } from '../utils/nodeTypes.js';
|
|
17
17
|
|
|
18
|
-
// Fallback components for when registry is not available
|
|
19
|
-
// These are only used as last-resort fallbacks
|
|
20
|
-
import WorkflowNodeComponent from './WorkflowNode.svelte';
|
|
21
|
-
import NotesNode from './NotesNode.svelte';
|
|
22
|
-
import SimpleNode from './SimpleNode.svelte';
|
|
23
|
-
import SquareNode from './SquareNode.svelte';
|
|
24
|
-
import ToolNode from './ToolNode.svelte';
|
|
25
|
-
import GatewayNode from './GatewayNode.svelte';
|
|
26
|
-
|
|
27
18
|
let {
|
|
28
19
|
data,
|
|
29
20
|
selected = false
|
|
@@ -52,7 +43,6 @@
|
|
|
52
43
|
|
|
53
44
|
/**
|
|
54
45
|
* Get the node component from the registry.
|
|
55
|
-
* Falls back to built-in components if registry lookup fails.
|
|
56
46
|
*/
|
|
57
47
|
let nodeComponent = $derived(getNodeComponent(resolvedComponentName));
|
|
58
48
|
|
|
@@ -70,8 +60,7 @@
|
|
|
70
60
|
);
|
|
71
61
|
|
|
72
62
|
/**
|
|
73
|
-
* Get the node component for the given type.
|
|
74
|
-
* First tries the registry, then falls back to hardcoded components.
|
|
63
|
+
* Get the node component for the given type from the registry.
|
|
75
64
|
*
|
|
76
65
|
* @param nodeType - The node type identifier
|
|
77
66
|
* @returns The Svelte component to render
|
|
@@ -80,29 +69,14 @@
|
|
|
80
69
|
// Resolve any aliases (e.g., "default" -> "workflowNode")
|
|
81
70
|
const resolvedType = resolveBuiltinAlias(nodeType);
|
|
82
71
|
|
|
83
|
-
//
|
|
84
|
-
const
|
|
85
|
-
if (
|
|
86
|
-
return
|
|
72
|
+
// Get component from registry (defaults to workflowNode if not found)
|
|
73
|
+
const component = nodeComponentRegistry.getComponent(resolvedType);
|
|
74
|
+
if (component) {
|
|
75
|
+
return component;
|
|
87
76
|
}
|
|
88
77
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
switch (resolvedType) {
|
|
92
|
-
case 'note':
|
|
93
|
-
return NotesNode;
|
|
94
|
-
case 'simple':
|
|
95
|
-
return SimpleNode;
|
|
96
|
-
case 'square':
|
|
97
|
-
return SquareNode;
|
|
98
|
-
case 'tool':
|
|
99
|
-
return ToolNode;
|
|
100
|
-
case 'gateway':
|
|
101
|
-
return GatewayNode;
|
|
102
|
-
case 'workflowNode':
|
|
103
|
-
default:
|
|
104
|
-
return WorkflowNodeComponent;
|
|
105
|
-
}
|
|
78
|
+
// Return the default component from registry
|
|
79
|
+
return nodeComponentRegistry.getComponent('workflowNode');
|
|
106
80
|
}
|
|
107
81
|
|
|
108
82
|
/**
|
|
@@ -244,16 +244,8 @@
|
|
|
244
244
|
|
|
245
245
|
// Node types for Svelte Flow - using UniversalNode for all node types
|
|
246
246
|
// All nodes use 'universalNode' type, and UniversalNode handles internal switching
|
|
247
|
-
// Include legacy types for backward compatibility with existing workflows
|
|
248
247
|
const nodeTypes = {
|
|
249
|
-
universalNode: UniversalNode
|
|
250
|
-
// Legacy types for backward compatibility
|
|
251
|
-
workflowNode: UniversalNode,
|
|
252
|
-
note: UniversalNode,
|
|
253
|
-
simple: UniversalNode,
|
|
254
|
-
square: UniversalNode,
|
|
255
|
-
tool: UniversalNode,
|
|
256
|
-
gateway: UniversalNode
|
|
248
|
+
universalNode: UniversalNode
|
|
257
249
|
};
|
|
258
250
|
|
|
259
251
|
// Handle arrows in our custom connection handler
|
|
@@ -24,10 +24,10 @@ export declare class EdgeStylingHelper {
|
|
|
24
24
|
/**
|
|
25
25
|
* Extract the port ID from a handle ID
|
|
26
26
|
* Supports two formats:
|
|
27
|
-
* 1.
|
|
28
|
-
* 2.
|
|
27
|
+
* 1. Standard format: "${nodeId}-output-${portId}" or "${nodeId}-input-${portId}"
|
|
28
|
+
* 2. Short format: just the portId (e.g., "text", "trigger")
|
|
29
29
|
* @param handleId - The handle ID string (e.g., "sample-node.1-output-trigger" or "trigger")
|
|
30
|
-
* @returns The port ID (e.g., "trigger") or the handleId itself for
|
|
30
|
+
* @returns The port ID (e.g., "trigger") or the handleId itself for short format
|
|
31
31
|
*/
|
|
32
32
|
static extractPortIdFromHandle(handleId: string | undefined): string | null;
|
|
33
33
|
/**
|