@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
|
@@ -279,6 +279,42 @@
|
|
|
279
279
|
"description": "Configuration for autocomplete fields (when format is \"autocomplete\")"
|
|
280
280
|
}
|
|
281
281
|
]
|
|
282
|
+
},
|
|
283
|
+
"readOnly": {
|
|
284
|
+
"type": "boolean",
|
|
285
|
+
"description": "JSON Schema `readOnly` keyword. When true, the field is displayed but\ncannot be edited (rendered in a disabled state).\n"
|
|
286
|
+
},
|
|
287
|
+
"height": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"description": "Editor height as a CSS value (e.g. `200px`).\nApplies to editor fields: `json`/`code`, `markdown`, and `template`.\nDefaults: `200px` (code), `300px` (markdown), `250px` (template).\n"
|
|
290
|
+
},
|
|
291
|
+
"darkTheme": {
|
|
292
|
+
"type": "boolean",
|
|
293
|
+
"description": "Force the editor's dark theme on or off.\nApplies to `json`/`code` and `template` fields.\nWhen omitted, the editor follows the resolved app theme.\n"
|
|
294
|
+
},
|
|
295
|
+
"autoFormat": {
|
|
296
|
+
"type": "boolean",
|
|
297
|
+
"default": true,
|
|
298
|
+
"description": "Whether to auto-format JSON on blur.\nApplies to `json`/`code` editor fields.\n"
|
|
299
|
+
},
|
|
300
|
+
"showToolbar": {
|
|
301
|
+
"type": "boolean",
|
|
302
|
+
"default": true,
|
|
303
|
+
"description": "Whether to show the editor toolbar.\nApplies to `markdown` editor fields.\n"
|
|
304
|
+
},
|
|
305
|
+
"showStatusBar": {
|
|
306
|
+
"type": "boolean",
|
|
307
|
+
"default": true,
|
|
308
|
+
"description": "Whether to show the editor status bar.\nApplies to `markdown` editor fields.\n"
|
|
309
|
+
},
|
|
310
|
+
"spellChecker": {
|
|
311
|
+
"type": "boolean",
|
|
312
|
+
"default": false,
|
|
313
|
+
"description": "Whether to enable spell checking.\nApplies to `markdown` editor fields.\n"
|
|
314
|
+
},
|
|
315
|
+
"placeholderExample": {
|
|
316
|
+
"type": "string",
|
|
317
|
+
"description": "Example template string shown as a placeholder hint.\nApplies to `template` fields.\n"
|
|
282
318
|
}
|
|
283
319
|
},
|
|
284
320
|
"required": [
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
* @module services/playgroundService
|
|
8
8
|
*/
|
|
9
9
|
import type { PlaygroundSession, PlaygroundMessage, PlaygroundMessagesApiResponse, PlaygroundSessionStatus } from '../types/playground.js';
|
|
10
|
+
/**
|
|
11
|
+
* Pagination options for {@link PlaygroundService.getMessages}.
|
|
12
|
+
* `since`, `before`, and `latest` are mutually exclusive.
|
|
13
|
+
*/
|
|
14
|
+
export interface GetMessagesOptions {
|
|
15
|
+
/** Forward cursor — only messages with sequenceNumber greater than this value */
|
|
16
|
+
since?: number;
|
|
17
|
+
/** Backward cursor — the page of messages immediately older than this sequence number */
|
|
18
|
+
before?: number;
|
|
19
|
+
/** Return the most recent `limit` messages (conversation tail) */
|
|
20
|
+
latest?: boolean;
|
|
21
|
+
/** Maximum number of messages to return */
|
|
22
|
+
limit?: number;
|
|
23
|
+
}
|
|
10
24
|
/**
|
|
11
25
|
* Playground Service class
|
|
12
26
|
*
|
|
@@ -75,14 +89,19 @@ export declare class PlaygroundService {
|
|
|
75
89
|
*/
|
|
76
90
|
deleteSession(sessionId: string): Promise<void>;
|
|
77
91
|
/**
|
|
78
|
-
* Get messages from a playground session
|
|
92
|
+
* Get messages from a playground session.
|
|
93
|
+
*
|
|
94
|
+
* Three pagination modes (see the OpenAPI spec for the contract):
|
|
95
|
+
* - `since`: forward cursor, returns messages with sequenceNumber > value (polling the live tail)
|
|
96
|
+
* - `before`: backward cursor, returns the page immediately older than the value (scroll-up)
|
|
97
|
+
* - `latest`: returns the most recent `limit` messages (initial load)
|
|
98
|
+
* `since`, `before`, and `latest` are mutually exclusive.
|
|
79
99
|
*
|
|
80
100
|
* @param sessionId - The session UUID
|
|
81
|
-
* @param
|
|
82
|
-
* @param limit - Maximum number of messages to return
|
|
101
|
+
* @param options - Pagination options
|
|
83
102
|
* @returns Messages and session status
|
|
84
103
|
*/
|
|
85
|
-
getMessages(sessionId: string,
|
|
104
|
+
getMessages(sessionId: string, options?: GetMessagesOptions): Promise<PlaygroundMessagesApiResponse>;
|
|
86
105
|
/**
|
|
87
106
|
* Send a message to a playground session
|
|
88
107
|
*
|
|
@@ -168,24 +168,35 @@ export class PlaygroundService {
|
|
|
168
168
|
// Message Handling
|
|
169
169
|
// =========================================================================
|
|
170
170
|
/**
|
|
171
|
-
* Get messages from a playground session
|
|
171
|
+
* Get messages from a playground session.
|
|
172
|
+
*
|
|
173
|
+
* Three pagination modes (see the OpenAPI spec for the contract):
|
|
174
|
+
* - `since`: forward cursor, returns messages with sequenceNumber > value (polling the live tail)
|
|
175
|
+
* - `before`: backward cursor, returns the page immediately older than the value (scroll-up)
|
|
176
|
+
* - `latest`: returns the most recent `limit` messages (initial load)
|
|
177
|
+
* `since`, `before`, and `latest` are mutually exclusive.
|
|
172
178
|
*
|
|
173
179
|
* @param sessionId - The session UUID
|
|
174
|
-
* @param
|
|
175
|
-
* @param limit - Maximum number of messages to return
|
|
180
|
+
* @param options - Pagination options
|
|
176
181
|
* @returns Messages and session status
|
|
177
182
|
*/
|
|
178
|
-
async getMessages(sessionId,
|
|
183
|
+
async getMessages(sessionId, options = {}) {
|
|
179
184
|
const config = this.getConfig();
|
|
180
185
|
let url = buildEndpointUrl(config, config.endpoints.playground.getMessages, {
|
|
181
186
|
sessionId
|
|
182
187
|
});
|
|
183
188
|
const params = new URLSearchParams();
|
|
184
|
-
if (
|
|
185
|
-
params.append('since',
|
|
189
|
+
if (options.since !== undefined) {
|
|
190
|
+
params.append('since', options.since.toString());
|
|
191
|
+
}
|
|
192
|
+
if (options.before !== undefined) {
|
|
193
|
+
params.append('before', options.before.toString());
|
|
186
194
|
}
|
|
187
|
-
if (
|
|
188
|
-
params.append('
|
|
195
|
+
if (options.latest) {
|
|
196
|
+
params.append('latest', 'true');
|
|
197
|
+
}
|
|
198
|
+
if (options.limit !== undefined) {
|
|
199
|
+
params.append('limit', options.limit.toString());
|
|
189
200
|
}
|
|
190
201
|
const queryString = params.toString();
|
|
191
202
|
if (queryString) {
|
|
@@ -257,7 +268,9 @@ export class PlaygroundService {
|
|
|
257
268
|
return;
|
|
258
269
|
}
|
|
259
270
|
try {
|
|
260
|
-
const response = await this.getMessages(sessionId,
|
|
271
|
+
const response = await this.getMessages(sessionId, {
|
|
272
|
+
since: this.lastSequenceNumber ?? undefined
|
|
273
|
+
});
|
|
261
274
|
// Update last sequence number cursor
|
|
262
275
|
if (response.data && response.data.length > 0) {
|
|
263
276
|
const lastMessage = response.data[response.data.length - 1];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module stores/playgroundStore
|
|
8
8
|
*/
|
|
9
|
-
import type { PlaygroundSession, PlaygroundMessage, PlaygroundInputField, PlaygroundSessionStatus, PlaygroundMessagesApiResponse } from '../types/playground.js';
|
|
9
|
+
import type { PlaygroundSession, PlaygroundMessage, PlaygroundInputField, PlaygroundSessionStatus, PlaygroundMessagesApiResponse, PlaygroundExecution } from '../types/playground.js';
|
|
10
10
|
import type { Workflow } from '../types/index.js';
|
|
11
11
|
/**
|
|
12
12
|
* Get the current session
|
|
@@ -83,6 +83,11 @@ export declare function getSessionCount(): number;
|
|
|
83
83
|
export declare function getPinnedExecutionId(): string | null;
|
|
84
84
|
export declare function getLatestExecutionId(): string | null;
|
|
85
85
|
export declare function getActiveExecutionId(): string | null;
|
|
86
|
+
/**
|
|
87
|
+
* Main pipeline runs for the run-switcher. Excludes sub-flow runs, which can't
|
|
88
|
+
* render their own graph and so aren't user-selectable.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getSelectableExecutions(): PlaygroundExecution[];
|
|
86
91
|
/**
|
|
87
92
|
* Counter that increments whenever new messages arrive and the pipeline display
|
|
88
93
|
* should re-fetch — i.e. when following latest or pinned to the latest execution.
|
|
@@ -221,6 +226,22 @@ export declare function getMessagesSnapshot(): PlaygroundMessage[];
|
|
|
221
226
|
* @returns Sequence number of the last message, or null
|
|
222
227
|
*/
|
|
223
228
|
export declare function getLatestSequenceNumber(): number | null;
|
|
229
|
+
/**
|
|
230
|
+
* Get the sequence number of the oldest loaded message, used as the cursor
|
|
231
|
+
* for backward "load older" pagination.
|
|
232
|
+
*
|
|
233
|
+
* @returns Sequence number of the first message, or null
|
|
234
|
+
*/
|
|
235
|
+
export declare function getOldestSequenceNumber(): number | null;
|
|
236
|
+
/**
|
|
237
|
+
* Whether older messages exist before the oldest one currently loaded.
|
|
238
|
+
*/
|
|
239
|
+
export declare function getHasOlder(): boolean;
|
|
240
|
+
/**
|
|
241
|
+
* Set whether older messages remain to be loaded, derived from a
|
|
242
|
+
* backward-pagination response.
|
|
243
|
+
*/
|
|
244
|
+
export declare function setHasOlder(hasOlder: boolean): void;
|
|
224
245
|
/**
|
|
225
246
|
* Subscribe to session status changes using $effect.root.
|
|
226
247
|
* This is designed for use in non-component contexts (e.g., mount.ts).
|
|
@@ -23,6 +23,12 @@ let _sessions = $state([]);
|
|
|
23
23
|
* Messages in the current session
|
|
24
24
|
*/
|
|
25
25
|
let _messages = $state([]);
|
|
26
|
+
/**
|
|
27
|
+
* Whether older messages exist before the oldest one currently loaded.
|
|
28
|
+
* Drives the scroll-up "load older" affordance. Reset whenever the message
|
|
29
|
+
* set is replaced (session switch / clear).
|
|
30
|
+
*/
|
|
31
|
+
let _hasOlder = $state(false);
|
|
26
32
|
/**
|
|
27
33
|
* Whether we are currently loading data
|
|
28
34
|
*/
|
|
@@ -45,8 +51,19 @@ let _pinnedExecutionId = $state(null);
|
|
|
45
51
|
let _pipelineRefreshTrigger = $state(0);
|
|
46
52
|
/** Whether log messages are visible in the execution console */
|
|
47
53
|
let _showLogs = $state(true);
|
|
48
|
-
/**
|
|
49
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The main pipeline runs — the single source of truth for "what's selectable".
|
|
56
|
+
* Sub-flow runs are tracked for classification but excluded here: selecting one
|
|
57
|
+
* can't show its own graph (the panel renders the main pipeline regardless), so
|
|
58
|
+
* listing them would be dead UI. Feeds both the run-switcher and "latest".
|
|
59
|
+
*/
|
|
60
|
+
const _selectableExecutions = $derived((_currentSession?.executions ?? []).filter((e) => !e.isSubflow));
|
|
61
|
+
/**
|
|
62
|
+
* Latest execution ID: the most recent main run, so the sidebar keeps the main
|
|
63
|
+
* pipeline in focus and never auto-follows a sub-flow. Null when no main run is
|
|
64
|
+
* known yet — better an empty panel for a poll than the wrong graph.
|
|
65
|
+
*/
|
|
66
|
+
const _latestExecutionId = $derived(_selectableExecutions.at(-1)?.id ?? null);
|
|
50
67
|
/** Active execution: pinned if set, otherwise latest */
|
|
51
68
|
const _activeExecutionId = $derived(_pinnedExecutionId ?? _latestExecutionId);
|
|
52
69
|
// Derived from server status — never manually set.
|
|
@@ -231,6 +248,13 @@ export function getLatestExecutionId() {
|
|
|
231
248
|
export function getActiveExecutionId() {
|
|
232
249
|
return _activeExecutionId;
|
|
233
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Main pipeline runs for the run-switcher. Excludes sub-flow runs, which can't
|
|
253
|
+
* render their own graph and so aren't user-selectable.
|
|
254
|
+
*/
|
|
255
|
+
export function getSelectableExecutions() {
|
|
256
|
+
return _selectableExecutions;
|
|
257
|
+
}
|
|
234
258
|
/**
|
|
235
259
|
* Counter that increments whenever new messages arrive and the pipeline display
|
|
236
260
|
* should re-fetch — i.e. when following latest or pinned to the latest execution.
|
|
@@ -279,33 +303,57 @@ function sortMessagesChronologically(messageList) {
|
|
|
279
303
|
return a.id.localeCompare(b.id);
|
|
280
304
|
});
|
|
281
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Whether a message was produced by a nested sub-flow (vs the main pipeline).
|
|
308
|
+
* `parentPipelineId` is the authoritative signal — non-null means a parent run
|
|
309
|
+
* triggered this one. Legacy runs that predate the field carry no nesting info,
|
|
310
|
+
* so they're treated as main runs (by design — we don't reclassify history).
|
|
311
|
+
*/
|
|
312
|
+
function isSubflowMessage(msg) {
|
|
313
|
+
return msg.parentPipelineId != null;
|
|
314
|
+
}
|
|
282
315
|
/**
|
|
283
316
|
* Syncs the current session's executions list from incoming messages.
|
|
284
|
-
*
|
|
317
|
+
*
|
|
318
|
+
* Each message's `executionId` identifies the run that produced it, and
|
|
319
|
+
* `parentPipelineId` says whether that run is the main pipeline or a nested
|
|
320
|
+
* sub-flow (see {@link isSubflowMessage}). A run's classification is fixed by
|
|
321
|
+
* its first sighting — every message from a run reports the same parent — so we
|
|
322
|
+
* only act on executionIds we haven't seen before. Sub-flows are tracked but
|
|
323
|
+
* hidden from the run-switcher; a new *main* run clears the pin so the panel
|
|
324
|
+
* auto-follows it, while sub-flow runs never take focus.
|
|
325
|
+
*
|
|
326
|
+
* Executions are appended in arrival order; new runs land at the tail, which is
|
|
327
|
+
* why "latest" reads the last main run.
|
|
285
328
|
*/
|
|
286
329
|
function syncExecutionsFromMessages(messages) {
|
|
287
330
|
if (!_currentSession)
|
|
288
331
|
return;
|
|
289
|
-
const
|
|
290
|
-
const
|
|
332
|
+
const executions = [...(_currentSession.executions ?? [])];
|
|
333
|
+
const seenIds = new Set(executions.map((e) => e.id));
|
|
334
|
+
let added = false;
|
|
335
|
+
let gainedMainRun = false;
|
|
291
336
|
for (const msg of messages) {
|
|
292
|
-
if (msg.executionId
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
337
|
+
if (!msg.executionId || seenIds.has(msg.executionId))
|
|
338
|
+
continue;
|
|
339
|
+
seenIds.add(msg.executionId);
|
|
340
|
+
const isSubflow = isSubflowMessage(msg);
|
|
341
|
+
executions.push({
|
|
342
|
+
id: msg.executionId,
|
|
343
|
+
startedAt: msg.timestamp,
|
|
344
|
+
status: 'running',
|
|
345
|
+
isSubflow
|
|
346
|
+
});
|
|
347
|
+
added = true;
|
|
348
|
+
if (!isSubflow)
|
|
349
|
+
gainedMainRun = true;
|
|
300
350
|
}
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Clear any manual pin so the panel automatically follows the new run.
|
|
351
|
+
if (!added)
|
|
352
|
+
return;
|
|
353
|
+
_currentSession = { ..._currentSession, executions };
|
|
354
|
+
// Auto-follow the new main run by dropping any manual pin.
|
|
355
|
+
if (gainedMainRun)
|
|
307
356
|
_pinnedExecutionId = null;
|
|
308
|
-
}
|
|
309
357
|
}
|
|
310
358
|
// =========================================================================
|
|
311
359
|
// Actions
|
|
@@ -348,22 +396,23 @@ export const playgroundActions = {
|
|
|
348
396
|
updatedAt: new Date().toISOString()
|
|
349
397
|
};
|
|
350
398
|
}
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
//
|
|
399
|
+
// When the session reaches a terminal state, the whole run is finished —
|
|
400
|
+
// including any sub-flow executions, which may sit anywhere in the list
|
|
401
|
+
// (not just the tail), so mark every still-running execution terminal.
|
|
354
402
|
// 'idle' means the run finished normally (server returns 'idle' post-completion,
|
|
355
|
-
// not 'completed'), so map it to 'completed' for the execution
|
|
403
|
+
// not 'completed'), so map it to 'completed' for the execution entries.
|
|
356
404
|
const terminalExecutionStatus = status === 'failed'
|
|
357
405
|
? 'failed'
|
|
358
406
|
: status === 'completed' || status === 'idle'
|
|
359
407
|
? 'completed'
|
|
360
408
|
: null;
|
|
361
409
|
if (terminalExecutionStatus && _currentSession?.executions?.length) {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
410
|
+
const hasRunning = _currentSession.executions.some((e) => e.status === 'running');
|
|
411
|
+
if (hasRunning) {
|
|
412
|
+
_currentSession = {
|
|
413
|
+
..._currentSession,
|
|
414
|
+
executions: _currentSession.executions.map((e) => e.status === 'running' ? { ...e, status: terminalExecutionStatus } : e)
|
|
415
|
+
};
|
|
367
416
|
}
|
|
368
417
|
}
|
|
369
418
|
// Also update in sessions list
|
|
@@ -417,7 +466,7 @@ export const playgroundActions = {
|
|
|
417
466
|
* @param message - The message to add
|
|
418
467
|
*/
|
|
419
468
|
addMessage: (message) => {
|
|
420
|
-
if (_messages.some(m => m.id === message.id))
|
|
469
|
+
if (_messages.some((m) => m.id === message.id))
|
|
421
470
|
return;
|
|
422
471
|
const seq = message.sequenceNumber ?? 0;
|
|
423
472
|
let lo = 0, hi = _messages.length;
|
|
@@ -460,6 +509,7 @@ export const playgroundActions = {
|
|
|
460
509
|
clearMessages: () => {
|
|
461
510
|
_messages = [];
|
|
462
511
|
_lastPollSequenceNumber = null;
|
|
512
|
+
_hasOlder = false;
|
|
463
513
|
},
|
|
464
514
|
/**
|
|
465
515
|
* Set the loading state
|
|
@@ -533,8 +583,8 @@ export const playgroundActions = {
|
|
|
533
583
|
export function applyServerResponse(response) {
|
|
534
584
|
if (response.data && response.data.length > 0) {
|
|
535
585
|
playgroundActions.addMessages(response.data);
|
|
536
|
-
// Refresh pipeline when following latest or pinned to the latest
|
|
537
|
-
// Skip
|
|
586
|
+
// Refresh the pipeline panel when following latest or pinned to the latest
|
|
587
|
+
// run. Skip when pinned to an older run — a historical view that won't change.
|
|
538
588
|
if (_pinnedExecutionId === null || _pinnedExecutionId === _latestExecutionId) {
|
|
539
589
|
_pipelineRefreshTrigger++;
|
|
540
590
|
}
|
|
@@ -584,6 +634,33 @@ export function getLatestSequenceNumber() {
|
|
|
584
634
|
}
|
|
585
635
|
return null;
|
|
586
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Get the sequence number of the oldest loaded message, used as the cursor
|
|
639
|
+
* for backward "load older" pagination.
|
|
640
|
+
*
|
|
641
|
+
* @returns Sequence number of the first message, or null
|
|
642
|
+
*/
|
|
643
|
+
export function getOldestSequenceNumber() {
|
|
644
|
+
for (let i = 0; i < _messages.length; i++) {
|
|
645
|
+
if (_messages[i].sequenceNumber !== undefined) {
|
|
646
|
+
return _messages[i].sequenceNumber;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Whether older messages exist before the oldest one currently loaded.
|
|
653
|
+
*/
|
|
654
|
+
export function getHasOlder() {
|
|
655
|
+
return _hasOlder;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Set whether older messages remain to be loaded, derived from a
|
|
659
|
+
* backward-pagination response.
|
|
660
|
+
*/
|
|
661
|
+
export function setHasOlder(hasOlder) {
|
|
662
|
+
_hasOlder = hasOlder;
|
|
663
|
+
}
|
|
587
664
|
/**
|
|
588
665
|
* Subscribe to session status changes using $effect.root.
|
|
589
666
|
* This is designed for use in non-component contexts (e.g., mount.ts).
|
|
@@ -62,6 +62,14 @@ export interface PlaygroundExecution {
|
|
|
62
62
|
id: string;
|
|
63
63
|
startedAt: string;
|
|
64
64
|
status: 'running' | 'completed' | 'failed';
|
|
65
|
+
/**
|
|
66
|
+
* Client-derived flag: true when this run is a nested sub-flow rather than a
|
|
67
|
+
* main pipeline run. Inferred from its messages' `parentPipelineId` (with a
|
|
68
|
+
* `hierarchy` depth ≥ 2 fallback for legacy runs predating that field). Used
|
|
69
|
+
* to keep the sidebar on the main pipeline and hide sub-flows from the
|
|
70
|
+
* run-switcher. Not part of the wire contract — the server never sets this.
|
|
71
|
+
*/
|
|
72
|
+
isSubflow?: boolean;
|
|
65
73
|
}
|
|
66
74
|
/**
|
|
67
75
|
* Playground session representing a test conversation
|
|
@@ -94,7 +102,12 @@ export interface PlaygroundSession {
|
|
|
94
102
|
createdAt: string;
|
|
95
103
|
/** Last activity timestamp (ISO 8601) */
|
|
96
104
|
updatedAt: string;
|
|
97
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Main pipeline runs triggered within this session, ordered oldest-first.
|
|
107
|
+
* Sub-flow (nested) runs are excluded — they're surfaced only on individual
|
|
108
|
+
* messages via `parentPipelineId`. The run-switcher relies on this being
|
|
109
|
+
* main-runs-only; runs added from messages are classified client-side.
|
|
110
|
+
*/
|
|
98
111
|
executions?: PlaygroundExecution[];
|
|
99
112
|
/** Custom session metadata */
|
|
100
113
|
metadata?: Record<string, unknown>;
|
|
@@ -216,6 +229,19 @@ export interface PlaygroundMessage {
|
|
|
216
229
|
parentMessageId?: string;
|
|
217
230
|
/** Pipeline/execution ID that generated this message */
|
|
218
231
|
executionId?: string | null;
|
|
232
|
+
/**
|
|
233
|
+
* Execution ID of the parent pipeline when this message came from a nested
|
|
234
|
+
* sub-flow; `null`/absent for top-level (main pipeline) messages. Authoritative
|
|
235
|
+
* nesting signal (unlike the display-only `hierarchy`) — used to keep the main
|
|
236
|
+
* pipeline in focus and hide sub-flow runs from the run-switcher.
|
|
237
|
+
*/
|
|
238
|
+
parentPipelineId?: string | null;
|
|
239
|
+
/**
|
|
240
|
+
* Execution ID of the top-level pipeline this message ultimately belongs to.
|
|
241
|
+
* Equals `executionId` for main-pipeline messages; for sub-flow messages it
|
|
242
|
+
* points to the main run that triggered the sub-flow.
|
|
243
|
+
*/
|
|
244
|
+
rootPipelineId?: string | null;
|
|
219
245
|
/** Associated node ID (for log/assistant messages) */
|
|
220
246
|
nodeId?: string | null;
|
|
221
247
|
/**
|
|
@@ -301,6 +327,12 @@ export interface PlaygroundConfig {
|
|
|
301
327
|
pollingInterval?: number;
|
|
302
328
|
/** Maximum number of messages to display (default: 500) */
|
|
303
329
|
maxMessages?: number;
|
|
330
|
+
/**
|
|
331
|
+
* Number of messages to fetch per page (default: 50).
|
|
332
|
+
* The initial load fetches the most recent page; scrolling up loads
|
|
333
|
+
* older pages of this size on demand.
|
|
334
|
+
*/
|
|
335
|
+
messagePageSize?: number;
|
|
304
336
|
/** Auto-scroll to bottom on new messages (default: true) */
|
|
305
337
|
autoScroll?: boolean;
|
|
306
338
|
/** Show timestamps on messages (default: true) */
|
|
@@ -433,8 +465,10 @@ export type PlaygroundMessageResponse = PlaygroundApiResponse<PlaygroundMessage>
|
|
|
433
465
|
* Type alias for messages list response with polling metadata
|
|
434
466
|
*/
|
|
435
467
|
export interface PlaygroundMessagesApiResponse extends PlaygroundApiResponse<PlaygroundMessage[]> {
|
|
436
|
-
/** Whether
|
|
468
|
+
/** Whether more recent messages remain after this page (forward pagination via `since`) */
|
|
437
469
|
hasMore?: boolean;
|
|
470
|
+
/** Whether older messages exist before the first message in this page (backward pagination via `latest`/`before`) */
|
|
471
|
+
hasOlder?: boolean;
|
|
438
472
|
/** Current session status */
|
|
439
473
|
sessionStatus?: PlaygroundSessionStatus;
|
|
440
474
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "A drop-in visual workflow editor for any web application. You own the backend. You own the data. You own the orchestration.",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.14.0",
|
|
7
7
|
"author": "Shibin Das (D34dMan)",
|
|
8
8
|
"bugs": {
|
|
9
9
|
"url": "https://github.com/flowdrop-io/flowdrop/issues"
|
|
@@ -125,6 +125,12 @@
|
|
|
125
125
|
},
|
|
126
126
|
"./schema/v1": {
|
|
127
127
|
"default": "./dist/schemas/v1/workflow.schema.json"
|
|
128
|
+
},
|
|
129
|
+
"./openapi": {
|
|
130
|
+
"default": "./dist/openapi/v1/openapi.yaml"
|
|
131
|
+
},
|
|
132
|
+
"./openapi/v1": {
|
|
133
|
+
"default": "./dist/openapi/v1/openapi.yaml"
|
|
128
134
|
}
|
|
129
135
|
},
|
|
130
136
|
"peerDependencies": {
|