@d34dman/flowdrop 0.0.61 → 0.0.62

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 (204) hide show
  1. package/README.md +6 -0
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -1
  4. package/dist/api/client.d.ts +4 -0
  5. package/dist/api/client.js +6 -1
  6. package/dist/api/enhanced-client.js +7 -6
  7. package/dist/components/App.svelte +143 -219
  8. package/dist/components/CanvasBanner.stories.svelte +25 -0
  9. package/dist/components/CanvasBanner.stories.svelte.d.ts +27 -0
  10. package/dist/components/CanvasBanner.svelte +2 -2
  11. package/dist/components/ConfigForm.svelte +37 -36
  12. package/dist/components/ConfigPanel.stories.svelte +38 -0
  13. package/dist/components/ConfigPanel.stories.svelte.d.ts +27 -0
  14. package/dist/components/ConfigPanel.svelte +2 -2
  15. package/dist/components/ConnectionLine.svelte +2 -2
  16. package/dist/components/FlowDropZone.svelte +18 -2
  17. package/dist/components/FlowDropZone.svelte.d.ts +2 -0
  18. package/dist/components/LoadingSpinner.stories.svelte +30 -0
  19. package/dist/components/LoadingSpinner.stories.svelte.d.ts +27 -0
  20. package/dist/components/Logo.stories.svelte +22 -0
  21. package/dist/components/Logo.stories.svelte.d.ts +27 -0
  22. package/dist/components/Logo.svelte +33 -13
  23. package/dist/components/Logo.svelte.d.ts +1 -1
  24. package/dist/components/MarkdownDisplay.stories.svelte +21 -0
  25. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +27 -0
  26. package/dist/components/MarkdownDisplay.svelte +4 -3
  27. package/dist/components/Navbar.stories.svelte +41 -0
  28. package/dist/components/Navbar.stories.svelte.d.ts +27 -0
  29. package/dist/components/Navbar.svelte +4 -4
  30. package/dist/components/NodeSidebar.svelte +12 -12
  31. package/dist/components/NodeStatusOverlay.stories.svelte +74 -0
  32. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +27 -0
  33. package/dist/components/PipelineStatus.svelte +11 -4
  34. package/dist/components/PortCoordinateTracker.svelte +1 -1
  35. package/dist/components/SchemaForm.stories.svelte +101 -0
  36. package/dist/components/SchemaForm.stories.svelte.d.ts +27 -0
  37. package/dist/components/SchemaForm.svelte +17 -12
  38. package/dist/components/SettingsModal.svelte +3 -3
  39. package/dist/components/SettingsPanel.svelte +23 -22
  40. package/dist/components/StatusIcon.stories.svelte +60 -0
  41. package/dist/components/StatusIcon.stories.svelte.d.ts +27 -0
  42. package/dist/components/StatusIcon.svelte +7 -0
  43. package/dist/components/StatusLabel.stories.svelte +17 -0
  44. package/dist/components/StatusLabel.stories.svelte.d.ts +27 -0
  45. package/dist/components/ThemeToggle.stories.svelte +25 -0
  46. package/dist/components/ThemeToggle.stories.svelte.d.ts +27 -0
  47. package/dist/components/ThemeToggle.svelte +8 -8
  48. package/dist/components/UniversalNode.svelte +1 -1
  49. package/dist/components/WorkflowEditor.svelte +298 -294
  50. package/dist/components/form/FormAutocomplete.svelte +20 -19
  51. package/dist/components/form/FormCheckboxGroup.stories.svelte +28 -0
  52. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +27 -0
  53. package/dist/components/form/FormField.svelte +3 -3
  54. package/dist/components/form/FormFieldLight.svelte +2 -2
  55. package/dist/components/form/FormFieldWrapper.stories.svelte +31 -0
  56. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +27 -0
  57. package/dist/components/form/FormFieldset.svelte +7 -7
  58. package/dist/components/form/FormNumberField.stories.svelte +33 -0
  59. package/dist/components/form/FormNumberField.stories.svelte.d.ts +27 -0
  60. package/dist/components/form/FormRangeField.stories.svelte +31 -0
  61. package/dist/components/form/FormRangeField.stories.svelte.d.ts +27 -0
  62. package/dist/components/form/FormSelect.stories.svelte +50 -0
  63. package/dist/components/form/FormSelect.stories.svelte.d.ts +27 -0
  64. package/dist/components/form/FormTemplateEditor.svelte +2 -1
  65. package/dist/components/form/FormTextField.stories.svelte +30 -0
  66. package/dist/components/form/FormTextField.stories.svelte.d.ts +27 -0
  67. package/dist/components/form/FormTextarea.stories.svelte +31 -0
  68. package/dist/components/form/FormTextarea.stories.svelte.d.ts +27 -0
  69. package/dist/components/form/FormToggle.stories.svelte +30 -0
  70. package/dist/components/form/FormToggle.stories.svelte.d.ts +27 -0
  71. package/dist/components/form/FormUISchemaRenderer.svelte +1 -1
  72. package/dist/components/form/types.d.ts +15 -47
  73. package/dist/components/interrupt/ChoicePrompt.stories.svelte +43 -0
  74. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +27 -0
  75. package/dist/components/interrupt/ChoicePrompt.svelte +24 -24
  76. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +49 -0
  77. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +27 -0
  78. package/dist/components/interrupt/ConfirmationPrompt.svelte +19 -19
  79. package/dist/components/interrupt/FormPrompt.svelte +15 -15
  80. package/dist/components/interrupt/InterruptBubble.svelte +202 -236
  81. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +1 -1
  82. package/dist/components/interrupt/ReviewPrompt.stories.svelte +46 -0
  83. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +27 -0
  84. package/dist/components/interrupt/ReviewPrompt.svelte +842 -0
  85. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +23 -0
  86. package/dist/components/interrupt/TextInputPrompt.stories.svelte +34 -0
  87. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +27 -0
  88. package/dist/components/interrupt/TextInputPrompt.svelte +21 -21
  89. package/dist/components/nodes/GatewayNode.stories.svelte +76 -0
  90. package/dist/components/nodes/GatewayNode.stories.svelte.d.ts +26 -0
  91. package/dist/components/nodes/GatewayNode.svelte +19 -17
  92. package/dist/components/nodes/IdeaNode.stories.svelte +48 -0
  93. package/dist/components/nodes/IdeaNode.stories.svelte.d.ts +26 -0
  94. package/dist/components/nodes/IdeaNode.svelte +10 -26
  95. package/dist/components/nodes/NotesNode.stories.svelte +69 -0
  96. package/dist/components/nodes/NotesNode.stories.svelte.d.ts +26 -0
  97. package/dist/components/nodes/NotesNode.svelte +8 -8
  98. package/dist/components/nodes/SimpleNode.stories.svelte +101 -0
  99. package/dist/components/nodes/SimpleNode.stories.svelte.d.ts +26 -0
  100. package/dist/components/nodes/SimpleNode.svelte +16 -24
  101. package/dist/components/nodes/SquareNode.stories.svelte +56 -0
  102. package/dist/components/nodes/SquareNode.stories.svelte.d.ts +26 -0
  103. package/dist/components/nodes/SquareNode.svelte +13 -21
  104. package/dist/components/nodes/TerminalNode.stories.svelte +25 -0
  105. package/dist/components/nodes/TerminalNode.stories.svelte.d.ts +26 -0
  106. package/dist/components/nodes/TerminalNode.svelte +6 -6
  107. package/dist/components/nodes/ToolNode.stories.svelte +71 -0
  108. package/dist/components/nodes/ToolNode.stories.svelte.d.ts +26 -0
  109. package/dist/components/nodes/ToolNode.svelte +7 -15
  110. package/dist/components/nodes/WorkflowNode.stories.svelte +50 -0
  111. package/dist/components/nodes/WorkflowNode.stories.svelte.d.ts +26 -0
  112. package/dist/components/nodes/WorkflowNode.svelte +13 -13
  113. package/dist/components/playground/ChatPanel.svelte +48 -48
  114. package/dist/components/playground/ExecutionLogs.svelte +23 -23
  115. package/dist/components/playground/InputCollector.svelte +24 -24
  116. package/dist/components/playground/MessageBubble.stories.svelte +49 -0
  117. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +27 -0
  118. package/dist/components/playground/MessageBubble.svelte +49 -46
  119. package/dist/components/playground/Playground.svelte +194 -129
  120. package/dist/components/playground/PlaygroundModal.svelte +5 -5
  121. package/dist/components/playground/SessionManager.svelte +26 -26
  122. package/dist/config/constants.d.ts +22 -0
  123. package/dist/config/constants.js +22 -0
  124. package/dist/config/endpoints.d.ts +19 -0
  125. package/dist/config/runtimeConfig.js +2 -1
  126. package/dist/core/index.d.ts +5 -2
  127. package/dist/core/index.js +9 -1
  128. package/dist/editor/index.d.ts +13 -9
  129. package/dist/editor/index.js +15 -11
  130. package/dist/form/code.d.ts +2 -1
  131. package/dist/form/code.js +1 -3
  132. package/dist/form/markdown.d.ts +2 -1
  133. package/dist/form/markdown.js +1 -3
  134. package/dist/helpers/workflowEditorHelper.js +13 -9
  135. package/dist/mocks/app-forms.js +1 -0
  136. package/dist/mocks/app-navigation.js +3 -1
  137. package/dist/mocks/app-stores.d.ts +4 -4
  138. package/dist/playground/index.d.ts +4 -3
  139. package/dist/playground/index.js +12 -10
  140. package/dist/playground/mount.js +6 -13
  141. package/dist/services/agentSpecExecutionService.js +2 -1
  142. package/dist/services/api.js +10 -18
  143. package/dist/services/apiVariableService.js +2 -1
  144. package/dist/services/autoSaveService.d.ts +3 -3
  145. package/dist/services/autoSaveService.js +21 -17
  146. package/dist/services/categoriesApi.js +13 -5
  147. package/dist/services/draftStorage.js +5 -4
  148. package/dist/services/dynamicSchemaService.js +4 -4
  149. package/dist/services/globalSave.d.ts +60 -11
  150. package/dist/services/globalSave.js +160 -83
  151. package/dist/services/historyService.d.ts +2 -1
  152. package/dist/services/historyService.js +7 -3
  153. package/dist/services/interruptService.js +9 -8
  154. package/dist/services/nodeExecutionService.js +14 -6
  155. package/dist/services/playgroundService.js +2 -1
  156. package/dist/services/portConfigApi.js +11 -7
  157. package/dist/services/toastService.d.ts +1 -1
  158. package/dist/services/toastService.js +6 -5
  159. package/dist/services/variableService.js +3 -2
  160. package/dist/settings/index.d.ts +1 -1
  161. package/dist/settings/index.js +1 -1
  162. package/dist/stores/{categoriesStore.d.ts → categoriesStore.svelte.d.ts} +3 -3
  163. package/dist/stores/{categoriesStore.js → categoriesStore.svelte.js} +15 -18
  164. package/dist/stores/editorStateMachine.svelte.d.ts +42 -0
  165. package/dist/stores/editorStateMachine.svelte.js +132 -0
  166. package/dist/stores/{historyStore.d.ts → historyStore.svelte.d.ts} +18 -15
  167. package/dist/stores/{historyStore.js → historyStore.svelte.js} +40 -21
  168. package/dist/stores/{interruptStore.d.ts → interruptStore.svelte.d.ts} +16 -15
  169. package/dist/stores/{interruptStore.js → interruptStore.svelte.js} +85 -94
  170. package/dist/stores/{playgroundStore.d.ts → playgroundStore.svelte.d.ts} +41 -33
  171. package/dist/stores/{playgroundStore.js → playgroundStore.svelte.js} +164 -84
  172. package/dist/stores/{portCoordinateStore.d.ts → portCoordinateStore.svelte.d.ts} +10 -4
  173. package/dist/stores/{portCoordinateStore.js → portCoordinateStore.svelte.js} +38 -35
  174. package/dist/stores/{settingsStore.d.ts → settingsStore.svelte.d.ts} +45 -28
  175. package/dist/stores/{settingsStore.js → settingsStore.svelte.js} +169 -128
  176. package/dist/stores/{workflowStore.d.ts → workflowStore.svelte.d.ts} +101 -65
  177. package/dist/stores/{workflowStore.js → workflowStore.svelte.js} +285 -239
  178. package/dist/stories/CanvasDecorator.svelte +50 -0
  179. package/dist/stories/CanvasDecorator.svelte.d.ts +8 -0
  180. package/dist/stories/NodeDecorator.svelte +74 -0
  181. package/dist/stories/NodeDecorator.svelte.d.ts +8 -0
  182. package/dist/stories/utils.d.ts +93 -0
  183. package/dist/stories/utils.js +122 -0
  184. package/dist/styles/base.css +114 -61
  185. package/dist/styles/toast.css +2 -2
  186. package/dist/styles/tokens.css +250 -185
  187. package/dist/svelte-app.d.ts +0 -6
  188. package/dist/svelte-app.js +13 -31
  189. package/dist/types/index.d.ts +2 -0
  190. package/dist/types/interrupt.d.ts +89 -5
  191. package/dist/types/interrupt.js +13 -1
  192. package/dist/types/playground.d.ts +5 -0
  193. package/dist/types/settings.js +1 -1
  194. package/dist/utils/colors.js +4 -4
  195. package/dist/utils/connections.js +33 -8
  196. package/dist/utils/icons.js +1 -1
  197. package/dist/utils/logger.d.ts +47 -0
  198. package/dist/utils/logger.js +72 -0
  199. package/dist/utils/nodeWrapper.js +1 -1
  200. package/dist/utils/sanitize.d.ts +19 -0
  201. package/dist/utils/sanitize.js +31 -0
  202. package/dist/utils/validation.d.ts +29 -0
  203. package/dist/utils/validation.js +39 -0
  204. package/package.json +243 -232
@@ -1,29 +1,48 @@
1
1
  /**
2
- * Workflow Store for FlowDrop
2
+ * Workflow Store for FlowDrop (Svelte 5 Runes)
3
3
  *
4
4
  * Provides global state management for workflows with dirty state tracking
5
5
  * and undo/redo history integration.
6
6
  *
7
+ * **Important: Single-instance only.** This store uses module-level singletons.
8
+ * Only one FlowDrop editor instance per page is supported. Mounting multiple
9
+ * FlowDrop editors on the same page will cause them to share workflow state.
10
+ *
7
11
  * @module stores/workflowStore
8
12
  */
9
- import { writable, derived, get } from 'svelte/store';
10
13
  import { DEFAULT_WORKFLOW_FORMAT } from '../types/index.js';
11
14
  import { historyService } from '../services/historyService.js';
15
+ /**
16
+ * Safely build updated workflow metadata, providing defaults for required fields.
17
+ */
18
+ function buildMetadata(existing, updates) {
19
+ return {
20
+ version: existing?.version ?? '1.0',
21
+ createdAt: existing?.createdAt ?? new Date().toISOString(),
22
+ updatedAt: new Date().toISOString(),
23
+ author: existing?.author,
24
+ tags: existing?.tags,
25
+ versionId: existing?.versionId,
26
+ updateNumber: existing?.updateNumber,
27
+ format: existing?.format,
28
+ ...updates
29
+ };
30
+ }
12
31
  // =========================================================================
13
- // Core Workflow Store
32
+ // Core Workflow State (Svelte 5 Runes)
14
33
  // =========================================================================
15
34
  /** Global workflow state */
16
- export const workflowStore = writable(null);
35
+ let workflowState = $state(null);
17
36
  // =========================================================================
18
37
  // Dirty State Tracking
19
38
  // =========================================================================
20
39
  /**
21
- * Store for tracking if there are unsaved changes
40
+ * State for tracking if there are unsaved changes
22
41
  *
23
42
  * This is set to true whenever the workflow changes after initialization.
24
43
  * It can be reset to false by calling markAsSaved().
25
44
  */
26
- export const isDirtyStore = writable(false);
45
+ let isDirtyState = $state(false);
27
46
  /**
28
47
  * Snapshot of the workflow when it was last saved
29
48
  *
@@ -54,6 +73,152 @@ let isRestoringFromHistory = false;
54
73
  * Can be disabled for bulk operations or when history is not needed.
55
74
  */
56
75
  let historyEnabled = true;
76
+ // =========================================================================
77
+ // Getter Functions (Reactive State Access)
78
+ // =========================================================================
79
+ /**
80
+ * Get the current workflow store value reactively
81
+ *
82
+ * @returns The current workflow or null
83
+ */
84
+ export function getWorkflowStore() {
85
+ return workflowState;
86
+ }
87
+ /**
88
+ * Get the current dirty state reactively
89
+ *
90
+ * @returns true if there are unsaved changes
91
+ */
92
+ export function getIsDirty() {
93
+ return isDirtyState;
94
+ }
95
+ /**
96
+ * Get the workflow ID reactively
97
+ *
98
+ * @returns The workflow ID or null
99
+ */
100
+ export function getWorkflowId() {
101
+ return workflowState?.id ?? null;
102
+ }
103
+ /**
104
+ * Get the workflow name reactively
105
+ *
106
+ * @returns The workflow name or 'Untitled Workflow'
107
+ */
108
+ export function getWorkflowName() {
109
+ return workflowState?.name ?? 'Untitled Workflow';
110
+ }
111
+ /**
112
+ * Get the workflow nodes reactively
113
+ *
114
+ * @returns Array of workflow nodes
115
+ */
116
+ export function getWorkflowNodes() {
117
+ return workflowState?.nodes ?? [];
118
+ }
119
+ /**
120
+ * Get the workflow edges reactively
121
+ *
122
+ * @returns Array of workflow edges
123
+ */
124
+ export function getWorkflowEdges() {
125
+ return workflowState?.edges ?? [];
126
+ }
127
+ /**
128
+ * Get the workflow metadata reactively
129
+ *
130
+ * @returns The workflow metadata with defaults
131
+ */
132
+ export function getWorkflowMetadata() {
133
+ return (workflowState?.metadata ?? {
134
+ version: '1.0.0',
135
+ createdAt: new Date().toISOString(),
136
+ updatedAt: new Date().toISOString(),
137
+ versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
138
+ updateNumber: 0
139
+ });
140
+ }
141
+ /**
142
+ * Get the current workflow format reactively
143
+ *
144
+ * @returns The workflow format string
145
+ */
146
+ export function getWorkflowFormat() {
147
+ return workflowState?.metadata?.format ?? DEFAULT_WORKFLOW_FORMAT;
148
+ }
149
+ /**
150
+ * Get workflow change summary reactively (useful for triggering saves)
151
+ *
152
+ * @returns Object with nodes, edges, and name
153
+ */
154
+ export function getWorkflowChanged() {
155
+ return {
156
+ nodes: getWorkflowNodes(),
157
+ edges: getWorkflowEdges(),
158
+ name: getWorkflowName()
159
+ };
160
+ }
161
+ /**
162
+ * Get workflow validation state reactively
163
+ *
164
+ * @returns Validation info object
165
+ */
166
+ export function getWorkflowValidation() {
167
+ const nodes = getWorkflowNodes();
168
+ const edges = getWorkflowEdges();
169
+ return {
170
+ hasNodes: nodes.length > 0,
171
+ hasEdges: edges.length > 0,
172
+ nodeCount: nodes.length,
173
+ edgeCount: edges.length,
174
+ isValid: nodes.length > 0 && edges.length >= 0
175
+ };
176
+ }
177
+ /**
178
+ * Get workflow metadata change summary reactively
179
+ *
180
+ * @returns Metadata change info
181
+ */
182
+ export function getWorkflowMetadataChanged() {
183
+ const metadata = getWorkflowMetadata();
184
+ return {
185
+ createdAt: metadata.createdAt,
186
+ updatedAt: metadata.updatedAt,
187
+ version: metadata.version ?? '1.0.0'
188
+ };
189
+ }
190
+ /**
191
+ * Get connected handles reactively
192
+ *
193
+ * Provides a Set of all handle IDs that are currently connected to edges.
194
+ * Used by node components to implement hideUnconnectedHandles functionality.
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * import { getConnectedHandles } from './workflowStore.svelte.js';
199
+ *
200
+ * // Check if a specific handle is connected
201
+ * const isConnected = getConnectedHandles().has('node-1-input-data');
202
+ * ```
203
+ */
204
+ export function getConnectedHandles() {
205
+ const edges = getWorkflowEdges();
206
+ const handles = new Set();
207
+ edges.forEach((edge) => {
208
+ // Add source handle (output port)
209
+ if (edge.sourceHandle) {
210
+ handles.add(edge.sourceHandle);
211
+ }
212
+ // Add target handle (input port)
213
+ if (edge.targetHandle) {
214
+ handles.add(edge.targetHandle);
215
+ }
216
+ });
217
+ return handles;
218
+ }
219
+ // =========================================================================
220
+ // Callback Setters
221
+ // =========================================================================
57
222
  /**
58
223
  * Set the dirty state change callback
59
224
  *
@@ -70,6 +235,9 @@ export function setOnDirtyStateChange(callback) {
70
235
  export function setOnWorkflowChange(callback) {
71
236
  onWorkflowChangeCallback = callback;
72
237
  }
238
+ // =========================================================================
239
+ // Internal Helpers
240
+ // =========================================================================
73
241
  /**
74
242
  * Create a snapshot of the workflow for comparison
75
243
  *
@@ -107,14 +275,12 @@ function createSnapshot(workflow) {
107
275
  * Compares current workflow with saved snapshot.
108
276
  */
109
277
  function updateDirtyState() {
110
- const currentWorkflow = get(workflowStore);
111
- const currentSnapshot = createSnapshot(currentWorkflow);
112
- const isDirty = currentSnapshot !== savedSnapshot;
113
- const previousDirty = get(isDirtyStore);
114
- if (isDirty !== previousDirty) {
115
- isDirtyStore.set(isDirty);
278
+ const currentSnapshot = createSnapshot(workflowState);
279
+ const newIsDirty = currentSnapshot !== savedSnapshot;
280
+ if (newIsDirty !== isDirtyState) {
281
+ isDirtyState = newIsDirty;
116
282
  if (onDirtyStateChangeCallback) {
117
- onDirtyStateChangeCallback(isDirty);
283
+ onDirtyStateChangeCallback(newIsDirty);
118
284
  }
119
285
  }
120
286
  }
@@ -124,9 +290,8 @@ function updateDirtyState() {
124
290
  * @param changeType - The type of change that occurred
125
291
  */
126
292
  function notifyWorkflowChange(changeType) {
127
- const workflow = get(workflowStore);
128
- if (workflow && onWorkflowChangeCallback) {
129
- onWorkflowChangeCallback(workflow, changeType);
293
+ if (workflowState && onWorkflowChangeCallback) {
294
+ onWorkflowChangeCallback(workflowState, changeType);
130
295
  }
131
296
  updateDirtyState();
132
297
  }
@@ -136,20 +301,19 @@ function notifyWorkflowChange(changeType) {
136
301
  * Clears the dirty state by updating the saved snapshot.
137
302
  */
138
303
  export function markAsSaved() {
139
- const currentWorkflow = get(workflowStore);
140
- savedSnapshot = createSnapshot(currentWorkflow);
141
- isDirtyStore.set(false);
304
+ savedSnapshot = createSnapshot(workflowState);
305
+ isDirtyState = false;
142
306
  if (onDirtyStateChangeCallback) {
143
307
  onDirtyStateChangeCallback(false);
144
308
  }
145
309
  }
146
310
  /**
147
- * Check if there are unsaved changes
311
+ * Check if there are unsaved changes (non-reactive version for plain TS)
148
312
  *
149
313
  * @returns true if there are unsaved changes
150
314
  */
151
315
  export function isDirty() {
152
- return get(isDirtyStore);
316
+ return isDirtyState;
153
317
  }
154
318
  /**
155
319
  * Enable or disable history recording
@@ -189,41 +353,20 @@ function pushToHistory(description, workflow) {
189
353
  if (!historyEnabled || isRestoringFromHistory) {
190
354
  return;
191
355
  }
192
- const workflowToPush = workflow ?? get(workflowStore);
356
+ const workflowToPush = workflow ?? workflowState;
193
357
  if (workflowToPush) {
194
358
  historyService.push(workflowToPush, { description });
195
359
  }
196
360
  }
197
361
  /**
198
- * Get the current workflow
362
+ * Get the current workflow (non-reactive version for plain TS)
199
363
  *
200
364
  * @returns The current workflow or null
201
365
  */
202
366
  export function getWorkflow() {
203
- return get(workflowStore);
367
+ return workflowState;
204
368
  }
205
369
  // =========================================================================
206
- // Derived Stores
207
- // =========================================================================
208
- /** Derived store for workflow ID */
209
- export const workflowId = derived(workflowStore, ($workflow) => $workflow?.id ?? null);
210
- /** Derived store for workflow name */
211
- export const workflowName = derived(workflowStore, ($workflow) => $workflow?.name ?? 'Untitled Workflow');
212
- /** Derived store for workflow nodes */
213
- export const workflowNodes = derived(workflowStore, ($workflow) => $workflow?.nodes ?? []);
214
- /** Derived store for workflow edges */
215
- export const workflowEdges = derived(workflowStore, ($workflow) => $workflow?.edges ?? []);
216
- /** Derived store for workflow metadata */
217
- export const workflowMetadata = derived(workflowStore, ($workflow) => $workflow?.metadata ?? {
218
- version: '1.0.0',
219
- createdAt: new Date().toISOString(),
220
- updatedAt: new Date().toISOString(),
221
- versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
222
- updateNumber: 0
223
- });
224
- /** Derived store for the current workflow format */
225
- export const workflowFormat = derived(workflowStore, ($workflow) => $workflow?.metadata?.format ?? DEFAULT_WORKFLOW_FORMAT);
226
- // =========================================================================
227
370
  // Helper Functions
228
371
  // =========================================================================
229
372
  /**
@@ -281,10 +424,10 @@ export const workflowActions = {
281
424
  * This sets the initial saved snapshot, clears dirty state, and initializes history.
282
425
  */
283
426
  initialize: (workflow) => {
284
- workflowStore.set(workflow);
427
+ workflowState = workflow;
285
428
  // Set the saved snapshot - workflow is "clean" after initialization
286
429
  savedSnapshot = createSnapshot(workflow);
287
- isDirtyStore.set(false);
430
+ isDirtyState = false;
288
431
  if (onDirtyStateChangeCallback) {
289
432
  onDirtyStateChangeCallback(false);
290
433
  }
@@ -298,7 +441,7 @@ export const workflowActions = {
298
441
  * for every small change. History is pushed by specific actions or drag handlers.
299
442
  */
300
443
  updateWorkflow: (workflow) => {
301
- workflowStore.set(workflow);
444
+ workflowState = workflow;
302
445
  notifyWorkflowChange('metadata');
303
446
  },
304
447
  /**
@@ -308,7 +451,7 @@ export const workflowActions = {
308
451
  */
309
452
  restoreFromHistory: (workflow) => {
310
453
  isRestoringFromHistory = true;
311
- workflowStore.set(workflow);
454
+ workflowState = workflow;
312
455
  notifyWorkflowChange('metadata');
313
456
  isRestoringFromHistory = false;
314
457
  },
@@ -316,70 +459,57 @@ export const workflowActions = {
316
459
  * Update nodes
317
460
  */
318
461
  updateNodes: (nodes) => {
319
- workflowStore.update(($workflow) => {
320
- if (!$workflow)
321
- return null;
322
- // Check if nodes have actually changed to prevent infinite loops
323
- if (!hasWorkflowDataChanged($workflow, nodes, $workflow.edges)) {
324
- return $workflow;
325
- }
326
- // Generate unique version identifier
327
- const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
328
- return {
329
- ...$workflow,
330
- nodes,
331
- metadata: {
332
- ...$workflow.metadata,
333
- updatedAt: new Date().toISOString(),
334
- versionId,
335
- updateNumber: ($workflow.metadata?.updateNumber ?? 0) + 1
336
- }
337
- };
338
- });
462
+ if (!workflowState)
463
+ return;
464
+ // Check if nodes have actually changed to prevent infinite loops
465
+ if (!hasWorkflowDataChanged(workflowState, nodes, workflowState.edges)) {
466
+ return;
467
+ }
468
+ // Generate unique version identifier
469
+ const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
470
+ workflowState = {
471
+ ...workflowState,
472
+ nodes,
473
+ metadata: buildMetadata(workflowState.metadata, {
474
+ versionId,
475
+ updateNumber: (workflowState.metadata?.updateNumber ?? 0) + 1
476
+ })
477
+ };
339
478
  notifyWorkflowChange('node_move');
340
479
  },
341
480
  /**
342
481
  * Update edges
343
482
  */
344
483
  updateEdges: (edges) => {
345
- workflowStore.update(($workflow) => {
346
- if (!$workflow)
347
- return null;
348
- // Check if edges have actually changed to prevent infinite loops
349
- if (!hasWorkflowDataChanged($workflow, $workflow.nodes, edges)) {
350
- return $workflow;
351
- }
352
- // Generate unique version identifier
353
- const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
354
- return {
355
- ...$workflow,
356
- edges,
357
- metadata: {
358
- ...$workflow.metadata,
359
- updatedAt: new Date().toISOString(),
360
- versionId,
361
- updateNumber: ($workflow.metadata?.updateNumber ?? 0) + 1
362
- }
363
- };
364
- });
484
+ if (!workflowState)
485
+ return;
486
+ // Check if edges have actually changed to prevent infinite loops
487
+ if (!hasWorkflowDataChanged(workflowState, workflowState.nodes, edges)) {
488
+ return;
489
+ }
490
+ // Generate unique version identifier
491
+ const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
492
+ workflowState = {
493
+ ...workflowState,
494
+ edges,
495
+ metadata: buildMetadata(workflowState.metadata, {
496
+ versionId,
497
+ updateNumber: (workflowState.metadata?.updateNumber ?? 0) + 1
498
+ })
499
+ };
365
500
  notifyWorkflowChange('edge_add');
366
501
  },
367
502
  /**
368
503
  * Update workflow name
369
504
  */
370
505
  updateName: (name) => {
371
- workflowStore.update(($workflow) => {
372
- if (!$workflow)
373
- return null;
374
- return {
375
- ...$workflow,
376
- name,
377
- metadata: {
378
- ...$workflow.metadata,
379
- updatedAt: new Date().toISOString()
380
- }
381
- };
382
- });
506
+ if (!workflowState)
507
+ return;
508
+ workflowState = {
509
+ ...workflowState,
510
+ name,
511
+ metadata: buildMetadata(workflowState.metadata)
512
+ };
383
513
  notifyWorkflowChange('name');
384
514
  },
385
515
  /**
@@ -387,18 +517,13 @@ export const workflowActions = {
387
517
  */
388
518
  addNode: (node) => {
389
519
  pushToHistory('Add node');
390
- workflowStore.update(($workflow) => {
391
- if (!$workflow)
392
- return null;
393
- return {
394
- ...$workflow,
395
- nodes: [...$workflow.nodes, node],
396
- metadata: {
397
- ...$workflow.metadata,
398
- updatedAt: new Date().toISOString()
399
- }
400
- };
401
- });
520
+ if (!workflowState)
521
+ return;
522
+ workflowState = {
523
+ ...workflowState,
524
+ nodes: [...workflowState.nodes, node],
525
+ metadata: buildMetadata(workflowState.metadata)
526
+ };
402
527
  notifyWorkflowChange('node_add');
403
528
  },
404
529
  /**
@@ -409,19 +534,14 @@ export const workflowActions = {
409
534
  */
410
535
  removeNode: (nodeId) => {
411
536
  pushToHistory('Delete node');
412
- workflowStore.update(($workflow) => {
413
- if (!$workflow)
414
- return null;
415
- return {
416
- ...$workflow,
417
- nodes: $workflow.nodes.filter((node) => node.id !== nodeId),
418
- edges: $workflow.edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId),
419
- metadata: {
420
- ...$workflow.metadata,
421
- updatedAt: new Date().toISOString()
422
- }
423
- };
424
- });
537
+ if (!workflowState)
538
+ return;
539
+ workflowState = {
540
+ ...workflowState,
541
+ nodes: workflowState.nodes.filter((node) => node.id !== nodeId),
542
+ edges: workflowState.edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId),
543
+ metadata: buildMetadata(workflowState.metadata)
544
+ };
425
545
  notifyWorkflowChange('node_remove');
426
546
  },
427
547
  /**
@@ -429,18 +549,13 @@ export const workflowActions = {
429
549
  */
430
550
  addEdge: (edge) => {
431
551
  pushToHistory('Add connection');
432
- workflowStore.update(($workflow) => {
433
- if (!$workflow)
434
- return null;
435
- return {
436
- ...$workflow,
437
- edges: [...$workflow.edges, edge],
438
- metadata: {
439
- ...$workflow.metadata,
440
- updatedAt: new Date().toISOString()
441
- }
442
- };
443
- });
552
+ if (!workflowState)
553
+ return;
554
+ workflowState = {
555
+ ...workflowState,
556
+ edges: [...workflowState.edges, edge],
557
+ metadata: buildMetadata(workflowState.metadata)
558
+ };
444
559
  notifyWorkflowChange('edge_add');
445
560
  },
446
561
  /**
@@ -448,18 +563,13 @@ export const workflowActions = {
448
563
  */
449
564
  removeEdge: (edgeId) => {
450
565
  pushToHistory('Delete connection');
451
- workflowStore.update(($workflow) => {
452
- if (!$workflow)
453
- return null;
454
- return {
455
- ...$workflow,
456
- edges: $workflow.edges.filter((edge) => edge.id !== edgeId),
457
- metadata: {
458
- ...$workflow.metadata,
459
- updatedAt: new Date().toISOString()
460
- }
461
- };
462
- });
566
+ if (!workflowState)
567
+ return;
568
+ workflowState = {
569
+ ...workflowState,
570
+ edges: workflowState.edges.filter((edge) => edge.id !== edgeId),
571
+ metadata: buildMetadata(workflowState.metadata)
572
+ };
463
573
  notifyWorkflowChange('edge_remove');
464
574
  },
465
575
  /**
@@ -469,18 +579,13 @@ export const workflowActions = {
469
579
  */
470
580
  updateNode: (nodeId, updates) => {
471
581
  pushToHistory('Update node config');
472
- workflowStore.update(($workflow) => {
473
- if (!$workflow)
474
- return null;
475
- return {
476
- ...$workflow,
477
- nodes: $workflow.nodes.map((node) => (node.id === nodeId ? { ...node, ...updates } : node)),
478
- metadata: {
479
- ...$workflow.metadata,
480
- updatedAt: new Date().toISOString()
481
- }
482
- };
483
- });
582
+ if (!workflowState)
583
+ return;
584
+ workflowState = {
585
+ ...workflowState,
586
+ nodes: workflowState.nodes.map((node) => node.id === nodeId ? { ...node, ...updates } : node),
587
+ metadata: buildMetadata(workflowState.metadata)
588
+ };
484
589
  notifyWorkflowChange('node_config');
485
590
  },
486
591
  /**
@@ -489,9 +594,9 @@ export const workflowActions = {
489
594
  * Resets the workflow and clears history.
490
595
  */
491
596
  clear: () => {
492
- workflowStore.set(null);
597
+ workflowState = null;
493
598
  savedSnapshot = null;
494
- isDirtyStore.set(false);
599
+ isDirtyState = false;
495
600
  historyService.clear();
496
601
  if (onDirtyStateChangeCallback) {
497
602
  onDirtyStateChangeCallback(false);
@@ -501,18 +606,12 @@ export const workflowActions = {
501
606
  * Update workflow metadata
502
607
  */
503
608
  updateMetadata: (metadata) => {
504
- workflowStore.update(($workflow) => {
505
- if (!$workflow)
506
- return null;
507
- return {
508
- ...$workflow,
509
- metadata: {
510
- ...$workflow.metadata,
511
- ...metadata,
512
- updatedAt: new Date().toISOString()
513
- }
514
- };
515
- });
609
+ if (!workflowState)
610
+ return;
611
+ workflowState = {
612
+ ...workflowState,
613
+ metadata: buildMetadata(workflowState.metadata, metadata)
614
+ };
516
615
  notifyWorkflowChange('metadata');
517
616
  },
518
617
  /**
@@ -523,22 +622,16 @@ export const workflowActions = {
523
622
  */
524
623
  batchUpdate: (updates) => {
525
624
  pushToHistory('Batch update');
526
- workflowStore.update(($workflow) => {
527
- if (!$workflow)
528
- return null;
529
- return {
530
- ...$workflow,
531
- ...(updates.nodes && { nodes: updates.nodes }),
532
- ...(updates.edges && { edges: updates.edges }),
533
- ...(updates.name && { name: updates.name }),
534
- ...(updates.description !== undefined && { description: updates.description }),
535
- metadata: {
536
- ...$workflow.metadata,
537
- ...(updates.metadata && { ...updates.metadata }),
538
- updatedAt: new Date().toISOString()
539
- }
540
- };
541
- });
625
+ if (!workflowState)
626
+ return;
627
+ workflowState = {
628
+ ...workflowState,
629
+ ...(updates.nodes && { nodes: updates.nodes }),
630
+ ...(updates.edges && { edges: updates.edges }),
631
+ ...(updates.name && { name: updates.name }),
632
+ ...(updates.description !== undefined && { description: updates.description }),
633
+ metadata: buildMetadata(workflowState.metadata, updates.metadata ?? undefined)
634
+ };
542
635
  notifyWorkflowChange('metadata');
543
636
  },
544
637
  /**
@@ -554,50 +647,3 @@ export const workflowActions = {
554
647
  pushToHistory(description, workflow);
555
648
  }
556
649
  };
557
- // =========================================================================
558
- // Additional Derived Stores
559
- // =========================================================================
560
- /** Derived store for workflow changes (useful for triggering saves) */
561
- export const workflowChanged = derived([workflowNodes, workflowEdges, workflowName], ([nodes, edges, name]) => ({ nodes, edges, name }));
562
- /** Derived store for workflow validation */
563
- export const workflowValidation = derived([workflowNodes, workflowEdges], ([nodes, edges]) => ({
564
- hasNodes: nodes.length > 0,
565
- hasEdges: edges.length > 0,
566
- nodeCount: nodes.length,
567
- edgeCount: edges.length,
568
- isValid: nodes.length > 0 && edges.length >= 0
569
- }));
570
- /** Derived store for workflow metadata changes */
571
- export const workflowMetadataChanged = derived(workflowMetadata, (metadata) => ({
572
- createdAt: metadata.createdAt,
573
- updatedAt: metadata.updatedAt,
574
- version: metadata.version ?? '1.0.0'
575
- }));
576
- /**
577
- * Derived store for connected handles
578
- *
579
- * Provides a Set of all handle IDs that are currently connected to edges.
580
- * Used by node components to implement hideUnconnectedHandles functionality.
581
- *
582
- * @example
583
- * ```typescript
584
- * import { connectedHandles } from './workflowStore.js';
585
- *
586
- * // Check if a specific handle is connected
587
- * const isConnected = $connectedHandles.has('node-1-input-data');
588
- * ```
589
- */
590
- export const connectedHandles = derived(workflowEdges, (edges) => {
591
- const handles = new Set();
592
- edges.forEach((edge) => {
593
- // Add source handle (output port)
594
- if (edge.sourceHandle) {
595
- handles.add(edge.sourceHandle);
596
- }
597
- // Add target handle (input port)
598
- if (edge.targetHandle) {
599
- handles.add(edge.targetHandle);
600
- }
601
- });
602
- return handles;
603
- });