@flowdrop/flowdrop 1.14.0 → 2.0.0-beta.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/CHANGELOG.md +475 -0
- package/MIGRATION-2.0.md +472 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +162 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +110 -66
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +16 -67
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +45 -40
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +118 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
- package/dist/components/chat/AIChatPanel.svelte +93 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +29 -13
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +4 -2
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +16 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +292 -0
- package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +65 -6
- package/dist/components/playground/PipelinePanel.svelte +11 -5
- package/dist/components/playground/PipelineTableView.svelte +186 -44
- package/dist/components/playground/Playground.svelte +95 -92
- package/dist/components/playground/Playground.svelte.d.ts +2 -0
- package/dist/components/playground/PlaygroundApp.svelte +6 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundModal.svelte +13 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundStudio.svelte +34 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
- package/dist/config/endpoints.d.ts +0 -7
- package/dist/config/endpoints.js +2 -10
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/display/index.d.ts +0 -2
- package/dist/display/index.js +0 -6
- package/dist/editor/index.d.ts +19 -20
- package/dist/editor/index.js +25 -35
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +17 -13
- package/dist/form/full.js +22 -27
- package/dist/form/index.d.ts +3 -3
- package/dist/form/index.js +3 -3
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +7 -3
- package/dist/helpers/proximityConnect.js +19 -6
- package/dist/helpers/workflowEditorHelper.d.ts +12 -5
- package/dist/helpers/workflowEditorHelper.js +27 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +227 -164
- package/dist/playground/index.d.ts +2 -3
- package/dist/playground/index.js +2 -30
- package/dist/playground/mount.d.ts +15 -0
- package/dist/playground/mount.js +46 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodes.d.ts +1 -26
- package/dist/registry/builtinNodes.js +14 -50
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +53 -6
- package/dist/services/agentSpecExecutionService.js +0 -1
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +5 -22
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/chatService.d.ts +8 -4
- package/dist/services/chatService.js +15 -15
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +14 -9
- package/dist/services/interruptService.js +27 -27
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +14 -9
- package/dist/services/playgroundService.js +31 -30
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +45 -0
- package/dist/stores/apiContext.js +65 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +70 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -216
- package/dist/stores/playgroundStore.svelte.js +515 -572
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +157 -53
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/formMerge.d.ts +36 -0
- package/dist/utils/formMerge.js +70 -0
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -19
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +6 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
|
@@ -17,8 +17,14 @@
|
|
|
17
17
|
formatExecutionDuration,
|
|
18
18
|
formatLastExecuted
|
|
19
19
|
} from '../utils/nodeStatus.js';
|
|
20
|
+
import { formatMicroseconds } from '../utils/duration.js';
|
|
20
21
|
import { m } from '../messages/index.js';
|
|
21
22
|
|
|
23
|
+
/** Prefer the precise µs duration; fall back to the legacy ms value. */
|
|
24
|
+
function formatDuration(us: number | null | undefined, ms: number | null | undefined): string {
|
|
25
|
+
return formatMicroseconds(us) ?? formatExecutionDuration(ms ?? undefined);
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
interface Props {
|
|
23
29
|
nodeId?: string;
|
|
24
30
|
executionInfo?: NodeExecutionInfo;
|
|
@@ -81,6 +87,13 @@
|
|
|
81
87
|
executionInfo.status !== 'idle' || executionInfo.executionCount > 0 || executionInfo.isExecuting
|
|
82
88
|
);
|
|
83
89
|
|
|
90
|
+
// Number of jobs behind this node's status. Loop iterations create
|
|
91
|
+
// multiple jobs per node — including a never-started job swept to
|
|
92
|
+
// "skipped" when the loop exits — so the per-job history (when known)
|
|
93
|
+
// beats the started-runs count: a skipped node with an earlier completed
|
|
94
|
+
// run must still flag that there is more to inspect.
|
|
95
|
+
let runCount = $derived(executionInfo.jobs?.length ?? executionInfo.executionCount);
|
|
96
|
+
|
|
84
97
|
// Hoist the overlay branch — seven reads in the template.
|
|
85
98
|
const overlay = $derived(m().status.overlay);
|
|
86
99
|
</script>
|
|
@@ -88,6 +101,7 @@
|
|
|
88
101
|
{#if shouldShow}
|
|
89
102
|
<div
|
|
90
103
|
class="node-status-overlay"
|
|
104
|
+
data-node-id={props.nodeId}
|
|
91
105
|
class:node-status-overlay--hovered={isHovered}
|
|
92
106
|
class:node-status-overlay--top-left={true}
|
|
93
107
|
class:node-status-overlay--sm={size === 'sm'}
|
|
@@ -126,10 +140,11 @@
|
|
|
126
140
|
/>
|
|
127
141
|
</div>
|
|
128
142
|
|
|
129
|
-
<!--
|
|
130
|
-
|
|
143
|
+
<!-- Run Count Badge: only meaningful when the node has more than one
|
|
144
|
+
job (loop iterations) — a "1" on every executed node is noise -->
|
|
145
|
+
{#if runCount > 1}
|
|
131
146
|
<div class="node-status-overlay__count">
|
|
132
|
-
{
|
|
147
|
+
×{runCount}
|
|
133
148
|
</div>
|
|
134
149
|
{/if}
|
|
135
150
|
|
|
@@ -154,11 +169,14 @@
|
|
|
154
169
|
>
|
|
155
170
|
</div>
|
|
156
171
|
{/if}
|
|
157
|
-
{#if executionInfo.lastExecutionDuration}
|
|
172
|
+
{#if executionInfo.lastExecutionDurationUs || executionInfo.lastExecutionDuration}
|
|
158
173
|
<div class="node-status-overlay__detail-item">
|
|
159
174
|
<span class="node-status-overlay__detail-label">{overlay.durationLabel}</span>
|
|
160
175
|
<span class="node-status-overlay__detail-value"
|
|
161
|
-
>{
|
|
176
|
+
>{formatDuration(
|
|
177
|
+
executionInfo.lastExecutionDurationUs,
|
|
178
|
+
executionInfo.lastExecutionDuration
|
|
179
|
+
)}</span
|
|
162
180
|
>
|
|
163
181
|
</div>
|
|
164
182
|
{/if}
|
|
@@ -168,6 +186,33 @@
|
|
|
168
186
|
<span class="node-status-overlay__detail-value">{executionInfo.lastError}</span>
|
|
169
187
|
</div>
|
|
170
188
|
{/if}
|
|
189
|
+
<!-- Per-job history: loop iterations create multiple jobs for the
|
|
190
|
+
same node; list them so earlier runs stay inspectable -->
|
|
191
|
+
{#if executionInfo.jobs && executionInfo.jobs.length > 1}
|
|
192
|
+
<div class="node-status-overlay__history">
|
|
193
|
+
<span class="node-status-overlay__detail-label">{overlay.historyLabel}</span>
|
|
194
|
+
{#each executionInfo.jobs as job, i (job.id ?? i)}
|
|
195
|
+
<div class="node-status-overlay__history-item">
|
|
196
|
+
<span
|
|
197
|
+
class="node-status-overlay__history-dot"
|
|
198
|
+
style="background-color: {getStatusColor(job.status)}"
|
|
199
|
+
></span>
|
|
200
|
+
<span class="node-status-overlay__history-label" title={job.label}
|
|
201
|
+
>{job.label ?? `#${i + 1}`}</span
|
|
202
|
+
>
|
|
203
|
+
<span
|
|
204
|
+
class="node-status-overlay__history-status"
|
|
205
|
+
style="color: {getStatusColor(job.status)}">{getStatusLabel(job.status)}</span
|
|
206
|
+
>
|
|
207
|
+
{#if (job.executionTimeUs != null && job.executionTimeUs > 0) || (job.executionTime != null && job.executionTime > 0)}
|
|
208
|
+
<span class="node-status-overlay__history-duration"
|
|
209
|
+
>{formatDuration(job.executionTimeUs, job.executionTime)}</span
|
|
210
|
+
>
|
|
211
|
+
{/if}
|
|
212
|
+
</div>
|
|
213
|
+
{/each}
|
|
214
|
+
</div>
|
|
215
|
+
{/if}
|
|
171
216
|
</div>
|
|
172
217
|
{/if}
|
|
173
218
|
</div>
|
|
@@ -180,7 +225,9 @@
|
|
|
180
225
|
align-items: center;
|
|
181
226
|
gap: 0.75rem;
|
|
182
227
|
z-index: 1000;
|
|
183
|
-
pointer
|
|
228
|
+
/* Must receive pointer events at rest — the hover details panel (and
|
|
229
|
+
per-job history) is only reachable if mouseenter can fire here. */
|
|
230
|
+
pointer-events: auto;
|
|
184
231
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
185
232
|
height: 48px;
|
|
186
233
|
width: auto;
|
|
@@ -304,6 +351,47 @@
|
|
|
304
351
|
color: #ef4444;
|
|
305
352
|
}
|
|
306
353
|
|
|
354
|
+
.node-status-overlay__history {
|
|
355
|
+
margin-top: 0.5rem;
|
|
356
|
+
padding-top: 0.5rem;
|
|
357
|
+
border-top: 1px solid #e5e7eb;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.node-status-overlay__history-item {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
gap: 0.375rem;
|
|
364
|
+
margin-top: 0.25rem;
|
|
365
|
+
font-size: 0.75rem;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.node-status-overlay__history-dot {
|
|
369
|
+
width: 0.5rem;
|
|
370
|
+
height: 0.5rem;
|
|
371
|
+
border-radius: 50%;
|
|
372
|
+
flex-shrink: 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.node-status-overlay__history-label {
|
|
376
|
+
flex: 1;
|
|
377
|
+
min-width: 0;
|
|
378
|
+
font-weight: 500;
|
|
379
|
+
color: #374151;
|
|
380
|
+
overflow: hidden;
|
|
381
|
+
text-overflow: ellipsis;
|
|
382
|
+
white-space: nowrap;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.node-status-overlay__history-status {
|
|
386
|
+
font-weight: 600;
|
|
387
|
+
white-space: nowrap;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.node-status-overlay__history-duration {
|
|
391
|
+
color: #6b7280;
|
|
392
|
+
white-space: nowrap;
|
|
393
|
+
}
|
|
394
|
+
|
|
307
395
|
/* Size variants */
|
|
308
396
|
.node-status-overlay--sm {
|
|
309
397
|
gap: 0.125rem;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { getNodeIcon, getCategoryIcon } from '../utils/icons.js';
|
|
12
12
|
import { getCategoryColorToken } from '../utils/colors.js';
|
|
13
13
|
import { m } from '../messages/index.js';
|
|
14
|
-
import {
|
|
14
|
+
import { getInstance } from '../stores/getInstance.svelte.js';
|
|
15
15
|
import { getVersionUpgrade } from '../utils/nodeSwap.js';
|
|
16
16
|
|
|
17
17
|
interface Props {
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
|
|
30
30
|
const { currentNode, availableNodes, activeFormat, onSelect, onCancel }: Props = $props();
|
|
31
31
|
|
|
32
|
+
const fd = getInstance();
|
|
33
|
+
|
|
32
34
|
let searchInput = $state('');
|
|
33
35
|
|
|
34
36
|
/** Check version upgrade availability */
|
|
@@ -134,14 +136,14 @@
|
|
|
134
136
|
<!-- Flat style: dot + name rows (shown in minimal skin) -->
|
|
135
137
|
<div class="swap-picker__flat-section">
|
|
136
138
|
<div class="swap-picker__flat-category">
|
|
137
|
-
{
|
|
139
|
+
{fd.categories.getLabel(category).toUpperCase()}
|
|
138
140
|
</div>
|
|
139
141
|
<div class="swap-picker__flat-list">
|
|
140
142
|
{#each categoryNodes as nodeType (nodeType.id)}
|
|
141
143
|
<button class="swap-picker__flat-item" onclick={() => onSelect(nodeType)}>
|
|
142
144
|
<span
|
|
143
145
|
class="swap-picker__flat-dot"
|
|
144
|
-
style="background: {getCategoryColorToken(nodeType.category)}"
|
|
146
|
+
style="background: {getCategoryColorToken(fd.categories, nodeType.category)}"
|
|
145
147
|
></span>
|
|
146
148
|
<span class="swap-picker__flat-name">{nodeType.name}</span>
|
|
147
149
|
</button>
|
|
@@ -154,12 +156,12 @@
|
|
|
154
156
|
<div class="swap-picker__category-header">
|
|
155
157
|
<span
|
|
156
158
|
class="swap-picker__category-icon"
|
|
157
|
-
style="--_icon-color: {getCategoryColorToken(category)}"
|
|
159
|
+
style="--_icon-color: {getCategoryColorToken(fd.categories, category)}"
|
|
158
160
|
>
|
|
159
|
-
<Icon icon={getCategoryIcon(category)} />
|
|
161
|
+
<Icon icon={getCategoryIcon(fd.categories, category)} />
|
|
160
162
|
</span>
|
|
161
163
|
<span class="swap-picker__category-name">
|
|
162
|
-
{
|
|
164
|
+
{fd.categories.getLabel(category)}
|
|
163
165
|
</span>
|
|
164
166
|
<span class="swap-picker__category-count">
|
|
165
167
|
{categoryNodes.length}
|
|
@@ -170,9 +172,9 @@
|
|
|
170
172
|
<button class="swap-picker__item" onclick={() => onSelect(nodeType)}>
|
|
171
173
|
<span
|
|
172
174
|
class="swap-picker__item-icon"
|
|
173
|
-
style="--_icon-color: {getCategoryColorToken(nodeType.category)}"
|
|
175
|
+
style="--_icon-color: {getCategoryColorToken(fd.categories, nodeType.category)}"
|
|
174
176
|
>
|
|
175
|
-
<Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
|
|
177
|
+
<Icon icon={getNodeIcon(fd.categories, nodeType.icon, nodeType.category)} />
|
|
176
178
|
</span>
|
|
177
179
|
<div class="swap-picker__item-info">
|
|
178
180
|
<span class="swap-picker__item-name">{nodeType.name}</span>
|
|
@@ -54,46 +54,22 @@
|
|
|
54
54
|
let _prevRefreshTrigger = refreshTrigger;
|
|
55
55
|
|
|
56
56
|
// Initialize API client if not provided
|
|
57
|
-
//
|
|
57
|
+
// client created once from props
|
|
58
|
+
// svelte-ignore state_referenced_locally
|
|
58
59
|
const client =
|
|
59
60
|
apiClient ||
|
|
60
61
|
new EnhancedFlowDropApiClient(
|
|
61
62
|
endpointConfig ?? createEndpointConfig(baseUrl || '/api/flowdrop')
|
|
62
63
|
);
|
|
63
64
|
|
|
64
|
-
// Pipeline status and
|
|
65
|
+
// Pipeline status — drives breadcrumb label and the 5s poll while running
|
|
65
66
|
let pipelineStatus = $state<string>('unknown');
|
|
66
|
-
|
|
67
|
-
status: string;
|
|
68
|
-
[key: string]: unknown;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
let jobStatusData = $state<{
|
|
72
|
-
jobs: Record<string, unknown>[];
|
|
73
|
-
node_statuses: Record<string, PipelineNodeStatus>;
|
|
74
|
-
status_summary: {
|
|
75
|
-
total: number;
|
|
76
|
-
pending: number;
|
|
77
|
-
running: number;
|
|
78
|
-
completed: number;
|
|
79
|
-
failed: number;
|
|
80
|
-
cancelled: number;
|
|
81
|
-
};
|
|
82
|
-
}>({
|
|
83
|
-
jobs: [],
|
|
84
|
-
node_statuses: {},
|
|
85
|
-
status_summary: {
|
|
86
|
-
total: 0,
|
|
87
|
-
pending: 0,
|
|
88
|
-
running: 0,
|
|
89
|
-
completed: 0,
|
|
90
|
-
failed: 0,
|
|
91
|
-
cancelled: 0
|
|
92
|
-
}
|
|
93
|
-
});
|
|
67
|
+
let totalJobs = $state<number>(0);
|
|
94
68
|
|
|
95
|
-
//
|
|
96
|
-
|
|
69
|
+
// Child refresh counter — increments on every successful fetch so the
|
|
70
|
+
// embedded canvas (WorkflowEditor) re-pulls execution info from the server.
|
|
71
|
+
// PipelineStatus no longer owns a denormalized status channel; it just signals.
|
|
72
|
+
let childRefreshTrigger = $state(0);
|
|
97
73
|
|
|
98
74
|
// Loading and error states
|
|
99
75
|
let isLoadingJobStatus = $state(false);
|
|
@@ -113,39 +89,13 @@
|
|
|
113
89
|
const pipelineData = await client.getPipelineData(pipelineId);
|
|
114
90
|
|
|
115
91
|
pipelineStatus = pipelineData.status;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
pending: 0,
|
|
122
|
-
running: 0,
|
|
123
|
-
completed: 0,
|
|
124
|
-
failed: 0,
|
|
125
|
-
cancelled: 0
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Update node statuses based on job data — only set what the server reported
|
|
130
|
-
if (jobStatusData.node_statuses) {
|
|
131
|
-
const newNodeStatuses: Record<string, 'pending' | 'running' | 'completed' | 'error'> = {};
|
|
132
|
-
|
|
133
|
-
for (const nodeId in jobStatusData.node_statuses) {
|
|
134
|
-
const status = jobStatusData.node_statuses[nodeId].status;
|
|
135
|
-
if (status === 'failed' || status === 'cancelled') {
|
|
136
|
-
newNodeStatuses[nodeId] = 'error';
|
|
137
|
-
} else if (status === 'running' || status === 'paused' || status === 'interrupted') {
|
|
138
|
-
newNodeStatuses[nodeId] = 'running';
|
|
139
|
-
} else if (status === 'completed' || status === 'skipped') {
|
|
140
|
-
newNodeStatuses[nodeId] = 'completed';
|
|
141
|
-
} else if (status === 'pending' || status === 'idle') {
|
|
142
|
-
newNodeStatuses[nodeId] = 'pending';
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
nodeStatuses = newNodeStatuses;
|
|
146
|
-
}
|
|
92
|
+
totalJobs = pipelineData.job_status_summary?.total ?? 0;
|
|
93
|
+
|
|
94
|
+
// Signal the embedded canvas to re-fetch its own node execution info.
|
|
95
|
+
// It owns the read; we just tell it when to look again.
|
|
96
|
+
childRefreshTrigger += 1;
|
|
147
97
|
|
|
148
|
-
addLog('info', `Job status updated: ${
|
|
98
|
+
addLog('info', `Job status updated: ${totalJobs} total jobs`);
|
|
149
99
|
} catch (error) {
|
|
150
100
|
logger.error('Failed to fetch pipeline data:', error);
|
|
151
101
|
addLog(
|
|
@@ -317,10 +267,9 @@
|
|
|
317
267
|
width="100%"
|
|
318
268
|
showNavbar={false}
|
|
319
269
|
disableSidebar={true}
|
|
320
|
-
|
|
321
|
-
readOnly={true}
|
|
322
|
-
{nodeStatuses}
|
|
270
|
+
mode="locked"
|
|
323
271
|
{pipelineId}
|
|
272
|
+
refreshTrigger={childRefreshTrigger}
|
|
324
273
|
{endpointConfig}
|
|
325
274
|
/>
|
|
326
275
|
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import { useSvelteFlow, type InternalNode } from '@xyflow/svelte';
|
|
12
12
|
import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
|
|
13
|
-
import {
|
|
14
|
-
rebuildAllPortCoordinates,
|
|
15
|
-
updateNodePortCoordinates
|
|
16
|
-
} from '../stores/portCoordinateStore.svelte.js';
|
|
13
|
+
import { getInstance } from '../stores/getInstance.svelte.js';
|
|
17
14
|
|
|
18
15
|
interface Props {
|
|
19
16
|
/** Node to update coordinates for (e.g., during drag). Set to null when not dragging. */
|
|
@@ -26,6 +23,8 @@
|
|
|
26
23
|
|
|
27
24
|
let { nodeToUpdate, rebuildTrigger, nodes }: Props = $props();
|
|
28
25
|
|
|
26
|
+
const fd = getInstance();
|
|
27
|
+
|
|
29
28
|
const { getInternalNode } = useSvelteFlow();
|
|
30
29
|
|
|
31
30
|
// Cast the getInternalNode function for our use
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
const _trigger = rebuildTrigger;
|
|
41
40
|
if (_trigger > 0) {
|
|
42
41
|
const timeout = setTimeout(() => {
|
|
43
|
-
|
|
42
|
+
fd.portCoordinates.rebuildAll(nodes, getInternal);
|
|
44
43
|
}, 150);
|
|
45
44
|
return () => clearTimeout(timeout);
|
|
46
45
|
}
|
|
@@ -52,7 +51,7 @@
|
|
|
52
51
|
*/
|
|
53
52
|
$effect(() => {
|
|
54
53
|
if (nodeToUpdate) {
|
|
55
|
-
|
|
54
|
+
fd.portCoordinates.updateNode(nodeToUpdate, getInternal);
|
|
56
55
|
}
|
|
57
56
|
});
|
|
58
57
|
</script>
|
|
@@ -58,10 +58,12 @@
|
|
|
58
58
|
import Icon from '@iconify/svelte';
|
|
59
59
|
import type { ConfigSchema, AuthProvider } from '../types/index.js';
|
|
60
60
|
import type { UISchemaElement } from '../types/uischema.js';
|
|
61
|
+
import { provideInstance } from '../stores/getInstance.svelte.js';
|
|
61
62
|
import { FormField } from './form/index.js';
|
|
62
63
|
import FormUISchemaRenderer from './form/FormUISchemaRenderer.svelte';
|
|
63
64
|
import type { FieldSchema } from './form/index.js';
|
|
64
|
-
import { m
|
|
65
|
+
import { m } from '../messages/index.js';
|
|
66
|
+
import { mergeWithDefaults, cascadeClearAutocompleteDependents } from '../utils/formMerge.js';
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
69
|
* Props interface for SchemaForm component
|
|
@@ -100,16 +102,6 @@
|
|
|
100
102
|
*/
|
|
101
103
|
showActions?: boolean;
|
|
102
104
|
|
|
103
|
-
/**
|
|
104
|
-
* @deprecated since v1.8 — use `messages.form.schema.save`. Removed in v2.0.
|
|
105
|
-
*/
|
|
106
|
-
saveLabel?: string;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* @deprecated since v1.8 — use `messages.form.schema.cancel`. Removed in v2.0.
|
|
110
|
-
*/
|
|
111
|
-
cancelLabel?: string;
|
|
112
|
-
|
|
113
105
|
/**
|
|
114
106
|
* Callback fired when the Save button is clicked.
|
|
115
107
|
* Receives the final form values.
|
|
@@ -159,8 +151,6 @@
|
|
|
159
151
|
values = {},
|
|
160
152
|
onChange,
|
|
161
153
|
showActions = false,
|
|
162
|
-
saveLabel,
|
|
163
|
-
cancelLabel,
|
|
164
154
|
onSave,
|
|
165
155
|
onCancel,
|
|
166
156
|
loading = false,
|
|
@@ -170,17 +160,20 @@
|
|
|
170
160
|
baseUrl = ''
|
|
171
161
|
}: Props = $props();
|
|
172
162
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
163
|
+
// SchemaForm is a standalone container: its leaf <FormField>s call
|
|
164
|
+
// getInstance() and require an instance in context. When rendered without an
|
|
165
|
+
// <App>/<WorkflowEditor> ancestor (a bare embed), self-provide one so those
|
|
166
|
+
// leaves resolve. provideInstance() carries the established SSR semantics:
|
|
167
|
+
// - context instance present -> reuse it (nested inside a provider)
|
|
168
|
+
// - browser, no context -> the shared page-default instance
|
|
169
|
+
// - server, no context -> a fresh per-render instance (no leakage)
|
|
170
|
+
// We never destroy here: the browser path returns the shared default (must
|
|
171
|
+
// outlive this form) and the server path has no teardown — matching how
|
|
172
|
+
// <App> defers instance lifecycle to whoever created it.
|
|
173
|
+
provideInstance();
|
|
174
|
+
|
|
175
|
+
const resolvedSaveLabel = $derived(m().form.schema.save);
|
|
176
|
+
const resolvedCancelLabel = $derived(m().form.schema.cancel);
|
|
184
177
|
|
|
185
178
|
// Set context for child components (e.g., FormAutocomplete)
|
|
186
179
|
// Use getter functions to ensure child components always get the current prop value,
|
|
@@ -194,24 +187,25 @@
|
|
|
194
187
|
let formRef: HTMLFormElement | undefined = $state();
|
|
195
188
|
|
|
196
189
|
/**
|
|
197
|
-
*
|
|
190
|
+
* User edits — only keys the user has changed since the current schema
|
|
191
|
+
* was loaded. formValues is derived from props + edits, never synchronised
|
|
192
|
+
* via an effect, so children mount with the correct values already in place.
|
|
198
193
|
*/
|
|
199
|
-
let
|
|
194
|
+
let edits = $state<Record<string, unknown>>({});
|
|
195
|
+
|
|
196
|
+
const formValues = $derived(mergeWithDefaults(schema, values, edits));
|
|
197
|
+
|
|
200
198
|
setContext<() => Record<string, unknown>>('flowdrop:getFormValues', () => formValues);
|
|
201
199
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Use provided value if available, otherwise use schema default
|
|
212
|
-
mergedValues[key] = values[key] !== undefined ? values[key] : fieldConfig.default;
|
|
213
|
-
});
|
|
214
|
-
formValues = mergedValues;
|
|
200
|
+
// Drop edits when the schema reference changes (different form mounted).
|
|
201
|
+
// Identity comparison only — value churn in `values` preserves in-flight edits.
|
|
202
|
+
// capturing the initial prop reference is intentional; later changes are picked up by the effect below
|
|
203
|
+
// svelte-ignore state_referenced_locally
|
|
204
|
+
let prevSchemaRef = schema;
|
|
205
|
+
$effect.pre(() => {
|
|
206
|
+
if (schema !== prevSchemaRef) {
|
|
207
|
+
prevSchemaRef = schema;
|
|
208
|
+
edits = {};
|
|
215
209
|
}
|
|
216
210
|
});
|
|
217
211
|
|
|
@@ -234,7 +228,18 @@
|
|
|
234
228
|
* @param value - New field value
|
|
235
229
|
*/
|
|
236
230
|
function handleFieldChange(key: string, value: unknown): void {
|
|
237
|
-
formValues[key]
|
|
231
|
+
const previous = formValues[key];
|
|
232
|
+
edits[key] = value;
|
|
233
|
+
|
|
234
|
+
// Cascade-clear any autocomplete whose `params` references this field.
|
|
235
|
+
// Only runs on user-driven edits; undo/redo and external value-prop
|
|
236
|
+
// replacement flow through `values` and bypass this path (#33).
|
|
237
|
+
if (previous !== value) {
|
|
238
|
+
const dependents = cascadeClearAutocompleteDependents(schema, key);
|
|
239
|
+
for (const [depKey, depValue] of Object.entries(dependents)) {
|
|
240
|
+
edits[depKey] = depValue;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
238
243
|
|
|
239
244
|
// Notify parent of the change
|
|
240
245
|
if (onChange) {
|
|
@@ -32,14 +32,6 @@ interface Props {
|
|
|
32
32
|
* @default false
|
|
33
33
|
*/
|
|
34
34
|
showActions?: boolean;
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated since v1.8 — use `messages.form.schema.save`. Removed in v2.0.
|
|
37
|
-
*/
|
|
38
|
-
saveLabel?: string;
|
|
39
|
-
/**
|
|
40
|
-
* @deprecated since v1.8 — use `messages.form.schema.cancel`. Removed in v2.0.
|
|
41
|
-
*/
|
|
42
|
-
cancelLabel?: string;
|
|
43
35
|
/**
|
|
44
36
|
* Callback fired when the Save button is clicked.
|
|
45
37
|
* Receives the final form values.
|
|
@@ -59,6 +59,11 @@
|
|
|
59
59
|
class: className = ''
|
|
60
60
|
}: Props = $props();
|
|
61
61
|
|
|
62
|
+
// Unique per component instance so two FlowDrop editors on one page
|
|
63
|
+
// don't render colliding DOM ids (a11y).
|
|
64
|
+
const uid = $props.id();
|
|
65
|
+
const titleId = `${uid}-settings-modal-title`;
|
|
66
|
+
|
|
62
67
|
/**
|
|
63
68
|
* Reference to the modal dialog element
|
|
64
69
|
*/
|
|
@@ -118,19 +123,19 @@
|
|
|
118
123
|
}
|
|
119
124
|
</script>
|
|
120
125
|
|
|
121
|
-
<!--
|
|
126
|
+
<!-- native <dialog> backdrop click-to-close pattern -->
|
|
122
127
|
<dialog
|
|
123
128
|
bind:this={dialogRef}
|
|
124
129
|
class="flowdrop-settings-modal {className}"
|
|
125
130
|
onclick={handleBackdropClick}
|
|
126
131
|
onkeydown={handleKeydown}
|
|
127
132
|
onclose={handleDialogClose}
|
|
128
|
-
aria-labelledby=
|
|
133
|
+
aria-labelledby={titleId}
|
|
129
134
|
>
|
|
130
135
|
<div class="flowdrop-settings-modal__container">
|
|
131
136
|
<!-- Header -->
|
|
132
137
|
<div class="flowdrop-settings-modal__header">
|
|
133
|
-
<h2 id=
|
|
138
|
+
<h2 id={titleId} class="flowdrop-settings-modal__title">
|
|
134
139
|
<Icon icon="mdi:cog" class="flowdrop-settings-modal__title-icon" />
|
|
135
140
|
{m().navigation.settingsTitle}
|
|
136
141
|
</h2>
|
|
@@ -74,10 +74,15 @@
|
|
|
74
74
|
class: className = ''
|
|
75
75
|
}: Props = $props();
|
|
76
76
|
|
|
77
|
+
// Unique per component instance so two FlowDrop editors on one page
|
|
78
|
+
// don't render colliding tab/panel DOM ids (a11y).
|
|
79
|
+
const uid = $props.id();
|
|
80
|
+
|
|
77
81
|
/**
|
|
78
82
|
* Currently active tab
|
|
79
83
|
*/
|
|
80
|
-
//
|
|
84
|
+
// initial default, user switches tabs
|
|
85
|
+
// svelte-ignore state_referenced_locally
|
|
81
86
|
let activeTab = $state<SettingsCategory>(categories[0] ?? 'theme');
|
|
82
87
|
|
|
83
88
|
/**
|
|
@@ -216,6 +221,16 @@
|
|
|
216
221
|
maximum: 300000,
|
|
217
222
|
default: 30000
|
|
218
223
|
},
|
|
224
|
+
storeDraftsInBrowser: {
|
|
225
|
+
type: 'boolean',
|
|
226
|
+
title: 'Store Drafts in Browser',
|
|
227
|
+
description:
|
|
228
|
+
'Keep unsaved workflow drafts in browser storage so they survive page reloads. ' +
|
|
229
|
+
'Warning: drafts (including node configuration values) may stay stored on this ' +
|
|
230
|
+
'device even after the tab or browser is closed, until they are saved or cleared. ' +
|
|
231
|
+
'Turn off on shared devices.',
|
|
232
|
+
default: true
|
|
233
|
+
},
|
|
219
234
|
undoHistoryLimit: {
|
|
220
235
|
type: 'number',
|
|
221
236
|
title: 'Undo History Limit',
|
|
@@ -373,7 +388,8 @@
|
|
|
373
388
|
class:flowdrop-settings-panel__tab--active={activeTab === category}
|
|
374
389
|
role="tab"
|
|
375
390
|
aria-selected={activeTab === category}
|
|
376
|
-
aria-controls="panel-{category}"
|
|
391
|
+
aria-controls="{uid}-panel-{category}"
|
|
392
|
+
id="{uid}-tab-{category}"
|
|
377
393
|
data-tab={category}
|
|
378
394
|
tabindex={activeTab === category ? 0 : -1}
|
|
379
395
|
onclick={() => (activeTab = category)}
|
|
@@ -389,11 +405,11 @@
|
|
|
389
405
|
<div class="flowdrop-settings-panel__content">
|
|
390
406
|
{#each categories as category (category)}
|
|
391
407
|
<div
|
|
392
|
-
id="panel-{category}"
|
|
408
|
+
id="{uid}-panel-{category}"
|
|
393
409
|
class="flowdrop-settings-panel__panel"
|
|
394
410
|
class:flowdrop-settings-panel__panel--active={activeTab === category}
|
|
395
411
|
role="tabpanel"
|
|
396
|
-
aria-labelledby="tab-{category}"
|
|
412
|
+
aria-labelledby="{uid}-tab-{category}"
|
|
397
413
|
hidden={activeTab !== category}
|
|
398
414
|
>
|
|
399
415
|
{#if activeTab === category}
|