@flowdrop/flowdrop 1.13.0 → 1.14.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/README.md +5 -0
- package/dist/components/ConfigPanel.svelte +7 -1
- package/dist/components/NodeSwapPicker.svelte +5 -1
- package/dist/components/PipelineStatus.svelte +11 -2
- package/dist/components/SettingsPanel.svelte +5 -1
- package/dist/components/WorkflowEditor.svelte +5 -1
- package/dist/components/chat/AIChatPanel.svelte +1 -5
- package/dist/components/form/FormAutocomplete.svelte +2 -5
- package/dist/components/interrupt/ChoicePrompt.svelte +5 -1
- package/dist/components/interrupt/InterruptBubble.svelte +4 -5
- package/dist/components/playground/ChatBubble.svelte +6 -8
- package/dist/components/playground/ChatInput.svelte +11 -5
- package/dist/components/playground/ControlPanel.svelte +42 -29
- package/dist/components/playground/ExecutionConsole.svelte +5 -1
- package/dist/components/playground/ExecutionConsole.svelte.d.ts +2 -0
- package/dist/components/playground/ExecutionList.svelte +7 -2
- package/dist/components/playground/LogRow.svelte +2 -1
- package/dist/components/playground/MessageBubble.svelte +1 -4
- package/dist/components/playground/MessageCard.svelte +2 -1
- package/dist/components/playground/MessageMarkdown.svelte +15 -5
- package/dist/components/playground/MessageNotice.svelte +2 -1
- package/dist/components/playground/MessageStream.svelte +138 -17
- package/dist/components/playground/MessageStream.svelte.d.ts +5 -0
- package/dist/components/playground/MessageTagChip.svelte +24 -6
- package/dist/components/playground/PipelineKanbanView.svelte +40 -11
- package/dist/components/playground/PipelinePanel.svelte +5 -1
- package/dist/components/playground/PipelineTableView.svelte +20 -6
- package/dist/components/playground/Playground.svelte +84 -22
- package/dist/components/playground/PlaygroundStudio.svelte +21 -7
- package/dist/components/playground/pipelineViewUtils.svelte.js +11 -4
- package/dist/openapi/v1/openapi.yaml +6403 -0
- package/dist/schemas/v1/workflow.schema.json +36 -0
- package/dist/services/playgroundService.d.ts +23 -4
- package/dist/services/playgroundService.js +22 -9
- package/dist/stores/playgroundStore.svelte.d.ts +22 -1
- package/dist/stores/playgroundStore.svelte.js +109 -32
- package/dist/types/playground.d.ts +36 -2
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -158,6 +158,11 @@ FlowDrop provides tree-shakeable sub-module exports so you can import only what
|
|
|
158
158
|
| `@flowdrop/flowdrop/settings` | SettingsPanel, stores, services |
|
|
159
159
|
| `@flowdrop/flowdrop/styles` | Base CSS stylesheet |
|
|
160
160
|
| `@flowdrop/flowdrop/schema` | Workflow JSON schema |
|
|
161
|
+
| `@flowdrop/flowdrop/openapi` | OpenAPI spec (YAML) for the FlowDrop backend API |
|
|
162
|
+
|
|
163
|
+
### OpenAPI spec
|
|
164
|
+
|
|
165
|
+
The full OpenAPI spec for the FlowDrop backend API ships with the package, version-matched to your installed release. It defines the node-config / form-element schema (`ConfigProperty`), playground messages, and every endpoint. Resolve it from `@flowdrop/flowdrop/openapi`, or read it directly at `node_modules/@flowdrop/flowdrop/dist/openapi/v1/openapi.yaml`. Point your AI assistant at that file when authoring node config schemas. The latest spec is also browsable at [api.flowdrop.io](https://api.flowdrop.io).
|
|
161
166
|
|
|
162
167
|
## Integration
|
|
163
168
|
|
|
@@ -77,7 +77,13 @@
|
|
|
77
77
|
<Icon icon="heroicons:arrows-right-left" />
|
|
78
78
|
</button>
|
|
79
79
|
{/if}
|
|
80
|
-
<button
|
|
80
|
+
<button
|
|
81
|
+
class="config-panel__close"
|
|
82
|
+
onclick={onClose}
|
|
83
|
+
aria-label={m().layout.closeConfigPanel}
|
|
84
|
+
>
|
|
85
|
+
×
|
|
86
|
+
</button>
|
|
81
87
|
</div>
|
|
82
88
|
</div>
|
|
83
89
|
|
|
@@ -81,7 +81,11 @@
|
|
|
81
81
|
<div class="swap-picker">
|
|
82
82
|
<!-- Header -->
|
|
83
83
|
<div class="swap-picker__header">
|
|
84
|
-
<button
|
|
84
|
+
<button
|
|
85
|
+
class="swap-picker__back"
|
|
86
|
+
onclick={onCancel}
|
|
87
|
+
aria-label={m().layout.backToConfiguration}
|
|
88
|
+
>
|
|
85
89
|
<Icon icon="heroicons:arrow-left" />
|
|
86
90
|
</button>
|
|
87
91
|
<h2 class="swap-picker__title">Swap Node</h2>
|
|
@@ -37,8 +37,17 @@
|
|
|
37
37
|
) => void;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
let {
|
|
41
|
-
|
|
40
|
+
let {
|
|
41
|
+
pipelineId,
|
|
42
|
+
workflow,
|
|
43
|
+
apiClient,
|
|
44
|
+
baseUrl,
|
|
45
|
+
endpointConfig,
|
|
46
|
+
onActionsReady,
|
|
47
|
+
runLabel,
|
|
48
|
+
isEmbedded = false,
|
|
49
|
+
refreshTrigger = 0
|
|
50
|
+
}: Props = $props();
|
|
42
51
|
|
|
43
52
|
// Track previous trigger value so the $effect only fires on increments, not on initial mount.
|
|
44
53
|
// svelte-ignore state_referenced_locally
|
|
@@ -362,7 +362,11 @@
|
|
|
362
362
|
|
|
363
363
|
<div class="flowdrop-settings-panel {className}">
|
|
364
364
|
<!-- Tab Navigation -->
|
|
365
|
-
<div
|
|
365
|
+
<div
|
|
366
|
+
class="flowdrop-settings-panel__tabs"
|
|
367
|
+
role="tablist"
|
|
368
|
+
aria-label={m().layout.settingsCategories}
|
|
369
|
+
>
|
|
366
370
|
{#each categories as category, index (category)}
|
|
367
371
|
<button
|
|
368
372
|
class="flowdrop-settings-panel__tab"
|
|
@@ -277,7 +277,11 @@
|
|
|
277
277
|
const rawStatus = statuses[node.id];
|
|
278
278
|
if (!rawStatus) return node;
|
|
279
279
|
|
|
280
|
-
const existing = node.data.executionInfo ?? {
|
|
280
|
+
const existing = node.data.executionInfo ?? {
|
|
281
|
+
status: 'idle' as const,
|
|
282
|
+
executionCount: 0,
|
|
283
|
+
isExecuting: false
|
|
284
|
+
};
|
|
281
285
|
return {
|
|
282
286
|
...node,
|
|
283
287
|
data: {
|
|
@@ -288,11 +288,7 @@
|
|
|
288
288
|
return;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
if (
|
|
292
|
-
getBehaviorSettings().chatAutoRetry &&
|
|
293
|
-
workflowId &&
|
|
294
|
-
autoRetryCount < MAX_AUTO_RETRIES
|
|
295
|
-
) {
|
|
291
|
+
if (getBehaviorSettings().chatAutoRetry && workflowId && autoRetryCount < MAX_AUTO_RETRIES) {
|
|
296
292
|
autoRetryCount++;
|
|
297
293
|
const errorText = buildBatchErrorMessage(
|
|
298
294
|
completedCount,
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
// Stable fingerprint — any change triggers selection clearing.
|
|
122
122
|
// JSON.stringify gives a canonical string without null-byte ambiguity.
|
|
123
123
|
const depFingerprint = $derived(JSON.stringify(depParamValues));
|
|
124
|
+
// svelte-ignore state_referenced_locally — intentional initial snapshot; the effect below tracks subsequent changes
|
|
124
125
|
let prevDepFingerprint = depFingerprint;
|
|
125
126
|
|
|
126
127
|
$effect(() => {
|
|
@@ -721,11 +722,7 @@
|
|
|
721
722
|
style={popoverStyle}
|
|
722
723
|
onmousedown={(e) => e.preventDefault()}
|
|
723
724
|
>
|
|
724
|
-
<ul
|
|
725
|
-
class="form-autocomplete__listbox"
|
|
726
|
-
role="listbox"
|
|
727
|
-
aria-label={t.suggestions}
|
|
728
|
-
>
|
|
725
|
+
<ul class="form-autocomplete__listbox" role="listbox" aria-label={t.suggestions}>
|
|
729
726
|
{#if isLoading}
|
|
730
727
|
<li class="form-autocomplete__status form-autocomplete__status--loading">
|
|
731
728
|
<Icon icon="heroicons:arrow-path" class="form-autocomplete__status-icon" />
|
|
@@ -135,7 +135,11 @@
|
|
|
135
135
|
{/if}
|
|
136
136
|
|
|
137
137
|
<!-- Options -->
|
|
138
|
-
<div
|
|
138
|
+
<div
|
|
139
|
+
class="choice-prompt__options"
|
|
140
|
+
role={isMultiple ? 'group' : 'radiogroup'}
|
|
141
|
+
aria-label={config.message}
|
|
142
|
+
>
|
|
139
143
|
{#each config.options as option (option.value)}
|
|
140
144
|
{@const isChecked = isResolved ? isOptionResolved(option) : selectedValues.has(option.value)}
|
|
141
145
|
<label
|
|
@@ -16,10 +16,7 @@
|
|
|
16
16
|
import ReviewPrompt from './ReviewPrompt.svelte';
|
|
17
17
|
import MessageTagStrip from '../playground/MessageTagStrip.svelte';
|
|
18
18
|
import HierarchyTrail from '../playground/HierarchyTrail.svelte';
|
|
19
|
-
import type {
|
|
20
|
-
MessageHierarchyItem,
|
|
21
|
-
MessageTag
|
|
22
|
-
} from '../../types/playground.js';
|
|
19
|
+
import type { MessageHierarchyItem, MessageTag } from '../../types/playground.js';
|
|
23
20
|
import type {
|
|
24
21
|
Interrupt,
|
|
25
22
|
InterruptType,
|
|
@@ -304,7 +301,9 @@
|
|
|
304
301
|
<time
|
|
305
302
|
class="interrupt-bubble__timestamp"
|
|
306
303
|
datetime={currentInterrupt.resolvedAt ?? currentInterrupt.createdAt}
|
|
307
|
-
aria-label="sent at {formatTimestamp(
|
|
304
|
+
aria-label="sent at {formatTimestamp(
|
|
305
|
+
currentInterrupt.resolvedAt ?? currentInterrupt.createdAt
|
|
306
|
+
)}"
|
|
308
307
|
>
|
|
309
308
|
{formatTimestamp(currentInterrupt.resolvedAt ?? currentInterrupt.createdAt)}
|
|
310
309
|
</time>
|
|
@@ -10,12 +10,7 @@
|
|
|
10
10
|
import HierarchyTrail from './HierarchyTrail.svelte';
|
|
11
11
|
import MessageTagStrip from './MessageTagStrip.svelte';
|
|
12
12
|
import MessageMarkdown from './MessageMarkdown.svelte';
|
|
13
|
-
import {
|
|
14
|
-
formatDuration,
|
|
15
|
-
formatTimestamp,
|
|
16
|
-
getRoleIcon,
|
|
17
|
-
getRoleLabel
|
|
18
|
-
} from './messageDisplay.js';
|
|
13
|
+
import { formatDuration, formatTimestamp, getRoleIcon, getRoleLabel } from './messageDisplay.js';
|
|
19
14
|
import { m } from '../../messages/index.js';
|
|
20
15
|
|
|
21
16
|
interface Props {
|
|
@@ -55,7 +50,8 @@
|
|
|
55
50
|
class="message-bubble__timestamp"
|
|
56
51
|
datetime={message.timestamp}
|
|
57
52
|
aria-label="sent at {formatTimestamp(message.timestamp)}"
|
|
58
|
-
|
|
53
|
+
>{formatTimestamp(message.timestamp)}</time
|
|
54
|
+
>
|
|
59
55
|
{/if}
|
|
60
56
|
</div>
|
|
61
57
|
|
|
@@ -157,7 +153,9 @@
|
|
|
157
153
|
background-color: var(--fd-card);
|
|
158
154
|
border: 1px solid var(--fd-border);
|
|
159
155
|
color: var(--fd-card-foreground);
|
|
160
|
-
box-shadow:
|
|
156
|
+
box-shadow:
|
|
157
|
+
0 1px 3px 0 oklch(0% 0 0 / 0.06),
|
|
158
|
+
0 1px 2px -1px oklch(0% 0 0 / 0.04);
|
|
161
159
|
border-bottom-left-radius: var(--fd-radius-sm);
|
|
162
160
|
}
|
|
163
161
|
|
|
@@ -58,23 +58,27 @@
|
|
|
58
58
|
let runEnabled = $state(true);
|
|
59
59
|
|
|
60
60
|
let inputValue = $state('');
|
|
61
|
-
let inputField: HTMLTextAreaElement | undefined;
|
|
61
|
+
let inputField: HTMLTextAreaElement | undefined = $state();
|
|
62
62
|
|
|
63
63
|
// Count of enableRun messages seen so far — plain let, not $state.
|
|
64
64
|
// Written with untrack to make the bookkeeping intent explicit.
|
|
65
65
|
let seenEnableRunCount = 0;
|
|
66
66
|
|
|
67
67
|
$effect(() => {
|
|
68
|
-
const count = getMessages().filter(m => hasEnableRunFlag(m.metadata)).length;
|
|
68
|
+
const count = getMessages().filter((m) => hasEnableRunFlag(m.metadata)).length;
|
|
69
69
|
if (count > seenEnableRunCount) {
|
|
70
|
-
untrack(() => {
|
|
70
|
+
untrack(() => {
|
|
71
|
+
seenEnableRunCount = count;
|
|
72
|
+
});
|
|
71
73
|
runEnabled = true;
|
|
72
74
|
}
|
|
73
75
|
});
|
|
74
76
|
|
|
75
77
|
$effect(() => {
|
|
76
78
|
if (getCurrentSession()?.id) {
|
|
77
|
-
untrack(() => {
|
|
79
|
+
untrack(() => {
|
|
80
|
+
seenEnableRunCount = 0;
|
|
81
|
+
});
|
|
78
82
|
runEnabled = true;
|
|
79
83
|
}
|
|
80
84
|
});
|
|
@@ -87,7 +91,9 @@
|
|
|
87
91
|
if (wasExecuting && !nowExecuting && inputField) {
|
|
88
92
|
tick().then(() => inputField?.focus({ preventScroll: true }));
|
|
89
93
|
}
|
|
90
|
-
untrack(() => {
|
|
94
|
+
untrack(() => {
|
|
95
|
+
wasExecuting = nowExecuting;
|
|
96
|
+
});
|
|
91
97
|
});
|
|
92
98
|
|
|
93
99
|
function handleSend(): void {
|
|
@@ -159,35 +159,37 @@
|
|
|
159
159
|
</button>
|
|
160
160
|
{#if getSessions().length > 0}
|
|
161
161
|
<div class="control-panel__session-popover-divider"></div>
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
162
|
+
<div class="control-panel__session-popover-list">
|
|
163
|
+
{#each getSessions() as session (session.id)}
|
|
164
|
+
{@const isActive = getCurrentSession()?.id === session.id}
|
|
165
|
+
<div class="control-panel__session-popover-row">
|
|
166
|
+
<button
|
|
167
|
+
type="button"
|
|
168
|
+
role="menuitem"
|
|
169
|
+
class="control-panel__session-popover-item"
|
|
170
|
+
class:control-panel__session-popover-item--active={isActive}
|
|
171
|
+
onclick={() => handleSelect(session.id)}
|
|
172
|
+
>
|
|
173
|
+
{#if isActive}
|
|
174
|
+
<Icon icon="mdi:check" class="control-panel__session-popover-check" />
|
|
175
|
+
{:else}
|
|
176
|
+
<Icon icon="mdi:message-outline" />
|
|
177
|
+
{/if}
|
|
178
|
+
<span>{session.name}</span>
|
|
179
|
+
</button>
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
role="menuitem"
|
|
183
|
+
class="control-panel__session-popover-delete"
|
|
184
|
+
onclick={(e) => handleDelete(e, session.id)}
|
|
185
|
+
title={cp.deleteSession}
|
|
186
|
+
aria-label={cp.deleteSession}
|
|
187
|
+
>
|
|
188
|
+
<Icon icon="mdi:delete-outline" />
|
|
189
|
+
</button>
|
|
190
|
+
</div>
|
|
191
|
+
{/each}
|
|
192
|
+
</div>
|
|
191
193
|
{/if}
|
|
192
194
|
</div>
|
|
193
195
|
{/if}
|
|
@@ -333,6 +335,9 @@
|
|
|
333
335
|
z-index: 50;
|
|
334
336
|
min-width: 220px;
|
|
335
337
|
max-width: 300px;
|
|
338
|
+
max-height: min(60vh, 420px);
|
|
339
|
+
display: flex;
|
|
340
|
+
flex-direction: column;
|
|
336
341
|
padding: var(--fd-space-xs);
|
|
337
342
|
background-color: var(--fd-background);
|
|
338
343
|
border: 1px solid var(--fd-border);
|
|
@@ -344,6 +349,13 @@
|
|
|
344
349
|
height: 1px;
|
|
345
350
|
background-color: var(--fd-border-muted);
|
|
346
351
|
margin: var(--fd-space-xs) 0;
|
|
352
|
+
flex-shrink: 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.control-panel__session-popover-list {
|
|
356
|
+
flex: 1 1 auto;
|
|
357
|
+
min-height: 0;
|
|
358
|
+
overflow-y: auto;
|
|
347
359
|
}
|
|
348
360
|
|
|
349
361
|
.control-panel__session-popover-row {
|
|
@@ -396,6 +408,7 @@
|
|
|
396
408
|
color: var(--fd-primary);
|
|
397
409
|
font-weight: 500;
|
|
398
410
|
width: 100%;
|
|
411
|
+
flex: 0 0 auto;
|
|
399
412
|
}
|
|
400
413
|
|
|
401
414
|
.control-panel__session-popover-item--new :global(svg) {
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
onInterruptResolved?: () => void;
|
|
23
23
|
/** Optional callback that, when provided, shows a "New session" CTA in the welcome state */
|
|
24
24
|
onCreateSession?: () => void;
|
|
25
|
+
/** Called when the user scrolls near the top to load older messages */
|
|
26
|
+
onLoadOlder?: () => void | Promise<void>;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
let {
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
allowLogs = true,
|
|
32
34
|
compactSystemMessages = true,
|
|
33
35
|
onInterruptResolved,
|
|
34
|
-
onCreateSession
|
|
36
|
+
onCreateSession,
|
|
37
|
+
onLoadOlder
|
|
35
38
|
}: Props = $props();
|
|
36
39
|
|
|
37
40
|
const ec = $derived(m().playground.executionConsole);
|
|
@@ -50,6 +53,7 @@
|
|
|
50
53
|
{allowLogs}
|
|
51
54
|
{compactSystemMessages}
|
|
52
55
|
{onInterruptResolved}
|
|
56
|
+
{onLoadOlder}
|
|
53
57
|
welcome={welcomeState}
|
|
54
58
|
emptySession={readyState}
|
|
55
59
|
/>
|
|
@@ -8,6 +8,8 @@ interface Props {
|
|
|
8
8
|
onInterruptResolved?: () => void;
|
|
9
9
|
/** Optional callback that, when provided, shows a "New session" CTA in the welcome state */
|
|
10
10
|
onCreateSession?: () => void;
|
|
11
|
+
/** Called when the user scrolls near the top to load older messages */
|
|
12
|
+
onLoadOlder?: () => void | Promise<void>;
|
|
11
13
|
}
|
|
12
14
|
declare const ExecutionConsole: import("svelte").Component<Props, {}, "">;
|
|
13
15
|
type ExecutionConsole = ReturnType<typeof ExecutionConsole>;
|
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
-->
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
|
-
import {
|
|
14
|
-
resolveMessageDisplay,
|
|
15
|
-
type PlaygroundMessage
|
|
16
|
-
} from '../../types/playground.js';
|
|
13
|
+
import { resolveMessageDisplay, type PlaygroundMessage } from '../../types/playground.js';
|
|
17
14
|
import ChatBubble from './ChatBubble.svelte';
|
|
18
15
|
import LogRow from './LogRow.svelte';
|
|
19
16
|
import MessageNotice from './MessageNotice.svelte';
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
class="message-card__timestamp"
|
|
44
44
|
datetime={message.timestamp}
|
|
45
45
|
aria-label="sent at {formatTimestamp(message.timestamp)}"
|
|
46
|
-
|
|
46
|
+
>{formatTimestamp(message.timestamp)}</time
|
|
47
|
+
>
|
|
47
48
|
{/if}
|
|
48
49
|
</header>
|
|
49
50
|
{/if}
|
|
@@ -72,9 +72,15 @@
|
|
|
72
72
|
margin-top: 0;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
.message-markdown :global(h1) {
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
.message-markdown :global(h1) {
|
|
76
|
+
font-size: var(--fd-text-xl);
|
|
77
|
+
}
|
|
78
|
+
.message-markdown :global(h2) {
|
|
79
|
+
font-size: var(--fd-text-lg);
|
|
80
|
+
}
|
|
81
|
+
.message-markdown :global(h3) {
|
|
82
|
+
font-size: var(--fd-text-base);
|
|
83
|
+
}
|
|
78
84
|
|
|
79
85
|
.message-markdown :global(ul),
|
|
80
86
|
.message-markdown :global(ol) {
|
|
@@ -155,6 +161,10 @@
|
|
|
155
161
|
font-weight: 600;
|
|
156
162
|
}
|
|
157
163
|
|
|
158
|
-
.message-markdown :global(strong) {
|
|
159
|
-
|
|
164
|
+
.message-markdown :global(strong) {
|
|
165
|
+
font-weight: 600;
|
|
166
|
+
}
|
|
167
|
+
.message-markdown :global(em) {
|
|
168
|
+
font-style: italic;
|
|
169
|
+
}
|
|
160
170
|
</style>
|