@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,9 +1,9 @@
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
  *
@@ -12,47 +12,58 @@
12
12
  import type { FlowDropSettings, ThemeSettings, EditorSettings, UISettings, BehaviorSettings, ApiSettings, PartialSettings, SyncStatus, ResolvedTheme, ThemePreference, SettingsChangeCallback, SettingsCategory } from '../types/settings.js';
13
13
  export type { ThemePreference, ResolvedTheme } from '../types/settings.js';
14
14
  /**
15
- * Main settings store (read-only access to current settings)
15
+ * Get current settings (replaces settingsStore derived store)
16
16
  */
17
- export declare const settingsStore: import("svelte/store").Readable<FlowDropSettings>;
17
+ export declare function getSettings(): FlowDropSettings;
18
18
  /**
19
- * Sync status store for UI indicators
19
+ * Get sync status (replaces syncStatusStore derived store)
20
20
  */
21
- export declare const syncStatusStore: import("svelte/store").Readable<{
21
+ export declare function getSyncStatus(): {
22
22
  status: SyncStatus;
23
- lastSyncedAt: number;
24
- error: string;
25
- }>;
23
+ lastSyncedAt: number | null;
24
+ error: string | null;
25
+ };
26
26
  /**
27
- * Theme settings store
27
+ * Get theme settings (replaces themeSettings derived store)
28
28
  */
29
- export declare const themeSettings: import("svelte/store").Readable<ThemeSettings>;
29
+ export declare function getThemeSettings(): ThemeSettings;
30
30
  /**
31
- * Editor settings store
31
+ * Get editor settings (replaces editorSettings derived store)
32
32
  */
33
- export declare const editorSettings: import("svelte/store").Readable<EditorSettings>;
33
+ export declare function getEditorSettings(): EditorSettings;
34
34
  /**
35
- * UI settings store
35
+ * Get UI settings (replaces uiSettings derived store)
36
36
  */
37
- export declare const uiSettings: import("svelte/store").Readable<UISettings>;
37
+ export declare function getUiSettings(): UISettings;
38
38
  /**
39
- * Behavior settings store
39
+ * Get behavior settings (replaces behaviorSettings derived store)
40
40
  */
41
- export declare const behaviorSettings: import("svelte/store").Readable<BehaviorSettings>;
41
+ export declare function getBehaviorSettings(): BehaviorSettings;
42
42
  /**
43
- * API settings store
43
+ * Get API settings (replaces apiSettings derived store)
44
44
  */
45
- export declare const apiSettings: import("svelte/store").Readable<ApiSettings>;
45
+ export declare function getApiSettings(): ApiSettings;
46
46
  /**
47
- * Theme preference store
48
- * Derived from themeSettings for convenient access
47
+ * Get theme preference (replaces theme derived store)
49
48
  */
50
- export declare const theme: import("svelte/store").Readable<ThemePreference>;
49
+ export declare function getTheme(): ThemePreference;
51
50
  /**
52
- * Resolved theme - the actual theme applied ('light' or 'dark')
51
+ * Get resolved theme - the actual theme applied ('light' or 'dark')
53
52
  * When preference is 'auto', resolves based on system preference
53
+ * (replaces resolvedTheme derived store)
54
+ */
55
+ export declare function getResolvedTheme(): ResolvedTheme;
56
+ /**
57
+ * Get system theme state (for internal use)
58
+ */
59
+ export declare function getSystemThemeState(): ResolvedTheme;
60
+ /**
61
+ * Initialize the system theme change listener.
62
+ * Sets up a media query listener for the system color scheme preference.
63
+ *
64
+ * @returns Cleanup function that removes the listener
54
65
  */
55
- export declare const resolvedTheme: import("svelte/store").Readable<ResolvedTheme>;
66
+ export declare function initThemeListener(): () => void;
56
67
  /**
57
68
  * Update settings with partial values
58
69
  *
@@ -65,10 +76,6 @@ export declare function updateSettings(partial: PartialSettings): void;
65
76
  * @param categories - Optional categories to reset (all if not specified)
66
77
  */
67
78
  export declare function resetSettings(categories?: SettingsCategory[]): void;
68
- /**
69
- * Get current settings synchronously
70
- */
71
- export declare function getSettings(): FlowDropSettings;
72
79
  /**
73
80
  * Set the theme preference
74
81
  *
@@ -84,6 +91,12 @@ export declare function toggleTheme(): void;
84
91
  * Cycle through theme options: light -> dark -> auto -> light
85
92
  */
86
93
  export declare function cycleTheme(): void;
94
+ /**
95
+ * Clean up the theme subscription created by initializeTheme().
96
+ * Call this when tearing down the settings system (e.g., in tests or
97
+ * component cleanup) to prevent memory leaks.
98
+ */
99
+ export declare function cleanupThemeSubscription(): void;
87
100
  /**
88
101
  * Initialize the theme system
89
102
  * Should be called once on app startup
@@ -91,6 +104,10 @@ export declare function cycleTheme(): void;
91
104
  * This function:
92
105
  * 1. Applies the current resolved theme to the document
93
106
  * 2. Sets up reactivity to apply theme changes
107
+ *
108
+ * Note: In Svelte 5, we use $effect for reactivity. Since $effect can only
109
+ * be used in component context or $effect.root, we use $effect.root here
110
+ * to create a standalone reactive scope.
94
111
  */
95
112
  export declare function initializeTheme(): void;
96
113
  /**
@@ -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
  }