@flowdrop/flowdrop 1.10.0 → 1.12.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.
Files changed (53) hide show
  1. package/dist/api/enhanced-client.d.ts +29 -16
  2. package/dist/api/enhanced-client.js +0 -14
  3. package/dist/components/Navbar.svelte +1 -10
  4. package/dist/components/Navbar.svelte.d.ts +1 -9
  5. package/dist/components/PipelineStatus.svelte +9 -12
  6. package/dist/components/WorkflowEditor.svelte +3 -0
  7. package/dist/components/interrupt/ChoicePrompt.svelte +24 -5
  8. package/dist/components/interrupt/ConfirmationPrompt.svelte +5 -0
  9. package/dist/components/interrupt/InterruptBubble.svelte +12 -0
  10. package/dist/components/interrupt/ReviewPrompt.svelte +20 -0
  11. package/dist/components/interrupt/TextInputPrompt.svelte +5 -0
  12. package/dist/components/nodes/GatewayNode.svelte +2 -6
  13. package/dist/components/nodes/WorkflowNode.svelte +2 -6
  14. package/dist/components/playground/ChatInput.svelte +359 -0
  15. package/dist/components/playground/ChatInput.svelte.d.ts +14 -0
  16. package/dist/components/playground/ChatPanel.svelte +100 -724
  17. package/dist/components/playground/ChatPanel.svelte.d.ts +9 -26
  18. package/dist/components/playground/ControlPanel.svelte +496 -0
  19. package/dist/components/playground/ControlPanel.svelte.d.ts +20 -0
  20. package/dist/components/playground/ExecutionConsole.svelte +163 -0
  21. package/dist/components/playground/ExecutionConsole.svelte.d.ts +14 -0
  22. package/dist/components/playground/MessageStream.svelte +283 -0
  23. package/dist/components/playground/MessageStream.svelte.d.ts +27 -0
  24. package/dist/components/playground/PipelineKanbanView.svelte +284 -0
  25. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +11 -0
  26. package/dist/components/playground/PipelinePanel.svelte +204 -65
  27. package/dist/components/playground/PipelinePanel.svelte.d.ts +3 -1
  28. package/dist/components/playground/PipelineTableView.svelte +376 -0
  29. package/dist/components/playground/PipelineTableView.svelte.d.ts +11 -0
  30. package/dist/components/playground/Playground.svelte +262 -1200
  31. package/dist/components/playground/Playground.svelte.d.ts +0 -13
  32. package/dist/components/playground/PlaygroundApp.svelte +110 -0
  33. package/dist/components/playground/PlaygroundApp.svelte.d.ts +28 -0
  34. package/dist/components/playground/PlaygroundStudio.svelte +35 -61
  35. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -1
  36. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +22 -0
  37. package/dist/components/playground/pipelineViewUtils.svelte.js +77 -0
  38. package/dist/messages/defaults.d.ts +24 -0
  39. package/dist/messages/defaults.js +24 -0
  40. package/dist/playground/index.d.ts +8 -2
  41. package/dist/playground/index.js +8 -1
  42. package/dist/playground/mount.d.ts +59 -4
  43. package/dist/playground/mount.js +102 -9
  44. package/dist/stores/playgroundStore.svelte.d.ts +6 -0
  45. package/dist/stores/playgroundStore.svelte.js +21 -1
  46. package/dist/svelte-app.d.ts +2 -10
  47. package/dist/types/index.d.ts +28 -2
  48. package/dist/types/navbar.d.ts +14 -0
  49. package/dist/types/navbar.js +1 -0
  50. package/dist/types/playground.d.ts +5 -2
  51. package/dist/types/playground.js +5 -7
  52. package/dist/utils/nodeStatus.js +15 -5
  53. package/package.json +1 -1
@@ -46,6 +46,9 @@
46
46
  import type { Workflow } from '../types/index.js';
47
47
  import type { EndpointConfig } from '../config/endpoints.js';
48
48
  import type { PlaygroundMode, PlaygroundConfig, PlaygroundSession, PlaygroundMessagesApiResponse, PlaygroundSessionStatus } from '../types/playground.js';
49
+ import type { PartialSettings, SettingsCategory } from '../types/settings.js';
50
+ import type { NavbarAction } from '../types/navbar.js';
51
+ import type { PipelineViewDef } from '../types/index.js';
49
52
  /**
50
53
  * Mount options for Playground component
51
54
  */
@@ -83,13 +86,15 @@ export interface PlaygroundMountOptions {
83
86
  */
84
87
  config?: PlaygroundConfig;
85
88
  /**
86
- * Container height (CSS value)
87
- * @default "100%"
89
+ * Container height (CSS value). If omitted, the library does NOT set an
90
+ * inline height — the host's own CSS owns sizing. Pass a definite value
91
+ * (e.g. `"100dvh"`, `"600px"`) only when mounting into an unsized
92
+ * container.
88
93
  */
89
94
  height?: string;
90
95
  /**
91
- * Container width (CSS value)
92
- * @default "100%"
96
+ * Container width (CSS value). If omitted, the library does NOT set an
97
+ * inline width. See `height` for rationale.
93
98
  */
94
99
  width?: string;
95
100
  /**
@@ -103,6 +108,12 @@ export interface PlaygroundMountOptions {
103
108
  * @param previousStatus - The previous session status
104
109
  */
105
110
  onSessionStatusChange?: (status: PlaygroundSessionStatus, previousStatus: PlaygroundSessionStatus) => void;
111
+ /**
112
+ * Optional setting overrides deep-merged over current settings before mount.
113
+ * Theme is re-initialized on every mount regardless. Mirrors mountFlowDropApp's
114
+ * `settings` option.
115
+ */
116
+ settings?: PartialSettings;
106
117
  }
107
118
  /**
108
119
  * Return type for mounted Playground instance
@@ -206,5 +217,49 @@ export interface PlaygroundStudioMountOptions extends PlaygroundMountOptions {
206
217
  minChatWidth?: number;
207
218
  initialPipelineWidth?: number;
208
219
  onSessionNavigate?: (sessionId: string) => void;
220
+ /** Additional pipeline views injected into the view switcher */
221
+ pipelineViews?: PipelineViewDef[];
209
222
  }
210
223
  export declare function mountPlaygroundStudio(container: HTMLElement, options: PlaygroundStudioMountOptions): Promise<MountedPlayground>;
224
+ export interface PlaygroundAppMountOptions extends Omit<PlaygroundStudioMountOptions, 'mode'> {
225
+ /**
226
+ * Display mode. Modal is unsupported — use mountPlayground() for that.
227
+ * @default "standalone"
228
+ */
229
+ mode?: 'standalone' | 'embedded';
230
+ /** Render the FlowDrop Navbar above the playground (default: true). */
231
+ showNavbar?: boolean;
232
+ /** Title shown in the navbar. Falls back to the workflow name, then "Playground". */
233
+ navbarTitle?: string;
234
+ /** Action buttons rendered in the navbar. Passed straight through to <Navbar primaryActions>. */
235
+ primaryActions?: NavbarAction[];
236
+ /** Show the settings gear icon in the navbar (default: true). */
237
+ showSettings?: boolean;
238
+ /** Restrict which settings categories are exposed in the settings modal. */
239
+ settingsCategories?: SettingsCategory[];
240
+ /** Show the "Sync to Cloud" button in the settings modal. */
241
+ showSettingsSyncButton?: boolean;
242
+ /** Show the reset buttons in the settings modal. */
243
+ showSettingsResetButton?: boolean;
244
+ }
245
+ /**
246
+ * Mount the full-page PlaygroundApp (Navbar + PlaygroundStudio) into a container.
247
+ *
248
+ * Use this when you want the same chrome as the FlowDrop editor — logo,
249
+ * branding, and settings modal — wrapped around the playground. For an
250
+ * embeddable split-pane without the navbar, use mountPlaygroundStudio().
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * const app = await mountPlaygroundApp(container, {
255
+ * workflowId: 'wf-123',
256
+ * endpointConfig: createEndpointConfig('/api/flowdrop'),
257
+ * navbarTitle: 'My Workflow',
258
+ * primaryActions: [
259
+ * { label: 'Edit', href: '/workflows/wf-123/edit', icon: 'mdi:pencil-outline', variant: 'secondary' },
260
+ * { label: 'Workflows', href: '/workflows', icon: 'mdi:arrow-left', variant: 'outline' }
261
+ * ]
262
+ * });
263
+ * ```
264
+ */
265
+ export declare function mountPlaygroundApp(container: HTMLElement, options: PlaygroundAppMountOptions): Promise<MountedPlayground>;
@@ -47,6 +47,8 @@ import { mount, unmount } from 'svelte';
47
47
  import Playground from '../components/playground/Playground.svelte';
48
48
  import PlaygroundModal from '../components/playground/PlaygroundModal.svelte';
49
49
  import PlaygroundStudio from '../components/playground/PlaygroundStudio.svelte';
50
+ import PlaygroundApp from '../components/playground/PlaygroundApp.svelte';
51
+ import { initializeSettings } from '../stores/settingsStore.svelte.js';
50
52
  import { setEndpointConfig } from '../services/api.js';
51
53
  import { playgroundService } from '../services/playgroundService.js';
52
54
  import { getCurrentSession, getSessions, getMessages, getIsExecuting, playgroundActions, applyServerResponse, subscribeToSessionStatus } from '../stores/playgroundStore.svelte.js';
@@ -62,6 +64,28 @@ async function resolveEndpointConfig(endpointConfig) {
62
64
  setEndpointConfig(resolved);
63
65
  return resolved;
64
66
  }
67
+ /**
68
+ * Shared prelude for the playground mount functions: resolves the endpoint
69
+ * config and initializes settings/theme. Each mount function still owns its
70
+ * own argument validation and container sizing (those vary per mode).
71
+ */
72
+ async function prepareMount(options) {
73
+ const finalEndpointConfig = await resolveEndpointConfig(options.endpointConfig);
74
+ await initializeSettings({ defaults: options.settings });
75
+ return finalEndpointConfig;
76
+ }
77
+ /**
78
+ * Apply caller-supplied height/width as inline styles. When omitted, the host
79
+ * CSS owns container sizing — this is deliberate: a default of `100%` does not
80
+ * resolve inside parents with only `min-height` set (a common pattern in
81
+ * Drupal admin chrome), collapsing the playground to zero.
82
+ */
83
+ function sizeContainer(container, height, width) {
84
+ if (height !== undefined)
85
+ container.style.height = height;
86
+ if (width !== undefined)
87
+ container.style.width = width;
88
+ }
65
89
  function buildMountedPlayground(svelteApp, workflowId, config, onSessionStatusChange) {
66
90
  const pollingInterval = config.pollingInterval ?? 1500;
67
91
  const unsubscribeStatus = onSessionStatusChange
@@ -122,7 +146,7 @@ function buildMountedPlayground(svelteApp, workflowId, config, onSessionStatusCh
122
146
  * ```
123
147
  */
124
148
  export async function mountPlayground(container, options) {
125
- const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height = '100%', width = '100%', onClose, onSessionStatusChange } = options;
149
+ const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height, width, settings: initialSettings, onClose, onSessionStatusChange } = options;
126
150
  // Validate required parameters
127
151
  if (!workflowId) {
128
152
  throw new Error('workflowId is required for mountPlayground()');
@@ -134,14 +158,16 @@ export async function mountPlayground(container, options) {
134
158
  if (mode === 'modal' && !onClose) {
135
159
  throw new Error('onClose callback is required for modal mode');
136
160
  }
137
- const finalEndpointConfig = await resolveEndpointConfig(endpointConfig);
161
+ const finalEndpointConfig = await prepareMount({
162
+ endpointConfig,
163
+ settings: initialSettings
164
+ });
138
165
  let targetContainer = container;
139
166
  if (mode === 'modal') {
140
167
  targetContainer = document.body;
141
168
  }
142
169
  else {
143
- container.style.height = height;
144
- container.style.width = width;
170
+ sizeContainer(container, height, width);
145
171
  }
146
172
  let svelteApp;
147
173
  if (mode === 'modal') {
@@ -199,7 +225,7 @@ export function unmountPlayground(app) {
199
225
  }
200
226
  }
201
227
  export async function mountPlaygroundStudio(container, options) {
202
- const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height = '100%', width = '100%', initialPipelineOpen, minChatWidth, initialPipelineWidth, onClose, onSessionNavigate, onSessionStatusChange } = options;
228
+ const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height, width, initialPipelineOpen, minChatWidth, initialPipelineWidth, settings: initialSettings, onClose, onSessionNavigate, onSessionStatusChange, pipelineViews } = options;
203
229
  if (!workflowId) {
204
230
  throw new Error('workflowId is required for mountPlaygroundStudio()');
205
231
  }
@@ -209,9 +235,11 @@ export async function mountPlaygroundStudio(container, options) {
209
235
  if (mode === 'modal') {
210
236
  throw new Error('modal mode is not supported by mountPlaygroundStudio() — use mountPlayground() instead');
211
237
  }
212
- const finalEndpointConfig = await resolveEndpointConfig(endpointConfig);
213
- container.style.height = height;
214
- container.style.width = width;
238
+ const finalEndpointConfig = await prepareMount({
239
+ endpointConfig,
240
+ settings: initialSettings
241
+ });
242
+ sizeContainer(container, height, width);
215
243
  const svelteApp = mount(PlaygroundStudio, {
216
244
  target: container,
217
245
  props: {
@@ -225,7 +253,72 @@ export async function mountPlaygroundStudio(container, options) {
225
253
  onSessionNavigate,
226
254
  initialPipelineOpen,
227
255
  minChatWidth,
228
- initialPipelineWidth
256
+ initialPipelineWidth,
257
+ extraPipelineViews: pipelineViews
258
+ }
259
+ });
260
+ return buildMountedPlayground(svelteApp, workflowId, config, onSessionStatusChange);
261
+ }
262
+ /**
263
+ * Mount the full-page PlaygroundApp (Navbar + PlaygroundStudio) into a container.
264
+ *
265
+ * Use this when you want the same chrome as the FlowDrop editor — logo,
266
+ * branding, and settings modal — wrapped around the playground. For an
267
+ * embeddable split-pane without the navbar, use mountPlaygroundStudio().
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const app = await mountPlaygroundApp(container, {
272
+ * workflowId: 'wf-123',
273
+ * endpointConfig: createEndpointConfig('/api/flowdrop'),
274
+ * navbarTitle: 'My Workflow',
275
+ * primaryActions: [
276
+ * { label: 'Edit', href: '/workflows/wf-123/edit', icon: 'mdi:pencil-outline', variant: 'secondary' },
277
+ * { label: 'Workflows', href: '/workflows', icon: 'mdi:arrow-left', variant: 'outline' }
278
+ * ]
279
+ * });
280
+ * ```
281
+ */
282
+ export async function mountPlaygroundApp(container, options) {
283
+ const { workflowId, workflow, mode = 'standalone', initialSessionId, endpointConfig, config = {}, height, width, showNavbar = true, navbarTitle, primaryActions, showSettings = true, settingsCategories, showSettingsSyncButton, showSettingsResetButton, initialPipelineOpen, minChatWidth, initialPipelineWidth, settings: initialSettings, onClose, onSessionNavigate, onSessionStatusChange } = options;
284
+ if (!workflowId) {
285
+ throw new Error('workflowId is required for mountPlaygroundApp()');
286
+ }
287
+ if (!container) {
288
+ throw new Error('container element is required for mountPlaygroundApp()');
289
+ }
290
+ // Positive narrowing (not `=== 'modal'`) because PlaygroundAppMountOptions
291
+ // Omit-narrows `mode`, so the type lies for JS callers — check the values
292
+ // we actually accept instead of the one we don't.
293
+ if (mode !== 'standalone' && mode !== 'embedded') {
294
+ throw new Error(`mountPlaygroundApp(): mode must be 'standalone' or 'embedded', got ${String(mode)}`);
295
+ }
296
+ const finalEndpointConfig = await prepareMount({
297
+ endpointConfig,
298
+ settings: initialSettings
299
+ });
300
+ sizeContainer(container, height, width);
301
+ const svelteApp = mount(PlaygroundApp, {
302
+ target: container,
303
+ props: {
304
+ workflowId,
305
+ workflow,
306
+ mode,
307
+ initialSessionId,
308
+ endpointConfig: finalEndpointConfig,
309
+ config,
310
+ showNavbar,
311
+ navbarTitle,
312
+ primaryActions,
313
+ showSettings,
314
+ settingsCategories,
315
+ showSettingsSyncButton,
316
+ showSettingsResetButton,
317
+ initialPipelineOpen,
318
+ minChatWidth,
319
+ initialPipelineWidth,
320
+ onClose,
321
+ onSessionNavigate
229
322
  }
230
323
  });
231
324
  return buildMountedPlayground(svelteApp, workflowId, config, onSessionStatusChange);
@@ -89,6 +89,10 @@ export declare function getActiveExecutionId(): string | null;
89
89
  * Pass to PipelinePanel's refreshTrigger prop.
90
90
  */
91
91
  export declare function getPipelineRefreshTrigger(): number;
92
+ /**
93
+ * Whether log messages should be shown in the execution console
94
+ */
95
+ export declare function getShowLogs(): boolean;
92
96
  /**
93
97
  * Playground store actions for modifying state
94
98
  */
@@ -183,6 +187,8 @@ export declare const playgroundActions: {
183
187
  */
184
188
  switchSession: (sessionId: string) => void;
185
189
  pinExecution(executionId: string | null): void;
190
+ setShowLogs(value: boolean): void;
191
+ toggleShowLogs(): void;
186
192
  };
187
193
  /**
188
194
  * Apply a server response to the store. All message and status updates from
@@ -43,6 +43,8 @@ let _lastPollSequenceNumber = $state(null);
43
43
  let _pinnedExecutionId = $state(null);
44
44
  /** Incremented on every message batch that should trigger a pipeline re-fetch */
45
45
  let _pipelineRefreshTrigger = $state(0);
46
+ /** Whether log messages are visible in the execution console */
47
+ let _showLogs = $state(true);
46
48
  /** Latest execution ID derived from current session's executions list */
47
49
  const _latestExecutionId = $derived(_currentSession?.executions?.at(-1)?.id ?? null);
48
50
  /** Active execution: pinned if set, otherwise latest */
@@ -237,6 +239,12 @@ export function getActiveExecutionId() {
237
239
  export function getPipelineRefreshTrigger() {
238
240
  return _pipelineRefreshTrigger;
239
241
  }
242
+ /**
243
+ * Whether log messages should be shown in the execution console
244
+ */
245
+ export function getShowLogs() {
246
+ return _showLogs;
247
+ }
240
248
  // =========================================================================
241
249
  // Helper Functions
242
250
  // =========================================================================
@@ -345,7 +353,11 @@ export const playgroundActions = {
345
353
  // so we only need to check and update the tail entry.
346
354
  // 'idle' means the run finished normally (server returns 'idle' post-completion,
347
355
  // not 'completed'), so map it to 'completed' for the execution entry.
348
- const terminalExecutionStatus = status === 'failed' ? 'failed' : status === 'completed' || status === 'idle' ? 'completed' : null;
356
+ const terminalExecutionStatus = status === 'failed'
357
+ ? 'failed'
358
+ : status === 'completed' || status === 'idle'
359
+ ? 'completed'
360
+ : null;
349
361
  if (terminalExecutionStatus && _currentSession?.executions?.length) {
350
362
  const execs = [..._currentSession.executions];
351
363
  const last = execs[execs.length - 1];
@@ -405,6 +417,8 @@ export const playgroundActions = {
405
417
  * @param message - The message to add
406
418
  */
407
419
  addMessage: (message) => {
420
+ if (_messages.some(m => m.id === message.id))
421
+ return;
408
422
  const seq = message.sequenceNumber ?? 0;
409
423
  let lo = 0, hi = _messages.length;
410
424
  while (lo < hi) {
@@ -500,6 +514,12 @@ export const playgroundActions = {
500
514
  },
501
515
  pinExecution(executionId) {
502
516
  _pinnedExecutionId = executionId;
517
+ },
518
+ setShowLogs(value) {
519
+ _showLogs = value;
520
+ },
521
+ toggleShowLogs() {
522
+ _showLogs = !_showLogs;
503
523
  }
504
524
  };
505
525
  // =========================================================================
@@ -14,16 +14,8 @@ import type { FlowDropTheme, FlowDropThemeName } from './types/theme.js';
14
14
  import type { WorkflowFormatAdapter } from './registry/workflowFormatRegistry.js';
15
15
  import './registry/builtinFormats.js';
16
16
  import type { PartialSettings, SettingsCategory } from './types/settings.js';
17
- /**
18
- * Navbar action configuration
19
- */
20
- export interface NavbarAction {
21
- label: string;
22
- href: string;
23
- icon?: string;
24
- variant?: 'primary' | 'secondary' | 'outline';
25
- onclick?: (event: Event) => void;
26
- }
17
+ import type { NavbarAction } from './types/navbar.js';
18
+ export type { NavbarAction };
27
19
  /**
28
20
  * Mount options for FlowDrop App
29
21
  */
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Core types for the Workflow Library
3
3
  */
4
+ import type { Component } from 'svelte';
4
5
  import type { Node, Edge, XYPosition } from '@xyflow/svelte';
5
6
  import { ConnectionLineType } from '@xyflow/svelte';
6
7
  import type { EndpointConfig } from '../config/endpoints.js';
@@ -1168,7 +1169,32 @@ export type WorkflowsResponse = ApiResponse<Workflow[]>;
1168
1169
  /**
1169
1170
  * Node execution status enum
1170
1171
  */
1171
- export type NodeExecutionStatus = 'idle' | 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'skipped';
1172
+ export type NodeExecutionStatus = 'idle' | 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'skipped' | 'paused' | 'interrupted';
1173
+ export interface KanbanColumnDef {
1174
+ key: string;
1175
+ label: string;
1176
+ statuses: NodeExecutionStatus[];
1177
+ icon?: string;
1178
+ color?: string;
1179
+ }
1180
+ /** Props passed to every pipeline view component, built-in or custom. */
1181
+ export interface PipelineViewProps {
1182
+ pipelineId: string | null;
1183
+ workflow: Workflow;
1184
+ endpointConfig: EndpointConfig;
1185
+ refreshTrigger?: number;
1186
+ }
1187
+ /** A custom view injected into the PipelinePanel view switcher. */
1188
+ export interface PipelineViewDef {
1189
+ /** Unique key — must not clash with built-ins: 'graph' | 'kanban' | 'table' */
1190
+ key: string;
1191
+ /** Label shown in the toggle button tooltip */
1192
+ label: string;
1193
+ /** Iconify icon name, e.g. 'mdi:chart-bar' */
1194
+ icon: string;
1195
+ /** Svelte component that receives PipelineViewProps */
1196
+ component: Component<PipelineViewProps>;
1197
+ }
1172
1198
  /**
1173
1199
  * Node execution tracking information
1174
1200
  */
@@ -1191,7 +1217,7 @@ export interface NodeExecutionInfo {
1191
1217
  /**
1192
1218
  * Workflow execution status
1193
1219
  */
1194
- export type ExecutionStatus = 'idle' | 'running' | 'completed' | 'failed' | 'cancelled';
1220
+ export type ExecutionStatus = 'idle' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused' | 'interrupted';
1195
1221
  /**
1196
1222
  * Workflow execution result
1197
1223
  */
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Navbar action button configuration.
3
+ *
4
+ * Rendered as a link by `<Navbar>` and the mount functions that wrap it.
5
+ */
6
+ export interface NavbarAction {
7
+ label: string;
8
+ href: string;
9
+ icon?: string;
10
+ variant?: 'primary' | 'secondary' | 'outline';
11
+ onclick?: (event: Event) => void;
12
+ /** If true, opens link in new tab with `rel="noopener noreferrer"`. */
13
+ external?: boolean;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,7 +12,10 @@ import type { ConfigProperty } from './index.js';
12
12
  */
13
13
  export type PlaygroundSessionStatus = 'idle' | 'running' | 'awaiting_input' | 'completed' | 'failed';
14
14
  /**
15
- * Statuses that stop polling by default (resource efficiency)
15
+ * Statuses that stop polling by default.
16
+ * Only truly-dormant sessions stop polling — completed/failed/awaiting_input
17
+ * sessions on async servers can still generate messages after the status change,
18
+ * so polling continues until the session is explicitly idle.
16
19
  */
17
20
  export declare const DEFAULT_STOP_POLLING_STATUSES: PlaygroundSessionStatus[];
18
21
  /**
@@ -273,7 +276,7 @@ export interface PlaygroundConfig {
273
276
  /**
274
277
  * Determines if polling should stop for a given session status.
275
278
  * Override to customize which statuses pause polling.
276
- * @default defaultShouldStopPolling (stops on idle, completed, failed, awaiting_input)
279
+ * @default defaultShouldStopPolling (stops only on idle)
277
280
  */
278
281
  shouldStopPolling?: (status: PlaygroundSessionStatus) => boolean;
279
282
  /**
@@ -7,14 +7,12 @@
7
7
  * @module types/playground
8
8
  */
9
9
  /**
10
- * Statuses that stop polling by default (resource efficiency)
10
+ * Statuses that stop polling by default.
11
+ * Only truly-dormant sessions stop polling — completed/failed/awaiting_input
12
+ * sessions on async servers can still generate messages after the status change,
13
+ * so polling continues until the session is explicitly idle.
11
14
  */
12
- export const DEFAULT_STOP_POLLING_STATUSES = [
13
- 'idle',
14
- 'completed',
15
- 'failed',
16
- 'awaiting_input'
17
- ];
15
+ export const DEFAULT_STOP_POLLING_STATUSES = ['idle'];
18
16
  /**
19
17
  * Statuses that are considered terminal by default (clears isExecuting)
20
18
  */
@@ -13,7 +13,9 @@ export function getStatusColor(status) {
13
13
  completed: '#10b981', // emerald
14
14
  failed: '#ef4444', // red
15
15
  cancelled: '#6b7280', // gray
16
- skipped: '#8b5cf6' // violet
16
+ skipped: '#8b5cf6', // violet
17
+ paused: '#f97316', // orange
18
+ interrupted: '#06b6d4' // cyan
17
19
  };
18
20
  return statusColors[status] || statusColors.idle;
19
21
  }
@@ -28,7 +30,9 @@ export function getStatusIcon(status) {
28
30
  completed: 'mdi:check-circle',
29
31
  failed: 'mdi:alert-circle',
30
32
  cancelled: 'mdi:cancel',
31
- skipped: 'mdi:skip-next'
33
+ skipped: 'mdi:skip-next',
34
+ paused: 'mdi:pause-circle-outline',
35
+ interrupted: 'mdi:account-clock-outline'
32
36
  };
33
37
  return statusIcons[status] || statusIcons.idle;
34
38
  }
@@ -43,7 +47,9 @@ export function getStatusLabel(status) {
43
47
  completed: 'Completed',
44
48
  failed: 'Failed',
45
49
  cancelled: 'Cancelled',
46
- skipped: 'Skipped'
50
+ skipped: 'Skipped',
51
+ paused: 'Paused',
52
+ interrupted: 'Waiting'
47
53
  };
48
54
  return statusLabels[status] || statusLabels.idle;
49
55
  }
@@ -58,7 +64,9 @@ export function getStatusBackgroundColor(status) {
58
64
  completed: '#d1fae5', // light emerald
59
65
  failed: '#fee2e2', // light red
60
66
  cancelled: '#f3f4f6', // light gray
61
- skipped: '#ede9fe' // light violet
67
+ skipped: '#ede9fe', // light violet
68
+ paused: '#ffedd5', // light orange
69
+ interrupted: '#cffafe' // light cyan
62
70
  };
63
71
  return statusBackgroundColors[status] || statusBackgroundColors.idle;
64
72
  }
@@ -73,7 +81,9 @@ export function getStatusTextColor(status) {
73
81
  completed: '#059669', // emerald
74
82
  failed: '#dc2626', // red
75
83
  cancelled: '#6b7280', // gray
76
- skipped: '#7c3aed' // violet
84
+ skipped: '#7c3aed', // violet
85
+ paused: '#ea580c', // orange
86
+ interrupted: '#0891b2' // cyan
77
87
  };
78
88
  return statusTextColors[status] || statusTextColors.idle;
79
89
  }
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.10.0",
6
+ "version": "1.12.0",
7
7
  "author": "Shibin Das (D34dMan)",
8
8
  "bugs": {
9
9
  "url": "https://github.com/flowdrop-io/flowdrop/issues"