@d34dman/flowdrop 0.0.1 → 0.0.2
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 +307 -215
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +30 -30
- package/dist/api/client.d.ts +24 -1
- package/dist/api/client.js +55 -38
- package/dist/api/enhanced-client.d.ts +46 -0
- package/dist/api/enhanced-client.js +211 -0
- package/dist/clients/ApiClient.d.ts +19 -23
- package/dist/clients/ApiClient.js +36 -34
- package/dist/components/App.svelte +1299 -230
- package/dist/components/App.svelte.d.ts +21 -1
- package/dist/components/CanvasBanner.svelte +50 -44
- package/dist/components/CanvasBanner.svelte.d.ts +5 -19
- package/dist/components/ConfigForm.svelte +555 -0
- package/dist/components/ConfigForm.svelte.d.ts +32 -0
- package/dist/components/ConfigModal.svelte +261 -0
- package/dist/components/ConfigModal.svelte.d.ts +31 -0
- package/dist/components/ConfigSidebar.svelte +934 -0
- package/dist/components/ConfigSidebar.svelte.d.ts +51 -0
- package/dist/components/ConnectionLine.svelte +32 -0
- package/dist/components/ConnectionLine.svelte.d.ts +3 -0
- package/dist/components/GatewayNode.svelte +471 -0
- package/dist/components/GatewayNode.svelte.d.ts +15 -0
- package/dist/components/LoadingSpinner.svelte +23 -23
- package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
- package/dist/components/Logo.svelte +82 -0
- package/dist/components/Logo.svelte.d.ts +26 -0
- package/dist/components/LogsSidebar.svelte +565 -0
- package/dist/components/LogsSidebar.svelte.d.ts +34 -0
- package/dist/components/MarkdownDisplay.svelte +28 -0
- package/dist/components/MarkdownDisplay.svelte.d.ts +7 -0
- package/dist/components/Navbar.svelte +663 -0
- package/dist/components/Navbar.svelte.d.ts +21 -0
- package/dist/components/NodeSidebar.svelte +629 -488
- package/dist/components/NodeSidebar.svelte.d.ts +1 -2
- package/dist/components/NodeStatusOverlay.svelte +327 -0
- package/dist/components/NodeStatusOverlay.svelte.d.ts +11 -0
- package/dist/components/NotesNode.svelte +566 -0
- package/dist/components/NotesNode.svelte.d.ts +43 -0
- package/dist/components/PipelineStatus.svelte +331 -0
- package/dist/components/PipelineStatus.svelte.d.ts +18 -0
- package/dist/components/SimpleNode.svelte +447 -0
- package/dist/components/SimpleNode.svelte.d.ts +24 -0
- package/dist/components/SquareNode.svelte +346 -0
- package/dist/components/SquareNode.svelte.d.ts +24 -0
- package/dist/components/StatusIcon.svelte +112 -0
- package/dist/components/StatusIcon.svelte.d.ts +10 -0
- package/dist/components/StatusLabel.svelte +33 -0
- package/dist/components/StatusLabel.svelte.d.ts +7 -0
- package/dist/components/ToolNode.svelte +385 -0
- package/dist/components/ToolNode.svelte.d.ts +36 -0
- package/dist/components/UniversalNode.svelte +126 -0
- package/dist/components/UniversalNode.svelte.d.ts +15 -0
- package/dist/components/WorkflowEditor.svelte +871 -528
- package/dist/components/WorkflowEditor.svelte.d.ts +15 -5
- package/dist/components/WorkflowNode.svelte +428 -542
- package/dist/components/WorkflowNode.svelte.d.ts +7 -3
- package/dist/config/apiConfig.d.ts +33 -0
- package/dist/config/apiConfig.js +39 -0
- package/dist/config/defaultPortConfig.d.ts +6 -0
- package/dist/config/defaultPortConfig.js +192 -0
- package/dist/config/demo.d.ts +58 -0
- package/dist/config/demo.js +142 -0
- package/dist/config/endpoints.d.ts +106 -0
- package/dist/config/endpoints.js +128 -0
- package/dist/data/samples.d.ts +38 -4
- package/dist/data/samples.js +2789 -737
- package/dist/examples/adapter-usage.d.ts +4 -4
- package/dist/examples/adapter-usage.js +21 -26
- package/dist/examples/api-client-usage.d.ts +6 -6
- package/dist/examples/api-client-usage.js +55 -54
- package/dist/index.d.ts +23 -15
- package/dist/index.js +23 -15
- package/dist/mocks/app-environment.d.ts +8 -0
- package/dist/mocks/app-environment.js +16 -0
- package/dist/mocks/app-forms.d.ts +2 -0
- package/dist/mocks/app-forms.js +21 -0
- package/dist/mocks/app-navigation.d.ts +5 -0
- package/dist/mocks/app-navigation.js +34 -0
- package/dist/mocks/app-stores.d.ts +14 -0
- package/dist/mocks/app-stores.js +26 -0
- package/dist/services/api.d.ts +13 -3
- package/dist/services/api.js +91 -36
- package/dist/services/globalSave.d.ts +20 -0
- package/dist/services/globalSave.js +165 -0
- package/dist/services/nodeExecutionService.d.ts +63 -0
- package/dist/services/nodeExecutionService.js +261 -0
- package/dist/services/portConfigApi.d.ts +14 -0
- package/dist/services/portConfigApi.js +69 -0
- package/dist/services/toastService.d.ts +147 -0
- package/dist/services/toastService.js +235 -0
- package/dist/services/workflowStorage.d.ts +2 -2
- package/dist/services/workflowStorage.js +10 -10
- package/dist/stores/workflowStore.d.ts +53 -0
- package/dist/stores/workflowStore.js +264 -0
- package/dist/styles/base.css +896 -363
- package/dist/svelte-app.d.ts +52 -5
- package/dist/svelte-app.js +128 -6
- package/dist/types/config.d.ts +291 -0
- package/dist/types/config.js +4 -0
- package/dist/types/index.d.ts +231 -19
- package/dist/types/index.js +1 -1
- package/dist/utils/colors.d.ts +67 -33
- package/dist/utils/colors.js +183 -118
- package/dist/utils/config.d.ts +41 -0
- package/dist/utils/config.js +248 -0
- package/dist/utils/connections.d.ts +40 -3
- package/dist/utils/connections.js +115 -44
- package/dist/utils/icons.d.ts +1 -1
- package/dist/utils/icons.js +71 -70
- package/dist/utils/nodeStatus.d.ts +53 -0
- package/dist/utils/nodeStatus.js +183 -0
- package/dist/utils/nodeTypes.d.ts +57 -0
- package/dist/utils/nodeTypes.js +109 -0
- package/dist/utils/nodeWrapper.d.ts +39 -0
- package/dist/utils/nodeWrapper.js +62 -0
- package/package.json +129 -97
- package/dist/components/Node.svelte +0 -38
- package/dist/components/Node.svelte.d.ts +0 -4
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Tool Node Component
|
|
3
|
+
A specialized node for tools with metadata port
|
|
4
|
+
Styled with BEM syntax
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { Position, Handle } from '@xyflow/svelte';
|
|
9
|
+
import Icon from '@iconify/svelte';
|
|
10
|
+
import { getDataTypeColor } from '../utils/colors';
|
|
11
|
+
import type { NodeMetadata } from '../types/index.js';
|
|
12
|
+
|
|
13
|
+
interface ToolNodeParameter {
|
|
14
|
+
name: string;
|
|
15
|
+
type?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const props = $props<{
|
|
20
|
+
data: {
|
|
21
|
+
label: string;
|
|
22
|
+
config: {
|
|
23
|
+
icon?: string;
|
|
24
|
+
color?: string;
|
|
25
|
+
toolName?: string;
|
|
26
|
+
toolDescription?: string;
|
|
27
|
+
toolVersion?: string;
|
|
28
|
+
parameters?: ToolNodeParameter[];
|
|
29
|
+
};
|
|
30
|
+
metadata: NodeMetadata;
|
|
31
|
+
nodeId?: string;
|
|
32
|
+
onConfigOpen?: (node: {
|
|
33
|
+
id: string;
|
|
34
|
+
type: string;
|
|
35
|
+
data: { label: string; config: Record<string, unknown>; metadata: NodeMetadata };
|
|
36
|
+
}) => void;
|
|
37
|
+
};
|
|
38
|
+
selected?: boolean;
|
|
39
|
+
isProcessing?: boolean;
|
|
40
|
+
isError?: boolean;
|
|
41
|
+
}>();
|
|
42
|
+
|
|
43
|
+
// Prioritize metadata over config for tool nodes (metadata is the node definition)
|
|
44
|
+
let toolIcon = $derived(
|
|
45
|
+
(props.data.metadata?.icon as string) || (props.data.config?.icon as string) || 'mdi:tools'
|
|
46
|
+
);
|
|
47
|
+
let toolColor = $derived(
|
|
48
|
+
(props.data.metadata?.color as string) || (props.data.config?.color as string) || '#f59e0b'
|
|
49
|
+
);
|
|
50
|
+
let toolName = $derived(
|
|
51
|
+
(props.data.metadata?.name as string) ||
|
|
52
|
+
(props.data.config?.toolName as string) ||
|
|
53
|
+
props.data.label ||
|
|
54
|
+
'Tool'
|
|
55
|
+
);
|
|
56
|
+
let toolDescription = $derived(
|
|
57
|
+
(props.data.metadata?.description as string) ||
|
|
58
|
+
(props.data.config?.toolDescription as string) ||
|
|
59
|
+
'A configurable tool for agents'
|
|
60
|
+
);
|
|
61
|
+
let toolVersion = $derived(
|
|
62
|
+
(props.data.metadata?.version as string) ||
|
|
63
|
+
(props.data.config?.toolVersion as string) ||
|
|
64
|
+
'1.0.0'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Check for tool interface ports in metadata
|
|
68
|
+
let hasToolInputPort = $derived(
|
|
69
|
+
props.data.metadata?.inputs?.some((port) => port.dataType === 'tool') || false
|
|
70
|
+
);
|
|
71
|
+
let hasToolOutputPort = $derived(
|
|
72
|
+
props.data.metadata?.outputs?.some((port) => port.dataType === 'tool') || false
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Get the actual tool ports for proper handle generation
|
|
76
|
+
let toolInputPort = $derived(
|
|
77
|
+
props.data.metadata?.inputs?.find((port) => port.dataType === 'tool')
|
|
78
|
+
);
|
|
79
|
+
let toolOutputPort = $derived(
|
|
80
|
+
props.data.metadata?.outputs?.find((port) => port.dataType === 'tool')
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Handle configuration sidebar - using global ConfigSidebar
|
|
84
|
+
function openConfigSidebar(): void {
|
|
85
|
+
if (props.data.onConfigOpen) {
|
|
86
|
+
// Create a WorkflowNodeType-like object for the global ConfigSidebar
|
|
87
|
+
const nodeForConfig = {
|
|
88
|
+
id: props.data.nodeId || 'unknown',
|
|
89
|
+
type: 'tool',
|
|
90
|
+
data: props.data
|
|
91
|
+
};
|
|
92
|
+
props.data.onConfigOpen(nodeForConfig);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle double-click to open config
|
|
97
|
+
function handleDoubleClick(): void {
|
|
98
|
+
openConfigSidebar();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Handle click events
|
|
102
|
+
function handleClick(): void {
|
|
103
|
+
// Node selection is handled by Svelte Flow
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle keyboard events for accessibility
|
|
107
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
108
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
handleDoubleClick();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<!-- Tool Input Handle (optional) -->
|
|
116
|
+
{#if hasToolInputPort && toolInputPort}
|
|
117
|
+
<Handle
|
|
118
|
+
type="target"
|
|
119
|
+
position={Position.Left}
|
|
120
|
+
id={`${props.data.nodeId}-input-${toolInputPort.id}`}
|
|
121
|
+
style="background-color: {getDataTypeColor('tool')}; border-color: '#ffffff';"
|
|
122
|
+
/>
|
|
123
|
+
{/if}
|
|
124
|
+
|
|
125
|
+
<!-- Tool Node -->
|
|
126
|
+
<div
|
|
127
|
+
class="flowdrop-tool-node"
|
|
128
|
+
class:flowdrop-tool-node--selected={props.selected}
|
|
129
|
+
class:flowdrop-tool-node--processing={props.isProcessing}
|
|
130
|
+
class:flowdrop-tool-node--error={props.isError}
|
|
131
|
+
onclick={handleClick}
|
|
132
|
+
ondblclick={handleDoubleClick}
|
|
133
|
+
onkeydown={handleKeydown}
|
|
134
|
+
role="button"
|
|
135
|
+
tabindex="0"
|
|
136
|
+
>
|
|
137
|
+
<!-- Node Header -->
|
|
138
|
+
<div class="flowdrop-tool-node__header">
|
|
139
|
+
<div class="flowdrop-tool-node__header-content">
|
|
140
|
+
<!-- Tool Icon -->
|
|
141
|
+
<div class="flowdrop-tool-node__icon-container" style="background-color: {toolColor}">
|
|
142
|
+
<Icon icon={toolIcon} class="flowdrop-tool-node__icon" />
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<!-- Tool Info -->
|
|
146
|
+
<div class="flowdrop-tool-node__info">
|
|
147
|
+
<h3 class="flowdrop-tool-node__title">
|
|
148
|
+
{toolName}
|
|
149
|
+
</h3>
|
|
150
|
+
<div class="flowdrop-tool-node__version">
|
|
151
|
+
v{toolVersion}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Tool Badge -->
|
|
156
|
+
<div class="flowdrop-tool-node__badge">TOOL</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Tool Description -->
|
|
160
|
+
<p class="flowdrop-tool-node__description">
|
|
161
|
+
{toolDescription}
|
|
162
|
+
</p>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Processing indicator -->
|
|
166
|
+
{#if props.isProcessing}
|
|
167
|
+
<div class="flowdrop-tool-node__processing">
|
|
168
|
+
<div class="flowdrop-tool-node__spinner"></div>
|
|
169
|
+
</div>
|
|
170
|
+
{/if}
|
|
171
|
+
|
|
172
|
+
<!-- Error indicator -->
|
|
173
|
+
{#if props.isError}
|
|
174
|
+
<div class="flowdrop-tool-node__error">
|
|
175
|
+
<Icon icon="mdi:alert-circle" class="flowdrop-tool-node__error-icon" />
|
|
176
|
+
</div>
|
|
177
|
+
{/if}
|
|
178
|
+
|
|
179
|
+
<!-- Config button -->
|
|
180
|
+
<button class="flowdrop-tool-node__config-btn" onclick={openConfigSidebar} title="Configure tool">
|
|
181
|
+
<Icon icon="mdi:cog" />
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- Tool Output Handle (optional) -->
|
|
186
|
+
{#if hasToolOutputPort && toolOutputPort}
|
|
187
|
+
<Handle
|
|
188
|
+
type="source"
|
|
189
|
+
position={Position.Right}
|
|
190
|
+
id={`${props.data.nodeId}-output-${toolOutputPort.id}`}
|
|
191
|
+
style="background-color: {getDataTypeColor('tool')}; border-color: '#ffffff';"
|
|
192
|
+
/>
|
|
193
|
+
{/if}
|
|
194
|
+
|
|
195
|
+
<style>
|
|
196
|
+
.flowdrop-tool-node {
|
|
197
|
+
position: relative;
|
|
198
|
+
background-color: #ffffff;
|
|
199
|
+
border: 2px solid #e5e7eb;
|
|
200
|
+
border-radius: 0.75rem;
|
|
201
|
+
width: 18rem;
|
|
202
|
+
display: flex;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
transition: all 0.2s ease-in-out;
|
|
206
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
207
|
+
overflow: visible;
|
|
208
|
+
z-index: 10;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.flowdrop-tool-node:hover {
|
|
212
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.flowdrop-tool-node--selected {
|
|
216
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
217
|
+
border: 2px solid #f59e0b;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.flowdrop-tool-node--processing {
|
|
221
|
+
opacity: 0.7;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.flowdrop-tool-node--error {
|
|
225
|
+
border-color: #ef4444 !important;
|
|
226
|
+
background-color: #fef2f2 !important;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.flowdrop-tool-node__header {
|
|
230
|
+
padding: 1rem;
|
|
231
|
+
background-color: #fffbeb;
|
|
232
|
+
border-radius: 0.75rem;
|
|
233
|
+
border: 1px solid #fcd34d;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.flowdrop-tool-node__header-content {
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
gap: 0.75rem;
|
|
240
|
+
margin-bottom: 0.5rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.flowdrop-tool-node__icon-container {
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: center;
|
|
246
|
+
justify-content: center;
|
|
247
|
+
width: 2.5rem;
|
|
248
|
+
height: 2.5rem;
|
|
249
|
+
border-radius: 0.5rem;
|
|
250
|
+
flex-shrink: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.flowdrop-tool-node__info {
|
|
254
|
+
flex: 1;
|
|
255
|
+
min-width: 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.flowdrop-tool-node__title {
|
|
259
|
+
font-size: 1rem;
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
color: #1f2937;
|
|
262
|
+
margin: 0;
|
|
263
|
+
line-height: 1.4;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.flowdrop-tool-node__version {
|
|
267
|
+
font-size: 0.75rem;
|
|
268
|
+
color: #6b7280;
|
|
269
|
+
font-weight: 500;
|
|
270
|
+
margin-top: 0.125rem;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.flowdrop-tool-node__badge {
|
|
274
|
+
background-color: #f59e0b;
|
|
275
|
+
color: white;
|
|
276
|
+
font-size: 0.625rem;
|
|
277
|
+
font-weight: 700;
|
|
278
|
+
padding: 0.25rem 0.5rem;
|
|
279
|
+
border-radius: 0.25rem;
|
|
280
|
+
letter-spacing: 0.05em;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.flowdrop-tool-node__description {
|
|
284
|
+
font-size: 0.75rem;
|
|
285
|
+
color: #6b7280;
|
|
286
|
+
margin: 0;
|
|
287
|
+
line-height: 1.3;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
:global(.flowdrop-tool-node__icon) {
|
|
291
|
+
color: white;
|
|
292
|
+
font-size: 1.25rem;
|
|
293
|
+
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.2));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.flowdrop-tool-node__processing {
|
|
297
|
+
position: absolute;
|
|
298
|
+
top: 4px;
|
|
299
|
+
right: 4px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.flowdrop-tool-node__spinner {
|
|
303
|
+
width: 12px;
|
|
304
|
+
height: 12px;
|
|
305
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
306
|
+
border-top: 1px solid white;
|
|
307
|
+
border-radius: 50%;
|
|
308
|
+
animation: spin 1s linear infinite;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.flowdrop-tool-node__error {
|
|
312
|
+
position: absolute;
|
|
313
|
+
top: 4px;
|
|
314
|
+
right: 4px;
|
|
315
|
+
color: #ef4444;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
:global(.flowdrop-tool-node__error-icon) {
|
|
319
|
+
width: 12px;
|
|
320
|
+
height: 12px;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.flowdrop-tool-node__config-btn {
|
|
324
|
+
position: absolute;
|
|
325
|
+
top: 0.5rem;
|
|
326
|
+
right: 0.5rem;
|
|
327
|
+
width: 1.5rem;
|
|
328
|
+
height: 1.5rem;
|
|
329
|
+
background-color: rgba(255, 255, 255, 0.9);
|
|
330
|
+
border: 1px solid #e5e7eb;
|
|
331
|
+
border-radius: 0.25rem;
|
|
332
|
+
color: #6b7280;
|
|
333
|
+
cursor: pointer;
|
|
334
|
+
display: flex;
|
|
335
|
+
align-items: center;
|
|
336
|
+
justify-content: center;
|
|
337
|
+
opacity: 0;
|
|
338
|
+
transition: all 0.2s ease-in-out;
|
|
339
|
+
backdrop-filter: blur(4px);
|
|
340
|
+
z-index: 15;
|
|
341
|
+
font-size: 0.875rem;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.flowdrop-tool-node:hover .flowdrop-tool-node__config-btn {
|
|
345
|
+
opacity: 1;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.flowdrop-tool-node__config-btn:hover {
|
|
349
|
+
background-color: #f9fafb;
|
|
350
|
+
border-color: #d1d5db;
|
|
351
|
+
color: #374151;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@keyframes spin {
|
|
355
|
+
to {
|
|
356
|
+
transform: rotate(360deg);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* Handle styles - special metadata port styling */
|
|
361
|
+
:global(.svelte-flow__node-tool .svelte-flow__handle) {
|
|
362
|
+
width: 16px !important;
|
|
363
|
+
height: 16px !important;
|
|
364
|
+
border: 2px solid #ffffff !important;
|
|
365
|
+
border-radius: 50% !important;
|
|
366
|
+
transition: all 0.2s ease-in-out !important;
|
|
367
|
+
cursor: pointer !important;
|
|
368
|
+
z-index: 20 !important;
|
|
369
|
+
pointer-events: auto !important;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
:global(.svelte-flow__node-tool .svelte-flow__handle-right) {
|
|
373
|
+
right: -6px !important;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Metadata port hover effects */
|
|
377
|
+
:global(.svelte-flow__node-tool .svelte-flow__handle:hover) {
|
|
378
|
+
box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.3) !important;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
:global(.svelte-flow__node-tool .svelte-flow__handle:focus) {
|
|
382
|
+
outline: 2px solid #f59e0b !important;
|
|
383
|
+
outline-offset: 2px !important;
|
|
384
|
+
}
|
|
385
|
+
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { NodeMetadata } from '../types/index.js';
|
|
2
|
+
interface ToolNodeParameter {
|
|
3
|
+
name: string;
|
|
4
|
+
type?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
type $$ComponentProps = {
|
|
8
|
+
data: {
|
|
9
|
+
label: string;
|
|
10
|
+
config: {
|
|
11
|
+
icon?: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
toolName?: string;
|
|
14
|
+
toolDescription?: string;
|
|
15
|
+
toolVersion?: string;
|
|
16
|
+
parameters?: ToolNodeParameter[];
|
|
17
|
+
};
|
|
18
|
+
metadata: NodeMetadata;
|
|
19
|
+
nodeId?: string;
|
|
20
|
+
onConfigOpen?: (node: {
|
|
21
|
+
id: string;
|
|
22
|
+
type: string;
|
|
23
|
+
data: {
|
|
24
|
+
label: string;
|
|
25
|
+
config: Record<string, unknown>;
|
|
26
|
+
metadata: NodeMetadata;
|
|
27
|
+
};
|
|
28
|
+
}) => void;
|
|
29
|
+
};
|
|
30
|
+
selected?: boolean;
|
|
31
|
+
isProcessing?: boolean;
|
|
32
|
+
isError?: boolean;
|
|
33
|
+
};
|
|
34
|
+
declare const ToolNode: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
35
|
+
type ToolNode = ReturnType<typeof ToolNode>;
|
|
36
|
+
export default ToolNode;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Universal Node Component
|
|
3
|
+
Renders any node type with automatic status overlay injection
|
|
4
|
+
This component can replace individual node components in SvelteFlow
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import type { WorkflowNode } from '../types/index.js';
|
|
9
|
+
import WorkflowNodeComponent from './WorkflowNode.svelte';
|
|
10
|
+
import NotesNode from './NotesNode.svelte';
|
|
11
|
+
import SimpleNode from './SimpleNode.svelte';
|
|
12
|
+
import SquareNode from './SquareNode.svelte';
|
|
13
|
+
import ToolNode from './ToolNode.svelte';
|
|
14
|
+
import GatewayNode from './GatewayNode.svelte';
|
|
15
|
+
import NodeStatusOverlay from './NodeStatusOverlay.svelte';
|
|
16
|
+
import {
|
|
17
|
+
shouldShowNodeStatus,
|
|
18
|
+
getOptimalStatusPosition,
|
|
19
|
+
getOptimalStatusSize
|
|
20
|
+
} from '../utils/nodeWrapper.js';
|
|
21
|
+
import { resolveComponentName } from '../utils/nodeTypes.js';
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
data: WorkflowNode['data'] & {
|
|
25
|
+
nodeId?: string;
|
|
26
|
+
onConfigOpen?: (node: { id: string; type: string; data: WorkflowNode['data'] }) => void;
|
|
27
|
+
};
|
|
28
|
+
selected?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
data,
|
|
33
|
+
selected = false
|
|
34
|
+
}: {
|
|
35
|
+
data: WorkflowNode['data'] & {
|
|
36
|
+
nodeId?: string;
|
|
37
|
+
onConfigOpen?: (node: { id: string; type: string; data: WorkflowNode['data'] }) => void;
|
|
38
|
+
};
|
|
39
|
+
selected?: boolean;
|
|
40
|
+
} = $props();
|
|
41
|
+
|
|
42
|
+
// Determine which node component to render based on node type
|
|
43
|
+
// Priority: config.nodeType > metadata.type
|
|
44
|
+
// Explicitly track config.nodeType to ensure reactivity
|
|
45
|
+
let configNodeType = $derived(data.config?.nodeType as string | undefined);
|
|
46
|
+
let resolvedComponentName = $derived(
|
|
47
|
+
data.metadata ? resolveComponentName(data.metadata, configNodeType) : 'workflowNode'
|
|
48
|
+
);
|
|
49
|
+
let nodeComponent = $derived(getNodeComponent(resolvedComponentName));
|
|
50
|
+
|
|
51
|
+
// Get execution info
|
|
52
|
+
let executionInfo = $derived(data.executionInfo);
|
|
53
|
+
let shouldShowStatus = $derived(
|
|
54
|
+
shouldShowNodeStatus(executionInfo) && resolvedComponentName !== 'note'
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the appropriate node component based on type
|
|
59
|
+
*/
|
|
60
|
+
function getNodeComponent(nodeType: string) {
|
|
61
|
+
switch (nodeType) {
|
|
62
|
+
case 'note':
|
|
63
|
+
return NotesNode;
|
|
64
|
+
case 'simple':
|
|
65
|
+
return SimpleNode;
|
|
66
|
+
case 'square':
|
|
67
|
+
return SquareNode;
|
|
68
|
+
case 'tool':
|
|
69
|
+
return ToolNode;
|
|
70
|
+
case 'gateway':
|
|
71
|
+
return GatewayNode;
|
|
72
|
+
case 'workflowNode':
|
|
73
|
+
default:
|
|
74
|
+
return WorkflowNodeComponent;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get optimal status position for this node type
|
|
80
|
+
*/
|
|
81
|
+
function getStatusPosition(): 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' {
|
|
82
|
+
return getOptimalStatusPosition(resolvedComponentName);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get optimal status size for this node type
|
|
87
|
+
*/
|
|
88
|
+
function getStatusSize(): 'sm' | 'md' | 'lg' {
|
|
89
|
+
return getOptimalStatusSize(resolvedComponentName);
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<div class="universal-node">
|
|
94
|
+
<!-- Render the appropriate node component -->
|
|
95
|
+
{#if nodeComponent === WorkflowNodeComponent}
|
|
96
|
+
<WorkflowNodeComponent {data} {selected} />
|
|
97
|
+
{:else if nodeComponent === NotesNode}
|
|
98
|
+
<NotesNode {data} {selected} />
|
|
99
|
+
{:else if nodeComponent === SimpleNode}
|
|
100
|
+
<SimpleNode {data} {selected} />
|
|
101
|
+
{:else if nodeComponent === SquareNode}
|
|
102
|
+
<SquareNode {data} {selected} />
|
|
103
|
+
{:else if nodeComponent === ToolNode}
|
|
104
|
+
<ToolNode {data} {selected} />
|
|
105
|
+
{:else if nodeComponent === GatewayNode}
|
|
106
|
+
<GatewayNode {data} {selected} />
|
|
107
|
+
{/if}
|
|
108
|
+
|
|
109
|
+
<!-- Status overlay - only show if there's meaningful status information -->
|
|
110
|
+
{#if shouldShowStatus}
|
|
111
|
+
<NodeStatusOverlay
|
|
112
|
+
nodeId={data.nodeId || 'unknown'}
|
|
113
|
+
{executionInfo}
|
|
114
|
+
position={getStatusPosition()}
|
|
115
|
+
size={getStatusSize()}
|
|
116
|
+
showDetails={true}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<style>
|
|
122
|
+
.universal-node {
|
|
123
|
+
position: relative;
|
|
124
|
+
display: inline-block;
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WorkflowNode } from '../types/index.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
data: WorkflowNode['data'] & {
|
|
4
|
+
nodeId?: string;
|
|
5
|
+
onConfigOpen?: (node: {
|
|
6
|
+
id: string;
|
|
7
|
+
type: string;
|
|
8
|
+
data: WorkflowNode['data'];
|
|
9
|
+
}) => void;
|
|
10
|
+
};
|
|
11
|
+
selected?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const UniversalNode: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type UniversalNode = ReturnType<typeof UniversalNode>;
|
|
15
|
+
export default UniversalNode;
|