@flowdrop/flowdrop 1.11.0 → 1.13.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.d.ts +29 -16
- package/dist/api/enhanced-client.js +0 -14
- package/dist/components/ConfigForm.svelte +1 -0
- package/dist/components/PipelineStatus.svelte +9 -12
- package/dist/components/SchemaForm.svelte +1 -0
- package/dist/components/WorkflowEditor.svelte +3 -0
- package/dist/components/form/FormAutocomplete.svelte +67 -10
- package/dist/components/form/FormField.svelte +21 -0
- package/dist/components/form/FormFieldLight.svelte +1 -0
- package/dist/components/interrupt/ChoicePrompt.svelte +24 -5
- package/dist/components/interrupt/ConfirmationPrompt.svelte +5 -0
- package/dist/components/interrupt/InterruptBubble.svelte +88 -17
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +11 -0
- package/dist/components/interrupt/ReviewPrompt.svelte +20 -0
- package/dist/components/interrupt/TextInputPrompt.svelte +5 -0
- package/dist/components/nodes/GatewayNode.svelte +2 -6
- package/dist/components/nodes/WorkflowNode.svelte +2 -6
- package/dist/components/playground/ChatBubble.svelte +289 -0
- package/dist/components/playground/ChatBubble.svelte.d.ts +10 -0
- package/dist/components/playground/ChatInput.svelte +359 -0
- package/dist/components/playground/ChatInput.svelte.d.ts +14 -0
- package/dist/components/playground/ChatPanel.svelte +100 -724
- package/dist/components/playground/ChatPanel.svelte.d.ts +9 -26
- package/dist/components/playground/ControlPanel.svelte +496 -0
- package/dist/components/playground/ControlPanel.svelte.d.ts +20 -0
- package/dist/components/playground/ExecutionConsole.svelte +163 -0
- package/dist/components/playground/ExecutionConsole.svelte.d.ts +14 -0
- package/dist/components/playground/HierarchyTrail.svelte +88 -0
- package/dist/components/playground/HierarchyTrail.svelte.d.ts +7 -0
- package/dist/components/playground/LogRow.svelte +178 -0
- package/dist/components/playground/LogRow.svelte.d.ts +8 -0
- package/dist/components/playground/MessageBubble.stories.svelte +89 -0
- package/dist/components/playground/MessageBubble.svelte +25 -737
- package/dist/components/playground/MessageBubble.svelte.d.ts +3 -11
- package/dist/components/playground/MessageCard.svelte +106 -0
- package/dist/components/playground/MessageCard.svelte.d.ts +10 -0
- package/dist/components/playground/MessageMarkdown.svelte +160 -0
- package/dist/components/playground/MessageMarkdown.svelte.d.ts +7 -0
- package/dist/components/playground/MessageNotice.svelte +120 -0
- package/dist/components/playground/MessageNotice.svelte.d.ts +9 -0
- package/dist/components/playground/MessageStream.svelte +367 -0
- package/dist/components/playground/MessageStream.svelte.d.ts +27 -0
- package/dist/components/playground/MessageTagChip.svelte +99 -0
- package/dist/components/playground/MessageTagChip.svelte.d.ts +7 -0
- package/dist/components/playground/MessageTagStrip.svelte +37 -0
- package/dist/components/playground/MessageTagStrip.svelte.d.ts +7 -0
- package/dist/components/playground/PipelineKanbanView.svelte +284 -0
- package/dist/components/playground/PipelineKanbanView.svelte.d.ts +11 -0
- package/dist/components/playground/PipelinePanel.svelte +204 -65
- package/dist/components/playground/PipelinePanel.svelte.d.ts +3 -1
- package/dist/components/playground/PipelineTableView.svelte +376 -0
- package/dist/components/playground/PipelineTableView.svelte.d.ts +11 -0
- package/dist/components/playground/Playground.svelte +262 -1200
- package/dist/components/playground/Playground.svelte.d.ts +0 -13
- package/dist/components/playground/PlaygroundStudio.svelte +113 -61
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -1
- package/dist/components/playground/messageDisplay.d.ts +19 -0
- package/dist/components/playground/messageDisplay.js +62 -0
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +22 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +77 -0
- package/dist/form/autocomplete.d.ts +1 -0
- package/dist/form/autocomplete.js +1 -0
- package/dist/form/index.d.ts +17 -0
- package/dist/form/index.js +19 -0
- package/dist/messages/defaults.d.ts +29 -0
- package/dist/messages/defaults.js +30 -0
- package/dist/playground/index.d.ts +6 -1
- package/dist/playground/index.js +6 -0
- package/dist/playground/mount.d.ts +3 -0
- package/dist/playground/mount.js +3 -2
- package/dist/schemas/v1/workflow.schema.json +10 -1
- package/dist/services/categoriesApi.d.ts +2 -1
- package/dist/services/categoriesApi.js +5 -3
- package/dist/services/portConfigApi.d.ts +2 -1
- package/dist/services/portConfigApi.js +5 -3
- package/dist/stores/playgroundStore.svelte.d.ts +6 -0
- package/dist/stores/playgroundStore.svelte.js +21 -1
- package/dist/svelte-app.d.ts +1 -0
- package/dist/svelte-app.js +5 -5
- package/dist/types/index.d.ts +41 -2
- package/dist/types/playground.d.ts +81 -2
- package/dist/types/playground.js +19 -7
- package/dist/utils/nodeStatus.js +15 -5
- package/package.json +6 -1
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
import type { Workflow } from '../../types/index.js';
|
|
2
2
|
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
3
3
|
import type { PlaygroundMode, PlaygroundConfig } from '../../types/playground.js';
|
|
4
|
-
/**
|
|
5
|
-
* Component props
|
|
6
|
-
*/
|
|
7
4
|
interface Props {
|
|
8
|
-
/** Target workflow ID */
|
|
9
5
|
workflowId: string;
|
|
10
|
-
/** Pre-loaded workflow (optional, will be fetched if not provided) */
|
|
11
6
|
workflow?: Workflow;
|
|
12
|
-
/** Display mode: embedded (panel) or standalone (page) */
|
|
13
7
|
mode?: PlaygroundMode;
|
|
14
|
-
/** Resume a specific session */
|
|
15
8
|
initialSessionId?: string;
|
|
16
|
-
/** API endpoint configuration */
|
|
17
9
|
endpointConfig?: EndpointConfig;
|
|
18
|
-
/** Playground configuration options */
|
|
19
10
|
config?: PlaygroundConfig;
|
|
20
|
-
/** Callback when playground is closed (for embedded mode) */
|
|
21
11
|
onClose?: () => void;
|
|
22
|
-
/** Callback to toggle the pipeline panel (if undefined, toggle button is hidden) */
|
|
23
12
|
onTogglePanel?: () => void;
|
|
24
|
-
/** Whether the pipeline panel is currently open (for toggle button active state) */
|
|
25
13
|
isPipelinePanelOpen?: boolean;
|
|
26
|
-
/** When provided, session switches and creation navigate to a URL instead of mutating store state */
|
|
27
14
|
onSessionNavigate?: (sessionId: string) => void;
|
|
28
15
|
}
|
|
29
16
|
declare const Playground: import("svelte").Component<Props, {}, "">;
|
|
@@ -1,48 +1,5 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
PlaygroundStudio Component
|
|
3
|
-
|
|
4
|
-
Drop-in embeddable split-pane layout: PipelinePanel (left, resizable) + Playground
|
|
5
|
-
chat (right). Provides the same integrated experience as the FlowDrop demo app with a
|
|
6
|
-
single import and zero layout glue code.
|
|
7
|
-
|
|
8
|
-
@example Basic usage — workflow fetched automatically
|
|
9
|
-
```svelte
|
|
10
|
-
<script>
|
|
11
|
-
import { PlaygroundStudio, createEndpointConfig } from '@flowdrop/flowdrop/playground';
|
|
12
|
-
import '@flowdrop/flowdrop/styles';
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<PlaygroundStudio
|
|
16
|
-
workflowId="wf-123"
|
|
17
|
-
endpointConfig={createEndpointConfig('/api/flowdrop')}
|
|
18
|
-
/>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
@example With pre-loaded workflow and URL-based session routing
|
|
22
|
-
```svelte
|
|
23
|
-
<script>
|
|
24
|
-
import { PlaygroundStudio, createEndpointConfig } from '@flowdrop/flowdrop/playground';
|
|
25
|
-
|
|
26
|
-
let { data } = $props(); // workflow from server load
|
|
27
|
-
let sessionId = $derived($page.params.sessionId);
|
|
28
|
-
|
|
29
|
-
function handleSessionNavigate(id) {
|
|
30
|
-
goto(`/playground/${id}`);
|
|
31
|
-
}
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<PlaygroundStudio
|
|
35
|
-
workflowId={data.workflow.id}
|
|
36
|
-
workflow={data.workflow}
|
|
37
|
-
endpointConfig={createEndpointConfig('/api/flowdrop')}
|
|
38
|
-
initialSessionId={sessionId}
|
|
39
|
-
onSessionNavigate={handleSessionNavigate}
|
|
40
|
-
/>
|
|
41
|
-
```
|
|
42
|
-
-->
|
|
43
|
-
|
|
44
1
|
<script lang="ts">
|
|
45
|
-
import { onMount } from 'svelte';
|
|
2
|
+
import { onMount, untrack } from 'svelte';
|
|
46
3
|
import Icon from '@iconify/svelte';
|
|
47
4
|
import Playground from './Playground.svelte';
|
|
48
5
|
import PipelinePanel from './PipelinePanel.svelte';
|
|
@@ -57,7 +14,7 @@
|
|
|
57
14
|
} from '../../stores/playgroundStore.svelte.js';
|
|
58
15
|
import { setEndpointConfig, workflowApi } from '../../services/api.js';
|
|
59
16
|
import { logger } from '../../utils/logger.js';
|
|
60
|
-
import type { Workflow } from '../../types/index.js';
|
|
17
|
+
import type { Workflow, PipelineViewDef } from '../../types/index.js';
|
|
61
18
|
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
62
19
|
import type { PlaygroundConfig, PlaygroundMode } from '../../types/playground.js';
|
|
63
20
|
|
|
@@ -84,6 +41,8 @@
|
|
|
84
41
|
onSessionNavigate?: (sessionId: string) => void;
|
|
85
42
|
/** Called when the playground is closed (embedded / modal mode) */
|
|
86
43
|
onClose?: () => void;
|
|
44
|
+
/** Additional pipeline views injected by the consumer */
|
|
45
|
+
extraPipelineViews?: PipelineViewDef[];
|
|
87
46
|
}
|
|
88
47
|
|
|
89
48
|
let {
|
|
@@ -98,19 +57,18 @@
|
|
|
98
57
|
initialPipelineWidth = 500,
|
|
99
58
|
onSessionNavigate,
|
|
100
59
|
onClose,
|
|
60
|
+
extraPipelineViews = [],
|
|
101
61
|
}: Props = $props();
|
|
102
62
|
|
|
103
|
-
// svelte-ignore state_referenced_locally
|
|
104
63
|
let resolvedWorkflow = $state<Workflow | null>(workflowProp ?? null);
|
|
105
|
-
// svelte-ignore state_referenced_locally
|
|
106
64
|
let workflowLoading = $state(workflowProp === undefined);
|
|
107
65
|
let workflowError = $state<string | null>(null);
|
|
108
66
|
|
|
109
67
|
let splitEl = $state<HTMLElement | null>(null);
|
|
110
|
-
// svelte-ignore state_referenced_locally
|
|
111
68
|
let pipelineWidth = $state(initialPipelineWidth);
|
|
112
69
|
let isResizing = $state(false);
|
|
113
70
|
let containerWidth = $state(0);
|
|
71
|
+
let dragContainerLeft = 0;
|
|
114
72
|
|
|
115
73
|
$effect(() => {
|
|
116
74
|
if (!splitEl) return;
|
|
@@ -121,6 +79,12 @@
|
|
|
121
79
|
return () => observer.disconnect();
|
|
122
80
|
});
|
|
123
81
|
|
|
82
|
+
$effect(() => {
|
|
83
|
+
if (containerWidth > 0) {
|
|
84
|
+
pipelineWidth = clampPipelineWidth(untrack(() => pipelineWidth));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
124
88
|
onMount(() => {
|
|
125
89
|
pipelinePanelActions.init();
|
|
126
90
|
if (initialPipelineOpen !== undefined) {
|
|
@@ -153,23 +117,36 @@
|
|
|
153
117
|
}
|
|
154
118
|
}
|
|
155
119
|
|
|
120
|
+
function clampPipelineWidth(w: number): number {
|
|
121
|
+
if (!containerWidth) return Math.max(w, 0);
|
|
122
|
+
return Math.min(Math.max(w, containerWidth - minChatWidth), containerWidth * 0.75);
|
|
123
|
+
}
|
|
124
|
+
|
|
156
125
|
function handleResizerPointerDown(e: PointerEvent) {
|
|
126
|
+
if (splitEl) dragContainerLeft = splitEl.getBoundingClientRect().left;
|
|
157
127
|
isResizing = true;
|
|
158
128
|
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
|
159
129
|
}
|
|
160
130
|
|
|
161
131
|
function handleResizerPointerMove(e: PointerEvent) {
|
|
162
|
-
if (!isResizing
|
|
163
|
-
|
|
164
|
-
pipelineWidth = Math.min(
|
|
165
|
-
Math.max(e.clientX - rect.left, rect.width - minChatWidth),
|
|
166
|
-
rect.width * 0.75
|
|
167
|
-
);
|
|
132
|
+
if (!isResizing) return;
|
|
133
|
+
pipelineWidth = clampPipelineWidth(e.clientX - dragContainerLeft);
|
|
168
134
|
}
|
|
169
135
|
|
|
170
136
|
function handleResizerPointerUp() {
|
|
171
137
|
isResizing = false;
|
|
172
138
|
}
|
|
139
|
+
|
|
140
|
+
function handleResizerKeyDown(e: KeyboardEvent) {
|
|
141
|
+
const step = e.shiftKey ? 50 : 20;
|
|
142
|
+
if (e.key === 'ArrowLeft') {
|
|
143
|
+
e.preventDefault();
|
|
144
|
+
pipelineWidth = clampPipelineWidth(pipelineWidth - step);
|
|
145
|
+
} else if (e.key === 'ArrowRight') {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
pipelineWidth = clampPipelineWidth(pipelineWidth + step);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
173
150
|
</script>
|
|
174
151
|
|
|
175
152
|
<div class="playground-studio" class:playground-studio--resizing={isResizing} style="--playground-studio-min-chat-width: {minChatWidth}px">
|
|
@@ -179,6 +156,15 @@
|
|
|
179
156
|
{@const executions = getCurrentSession()?.executions ?? []}
|
|
180
157
|
|
|
181
158
|
<div class="playground-studio__pipeline" style="width: {pipelineWidth}px;">
|
|
159
|
+
<button
|
|
160
|
+
type="button"
|
|
161
|
+
class="playground-studio__back-to-chat"
|
|
162
|
+
aria-label="Back to chat"
|
|
163
|
+
onclick={pipelinePanelActions.toggle}
|
|
164
|
+
>
|
|
165
|
+
<Icon icon="mdi:arrow-left" aria-hidden="true" />
|
|
166
|
+
<span>Back to chat</span>
|
|
167
|
+
</button>
|
|
182
168
|
<PipelinePanel
|
|
183
169
|
pipelineId={activeId}
|
|
184
170
|
workflow={resolvedWorkflow}
|
|
@@ -188,10 +174,10 @@
|
|
|
188
174
|
latestExecutionId={getLatestExecutionId()}
|
|
189
175
|
onSelectExecution={(id) => playgroundActions.pinExecution(id)}
|
|
190
176
|
refreshTrigger={getPipelineRefreshTrigger()}
|
|
177
|
+
extraViews={extraPipelineViews}
|
|
191
178
|
/>
|
|
192
179
|
</div>
|
|
193
180
|
|
|
194
|
-
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
195
181
|
<div
|
|
196
182
|
class="playground-studio__resizer"
|
|
197
183
|
class:playground-studio__resizer--active={isResizing}
|
|
@@ -200,11 +186,13 @@
|
|
|
200
186
|
aria-valuenow={Math.round(pipelineWidth)}
|
|
201
187
|
aria-valuemin={0}
|
|
202
188
|
aria-valuemax={Math.round(containerWidth * 0.75)}
|
|
189
|
+
aria-label="Resize pipeline panel"
|
|
203
190
|
tabindex="0"
|
|
204
191
|
onpointerdown={handleResizerPointerDown}
|
|
205
192
|
onpointermove={handleResizerPointerMove}
|
|
206
193
|
onpointerup={handleResizerPointerUp}
|
|
207
194
|
onpointercancel={handleResizerPointerUp}
|
|
195
|
+
onkeydown={handleResizerKeyDown}
|
|
208
196
|
>
|
|
209
197
|
<div class="playground-studio__resizer-handle"></div>
|
|
210
198
|
</div>
|
|
@@ -271,12 +259,17 @@
|
|
|
271
259
|
overflow: hidden;
|
|
272
260
|
}
|
|
273
261
|
|
|
274
|
-
/* Pipeline pane — explicit width
|
|
262
|
+
/* Pipeline pane — explicit width driven by JS; clamping keeps it in bounds */
|
|
275
263
|
.playground-studio__pipeline {
|
|
276
|
-
min-width: calc(100% - var(--playground-studio-min-chat-width));
|
|
277
|
-
max-width: 75%;
|
|
278
264
|
overflow: hidden;
|
|
279
265
|
flex-shrink: 0;
|
|
266
|
+
position: relative;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Mobile-only "back to chat" affordance. Hidden on wider viewports where
|
|
270
|
+
the ControlPanel's pipeline toggle remains reachable. */
|
|
271
|
+
.playground-studio__back-to-chat {
|
|
272
|
+
display: none;
|
|
280
273
|
}
|
|
281
274
|
|
|
282
275
|
/* Drag handle between the two panes */
|
|
@@ -351,9 +344,6 @@
|
|
|
351
344
|
}
|
|
352
345
|
|
|
353
346
|
@keyframes studio-spin {
|
|
354
|
-
from {
|
|
355
|
-
transform: rotate(0deg);
|
|
356
|
-
}
|
|
357
347
|
to {
|
|
358
348
|
transform: rotate(360deg);
|
|
359
349
|
}
|
|
@@ -401,4 +391,66 @@
|
|
|
401
391
|
.playground-studio__retry-btn:hover {
|
|
402
392
|
background-color: var(--fd-primary-hover);
|
|
403
393
|
}
|
|
394
|
+
|
|
395
|
+
/* ============================================================
|
|
396
|
+
Mobile layout (< 768px)
|
|
397
|
+
Switch from side-by-side panes to one-at-a-time fullscreen.
|
|
398
|
+
The pipeline panel, when open, covers the chat. Users toggle
|
|
399
|
+
between them via the pipeline panel button. The resizer is
|
|
400
|
+
hidden — at this width there's nothing to resize.
|
|
401
|
+
============================================================ */
|
|
402
|
+
@media (max-width: 768px) {
|
|
403
|
+
.playground-studio__pipeline {
|
|
404
|
+
/* Override the JS-driven width — take the whole row */
|
|
405
|
+
width: 100% !important;
|
|
406
|
+
flex: 1 1 auto;
|
|
407
|
+
display: flex;
|
|
408
|
+
flex-direction: column;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.playground-studio__resizer {
|
|
412
|
+
display: none;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/* When pipeline is open (chat is NOT solo), hide chat to give the
|
|
416
|
+
pipeline the full viewport. When pipeline closes, chat goes back
|
|
417
|
+
to full-width via the existing --solo class. */
|
|
418
|
+
.playground-studio__chat:not(.playground-studio__chat--solo) {
|
|
419
|
+
display: none;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.playground-studio__back-to-chat {
|
|
423
|
+
display: inline-flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
gap: var(--fd-space-2xs);
|
|
426
|
+
align-self: flex-start;
|
|
427
|
+
margin: var(--fd-space-xs);
|
|
428
|
+
padding: var(--fd-space-2xs) var(--fd-space-md);
|
|
429
|
+
min-height: 2.5rem;
|
|
430
|
+
font-size: var(--fd-text-sm);
|
|
431
|
+
font-weight: 500;
|
|
432
|
+
font-family: inherit;
|
|
433
|
+
color: var(--fd-foreground);
|
|
434
|
+
background-color: var(--fd-card);
|
|
435
|
+
border: 1px solid var(--fd-border);
|
|
436
|
+
border-radius: var(--fd-radius-md);
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
transition: background-color var(--fd-transition-fast);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.playground-studio__back-to-chat:hover {
|
|
442
|
+
background-color: var(--fd-muted);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.playground-studio__back-to-chat:focus-visible {
|
|
446
|
+
outline: 2px solid var(--fd-ring);
|
|
447
|
+
outline-offset: 2px;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
@media (prefers-reduced-motion: reduce) {
|
|
452
|
+
.playground-studio__back-to-chat {
|
|
453
|
+
transition: none;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
404
456
|
</style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Workflow } from '../../types/index.js';
|
|
1
|
+
import type { Workflow, PipelineViewDef } from '../../types/index.js';
|
|
2
2
|
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
3
3
|
import type { PlaygroundConfig, PlaygroundMode } from '../../types/playground.js';
|
|
4
4
|
interface Props {
|
|
@@ -24,6 +24,8 @@ interface Props {
|
|
|
24
24
|
onSessionNavigate?: (sessionId: string) => void;
|
|
25
25
|
/** Called when the playground is closed (embedded / modal mode) */
|
|
26
26
|
onClose?: () => void;
|
|
27
|
+
/** Additional pipeline views injected by the consumer */
|
|
28
|
+
extraPipelineViews?: PipelineViewDef[];
|
|
27
29
|
}
|
|
28
30
|
declare const PlaygroundStudio: import("svelte").Component<Props, {}, "">;
|
|
29
31
|
type PlaygroundStudio = ReturnType<typeof PlaygroundStudio>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared formatters / icon maps / label resolver for the message layout
|
|
3
|
+
* components. Pure functions only — i18n strings come in via the `roles`
|
|
4
|
+
* argument; the helper has no runtime dependency on the messages context.
|
|
5
|
+
*/
|
|
6
|
+
import type { PlaygroundMessage, PlaygroundMessageLevel, PlaygroundMessageRole } from '../../types/playground.js';
|
|
7
|
+
import type { Messages } from '../../messages/types.js';
|
|
8
|
+
export type RoleLabels = Messages['playground']['roles'];
|
|
9
|
+
export declare function formatTimestamp(timestamp: string): string;
|
|
10
|
+
export declare function formatDuration(ms: number): string;
|
|
11
|
+
export declare function getLogLevelIcon(level: PlaygroundMessageLevel | undefined): string;
|
|
12
|
+
export declare function getRoleIcon(role: PlaygroundMessageRole): string;
|
|
13
|
+
/**
|
|
14
|
+
* Localised author label. Backend-supplied overrides win:
|
|
15
|
+
* - user → metadata.userName (display name)
|
|
16
|
+
* - log → metadata.nodeLabel (human-readable node label)
|
|
17
|
+
* Anything else returns the role's i18n default.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getRoleLabel(message: Pick<PlaygroundMessage, 'role' | 'metadata'>, roles: RoleLabels): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared formatters / icon maps / label resolver for the message layout
|
|
3
|
+
* components. Pure functions only — i18n strings come in via the `roles`
|
|
4
|
+
* argument; the helper has no runtime dependency on the messages context.
|
|
5
|
+
*/
|
|
6
|
+
export function formatTimestamp(timestamp) {
|
|
7
|
+
return new Date(timestamp).toLocaleTimeString('en-US', {
|
|
8
|
+
hour12: false,
|
|
9
|
+
hour: '2-digit',
|
|
10
|
+
minute: '2-digit',
|
|
11
|
+
second: '2-digit'
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function formatDuration(ms) {
|
|
15
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(2)}s`;
|
|
16
|
+
}
|
|
17
|
+
export function getLogLevelIcon(level) {
|
|
18
|
+
switch (level) {
|
|
19
|
+
case 'error':
|
|
20
|
+
return 'mdi:alert-circle';
|
|
21
|
+
case 'warning':
|
|
22
|
+
return 'mdi:alert';
|
|
23
|
+
case 'debug':
|
|
24
|
+
return 'mdi:bug';
|
|
25
|
+
default:
|
|
26
|
+
return 'mdi:information';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function getRoleIcon(role) {
|
|
30
|
+
switch (role) {
|
|
31
|
+
case 'user':
|
|
32
|
+
return 'mdi:account';
|
|
33
|
+
case 'assistant':
|
|
34
|
+
return 'mdi:robot';
|
|
35
|
+
case 'system':
|
|
36
|
+
return 'mdi:cog';
|
|
37
|
+
case 'log':
|
|
38
|
+
return 'mdi:console';
|
|
39
|
+
default:
|
|
40
|
+
return 'mdi:message';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Localised author label. Backend-supplied overrides win:
|
|
45
|
+
* - user → metadata.userName (display name)
|
|
46
|
+
* - log → metadata.nodeLabel (human-readable node label)
|
|
47
|
+
* Anything else returns the role's i18n default.
|
|
48
|
+
*/
|
|
49
|
+
export function getRoleLabel(message, roles) {
|
|
50
|
+
switch (message.role) {
|
|
51
|
+
case 'user':
|
|
52
|
+
return message.metadata?.userName ?? roles.you;
|
|
53
|
+
case 'assistant':
|
|
54
|
+
return roles.assistant;
|
|
55
|
+
case 'system':
|
|
56
|
+
return roles.system;
|
|
57
|
+
case 'log':
|
|
58
|
+
return message.metadata?.nodeLabel ?? roles.log;
|
|
59
|
+
default:
|
|
60
|
+
return roles.message;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
2
|
+
import type { NodeExecutionStatus, KanbanColumnDef } from '../../types/index.js';
|
|
3
|
+
export type NodeStatus = NodeExecutionStatus;
|
|
4
|
+
export interface NodeStatusData {
|
|
5
|
+
status: string;
|
|
6
|
+
last_executed?: string | null;
|
|
7
|
+
execution_time?: number | null;
|
|
8
|
+
error?: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveStatus(raw: NodeStatusData | undefined): NodeStatus;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a reactive pipeline data fetcher.
|
|
13
|
+
* `endpointConfig` is used once to construct the API client — it must be stable.
|
|
14
|
+
* `getPipelineId` is called on every fetch so pipeline ID changes are picked up.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createPipelineDataFetcher(getPipelineId: () => string, endpointConfig: EndpointConfig): {
|
|
17
|
+
readonly nodeStatusMap: Record<string, NodeStatusData>;
|
|
18
|
+
readonly kanbanConfig: KanbanColumnDef[] | null;
|
|
19
|
+
readonly isLoading: boolean;
|
|
20
|
+
readonly isError: boolean;
|
|
21
|
+
fetchData: () => Promise<void>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { EnhancedFlowDropApiClient } from '../../api/enhanced-client.js';
|
|
2
|
+
import { logger } from '../../utils/logger.js';
|
|
3
|
+
const KNOWN_STATUSES = new Set([
|
|
4
|
+
'idle', 'pending', 'running', 'paused', 'interrupted',
|
|
5
|
+
'completed', 'skipped', 'failed', 'cancelled'
|
|
6
|
+
]);
|
|
7
|
+
export function resolveStatus(raw) {
|
|
8
|
+
if (!raw)
|
|
9
|
+
return 'pending';
|
|
10
|
+
const s = raw.status || 'pending';
|
|
11
|
+
if (!KNOWN_STATUSES.has(s)) {
|
|
12
|
+
logger.warn(`[FlowDrop] Unknown node status from server: "${s}" — falling back to "pending"`);
|
|
13
|
+
return 'pending';
|
|
14
|
+
}
|
|
15
|
+
return s;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a reactive pipeline data fetcher.
|
|
19
|
+
* `endpointConfig` is used once to construct the API client — it must be stable.
|
|
20
|
+
* `getPipelineId` is called on every fetch so pipeline ID changes are picked up.
|
|
21
|
+
*/
|
|
22
|
+
export function createPipelineDataFetcher(getPipelineId, endpointConfig) {
|
|
23
|
+
const client = new EnhancedFlowDropApiClient(endpointConfig);
|
|
24
|
+
let nodeStatusMap = $state({});
|
|
25
|
+
let kanbanConfig = $state(null);
|
|
26
|
+
let isLoading = $state(false);
|
|
27
|
+
let isError = $state(false);
|
|
28
|
+
async function fetchData() {
|
|
29
|
+
try {
|
|
30
|
+
isLoading = true;
|
|
31
|
+
isError = false;
|
|
32
|
+
const data = await client.getPipelineData(getPipelineId());
|
|
33
|
+
const map = {};
|
|
34
|
+
for (const [nodeId, info] of Object.entries(data.node_statuses)) {
|
|
35
|
+
map[nodeId] = {
|
|
36
|
+
status: info.status,
|
|
37
|
+
last_executed: info.last_executed,
|
|
38
|
+
execution_time: info.execution_time,
|
|
39
|
+
error: info.error,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
nodeStatusMap = map;
|
|
43
|
+
if (data.kanban_config?.columns) {
|
|
44
|
+
// Server sends statuses as string[]; trust the server and cast at this
|
|
45
|
+
// boundary. resolveStatus() handles unknown values at read time.
|
|
46
|
+
kanbanConfig = data.kanban_config.columns.map((col) => ({
|
|
47
|
+
key: col.key,
|
|
48
|
+
label: col.label,
|
|
49
|
+
statuses: col.statuses,
|
|
50
|
+
icon: col.icon,
|
|
51
|
+
color: col.color,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
isError = true;
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
isLoading = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
get nodeStatusMap() {
|
|
64
|
+
return nodeStatusMap;
|
|
65
|
+
},
|
|
66
|
+
get kanbanConfig() {
|
|
67
|
+
return kanbanConfig;
|
|
68
|
+
},
|
|
69
|
+
get isLoading() {
|
|
70
|
+
return isLoading;
|
|
71
|
+
},
|
|
72
|
+
get isError() {
|
|
73
|
+
return isError;
|
|
74
|
+
},
|
|
75
|
+
fetchData
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as FormAutocomplete } from '../components/form/FormAutocomplete.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as FormAutocomplete } from '../components/form/FormAutocomplete.svelte';
|
package/dist/form/index.d.ts
CHANGED
|
@@ -75,3 +75,20 @@ export type { FieldSchema, FieldType, FieldFormat, FieldOption, OneOfItem, Schem
|
|
|
75
75
|
export { isFieldOptionArray, isOneOfArray, normalizeOptions, oneOfToOptions, getSchemaOptions } from '../components/form/types.js';
|
|
76
76
|
export { fieldComponentRegistry, hiddenFieldMatcher, checkboxGroupMatcher, enumSelectMatcher, textareaMatcher, rangeMatcher, textFieldMatcher, numberFieldMatcher, toggleMatcher, selectOptionsMatcher, arrayMatcher } from './fieldRegistry.js';
|
|
77
77
|
export type { FieldComponentProps, FieldMatcher, FieldMatcherRegistration, FieldComponent, FieldComponentRegistration } from './fieldRegistry.js';
|
|
78
|
+
/**
|
|
79
|
+
* Use with Svelte's `getContext` inside custom field components registered
|
|
80
|
+
* via `fieldComponentRegistry` to read sibling form field values.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```svelte
|
|
84
|
+
* <script lang="ts">
|
|
85
|
+
* import { getContext } from 'svelte';
|
|
86
|
+
* import { FORM_VALUES_KEY, type FormValuesGetter } from '@flowdrop/flowdrop/form';
|
|
87
|
+
*
|
|
88
|
+
* const getFormValues = getContext<FormValuesGetter | undefined>(FORM_VALUES_KEY);
|
|
89
|
+
* const account = $derived(getFormValues?.()['account']);
|
|
90
|
+
* </script>
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare const FORM_VALUES_KEY: "flowdrop:getFormValues";
|
|
94
|
+
export type FormValuesGetter = () => Record<string, unknown>;
|
package/dist/form/index.js
CHANGED
|
@@ -89,3 +89,22 @@ export {
|
|
|
89
89
|
fieldComponentRegistry,
|
|
90
90
|
// Built-in matchers for custom components
|
|
91
91
|
hiddenFieldMatcher, checkboxGroupMatcher, enumSelectMatcher, textareaMatcher, rangeMatcher, textFieldMatcher, numberFieldMatcher, toggleMatcher, selectOptionsMatcher, arrayMatcher } from './fieldRegistry.js';
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Context keys (for custom field components)
|
|
94
|
+
// ============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Use with Svelte's `getContext` inside custom field components registered
|
|
97
|
+
* via `fieldComponentRegistry` to read sibling form field values.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```svelte
|
|
101
|
+
* <script lang="ts">
|
|
102
|
+
* import { getContext } from 'svelte';
|
|
103
|
+
* import { FORM_VALUES_KEY, type FormValuesGetter } from '@flowdrop/flowdrop/form';
|
|
104
|
+
*
|
|
105
|
+
* const getFormValues = getContext<FormValuesGetter | undefined>(FORM_VALUES_KEY);
|
|
106
|
+
* const account = $derived(getFormValues?.()['account']);
|
|
107
|
+
* </script>
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export const FORM_VALUES_KEY = 'flowdrop:getFormValues';
|
|
@@ -310,6 +310,11 @@ export declare const defaultMessages: {
|
|
|
310
310
|
}) => string;
|
|
311
311
|
readonly executionDuration: "Execution duration";
|
|
312
312
|
};
|
|
313
|
+
readonly messageAnnotations: {
|
|
314
|
+
readonly hierarchyOf: ({ path }: {
|
|
315
|
+
path: string;
|
|
316
|
+
}) => string;
|
|
317
|
+
};
|
|
313
318
|
readonly sessions: {
|
|
314
319
|
readonly header: "Sessions";
|
|
315
320
|
readonly newSession: "New Session";
|
|
@@ -328,6 +333,30 @@ export declare const defaultMessages: {
|
|
|
328
333
|
n: number;
|
|
329
334
|
}) => string;
|
|
330
335
|
};
|
|
336
|
+
readonly executionConsole: {
|
|
337
|
+
readonly header: "Execution";
|
|
338
|
+
readonly noExecutionTitle: "No execution yet";
|
|
339
|
+
readonly noExecutionText: "Create or select a session below, then run your workflow to see execution output here.";
|
|
340
|
+
readonly readyTitle: "Ready to run";
|
|
341
|
+
readonly readyText: "Use the controls below to start the workflow. Output and interactive prompts will appear here.";
|
|
342
|
+
readonly newSession: "New session";
|
|
343
|
+
};
|
|
344
|
+
readonly controlPanel: {
|
|
345
|
+
readonly sessionsLabel: "Sessions";
|
|
346
|
+
readonly noSession: "No session";
|
|
347
|
+
readonly switchSession: "Switch session";
|
|
348
|
+
readonly newSession: "New session";
|
|
349
|
+
readonly pipeline: "Pipeline";
|
|
350
|
+
readonly showPipeline: "Show pipeline";
|
|
351
|
+
readonly hidePipeline: "Hide pipeline";
|
|
352
|
+
readonly refresh: "Refresh";
|
|
353
|
+
readonly refreshTitle: "Refresh status";
|
|
354
|
+
readonly logs: "Logs";
|
|
355
|
+
readonly showLogs: "Show log messages";
|
|
356
|
+
readonly hideLogs: "Hide log messages";
|
|
357
|
+
readonly deleteSession: "Delete session";
|
|
358
|
+
readonly messageStreamLabel: "Execution output";
|
|
359
|
+
};
|
|
331
360
|
};
|
|
332
361
|
readonly nodes: {
|
|
333
362
|
readonly notes: {
|
|
@@ -288,6 +288,12 @@ export const defaultMessages = {
|
|
|
288
288
|
nodeId: ({ id }) => `Node ID: ${id}`,
|
|
289
289
|
executionDuration: 'Execution duration'
|
|
290
290
|
},
|
|
291
|
+
// ARIA labels for message annotations. The hierarchy trail names the
|
|
292
|
+
// actual path so AT users hear "From: ForEach Loop / Greeter" rather
|
|
293
|
+
// than a generic "hierarchy".
|
|
294
|
+
messageAnnotations: {
|
|
295
|
+
hierarchyOf: ({ path }) => `From: ${path}`
|
|
296
|
+
},
|
|
291
297
|
sessions: {
|
|
292
298
|
header: 'Sessions',
|
|
293
299
|
newSession: 'New Session',
|
|
@@ -300,6 +306,30 @@ export const defaultMessages = {
|
|
|
300
306
|
minutesAgo: ({ n }) => `${n}m ago`,
|
|
301
307
|
hoursAgo: ({ n }) => `${n}h ago`,
|
|
302
308
|
daysAgo: ({ n }) => `${n}d ago`
|
|
309
|
+
},
|
|
310
|
+
executionConsole: {
|
|
311
|
+
header: 'Execution',
|
|
312
|
+
noExecutionTitle: 'No execution yet',
|
|
313
|
+
noExecutionText: 'Create or select a session below, then run your workflow to see execution output here.',
|
|
314
|
+
readyTitle: 'Ready to run',
|
|
315
|
+
readyText: 'Use the controls below to start the workflow. Output and interactive prompts will appear here.',
|
|
316
|
+
newSession: 'New session'
|
|
317
|
+
},
|
|
318
|
+
controlPanel: {
|
|
319
|
+
sessionsLabel: 'Sessions',
|
|
320
|
+
noSession: 'No session',
|
|
321
|
+
switchSession: 'Switch session',
|
|
322
|
+
newSession: 'New session',
|
|
323
|
+
pipeline: 'Pipeline',
|
|
324
|
+
showPipeline: 'Show pipeline',
|
|
325
|
+
hidePipeline: 'Hide pipeline',
|
|
326
|
+
refresh: 'Refresh',
|
|
327
|
+
refreshTitle: 'Refresh status',
|
|
328
|
+
logs: 'Logs',
|
|
329
|
+
showLogs: 'Show log messages',
|
|
330
|
+
hideLogs: 'Hide log messages',
|
|
331
|
+
deleteSession: 'Delete session',
|
|
332
|
+
messageStreamLabel: 'Execution output'
|
|
303
333
|
}
|
|
304
334
|
},
|
|
305
335
|
nodes: {
|