@d34dman/flowdrop 0.0.60 → 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 (207) 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 +203 -172
  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 +5 -4
  139. package/dist/playground/index.js +15 -11
  140. package/dist/playground/mount.d.ts +20 -1
  141. package/dist/playground/mount.js +24 -6
  142. package/dist/services/agentSpecExecutionService.js +2 -1
  143. package/dist/services/api.js +10 -18
  144. package/dist/services/apiVariableService.js +2 -1
  145. package/dist/services/autoSaveService.d.ts +3 -3
  146. package/dist/services/autoSaveService.js +21 -17
  147. package/dist/services/categoriesApi.js +13 -5
  148. package/dist/services/draftStorage.js +5 -4
  149. package/dist/services/dynamicSchemaService.js +4 -4
  150. package/dist/services/globalSave.d.ts +60 -11
  151. package/dist/services/globalSave.js +160 -83
  152. package/dist/services/historyService.d.ts +2 -1
  153. package/dist/services/historyService.js +7 -3
  154. package/dist/services/interruptService.js +9 -8
  155. package/dist/services/nodeExecutionService.js +14 -6
  156. package/dist/services/playgroundService.d.ts +3 -2
  157. package/dist/services/playgroundService.js +8 -7
  158. package/dist/services/portConfigApi.js +11 -7
  159. package/dist/services/toastService.d.ts +1 -1
  160. package/dist/services/toastService.js +6 -5
  161. package/dist/services/variableService.js +3 -2
  162. package/dist/settings/index.d.ts +1 -1
  163. package/dist/settings/index.js +1 -1
  164. package/dist/stores/{categoriesStore.d.ts → categoriesStore.svelte.d.ts} +3 -3
  165. package/dist/stores/{categoriesStore.js → categoriesStore.svelte.js} +15 -18
  166. package/dist/stores/editorStateMachine.svelte.d.ts +42 -0
  167. package/dist/stores/editorStateMachine.svelte.js +132 -0
  168. package/dist/stores/{historyStore.d.ts → historyStore.svelte.d.ts} +18 -15
  169. package/dist/stores/{historyStore.js → historyStore.svelte.js} +40 -21
  170. package/dist/stores/{interruptStore.d.ts → interruptStore.svelte.d.ts} +16 -15
  171. package/dist/stores/{interruptStore.js → interruptStore.svelte.js} +85 -94
  172. package/dist/stores/{playgroundStore.d.ts → playgroundStore.svelte.d.ts} +52 -34
  173. package/dist/stores/{playgroundStore.js → playgroundStore.svelte.js} +193 -100
  174. package/dist/stores/{portCoordinateStore.d.ts → portCoordinateStore.svelte.d.ts} +10 -4
  175. package/dist/stores/{portCoordinateStore.js → portCoordinateStore.svelte.js} +38 -35
  176. package/dist/stores/{settingsStore.d.ts → settingsStore.svelte.d.ts} +45 -28
  177. package/dist/stores/{settingsStore.js → settingsStore.svelte.js} +169 -128
  178. package/dist/stores/{workflowStore.d.ts → workflowStore.svelte.d.ts} +101 -65
  179. package/dist/stores/{workflowStore.js → workflowStore.svelte.js} +285 -239
  180. package/dist/stories/CanvasDecorator.svelte +50 -0
  181. package/dist/stories/CanvasDecorator.svelte.d.ts +8 -0
  182. package/dist/stories/NodeDecorator.svelte +74 -0
  183. package/dist/stories/NodeDecorator.svelte.d.ts +8 -0
  184. package/dist/stories/utils.d.ts +93 -0
  185. package/dist/stories/utils.js +122 -0
  186. package/dist/styles/base.css +114 -61
  187. package/dist/styles/toast.css +2 -2
  188. package/dist/styles/tokens.css +250 -185
  189. package/dist/svelte-app.d.ts +0 -6
  190. package/dist/svelte-app.js +13 -31
  191. package/dist/types/index.d.ts +2 -0
  192. package/dist/types/interrupt.d.ts +89 -5
  193. package/dist/types/interrupt.js +13 -1
  194. package/dist/types/playground.d.ts +42 -1
  195. package/dist/types/playground.js +38 -0
  196. package/dist/types/settings.js +1 -1
  197. package/dist/utils/colors.js +4 -4
  198. package/dist/utils/connections.js +33 -8
  199. package/dist/utils/icons.js +1 -1
  200. package/dist/utils/logger.d.ts +47 -0
  201. package/dist/utils/logger.js +72 -0
  202. package/dist/utils/nodeWrapper.js +1 -1
  203. package/dist/utils/sanitize.d.ts +19 -0
  204. package/dist/utils/sanitize.js +31 -0
  205. package/dist/utils/validation.d.ts +29 -0
  206. package/dist/utils/validation.js +39 -0
  207. package/package.json +243 -232
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Settings Store for FlowDrop
2
+ * Settings Store for FlowDrop (Svelte 5 Runes)
3
3
  *
4
4
  * Provides unified state management for all user-configurable settings with:
5
5
  * - Hybrid persistence (localStorage primary, optional API sync)
6
- * - Category-specific derived stores for performance
6
+ * - Category-specific getter functions for performance
7
7
  * - Deep merge support for partial updates
8
8
  * - Integrated theme system with system preference detection
9
9
  *
10
10
  * @module stores/settingsStore
11
11
  */
12
- import { writable, derived, get } from 'svelte/store';
13
12
  import { DEFAULT_SETTINGS, SETTINGS_STORAGE_KEY } from '../types/settings.js';
13
+ import { logger } from '../utils/logger.js';
14
14
  // =========================================================================
15
15
  // Internal State
16
16
  // =========================================================================
@@ -44,7 +44,7 @@ function loadFromStorage() {
44
44
  }
45
45
  }
46
46
  catch (error) {
47
- console.warn('Failed to load settings from localStorage:', error);
47
+ logger.warn('Failed to load settings from localStorage:', error);
48
48
  }
49
49
  return null;
50
50
  }
@@ -61,7 +61,7 @@ function saveToStorage(settings) {
61
61
  localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
62
62
  }
63
63
  catch (error) {
64
- console.warn('Failed to save settings to localStorage:', error);
64
+ logger.warn('Failed to save settings to localStorage:', error);
65
65
  }
66
66
  }
67
67
  // =========================================================================
@@ -105,16 +105,16 @@ function deepMergeSettings(target, source) {
105
105
  return result;
106
106
  }
107
107
  // =========================================================================
108
- // Main Settings Store
108
+ // Main Settings Store (Rune-based)
109
109
  // =========================================================================
110
110
  /**
111
111
  * Initial state loaded from localStorage or defaults
112
112
  */
113
113
  const initialSettings = loadFromStorage() ?? DEFAULT_SETTINGS;
114
114
  /**
115
- * Main settings store state
115
+ * Main settings store state using $state rune
116
116
  */
117
- const storeState = writable({
117
+ let storeState = $state({
118
118
  settings: initialSettings,
119
119
  initialized: true,
120
120
  syncStatus: 'idle',
@@ -122,48 +122,85 @@ const storeState = writable({
122
122
  syncError: null
123
123
  });
124
124
  /**
125
- * Main settings store (read-only access to current settings)
126
- */
127
- export const settingsStore = derived(storeState, ($state) => $state.settings);
128
- /**
129
- * Sync status store for UI indicators
125
+ * System theme preference using $state rune
126
+ * Updates when system preference changes
130
127
  */
131
- export const syncStatusStore = derived(storeState, ($state) => ({
132
- status: $state.syncStatus,
133
- lastSyncedAt: $state.lastSyncedAt,
134
- error: $state.syncError
135
- }));
128
+ let systemThemeState = $state(typeof window !== 'undefined' ? getSystemTheme() : 'light');
136
129
  // =========================================================================
137
- // Category-Specific Derived Stores
130
+ // Getter Functions (replacing derived stores)
138
131
  // =========================================================================
139
132
  /**
140
- * Theme settings store
133
+ * Get current settings (replaces settingsStore derived store)
134
+ */
135
+ export function getSettings() {
136
+ return storeState.settings;
137
+ }
138
+ /**
139
+ * Get sync status (replaces syncStatusStore derived store)
140
+ */
141
+ export function getSyncStatus() {
142
+ return {
143
+ status: storeState.syncStatus,
144
+ lastSyncedAt: storeState.lastSyncedAt,
145
+ error: storeState.syncError
146
+ };
147
+ }
148
+ /**
149
+ * Get theme settings (replaces themeSettings derived store)
150
+ */
151
+ export function getThemeSettings() {
152
+ return storeState.settings.theme;
153
+ }
154
+ /**
155
+ * Get editor settings (replaces editorSettings derived store)
156
+ */
157
+ export function getEditorSettings() {
158
+ return storeState.settings.editor;
159
+ }
160
+ /**
161
+ * Get UI settings (replaces uiSettings derived store)
162
+ */
163
+ export function getUiSettings() {
164
+ return storeState.settings.ui;
165
+ }
166
+ /**
167
+ * Get behavior settings (replaces behaviorSettings derived store)
141
168
  */
142
- export const themeSettings = derived(settingsStore, ($settings) => $settings.theme);
169
+ export function getBehaviorSettings() {
170
+ return storeState.settings.behavior;
171
+ }
143
172
  /**
144
- * Editor settings store
173
+ * Get API settings (replaces apiSettings derived store)
145
174
  */
146
- export const editorSettings = derived(settingsStore, ($settings) => $settings.editor);
175
+ export function getApiSettings() {
176
+ return storeState.settings.api;
177
+ }
147
178
  /**
148
- * UI settings store
179
+ * Get theme preference (replaces theme derived store)
149
180
  */
150
- export const uiSettings = derived(settingsStore, ($settings) => $settings.ui);
181
+ export function getTheme() {
182
+ return storeState.settings.theme.preference;
183
+ }
151
184
  /**
152
- * Behavior settings store
185
+ * Get resolved theme - the actual theme applied ('light' or 'dark')
186
+ * When preference is 'auto', resolves based on system preference
187
+ * (replaces resolvedTheme derived store)
153
188
  */
154
- export const behaviorSettings = derived(settingsStore, ($settings) => $settings.behavior);
189
+ export function getResolvedTheme() {
190
+ if (storeState.settings.theme.preference === 'auto') {
191
+ return systemThemeState;
192
+ }
193
+ return storeState.settings.theme.preference;
194
+ }
155
195
  /**
156
- * API settings store
196
+ * Get system theme state (for internal use)
157
197
  */
158
- export const apiSettings = derived(settingsStore, ($settings) => $settings.api);
198
+ export function getSystemThemeState() {
199
+ return systemThemeState;
200
+ }
159
201
  // =========================================================================
160
202
  // Theme System
161
203
  // =========================================================================
162
- /**
163
- * System theme preference store
164
- * Updates when system preference changes
165
- */
166
- const systemTheme = writable(typeof window !== 'undefined' ? getSystemTheme() : 'light');
167
204
  /**
168
205
  * Get the system's color scheme preference
169
206
  */
@@ -173,35 +210,25 @@ function getSystemTheme() {
173
210
  }
174
211
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
175
212
  }
176
- // Listen for system theme changes
177
- if (typeof window !== 'undefined') {
213
+ /**
214
+ * Initialize the system theme change listener.
215
+ * Sets up a media query listener for the system color scheme preference.
216
+ *
217
+ * @returns Cleanup function that removes the listener
218
+ */
219
+ export function initThemeListener() {
220
+ if (typeof window === 'undefined') {
221
+ return () => { };
222
+ }
178
223
  const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
179
224
  const handleSystemThemeChange = (event) => {
180
- systemTheme.set(event.matches ? 'dark' : 'light');
225
+ systemThemeState = event.matches ? 'dark' : 'light';
226
+ };
227
+ mediaQuery.addEventListener('change', handleSystemThemeChange);
228
+ return () => {
229
+ mediaQuery.removeEventListener('change', handleSystemThemeChange);
181
230
  };
182
- if (mediaQuery.addEventListener) {
183
- mediaQuery.addEventListener('change', handleSystemThemeChange);
184
- }
185
- else {
186
- // Fallback for older browsers
187
- mediaQuery.addListener(handleSystemThemeChange);
188
- }
189
231
  }
190
- /**
191
- * Theme preference store
192
- * Derived from themeSettings for convenient access
193
- */
194
- export const theme = derived(themeSettings, ($theme) => $theme.preference);
195
- /**
196
- * Resolved theme - the actual theme applied ('light' or 'dark')
197
- * When preference is 'auto', resolves based on system preference
198
- */
199
- export const resolvedTheme = derived([themeSettings, systemTheme], ([$themeSettings, $systemTheme]) => {
200
- if ($themeSettings.preference === 'auto') {
201
- return $systemTheme;
202
- }
203
- return $themeSettings.preference;
204
- });
205
232
  // =========================================================================
206
233
  // Settings Update Functions
207
234
  // =========================================================================
@@ -220,7 +247,7 @@ function notifyChange(category, key, previousValue, newValue) {
220
247
  listener(event);
221
248
  }
222
249
  catch (error) {
223
- console.error('Settings change listener error:', error);
250
+ logger.error('Settings change listener error:', error);
224
251
  }
225
252
  });
226
253
  }
@@ -236,29 +263,27 @@ function getCategoryAsRecord(settings, category) {
236
263
  * @param partial - Partial settings to merge
237
264
  */
238
265
  export function updateSettings(partial) {
239
- storeState.update((state) => {
240
- const previousSettings = state.settings;
241
- const newSettings = deepMergeSettings(state.settings, partial);
242
- // Persist to localStorage immediately
243
- saveToStorage(newSettings);
244
- // Notify listeners for each changed category
245
- for (const category of Object.keys(partial)) {
246
- const partialCategory = partial[category];
247
- if (partialCategory && typeof partialCategory === 'object') {
248
- for (const key of Object.keys(partialCategory)) {
249
- const prevCat = getCategoryAsRecord(previousSettings, category);
250
- const newCat = getCategoryAsRecord(newSettings, category);
251
- if (prevCat[key] !== newCat[key]) {
252
- notifyChange(category, key, prevCat[key], newCat[key]);
253
- }
266
+ const previousSettings = storeState.settings;
267
+ const newSettings = deepMergeSettings(storeState.settings, partial);
268
+ // Persist to localStorage immediately
269
+ saveToStorage(newSettings);
270
+ // Notify listeners for each changed category
271
+ for (const category of Object.keys(partial)) {
272
+ const partialCategory = partial[category];
273
+ if (partialCategory && typeof partialCategory === 'object') {
274
+ for (const key of Object.keys(partialCategory)) {
275
+ const prevCat = getCategoryAsRecord(previousSettings, category);
276
+ const newCat = getCategoryAsRecord(newSettings, category);
277
+ if (prevCat[key] !== newCat[key]) {
278
+ notifyChange(category, key, prevCat[key], newCat[key]);
254
279
  }
255
280
  }
256
281
  }
257
- return {
258
- ...state,
259
- settings: newSettings
260
- };
261
- });
282
+ }
283
+ storeState = {
284
+ ...storeState,
285
+ settings: newSettings
286
+ };
262
287
  }
263
288
  /**
264
289
  * Reset settings to defaults
@@ -274,21 +299,13 @@ export function resetSettings(categories) {
274
299
  updateSettings(partial);
275
300
  }
276
301
  else {
277
- storeState.update((state) => {
278
- saveToStorage(DEFAULT_SETTINGS);
279
- return {
280
- ...state,
281
- settings: DEFAULT_SETTINGS
282
- };
283
- });
302
+ saveToStorage(DEFAULT_SETTINGS);
303
+ storeState = {
304
+ ...storeState,
305
+ settings: DEFAULT_SETTINGS
306
+ };
284
307
  }
285
308
  }
286
- /**
287
- * Get current settings synchronously
288
- */
289
- export function getSettings() {
290
- return get(settingsStore);
291
- }
292
309
  // =========================================================================
293
310
  // Theme Actions
294
311
  // =========================================================================
@@ -305,8 +322,8 @@ export function setTheme(newTheme) {
305
322
  * If currently 'auto', switches to the opposite of system preference
306
323
  */
307
324
  export function toggleTheme() {
308
- const currentTheme = get(theme);
309
- const currentResolved = get(resolvedTheme);
325
+ const currentTheme = getTheme();
326
+ const currentResolved = getResolvedTheme();
310
327
  if (currentTheme === 'auto') {
311
328
  setTheme(currentResolved === 'dark' ? 'light' : 'dark');
312
329
  }
@@ -318,7 +335,7 @@ export function toggleTheme() {
318
335
  * Cycle through theme options: light -> dark -> auto -> light
319
336
  */
320
337
  export function cycleTheme() {
321
- const currentTheme = get(theme);
338
+ const currentTheme = getTheme();
322
339
  switch (currentTheme) {
323
340
  case 'light':
324
341
  setTheme('dark');
@@ -342,6 +359,22 @@ function applyTheme(resolved) {
342
359
  }
343
360
  document.documentElement.setAttribute('data-theme', resolved);
344
361
  }
362
+ /**
363
+ * Stored cleanup function for the theme effect.
364
+ * Retained so it can be called by cleanupThemeSubscription().
365
+ */
366
+ let themeEffectCleanup = null;
367
+ /**
368
+ * Clean up the theme subscription created by initializeTheme().
369
+ * Call this when tearing down the settings system (e.g., in tests or
370
+ * component cleanup) to prevent memory leaks.
371
+ */
372
+ export function cleanupThemeSubscription() {
373
+ if (themeEffectCleanup) {
374
+ themeEffectCleanup();
375
+ themeEffectCleanup = null;
376
+ }
377
+ }
345
378
  /**
346
379
  * Initialize the theme system
347
380
  * Should be called once on app startup
@@ -349,13 +382,21 @@ function applyTheme(resolved) {
349
382
  * This function:
350
383
  * 1. Applies the current resolved theme to the document
351
384
  * 2. Sets up reactivity to apply theme changes
385
+ *
386
+ * Note: In Svelte 5, we use $effect for reactivity. Since $effect can only
387
+ * be used in component context or $effect.root, we use $effect.root here
388
+ * to create a standalone reactive scope.
352
389
  */
353
390
  export function initializeTheme() {
354
- const resolved = get(resolvedTheme);
391
+ const resolved = getResolvedTheme();
355
392
  applyTheme(resolved);
356
- // Subscribe to resolved theme changes and apply them
357
- resolvedTheme.subscribe((theme) => {
358
- applyTheme(theme);
393
+ // Create a standalone reactive root to watch for theme changes.
394
+ // $effect.root returns a cleanup function.
395
+ themeEffectCleanup = $effect.root(() => {
396
+ $effect(() => {
397
+ const currentResolved = getResolvedTheme();
398
+ applyTheme(currentResolved);
399
+ });
359
400
  });
360
401
  }
361
402
  /**
@@ -388,31 +429,31 @@ export function setSettingsService(service) {
388
429
  */
389
430
  export async function syncSettingsToApi() {
390
431
  if (!settingsService) {
391
- console.warn('Settings service not configured for API sync');
432
+ logger.warn('Settings service not configured for API sync');
392
433
  return;
393
434
  }
394
- storeState.update((state) => ({
395
- ...state,
435
+ storeState = {
436
+ ...storeState,
396
437
  syncStatus: 'syncing',
397
438
  syncError: null
398
- }));
439
+ };
399
440
  try {
400
- const currentSettings = get(settingsStore);
441
+ const currentSettings = getSettings();
401
442
  await settingsService.savePreferences(currentSettings);
402
- storeState.update((state) => ({
403
- ...state,
443
+ storeState = {
444
+ ...storeState,
404
445
  syncStatus: 'synced',
405
446
  lastSyncedAt: Date.now(),
406
447
  syncError: null
407
- }));
448
+ };
408
449
  }
409
450
  catch (error) {
410
451
  const errorMessage = error instanceof Error ? error.message : 'Failed to sync settings';
411
- storeState.update((state) => ({
412
- ...state,
452
+ storeState = {
453
+ ...storeState,
413
454
  syncStatus: 'error',
414
455
  syncError: errorMessage
415
- }));
456
+ };
416
457
  throw error;
417
458
  }
418
459
  }
@@ -423,34 +464,34 @@ export async function syncSettingsToApi() {
423
464
  */
424
465
  export async function loadSettingsFromApi() {
425
466
  if (!settingsService) {
426
- console.warn('Settings service not configured for API sync');
467
+ logger.warn('Settings service not configured for API sync');
427
468
  return;
428
469
  }
429
- storeState.update((state) => ({
430
- ...state,
470
+ storeState = {
471
+ ...storeState,
431
472
  syncStatus: 'syncing',
432
473
  syncError: null
433
- }));
474
+ };
434
475
  try {
435
- const apiSettings = await settingsService.getPreferences();
436
- const mergedSettings = deepMergeSettings(DEFAULT_SETTINGS, apiSettings);
437
- storeState.update((state) => ({
438
- ...state,
476
+ const apiSettingsData = await settingsService.getPreferences();
477
+ const mergedSettings = deepMergeSettings(DEFAULT_SETTINGS, apiSettingsData);
478
+ storeState = {
479
+ ...storeState,
439
480
  settings: mergedSettings,
440
481
  syncStatus: 'synced',
441
482
  lastSyncedAt: Date.now(),
442
483
  syncError: null
443
- }));
484
+ };
444
485
  // Also persist to localStorage
445
486
  saveToStorage(mergedSettings);
446
487
  }
447
488
  catch (error) {
448
489
  const errorMessage = error instanceof Error ? error.message : 'Failed to load settings from API';
449
- storeState.update((state) => ({
450
- ...state,
490
+ storeState = {
491
+ ...storeState,
451
492
  syncStatus: 'error',
452
493
  syncError: errorMessage
453
- }));
494
+ };
454
495
  throw error;
455
496
  }
456
497
  }
@@ -480,12 +521,12 @@ export function onSettingsChange(callback) {
480
521
  export async function initializeSettings(options) {
481
522
  // Apply custom defaults if provided
482
523
  if (options?.defaults) {
483
- const currentSettings = get(settingsStore);
524
+ const currentSettings = getSettings();
484
525
  const withDefaults = deepMergeSettings(currentSettings, options.defaults);
485
- storeState.update((state) => ({
486
- ...state,
526
+ storeState = {
527
+ ...storeState,
487
528
  settings: withDefaults
488
- }));
529
+ };
489
530
  saveToStorage(withDefaults);
490
531
  }
491
532
  // Initialize theme system
@@ -497,7 +538,7 @@ export async function initializeSettings(options) {
497
538
  }
498
539
  catch {
499
540
  // Silently fail - local settings are still available
500
- console.warn('Failed to sync settings from API on initialization');
541
+ logger.warn('Failed to sync settings from API on initialization');
501
542
  }
502
543
  }
503
544
  }
@@ -1,22 +1,113 @@
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
13
  import type { Workflow, WorkflowNode, WorkflowEdge } from '../types';
10
14
  import type { WorkflowChangeType } from '../types/events.js';
11
- /** Global workflow state */
12
- export declare const workflowStore: import("svelte/store").Writable<Workflow>;
15
+ type WorkflowMetadata = NonNullable<Workflow['metadata']>;
16
+ /**
17
+ * Get the current workflow store value reactively
18
+ *
19
+ * @returns The current workflow or null
20
+ */
21
+ export declare function getWorkflowStore(): Workflow | null;
22
+ /**
23
+ * Get the current dirty state reactively
24
+ *
25
+ * @returns true if there are unsaved changes
26
+ */
27
+ export declare function getIsDirty(): boolean;
28
+ /**
29
+ * Get the workflow ID reactively
30
+ *
31
+ * @returns The workflow ID or null
32
+ */
33
+ export declare function getWorkflowId(): string | null;
34
+ /**
35
+ * Get the workflow name reactively
36
+ *
37
+ * @returns The workflow name or 'Untitled Workflow'
38
+ */
39
+ export declare function getWorkflowName(): string;
40
+ /**
41
+ * Get the workflow nodes reactively
42
+ *
43
+ * @returns Array of workflow nodes
44
+ */
45
+ export declare function getWorkflowNodes(): WorkflowNode[];
46
+ /**
47
+ * Get the workflow edges reactively
48
+ *
49
+ * @returns Array of workflow edges
50
+ */
51
+ export declare function getWorkflowEdges(): WorkflowEdge[];
13
52
  /**
14
- * Store for tracking if there are unsaved changes
53
+ * Get the workflow metadata reactively
15
54
  *
16
- * This is set to true whenever the workflow changes after initialization.
17
- * It can be reset to false by calling markAsSaved().
55
+ * @returns The workflow metadata with defaults
18
56
  */
19
- export declare const isDirtyStore: import("svelte/store").Writable<boolean>;
57
+ export declare function getWorkflowMetadata(): WorkflowMetadata;
58
+ /**
59
+ * Get the current workflow format reactively
60
+ *
61
+ * @returns The workflow format string
62
+ */
63
+ export declare function getWorkflowFormat(): string;
64
+ /**
65
+ * Get workflow change summary reactively (useful for triggering saves)
66
+ *
67
+ * @returns Object with nodes, edges, and name
68
+ */
69
+ export declare function getWorkflowChanged(): {
70
+ nodes: WorkflowNode[];
71
+ edges: WorkflowEdge[];
72
+ name: string;
73
+ };
74
+ /**
75
+ * Get workflow validation state reactively
76
+ *
77
+ * @returns Validation info object
78
+ */
79
+ export declare function getWorkflowValidation(): {
80
+ hasNodes: boolean;
81
+ hasEdges: boolean;
82
+ nodeCount: number;
83
+ edgeCount: number;
84
+ isValid: boolean;
85
+ };
86
+ /**
87
+ * Get workflow metadata change summary reactively
88
+ *
89
+ * @returns Metadata change info
90
+ */
91
+ export declare function getWorkflowMetadataChanged(): {
92
+ createdAt: string;
93
+ updatedAt: string;
94
+ version: string;
95
+ };
96
+ /**
97
+ * Get connected handles reactively
98
+ *
99
+ * Provides a Set of all handle IDs that are currently connected to edges.
100
+ * Used by node components to implement hideUnconnectedHandles functionality.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * import { getConnectedHandles } from './workflowStore.svelte.js';
105
+ *
106
+ * // Check if a specific handle is connected
107
+ * const isConnected = getConnectedHandles().has('node-1-input-data');
108
+ * ```
109
+ */
110
+ export declare function getConnectedHandles(): Set<string>;
20
111
  /**
21
112
  * Set the dirty state change callback
22
113
  *
@@ -36,7 +127,7 @@ export declare function setOnWorkflowChange(callback: ((workflow: Workflow, chan
36
127
  */
37
128
  export declare function markAsSaved(): void;
38
129
  /**
39
- * Check if there are unsaved changes
130
+ * Check if there are unsaved changes (non-reactive version for plain TS)
40
131
  *
41
132
  * @returns true if there are unsaved changes
42
133
  */
@@ -64,32 +155,11 @@ export declare function isHistoryEnabled(): boolean;
64
155
  */
65
156
  export declare function setRestoringFromHistory(restoring: boolean): void;
66
157
  /**
67
- * Get the current workflow
158
+ * Get the current workflow (non-reactive version for plain TS)
68
159
  *
69
160
  * @returns The current workflow or null
70
161
  */
71
162
  export declare function getWorkflow(): Workflow | null;
72
- /** Derived store for workflow ID */
73
- export declare const workflowId: import("svelte/store").Readable<string>;
74
- /** Derived store for workflow name */
75
- export declare const workflowName: import("svelte/store").Readable<string>;
76
- /** Derived store for workflow nodes */
77
- export declare const workflowNodes: import("svelte/store").Readable<WorkflowNode[]>;
78
- /** Derived store for workflow edges */
79
- export declare const workflowEdges: import("svelte/store").Readable<WorkflowEdge[]>;
80
- /** Derived store for workflow metadata */
81
- export declare const workflowMetadata: import("svelte/store").Readable<{
82
- version: string;
83
- createdAt: string;
84
- updatedAt: string;
85
- author?: string;
86
- tags?: string[];
87
- versionId?: string;
88
- updateNumber?: number;
89
- format?: import("../types").WorkflowFormat;
90
- }>;
91
- /** Derived store for the current workflow format */
92
- export declare const workflowFormat: import("svelte/store").Readable<import("../types").WorkflowFormat>;
93
163
  /**
94
164
  * Actions for updating the workflow
95
165
  *
@@ -187,38 +257,4 @@ export declare const workflowActions: {
187
257
  */
188
258
  pushHistory: (description?: string, workflow?: Workflow) => void;
189
259
  };
190
- /** Derived store for workflow changes (useful for triggering saves) */
191
- export declare const workflowChanged: import("svelte/store").Readable<{
192
- nodes: WorkflowNode[];
193
- edges: WorkflowEdge[];
194
- name: string;
195
- }>;
196
- /** Derived store for workflow validation */
197
- export declare const workflowValidation: import("svelte/store").Readable<{
198
- hasNodes: boolean;
199
- hasEdges: boolean;
200
- nodeCount: number;
201
- edgeCount: number;
202
- isValid: boolean;
203
- }>;
204
- /** Derived store for workflow metadata changes */
205
- export declare const workflowMetadataChanged: import("svelte/store").Readable<{
206
- createdAt: string;
207
- updatedAt: string;
208
- version: string;
209
- }>;
210
- /**
211
- * Derived store for connected handles
212
- *
213
- * Provides a Set of all handle IDs that are currently connected to edges.
214
- * Used by node components to implement hideUnconnectedHandles functionality.
215
- *
216
- * @example
217
- * ```typescript
218
- * import { connectedHandles } from './workflowStore.js';
219
- *
220
- * // Check if a specific handle is connected
221
- * const isConnected = $connectedHandles.has('node-1-input-data');
222
- * ```
223
- */
224
- export declare const connectedHandles: import("svelte/store").Readable<Set<string>>;
260
+ export {};