@flowdrop/flowdrop 1.8.1 → 1.10.0
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/dist/api/enhanced-client.js +5 -1
- package/dist/components/PipelineStatus.svelte +31 -8
- package/dist/components/PipelineStatus.svelte.d.ts +5 -0
- package/dist/components/WorkflowEditor.svelte +26 -0
- package/dist/components/chat/AIChatPanel.svelte +16 -5
- package/dist/components/playground/ChatPanel.svelte +31 -108
- package/dist/components/playground/ChatPanel.svelte.d.ts +3 -1
- package/dist/components/playground/ExecutionList.svelte +138 -0
- package/dist/components/playground/ExecutionList.svelte.d.ts +10 -0
- package/dist/components/playground/MessageBubble.svelte +281 -156
- package/dist/components/playground/PipelinePanel.svelte +382 -0
- package/dist/components/playground/PipelinePanel.svelte.d.ts +20 -0
- package/dist/components/playground/Playground.svelte +707 -174
- package/dist/components/playground/Playground.svelte.d.ts +6 -0
- package/dist/components/playground/PlaygroundStudio.svelte +404 -0
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +30 -0
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/playground/index.d.ts +7 -3
- package/dist/playground/index.js +14 -5
- package/dist/playground/mount.d.ts +7 -0
- package/dist/playground/mount.js +78 -81
- package/dist/services/globalSave.d.ts +7 -0
- package/dist/services/globalSave.js +5 -1
- package/dist/services/nodeExecutionService.js +4 -2
- package/dist/services/playgroundService.d.ts +11 -4
- package/dist/services/playgroundService.js +22 -12
- package/dist/stores/pipelinePanelStore.svelte.d.ts +6 -0
- package/dist/stores/pipelinePanelStore.svelte.js +24 -0
- package/dist/stores/playgroundStore.svelte.d.ts +26 -21
- package/dist/stores/playgroundStore.svelte.js +134 -55
- package/dist/svelte-app.js +25 -2
- package/dist/types/playground.d.ts +15 -5
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import PipelineStatus from '../PipelineStatus.svelte';
|
|
3
|
+
import Icon from '@iconify/svelte';
|
|
4
|
+
import type { Workflow } from '../../types/index.js';
|
|
5
|
+
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
6
|
+
import type { PlaygroundExecution } from '../../types/playground.js';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
pipelineId: string | null;
|
|
10
|
+
workflow: Workflow;
|
|
11
|
+
endpointConfig: EndpointConfig;
|
|
12
|
+
isPinned: boolean;
|
|
13
|
+
/** All executions for the current session, oldest-first */
|
|
14
|
+
executions?: PlaygroundExecution[];
|
|
15
|
+
/** ID of the most-recent execution */
|
|
16
|
+
latestExecutionId?: string | null;
|
|
17
|
+
/** Called with an execution ID to pin it, or null to follow latest */
|
|
18
|
+
onSelectExecution?: (id: string | null) => void;
|
|
19
|
+
/** Increments when new messages arrive — forwarded to PipelineStatus for immediate refresh */
|
|
20
|
+
refreshTrigger?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
pipelineId,
|
|
25
|
+
workflow,
|
|
26
|
+
endpointConfig,
|
|
27
|
+
isPinned,
|
|
28
|
+
executions = [],
|
|
29
|
+
latestExecutionId = null,
|
|
30
|
+
onSelectExecution,
|
|
31
|
+
refreshTrigger = 0,
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
|
|
34
|
+
let runDropdownOpen = $state(false);
|
|
35
|
+
let chipWrapEl = $state<HTMLElement | null>(null);
|
|
36
|
+
|
|
37
|
+
// Close run popover on outside click
|
|
38
|
+
$effect(() => {
|
|
39
|
+
if (!runDropdownOpen) return;
|
|
40
|
+
function handleOutside(e: MouseEvent) {
|
|
41
|
+
if (!chipWrapEl?.contains(e.target as Node)) {
|
|
42
|
+
runDropdownOpen = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
document.addEventListener('click', handleOutside);
|
|
46
|
+
return () => document.removeEventListener('click', handleOutside);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function statusIcon(status: PlaygroundExecution['status']): string {
|
|
50
|
+
if (status === 'running') return 'mdi:loading';
|
|
51
|
+
if (status === 'failed') return 'mdi:alert-circle';
|
|
52
|
+
return 'mdi:check-circle';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function statusClass(status: PlaygroundExecution['status']): string {
|
|
56
|
+
if (status === 'running') return 'pipeline-panel__run-status--running';
|
|
57
|
+
if (status === 'failed') return 'pipeline-panel__run-status--failed';
|
|
58
|
+
return 'pipeline-panel__run-status--completed';
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<div class="pipeline-panel">
|
|
63
|
+
<div class="pipeline-panel__header">
|
|
64
|
+
<Icon icon="mdi:source-branch" class="pipeline-panel__icon" />
|
|
65
|
+
<span class="pipeline-panel__title">Pipeline</span>
|
|
66
|
+
|
|
67
|
+
{#if pipelineId && executions.length > 0}
|
|
68
|
+
<!-- Run picker chip -->
|
|
69
|
+
<div class="pipeline-panel__run-chip-wrap" bind:this={chipWrapEl}>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="pipeline-panel__run-chip"
|
|
73
|
+
class:pipeline-panel__run-chip--pinned={isPinned}
|
|
74
|
+
class:pipeline-panel__run-chip--open={runDropdownOpen}
|
|
75
|
+
onclick={() => (runDropdownOpen = !runDropdownOpen)}
|
|
76
|
+
title="Switch run"
|
|
77
|
+
>
|
|
78
|
+
<span class="pipeline-panel__run-chip-label">{pipelineId ?? 'Run'}</span>
|
|
79
|
+
<Icon icon={runDropdownOpen ? 'mdi:chevron-up' : 'mdi:chevron-down'} class="pipeline-panel__run-chip-chevron" />
|
|
80
|
+
</button>
|
|
81
|
+
|
|
82
|
+
{#if runDropdownOpen}
|
|
83
|
+
<div class="pipeline-panel__run-popover">
|
|
84
|
+
{#each [...executions].reverse() as exec (exec.id)}
|
|
85
|
+
{@const isActive = pipelineId === exec.id}
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
class="pipeline-panel__run-popover-item"
|
|
89
|
+
class:pipeline-panel__run-popover-item--active={isActive}
|
|
90
|
+
onclick={() => {
|
|
91
|
+
onSelectExecution?.(exec.id === latestExecutionId ? null : exec.id);
|
|
92
|
+
runDropdownOpen = false;
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
<Icon
|
|
96
|
+
icon={statusIcon(exec.status)}
|
|
97
|
+
class="pipeline-panel__run-status {statusClass(exec.status)}"
|
|
98
|
+
/>
|
|
99
|
+
<span class="pipeline-panel__run-id">{exec.id}</span>
|
|
100
|
+
{#if isActive}
|
|
101
|
+
<Icon icon="mdi:check" class="pipeline-panel__run-popover-check" />
|
|
102
|
+
{/if}
|
|
103
|
+
</button>
|
|
104
|
+
{/each}
|
|
105
|
+
</div>
|
|
106
|
+
{/if}
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<!-- Latest toggle -->
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
class="pipeline-panel__latest-toggle"
|
|
113
|
+
class:pipeline-panel__latest-toggle--active={!isPinned}
|
|
114
|
+
onclick={() => {
|
|
115
|
+
if (isPinned) {
|
|
116
|
+
onSelectExecution?.(null);
|
|
117
|
+
} else {
|
|
118
|
+
onSelectExecution?.(latestExecutionId);
|
|
119
|
+
}
|
|
120
|
+
}}
|
|
121
|
+
title={isPinned ? 'Following latest is off — click to resume' : 'Always showing the most recent run'}
|
|
122
|
+
>
|
|
123
|
+
<Icon icon="mdi:refresh" />
|
|
124
|
+
Latest
|
|
125
|
+
</button>
|
|
126
|
+
{:else if pipelineId}
|
|
127
|
+
<span
|
|
128
|
+
class="pipeline-panel__status-badge pipeline-panel__status-badge--live"
|
|
129
|
+
title="Showing the most recent execution"
|
|
130
|
+
>Latest</span>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{#if pipelineId}
|
|
135
|
+
{#key pipelineId}
|
|
136
|
+
<div class="pipeline-panel__content">
|
|
137
|
+
<PipelineStatus {pipelineId} {workflow} {endpointConfig} runLabel={pipelineId ?? undefined} {refreshTrigger} isEmbedded={true} />
|
|
138
|
+
</div>
|
|
139
|
+
{/key}
|
|
140
|
+
{:else}
|
|
141
|
+
<div class="pipeline-panel__empty">
|
|
142
|
+
<Icon icon="mdi:source-branch" class="pipeline-panel__empty-icon" />
|
|
143
|
+
<p class="pipeline-panel__empty-text">Run the workflow to see the pipeline.</p>
|
|
144
|
+
</div>
|
|
145
|
+
{/if}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<style>
|
|
149
|
+
.pipeline-panel {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
height: 100%;
|
|
153
|
+
min-height: 0;
|
|
154
|
+
overflow: hidden;
|
|
155
|
+
background-color: var(--fd-muted);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.pipeline-panel__header {
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: var(--fd-space-xs);
|
|
162
|
+
padding: 0 var(--fd-space-xl);
|
|
163
|
+
height: var(--fd-playground-header-height);
|
|
164
|
+
min-height: var(--fd-playground-header-height);
|
|
165
|
+
border-bottom: 1px solid var(--fd-border);
|
|
166
|
+
flex-shrink: 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
:global(.pipeline-panel__icon) {
|
|
170
|
+
font-size: var(--fd-text-base);
|
|
171
|
+
color: var(--fd-muted-foreground);
|
|
172
|
+
flex-shrink: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.pipeline-panel__title {
|
|
176
|
+
font-size: var(--fd-text-sm);
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
color: var(--fd-foreground);
|
|
179
|
+
flex: 1;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.pipeline-panel__status-badge {
|
|
183
|
+
font-size: var(--fd-text-2xs);
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
text-transform: uppercase;
|
|
186
|
+
letter-spacing: 0.05em;
|
|
187
|
+
padding: 2px var(--fd-space-xs);
|
|
188
|
+
border-radius: var(--fd-radius-sm);
|
|
189
|
+
flex-shrink: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.pipeline-panel__status-badge--live {
|
|
193
|
+
background-color: var(--fd-success-muted);
|
|
194
|
+
color: var(--fd-success);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Run picker chip */
|
|
198
|
+
.pipeline-panel__run-chip-wrap {
|
|
199
|
+
position: relative;
|
|
200
|
+
flex-shrink: 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.pipeline-panel__run-chip {
|
|
204
|
+
display: inline-flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
gap: var(--fd-space-3xs);
|
|
207
|
+
padding: 2px var(--fd-space-xs) 2px var(--fd-space-sm);
|
|
208
|
+
border: 1px solid var(--fd-border);
|
|
209
|
+
border-radius: var(--fd-radius-md);
|
|
210
|
+
background: var(--fd-background);
|
|
211
|
+
color: var(--fd-foreground);
|
|
212
|
+
font-size: var(--fd-text-xs);
|
|
213
|
+
font-weight: 500;
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
transition: all var(--fd-transition-fast);
|
|
216
|
+
line-height: 1;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.pipeline-panel__run-chip:hover,
|
|
220
|
+
.pipeline-panel__run-chip--open {
|
|
221
|
+
background-color: var(--fd-muted);
|
|
222
|
+
border-color: var(--fd-border-strong);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.pipeline-panel__run-chip--pinned {
|
|
226
|
+
background-color: var(--fd-primary-muted);
|
|
227
|
+
border-color: transparent;
|
|
228
|
+
color: var(--fd-primary);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.pipeline-panel__run-chip--pinned:hover,
|
|
232
|
+
.pipeline-panel__run-chip--pinned.pipeline-panel__run-chip--open {
|
|
233
|
+
border-color: var(--fd-primary);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
:global(.pipeline-panel__run-chip-chevron) {
|
|
237
|
+
color: var(--fd-muted-foreground);
|
|
238
|
+
font-size: var(--fd-text-xs);
|
|
239
|
+
flex-shrink: 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Run popover */
|
|
243
|
+
.pipeline-panel__run-popover {
|
|
244
|
+
position: absolute;
|
|
245
|
+
top: calc(100% + var(--fd-space-xs));
|
|
246
|
+
right: 0;
|
|
247
|
+
z-index: 50;
|
|
248
|
+
min-width: 160px;
|
|
249
|
+
padding: var(--fd-space-xs);
|
|
250
|
+
background-color: var(--fd-background);
|
|
251
|
+
border: 1px solid var(--fd-border);
|
|
252
|
+
border-radius: var(--fd-radius-lg);
|
|
253
|
+
box-shadow: var(--fd-shadow-lg);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.pipeline-panel__run-popover-item {
|
|
257
|
+
display: flex;
|
|
258
|
+
align-items: center;
|
|
259
|
+
gap: var(--fd-space-sm);
|
|
260
|
+
width: 100%;
|
|
261
|
+
padding: var(--fd-space-sm) var(--fd-space-sm);
|
|
262
|
+
border: none;
|
|
263
|
+
border-radius: var(--fd-radius-sm);
|
|
264
|
+
background: transparent;
|
|
265
|
+
color: var(--fd-foreground);
|
|
266
|
+
font-size: var(--fd-text-sm);
|
|
267
|
+
text-align: left;
|
|
268
|
+
cursor: pointer;
|
|
269
|
+
transition: background-color var(--fd-transition-fast);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.pipeline-panel__run-popover-item:hover {
|
|
273
|
+
background-color: var(--fd-muted);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.pipeline-panel__run-popover-item--active {
|
|
277
|
+
font-weight: 500;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.pipeline-panel__run-id {
|
|
281
|
+
flex: 1;
|
|
282
|
+
min-width: 0;
|
|
283
|
+
overflow: hidden;
|
|
284
|
+
text-overflow: ellipsis;
|
|
285
|
+
white-space: nowrap;
|
|
286
|
+
font-family: var(--fd-font-mono, monospace);
|
|
287
|
+
font-size: var(--fd-text-xs);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
:global(.pipeline-panel__run-popover-check) {
|
|
291
|
+
color: var(--fd-primary) !important;
|
|
292
|
+
margin-left: auto;
|
|
293
|
+
font-size: var(--fd-text-sm);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
:global(.pipeline-panel__run-status) {
|
|
297
|
+
flex-shrink: 0;
|
|
298
|
+
font-size: var(--fd-text-sm);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
:global(.pipeline-panel__run-status--running) {
|
|
302
|
+
color: var(--fd-warning);
|
|
303
|
+
animation: pp-spin 1s linear infinite;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
:global(.pipeline-panel__run-status--completed) {
|
|
307
|
+
color: var(--fd-success);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
:global(.pipeline-panel__run-status--failed) {
|
|
311
|
+
color: var(--fd-error);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@keyframes pp-spin {
|
|
315
|
+
from { transform: rotate(0deg); }
|
|
316
|
+
to { transform: rotate(360deg); }
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Latest toggle */
|
|
320
|
+
.pipeline-panel__latest-toggle {
|
|
321
|
+
display: inline-flex;
|
|
322
|
+
align-items: center;
|
|
323
|
+
gap: var(--fd-space-3xs);
|
|
324
|
+
padding: var(--fd-space-3xs) var(--fd-space-sm);
|
|
325
|
+
border: 1px solid var(--fd-border);
|
|
326
|
+
border-radius: var(--fd-radius-md);
|
|
327
|
+
background: transparent;
|
|
328
|
+
color: var(--fd-muted-foreground);
|
|
329
|
+
font-size: var(--fd-text-xs);
|
|
330
|
+
font-weight: 500;
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
transition: all var(--fd-transition-fast);
|
|
333
|
+
line-height: 1;
|
|
334
|
+
flex-shrink: 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.pipeline-panel__latest-toggle :global(svg) {
|
|
338
|
+
font-size: var(--fd-text-xs);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.pipeline-panel__latest-toggle:hover {
|
|
342
|
+
background-color: var(--fd-muted);
|
|
343
|
+
color: var(--fd-foreground);
|
|
344
|
+
border-color: var(--fd-border-strong);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.pipeline-panel__latest-toggle--active {
|
|
348
|
+
background-color: var(--fd-primary-muted);
|
|
349
|
+
border-color: var(--fd-primary);
|
|
350
|
+
color: var(--fd-primary);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.pipeline-panel__content {
|
|
354
|
+
flex: 1;
|
|
355
|
+
min-height: 0;
|
|
356
|
+
overflow: hidden;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.pipeline-panel__empty {
|
|
360
|
+
flex: 1;
|
|
361
|
+
display: flex;
|
|
362
|
+
flex-direction: column;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: center;
|
|
365
|
+
gap: var(--fd-space-md);
|
|
366
|
+
color: var(--fd-muted-foreground);
|
|
367
|
+
padding: var(--fd-space-4xl);
|
|
368
|
+
text-align: center;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
:global(.pipeline-panel__empty-icon) {
|
|
372
|
+
font-size: var(--fd-space-6xl);
|
|
373
|
+
opacity: 0.4;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.pipeline-panel__empty-text {
|
|
377
|
+
font-size: var(--fd-text-sm);
|
|
378
|
+
margin: 0;
|
|
379
|
+
max-width: 200px;
|
|
380
|
+
line-height: var(--fd-leading-relaxed);
|
|
381
|
+
}
|
|
382
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Workflow } from '../../types/index.js';
|
|
2
|
+
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
3
|
+
import type { PlaygroundExecution } from '../../types/playground.js';
|
|
4
|
+
interface Props {
|
|
5
|
+
pipelineId: string | null;
|
|
6
|
+
workflow: Workflow;
|
|
7
|
+
endpointConfig: EndpointConfig;
|
|
8
|
+
isPinned: boolean;
|
|
9
|
+
/** All executions for the current session, oldest-first */
|
|
10
|
+
executions?: PlaygroundExecution[];
|
|
11
|
+
/** ID of the most-recent execution */
|
|
12
|
+
latestExecutionId?: string | null;
|
|
13
|
+
/** Called with an execution ID to pin it, or null to follow latest */
|
|
14
|
+
onSelectExecution?: (id: string | null) => void;
|
|
15
|
+
/** Increments when new messages arrive — forwarded to PipelineStatus for immediate refresh */
|
|
16
|
+
refreshTrigger?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const PipelinePanel: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type PipelinePanel = ReturnType<typeof PipelinePanel>;
|
|
20
|
+
export default PipelinePanel;
|