@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.2

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 (235) hide show
  1. package/CHANGELOG.md +508 -0
  2. package/MIGRATION-2.0.md +629 -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/api/enhanced-client.js +6 -11
  8. package/dist/chat/batchFeedback.d.ts +39 -0
  9. package/dist/chat/batchFeedback.js +51 -0
  10. package/dist/commands/executor.js +15 -1
  11. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  12. package/dist/commands/storeIntegration.svelte.js +26 -21
  13. package/dist/commands/types.d.ts +2 -0
  14. package/dist/components/App.svelte +163 -192
  15. package/dist/components/App.svelte.d.ts +47 -8
  16. package/dist/components/ConfigForm.svelte +77 -49
  17. package/dist/components/ConfigModal.svelte +7 -2
  18. package/dist/components/ConnectionLine.svelte +4 -2
  19. package/dist/components/Navbar.svelte +61 -1
  20. package/dist/components/NodeSidebar.svelte +27 -45
  21. package/dist/components/NodeStatusOverlay.svelte +94 -6
  22. package/dist/components/NodeSwapPicker.svelte +10 -8
  23. package/dist/components/PipelineStatus.svelte +22 -68
  24. package/dist/components/PipelineStatus.svelte.d.ts +3 -0
  25. package/dist/components/PortCoordinateTracker.svelte +5 -6
  26. package/dist/components/SchemaForm.stories.svelte +1 -3
  27. package/dist/components/SchemaForm.svelte +22 -27
  28. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  29. package/dist/components/SettingsModal.svelte +8 -3
  30. package/dist/components/SettingsPanel.svelte +20 -4
  31. package/dist/components/SwapMappingEditor.svelte +67 -49
  32. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  33. package/dist/components/UniversalNode.svelte +9 -7
  34. package/dist/components/WorkflowEditor.svelte +121 -111
  35. package/dist/components/WorkflowEditor.svelte.d.ts +21 -10
  36. package/dist/components/chat/AIChatPanel.svelte +98 -89
  37. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  38. package/dist/components/chat/CommandPreview.svelte +2 -1
  39. package/dist/components/console/CommandConsole.svelte +7 -5
  40. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  41. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  42. package/dist/components/console/ConsoleInput.svelte +15 -6
  43. package/dist/components/console/ConsoleOutput.svelte +2 -1
  44. package/dist/components/form/FormArray.svelte +5 -9
  45. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  46. package/dist/components/form/FormAutocomplete.svelte +16 -15
  47. package/dist/components/form/FormField.svelte +4 -2
  48. package/dist/components/form/FormFieldLight.svelte +34 -3
  49. package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
  50. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  51. package/dist/components/form/FormRangeField.svelte +1 -0
  52. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  53. package/dist/components/form/FormToggle.svelte +5 -12
  54. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  55. package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
  56. package/dist/components/form/templateAutocomplete.js +1 -5
  57. package/dist/components/form/types.d.ts +1 -14
  58. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  59. package/dist/components/interrupt/InterruptBubble.svelte +25 -17
  60. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  61. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  62. package/dist/components/layouts/MainLayout.svelte +20 -13
  63. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  64. package/dist/components/nodes/AtomNode.svelte +17 -5
  65. package/dist/components/nodes/GatewayNode.svelte +19 -10
  66. package/dist/components/nodes/IdeaNode.svelte +7 -0
  67. package/dist/components/nodes/SimpleNode.svelte +11 -6
  68. package/dist/components/nodes/SquareNode.svelte +15 -8
  69. package/dist/components/nodes/TerminalNode.svelte +9 -4
  70. package/dist/components/nodes/ToolNode.svelte +7 -1
  71. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  72. package/dist/components/playground/ChatInput.svelte +11 -14
  73. package/dist/components/playground/ChatPanel.svelte +6 -49
  74. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  75. package/dist/components/playground/ControlPanel.svelte +134 -123
  76. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  77. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  78. package/dist/components/playground/InputCollector.svelte +11 -9
  79. package/dist/components/playground/MessageStream.svelte +17 -23
  80. package/dist/components/playground/PipelineKanbanView.svelte +69 -8
  81. package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
  82. package/dist/components/playground/PipelinePanel.svelte +31 -8
  83. package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
  84. package/dist/components/playground/PipelineTableView.svelte +188 -44
  85. package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
  86. package/dist/components/playground/Playground.svelte +154 -105
  87. package/dist/components/playground/Playground.svelte.d.ts +5 -0
  88. package/dist/components/playground/PlaygroundApp.svelte +11 -1
  89. package/dist/components/playground/PlaygroundApp.svelte.d.ts +6 -0
  90. package/dist/components/playground/PlaygroundModal.svelte +18 -3
  91. package/dist/components/playground/PlaygroundModal.svelte.d.ts +6 -0
  92. package/dist/components/playground/PlaygroundStudio.svelte +40 -32
  93. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +6 -0
  94. package/dist/components/playground/SessionManager.svelte +9 -12
  95. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +30 -1
  96. package/dist/components/playground/pipelineViewUtils.svelte.js +40 -3
  97. package/dist/config/endpoints.d.ts +23 -7
  98. package/dist/config/endpoints.js +30 -10
  99. package/dist/core/index.d.ts +5 -6
  100. package/dist/core/index.js +8 -12
  101. package/dist/display/index.d.ts +6 -3
  102. package/dist/display/index.js +7 -5
  103. package/dist/editor/index.d.ts +20 -21
  104. package/dist/editor/index.js +26 -36
  105. package/dist/form/code.d.ts +25 -15
  106. package/dist/form/code.js +44 -41
  107. package/dist/form/fieldRegistry.d.ts +17 -13
  108. package/dist/form/fieldRegistry.js +32 -12
  109. package/dist/form/full.d.ts +19 -14
  110. package/dist/form/full.js +26 -28
  111. package/dist/form/index.d.ts +3 -4
  112. package/dist/form/index.js +6 -5
  113. package/dist/form/markdown.d.ts +13 -8
  114. package/dist/form/markdown.js +22 -23
  115. package/dist/helpers/proximityConnect.d.ts +3 -2
  116. package/dist/helpers/proximityConnect.js +2 -5
  117. package/dist/helpers/workflowEditorHelper.d.ts +14 -5
  118. package/dist/helpers/workflowEditorHelper.js +28 -25
  119. package/dist/index.d.ts +28 -24
  120. package/dist/index.js +27 -50
  121. package/dist/messages/defaults.d.ts +2 -5
  122. package/dist/messages/defaults.js +3 -6
  123. package/dist/messages/index.d.ts +0 -1
  124. package/dist/messages/index.js +0 -1
  125. package/dist/mocks/app-forms.d.ts +6 -2
  126. package/dist/mocks/app-forms.js +11 -4
  127. package/dist/openapi/v1/openapi.yaml +3 -3
  128. package/dist/playground/index.d.ts +4 -5
  129. package/dist/playground/index.js +4 -32
  130. package/dist/playground/mount.d.ts +25 -0
  131. package/dist/playground/mount.js +50 -20
  132. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  133. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  134. package/dist/registry/builtinFormats.d.ts +9 -18
  135. package/dist/registry/builtinFormats.js +9 -39
  136. package/dist/registry/builtinNodeTypes.d.ts +53 -0
  137. package/dist/registry/builtinNodeTypes.js +67 -0
  138. package/dist/registry/builtinNodes.d.ts +2 -64
  139. package/dist/registry/builtinNodes.js +7 -103
  140. package/dist/registry/index.d.ts +3 -4
  141. package/dist/registry/index.js +4 -6
  142. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  143. package/dist/registry/nodeComponentRegistry.js +235 -17
  144. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  145. package/dist/registry/workflowFormatRegistry.js +24 -8
  146. package/dist/{schema → schemas}/index.d.ts +2 -2
  147. package/dist/{schema → schemas}/index.js +2 -2
  148. package/dist/schemas/v1/workflow.schema.json +3 -3
  149. package/dist/services/agentSpecExecutionService.d.ts +0 -2
  150. package/dist/services/agentSpecExecutionService.js +0 -3
  151. package/dist/services/apiVariableService.d.ts +2 -1
  152. package/dist/services/apiVariableService.js +16 -47
  153. package/dist/services/autoSaveService.d.ts +7 -0
  154. package/dist/services/autoSaveService.js +6 -4
  155. package/dist/services/categoriesApi.js +3 -6
  156. package/dist/services/chatService.d.ts +9 -4
  157. package/dist/services/chatService.js +23 -28
  158. package/dist/services/draftStorage.d.ts +129 -13
  159. package/dist/services/draftStorage.js +185 -37
  160. package/dist/services/dynamicSchemaService.d.ts +2 -1
  161. package/dist/services/dynamicSchemaService.js +5 -22
  162. package/dist/services/globalSave.d.ts +13 -12
  163. package/dist/services/globalSave.js +29 -51
  164. package/dist/services/historyService.d.ts +9 -3
  165. package/dist/services/historyService.js +9 -3
  166. package/dist/services/interruptService.d.ts +15 -9
  167. package/dist/services/interruptService.js +35 -37
  168. package/dist/services/nodeExecutionService.d.ts +18 -3
  169. package/dist/services/nodeExecutionService.js +71 -45
  170. package/dist/services/playgroundService.d.ts +16 -10
  171. package/dist/services/playgroundService.js +42 -43
  172. package/dist/services/portConfigApi.js +3 -6
  173. package/dist/services/settingsService.d.ts +9 -4
  174. package/dist/services/settingsService.js +23 -12
  175. package/dist/services/variableService.d.ts +2 -1
  176. package/dist/services/variableService.js +2 -2
  177. package/dist/services/workflowStorage.js +6 -6
  178. package/dist/stores/apiContext.d.ts +56 -0
  179. package/dist/stores/apiContext.js +80 -0
  180. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  181. package/dist/stores/categoriesStore.svelte.js +69 -64
  182. package/dist/stores/getInstance.svelte.d.ts +39 -0
  183. package/dist/stores/getInstance.svelte.js +65 -0
  184. package/dist/stores/historyStore.svelte.d.ts +77 -93
  185. package/dist/stores/historyStore.svelte.js +134 -160
  186. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  187. package/dist/stores/instanceContainer.svelte.js +114 -0
  188. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  189. package/dist/stores/interruptStore.svelte.js +253 -226
  190. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  191. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  192. package/dist/stores/playgroundStore.svelte.d.ts +169 -222
  193. package/dist/stores/playgroundStore.svelte.js +513 -580
  194. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  195. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  196. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  197. package/dist/stores/settingsStore.svelte.js +47 -12
  198. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  199. package/dist/stores/workflowStore.svelte.js +449 -501
  200. package/dist/stories/EdgeDecorator.svelte +5 -2
  201. package/dist/stories/NodeDecorator.svelte +5 -3
  202. package/dist/svelte-app.d.ts +60 -10
  203. package/dist/svelte-app.js +159 -54
  204. package/dist/types/auth.d.ts +9 -51
  205. package/dist/types/auth.js +4 -54
  206. package/dist/types/events.d.ts +6 -3
  207. package/dist/types/index.d.ts +37 -5
  208. package/dist/types/index.js +0 -1
  209. package/dist/types/navbar.d.ts +7 -0
  210. package/dist/types/playground.d.ts +18 -3
  211. package/dist/types/settings.d.ts +13 -0
  212. package/dist/types/settings.js +1 -0
  213. package/dist/utils/colors.d.ts +47 -21
  214. package/dist/utils/colors.js +69 -68
  215. package/dist/utils/connections.d.ts +9 -15
  216. package/dist/utils/connections.js +13 -32
  217. package/dist/utils/duration.d.ts +13 -0
  218. package/dist/utils/duration.js +45 -0
  219. package/dist/utils/edgeStyling.js +9 -5
  220. package/dist/utils/fetchWithAuth.d.ts +36 -15
  221. package/dist/utils/fetchWithAuth.js +53 -23
  222. package/dist/utils/icons.d.ts +5 -2
  223. package/dist/utils/icons.js +6 -5
  224. package/dist/utils/nodeSwap.d.ts +6 -2
  225. package/dist/utils/nodeSwap.js +62 -126
  226. package/dist/utils/nodeTypes.d.ts +17 -8
  227. package/dist/utils/nodeTypes.js +27 -20
  228. package/dist/utils/performanceUtils.js +7 -0
  229. package/package.json +7 -5
  230. package/dist/messages/deprecation.d.ts +0 -20
  231. package/dist/messages/deprecation.js +0 -33
  232. package/dist/registry/plugin.d.ts +0 -215
  233. package/dist/registry/plugin.js +0 -249
  234. package/dist/services/api.d.ts +0 -129
  235. package/dist/services/api.js +0 -217
@@ -21,23 +21,19 @@
21
21
  getEditorSettings,
22
22
  getBehaviorSettings
23
23
  } from '../stores/settingsStore.svelte.js';
24
- import type {
25
- WorkflowNode as WorkflowNodeType,
26
- NodeMetadata,
27
- Workflow,
28
- WorkflowEdge
29
- } from '../types/index.js';
24
+ import type { WorkflowNode as WorkflowNodeType, Workflow, WorkflowEdge } from '../types/index.js';
30
25
  import CanvasBanner from './CanvasBanner.svelte';
31
26
  import CanvasController from './CanvasController.svelte';
32
27
  import FlowDropZone from './FlowDropZone.svelte';
33
28
  import EdgeRefresher from './EdgeRefresher.svelte';
34
29
  import { tick, untrack } from 'svelte';
35
30
  import type { EndpointConfig } from '../config/endpoints.js';
31
+ import type { AuthProvider } from '../types/auth.js';
36
32
  import ConnectionLine from './ConnectionLine.svelte';
37
33
  import FlowDropEdge from './FlowDropEdge.svelte';
38
34
  import { m } from '../messages/index.js';
39
- import { getWorkflowStore, workflowActions } from '../stores/workflowStore.svelte.js';
40
- import { historyActions, setOnRestoreCallback } from '../stores/historyStore.svelte.js';
35
+ import { provideInstance } from '../stores/getInstance.svelte.js';
36
+ import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
41
37
  import UniversalNode from './UniversalNode.svelte';
42
38
  import {
43
39
  EdgeStylingHelper,
@@ -57,34 +53,52 @@
57
53
  type ProximityEdgeCandidate
58
54
  } from '../helpers/proximityConnect.js';
59
55
  import PortCoordinateTracker from './PortCoordinateTracker.svelte';
60
- import { getPortCoordinateSnapshot } from '../stores/portCoordinateStore.svelte.js';
61
56
  import { logger } from '../utils/logger.js';
62
57
  import { validateWorkflowData } from '../utils/validation.js';
63
58
  import { createEditorStateMachine } from '../stores/editorStateMachine.svelte.js';
64
59
  import Icon from '@iconify/svelte';
60
+ import { DEV } from 'esm-env';
65
61
 
66
62
  interface Props {
67
- nodes?: NodeMetadata[];
68
63
  endpointConfig?: EndpointConfig;
69
- height?: string | number;
70
- width?: string | number;
71
- isConfigSidebarOpen?: boolean;
72
- selectedNodeForConfig?: WorkflowNodeType | null;
64
+ /** Auth provider applied to this instance's API requests. */
65
+ authProvider?: AuthProvider;
73
66
  openConfigSidebar?: (node: WorkflowNodeType) => void;
74
- closeConfigSidebar?: () => void;
75
- // New configuration options for pipeline status mode
76
- lockWorkflow?: boolean;
77
- readOnly?: boolean;
78
- nodeStatuses?: Record<string, 'pending' | 'running' | 'completed' | 'error'>;
67
+ /**
68
+ * Editor interaction mode. `'edit'` allows node drag/connect/select and
69
+ * proximity-connect; `'readonly'` and `'locked'` disable all canvas
70
+ * editing (they behave identically today — see App's `mode` prop for the
71
+ * full matrix). Replaces the former `readOnly` + `lockWorkflow` booleans.
72
+ * @default 'edit'
73
+ */
74
+ mode?: 'edit' | 'readonly' | 'locked';
79
75
  // Pipeline ID for fetching node execution info from jobs
80
76
  pipelineId?: string;
77
+ /**
78
+ * Increments to force a re-fetch of node execution info from the server.
79
+ * Used by parents (e.g. PipelineStatus) to push poll ticks / chat-message
80
+ * arrivals down to the canvas without owning a separate status channel.
81
+ */
82
+ refreshTrigger?: number;
81
83
  // Console toggle
82
84
  consoleOpen?: boolean;
83
85
  onToggleConsole?: () => void;
86
+ /** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
87
+ instance?: FlowDropInstance;
84
88
  }
85
89
 
86
90
  let props: Props = $props();
87
91
 
92
+ // Resolve (and provide to children) the per-instance state container.
93
+ // Must run during component init — provideInstance reads/sets Svelte context.
94
+ // The instance never changes for a mounted component, so capturing it once is correct.
95
+ // svelte-ignore state_referenced_locally
96
+ const fd = provideInstance(props.instance);
97
+
98
+ // `mode` is the public API; the canvas only needs to know whether editing is
99
+ // enabled. 'readonly' and 'locked' both disable interaction identically.
100
+ const canvasEditable = $derived((props.mode ?? 'edit') === 'edit');
101
+
88
102
  // ---------------------------------------------------------------------------
89
103
  // Editor State Machine
90
104
  // Centralizes reactive guards — replaces scattered boolean flags
@@ -93,7 +107,7 @@
93
107
  const machine = createEditorStateMachine();
94
108
 
95
109
  // Dev-mode transition logging
96
- if (import.meta.env?.DEV) {
110
+ if (DEV) {
97
111
  machine.onTransition((from, event, to) => {
98
112
  logger.debug(`[EditorFSM] ${from} --${event}--> ${to}`);
99
113
  });
@@ -122,7 +136,7 @@
122
136
  * Key for SvelteFlow component — changes when workflow ID changes.
123
137
  * Forces SvelteFlow to remount with fresh state, allowing fitView to work correctly.
124
138
  */
125
- let svelteFlowKey = $derived(getWorkflowStore()?.id ?? 'default');
139
+ let svelteFlowKey = $derived(fd.workflow.current?.id ?? 'default');
126
140
 
127
141
  /**
128
142
  * Derive snap grid configuration from editor settings
@@ -164,14 +178,14 @@
164
178
  // Helper: sync current flowNodes/flowEdges back to the global store
165
179
  // ---------------------------------------------------------------------------
166
180
  function syncFlowToStore(): void {
167
- const storeValue = untrack(() => getWorkflowStore());
181
+ const storeValue = untrack(() => fd.workflow.current);
168
182
  if (!storeValue) return;
169
183
  const updatedWorkflow = WorkflowOperationsHelper.updateWorkflow(
170
184
  storeValue,
171
185
  flowNodes,
172
186
  flowEdges
173
187
  );
174
- workflowActions.updateWorkflow(updatedWorkflow);
188
+ fd.workflow.updateWorkflow(updatedWorkflow);
175
189
  }
176
190
 
177
191
  // ---------------------------------------------------------------------------
@@ -182,7 +196,7 @@
182
196
  let previousSyncedWorkflowId: string | null = null;
183
197
 
184
198
  $effect(() => {
185
- const storeValue = getWorkflowStore();
199
+ const storeValue = fd.workflow.current;
186
200
 
187
201
  // Suppressed during operations — handlers write to flowNodes directly
188
202
  if (untrack(() => machine.permissions.suppressEffect)) return;
@@ -228,7 +242,7 @@
228
242
  let previousExecPipelineId: string | undefined = undefined;
229
243
 
230
244
  $effect(() => {
231
- const storeValue = getWorkflowStore();
245
+ const storeValue = fd.workflow.current;
232
246
  const pipelineId = props.pipelineId;
233
247
 
234
248
  if (!storeValue || !pipelineId) return;
@@ -241,15 +255,12 @@
241
255
  previousExecWorkflowId = storeValue.id;
242
256
  previousExecPipelineId = pipelineId;
243
257
 
244
- // Cancel any pending timeout / in-flight request
258
+ // Cancel any pending idle/timeout schedule. In-flight fetches are
259
+ // cancelled by loadNodeExecutionInfo() itself when it's re-entered.
245
260
  if (loadExecutionInfoTimeout) {
246
261
  clearTimeout(loadExecutionInfoTimeout);
247
262
  loadExecutionInfoTimeout = null;
248
263
  }
249
- if (executionInfoAbortController) {
250
- executionInfoAbortController.abort();
251
- executionInfoAbortController = null;
252
- }
253
264
 
254
265
  // Schedule loading with requestIdleCallback (falls back to setTimeout)
255
266
  if (typeof requestIdleCallback !== 'undefined') {
@@ -266,44 +277,26 @@
266
277
  }
267
278
  });
268
279
 
269
- // Apply nodeStatuses from the parent (PipelineStatus embedded mode) to flowNodes
270
- // whenever they change. loadNodeExecutionInfo() only fires on pipelineId change,
271
- // so this is the update path for subsequent refreshes (e.g. after HITL resolution).
280
+ // Re-fetch node execution info when the parent bumps refreshTrigger
281
+ // (poll ticks, chat-message arrivals, manual refresh). loadNodeExecutionInfo()
282
+ // is the single writer of executionInfo on flowNodes no parallel channel.
283
+ // svelte-ignore state_referenced_locally
284
+ let _prevExecRefreshTrigger = props.refreshTrigger ?? 0;
272
285
  $effect(() => {
273
- const statuses = props.nodeStatuses;
274
- if (!statuses || Object.keys(statuses).length === 0) return;
275
-
276
- flowNodes = untrack(() => flowNodes).map((node) => {
277
- const rawStatus = statuses[node.id];
278
- if (!rawStatus) return node;
279
-
280
- const existing = node.data.executionInfo ?? {
281
- status: 'idle' as const,
282
- executionCount: 0,
283
- isExecuting: false
284
- };
285
- return {
286
- ...node,
287
- data: {
288
- ...node.data,
289
- executionInfo: {
290
- ...existing,
291
- status: rawStatus === 'error' ? ('failed' as const) : rawStatus,
292
- isExecuting: rawStatus === 'running'
293
- }
294
- }
295
- };
296
- });
286
+ const t = props.refreshTrigger ?? 0;
287
+ if (t === 0 || t === _prevExecRefreshTrigger) return;
288
+ _prevExecRefreshTrigger = t;
289
+ loadNodeExecutionInfo();
297
290
  });
298
291
 
299
292
  // ---------------------------------------------------------------------------
300
293
  // History restore callback
301
294
  // ---------------------------------------------------------------------------
302
295
  $effect(() => {
303
- setOnRestoreCallback((restoredWorkflow: Workflow) => {
296
+ fd.historyBindings.setOnRestoreCallback((restoredWorkflow: Workflow) => {
304
297
  machine.send('START_RESTORE');
305
298
  // Update the store (effect is suppressed during 'restoring')
306
- workflowActions.restoreFromHistory(restoredWorkflow);
299
+ fd.workflow.restoreFromHistory(restoredWorkflow);
307
300
  // Derive flowNodes/flowEdges directly for immediate visual update
308
301
  const derived = buildFlowNodesFromStore(restoredWorkflow);
309
302
  flowNodes = derived.nodes;
@@ -314,26 +307,38 @@
314
307
  });
315
308
 
316
309
  return () => {
317
- setOnRestoreCallback(null);
310
+ // Reinstate the container's default wiring rather than nulling it: the
311
+ // default instance survives unmount, and a bare null would make legacy
312
+ // historyActions.undo() silently restore nothing afterwards.
313
+ fd.historyBindings.setOnRestoreCallback((restored: Workflow) =>
314
+ fd.workflow.restoreFromHistory(restored)
315
+ );
318
316
  };
319
317
  });
320
318
 
321
319
  /**
322
- * Load node execution information for all nodes in the workflow
320
+ * Load node execution information for all nodes in the workflow.
321
+ * Cancels any in-flight fetch so concurrent callers (pipelineId change
322
+ * and refreshTrigger bumps) can't race on flowNodes.
323
323
  */
324
324
  async function loadNodeExecutionInfo(): Promise<void> {
325
- const workflow = untrack(() => getWorkflowStore());
325
+ const workflow = untrack(() => fd.workflow.current);
326
326
  if (!workflow?.nodes || !props.pipelineId) return;
327
327
 
328
- try {
329
- executionInfoAbortController = new AbortController();
328
+ if (executionInfoAbortController) {
329
+ executionInfoAbortController.abort();
330
+ }
331
+ const controller = new AbortController();
332
+ executionInfoAbortController = controller;
330
333
 
334
+ try {
331
335
  const executionInfo = await NodeOperationsHelper.loadNodeExecutionInfo(
336
+ fd.api,
332
337
  workflow,
333
338
  props.pipelineId
334
339
  );
335
340
 
336
- if (executionInfoAbortController?.signal.aborted) return;
341
+ if (controller.signal.aborted) return;
337
342
 
338
343
  const defaultExecutionInfo: NodeExecutionInfo = {
339
344
  status: 'idle' as const,
@@ -350,7 +355,9 @@
350
355
  }
351
356
  }));
352
357
 
353
- executionInfoAbortController = null;
358
+ if (executionInfoAbortController === controller) {
359
+ executionInfoAbortController = null;
360
+ }
354
361
  } catch (error) {
355
362
  if (error instanceof Error && error.name !== 'AbortError') {
356
363
  logger.error('Failed to load node execution info:', error);
@@ -401,12 +408,7 @@
401
408
  nodes: WorkflowNodeType[];
402
409
  event: MouseEvent | TouchEvent;
403
410
  }): void {
404
- if (
405
- !getEditorSettings().proximityConnect ||
406
- !targetNode ||
407
- props.readOnly ||
408
- props.lockWorkflow
409
- ) {
411
+ if (!getEditorSettings().proximityConnect || !targetNode || !canvasEditable) {
410
412
  if (currentProximityCandidates.length > 0) {
411
413
  flowEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
412
414
  currentProximityCandidates = [];
@@ -422,16 +424,18 @@
422
424
  const baseEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
423
425
 
424
426
  // Find the best compatible edge using port-to-port distance
425
- const portCoordinates = getPortCoordinateSnapshot();
427
+ const portCoordinates = fd.portCoordinates.coordinates;
426
428
  const candidates =
427
429
  portCoordinates.size > 0
428
430
  ? ProximityConnectHelper.findCompatibleEdgesByPortCoordinates(
431
+ fd.portCompatibility,
429
432
  targetNode.id,
430
433
  portCoordinates,
431
434
  baseEdges,
432
435
  getEditorSettings().proximityConnectDistance
433
436
  )
434
437
  : ProximityConnectHelper.findCompatibleEdges(
438
+ fd.portCompatibility,
435
439
  targetNode,
436
440
  flowNodes,
437
441
  baseEdges,
@@ -478,9 +482,9 @@
478
482
  syncFlowToStore();
479
483
 
480
484
  // Push history AFTER the drag completed
481
- const storeValue = getWorkflowStore();
485
+ const storeValue = fd.workflow.current;
482
486
  if (storeValue) {
483
- workflowActions.pushHistory('Move node', storeValue);
487
+ fd.workflow.pushHistory('Move node', storeValue);
484
488
  }
485
489
 
486
490
  // Transition to idle — sync effect is now unblocked
@@ -488,14 +492,11 @@
488
492
  }
489
493
 
490
494
  /**
491
- * Handle new connections between nodes
495
+ * Handle new connections between nodes.
496
+ * The connection details aren't needed — SvelteFlow auto-creates the edge
497
+ * via bind:edges; this only drives the state machine and history snapshot.
492
498
  */
493
- async function handleConnect(connection: {
494
- source: string;
495
- target: string;
496
- sourceHandle?: string;
497
- targetHandle?: string;
498
- }): Promise<void> {
499
+ async function handleConnect(): Promise<void> {
499
500
  machine.send('START_CONNECT');
500
501
 
501
502
  // SvelteFlow auto-creates the edge via bind:edges — wait for DOM update
@@ -507,9 +508,9 @@
507
508
  // Sync to store
508
509
  syncFlowToStore();
509
510
 
510
- const storeValue = getWorkflowStore();
511
+ const storeValue = fd.workflow.current;
511
512
  if (storeValue) {
512
- workflowActions.pushHistory('Add connection', storeValue);
513
+ fd.workflow.pushHistory('Add connection', storeValue);
513
514
  }
514
515
 
515
516
  machine.send('CONNECTION_MADE');
@@ -585,9 +586,9 @@
585
586
  } else if (edgeCount > 0) {
586
587
  description = `Delete ${edgeCount} connection${edgeCount > 1 ? 's' : ''}`;
587
588
  }
588
- const storeValue = getWorkflowStore();
589
+ const storeValue = fd.workflow.current;
589
590
  if (storeValue) {
590
- workflowActions.pushHistory(description, storeValue);
591
+ fd.workflow.pushHistory(description, storeValue);
591
592
  }
592
593
 
593
594
  machine.send('DELETE_COMPLETE');
@@ -598,7 +599,7 @@
598
599
  // Configure endpoints when props change
599
600
  $effect(() => {
600
601
  if (props.endpointConfig) {
601
- ConfigurationHelper.configureEndpoints(props.endpointConfig);
602
+ ConfigurationHelper.configureEndpoints(fd.api, props.endpointConfig, props.authProvider);
602
603
  }
603
604
  });
604
605
 
@@ -635,9 +636,9 @@
635
636
 
636
637
  await tick();
637
638
 
638
- const storeValue = getWorkflowStore();
639
+ const storeValue = fd.workflow.current;
639
640
  if (storeValue) {
640
- workflowActions.pushHistory('Add node', storeValue);
641
+ fd.workflow.pushHistory('Add node', storeValue);
641
642
  }
642
643
  } else {
643
644
  logger.warn('Failed to create node from drop data');
@@ -667,7 +668,7 @@
667
668
  logger.warn('Workflow file drop validation failed:', validation.error);
668
669
  return;
669
670
  }
670
- workflowActions.initialize(data as Workflow);
671
+ fd.workflow.initialize(data as Workflow);
671
672
  } catch (error) {
672
673
  const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
673
674
  logger.error('Workflow file drop import failed:', errorObj);
@@ -693,7 +694,7 @@
693
694
  /**
694
695
  * Update a node's data in the local editor state.
695
696
  * Called by App.svelte AFTER it has already updated the global store via
696
- * workflowActions.updateNode(). We only need to update flowNodes for
697
+ * fd.workflow.updateNode(). We only need to update flowNodes for
697
698
  * immediate visual feedback — no store sync needed.
698
699
  *
699
700
  * @param nodeId - The ID of the node to update
@@ -774,15 +775,15 @@
774
775
  * - Ctrl+Z (or Cmd+Z on Mac): Undo
775
776
  * - Ctrl+Shift+Z (or Cmd+Shift+Z): Redo
776
777
  * - Ctrl+Y (or Cmd+Y): Redo (Windows convention)
778
+ *
779
+ * Also suppresses WebKit's legacy default action for Backspace outside
780
+ * editable content — history back-navigation. SvelteFlow's KeyHandler
781
+ * deletes the selected elements on Backspace but never preventDefaults,
782
+ * so in embedded WebKit the page navigates away mid-delete. Scoped to
783
+ * keydowns originating inside the flow canvas so host-page behavior
784
+ * outside the editor is untouched.
777
785
  */
778
786
  function handleKeydown(event: KeyboardEvent): void {
779
- // Check for Ctrl (Windows/Linux) or Cmd (Mac)
780
- const isModifierPressed = event.ctrlKey || event.metaKey;
781
-
782
- if (!isModifierPressed) {
783
- return;
784
- }
785
-
786
787
  // Don't handle shortcuts if user is typing in an input, textarea, or contenteditable
787
788
  const target = event.target as HTMLElement;
788
789
  const isInputElement =
@@ -792,17 +793,31 @@
792
793
  return;
793
794
  }
794
795
 
796
+ // Backspace/Delete on a canvas element: let SvelteFlow handle the
797
+ // deletion, but block the browser default (WebKit navigates back).
798
+ if ((event.key === 'Backspace' || event.key === 'Delete') && target.closest('.svelte-flow')) {
799
+ event.preventDefault();
800
+ return;
801
+ }
802
+
803
+ // Check for Ctrl (Windows/Linux) or Cmd (Mac)
804
+ const isModifierPressed = event.ctrlKey || event.metaKey;
805
+
806
+ if (!isModifierPressed) {
807
+ return;
808
+ }
809
+
795
810
  // Undo: Ctrl+Z (without Shift)
796
811
  if (event.key === 'z' && !event.shiftKey) {
797
812
  event.preventDefault();
798
- historyActions.undo();
813
+ fd.historyBindings.undo();
799
814
  return;
800
815
  }
801
816
 
802
817
  // Redo: Ctrl+Shift+Z or Ctrl+Y
803
818
  if ((event.key === 'z' && event.shiftKey) || event.key === 'y') {
804
819
  event.preventDefault();
805
- historyActions.redo();
820
+ fd.historyBindings.redo();
806
821
  return;
807
822
  }
808
823
  }
@@ -832,18 +847,13 @@
832
847
  <FlowDropZone ondrop={handleNodeDrop} onfiledrop={handleWorkflowFileDrop}>
833
848
  {#key svelteFlowKey}
834
849
  <SvelteFlow
850
+ id={fd.id}
835
851
  bind:nodes={flowNodes}
836
852
  bind:edges={flowEdges}
837
853
  {nodeTypes}
838
854
  {edgeTypes}
839
855
  {defaultEdgeOptions}
840
- onconnect={(connection) =>
841
- void handleConnect({
842
- source: connection.source,
843
- target: connection.target,
844
- sourceHandle: connection.sourceHandle ?? undefined,
845
- targetHandle: connection.targetHandle ?? undefined
846
- })}
856
+ onconnect={() => void handleConnect()}
847
857
  onbeforedelete={handleBeforeDelete}
848
858
  ondelete={handleNodesDelete}
849
859
  onnodedragstart={handleNodeDragStart}
@@ -859,12 +869,12 @@
859
869
  {initialViewport}
860
870
  colorMode={getResolvedTheme() as ColorMode}
861
871
  fitView={getEditorSettings().fitViewOnLoad}
862
- nodesDraggable={!props.lockWorkflow && !props.readOnly}
863
- nodesConnectable={!props.lockWorkflow && !props.readOnly}
864
- elementsSelectable={!props.lockWorkflow && !props.readOnly}
872
+ nodesDraggable={canvasEditable}
873
+ nodesConnectable={canvasEditable}
874
+ elementsSelectable={canvasEditable}
865
875
  >
866
876
  <Controls />
867
- {#if !props.readOnly && !props.lockWorkflow && props.onToggleConsole}
877
+ {#if canvasEditable && props.onToggleConsole}
868
878
  <button
869
879
  class="flowdrop-console-toggle"
870
880
  class:flowdrop-console-toggle--active={props.consoleOpen}
@@ -1,21 +1,32 @@
1
1
  import '@xyflow/svelte/dist/style.css';
2
- import type { WorkflowNode as WorkflowNodeType, NodeMetadata } from '../types/index.js';
2
+ import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
3
3
  import type { EndpointConfig } from '../config/endpoints.js';
4
+ import type { AuthProvider } from '../types/auth.js';
5
+ import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
4
6
  interface Props {
5
- nodes?: NodeMetadata[];
6
7
  endpointConfig?: EndpointConfig;
7
- height?: string | number;
8
- width?: string | number;
9
- isConfigSidebarOpen?: boolean;
10
- selectedNodeForConfig?: WorkflowNodeType | null;
8
+ /** Auth provider applied to this instance's API requests. */
9
+ authProvider?: AuthProvider;
11
10
  openConfigSidebar?: (node: WorkflowNodeType) => void;
12
- closeConfigSidebar?: () => void;
13
- lockWorkflow?: boolean;
14
- readOnly?: boolean;
15
- nodeStatuses?: Record<string, 'pending' | 'running' | 'completed' | 'error'>;
11
+ /**
12
+ * Editor interaction mode. `'edit'` allows node drag/connect/select and
13
+ * proximity-connect; `'readonly'` and `'locked'` disable all canvas
14
+ * editing (they behave identically today — see App's `mode` prop for the
15
+ * full matrix). Replaces the former `readOnly` + `lockWorkflow` booleans.
16
+ * @default 'edit'
17
+ */
18
+ mode?: 'edit' | 'readonly' | 'locked';
16
19
  pipelineId?: string;
20
+ /**
21
+ * Increments to force a re-fetch of node execution info from the server.
22
+ * Used by parents (e.g. PipelineStatus) to push poll ticks / chat-message
23
+ * arrivals down to the canvas without owning a separate status channel.
24
+ */
25
+ refreshTrigger?: number;
17
26
  consoleOpen?: boolean;
18
27
  onToggleConsole?: () => void;
28
+ /** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
29
+ instance?: FlowDropInstance;
19
30
  }
20
31
  declare const WorkflowEditor: import("svelte").Component<Props, {
21
32
  updateNodeData: (nodeId: string, dataUpdates: Partial<WorkflowNodeType["data"]>) => void;