@flowdrop/flowdrop 1.14.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 (218) 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 +110 -66
  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 +45 -40
  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 +29 -13
  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 +292 -0
  61. package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
  62. package/dist/components/nodes/GatewayNode.svelte +19 -10
  63. package/dist/components/nodes/IdeaNode.svelte +7 -0
  64. package/dist/components/nodes/SimpleNode.svelte +11 -6
  65. package/dist/components/nodes/SquareNode.svelte +15 -8
  66. package/dist/components/nodes/TerminalNode.svelte +9 -4
  67. package/dist/components/nodes/ToolNode.svelte +7 -1
  68. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  69. package/dist/components/playground/ChatInput.svelte +11 -14
  70. package/dist/components/playground/ChatPanel.svelte +6 -49
  71. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  72. package/dist/components/playground/ControlPanel.svelte +134 -123
  73. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  74. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  75. package/dist/components/playground/InputCollector.svelte +11 -9
  76. package/dist/components/playground/MessageStream.svelte +17 -23
  77. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  78. package/dist/components/playground/PipelinePanel.svelte +11 -5
  79. package/dist/components/playground/PipelineTableView.svelte +186 -44
  80. package/dist/components/playground/Playground.svelte +95 -92
  81. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  82. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  83. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  84. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  85. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  86. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  87. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  88. package/dist/components/playground/SessionManager.svelte +9 -12
  89. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  90. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  91. package/dist/config/endpoints.d.ts +0 -7
  92. package/dist/config/endpoints.js +2 -10
  93. package/dist/core/index.d.ts +4 -4
  94. package/dist/core/index.js +6 -6
  95. package/dist/display/index.d.ts +0 -2
  96. package/dist/display/index.js +0 -6
  97. package/dist/editor/index.d.ts +19 -20
  98. package/dist/editor/index.js +25 -35
  99. package/dist/form/code.d.ts +25 -15
  100. package/dist/form/code.js +44 -41
  101. package/dist/form/fieldRegistry.d.ts +17 -13
  102. package/dist/form/fieldRegistry.js +32 -12
  103. package/dist/form/full.d.ts +17 -13
  104. package/dist/form/full.js +22 -27
  105. package/dist/form/index.d.ts +3 -3
  106. package/dist/form/index.js +3 -3
  107. package/dist/form/markdown.d.ts +13 -8
  108. package/dist/form/markdown.js +22 -23
  109. package/dist/helpers/proximityConnect.d.ts +7 -3
  110. package/dist/helpers/proximityConnect.js +19 -6
  111. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  112. package/dist/helpers/workflowEditorHelper.js +27 -25
  113. package/dist/index.d.ts +28 -24
  114. package/dist/index.js +27 -50
  115. package/dist/messages/defaults.d.ts +2 -5
  116. package/dist/messages/defaults.js +3 -6
  117. package/dist/messages/index.d.ts +0 -1
  118. package/dist/messages/index.js +0 -1
  119. package/dist/mocks/app-forms.d.ts +6 -2
  120. package/dist/mocks/app-forms.js +11 -4
  121. package/dist/openapi/v1/openapi.yaml +227 -164
  122. package/dist/playground/index.d.ts +2 -3
  123. package/dist/playground/index.js +2 -30
  124. package/dist/playground/mount.d.ts +15 -0
  125. package/dist/playground/mount.js +46 -20
  126. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  127. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  128. package/dist/registry/builtinFormats.d.ts +9 -18
  129. package/dist/registry/builtinFormats.js +9 -39
  130. package/dist/registry/builtinNodes.d.ts +1 -26
  131. package/dist/registry/builtinNodes.js +14 -50
  132. package/dist/registry/index.d.ts +3 -4
  133. package/dist/registry/index.js +4 -6
  134. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  135. package/dist/registry/nodeComponentRegistry.js +235 -17
  136. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  137. package/dist/registry/workflowFormatRegistry.js +24 -8
  138. package/dist/{schema → schemas}/index.d.ts +2 -2
  139. package/dist/{schema → schemas}/index.js +2 -2
  140. package/dist/schemas/v1/workflow.schema.json +53 -6
  141. package/dist/services/agentSpecExecutionService.js +0 -1
  142. package/dist/services/apiVariableService.d.ts +2 -1
  143. package/dist/services/apiVariableService.js +5 -22
  144. package/dist/services/autoSaveService.d.ts +7 -0
  145. package/dist/services/autoSaveService.js +6 -4
  146. package/dist/services/chatService.d.ts +8 -4
  147. package/dist/services/chatService.js +15 -15
  148. package/dist/services/draftStorage.d.ts +129 -13
  149. package/dist/services/draftStorage.js +185 -37
  150. package/dist/services/dynamicSchemaService.d.ts +2 -1
  151. package/dist/services/dynamicSchemaService.js +5 -22
  152. package/dist/services/globalSave.d.ts +13 -12
  153. package/dist/services/globalSave.js +29 -51
  154. package/dist/services/historyService.d.ts +9 -3
  155. package/dist/services/historyService.js +9 -3
  156. package/dist/services/interruptService.d.ts +14 -9
  157. package/dist/services/interruptService.js +27 -27
  158. package/dist/services/nodeExecutionService.d.ts +18 -3
  159. package/dist/services/nodeExecutionService.js +71 -45
  160. package/dist/services/playgroundService.d.ts +14 -9
  161. package/dist/services/playgroundService.js +31 -30
  162. package/dist/services/variableService.d.ts +2 -1
  163. package/dist/services/variableService.js +2 -2
  164. package/dist/services/workflowStorage.js +6 -6
  165. package/dist/stores/apiContext.d.ts +45 -0
  166. package/dist/stores/apiContext.js +65 -0
  167. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  168. package/dist/stores/categoriesStore.svelte.js +70 -64
  169. package/dist/stores/getInstance.svelte.d.ts +39 -0
  170. package/dist/stores/getInstance.svelte.js +65 -0
  171. package/dist/stores/historyStore.svelte.d.ts +77 -93
  172. package/dist/stores/historyStore.svelte.js +134 -160
  173. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  174. package/dist/stores/instanceContainer.svelte.js +114 -0
  175. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  176. package/dist/stores/interruptStore.svelte.js +253 -226
  177. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  178. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  179. package/dist/stores/playgroundStore.svelte.d.ts +169 -216
  180. package/dist/stores/playgroundStore.svelte.js +515 -572
  181. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  182. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  183. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  184. package/dist/stores/settingsStore.svelte.js +47 -12
  185. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  186. package/dist/stores/workflowStore.svelte.js +449 -501
  187. package/dist/stories/EdgeDecorator.svelte +5 -2
  188. package/dist/stories/NodeDecorator.svelte +5 -3
  189. package/dist/svelte-app.d.ts +60 -10
  190. package/dist/svelte-app.js +157 -53
  191. package/dist/types/events.d.ts +6 -3
  192. package/dist/types/index.d.ts +71 -6
  193. package/dist/types/navbar.d.ts +7 -0
  194. package/dist/types/playground.d.ts +18 -3
  195. package/dist/types/settings.d.ts +13 -0
  196. package/dist/types/settings.js +1 -0
  197. package/dist/utils/colors.d.ts +47 -21
  198. package/dist/utils/colors.js +69 -68
  199. package/dist/utils/connections.d.ts +9 -15
  200. package/dist/utils/connections.js +13 -32
  201. package/dist/utils/duration.d.ts +13 -0
  202. package/dist/utils/duration.js +45 -0
  203. package/dist/utils/formMerge.d.ts +36 -0
  204. package/dist/utils/formMerge.js +70 -0
  205. package/dist/utils/icons.d.ts +5 -2
  206. package/dist/utils/icons.js +6 -5
  207. package/dist/utils/nodeSwap.d.ts +6 -2
  208. package/dist/utils/nodeSwap.js +62 -126
  209. package/dist/utils/nodeTypes.d.ts +17 -8
  210. package/dist/utils/nodeTypes.js +27 -19
  211. package/dist/utils/performanceUtils.js +7 -0
  212. package/package.json +6 -5
  213. package/dist/messages/deprecation.d.ts +0 -20
  214. package/dist/messages/deprecation.js +0 -33
  215. package/dist/registry/plugin.d.ts +0 -215
  216. package/dist/registry/plugin.js +0 -249
  217. package/dist/services/api.d.ts +0 -129
  218. 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))
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);
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,72 +308,77 @@
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);
317
+
318
+ // Stop polling the previous (possibly running) session before switching,
319
+ // mirroring handleSelectSession. Otherwise its next poll keeps the old
320
+ // 'running' status alive and the new session's chat input stays disabled.
321
+ playgroundService.stopPolling();
323
322
 
324
323
  if (onSessionNavigate) {
325
324
  onSessionNavigate(session.id);
326
325
  return;
327
326
  }
328
327
 
329
- playgroundActions.addSession(session);
330
- playgroundActions.setCurrentSession(session);
331
- playgroundActions.clearMessages();
328
+ fd.playground.addSession(session);
329
+ fd.playground.setCurrentSession(session);
330
+ fd.playground.clearMessages();
332
331
  } catch (err) {
333
332
  const errorMessage = err instanceof Error ? err.message : 'Failed to create session';
334
- playgroundActions.setError(errorMessage);
333
+ fd.playground.setError(errorMessage);
335
334
  logger.error('Failed to create session:', err);
336
335
  } finally {
337
- playgroundActions.setLoading(false);
336
+ fd.playground.setLoading(false);
338
337
  }
339
338
  }
340
339
 
341
340
  async function handleSelectSession(sessionId: string): Promise<void> {
342
- playgroundActions.pinExecution(null);
343
- const currentSessionId = getCurrentSession()?.id;
341
+ fd.playground.pinExecution(null);
342
+ const currentSessionId = fd.playground.currentSession?.id;
344
343
  if (currentSessionId === sessionId) return;
345
344
 
346
345
  playgroundService.stopPolling();
347
- playgroundActions.updateSessionStatus('idle');
346
+ fd.playground.updateSessionStatus('idle');
348
347
  await loadSession(sessionId);
349
348
  }
350
349
 
351
350
  async function handleDeleteSession(sessionId: string): Promise<void> {
352
351
  try {
353
- await playgroundService.deleteSession(sessionId);
354
- playgroundActions.removeSession(sessionId);
352
+ await playgroundService.deleteSession(fd.api.config, sessionId);
353
+ fd.playground.removeSession(sessionId);
355
354
 
356
- if (getCurrentSession()?.id === sessionId) {
355
+ if (fd.playground.currentSession?.id === sessionId) {
357
356
  playgroundService.stopPolling();
358
357
  }
359
358
  } catch (err) {
360
359
  const errorMessage = err instanceof Error ? err.message : 'Failed to delete session';
361
- playgroundActions.setError(errorMessage);
360
+ fd.playground.setError(errorMessage);
362
361
  logger.error('Failed to delete session:', err);
363
362
  }
364
363
  }
365
364
 
366
365
  async function handleSendMessage(content: string): Promise<void> {
367
- if (getIsExecuting()) return;
366
+ if (fd.playground.isExecuting) return;
368
367
 
369
- if (!getCurrentSession()) {
368
+ if (!fd.playground.currentSession) {
370
369
  await handleCreateSession();
371
- if (!getCurrentSession()) return;
370
+ if (!fd.playground.currentSession) return;
372
371
  }
373
372
 
374
- const sessionId = getCurrentSession()!.id;
373
+ const sessionId = fd.playground.currentSession!.id;
375
374
 
376
- playgroundActions.updateSessionStatus('running');
377
- playgroundActions.pinExecution(null);
378
- playgroundActions.setError(null);
375
+ fd.playground.updateSessionStatus('running');
376
+ fd.playground.pinExecution(null);
377
+ fd.playground.setError(null);
379
378
 
380
379
  try {
381
- const message = await playgroundService.sendMessage(sessionId, content, {});
382
- playgroundActions.addMessage(message);
380
+ const message = await playgroundService.sendMessage(fd.api.config, sessionId, content, {});
381
+ fd.playground.addMessage(message);
383
382
  // Only start polling if not already active — avoids resetting the cursor
384
383
  // mid-session and re-fetching messages that are already in the store.
385
384
  // Seed from the newest loaded message so polling tails live updates
@@ -389,25 +388,25 @@
389
388
  }
390
389
  } catch (err) {
391
390
  const errorMessage = err instanceof Error ? err.message : 'Failed to send message';
392
- playgroundActions.setError(errorMessage);
393
- playgroundActions.updateSessionStatus('idle');
391
+ fd.playground.setError(errorMessage);
392
+ fd.playground.updateSessionStatus('idle');
394
393
  logger.error('Failed to send message:', err);
395
394
  }
396
395
  }
397
396
 
398
397
  async function handleStopExecution(): Promise<void> {
399
- const sessionId = getCurrentSession()?.id;
398
+ const sessionId = fd.playground.currentSession?.id;
400
399
  if (!sessionId) return;
401
400
 
402
401
  try {
403
- await playgroundService.stopExecution(sessionId);
402
+ await playgroundService.stopExecution(fd.api.config, sessionId);
404
403
  playgroundService.stopPolling();
405
- playgroundActions.updateSessionStatus('idle');
404
+ fd.playground.updateSessionStatus('idle');
406
405
  } catch (err) {
407
406
  const errorMessage = err instanceof Error ? err.message : 'Failed to stop execution';
408
- playgroundActions.setError(errorMessage);
407
+ fd.playground.setError(errorMessage);
409
408
  playgroundService.stopPolling();
410
- playgroundActions.updateSessionStatus('idle');
409
+ fd.playground.updateSessionStatus('idle');
411
410
  logger.error('Failed to stop execution:', err);
412
411
  }
413
412
  }
@@ -418,11 +417,12 @@
418
417
  overrideShouldStopPolling?: (status: PlaygroundSessionStatus) => boolean
419
418
  ): void {
420
419
  const pollingInterval = config.pollingInterval ?? 1500;
421
- const initialSequenceNumber = seedSequence ? getLatestSequenceNumber() : null;
420
+ const initialSequenceNumber = seedSequence ? fd.playground.latestSequenceNumber : null;
422
421
 
423
422
  playgroundService.startPolling(
423
+ fd.api.config,
424
424
  sessionId,
425
- (response) => applyServerResponse(response),
425
+ (response) => fd.playground.applyServerResponse(response, sessionId),
426
426
  pollingInterval,
427
427
  overrideShouldStopPolling ?? config.shouldStopPolling,
428
428
  initialSequenceNumber
@@ -430,14 +430,14 @@
430
430
  }
431
431
 
432
432
  async function refreshFromServer(): Promise<void> {
433
- const sessionId = getCurrentSession()?.id;
433
+ const sessionId = fd.playground.currentSession?.id;
434
434
  if (!sessionId || isRefreshing) return;
435
435
  isRefreshing = true;
436
436
  try {
437
- const response = await playgroundService.getMessages(sessionId, {
437
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
438
438
  since: playgroundService.getLastSequenceNumber() ?? undefined
439
439
  });
440
- applyServerResponse(response);
440
+ fd.playground.applyServerResponse(response, sessionId);
441
441
  if (response.sessionStatus === 'running' && !playgroundService.isPolling()) {
442
442
  startPolling(sessionId, true);
443
443
  }
@@ -449,16 +449,16 @@
449
449
  }
450
450
 
451
451
  async function handleInterruptResolved(): Promise<void> {
452
- const sessionId = getCurrentSession()?.id;
452
+ const sessionId = fd.playground.currentSession?.id;
453
453
  if (!sessionId) return;
454
454
 
455
455
  try {
456
456
  // Catch up immediately rather than waiting for the next poll interval.
457
457
  // Use the service's sequence cursor so we only fetch new messages.
458
- const response = await playgroundService.getMessages(sessionId, {
458
+ const response = await playgroundService.getMessages(fd.api.config, sessionId, {
459
459
  since: playgroundService.getLastSequenceNumber() ?? undefined
460
460
  });
461
- applyServerResponse(response);
461
+ fd.playground.applyServerResponse(response, sessionId);
462
462
  } catch (err) {
463
463
  logger.error('[Playground] Failed to refresh after interrupt:', err);
464
464
  }
@@ -478,14 +478,14 @@
478
478
  class:playground--modal={mode === 'modal'}
479
479
  >
480
480
  <main class="playground__main">
481
- {#if getError()}
481
+ {#if fd.playground.error}
482
482
  <div class="playground__error">
483
483
  <Icon icon="mdi:alert-circle" />
484
- <span>{getError()}</span>
484
+ <span>{fd.playground.error}</span>
485
485
  <button
486
486
  type="button"
487
487
  class="playground__error-dismiss"
488
- onclick={() => playgroundActions.setError(null)}
488
+ onclick={() => fd.playground.setError(null)}
489
489
  >
490
490
  <Icon icon="mdi:close" />
491
491
  </button>
@@ -493,7 +493,7 @@
493
493
  {/if}
494
494
 
495
495
  <div class="playground__content" bind:this={playgroundContentEl}>
496
- {#if getIsLoading() && !getCurrentSession()}
496
+ {#if fd.playground.isLoading && !fd.playground.currentSession}
497
497
  <div class="playground__loading">
498
498
  <Icon icon="mdi:loading" class="playground__loading-icon" />
499
499
  <span>Loading...</span>
@@ -504,7 +504,7 @@
504
504
  autoScroll={config.autoScroll ?? true}
505
505
  enableMarkdown={config.enableMarkdown ?? true}
506
506
  onInterruptResolved={handleInterruptResolved}
507
- onCreateSession={getSessions().length === 0 ? handleCreateSession : undefined}
507
+ onCreateSession={fd.playground.sessions.length === 0 ? handleCreateSession : undefined}
508
508
  onLoadOlder={loadOlderMessages}
509
509
  />
510
510
 
@@ -545,6 +545,9 @@
545
545
  showChatInput={config.showChatInput ?? true}
546
546
  showRunButton={config.showRunButton ?? true}
547
547
  predefinedMessage={config.predefinedMessage}
548
+ showSessionHeader={config.showSessionHeader ?? true}
549
+ showNewSessionButton={config.showNewSessionButton ?? true}
550
+ showSessionList={config.showSessionList ?? true}
548
551
  />
549
552
  {/if}
550
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
  }