@d34dman/flowdrop 0.0.44 → 0.0.46
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 +2 -2
- package/dist/components/ConfigForm.svelte +4 -20
- package/dist/components/Navbar.svelte +6 -7
- package/dist/components/NodeSidebar.svelte +6 -2
- package/dist/components/SchemaForm.svelte +2 -10
- package/dist/components/WorkflowEditor.svelte +143 -13
- package/dist/components/form/FormAutocomplete.svelte +5 -9
- package/dist/components/form/FormCheckboxGroup.svelte +11 -1
- package/dist/components/form/FormCheckboxGroup.svelte.d.ts +2 -0
- package/dist/components/form/FormCodeEditor.svelte +16 -7
- package/dist/components/form/FormCodeEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormField.svelte +20 -1
- package/dist/components/form/FormMarkdownEditor.svelte +29 -19
- package/dist/components/form/FormMarkdownEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormNumberField.svelte +4 -0
- package/dist/components/form/FormNumberField.svelte.d.ts +2 -0
- package/dist/components/form/FormRangeField.svelte +4 -0
- package/dist/components/form/FormRangeField.svelte.d.ts +2 -0
- package/dist/components/form/FormSelect.svelte +4 -0
- package/dist/components/form/FormSelect.svelte.d.ts +2 -0
- package/dist/components/form/FormTemplateEditor.svelte +16 -7
- package/dist/components/form/FormTemplateEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormTextField.svelte +4 -0
- package/dist/components/form/FormTextField.svelte.d.ts +2 -0
- package/dist/components/form/FormTextarea.svelte +4 -0
- package/dist/components/form/FormTextarea.svelte.d.ts +2 -0
- package/dist/components/form/FormToggle.svelte +4 -0
- package/dist/components/form/FormToggle.svelte.d.ts +2 -0
- package/dist/components/form/types.d.ts +5 -0
- package/dist/components/form/types.js +1 -1
- package/dist/components/layouts/MainLayout.svelte +5 -2
- package/dist/components/nodes/GatewayNode.svelte +99 -86
- package/dist/components/nodes/IdeaNode.svelte +20 -35
- package/dist/components/nodes/NotesNode.svelte +6 -2
- package/dist/components/nodes/SimpleNode.svelte +32 -31
- package/dist/components/nodes/SquareNode.svelte +35 -45
- package/dist/components/nodes/TerminalNode.svelte +25 -61
- package/dist/components/nodes/ToolNode.svelte +36 -18
- package/dist/components/nodes/WorkflowNode.svelte +97 -73
- package/dist/components/playground/Playground.svelte +43 -38
- package/dist/editor/index.d.ts +3 -1
- package/dist/editor/index.js +5 -1
- package/dist/helpers/nodeLayoutHelper.d.ts +14 -0
- package/dist/helpers/nodeLayoutHelper.js +19 -0
- package/dist/helpers/workflowEditorHelper.js +1 -2
- package/dist/services/autoSaveService.js +5 -5
- package/dist/services/historyService.d.ts +207 -0
- package/dist/services/historyService.js +317 -0
- package/dist/services/settingsService.d.ts +2 -2
- package/dist/services/settingsService.js +15 -21
- package/dist/services/toastService.d.ts +1 -1
- package/dist/services/toastService.js +10 -10
- package/dist/stores/historyStore.d.ts +133 -0
- package/dist/stores/historyStore.js +188 -0
- package/dist/stores/settingsStore.d.ts +1 -1
- package/dist/stores/settingsStore.js +40 -42
- package/dist/stores/themeStore.d.ts +2 -2
- package/dist/stores/themeStore.js +30 -32
- package/dist/stores/workflowStore.d.ts +52 -2
- package/dist/stores/workflowStore.js +102 -2
- package/dist/styles/base.css +67 -7
- package/dist/styles/toast.css +3 -1
- package/dist/styles/tokens.css +38 -2
- package/dist/types/settings.d.ts +3 -3
- package/dist/types/settings.js +13 -19
- package/dist/utils/colors.js +18 -18
- package/package.json +1 -1
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
import { dynamicPortToNodePort } from '../../types/index.js';
|
|
15
15
|
import Icon from '@iconify/svelte';
|
|
16
16
|
import { getNodeIcon } from '../../utils/icons.js';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getDataTypeColorToken,
|
|
19
|
+
getCategoryColorToken,
|
|
20
|
+
getPortBackgroundColor
|
|
21
|
+
} from '../../utils/colors.js';
|
|
18
22
|
import { connectedHandles } from '../../stores/workflowStore.js';
|
|
19
23
|
|
|
20
24
|
interface Props {
|
|
@@ -176,15 +180,18 @@
|
|
|
176
180
|
aria-label="Workflow node: {props.data.metadata.name}"
|
|
177
181
|
aria-describedby="node-description-{props.data.nodeId || 'unknown'}"
|
|
178
182
|
>
|
|
179
|
-
<!-- Default Node Header -->
|
|
183
|
+
<!-- Default Node Header: expands in multiples of 10 (title row 40px + gap 10px + description 20px per line) -->
|
|
180
184
|
<div class="flowdrop-workflow-node__header">
|
|
181
|
-
<div class="flowdrop-
|
|
185
|
+
<div class="flowdrop-workflow-node__header-title">
|
|
182
186
|
<!-- Node Icon with Squircle Background -->
|
|
183
187
|
<div
|
|
184
188
|
class="flowdrop-workflow-node__icon-wrapper"
|
|
185
189
|
style="--_icon-color: {getCategoryColorToken(props.data.metadata.category)}"
|
|
186
190
|
>
|
|
187
|
-
<Icon
|
|
191
|
+
<Icon
|
|
192
|
+
icon={getNodeIcon(props.data.metadata.icon, props.data.metadata.category)}
|
|
193
|
+
class="flowdrop-workflow-node__icon"
|
|
194
|
+
/>
|
|
188
195
|
</div>
|
|
189
196
|
|
|
190
197
|
<!-- Node Title - Icon and Title on same line -->
|
|
@@ -197,9 +204,9 @@
|
|
|
197
204
|
<!-- Status indicators removed - using outer NodeStatusOverlay instead -->
|
|
198
205
|
</div>
|
|
199
206
|
</div>
|
|
200
|
-
<!-- Node Description -
|
|
207
|
+
<!-- Node Description - line-height 20px so header grows in steps of 10 -->
|
|
201
208
|
<p
|
|
202
|
-
class="flowdrop-
|
|
209
|
+
class="flowdrop-workflow-node__header-desc"
|
|
203
210
|
id="node-description-{props.data.nodeId || 'unknown'}"
|
|
204
211
|
>
|
|
205
212
|
{displayDescription}
|
|
@@ -209,31 +216,35 @@
|
|
|
209
216
|
<!-- Input Ports Container -->
|
|
210
217
|
{#if visibleInputPorts.length > 0}
|
|
211
218
|
<div class="flowdrop-workflow-node__ports">
|
|
212
|
-
<div class="flowdrop-workflow-node__ports-header">
|
|
213
|
-
<h5 class="flowdrop-workflow-node__ports-title">Inputs</h5>
|
|
214
|
-
</div>
|
|
215
219
|
<div class="flowdrop-workflow-node__ports-list">
|
|
216
|
-
{#each visibleInputPorts as port (port.id)}
|
|
220
|
+
{#each visibleInputPorts as port, inputIndex (port.id)}
|
|
217
221
|
<div class="flowdrop-workflow-node__port">
|
|
218
|
-
<!-- Input Handle -->
|
|
222
|
+
<!-- Input Handle: centered in row, at node edge (ports have no padding) -->
|
|
219
223
|
<Handle
|
|
220
224
|
type="target"
|
|
221
225
|
position={Position.Left}
|
|
222
226
|
id={`${props.data.nodeId}-input-${port.id}`}
|
|
223
227
|
class="flowdrop-workflow-node__handle"
|
|
224
|
-
style="top: 50%; transform: translateY(-50%);
|
|
228
|
+
style="top: 50%; transform: translateY(-50%); left: calc(-1 * (var(--fd-handle-size) / 2)); --fd-handle-fill: {getDataTypeColorToken(
|
|
229
|
+
port.dataType
|
|
230
|
+
)}; --fd-handle-border-color: var(--fd-handle-border);"
|
|
225
231
|
role="button"
|
|
226
232
|
tabindex={0}
|
|
227
233
|
aria-label="Connect to {port.name} input port"
|
|
228
234
|
/>
|
|
229
235
|
|
|
230
|
-
<!-- Port Info -->
|
|
231
|
-
<div class="flowdrop-flex--1 flowdrop-min-w--0">
|
|
236
|
+
<!-- Port Info: padding lives here so handle position is simple -->
|
|
237
|
+
<div class="flowdrop-workflow-node__port-content flowdrop-flex--1 flowdrop-min-w--0">
|
|
232
238
|
<div class="flowdrop-flex flowdrop-gap--2">
|
|
233
239
|
<span class="flowdrop-text--xs flowdrop-font--medium">{port.name}</span>
|
|
234
240
|
<span
|
|
235
241
|
class="flowdrop-badge flowdrop-badge--sm"
|
|
236
|
-
style="background-color: {getPortBackgroundColor(
|
|
242
|
+
style="background-color: {getPortBackgroundColor(
|
|
243
|
+
port.dataType,
|
|
244
|
+
15
|
|
245
|
+
)}; color: {getDataTypeColorToken(
|
|
246
|
+
port.dataType
|
|
247
|
+
)}; border: 1px solid {getPortBackgroundColor(port.dataType, 30)};"
|
|
237
248
|
>
|
|
238
249
|
{port.dataType}
|
|
239
250
|
</span>
|
|
@@ -258,19 +269,23 @@
|
|
|
258
269
|
<!-- Output Ports Container -->
|
|
259
270
|
{#if visibleOutputPorts.length > 0}
|
|
260
271
|
<div class="flowdrop-workflow-node__ports">
|
|
261
|
-
<div class="flowdrop-workflow-node__ports-header">
|
|
262
|
-
<h5 class="flowdrop-workflow-node__ports-title">Outputs</h5>
|
|
263
|
-
</div>
|
|
264
272
|
<div class="flowdrop-workflow-node__ports-list">
|
|
265
|
-
{#each visibleOutputPorts as port (port.id)}
|
|
273
|
+
{#each visibleOutputPorts as port, outputIndex (port.id)}
|
|
266
274
|
<div class="flowdrop-workflow-node__port">
|
|
267
|
-
<!-- Port Info -->
|
|
268
|
-
<div
|
|
275
|
+
<!-- Port Info: padding lives here so handle position is simple -->
|
|
276
|
+
<div
|
|
277
|
+
class="flowdrop-workflow-node__port-content flowdrop-flex--1 flowdrop-min-w--0 flowdrop-text--right"
|
|
278
|
+
>
|
|
269
279
|
<div class="flowdrop-flex flowdrop-gap--2 flowdrop-justify--end">
|
|
270
280
|
<span class="flowdrop-text--xs flowdrop-font--medium">{port.name}</span>
|
|
271
281
|
<span
|
|
272
282
|
class="flowdrop-badge flowdrop-badge--sm"
|
|
273
|
-
style="background-color: {getPortBackgroundColor(
|
|
283
|
+
style="background-color: {getPortBackgroundColor(
|
|
284
|
+
port.dataType,
|
|
285
|
+
15
|
|
286
|
+
)}; color: {getDataTypeColorToken(
|
|
287
|
+
port.dataType
|
|
288
|
+
)}; border: 1px solid {getPortBackgroundColor(port.dataType, 30)};"
|
|
274
289
|
>
|
|
275
290
|
{port.dataType}
|
|
276
291
|
</span>
|
|
@@ -282,13 +297,15 @@
|
|
|
282
297
|
{/if}
|
|
283
298
|
</div>
|
|
284
299
|
|
|
285
|
-
<!-- Output Handle -->
|
|
300
|
+
<!-- Output Handle: centered in row, at node edge (ports have no padding) -->
|
|
286
301
|
<Handle
|
|
287
302
|
type="source"
|
|
288
303
|
position={Position.Right}
|
|
289
304
|
id={`${props.data.nodeId}-output-${port.id}`}
|
|
290
305
|
class="flowdrop-workflow-node__handle"
|
|
291
|
-
style="top: 50%; transform: translateY(-50%);
|
|
306
|
+
style="top: 50%; transform: translateY(-50%); right: calc(-1 * (var(--fd-handle-size) / 2)); --fd-handle-fill: {getDataTypeColorToken(
|
|
307
|
+
port.dataType
|
|
308
|
+
)}; --fd-handle-border-color: var(--fd-handle-border);"
|
|
292
309
|
role="button"
|
|
293
310
|
tabindex={0}
|
|
294
311
|
aria-label="Connect from {port.name} output port"
|
|
@@ -318,7 +335,7 @@
|
|
|
318
335
|
border: 1.5px solid var(--fd-node-border);
|
|
319
336
|
border-radius: var(--fd-radius-xl);
|
|
320
337
|
box-shadow: var(--fd-shadow-md);
|
|
321
|
-
width:
|
|
338
|
+
width: var(--fd-node-default-width);
|
|
322
339
|
z-index: 10;
|
|
323
340
|
color: var(--fd-foreground);
|
|
324
341
|
transition: all var(--fd-transition-fast);
|
|
@@ -330,12 +347,16 @@
|
|
|
330
347
|
}
|
|
331
348
|
|
|
332
349
|
.flowdrop-workflow-node--selected {
|
|
333
|
-
box-shadow:
|
|
350
|
+
box-shadow:
|
|
351
|
+
0 0 0 2px var(--fd-primary-muted),
|
|
352
|
+
var(--fd-shadow-lg);
|
|
334
353
|
border-color: var(--fd-primary);
|
|
335
354
|
}
|
|
336
355
|
|
|
337
356
|
.flowdrop-workflow-node--selected:hover {
|
|
338
|
-
box-shadow:
|
|
357
|
+
box-shadow:
|
|
358
|
+
0 0 0 2px var(--fd-primary-muted),
|
|
359
|
+
var(--fd-shadow-lg);
|
|
339
360
|
border-color: var(--fd-primary);
|
|
340
361
|
}
|
|
341
362
|
|
|
@@ -345,11 +366,41 @@
|
|
|
345
366
|
}
|
|
346
367
|
|
|
347
368
|
.flowdrop-workflow-node__header {
|
|
348
|
-
|
|
369
|
+
box-sizing: border-box;
|
|
370
|
+
padding: var(--fd-node-header-gap) var(--fd-space-4);
|
|
349
371
|
border-bottom: 1px solid var(--fd-border-muted);
|
|
350
372
|
background: var(--fd-header);
|
|
351
373
|
border-top-left-radius: var(--fd-radius-xl);
|
|
352
374
|
border-top-right-radius: var(--fd-radius-xl);
|
|
375
|
+
display: flex;
|
|
376
|
+
flex-direction: column;
|
|
377
|
+
gap: var(--fd-node-header-gap);
|
|
378
|
+
min-height: calc(
|
|
379
|
+
var(--fd-node-header-gap) * 2 + var(--fd-node-header-title-height) +
|
|
380
|
+
var(--fd-node-header-desc-line)
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.flowdrop-workflow-node__header-title {
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
gap: var(--fd-space-3);
|
|
388
|
+
min-height: var(--fd-node-header-title-height);
|
|
389
|
+
flex-shrink: 0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.flowdrop-workflow-node__header-desc {
|
|
393
|
+
margin: 0;
|
|
394
|
+
font-size: var(--fd-text-xs);
|
|
395
|
+
color: var(--fd-muted-foreground);
|
|
396
|
+
line-height: var(--fd-node-header-desc-line);
|
|
397
|
+
min-height: var(--fd-node-header-desc-line);
|
|
398
|
+
overflow: hidden;
|
|
399
|
+
text-overflow: ellipsis;
|
|
400
|
+
display: -webkit-box;
|
|
401
|
+
-webkit-line-clamp: 3;
|
|
402
|
+
line-clamp: 3;
|
|
403
|
+
-webkit-box-orient: vertical;
|
|
353
404
|
}
|
|
354
405
|
|
|
355
406
|
/* Squircle icon wrapper - Apple-style rounded square background */
|
|
@@ -360,13 +411,17 @@
|
|
|
360
411
|
width: 2.25rem;
|
|
361
412
|
height: 2.25rem;
|
|
362
413
|
border-radius: 0.5rem;
|
|
363
|
-
background: color-mix(in srgb, var(--_icon-color)
|
|
414
|
+
background: color-mix(in srgb, var(--_icon-color) var(--fd-node-icon-bg-opacity), transparent);
|
|
364
415
|
flex-shrink: 0;
|
|
365
416
|
transition: all var(--fd-transition-normal);
|
|
366
417
|
}
|
|
367
418
|
|
|
368
419
|
.flowdrop-workflow-node:hover .flowdrop-workflow-node__icon-wrapper {
|
|
369
|
-
background: color-mix(
|
|
420
|
+
background: color-mix(
|
|
421
|
+
in srgb,
|
|
422
|
+
var(--_icon-color) var(--fd-node-icon-bg-opacity-hover),
|
|
423
|
+
transparent
|
|
424
|
+
);
|
|
370
425
|
transform: scale(1.05);
|
|
371
426
|
}
|
|
372
427
|
|
|
@@ -376,7 +431,7 @@
|
|
|
376
431
|
color: var(--fd-node-icon);
|
|
377
432
|
}
|
|
378
433
|
|
|
379
|
-
.flowdrop-workflow-node__header h3 {
|
|
434
|
+
.flowdrop-workflow-node__header-title h3 {
|
|
380
435
|
margin: 0;
|
|
381
436
|
line-height: 1;
|
|
382
437
|
}
|
|
@@ -394,36 +449,29 @@
|
|
|
394
449
|
}
|
|
395
450
|
|
|
396
451
|
.flowdrop-workflow-node__ports {
|
|
397
|
-
padding:
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.flowdrop-workflow-node__ports-header {
|
|
401
|
-
margin-bottom: var(--fd-space-2);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
.flowdrop-workflow-node__ports-title {
|
|
405
|
-
margin: 0;
|
|
406
|
-
font-size: var(--fd-text-xs);
|
|
407
|
-
font-weight: 600;
|
|
408
|
-
color: var(--fd-foreground);
|
|
409
|
-
text-transform: uppercase;
|
|
410
|
-
letter-spacing: 0.05em;
|
|
452
|
+
padding: 0;
|
|
411
453
|
}
|
|
412
454
|
|
|
413
455
|
.flowdrop-workflow-node__ports-list {
|
|
414
456
|
display: flex;
|
|
415
457
|
flex-direction: column;
|
|
416
|
-
gap: var(--fd-
|
|
458
|
+
gap: var(--fd-node-header-gap);
|
|
459
|
+
padding: var(--fd-node-header-gap) 0;
|
|
417
460
|
}
|
|
418
461
|
|
|
419
462
|
.flowdrop-workflow-node__port {
|
|
420
463
|
display: flex;
|
|
421
464
|
align-items: center;
|
|
422
|
-
gap:
|
|
465
|
+
gap: 0;
|
|
466
|
+
min-height: var(--fd-node-port-row-height);
|
|
423
467
|
padding: var(--fd-space-1) 0;
|
|
424
468
|
position: relative;
|
|
425
469
|
}
|
|
426
470
|
|
|
471
|
+
.flowdrop-workflow-node__port-content {
|
|
472
|
+
padding: 0 var(--fd-space-4);
|
|
473
|
+
}
|
|
474
|
+
|
|
427
475
|
.flowdrop-badge {
|
|
428
476
|
padding: 0.125rem var(--fd-space-1);
|
|
429
477
|
border-radius: var(--fd-radius-sm);
|
|
@@ -443,25 +491,9 @@
|
|
|
443
491
|
padding: 0.125rem var(--fd-space-1);
|
|
444
492
|
}
|
|
445
493
|
|
|
446
|
-
/* Handle
|
|
447
|
-
:global(.flowdrop-workflow-node__handle) {
|
|
448
|
-
width: 0.75rem;
|
|
449
|
-
height: 0.75rem;
|
|
450
|
-
background-color: var(--fd-muted-foreground);
|
|
451
|
-
border: 2px solid var(--fd-handle-border);
|
|
452
|
-
border-radius: 50%;
|
|
453
|
-
transition: all var(--fd-transition-normal);
|
|
454
|
-
cursor: pointer;
|
|
455
|
-
}
|
|
456
|
-
|
|
494
|
+
/* Handle overrides: hover scale (base 20px/12px from base.css) */
|
|
457
495
|
:global(.flowdrop-workflow-node__handle:hover) {
|
|
458
|
-
|
|
459
|
-
transform: scale(1.2);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
:global(.flowdrop-workflow-node__handle:focus) {
|
|
463
|
-
outline: 2px solid var(--fd-ring);
|
|
464
|
-
outline-offset: 2px;
|
|
496
|
+
transform: translateY(-50%) scale(1.2);
|
|
465
497
|
}
|
|
466
498
|
|
|
467
499
|
/* Utility classes */
|
|
@@ -477,10 +509,6 @@
|
|
|
477
509
|
gap: var(--fd-space-2);
|
|
478
510
|
}
|
|
479
511
|
|
|
480
|
-
.flowdrop-gap--3 {
|
|
481
|
-
gap: var(--fd-space-3);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
512
|
.flowdrop-items--center {
|
|
485
513
|
align-items: center;
|
|
486
514
|
}
|
|
@@ -517,10 +545,6 @@
|
|
|
517
545
|
white-space: nowrap;
|
|
518
546
|
}
|
|
519
547
|
|
|
520
|
-
.flowdrop-mt--1 {
|
|
521
|
-
margin-top: var(--fd-space-1);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
548
|
.flowdrop-text--right {
|
|
525
549
|
text-align: right;
|
|
526
550
|
}
|
|
@@ -556,43 +556,43 @@
|
|
|
556
556
|
<p class="playground__sessions-hint">Click a session to load it</p>
|
|
557
557
|
{/if}
|
|
558
558
|
<div class="playground__sessions">
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
</div>
|
|
563
|
-
{:else}
|
|
564
|
-
{#each $sessions as session (session.id)}
|
|
565
|
-
<div
|
|
566
|
-
class="playground__session"
|
|
567
|
-
class:playground__session--active={$currentSession?.id === session.id}
|
|
568
|
-
role="button"
|
|
569
|
-
tabindex="0"
|
|
570
|
-
title="Click to load this session"
|
|
571
|
-
aria-label="Load session: {session.name}"
|
|
572
|
-
onclick={() => handleSelectSession(session.id)}
|
|
573
|
-
onkeydown={(e) => e.key === 'Enter' && handleSelectSession(session.id)}
|
|
574
|
-
>
|
|
575
|
-
<span class="playground__session-name" title={session.name}>
|
|
576
|
-
{session.name}
|
|
577
|
-
</span>
|
|
578
|
-
<button
|
|
579
|
-
type="button"
|
|
580
|
-
class="playground__session-menu"
|
|
581
|
-
class:playground__session-menu--delete={pendingDeleteId === session.id}
|
|
582
|
-
onclick={(e) => handleDeleteClick(e, session.id)}
|
|
583
|
-
title={pendingDeleteId === session.id
|
|
584
|
-
? 'Click to confirm delete'
|
|
585
|
-
: 'Delete session'}
|
|
586
|
-
>
|
|
587
|
-
{#if pendingDeleteId === session.id}
|
|
588
|
-
<Icon icon="mdi:check" />
|
|
589
|
-
{:else}
|
|
590
|
-
<Icon icon="mdi:dots-horizontal" />
|
|
591
|
-
{/if}
|
|
592
|
-
</button>
|
|
559
|
+
{#if $sessions.length === 0 && !$isLoading}
|
|
560
|
+
<div class="playground__sessions-empty">
|
|
561
|
+
<span>No sessions yet</span>
|
|
593
562
|
</div>
|
|
594
|
-
{
|
|
595
|
-
|
|
563
|
+
{:else}
|
|
564
|
+
{#each $sessions as session (session.id)}
|
|
565
|
+
<div
|
|
566
|
+
class="playground__session"
|
|
567
|
+
class:playground__session--active={$currentSession?.id === session.id}
|
|
568
|
+
role="button"
|
|
569
|
+
tabindex="0"
|
|
570
|
+
title="Click to load this session"
|
|
571
|
+
aria-label="Load session: {session.name}"
|
|
572
|
+
onclick={() => handleSelectSession(session.id)}
|
|
573
|
+
onkeydown={(e) => e.key === 'Enter' && handleSelectSession(session.id)}
|
|
574
|
+
>
|
|
575
|
+
<span class="playground__session-name" title={session.name}>
|
|
576
|
+
{session.name}
|
|
577
|
+
</span>
|
|
578
|
+
<button
|
|
579
|
+
type="button"
|
|
580
|
+
class="playground__session-menu"
|
|
581
|
+
class:playground__session-menu--delete={pendingDeleteId === session.id}
|
|
582
|
+
onclick={(e) => handleDeleteClick(e, session.id)}
|
|
583
|
+
title={pendingDeleteId === session.id
|
|
584
|
+
? 'Click to confirm delete'
|
|
585
|
+
: 'Delete session'}
|
|
586
|
+
>
|
|
587
|
+
{#if pendingDeleteId === session.id}
|
|
588
|
+
<Icon icon="mdi:check" />
|
|
589
|
+
{:else}
|
|
590
|
+
<Icon icon="mdi:dots-horizontal" />
|
|
591
|
+
{/if}
|
|
592
|
+
</button>
|
|
593
|
+
</div>
|
|
594
|
+
{/each}
|
|
595
|
+
{/if}
|
|
596
596
|
</div>
|
|
597
597
|
</div>
|
|
598
598
|
</div>
|
|
@@ -774,7 +774,10 @@
|
|
|
774
774
|
font-size: 0.875rem;
|
|
775
775
|
font-weight: 500;
|
|
776
776
|
cursor: pointer;
|
|
777
|
-
transition:
|
|
777
|
+
transition:
|
|
778
|
+
background-color 0.15s ease,
|
|
779
|
+
border-color 0.15s ease,
|
|
780
|
+
transform 0.1s ease;
|
|
778
781
|
box-sizing: border-box;
|
|
779
782
|
}
|
|
780
783
|
|
|
@@ -839,7 +842,9 @@
|
|
|
839
842
|
border-radius: var(--fd-radius-md);
|
|
840
843
|
border-left: 3px solid transparent;
|
|
841
844
|
cursor: pointer;
|
|
842
|
-
transition:
|
|
845
|
+
transition:
|
|
846
|
+
background-color 0.15s ease,
|
|
847
|
+
border-left-color 0.15s ease;
|
|
843
848
|
}
|
|
844
849
|
|
|
845
850
|
.playground__session:hover {
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -63,7 +63,9 @@ export { default as MessageBubble } from '../components/playground/MessageBubble
|
|
|
63
63
|
export { mountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from '../svelte-app.js';
|
|
64
64
|
export { nodeComponentRegistry, createNamespacedType, parseNamespacedType, BUILTIN_NODE_COMPONENTS, BUILTIN_NODE_TYPES, FLOWDROP_SOURCE, registerBuiltinNodes, areBuiltinsRegistered, isBuiltinType, getBuiltinTypes, resolveBuiltinAlias, registerFlowDropPlugin, unregisterFlowDropPlugin, registerCustomNode, createPlugin, isValidNamespace, getRegisteredPlugins, getPluginNodeCount } from '../registry/index.js';
|
|
65
65
|
export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from '../helpers/workflowEditorHelper.js';
|
|
66
|
-
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, connectedHandles, isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange } from '../stores/workflowStore.js';
|
|
66
|
+
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, connectedHandles, isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange, setHistoryEnabled, isHistoryEnabled, setRestoringFromHistory } from '../stores/workflowStore.js';
|
|
67
|
+
export { historyStateStore, canUndo, canRedo, historyActions, setOnRestoreCallback, historyService, HistoryService } from '../stores/historyStore.js';
|
|
68
|
+
export type { HistoryEntry, HistoryState, PushOptions } from '../stores/historyStore.js';
|
|
67
69
|
export * from '../services/api.js';
|
|
68
70
|
export { showSuccess, showError, showWarning, showInfo, showLoading, dismissToast, dismissAllToasts, showPromise, showConfirmation, apiToasts, workflowToasts, pipelineToasts } from '../services/toastService.js';
|
|
69
71
|
export { NodeExecutionService, nodeExecutionService } from '../services/nodeExecutionService.js';
|
package/dist/editor/index.js
CHANGED
|
@@ -98,7 +98,11 @@ export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, Conf
|
|
|
98
98
|
// ============================================================================
|
|
99
99
|
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, connectedHandles,
|
|
100
100
|
// Dirty state tracking
|
|
101
|
-
isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange
|
|
101
|
+
isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange,
|
|
102
|
+
// History control
|
|
103
|
+
setHistoryEnabled, isHistoryEnabled, setRestoringFromHistory } from '../stores/workflowStore.js';
|
|
104
|
+
// History Store and Service
|
|
105
|
+
export { historyStateStore, canUndo, canRedo, historyActions, setOnRestoreCallback, historyService, HistoryService } from '../stores/historyStore.js';
|
|
102
106
|
// ============================================================================
|
|
103
107
|
// Services
|
|
104
108
|
// ============================================================================
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node layout helper
|
|
3
|
+
* Utilities for node dimensions and port positions aligned to a 10px grid.
|
|
4
|
+
* Used so edge connection points (handle centers) land on multiples of 10.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Y position (in px) for the center of a port handle in a list-based node
|
|
8
|
+
* (WorkflowNode, GatewayNode). Layout: header 60px, then section title row 20px,
|
|
9
|
+
* then port rows 20px each. First port center = 90px, then 110, 130, … (multiples of 10).
|
|
10
|
+
*
|
|
11
|
+
* @param portIndex - Zero-based index in the combined list (section title counts as row 0; first port is index 0)
|
|
12
|
+
* @returns Y coordinate in px for the handle center (use with transform: translateY(-50%))
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPortCenterY(portIndex: number): number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node layout helper
|
|
3
|
+
* Utilities for node dimensions and port positions aligned to a 10px grid.
|
|
4
|
+
* Used so edge connection points (handle centers) land on multiples of 10.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Y position (in px) for the center of a port handle in a list-based node
|
|
8
|
+
* (WorkflowNode, GatewayNode). Layout: header 60px, then section title row 20px,
|
|
9
|
+
* then port rows 20px each. First port center = 90px, then 110, 130, … (multiples of 10).
|
|
10
|
+
*
|
|
11
|
+
* @param portIndex - Zero-based index in the combined list (section title counts as row 0; first port is index 0)
|
|
12
|
+
* @returns Y coordinate in px for the handle center (use with transform: translateY(-50%))
|
|
13
|
+
*/
|
|
14
|
+
export function getPortCenterY(portIndex) {
|
|
15
|
+
const headerHeight = 60;
|
|
16
|
+
const sectionTitleHeight = 20;
|
|
17
|
+
const rowHeight = 20;
|
|
18
|
+
return headerHeight + sectionTitleHeight + (portIndex + 0.5) * rowHeight;
|
|
19
|
+
}
|
|
@@ -189,8 +189,7 @@ export class EdgeStylingHelper {
|
|
|
189
189
|
break;
|
|
190
190
|
case 'trigger':
|
|
191
191
|
// Trigger edges: solid dark line for control flow
|
|
192
|
-
edge.style =
|
|
193
|
-
'stroke: var(--fd-edge-trigger); stroke-width: var(--fd-edge-trigger-width);';
|
|
192
|
+
edge.style = 'stroke: var(--fd-edge-trigger); stroke-width: var(--fd-edge-trigger-width);';
|
|
194
193
|
edge.class = 'flowdrop--edge--trigger';
|
|
195
194
|
edge.markerEnd = {
|
|
196
195
|
type: MarkerType.ArrowClosed,
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module services/autoSaveService
|
|
8
8
|
*/
|
|
9
|
-
import { get } from
|
|
10
|
-
import { behaviorSettings } from
|
|
11
|
-
import { isDirtyStore, isDirty } from
|
|
9
|
+
import { get } from 'svelte/store';
|
|
10
|
+
import { behaviorSettings } from '../stores/settingsStore.js';
|
|
11
|
+
import { isDirtyStore, isDirty } from '../stores/workflowStore.js';
|
|
12
12
|
/**
|
|
13
13
|
* Initialize auto-save functionality based on user settings
|
|
14
14
|
*
|
|
@@ -62,7 +62,7 @@ export function initAutoSave(options) {
|
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
64
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
65
|
-
console.error(
|
|
65
|
+
console.error('Auto-save failed:', err);
|
|
66
66
|
onError?.(err);
|
|
67
67
|
}
|
|
68
68
|
finally {
|
|
@@ -213,7 +213,7 @@ export class AutoSaveManager {
|
|
|
213
213
|
}
|
|
214
214
|
catch (error) {
|
|
215
215
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
216
|
-
console.error(
|
|
216
|
+
console.error('Auto-save failed:', err);
|
|
217
217
|
this.onError?.(err);
|
|
218
218
|
}
|
|
219
219
|
finally {
|