@d34dman/flowdrop 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +293 -0
- package/dist/adapters/WorkflowAdapter.d.ts +166 -0
- package/dist/adapters/WorkflowAdapter.js +337 -0
- package/dist/api/client.d.ts +79 -0
- package/dist/api/client.js +208 -0
- package/dist/app.css +0 -0
- package/dist/clients/ApiClient.d.ts +203 -0
- package/dist/clients/ApiClient.js +212 -0
- package/dist/components/App.svelte +237 -0
- package/dist/components/App.svelte.d.ts +3 -0
- package/dist/components/CanvasBanner.svelte +51 -0
- package/dist/components/CanvasBanner.svelte.d.ts +22 -0
- package/dist/components/LoadingSpinner.svelte +36 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +8 -0
- package/dist/components/Node.svelte +38 -0
- package/dist/components/Node.svelte.d.ts +4 -0
- package/dist/components/NodeSidebar.svelte +500 -0
- package/dist/components/NodeSidebar.svelte.d.ts +9 -0
- package/dist/components/WorkflowEditor.svelte +542 -0
- package/dist/components/WorkflowEditor.svelte.d.ts +10 -0
- package/dist/components/WorkflowNode.svelte +558 -0
- package/dist/components/WorkflowNode.svelte.d.ts +11 -0
- package/dist/data/samples.d.ts +17 -0
- package/dist/data/samples.js +1193 -0
- package/dist/examples/adapter-usage.d.ts +66 -0
- package/dist/examples/adapter-usage.js +138 -0
- package/dist/examples/api-client-usage.d.ts +31 -0
- package/dist/examples/api-client-usage.js +241 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +27 -0
- package/dist/services/api.d.ts +110 -0
- package/dist/services/api.js +149 -0
- package/dist/services/workflowStorage.d.ts +37 -0
- package/dist/services/workflowStorage.js +116 -0
- package/dist/styles/base.css +858 -0
- package/dist/svelte-app.d.ts +17 -0
- package/dist/svelte-app.js +30 -0
- package/dist/types/index.d.ts +179 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/colors.d.ts +121 -0
- package/dist/utils/colors.js +240 -0
- package/dist/utils/connections.d.ts +47 -0
- package/dist/utils/connections.js +240 -0
- package/dist/utils/icons.d.ts +102 -0
- package/dist/utils/icons.js +149 -0
- package/package.json +99 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Workflow Node Component
|
|
3
|
+
Renders individual nodes in the workflow editor with full functionality
|
|
4
|
+
Uses SvelteFlow's Handle for connection ports
|
|
5
|
+
Styled with BEM syntax
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import {
|
|
10
|
+
Position,
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
Handle,
|
|
13
|
+
type NodeProps
|
|
14
|
+
} from "@xyflow/svelte";
|
|
15
|
+
import type { WorkflowNode, NodePort } from "../types/index.js";
|
|
16
|
+
import Icon from "@iconify/svelte";
|
|
17
|
+
import { getNodeIcon } from "../utils/icons.js";
|
|
18
|
+
import { getDataTypeColorToken, getCategoryColorToken } from "../utils/colors.js";
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
data: WorkflowNode["data"] & { nodeId?: string };
|
|
22
|
+
selected?: boolean;
|
|
23
|
+
onPortClick?: (nodeId: string, portId: string, isOutput: boolean, event: MouseEvent) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let props: Props = $props();
|
|
27
|
+
let isExpanded = $state(false);
|
|
28
|
+
let configValues = $state({ ...props.data.config });
|
|
29
|
+
let isHandleInteraction = $state(false);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Handle configuration value changes
|
|
33
|
+
*/
|
|
34
|
+
function handleConfigChange(key: string, value: unknown): void {
|
|
35
|
+
configValues = { ...configValues, [key]: value };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Handle node selection
|
|
40
|
+
*/
|
|
41
|
+
function handleNodeClick(): void {
|
|
42
|
+
// Node selection is handled through events
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Toggle node expansion
|
|
47
|
+
*/
|
|
48
|
+
function toggleExpansion(): void {
|
|
49
|
+
isExpanded = !isExpanded;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handle node drag start
|
|
54
|
+
*/
|
|
55
|
+
function handleDragStart(event: DragEvent): void {
|
|
56
|
+
// Prevent default drag behavior
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<!-- Node Container -->
|
|
62
|
+
<div
|
|
63
|
+
class="flowdrop-workflow-node"
|
|
64
|
+
class:flowdrop-workflow-node--selected={props.selected}
|
|
65
|
+
onclick={handleNodeClick}
|
|
66
|
+
onmouseup={() => {
|
|
67
|
+
isHandleInteraction = false;
|
|
68
|
+
}}
|
|
69
|
+
data-handle-interaction={isHandleInteraction}
|
|
70
|
+
role="button"
|
|
71
|
+
tabindex="0"
|
|
72
|
+
onkeydown={(e) => {
|
|
73
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
handleNodeClick();
|
|
76
|
+
}
|
|
77
|
+
}}
|
|
78
|
+
aria-label="Workflow node: {props.data.metadata.name}"
|
|
79
|
+
aria-describedby="node-description-{props.data.nodeId || 'unknown'}"
|
|
80
|
+
>
|
|
81
|
+
<!-- Node Header -->
|
|
82
|
+
<div class="flowdrop-workflow-node__header">
|
|
83
|
+
<div class="flowdrop-flex flowdrop-gap--3 flowdrop-items--center">
|
|
84
|
+
<!-- Node Icon -->
|
|
85
|
+
<div class="flowdrop-workflow-node__icon" style="background-color: {getCategoryColorToken(props.data.metadata.category)}">
|
|
86
|
+
<Icon icon={getNodeIcon(props.data.metadata.icon, props.data.metadata.category)} />
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<!-- Node Title - Icon and Title on same line -->
|
|
90
|
+
<h3 class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1">
|
|
91
|
+
{props.data.label}
|
|
92
|
+
</h3>
|
|
93
|
+
|
|
94
|
+
<!-- Status Indicators -->
|
|
95
|
+
<div class="flowdrop-flex flowdrop-gap--2">
|
|
96
|
+
{#if props.data.isProcessing}
|
|
97
|
+
<div class="flowdrop-workflow-node__status flowdrop-workflow-node__status--processing" title="Processing"></div>
|
|
98
|
+
{/if}
|
|
99
|
+
{#if props.data.error}
|
|
100
|
+
<div class="flowdrop-workflow-node__status flowdrop-workflow-node__status--error" title="Error"></div>
|
|
101
|
+
{/if}
|
|
102
|
+
<button
|
|
103
|
+
class="flowdrop-workflow-node__expand-btn"
|
|
104
|
+
onclick={(e) => {
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
toggleExpansion();
|
|
107
|
+
}}
|
|
108
|
+
type="button"
|
|
109
|
+
aria-label="{isExpanded ? 'Collapse' : 'Expand'} node configuration"
|
|
110
|
+
>
|
|
111
|
+
<span class="flowdrop-text--xs flowdrop-font--medium">{isExpanded ? "−" : "+"}</span>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<!-- Node Description - on new line below -->
|
|
116
|
+
<p class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1" id="node-description-{props.data.nodeId || 'unknown'}">
|
|
117
|
+
{props.data.metadata.description}
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Node Configuration (Expanded) -->
|
|
122
|
+
{#if isExpanded}
|
|
123
|
+
<div class="flowdrop-workflow-node__config">
|
|
124
|
+
<h4 class="flowdrop-workflow-node__config-title">Configuration</h4>
|
|
125
|
+
<div class="flowdrop-workflow-node__config-content">
|
|
126
|
+
{#each Object.entries(configValues) as [key, value]}
|
|
127
|
+
<div class="flowdrop-form-control">
|
|
128
|
+
<label class="flowdrop-form-control__label" for={`config-${key}`}>
|
|
129
|
+
<span class="flowdrop-text--xs flowdrop-font--medium">{key}</span>
|
|
130
|
+
</label>
|
|
131
|
+
{#if typeof value === "string"}
|
|
132
|
+
<input
|
|
133
|
+
id={`config-${key}`}
|
|
134
|
+
type="text"
|
|
135
|
+
class="flowdrop-input flowdrop-input--sm"
|
|
136
|
+
value={value}
|
|
137
|
+
oninput={(e) => {
|
|
138
|
+
const target = e.target as HTMLInputElement;
|
|
139
|
+
if (target) handleConfigChange(key, target.value);
|
|
140
|
+
}}
|
|
141
|
+
/>
|
|
142
|
+
{:else if typeof value === "number"}
|
|
143
|
+
<input
|
|
144
|
+
id={`config-${key}`}
|
|
145
|
+
type="number"
|
|
146
|
+
class="flowdrop-input flowdrop-input--sm"
|
|
147
|
+
value={value}
|
|
148
|
+
oninput={(e) => {
|
|
149
|
+
const target = e.target as HTMLInputElement;
|
|
150
|
+
if (target) {
|
|
151
|
+
const value = Number(target.value);
|
|
152
|
+
if (!isNaN(value)) {
|
|
153
|
+
handleConfigChange(key, value);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
{:else if typeof value === "boolean"}
|
|
159
|
+
<div class="flowdrop-flex flowdrop-gap--3">
|
|
160
|
+
<input
|
|
161
|
+
id={`config-${key}`}
|
|
162
|
+
type="checkbox"
|
|
163
|
+
class="flowdrop-toggle flowdrop-toggle--sm"
|
|
164
|
+
checked={value}
|
|
165
|
+
onchange={(e) => {
|
|
166
|
+
const target = e.target as HTMLInputElement;
|
|
167
|
+
if (target) handleConfigChange(key, target.checked);
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
<span class="flowdrop-text--xs">{key}</span>
|
|
171
|
+
</div>
|
|
172
|
+
{:else}
|
|
173
|
+
<textarea
|
|
174
|
+
id={`config-${key}`}
|
|
175
|
+
class="flowdrop-textarea flowdrop-textarea--sm"
|
|
176
|
+
placeholder="Enter value..."
|
|
177
|
+
value={JSON.stringify(value, null, 2)}
|
|
178
|
+
oninput={(e) => {
|
|
179
|
+
const target = e.target as HTMLInputElement;
|
|
180
|
+
if (target) {
|
|
181
|
+
try {
|
|
182
|
+
handleConfigChange(key, JSON.parse(target.value));
|
|
183
|
+
} catch {
|
|
184
|
+
handleConfigChange(key, target.value);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}}
|
|
188
|
+
></textarea>
|
|
189
|
+
{/if}
|
|
190
|
+
</div>
|
|
191
|
+
{/each}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
{/if}
|
|
195
|
+
|
|
196
|
+
<!-- Input Ports Container -->
|
|
197
|
+
{#if props.data.metadata.inputs.length > 0}
|
|
198
|
+
<div class="flowdrop-workflow-node__ports">
|
|
199
|
+
<div class="flowdrop-workflow-node__ports-header">
|
|
200
|
+
<h5 class="flowdrop-workflow-node__ports-title">Inputs</h5>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="flowdrop-workflow-node__ports-list">
|
|
203
|
+
{#each props.data.metadata.inputs as port (port.id)}
|
|
204
|
+
<div class="flowdrop-workflow-node__port">
|
|
205
|
+
<!-- Input Handle -->
|
|
206
|
+
<Handle
|
|
207
|
+
type="target"
|
|
208
|
+
position={Position.Left}
|
|
209
|
+
id={port.id}
|
|
210
|
+
class="flowdrop-workflow-node__handle"
|
|
211
|
+
style="top: 50%; transform: translateY(-50%); margin-left: -16px;"
|
|
212
|
+
role="button"
|
|
213
|
+
tabindex={0}
|
|
214
|
+
aria-label="Connect to {port.name} input port"
|
|
215
|
+
/>
|
|
216
|
+
|
|
217
|
+
<!-- Port Info -->
|
|
218
|
+
<div class="flowdrop-flex--1 flowdrop-min-w--0">
|
|
219
|
+
<div class="flowdrop-flex flowdrop-gap--2">
|
|
220
|
+
<span class="flowdrop-text--xs flowdrop-font--medium">{port.name}</span>
|
|
221
|
+
<span class="flowdrop-badge flowdrop-badge--sm" style="background-color: {getDataTypeColorToken(port.dataType)}; color: #fff;">
|
|
222
|
+
{port.dataType}
|
|
223
|
+
</span>
|
|
224
|
+
{#if port.required}
|
|
225
|
+
<span class="flowdrop-badge flowdrop-badge--error flowdrop-badge--sm">Required</span>
|
|
226
|
+
{/if}
|
|
227
|
+
</div>
|
|
228
|
+
{#if port.description}
|
|
229
|
+
<p class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate">{port.description}</p>
|
|
230
|
+
{/if}
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
{/each}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
{/if}
|
|
237
|
+
|
|
238
|
+
<!-- Output Ports Container -->
|
|
239
|
+
{#if props.data.metadata.outputs.length > 0}
|
|
240
|
+
<div class="flowdrop-workflow-node__ports">
|
|
241
|
+
<div class="flowdrop-workflow-node__ports-header">
|
|
242
|
+
<h5 class="flowdrop-workflow-node__ports-title">Outputs</h5>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="flowdrop-workflow-node__ports-list">
|
|
245
|
+
{#each props.data.metadata.outputs as port (port.id)}
|
|
246
|
+
<div class="flowdrop-workflow-node__port">
|
|
247
|
+
<!-- Port Info -->
|
|
248
|
+
<div class="flowdrop-flex--1 flowdrop-min-w--0 flowdrop-text--right">
|
|
249
|
+
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-justify--end">
|
|
250
|
+
<span class="flowdrop-text--xs flowdrop-font--medium">{port.name}</span>
|
|
251
|
+
<span class="flowdrop-badge flowdrop-badge--sm" style="background-color: {getDataTypeColorToken(port.dataType)}; color: #fff;">
|
|
252
|
+
{port.dataType}
|
|
253
|
+
</span>
|
|
254
|
+
</div>
|
|
255
|
+
{#if port.description}
|
|
256
|
+
<p class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate">{port.description}</p>
|
|
257
|
+
{/if}
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<!-- Output Handle -->
|
|
261
|
+
<Handle
|
|
262
|
+
type="source"
|
|
263
|
+
position={Position.Right}
|
|
264
|
+
id={port.id}
|
|
265
|
+
class="flowdrop-workflow-node__handle"
|
|
266
|
+
style="top: 50%; transform: translateY(-50%); margin-right: -16px;"
|
|
267
|
+
role="button"
|
|
268
|
+
tabindex={0}
|
|
269
|
+
aria-label="Connect from {port.name} output port"
|
|
270
|
+
/>
|
|
271
|
+
</div>
|
|
272
|
+
{/each}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
{/if}
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<style>
|
|
279
|
+
.flowdrop-workflow-node {
|
|
280
|
+
position: relative;
|
|
281
|
+
background-color: #ffffff;
|
|
282
|
+
border: 2px solid #e5e7eb;
|
|
283
|
+
border-radius: 0.75rem;
|
|
284
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
285
|
+
min-width: 14rem;
|
|
286
|
+
max-width: 18rem;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.flowdrop-workflow-node--selected {
|
|
290
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
291
|
+
border: 2px solid #3b82f6;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.flowdrop-workflow-node__header {
|
|
295
|
+
padding: 1rem;
|
|
296
|
+
border-bottom: 1px solid #e5e7eb;
|
|
297
|
+
background-color: #f9fafb;
|
|
298
|
+
border-top-left-radius: 0.75rem;
|
|
299
|
+
border-top-right-radius: 0.75rem;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.flowdrop-workflow-node__icon {
|
|
303
|
+
width: 2rem;
|
|
304
|
+
height: 2rem;
|
|
305
|
+
border-radius: 0.5rem;
|
|
306
|
+
color: #ffffff;
|
|
307
|
+
font-size: 0.875rem;
|
|
308
|
+
font-weight: 500;
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.flowdrop-workflow-node__header h3 {
|
|
316
|
+
margin: 0;
|
|
317
|
+
line-height: 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.flowdrop-workflow-node__status {
|
|
321
|
+
width: 0.5rem;
|
|
322
|
+
height: 0.5rem;
|
|
323
|
+
border-radius: 50%;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.flowdrop-workflow-node__status--processing {
|
|
327
|
+
background-color: #f59e0b;
|
|
328
|
+
animation: flowdrop-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.flowdrop-workflow-node__status--error {
|
|
332
|
+
background-color: #ef4444;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.flowdrop-workflow-node__expand-btn {
|
|
336
|
+
width: 1.5rem;
|
|
337
|
+
height: 1.5rem;
|
|
338
|
+
border-radius: 0.375rem;
|
|
339
|
+
background-color: #f3f4f6;
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
justify-content: center;
|
|
343
|
+
color: #6b7280;
|
|
344
|
+
transition: all 0.2s ease-in-out;
|
|
345
|
+
border: none;
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.flowdrop-workflow-node__expand-btn:hover {
|
|
350
|
+
background-color: #e5e7eb;
|
|
351
|
+
color: #374151;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.flowdrop-workflow-node__config {
|
|
355
|
+
padding: 1rem;
|
|
356
|
+
border-bottom: 1px solid #e5e7eb;
|
|
357
|
+
background-color: #f9fafb;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.flowdrop-workflow-node__config-title {
|
|
361
|
+
font-size: 0.75rem;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
color: #374151;
|
|
364
|
+
margin-bottom: 0.75rem;
|
|
365
|
+
text-transform: uppercase;
|
|
366
|
+
letter-spacing: 0.05em;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.flowdrop-workflow-node__config-content {
|
|
370
|
+
display: flex;
|
|
371
|
+
flex-direction: column;
|
|
372
|
+
gap: 0.75rem;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.flowdrop-form-control {
|
|
376
|
+
display: flex;
|
|
377
|
+
flex-direction: column;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.flowdrop-form-control__label {
|
|
381
|
+
padding-bottom: 0.25rem;
|
|
382
|
+
display: flex;
|
|
383
|
+
flex-direction: column;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.flowdrop-textarea {
|
|
387
|
+
display: block;
|
|
388
|
+
width: 100%;
|
|
389
|
+
padding: 0.375rem 0.5rem;
|
|
390
|
+
border: 1px solid #d1d5db;
|
|
391
|
+
border-radius: 0.375rem;
|
|
392
|
+
font-size: 0.75rem;
|
|
393
|
+
line-height: 1.25rem;
|
|
394
|
+
color: #111827;
|
|
395
|
+
background-color: rgba(255, 255, 255, 0.7);
|
|
396
|
+
transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
|
397
|
+
resize: vertical;
|
|
398
|
+
min-height: 2.5rem;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.flowdrop-textarea:focus {
|
|
402
|
+
outline: none;
|
|
403
|
+
border-color: #3b82f6;
|
|
404
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.flowdrop-textarea--sm {
|
|
408
|
+
padding: 0.375rem 0.5rem;
|
|
409
|
+
font-size: 0.75rem;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.flowdrop-toggle {
|
|
413
|
+
appearance: none;
|
|
414
|
+
width: 2rem;
|
|
415
|
+
height: 1rem;
|
|
416
|
+
background-color: #d1d5db;
|
|
417
|
+
border-radius: 9999px;
|
|
418
|
+
position: relative;
|
|
419
|
+
cursor: pointer;
|
|
420
|
+
transition: background-color 0.2s ease-in-out;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.flowdrop-toggle:checked {
|
|
424
|
+
background-color: #3b82f6;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.flowdrop-toggle::before {
|
|
428
|
+
content: "";
|
|
429
|
+
position: absolute;
|
|
430
|
+
width: 0.75rem;
|
|
431
|
+
height: 0.75rem;
|
|
432
|
+
background-color: #ffffff;
|
|
433
|
+
border-radius: 50%;
|
|
434
|
+
top: 0.125rem;
|
|
435
|
+
left: 0.125rem;
|
|
436
|
+
transition: transform 0.2s ease-in-out;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.flowdrop-toggle:checked::before {
|
|
440
|
+
transform: translateX(1rem);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.flowdrop-toggle--sm {
|
|
444
|
+
width: 1.5rem;
|
|
445
|
+
height: 0.75rem;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.flowdrop-toggle--sm::before {
|
|
449
|
+
width: 0.5rem;
|
|
450
|
+
height: 0.5rem;
|
|
451
|
+
top: 0.125rem;
|
|
452
|
+
left: 0.125rem;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.flowdrop-toggle--sm:checked::before {
|
|
456
|
+
transform: translateX(0.75rem);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.flowdrop-workflow-node__ports {
|
|
460
|
+
border-bottom: 1px solid #e5e7eb;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.flowdrop-workflow-node__ports:last-child {
|
|
464
|
+
border-bottom: none;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.flowdrop-workflow-node__ports-header {
|
|
468
|
+
padding: 0.5rem 1rem;
|
|
469
|
+
background-color: #f3f4f6;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.flowdrop-workflow-node__ports-title {
|
|
473
|
+
font-size: 0.75rem;
|
|
474
|
+
font-weight: 600;
|
|
475
|
+
color: #6b7280;
|
|
476
|
+
text-transform: uppercase;
|
|
477
|
+
letter-spacing: 0.05em;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.flowdrop-workflow-node__ports-list {
|
|
481
|
+
display: flex;
|
|
482
|
+
flex-direction: column;
|
|
483
|
+
gap: 0.25rem;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.flowdrop-workflow-node__port {
|
|
487
|
+
position: relative;
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
gap: 0.5rem;
|
|
491
|
+
padding: 0.5rem 1rem;
|
|
492
|
+
transition: background-color 0.2s ease-in-out;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.flowdrop-workflow-node__port:hover {
|
|
496
|
+
background-color: #f9fafb;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
.flowdrop-badge--sm {
|
|
501
|
+
padding: 0.125rem 0.25rem;
|
|
502
|
+
font-size: 0.625rem;
|
|
503
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.flowdrop-badge--error {
|
|
507
|
+
background-color: #fee2e2;
|
|
508
|
+
color: #991b1b;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.flowdrop-justify--end {
|
|
512
|
+
justify-content: flex-end;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.flowdrop-text--right {
|
|
516
|
+
text-align: right;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.flowdrop-min-w--0 {
|
|
520
|
+
min-width: 0;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.flowdrop-truncate {
|
|
524
|
+
overflow: hidden;
|
|
525
|
+
text-overflow: ellipsis;
|
|
526
|
+
white-space: nowrap;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.flowdrop-mt--1 {
|
|
530
|
+
margin-top: 0.25rem;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@keyframes flowdrop-pulse {
|
|
534
|
+
0%, 100% {
|
|
535
|
+
opacity: 1;
|
|
536
|
+
}
|
|
537
|
+
50% {
|
|
538
|
+
opacity: 0.5;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/* Focus styles for accessibility */
|
|
543
|
+
.flowdrop-workflow-node:focus {
|
|
544
|
+
outline: 2px solid #3b82f6;
|
|
545
|
+
outline-offset: 2px;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.flowdrop-workflow-node:focus:not(:focus-visible) {
|
|
549
|
+
outline: none;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/* Configuration form styling */
|
|
553
|
+
.flowdrop-form-control input:focus,
|
|
554
|
+
.flowdrop-form-control textarea:focus {
|
|
555
|
+
transform: translateY(-1px);
|
|
556
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
557
|
+
}
|
|
558
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WorkflowNode } from "../types/index.js";
|
|
2
|
+
interface Props {
|
|
3
|
+
data: WorkflowNode["data"] & {
|
|
4
|
+
nodeId?: string;
|
|
5
|
+
};
|
|
6
|
+
selected?: boolean;
|
|
7
|
+
onPortClick?: (nodeId: string, portId: string, isOutput: boolean, event: MouseEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const WorkflowNode: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type WorkflowNode = ReturnType<typeof WorkflowNode>;
|
|
11
|
+
export default WorkflowNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample data for FlowDrop development and testing
|
|
3
|
+
* Full set matching Langflow's default categories and node types
|
|
4
|
+
*/
|
|
5
|
+
import type { NodeMetadata, Workflow } from "../types/index.js";
|
|
6
|
+
import { CATEGORY_ICONS } from "../utils/icons.js";
|
|
7
|
+
/**
|
|
8
|
+
* Sample node definitions for development
|
|
9
|
+
* Full set matching Langflow's default categories
|
|
10
|
+
*/
|
|
11
|
+
export declare const sampleNodes: NodeMetadata[];
|
|
12
|
+
export { CATEGORY_ICONS as categoryIcons };
|
|
13
|
+
/**
|
|
14
|
+
* Sample workflow for development
|
|
15
|
+
* Updated to use the new node types
|
|
16
|
+
*/
|
|
17
|
+
export declare const sampleWorkflow: Workflow;
|