@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.1

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 (215) hide show
  1. package/CHANGELOG.md +475 -0
  2. package/MIGRATION-2.0.md +472 -0
  3. package/README.md +23 -23
  4. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  5. package/dist/adapters/WorkflowAdapter.js +14 -8
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
  7. package/dist/chat/batchFeedback.d.ts +39 -0
  8. package/dist/chat/batchFeedback.js +51 -0
  9. package/dist/commands/executor.js +15 -1
  10. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  11. package/dist/commands/storeIntegration.svelte.js +26 -21
  12. package/dist/commands/types.d.ts +2 -0
  13. package/dist/components/App.svelte +162 -192
  14. package/dist/components/App.svelte.d.ts +47 -8
  15. package/dist/components/ConfigForm.svelte +71 -47
  16. package/dist/components/ConfigModal.svelte +7 -2
  17. package/dist/components/ConnectionLine.svelte +4 -2
  18. package/dist/components/Navbar.svelte +61 -1
  19. package/dist/components/NodeSidebar.svelte +27 -45
  20. package/dist/components/NodeStatusOverlay.svelte +94 -6
  21. package/dist/components/NodeSwapPicker.svelte +10 -8
  22. package/dist/components/PipelineStatus.svelte +16 -67
  23. package/dist/components/PortCoordinateTracker.svelte +5 -6
  24. package/dist/components/SchemaForm.stories.svelte +1 -3
  25. package/dist/components/SchemaForm.svelte +18 -25
  26. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  27. package/dist/components/SettingsModal.svelte +8 -3
  28. package/dist/components/SettingsPanel.svelte +20 -4
  29. package/dist/components/SwapMappingEditor.svelte +67 -49
  30. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  31. package/dist/components/UniversalNode.svelte +9 -7
  32. package/dist/components/WorkflowEditor.svelte +118 -111
  33. package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
  34. package/dist/components/chat/AIChatPanel.svelte +93 -89
  35. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  36. package/dist/components/chat/CommandPreview.svelte +2 -1
  37. package/dist/components/console/CommandConsole.svelte +7 -5
  38. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  39. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  40. package/dist/components/console/ConsoleInput.svelte +15 -6
  41. package/dist/components/console/ConsoleOutput.svelte +2 -1
  42. package/dist/components/form/FormArray.svelte +5 -9
  43. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  44. package/dist/components/form/FormAutocomplete.svelte +8 -6
  45. package/dist/components/form/FormField.svelte +4 -2
  46. package/dist/components/form/FormFieldLight.svelte +4 -2
  47. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  48. package/dist/components/form/FormRangeField.svelte +1 -0
  49. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  50. package/dist/components/form/FormToggle.svelte +5 -12
  51. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  52. package/dist/components/form/templateAutocomplete.js +1 -5
  53. package/dist/components/form/types.d.ts +1 -14
  54. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  55. package/dist/components/interrupt/InterruptBubble.svelte +16 -17
  56. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  57. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  58. package/dist/components/layouts/MainLayout.svelte +20 -13
  59. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  60. package/dist/components/nodes/AtomNode.svelte +17 -5
  61. package/dist/components/nodes/GatewayNode.svelte +19 -10
  62. package/dist/components/nodes/IdeaNode.svelte +7 -0
  63. package/dist/components/nodes/SimpleNode.svelte +11 -6
  64. package/dist/components/nodes/SquareNode.svelte +15 -8
  65. package/dist/components/nodes/TerminalNode.svelte +9 -4
  66. package/dist/components/nodes/ToolNode.svelte +7 -1
  67. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  68. package/dist/components/playground/ChatInput.svelte +11 -14
  69. package/dist/components/playground/ChatPanel.svelte +6 -49
  70. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  71. package/dist/components/playground/ControlPanel.svelte +134 -123
  72. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  73. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  74. package/dist/components/playground/InputCollector.svelte +11 -9
  75. package/dist/components/playground/MessageStream.svelte +17 -23
  76. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  77. package/dist/components/playground/PipelinePanel.svelte +11 -5
  78. package/dist/components/playground/PipelineTableView.svelte +186 -44
  79. package/dist/components/playground/Playground.svelte +90 -92
  80. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  81. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  82. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  83. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  84. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  85. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  86. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  87. package/dist/components/playground/SessionManager.svelte +9 -12
  88. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  89. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  90. package/dist/config/endpoints.d.ts +0 -7
  91. package/dist/config/endpoints.js +2 -10
  92. package/dist/core/index.d.ts +4 -4
  93. package/dist/core/index.js +6 -6
  94. package/dist/display/index.d.ts +0 -2
  95. package/dist/display/index.js +0 -6
  96. package/dist/editor/index.d.ts +19 -20
  97. package/dist/editor/index.js +25 -35
  98. package/dist/form/code.d.ts +25 -15
  99. package/dist/form/code.js +44 -41
  100. package/dist/form/fieldRegistry.d.ts +17 -13
  101. package/dist/form/fieldRegistry.js +32 -12
  102. package/dist/form/full.d.ts +17 -13
  103. package/dist/form/full.js +22 -27
  104. package/dist/form/index.d.ts +3 -3
  105. package/dist/form/index.js +3 -3
  106. package/dist/form/markdown.d.ts +13 -8
  107. package/dist/form/markdown.js +22 -23
  108. package/dist/helpers/proximityConnect.d.ts +3 -2
  109. package/dist/helpers/proximityConnect.js +2 -5
  110. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  111. package/dist/helpers/workflowEditorHelper.js +27 -25
  112. package/dist/index.d.ts +28 -24
  113. package/dist/index.js +27 -50
  114. package/dist/messages/defaults.d.ts +2 -5
  115. package/dist/messages/defaults.js +3 -6
  116. package/dist/messages/index.d.ts +0 -1
  117. package/dist/messages/index.js +0 -1
  118. package/dist/mocks/app-forms.d.ts +6 -2
  119. package/dist/mocks/app-forms.js +11 -4
  120. package/dist/openapi/v1/openapi.yaml +3 -3
  121. package/dist/playground/index.d.ts +2 -3
  122. package/dist/playground/index.js +2 -30
  123. package/dist/playground/mount.d.ts +15 -0
  124. package/dist/playground/mount.js +46 -20
  125. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  126. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  127. package/dist/registry/builtinFormats.d.ts +9 -18
  128. package/dist/registry/builtinFormats.js +9 -39
  129. package/dist/registry/builtinNodes.d.ts +0 -25
  130. package/dist/registry/builtinNodes.js +1 -50
  131. package/dist/registry/index.d.ts +3 -4
  132. package/dist/registry/index.js +4 -6
  133. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  134. package/dist/registry/nodeComponentRegistry.js +235 -17
  135. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  136. package/dist/registry/workflowFormatRegistry.js +24 -8
  137. package/dist/{schema → schemas}/index.d.ts +2 -2
  138. package/dist/{schema → schemas}/index.js +2 -2
  139. package/dist/schemas/v1/workflow.schema.json +3 -3
  140. package/dist/services/agentSpecExecutionService.js +0 -1
  141. package/dist/services/apiVariableService.d.ts +2 -1
  142. package/dist/services/apiVariableService.js +5 -22
  143. package/dist/services/autoSaveService.d.ts +7 -0
  144. package/dist/services/autoSaveService.js +6 -4
  145. package/dist/services/chatService.d.ts +8 -4
  146. package/dist/services/chatService.js +15 -15
  147. package/dist/services/draftStorage.d.ts +129 -13
  148. package/dist/services/draftStorage.js +185 -37
  149. package/dist/services/dynamicSchemaService.d.ts +2 -1
  150. package/dist/services/dynamicSchemaService.js +5 -22
  151. package/dist/services/globalSave.d.ts +13 -12
  152. package/dist/services/globalSave.js +29 -51
  153. package/dist/services/historyService.d.ts +9 -3
  154. package/dist/services/historyService.js +9 -3
  155. package/dist/services/interruptService.d.ts +14 -9
  156. package/dist/services/interruptService.js +27 -27
  157. package/dist/services/nodeExecutionService.d.ts +18 -3
  158. package/dist/services/nodeExecutionService.js +71 -45
  159. package/dist/services/playgroundService.d.ts +14 -9
  160. package/dist/services/playgroundService.js +31 -30
  161. package/dist/services/variableService.d.ts +2 -1
  162. package/dist/services/variableService.js +2 -2
  163. package/dist/services/workflowStorage.js +6 -6
  164. package/dist/stores/apiContext.d.ts +45 -0
  165. package/dist/stores/apiContext.js +65 -0
  166. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  167. package/dist/stores/categoriesStore.svelte.js +70 -64
  168. package/dist/stores/getInstance.svelte.d.ts +39 -0
  169. package/dist/stores/getInstance.svelte.js +65 -0
  170. package/dist/stores/historyStore.svelte.d.ts +77 -93
  171. package/dist/stores/historyStore.svelte.js +134 -160
  172. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  173. package/dist/stores/instanceContainer.svelte.js +114 -0
  174. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  175. package/dist/stores/interruptStore.svelte.js +253 -226
  176. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  177. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  178. package/dist/stores/playgroundStore.svelte.d.ts +169 -222
  179. package/dist/stores/playgroundStore.svelte.js +515 -580
  180. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  181. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  182. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  183. package/dist/stores/settingsStore.svelte.js +47 -12
  184. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  185. package/dist/stores/workflowStore.svelte.js +449 -501
  186. package/dist/stories/EdgeDecorator.svelte +5 -2
  187. package/dist/stories/NodeDecorator.svelte +5 -3
  188. package/dist/svelte-app.d.ts +60 -10
  189. package/dist/svelte-app.js +157 -53
  190. package/dist/types/events.d.ts +6 -3
  191. package/dist/types/index.d.ts +33 -3
  192. package/dist/types/navbar.d.ts +7 -0
  193. package/dist/types/playground.d.ts +18 -3
  194. package/dist/types/settings.d.ts +13 -0
  195. package/dist/types/settings.js +1 -0
  196. package/dist/utils/colors.d.ts +47 -21
  197. package/dist/utils/colors.js +69 -68
  198. package/dist/utils/connections.d.ts +9 -15
  199. package/dist/utils/connections.js +13 -32
  200. package/dist/utils/duration.d.ts +13 -0
  201. package/dist/utils/duration.js +45 -0
  202. package/dist/utils/icons.d.ts +5 -2
  203. package/dist/utils/icons.js +6 -5
  204. package/dist/utils/nodeSwap.d.ts +6 -2
  205. package/dist/utils/nodeSwap.js +62 -126
  206. package/dist/utils/nodeTypes.d.ts +17 -8
  207. package/dist/utils/nodeTypes.js +26 -19
  208. package/dist/utils/performanceUtils.js +7 -0
  209. package/package.json +6 -5
  210. package/dist/messages/deprecation.d.ts +0 -20
  211. package/dist/messages/deprecation.js +0 -33
  212. package/dist/registry/plugin.d.ts +0 -215
  213. package/dist/registry/plugin.js +0 -249
  214. package/dist/services/api.d.ts +0 -129
  215. package/dist/services/api.js +0 -217
@@ -24,21 +24,9 @@
24
24
  } from '../../types/playground.js';
25
25
  import { playgroundService } from '../../services/playgroundService.js';
26
26
  import { interruptService } from '../../services/interruptService.js';
27
- import { setEndpointConfig } from '../../services/api.js';
28
- import {
29
- getCurrentSession,
30
- getSessions,
31
- getIsExecuting,
32
- getIsLoading,
33
- getError,
34
- playgroundActions,
35
- applyServerResponse,
36
- getLatestSequenceNumber,
37
- getOldestSequenceNumber,
38
- setHasOlder
39
- } from '../../stores/playgroundStore.svelte.js';
27
+ import { provideInstance } from '../../stores/getInstance.svelte.js';
28
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
40
29
  import type { PlaygroundMessagesApiResponse } from '../../types/playground.js';
41
- import { interruptActions } from '../../stores/interruptStore.svelte.js';
42
30
  import { logger } from '../../utils/logger.js';
43
31
 
44
32
  interface Props {
@@ -52,6 +40,7 @@
52
40
  onTogglePanel?: () => void;
53
41
  isPipelinePanelOpen?: boolean;
54
42
  onSessionNavigate?: (sessionId: string) => void;
43
+ instance?: FlowDropInstance;
55
44
  }
56
45
 
57
46
  let {
@@ -64,9 +53,14 @@
64
53
  onClose,
65
54
  onTogglePanel,
66
55
  isPipelinePanelOpen = false,
67
- onSessionNavigate
56
+ onSessionNavigate,
57
+ instance
68
58
  }: Props = $props();
69
59
 
60
+ // Resolve/provide once at init; the instance prop is a fixed mount-time choice.
61
+ // svelte-ignore state_referenced_locally
62
+ const fd = provideInstance(instance);
63
+
70
64
  let loadedInitialSessionId = $state<string | undefined>(undefined);
71
65
  let autoRunTriggered = $state(false);
72
66
  let isRefreshing = $state(false);
@@ -78,7 +72,7 @@
78
72
 
79
73
  // Vertical resizer state for the ExecutionConsole ↔ ControlPanel split.
80
74
  let playgroundContentEl = $state<HTMLElement | null>(null);
81
- let controlPanelHeight = $state(280);
75
+ let controlPanelHeight = $state(140);
82
76
  let isVerticalResizing = $state(false);
83
77
  let containerHeight = $state(0);
84
78
  let dragContainerBottom = 0;
@@ -132,18 +126,18 @@
132
126
  }
133
127
 
134
128
  onMount(() => {
135
- if (endpointConfig) setEndpointConfig(endpointConfig);
136
- if (workflow) playgroundActions.setWorkflow(workflow);
129
+ if (endpointConfig) fd.api.configure(endpointConfig);
130
+ if (workflow) fd.playground.setWorkflow(workflow);
137
131
 
138
132
  const handleVisibility = () => {
139
133
  if (document.visibilityState === 'visible' && playgroundService.isPolling()) {
140
- const sessionId = getCurrentSession()?.id;
134
+ const sessionId = fd.playground.currentSession?.id;
141
135
  if (sessionId) {
142
136
  void playgroundService
143
- .getMessages(sessionId, {
137
+ .getMessages(fd.api.config, sessionId, {
144
138
  since: playgroundService.getLastSequenceNumber() ?? undefined
145
139
  })
146
- .then((response) => applyServerResponse(response, sessionId))
140
+ .then((response) => fd.playground.applyServerResponse(response, sessionId))
147
141
  .catch((err) => logger.error('[Playground] Visibility catchup failed:', err));
148
142
  }
149
143
  }
@@ -168,7 +162,7 @@
168
162
  if (!initialSessionId) return;
169
163
  if (loadedInitialSessionId === initialSessionId) return;
170
164
 
171
- const sessionList = getSessions();
165
+ const sessionList = fd.playground.sessions;
172
166
  if (sessionList.length === 0) return;
173
167
 
174
168
  void loadInitialSession(initialSessionId);
@@ -194,7 +188,7 @@
194
188
  }
195
189
 
196
190
  async function loadInitialSession(sessionId: string): Promise<void> {
197
- const sessionList = getSessions();
191
+ const sessionList = fd.playground.sessions;
198
192
  const sessionExists = sessionList.some((s) => s.id === sessionId);
199
193
 
200
194
  if (!sessionExists) {
@@ -217,48 +211,48 @@
217
211
  onDestroy(() => {
218
212
  playgroundService.stopPolling();
219
213
  interruptService.stopPolling();
220
- playgroundActions.reset();
221
- interruptActions.reset();
214
+ fd.playground.reset();
215
+ fd.interrupts.reset();
222
216
  });
223
217
 
224
218
  async function loadSessions(): Promise<void> {
225
- playgroundActions.setLoading(true);
226
- playgroundActions.setError(null);
219
+ fd.playground.setLoading(true);
220
+ fd.playground.setError(null);
227
221
 
228
222
  try {
229
- const sessionList = await playgroundService.listSessions(workflowId);
230
- playgroundActions.setSessions(sessionList);
223
+ const sessionList = await playgroundService.listSessions(fd.api.config, workflowId);
224
+ fd.playground.setSessions(sessionList);
231
225
  } catch (err) {
232
226
  const errorMessage = err instanceof Error ? err.message : 'Failed to load sessions';
233
- playgroundActions.setError(errorMessage);
227
+ fd.playground.setError(errorMessage);
234
228
  logger.error('Failed to load sessions:', err);
235
229
  } finally {
236
- playgroundActions.setLoading(false);
230
+ fd.playground.setLoading(false);
237
231
  }
238
232
  }
239
233
 
240
234
  async function loadSession(sessionId: string): Promise<void> {
241
- playgroundActions.setLoading(true);
242
- playgroundActions.setError(null);
235
+ fd.playground.setLoading(true);
236
+ fd.playground.setError(null);
243
237
  const token = ++loadToken;
244
238
 
245
239
  try {
246
- const session = await playgroundService.getSession(sessionId);
240
+ const session = await playgroundService.getSession(fd.api.config, sessionId);
247
241
  if (token !== loadToken) return; // a newer session load superseded us
248
- playgroundActions.setCurrentSession(session);
242
+ fd.playground.setCurrentSession(session);
249
243
 
250
244
  // Load only the most recent page; older messages load on demand when the
251
245
  // user scrolls up (loadOlderMessages). Clear right before applying the
252
246
  // fresh page — not before the await — so switching sessions doesn't blank
253
247
  // the view for the duration of the fetch.
254
- const response = await playgroundService.getMessages(sessionId, {
248
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
255
249
  latest: true,
256
250
  limit: messagePageSize
257
251
  });
258
252
  if (token !== loadToken) return;
259
- playgroundActions.clearMessages();
260
- applyServerResponse(response, sessionId);
261
- setHasOlder(deriveHasOlder(response));
253
+ fd.playground.clearMessages();
254
+ fd.playground.applyServerResponse(response, sessionId);
255
+ fd.playground.setHasOlder(deriveHasOlder(response));
262
256
 
263
257
  if (session.status !== 'idle') {
264
258
  // Seed polling from the newest loaded message so it tails live updates
@@ -268,10 +262,10 @@
268
262
  } catch (err) {
269
263
  if (token !== loadToken) return; // don't surface a superseded load's error
270
264
  const errorMessage = err instanceof Error ? err.message : 'Failed to load session';
271
- playgroundActions.setError(errorMessage);
265
+ fd.playground.setError(errorMessage);
272
266
  logger.error('Failed to load session:', err);
273
267
  } finally {
274
- if (token === loadToken) playgroundActions.setLoading(false);
268
+ if (token === loadToken) fd.playground.setLoading(false);
275
269
  }
276
270
  }
277
271
 
@@ -282,22 +276,22 @@
282
276
  * historical fetch never disturbs the live polling cursor or pipeline view.
283
277
  */
284
278
  async function loadOlderMessages(): Promise<void> {
285
- const sessionId = getCurrentSession()?.id;
286
- const before = getOldestSequenceNumber();
279
+ const sessionId = fd.playground.currentSession?.id;
280
+ const before = fd.playground.oldestSequenceNumber;
287
281
  if (!sessionId || before === null) return;
288
282
 
289
283
  try {
290
- const response = await playgroundService.getMessages(sessionId, {
284
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
291
285
  before,
292
286
  limit: messagePageSize
293
287
  });
294
288
  // The session may have changed while the fetch was in flight — don't
295
289
  // splice an old session's page into the new session's store.
296
- if (getCurrentSession()?.id !== sessionId) return;
290
+ if (fd.playground.currentSession?.id !== sessionId) return;
297
291
  if (response.data && response.data.length > 0) {
298
- playgroundActions.addMessages(response.data);
292
+ fd.playground.addMessages(response.data);
299
293
  }
300
- setHasOlder(deriveHasOlder(response));
294
+ fd.playground.setHasOlder(deriveHasOlder(response));
301
295
  } catch (err) {
302
296
  logger.error('[Playground] Failed to load older messages:', err);
303
297
  }
@@ -314,12 +308,12 @@
314
308
  }
315
309
 
316
310
  async function handleCreateSession(): Promise<void> {
317
- playgroundActions.setLoading(true);
318
- playgroundActions.setError(null);
311
+ fd.playground.setLoading(true);
312
+ fd.playground.setError(null);
319
313
 
320
314
  try {
321
- const sessionName = `Session ${getSessions().length + 1}`;
322
- const session = await playgroundService.createSession(workflowId, sessionName);
315
+ const sessionName = `Session ${fd.playground.sessions.length + 1}`;
316
+ const session = await playgroundService.createSession(fd.api.config, workflowId, sessionName);
323
317
 
324
318
  // Stop polling the previous (possibly running) session before switching,
325
319
  // mirroring handleSelectSession. Otherwise its next poll keeps the old
@@ -331,60 +325,60 @@
331
325
  return;
332
326
  }
333
327
 
334
- playgroundActions.addSession(session);
335
- playgroundActions.setCurrentSession(session);
336
- playgroundActions.clearMessages();
328
+ fd.playground.addSession(session);
329
+ fd.playground.setCurrentSession(session);
330
+ fd.playground.clearMessages();
337
331
  } catch (err) {
338
332
  const errorMessage = err instanceof Error ? err.message : 'Failed to create session';
339
- playgroundActions.setError(errorMessage);
333
+ fd.playground.setError(errorMessage);
340
334
  logger.error('Failed to create session:', err);
341
335
  } finally {
342
- playgroundActions.setLoading(false);
336
+ fd.playground.setLoading(false);
343
337
  }
344
338
  }
345
339
 
346
340
  async function handleSelectSession(sessionId: string): Promise<void> {
347
- playgroundActions.pinExecution(null);
348
- const currentSessionId = getCurrentSession()?.id;
341
+ fd.playground.pinExecution(null);
342
+ const currentSessionId = fd.playground.currentSession?.id;
349
343
  if (currentSessionId === sessionId) return;
350
344
 
351
345
  playgroundService.stopPolling();
352
- playgroundActions.updateSessionStatus('idle');
346
+ fd.playground.updateSessionStatus('idle');
353
347
  await loadSession(sessionId);
354
348
  }
355
349
 
356
350
  async function handleDeleteSession(sessionId: string): Promise<void> {
357
351
  try {
358
- await playgroundService.deleteSession(sessionId);
359
- playgroundActions.removeSession(sessionId);
352
+ await playgroundService.deleteSession(fd.api.config, sessionId);
353
+ fd.playground.removeSession(sessionId);
360
354
 
361
- if (getCurrentSession()?.id === sessionId) {
355
+ if (fd.playground.currentSession?.id === sessionId) {
362
356
  playgroundService.stopPolling();
363
357
  }
364
358
  } catch (err) {
365
359
  const errorMessage = err instanceof Error ? err.message : 'Failed to delete session';
366
- playgroundActions.setError(errorMessage);
360
+ fd.playground.setError(errorMessage);
367
361
  logger.error('Failed to delete session:', err);
368
362
  }
369
363
  }
370
364
 
371
365
  async function handleSendMessage(content: string): Promise<void> {
372
- if (getIsExecuting()) return;
366
+ if (fd.playground.isExecuting) return;
373
367
 
374
- if (!getCurrentSession()) {
368
+ if (!fd.playground.currentSession) {
375
369
  await handleCreateSession();
376
- if (!getCurrentSession()) return;
370
+ if (!fd.playground.currentSession) return;
377
371
  }
378
372
 
379
- const sessionId = getCurrentSession()!.id;
373
+ const sessionId = fd.playground.currentSession!.id;
380
374
 
381
- playgroundActions.updateSessionStatus('running');
382
- playgroundActions.pinExecution(null);
383
- playgroundActions.setError(null);
375
+ fd.playground.updateSessionStatus('running');
376
+ fd.playground.pinExecution(null);
377
+ fd.playground.setError(null);
384
378
 
385
379
  try {
386
- const message = await playgroundService.sendMessage(sessionId, content, {});
387
- playgroundActions.addMessage(message);
380
+ const message = await playgroundService.sendMessage(fd.api.config, sessionId, content, {});
381
+ fd.playground.addMessage(message);
388
382
  // Only start polling if not already active — avoids resetting the cursor
389
383
  // mid-session and re-fetching messages that are already in the store.
390
384
  // Seed from the newest loaded message so polling tails live updates
@@ -394,25 +388,25 @@
394
388
  }
395
389
  } catch (err) {
396
390
  const errorMessage = err instanceof Error ? err.message : 'Failed to send message';
397
- playgroundActions.setError(errorMessage);
398
- playgroundActions.updateSessionStatus('idle');
391
+ fd.playground.setError(errorMessage);
392
+ fd.playground.updateSessionStatus('idle');
399
393
  logger.error('Failed to send message:', err);
400
394
  }
401
395
  }
402
396
 
403
397
  async function handleStopExecution(): Promise<void> {
404
- const sessionId = getCurrentSession()?.id;
398
+ const sessionId = fd.playground.currentSession?.id;
405
399
  if (!sessionId) return;
406
400
 
407
401
  try {
408
- await playgroundService.stopExecution(sessionId);
402
+ await playgroundService.stopExecution(fd.api.config, sessionId);
409
403
  playgroundService.stopPolling();
410
- playgroundActions.updateSessionStatus('idle');
404
+ fd.playground.updateSessionStatus('idle');
411
405
  } catch (err) {
412
406
  const errorMessage = err instanceof Error ? err.message : 'Failed to stop execution';
413
- playgroundActions.setError(errorMessage);
407
+ fd.playground.setError(errorMessage);
414
408
  playgroundService.stopPolling();
415
- playgroundActions.updateSessionStatus('idle');
409
+ fd.playground.updateSessionStatus('idle');
416
410
  logger.error('Failed to stop execution:', err);
417
411
  }
418
412
  }
@@ -423,11 +417,12 @@
423
417
  overrideShouldStopPolling?: (status: PlaygroundSessionStatus) => boolean
424
418
  ): void {
425
419
  const pollingInterval = config.pollingInterval ?? 1500;
426
- const initialSequenceNumber = seedSequence ? getLatestSequenceNumber() : null;
420
+ const initialSequenceNumber = seedSequence ? fd.playground.latestSequenceNumber : null;
427
421
 
428
422
  playgroundService.startPolling(
423
+ fd.api.config,
429
424
  sessionId,
430
- (response) => applyServerResponse(response, sessionId),
425
+ (response) => fd.playground.applyServerResponse(response, sessionId),
431
426
  pollingInterval,
432
427
  overrideShouldStopPolling ?? config.shouldStopPolling,
433
428
  initialSequenceNumber
@@ -435,14 +430,14 @@
435
430
  }
436
431
 
437
432
  async function refreshFromServer(): Promise<void> {
438
- const sessionId = getCurrentSession()?.id;
433
+ const sessionId = fd.playground.currentSession?.id;
439
434
  if (!sessionId || isRefreshing) return;
440
435
  isRefreshing = true;
441
436
  try {
442
- const response = await playgroundService.getMessages(sessionId, {
437
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
443
438
  since: playgroundService.getLastSequenceNumber() ?? undefined
444
439
  });
445
- applyServerResponse(response, sessionId);
440
+ fd.playground.applyServerResponse(response, sessionId);
446
441
  if (response.sessionStatus === 'running' && !playgroundService.isPolling()) {
447
442
  startPolling(sessionId, true);
448
443
  }
@@ -454,16 +449,16 @@
454
449
  }
455
450
 
456
451
  async function handleInterruptResolved(): Promise<void> {
457
- const sessionId = getCurrentSession()?.id;
452
+ const sessionId = fd.playground.currentSession?.id;
458
453
  if (!sessionId) return;
459
454
 
460
455
  try {
461
456
  // Catch up immediately rather than waiting for the next poll interval.
462
457
  // Use the service's sequence cursor so we only fetch new messages.
463
- const response = await playgroundService.getMessages(sessionId, {
458
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
464
459
  since: playgroundService.getLastSequenceNumber() ?? undefined
465
460
  });
466
- applyServerResponse(response, sessionId);
461
+ fd.playground.applyServerResponse(response, sessionId);
467
462
  } catch (err) {
468
463
  logger.error('[Playground] Failed to refresh after interrupt:', err);
469
464
  }
@@ -483,14 +478,14 @@
483
478
  class:playground--modal={mode === 'modal'}
484
479
  >
485
480
  <main class="playground__main">
486
- {#if getError()}
481
+ {#if fd.playground.error}
487
482
  <div class="playground__error">
488
483
  <Icon icon="mdi:alert-circle" />
489
- <span>{getError()}</span>
484
+ <span>{fd.playground.error}</span>
490
485
  <button
491
486
  type="button"
492
487
  class="playground__error-dismiss"
493
- onclick={() => playgroundActions.setError(null)}
488
+ onclick={() => fd.playground.setError(null)}
494
489
  >
495
490
  <Icon icon="mdi:close" />
496
491
  </button>
@@ -498,7 +493,7 @@
498
493
  {/if}
499
494
 
500
495
  <div class="playground__content" bind:this={playgroundContentEl}>
501
- {#if getIsLoading() && !getCurrentSession()}
496
+ {#if fd.playground.isLoading && !fd.playground.currentSession}
502
497
  <div class="playground__loading">
503
498
  <Icon icon="mdi:loading" class="playground__loading-icon" />
504
499
  <span>Loading...</span>
@@ -509,7 +504,7 @@
509
504
  autoScroll={config.autoScroll ?? true}
510
505
  enableMarkdown={config.enableMarkdown ?? true}
511
506
  onInterruptResolved={handleInterruptResolved}
512
- onCreateSession={getSessions().length === 0 ? handleCreateSession : undefined}
507
+ onCreateSession={fd.playground.sessions.length === 0 ? handleCreateSession : undefined}
513
508
  onLoadOlder={loadOlderMessages}
514
509
  />
515
510
 
@@ -550,6 +545,9 @@
550
545
  showChatInput={config.showChatInput ?? true}
551
546
  showRunButton={config.showRunButton ?? true}
552
547
  predefinedMessage={config.predefinedMessage}
548
+ showSessionHeader={config.showSessionHeader ?? true}
549
+ showNewSessionButton={config.showNewSessionButton ?? true}
550
+ showSessionList={config.showSessionList ?? true}
553
551
  />
554
552
  {/if}
555
553
  </div>
@@ -1,6 +1,7 @@
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
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
4
5
  interface Props {
5
6
  workflowId: string;
6
7
  workflow?: Workflow;
@@ -12,6 +13,7 @@ interface Props {
12
13
  onTogglePanel?: () => void;
13
14
  isPipelinePanelOpen?: boolean;
14
15
  onSessionNavigate?: (sessionId: string) => void;
16
+ instance?: FlowDropInstance;
15
17
  }
16
18
  declare const Playground: import("svelte").Component<Props, {}, "">;
17
19
  type Playground = ReturnType<typeof Playground>;
@@ -13,6 +13,7 @@
13
13
  import Navbar from '../Navbar.svelte';
14
14
  import PlaygroundStudio from './PlaygroundStudio.svelte';
15
15
  import type { Workflow } from '../../types/index.js';
16
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
16
17
  import type { EndpointConfig } from '../../config/endpoints.js';
17
18
  import type { PlaygroundConfig } from '../../types/playground.js';
18
19
  import type { SettingsCategory } from '../../types/settings.js';
@@ -37,6 +38,8 @@
37
38
  initialPipelineWidth?: number;
38
39
  onClose?: () => void;
39
40
  onSessionNavigate?: (sessionId: string) => void;
41
+ /** Per-instance state container — forwarded to the inner PlaygroundStudio */
42
+ instance?: FlowDropInstance;
40
43
  }
41
44
 
42
45
  let {
@@ -57,7 +60,8 @@
57
60
  minChatWidth,
58
61
  initialPipelineWidth,
59
62
  onClose,
60
- onSessionNavigate
63
+ onSessionNavigate,
64
+ instance
61
65
  }: Props = $props();
62
66
 
63
67
  const displayTitle = $derived(navbarTitle ?? workflow?.name ?? 'Playground');
@@ -77,6 +81,7 @@
77
81
  {/if}
78
82
  <div class="fd-playground-app__content">
79
83
  <PlaygroundStudio
84
+ {instance}
80
85
  {workflowId}
81
86
  {workflow}
82
87
  {mode}
@@ -1,4 +1,5 @@
1
1
  import type { Workflow } from '../../types/index.js';
2
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
2
3
  import type { EndpointConfig } from '../../config/endpoints.js';
3
4
  import type { PlaygroundConfig } from '../../types/playground.js';
4
5
  import type { SettingsCategory } from '../../types/settings.js';
@@ -22,6 +23,8 @@ interface Props {
22
23
  initialPipelineWidth?: number;
23
24
  onClose?: () => void;
24
25
  onSessionNavigate?: (sessionId: string) => void;
26
+ /** Per-instance state container — forwarded to the inner PlaygroundStudio */
27
+ instance?: FlowDropInstance;
25
28
  }
26
29
  declare const PlaygroundApp: import("svelte").Component<Props, {}, "">;
27
30
  type PlaygroundApp = ReturnType<typeof PlaygroundApp>;
@@ -10,6 +10,7 @@
10
10
  import Icon from '@iconify/svelte';
11
11
  import Playground from './Playground.svelte';
12
12
  import type { Workflow } from '../../types/index.js';
13
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
13
14
  import type { EndpointConfig } from '../../config/endpoints.js';
14
15
  import type { PlaygroundConfig } from '../../types/playground.js';
15
16
  import { m } from '../../messages/index.js';
@@ -30,6 +31,8 @@
30
31
  endpointConfig?: EndpointConfig;
31
32
  /** Playground configuration options */
32
33
  config?: PlaygroundConfig;
34
+ /** Per-instance state container — forwarded to the inner Playground */
35
+ instance?: FlowDropInstance;
33
36
  /** Callback when modal is closed */
34
37
  onClose: () => void;
35
38
  }
@@ -41,9 +44,15 @@
41
44
  initialSessionId,
42
45
  endpointConfig,
43
46
  config = {},
47
+ instance,
44
48
  onClose
45
49
  }: Props = $props();
46
50
 
51
+ // Unique per component instance so two FlowDrop editors on one page
52
+ // don't render colliding DOM ids (a11y).
53
+ const uid = $props.id();
54
+ const titleId = `${uid}-playground-modal-title`;
55
+
47
56
  /**
48
57
  * Close modal on Escape key
49
58
  */
@@ -71,15 +80,15 @@
71
80
  onkeydown={handleKeydown}
72
81
  role="dialog"
73
82
  aria-modal="true"
74
- aria-labelledby="playground-modal-title"
83
+ aria-labelledby={titleId}
75
84
  tabindex="-1"
76
85
  >
77
86
  <!-- Modal Container -->
78
- <!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events — role="presentation" container stops backdrop click propagation -->
87
+ <!-- role="presentation" container stops backdrop click propagation -->
79
88
  <div class="playground-modal" onclick={(e) => e.stopPropagation()} role="presentation">
80
89
  <!-- Modal Header -->
81
90
  <div class="playground-modal__header">
82
- <div class="playground-modal__title" id="playground-modal-title">
91
+ <div class="playground-modal__title" id={titleId}>
83
92
  <Icon icon="mdi:play-circle-outline" />
84
93
  <span>Playground</span>
85
94
  </div>
@@ -96,6 +105,7 @@
96
105
  <!-- Modal Content -->
97
106
  <div class="playground-modal__content">
98
107
  <Playground
108
+ {instance}
99
109
  {workflowId}
100
110
  {workflow}
101
111
  mode="modal"
@@ -1,4 +1,5 @@
1
1
  import type { Workflow } from '../../types/index.js';
2
+ import type { FlowDropInstance } from '../../stores/instanceContainer.svelte.js';
2
3
  import type { EndpointConfig } from '../../config/endpoints.js';
3
4
  import type { PlaygroundConfig } from '../../types/playground.js';
4
5
  /**
@@ -17,6 +18,8 @@ interface Props {
17
18
  endpointConfig?: EndpointConfig;
18
19
  /** Playground configuration options */
19
20
  config?: PlaygroundConfig;
21
+ /** Per-instance state container — forwarded to the inner Playground */
22
+ instance?: FlowDropInstance;
20
23
  /** Callback when modal is closed */
21
24
  onClose: () => void;
22
25
  }