@d34dman/flowdrop 0.0.45 → 0.0.47

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 (75) hide show
  1. package/README.md +2 -2
  2. package/dist/components/App.svelte +6 -0
  3. package/dist/components/ConfigForm.svelte +56 -22
  4. package/dist/components/ConfigForm.svelte.d.ts +11 -1
  5. package/dist/components/Navbar.svelte +6 -7
  6. package/dist/components/SchemaForm.svelte +2 -10
  7. package/dist/components/SettingsPanel.svelte +5 -2
  8. package/dist/components/WorkflowEditor.svelte +158 -4
  9. package/dist/components/WorkflowEditor.svelte.d.ts +1 -0
  10. package/dist/components/form/FormAutocomplete.svelte +5 -9
  11. package/dist/components/form/FormCheckboxGroup.svelte +11 -1
  12. package/dist/components/form/FormCheckboxGroup.svelte.d.ts +2 -0
  13. package/dist/components/form/FormCodeEditor.svelte +16 -7
  14. package/dist/components/form/FormCodeEditor.svelte.d.ts +2 -0
  15. package/dist/components/form/FormField.svelte +33 -12
  16. package/dist/components/form/FormFieldLight.svelte +16 -12
  17. package/dist/components/form/FormMarkdownEditor.svelte +29 -19
  18. package/dist/components/form/FormMarkdownEditor.svelte.d.ts +2 -0
  19. package/dist/components/form/FormNumberField.svelte +4 -0
  20. package/dist/components/form/FormNumberField.svelte.d.ts +2 -0
  21. package/dist/components/form/FormRangeField.svelte +4 -0
  22. package/dist/components/form/FormRangeField.svelte.d.ts +2 -0
  23. package/dist/components/form/FormSelect.svelte +4 -0
  24. package/dist/components/form/FormSelect.svelte.d.ts +2 -0
  25. package/dist/components/form/FormTemplateEditor.svelte +140 -17
  26. package/dist/components/form/FormTemplateEditor.svelte.d.ts +19 -1
  27. package/dist/components/form/FormTextField.svelte +4 -0
  28. package/dist/components/form/FormTextField.svelte.d.ts +2 -0
  29. package/dist/components/form/FormTextarea.svelte +4 -0
  30. package/dist/components/form/FormTextarea.svelte.d.ts +2 -0
  31. package/dist/components/form/FormToggle.svelte +4 -0
  32. package/dist/components/form/FormToggle.svelte.d.ts +2 -0
  33. package/dist/components/form/index.d.ts +1 -0
  34. package/dist/components/form/index.js +2 -0
  35. package/dist/components/form/templateAutocomplete.d.ts +38 -0
  36. package/dist/components/form/templateAutocomplete.js +309 -0
  37. package/dist/components/form/types.d.ts +39 -2
  38. package/dist/components/form/types.js +1 -1
  39. package/dist/components/layouts/MainLayout.svelte +5 -2
  40. package/dist/components/nodes/GatewayNode.svelte +0 -8
  41. package/dist/components/nodes/SimpleNode.svelte +2 -3
  42. package/dist/components/nodes/WorkflowNode.svelte +0 -8
  43. package/dist/components/playground/Playground.svelte +43 -38
  44. package/dist/editor/index.d.ts +3 -1
  45. package/dist/editor/index.js +5 -1
  46. package/dist/helpers/workflowEditorHelper.js +1 -2
  47. package/dist/registry/nodeComponentRegistry.d.ts +9 -9
  48. package/dist/registry/nodeComponentRegistry.js +10 -10
  49. package/dist/services/autoSaveService.js +5 -5
  50. package/dist/services/historyService.d.ts +207 -0
  51. package/dist/services/historyService.js +317 -0
  52. package/dist/services/settingsService.d.ts +2 -2
  53. package/dist/services/settingsService.js +15 -21
  54. package/dist/services/toastService.d.ts +1 -1
  55. package/dist/services/toastService.js +10 -10
  56. package/dist/services/variableService.d.ts +100 -0
  57. package/dist/services/variableService.js +367 -0
  58. package/dist/stores/historyStore.d.ts +133 -0
  59. package/dist/stores/historyStore.js +188 -0
  60. package/dist/stores/settingsStore.d.ts +1 -1
  61. package/dist/stores/settingsStore.js +40 -42
  62. package/dist/stores/themeStore.d.ts +2 -2
  63. package/dist/stores/themeStore.js +30 -32
  64. package/dist/stores/workflowStore.d.ts +52 -2
  65. package/dist/stores/workflowStore.js +102 -2
  66. package/dist/styles/base.css +28 -8
  67. package/dist/styles/toast.css +3 -1
  68. package/dist/styles/tokens.css +2 -2
  69. package/dist/types/index.d.ts +120 -0
  70. package/dist/types/settings.d.ts +3 -3
  71. package/dist/types/settings.js +13 -19
  72. package/dist/utils/colors.js +17 -17
  73. package/dist/utils/nodeTypes.d.ts +15 -10
  74. package/dist/utils/nodeTypes.js +24 -22
  75. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ /**
2
+ * History Store for FlowDrop
3
+ *
4
+ * Provides reactive Svelte store bindings for the history service.
5
+ * Exposes undo/redo state and actions for the workflow editor.
6
+ *
7
+ * @module stores/historyStore
8
+ */
9
+ import { writable, derived, get } from 'svelte/store';
10
+ import { historyService } from '../services/historyService.js';
11
+ // =========================================================================
12
+ // Reactive State Store
13
+ // =========================================================================
14
+ /**
15
+ * Internal writable store for history state
16
+ */
17
+ const historyStateStore = writable({
18
+ canUndo: false,
19
+ canRedo: false,
20
+ currentIndex: 0,
21
+ historyLength: 0,
22
+ isInTransaction: false
23
+ });
24
+ // Subscribe to history service changes and update the store
25
+ historyService.subscribe((state) => {
26
+ historyStateStore.set(state);
27
+ });
28
+ /**
29
+ * Reactive history state store
30
+ *
31
+ * Use this for binding to UI elements like undo/redo buttons.
32
+ * Subscribe using Svelte's $ prefix or the subscribe method.
33
+ *
34
+ * @example
35
+ * ```svelte
36
+ * <script>
37
+ * import { historyStateStore } from "./historyStore.js";
38
+ * </script>
39
+ *
40
+ * <button disabled={!$historyStateStore.canUndo} onclick={historyActions.undo}>
41
+ * Undo
42
+ * </button>
43
+ * ```
44
+ */
45
+ export { historyStateStore };
46
+ /**
47
+ * Derived store for canUndo state
48
+ *
49
+ * Convenience store that directly exposes the canUndo boolean.
50
+ */
51
+ export const canUndo = derived(historyStateStore, ($state) => $state.canUndo);
52
+ /**
53
+ * Derived store for canRedo state
54
+ *
55
+ * Convenience store that directly exposes the canRedo boolean.
56
+ */
57
+ export const canRedo = derived(historyStateStore, ($state) => $state.canRedo);
58
+ // =========================================================================
59
+ // History Actions
60
+ // =========================================================================
61
+ /**
62
+ * Callback for when workflow state is restored from history
63
+ *
64
+ * Set this to handle the restored workflow state (e.g., update the workflow store)
65
+ */
66
+ let onRestoreCallback = null;
67
+ /**
68
+ * Set the callback for restoring workflow state
69
+ *
70
+ * This callback is invoked when undo/redo operations return a workflow.
71
+ * Use this to update the workflow store or other state management.
72
+ *
73
+ * @param callback - Function to call with restored workflow
74
+ */
75
+ export function setOnRestoreCallback(callback) {
76
+ onRestoreCallback = callback;
77
+ }
78
+ /**
79
+ * History actions for undo/redo operations
80
+ *
81
+ * Use these functions to interact with the history service.
82
+ * They handle the coordination between history and workflow state.
83
+ */
84
+ export const historyActions = {
85
+ /**
86
+ * Initialize history with the current workflow
87
+ *
88
+ * Call this when loading a new workflow to reset history.
89
+ *
90
+ * @param workflow - The initial workflow state
91
+ */
92
+ initialize: (workflow) => {
93
+ historyService.initialize(workflow);
94
+ },
95
+ /**
96
+ * Push the current state to history before making changes
97
+ *
98
+ * Call this BEFORE modifying the workflow to capture the "before" state.
99
+ *
100
+ * @param workflow - The current workflow state (before changes)
101
+ * @param options - Options for this history entry
102
+ */
103
+ pushState: (workflow, options) => {
104
+ historyService.push(workflow, options);
105
+ },
106
+ /**
107
+ * Undo the last change
108
+ *
109
+ * Restores the previous workflow state and invokes the restore callback.
110
+ *
111
+ * @returns true if undo was successful, false if at beginning of history
112
+ */
113
+ undo: () => {
114
+ const previousState = historyService.undo();
115
+ if (previousState && onRestoreCallback) {
116
+ onRestoreCallback(previousState);
117
+ return true;
118
+ }
119
+ return previousState !== null;
120
+ },
121
+ /**
122
+ * Redo the last undone change
123
+ *
124
+ * Restores the next workflow state and invokes the restore callback.
125
+ *
126
+ * @returns true if redo was successful, false if at end of history
127
+ */
128
+ redo: () => {
129
+ const nextState = historyService.redo();
130
+ if (nextState && onRestoreCallback) {
131
+ onRestoreCallback(nextState);
132
+ return true;
133
+ }
134
+ return false;
135
+ },
136
+ /**
137
+ * Start a transaction for grouping multiple changes
138
+ *
139
+ * All changes during a transaction are combined into a single undo entry.
140
+ *
141
+ * @param workflow - The current workflow state (before changes)
142
+ * @param description - Description for the combined change
143
+ */
144
+ startTransaction: (workflow, description) => {
145
+ historyService.startTransaction(workflow, description);
146
+ },
147
+ /**
148
+ * Commit the current transaction
149
+ */
150
+ commitTransaction: () => {
151
+ historyService.commitTransaction();
152
+ },
153
+ /**
154
+ * Cancel the current transaction without committing
155
+ */
156
+ cancelTransaction: () => {
157
+ historyService.cancelTransaction();
158
+ },
159
+ /**
160
+ * Clear all history
161
+ *
162
+ * @param currentWorkflow - If provided, keeps this as the initial state
163
+ */
164
+ clear: (currentWorkflow) => {
165
+ historyService.clear(currentWorkflow);
166
+ },
167
+ /**
168
+ * Check if undo is available
169
+ */
170
+ canUndo: () => {
171
+ return historyService.canUndo();
172
+ },
173
+ /**
174
+ * Check if redo is available
175
+ */
176
+ canRedo: () => {
177
+ return historyService.canRedo();
178
+ },
179
+ /**
180
+ * Get the current history state synchronously
181
+ *
182
+ * @returns The current history state
183
+ */
184
+ getState: () => {
185
+ return get(historyStateStore);
186
+ }
187
+ };
188
+ export { HistoryService, historyService } from '../services/historyService.js';
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * @module stores/settingsStore
11
11
  */
12
- import type { FlowDropSettings, ThemeSettings, EditorSettings, UISettings, BehaviorSettings, ApiSettings, PartialSettings, SyncStatus, ResolvedTheme, ThemePreference, SettingsChangeCallback, SettingsCategory } from "../types/settings.js";
12
+ import type { FlowDropSettings, ThemeSettings, EditorSettings, UISettings, BehaviorSettings, ApiSettings, PartialSettings, SyncStatus, ResolvedTheme, ThemePreference, SettingsChangeCallback, SettingsCategory } from '../types/settings.js';
13
13
  /**
14
14
  * Main settings store (read-only access to current settings)
15
15
  */
@@ -9,8 +9,8 @@
9
9
  *
10
10
  * @module stores/settingsStore
11
11
  */
12
- import { writable, derived, get } from "svelte/store";
13
- import { DEFAULT_SETTINGS, SETTINGS_STORAGE_KEY } from "../types/settings.js";
12
+ import { writable, derived, get } from 'svelte/store';
13
+ import { DEFAULT_SETTINGS, SETTINGS_STORAGE_KEY } from '../types/settings.js';
14
14
  // =========================================================================
15
15
  // Internal State
16
16
  // =========================================================================
@@ -32,7 +32,7 @@ const changeListeners = new Set();
32
32
  * @returns Saved settings or null if not found/invalid
33
33
  */
34
34
  function loadFromStorage() {
35
- if (typeof window === "undefined") {
35
+ if (typeof window === 'undefined') {
36
36
  return null;
37
37
  }
38
38
  try {
@@ -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
+ console.warn('Failed to load settings from localStorage:', error);
48
48
  }
49
49
  return null;
50
50
  }
@@ -54,14 +54,14 @@ function loadFromStorage() {
54
54
  * @param settings - Settings to persist
55
55
  */
56
56
  function saveToStorage(settings) {
57
- if (typeof window === "undefined") {
57
+ if (typeof window === 'undefined') {
58
58
  return;
59
59
  }
60
60
  try {
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
+ console.warn('Failed to save settings to localStorage:', error);
65
65
  }
66
66
  }
67
67
  // =========================================================================
@@ -117,7 +117,7 @@ const initialSettings = loadFromStorage() ?? DEFAULT_SETTINGS;
117
117
  const storeState = writable({
118
118
  settings: initialSettings,
119
119
  initialized: true,
120
- syncStatus: "idle",
120
+ syncStatus: 'idle',
121
121
  lastSyncedAt: null,
122
122
  syncError: null
123
123
  });
@@ -163,26 +163,24 @@ export const apiSettings = derived(settingsStore, ($settings) => $settings.api);
163
163
  * System theme preference store
164
164
  * Updates when system preference changes
165
165
  */
166
- const systemTheme = writable(typeof window !== "undefined" ? getSystemTheme() : "light");
166
+ const systemTheme = writable(typeof window !== 'undefined' ? getSystemTheme() : 'light');
167
167
  /**
168
168
  * Get the system's color scheme preference
169
169
  */
170
170
  function getSystemTheme() {
171
- if (typeof window === "undefined") {
172
- return "light";
171
+ if (typeof window === 'undefined') {
172
+ return 'light';
173
173
  }
174
- return window.matchMedia("(prefers-color-scheme: dark)").matches
175
- ? "dark"
176
- : "light";
174
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
177
175
  }
178
176
  // Listen for system theme changes
179
- if (typeof window !== "undefined") {
180
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
177
+ if (typeof window !== 'undefined') {
178
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
181
179
  const handleSystemThemeChange = (event) => {
182
- systemTheme.set(event.matches ? "dark" : "light");
180
+ systemTheme.set(event.matches ? 'dark' : 'light');
183
181
  };
184
182
  if (mediaQuery.addEventListener) {
185
- mediaQuery.addEventListener("change", handleSystemThemeChange);
183
+ mediaQuery.addEventListener('change', handleSystemThemeChange);
186
184
  }
187
185
  else {
188
186
  // Fallback for older browsers
@@ -198,7 +196,7 @@ export const theme = derived(themeSettings, ($theme) => $theme.preference);
198
196
  * Always returns the actual theme being applied ('light' or 'dark')
199
197
  */
200
198
  export const resolvedTheme = derived([themeSettings, systemTheme], ([$themeSettings, $systemTheme]) => {
201
- if ($themeSettings.preference === "auto") {
199
+ if ($themeSettings.preference === 'auto') {
202
200
  return $systemTheme;
203
201
  }
204
202
  return $themeSettings.preference;
@@ -221,7 +219,7 @@ function notifyChange(category, key, previousValue, newValue) {
221
219
  listener(event);
222
220
  }
223
221
  catch (error) {
224
- console.error("Settings change listener error:", error);
222
+ console.error('Settings change listener error:', error);
225
223
  }
226
224
  });
227
225
  }
@@ -245,7 +243,7 @@ export function updateSettings(partial) {
245
243
  // Notify listeners for each changed category
246
244
  for (const category of Object.keys(partial)) {
247
245
  const partialCategory = partial[category];
248
- if (partialCategory && typeof partialCategory === "object") {
246
+ if (partialCategory && typeof partialCategory === 'object') {
249
247
  for (const key of Object.keys(partialCategory)) {
250
248
  const prevCat = getCategoryAsRecord(previousSettings, category);
251
249
  const newCat = getCategoryAsRecord(newSettings, category);
@@ -308,11 +306,11 @@ export function setTheme(newTheme) {
308
306
  export function toggleTheme() {
309
307
  const currentTheme = get(theme);
310
308
  const currentResolved = get(resolvedTheme);
311
- if (currentTheme === "auto") {
312
- setTheme(currentResolved === "dark" ? "light" : "dark");
309
+ if (currentTheme === 'auto') {
310
+ setTheme(currentResolved === 'dark' ? 'light' : 'dark');
313
311
  }
314
312
  else {
315
- setTheme(currentTheme === "dark" ? "light" : "dark");
313
+ setTheme(currentTheme === 'dark' ? 'light' : 'dark');
316
314
  }
317
315
  }
318
316
  /**
@@ -321,14 +319,14 @@ export function toggleTheme() {
321
319
  export function cycleTheme() {
322
320
  const currentTheme = get(theme);
323
321
  switch (currentTheme) {
324
- case "light":
325
- setTheme("dark");
322
+ case 'light':
323
+ setTheme('dark');
326
324
  break;
327
- case "dark":
328
- setTheme("auto");
325
+ case 'dark':
326
+ setTheme('auto');
329
327
  break;
330
- case "auto":
331
- setTheme("light");
328
+ case 'auto':
329
+ setTheme('light');
332
330
  break;
333
331
  }
334
332
  }
@@ -338,10 +336,10 @@ export function cycleTheme() {
338
336
  * @param resolved - The resolved theme to apply
339
337
  */
340
338
  function applyTheme(resolved) {
341
- if (typeof document === "undefined") {
339
+ if (typeof document === 'undefined') {
342
340
  return;
343
341
  }
344
- document.documentElement.setAttribute("data-theme", resolved);
342
+ document.documentElement.setAttribute('data-theme', resolved);
345
343
  }
346
344
  /**
347
345
  * Initialize the theme system
@@ -373,12 +371,12 @@ export function setSettingsService(service) {
373
371
  */
374
372
  export async function syncSettingsToApi() {
375
373
  if (!settingsService) {
376
- console.warn("Settings service not configured for API sync");
374
+ console.warn('Settings service not configured for API sync');
377
375
  return;
378
376
  }
379
377
  storeState.update((state) => ({
380
378
  ...state,
381
- syncStatus: "syncing",
379
+ syncStatus: 'syncing',
382
380
  syncError: null
383
381
  }));
384
382
  try {
@@ -386,16 +384,16 @@ export async function syncSettingsToApi() {
386
384
  await settingsService.savePreferences(currentSettings);
387
385
  storeState.update((state) => ({
388
386
  ...state,
389
- syncStatus: "synced",
387
+ syncStatus: 'synced',
390
388
  lastSyncedAt: Date.now(),
391
389
  syncError: null
392
390
  }));
393
391
  }
394
392
  catch (error) {
395
- const errorMessage = error instanceof Error ? error.message : "Failed to sync settings";
393
+ const errorMessage = error instanceof Error ? error.message : 'Failed to sync settings';
396
394
  storeState.update((state) => ({
397
395
  ...state,
398
- syncStatus: "error",
396
+ syncStatus: 'error',
399
397
  syncError: errorMessage
400
398
  }));
401
399
  throw error;
@@ -408,12 +406,12 @@ export async function syncSettingsToApi() {
408
406
  */
409
407
  export async function loadSettingsFromApi() {
410
408
  if (!settingsService) {
411
- console.warn("Settings service not configured for API sync");
409
+ console.warn('Settings service not configured for API sync');
412
410
  return;
413
411
  }
414
412
  storeState.update((state) => ({
415
413
  ...state,
416
- syncStatus: "syncing",
414
+ syncStatus: 'syncing',
417
415
  syncError: null
418
416
  }));
419
417
  try {
@@ -422,7 +420,7 @@ export async function loadSettingsFromApi() {
422
420
  storeState.update((state) => ({
423
421
  ...state,
424
422
  settings: mergedSettings,
425
- syncStatus: "synced",
423
+ syncStatus: 'synced',
426
424
  lastSyncedAt: Date.now(),
427
425
  syncError: null
428
426
  }));
@@ -430,10 +428,10 @@ export async function loadSettingsFromApi() {
430
428
  saveToStorage(mergedSettings);
431
429
  }
432
430
  catch (error) {
433
- const errorMessage = error instanceof Error ? error.message : "Failed to load settings from API";
431
+ const errorMessage = error instanceof Error ? error.message : 'Failed to load settings from API';
434
432
  storeState.update((state) => ({
435
433
  ...state,
436
- syncStatus: "error",
434
+ syncStatus: 'error',
437
435
  syncError: errorMessage
438
436
  }));
439
437
  throw error;
@@ -482,7 +480,7 @@ export async function initializeSettings(options) {
482
480
  }
483
481
  catch {
484
482
  // Silently fail - local settings are still available
485
- console.warn("Failed to sync settings from API on initialization");
483
+ console.warn('Failed to sync settings from API on initialization');
486
484
  }
487
485
  }
488
486
  }
@@ -21,9 +21,9 @@
21
21
  * @module stores/themeStore
22
22
  */
23
23
  /** Theme preference options */
24
- export type ThemePreference = "light" | "dark" | "auto";
24
+ export type ThemePreference = 'light' | 'dark' | 'auto';
25
25
  /** Resolved theme (actual applied theme, never 'auto') */
26
- export type ResolvedTheme = "light" | "dark";
26
+ export type ResolvedTheme = 'light' | 'dark';
27
27
  /**
28
28
  * User's theme preference store
29
29
  * Can be 'light', 'dark', or 'auto'
@@ -20,11 +20,11 @@
20
20
  *
21
21
  * @module stores/themeStore
22
22
  */
23
- import { writable, derived, get } from "svelte/store";
23
+ import { writable, derived, get } from 'svelte/store';
24
24
  /** localStorage key for persisting theme preference */
25
- const STORAGE_KEY = "flowdrop-theme";
25
+ const STORAGE_KEY = 'flowdrop-theme';
26
26
  /** Default theme preference when none is stored */
27
- const DEFAULT_THEME = "auto";
27
+ const DEFAULT_THEME = 'auto';
28
28
  // =========================================================================
29
29
  // System Preference Detection
30
30
  // =========================================================================
@@ -34,30 +34,28 @@ const DEFAULT_THEME = "auto";
34
34
  * @returns 'dark' if system prefers dark mode, 'light' otherwise
35
35
  */
36
36
  function getSystemTheme() {
37
- if (typeof window === "undefined") {
38
- return "light";
37
+ if (typeof window === 'undefined') {
38
+ return 'light';
39
39
  }
40
- return window.matchMedia("(prefers-color-scheme: dark)").matches
41
- ? "dark"
42
- : "light";
40
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
43
41
  }
44
42
  /**
45
43
  * Store for system theme preference
46
44
  * Updates when system preference changes
47
45
  */
48
- const systemTheme = writable(typeof window !== "undefined" ? getSystemTheme() : "light");
46
+ const systemTheme = writable(typeof window !== 'undefined' ? getSystemTheme() : 'light');
49
47
  // Listen for system theme changes
50
- if (typeof window !== "undefined") {
51
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
48
+ if (typeof window !== 'undefined') {
49
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
52
50
  /**
53
51
  * Handler for system theme preference changes
54
52
  */
55
53
  const handleSystemThemeChange = (event) => {
56
- systemTheme.set(event.matches ? "dark" : "light");
54
+ systemTheme.set(event.matches ? 'dark' : 'light');
57
55
  };
58
56
  // Modern browsers use addEventListener
59
57
  if (mediaQuery.addEventListener) {
60
- mediaQuery.addEventListener("change", handleSystemThemeChange);
58
+ mediaQuery.addEventListener('change', handleSystemThemeChange);
61
59
  }
62
60
  else {
63
61
  // Fallback for older browsers
@@ -73,18 +71,18 @@ if (typeof window !== "undefined") {
73
71
  * @returns Saved theme preference or default
74
72
  */
75
73
  function loadSavedTheme() {
76
- if (typeof window === "undefined") {
74
+ if (typeof window === 'undefined') {
77
75
  return DEFAULT_THEME;
78
76
  }
79
77
  try {
80
78
  const saved = localStorage.getItem(STORAGE_KEY);
81
- if (saved === "light" || saved === "dark" || saved === "auto") {
79
+ if (saved === 'light' || saved === 'dark' || saved === 'auto') {
82
80
  return saved;
83
81
  }
84
82
  }
85
83
  catch {
86
84
  // localStorage may be unavailable (e.g., private browsing)
87
- console.warn("Failed to load theme from localStorage");
85
+ console.warn('Failed to load theme from localStorage');
88
86
  }
89
87
  return DEFAULT_THEME;
90
88
  }
@@ -94,7 +92,7 @@ function loadSavedTheme() {
94
92
  * @param theme - Theme preference to save
95
93
  */
96
94
  function saveTheme(theme) {
97
- if (typeof window === "undefined") {
95
+ if (typeof window === 'undefined') {
98
96
  return;
99
97
  }
100
98
  try {
@@ -102,7 +100,7 @@ function saveTheme(theme) {
102
100
  }
103
101
  catch {
104
102
  // localStorage may be unavailable
105
- console.warn("Failed to save theme to localStorage");
103
+ console.warn('Failed to save theme to localStorage');
106
104
  }
107
105
  }
108
106
  /**
@@ -119,7 +117,7 @@ export const theme = writable(loadSavedTheme());
119
117
  * When theme is 'auto', this reflects the system preference
120
118
  */
121
119
  export const resolvedTheme = derived([theme, systemTheme], ([$theme, $systemTheme]) => {
122
- if ($theme === "auto") {
120
+ if ($theme === 'auto') {
123
121
  return $systemTheme;
124
122
  }
125
123
  return $theme;
@@ -134,10 +132,10 @@ export const resolvedTheme = derived([theme, systemTheme], ([$theme, $systemThem
134
132
  * @param resolved - The resolved theme to apply
135
133
  */
136
134
  function applyTheme(resolved) {
137
- if (typeof document === "undefined") {
135
+ if (typeof document === 'undefined') {
138
136
  return;
139
137
  }
140
- document.documentElement.setAttribute("data-theme", resolved);
138
+ document.documentElement.setAttribute('data-theme', resolved);
141
139
  }
142
140
  /**
143
141
  * Set the theme preference
@@ -155,13 +153,13 @@ export function setTheme(newTheme) {
155
153
  export function toggleTheme() {
156
154
  const currentTheme = get(theme);
157
155
  const currentResolved = get(resolvedTheme);
158
- if (currentTheme === "auto") {
156
+ if (currentTheme === 'auto') {
159
157
  // Switch to opposite of system preference
160
- setTheme(currentResolved === "dark" ? "light" : "dark");
158
+ setTheme(currentResolved === 'dark' ? 'light' : 'dark');
161
159
  }
162
160
  else {
163
161
  // Toggle between light and dark
164
- setTheme(currentTheme === "dark" ? "light" : "dark");
162
+ setTheme(currentTheme === 'dark' ? 'light' : 'dark');
165
163
  }
166
164
  }
167
165
  /**
@@ -170,14 +168,14 @@ export function toggleTheme() {
170
168
  export function cycleTheme() {
171
169
  const currentTheme = get(theme);
172
170
  switch (currentTheme) {
173
- case "light":
174
- setTheme("dark");
171
+ case 'light':
172
+ setTheme('dark');
175
173
  break;
176
- case "dark":
177
- setTheme("auto");
174
+ case 'dark':
175
+ setTheme('auto');
178
176
  break;
179
- case "auto":
180
- setTheme("light");
177
+ case 'auto':
178
+ setTheme('light');
181
179
  break;
182
180
  }
183
181
  }
@@ -208,8 +206,8 @@ export function initializeTheme() {
208
206
  * @returns true if running in browser and theme is applied
209
207
  */
210
208
  export function isThemeInitialized() {
211
- if (typeof document === "undefined") {
209
+ if (typeof document === 'undefined') {
212
210
  return false;
213
211
  }
214
- return document.documentElement.hasAttribute("data-theme");
212
+ return document.documentElement.hasAttribute('data-theme');
215
213
  }