@d34dman/flowdrop 0.0.52 → 0.0.53

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.
@@ -738,6 +738,7 @@
738
738
  onClose={() => (isWorkflowSettingsOpen = false)}
739
739
  >
740
740
  <ConfigForm
741
+ {authProvider}
741
742
  schema={workflowConfigSchema}
742
743
  values={workflowConfigValues}
743
744
  showUIExtensions={false}
@@ -765,6 +766,7 @@
765
766
  onClose={closeConfigSidebar}
766
767
  >
767
768
  <ConfigForm
769
+ {authProvider}
768
770
  node={currentNode}
769
771
  workflowId={$workflowStore?.id}
770
772
  workflowNodes={$workflowStore?.nodes}
@@ -600,6 +600,7 @@
600
600
  ariaDescribedBy="ext-hideUnconnectedHandles-description"
601
601
  onChange={(val) => {
602
602
  uiExtensionValues.hideUnconnectedHandles = val;
603
+ handleFormBlur();
603
604
  }}
604
605
  />
605
606
  </FormFieldWrapper>
@@ -144,8 +144,7 @@
144
144
  proximityConnect: {
145
145
  type: 'boolean',
146
146
  title: 'Proximity Connect',
147
- description:
148
- 'Auto-connect compatible ports when dragging nodes near each other',
147
+ description: 'Auto-connect compatible ports when dragging nodes near each other',
149
148
  default: false
150
149
  },
151
150
  proximityConnectDistance: {
@@ -7,7 +7,7 @@
7
7
 
8
8
  <script lang="ts">
9
9
  import Icon from '@iconify/svelte';
10
- import { theme, resolvedTheme, cycleTheme } from '../stores/themeStore.js';
10
+ import { theme, resolvedTheme, cycleTheme } from '../stores/settingsStore.js';
11
11
  import type { ThemePreference } from '../types/settings.js';
12
12
 
13
13
  /**
@@ -16,8 +16,7 @@
16
16
  type ColorMode
17
17
  } from '@xyflow/svelte';
18
18
  import '@xyflow/svelte/dist/style.css';
19
- import { resolvedTheme } from '../stores/themeStore.js';
20
- import { editorSettings, behaviorSettings } from '../stores/settingsStore.js';
19
+ import { resolvedTheme, editorSettings, behaviorSettings } from '../stores/settingsStore.js';
21
20
  import type {
22
21
  WorkflowNode as WorkflowNodeType,
23
22
  NodeMetadata,
@@ -25,6 +25,7 @@
25
25
  import Icon from '@iconify/svelte';
26
26
  import type { AutocompleteConfig, AuthProvider } from '../../types/index.js';
27
27
  import type { FieldOption } from './types.js';
28
+ import { buildFetchHeaders } from '../../utils/fetchWithAuth.js';
28
29
 
29
30
  /**
30
31
  * Props interface for FormAutocomplete component
@@ -192,18 +193,8 @@
192
193
  abortController = new AbortController();
193
194
 
194
195
  try {
195
- // Build headers with authentication
196
- const headers: Record<string, string> = {
197
- Accept: 'application/json',
198
- 'Content-Type': 'application/json'
199
- };
200
-
201
- // Add auth headers if provider is available (call getter to get current value)
202
- const authProvider = getAuthProvider?.();
203
- if (authProvider) {
204
- const authHeaders = await authProvider.getAuthHeaders();
205
- Object.assign(headers, authHeaders);
206
- }
196
+ // Build headers with authentication (call getter to get current value)
197
+ const headers = await buildFetchHeaders(getAuthProvider?.());
207
198
 
208
199
  // Fetch with timeout
209
200
  const timeoutId = setTimeout(() => {
@@ -44,7 +44,7 @@
44
44
  import type { FieldSchema } from './types.js';
45
45
  import { getSchemaOptions } from './types.js';
46
46
  import type { WorkflowNode, WorkflowEdge, AuthProvider } from '../../types/index.js';
47
- import { resolvedTheme } from '../../stores/themeStore.js';
47
+ import { resolvedTheme } from '../../stores/settingsStore.js';
48
48
 
49
49
  interface Props {
50
50
  /** Unique key/id for the field */
@@ -43,7 +43,7 @@
43
43
  import FormCheckboxGroup from './FormCheckboxGroup.svelte';
44
44
  import FormArray from './FormArray.svelte';
45
45
  import { resolveFieldComponent } from '../../form/fieldRegistry.js';
46
- import { resolvedTheme } from '../../stores/themeStore.js';
46
+ import { resolvedTheme } from '../../stores/settingsStore.js';
47
47
  import type { FieldSchema } from './types.js';
48
48
  import { getSchemaOptions } from './types.js';
49
49
 
@@ -10,11 +10,12 @@
10
10
 
11
11
  <script lang="ts">
12
12
  import { Position, Handle } from '@xyflow/svelte';
13
- import type { ConfigValues, NodeMetadata } from '../../types/index.js';
13
+ import type { ConfigValues, NodeMetadata, NodeExtensions, NodePort } from '../../types/index.js';
14
14
  import Icon from '@iconify/svelte';
15
15
  import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors.js';
16
16
  import { getNodeIcon } from '../../utils/icons.js';
17
17
  import { getCircleHandlePosition } from '../../utils/handlePositioning.js';
18
+ import { connectedHandles } from '../../stores/workflowStore.js';
18
19
 
19
20
  /**
20
21
  * Terminal node variant types
@@ -70,6 +71,7 @@
70
71
  config: ConfigValues;
71
72
  metadata: NodeMetadata;
72
73
  nodeId?: string;
74
+ extensions?: NodeExtensions;
73
75
  onConfigOpen?: (node: {
74
76
  id: string;
75
77
  type: string;
@@ -133,6 +135,30 @@
133
135
  */
134
136
  let variantConfig = $derived(VARIANT_CONFIGS[variant]);
135
137
 
138
+ /**
139
+ * Get the hideUnconnectedHandles setting from extensions
140
+ * Merges node type defaults with instance overrides
141
+ */
142
+ const hideUnconnectedHandles = $derived(() => {
143
+ const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
144
+ const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
145
+ return instanceOverride ?? typeDefault;
146
+ });
147
+
148
+ /**
149
+ * Check if a port should be visible based on connection state and settings
150
+ */
151
+ function isPortVisible(port: NodePort, type: 'input' | 'output'): boolean {
152
+ if (!hideUnconnectedHandles()) {
153
+ return true;
154
+ }
155
+ if (port.required) {
156
+ return true;
157
+ }
158
+ const handleId = `${props.data.nodeId}-${type}-${port.id}`;
159
+ return $connectedHandles.has(handleId);
160
+ }
161
+
136
162
  /**
137
163
  * Get icon using the same resolution as WorkflowNode
138
164
  * Uses getNodeIcon utility with category fallback, or variant default
@@ -239,14 +265,24 @@
239
265
  );
240
266
 
241
267
  /**
242
- * Determine if we should show inputs based on ports
268
+ * Visible input ports filtered by hideUnconnectedHandles setting
269
+ */
270
+ let visibleInputPorts = $derived(inputPorts.filter((port) => isPortVisible(port, 'input')));
271
+
272
+ /**
273
+ * Visible output ports filtered by hideUnconnectedHandles setting
274
+ */
275
+ let visibleOutputPorts = $derived(outputPorts.filter((port) => isPortVisible(port, 'output')));
276
+
277
+ /**
278
+ * Determine if we should show inputs based on visible ports
243
279
  */
244
- let showInputs = $derived(inputPorts.length > 0);
280
+ let showInputs = $derived(visibleInputPorts.length > 0);
245
281
 
246
282
  /**
247
- * Determine if we should show outputs based on ports
283
+ * Determine if we should show outputs based on visible ports
248
284
  */
249
- let showOutputs = $derived(outputPorts.length > 0);
285
+ let showOutputs = $derived(visibleOutputPorts.length > 0);
250
286
 
251
287
  /**
252
288
  * Handle configuration sidebar - using global ConfigSidebar
@@ -317,8 +353,8 @@
317
353
  <div class="flowdrop-terminal-node__circle-wrapper">
318
354
  <!-- Input Handles (for end/exit variants) -->
319
355
  {#if showInputs}
320
- {#each inputPorts as port, index (port.id)}
321
- {@const pos = getCircleHandlePosition(index, inputPorts.length, 'left')}
356
+ {#each visibleInputPorts as port, index (`${port.id}-${visibleInputPorts.length}`)}
357
+ {@const pos = getCircleHandlePosition(index, visibleInputPorts.length, 'left')}
322
358
  <Handle
323
359
  type="target"
324
360
  position={Position.Left}
@@ -339,8 +375,8 @@
339
375
 
340
376
  <!-- Output Handles (for start variant) -->
341
377
  {#if showOutputs}
342
- {#each outputPorts as port, index (port.id)}
343
- {@const pos = getCircleHandlePosition(index, outputPorts.length, 'right')}
378
+ {#each visibleOutputPorts as port, index (`${port.id}-${visibleOutputPorts.length}`)}
379
+ {@const pos = getCircleHandlePosition(index, visibleOutputPorts.length, 'right')}
344
380
  <Handle
345
381
  type="source"
346
382
  position={Position.Right}
@@ -1,10 +1,11 @@
1
- import type { ConfigValues, NodeMetadata } from '../../types/index.js';
1
+ import type { ConfigValues, NodeMetadata, NodeExtensions } from '../../types/index.js';
2
2
  type $$ComponentProps = {
3
3
  data: {
4
4
  label: string;
5
5
  config: ConfigValues;
6
6
  metadata: NodeMetadata;
7
7
  nodeId?: string;
8
+ extensions?: NodeExtensions;
8
9
  onConfigOpen?: (node: {
9
10
  id: string;
10
11
  type: string;
@@ -67,9 +67,7 @@
67
67
  * This allows users to customize the badge text per-instance via config.
68
68
  */
69
69
  const displayBadge = $derived(
70
- (props.data.config?.instanceBadge as string) ||
71
- (props.data.metadata?.badge as string) ||
72
- 'TOOL'
70
+ (props.data.config?.instanceBadge as string) || (props.data.metadata?.badge as string) || 'TOOL'
73
71
  );
74
72
 
75
73
  /**
@@ -96,20 +94,28 @@
96
94
  */
97
95
  let nodeStyle = $derived(`--fd-tool-node-color: ${toolColor}`);
98
96
 
99
- // Check for tool interface ports in metadata
97
+ /**
98
+ * Configurable port dataType to expose on this tool node.
99
+ * Defaults to 'tool', but can be overridden via metadata.portDataType
100
+ * to show a different port type (e.g., 'trigger') when the node is
101
+ * repurposed with a custom badge.
102
+ */
103
+ let portDataType = $derived((props.data.metadata?.portDataType as string) || 'tool');
104
+
105
+ // Check for matching interface ports in metadata
100
106
  let hasToolInputPort = $derived(
101
- props.data.metadata?.inputs?.some((port) => port.dataType === 'tool') || false
107
+ props.data.metadata?.inputs?.some((port) => port.dataType === portDataType) || false
102
108
  );
103
109
  let hasToolOutputPort = $derived(
104
- props.data.metadata?.outputs?.some((port) => port.dataType === 'tool') || false
110
+ props.data.metadata?.outputs?.some((port) => port.dataType === portDataType) || false
105
111
  );
106
112
 
107
- // Get the actual tool ports for proper handle generation
113
+ // Get the actual matching ports for proper handle generation
108
114
  let toolInputPort = $derived(
109
- props.data.metadata?.inputs?.find((port) => port.dataType === 'tool')
115
+ props.data.metadata?.inputs?.find((port) => port.dataType === portDataType)
110
116
  );
111
117
  let toolOutputPort = $derived(
112
- props.data.metadata?.outputs?.find((port) => port.dataType === 'tool')
118
+ props.data.metadata?.outputs?.find((port) => port.dataType === portDataType)
113
119
  );
114
120
 
115
121
  /**
@@ -159,7 +165,7 @@
159
165
  position={Position.Left}
160
166
  id={`${props.data.nodeId}-input-${toolInputPort.id}`}
161
167
  style="top: 40px; transform: translateY(-50%); margin-left: -10px; --fd-handle-fill: {getDataTypeColor(
162
- 'tool'
168
+ portDataType
163
169
  )}; --fd-handle-border-color: var(--fd-handle-border);"
164
170
  />
165
171
  {/if}
@@ -232,7 +238,7 @@
232
238
  position={Position.Right}
233
239
  id={`${props.data.nodeId}-output-${toolOutputPort.id}`}
234
240
  style="top: 40px; transform: translateY(-50%); margin-right: -10px; --fd-handle-fill: {getDataTypeColor(
235
- 'tool'
241
+ portDataType
236
242
  )}; --fd-handle-border-color: var(--fd-handle-border);"
237
243
  />
238
244
  {/if}
@@ -40,5 +40,5 @@ export { isFieldOptionArray, normalizeOptions } from '../components/form/types.j
40
40
  export { DEFAULT_PORT_CONFIG } from '../config/defaultPortConfig.js';
41
41
  export { defaultEndpointConfig, createEndpointConfig } from '../config/endpoints.js';
42
42
  export * from '../adapters/WorkflowAdapter.js';
43
- export type { ThemePreference, ResolvedTheme } from '../stores/themeStore.js';
44
- export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/themeStore.js';
43
+ export type { ThemePreference, ResolvedTheme } from '../stores/settingsStore.js';
44
+ export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/settingsStore.js';
@@ -51,4 +51,4 @@ export { defaultEndpointConfig, createEndpointConfig } from '../config/endpoints
51
51
  // Adapters
52
52
  // ============================================================================
53
53
  export * from '../adapters/WorkflowAdapter.js';
54
- export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/themeStore.js';
54
+ export { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme, initializeTheme, isThemeInitialized } from '../stores/settingsStore.js';
@@ -5,7 +5,8 @@
5
5
  * preferences with hybrid persistence (localStorage + optional API sync).
6
6
  *
7
7
  * Theme stores and functions (theme, resolvedTheme, setTheme, toggleTheme,
8
- * cycleTheme, initializeTheme) are exported from `@d34dman/flowdrop/core`.
8
+ * cycleTheme, initializeTheme) are exported from `@d34dman/flowdrop/core`
9
+ * (sourced from settingsStore).
9
10
  *
10
11
  * @module settings
11
12
  *
@@ -5,7 +5,8 @@
5
5
  * preferences with hybrid persistence (localStorage + optional API sync).
6
6
  *
7
7
  * Theme stores and functions (theme, resolvedTheme, setTheme, toggleTheme,
8
- * cycleTheme, initializeTheme) are exported from `@d34dman/flowdrop/core`.
8
+ * cycleTheme, initializeTheme) are exported from `@d34dman/flowdrop/core`
9
+ * (sourced from settingsStore).
9
10
  *
10
11
  * @module settings
11
12
  *
@@ -5,11 +5,12 @@
5
5
  * - Hybrid persistence (localStorage primary, optional API sync)
6
6
  * - Category-specific derived stores for performance
7
7
  * - Deep merge support for partial updates
8
- * - Theme system compatibility (backward compatible with themeStore)
8
+ * - Integrated theme system with system preference detection
9
9
  *
10
10
  * @module stores/settingsStore
11
11
  */
12
- import type { FlowDropSettings, ThemeSettings, EditorSettings, UISettings, BehaviorSettings, ApiSettings, PartialSettings, SyncStatus, 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
+ export type { ThemePreference, ResolvedTheme } from '../types/settings.js';
13
14
  /**
14
15
  * Main settings store (read-only access to current settings)
15
16
  */
@@ -42,6 +43,16 @@ export declare const behaviorSettings: import("svelte/store").Readable<BehaviorS
42
43
  * API settings store
43
44
  */
44
45
  export declare const apiSettings: import("svelte/store").Readable<ApiSettings>;
46
+ /**
47
+ * Theme preference store
48
+ * Derived from themeSettings for convenient access
49
+ */
50
+ export declare const theme: import("svelte/store").Readable<ThemePreference>;
51
+ /**
52
+ * Resolved theme - the actual theme applied ('light' or 'dark')
53
+ * When preference is 'auto', resolves based on system preference
54
+ */
55
+ export declare const resolvedTheme: import("svelte/store").Readable<ResolvedTheme>;
45
56
  /**
46
57
  * Update settings with partial values
47
58
  *
@@ -58,6 +69,37 @@ export declare function resetSettings(categories?: SettingsCategory[]): void;
58
69
  * Get current settings synchronously
59
70
  */
60
71
  export declare function getSettings(): FlowDropSettings;
72
+ /**
73
+ * Set the theme preference
74
+ *
75
+ * @param newTheme - The new theme preference ('light', 'dark', or 'auto')
76
+ */
77
+ export declare function setTheme(newTheme: ThemePreference): void;
78
+ /**
79
+ * Toggle between light and dark themes
80
+ * If currently 'auto', switches to the opposite of system preference
81
+ */
82
+ export declare function toggleTheme(): void;
83
+ /**
84
+ * Cycle through theme options: light -> dark -> auto -> light
85
+ */
86
+ export declare function cycleTheme(): void;
87
+ /**
88
+ * Initialize the theme system
89
+ * Should be called once on app startup
90
+ *
91
+ * This function:
92
+ * 1. Applies the current resolved theme to the document
93
+ * 2. Sets up reactivity to apply theme changes
94
+ */
95
+ export declare function initializeTheme(): void;
96
+ /**
97
+ * Check if theme system is initialized
98
+ * Useful for SSR scenarios
99
+ *
100
+ * @returns true if running in browser and theme is applied
101
+ */
102
+ export declare function isThemeInitialized(): boolean;
61
103
  /**
62
104
  * Set the settings service for API operations
63
105
  *
@@ -5,7 +5,7 @@
5
5
  * - Hybrid persistence (localStorage primary, optional API sync)
6
6
  * - Category-specific derived stores for performance
7
7
  * - Deep merge support for partial updates
8
- * - Theme system compatibility (backward compatible with themeStore)
8
+ * - Integrated theme system with system preference detection
9
9
  *
10
10
  * @module stores/settingsStore
11
11
  */
@@ -157,7 +157,7 @@ export const behaviorSettings = derived(settingsStore, ($settings) => $settings.
157
157
  */
158
158
  export const apiSettings = derived(settingsStore, ($settings) => $settings.api);
159
159
  // =========================================================================
160
- // Theme Compatibility (for themeStore migration)
160
+ // Theme System
161
161
  // =========================================================================
162
162
  /**
163
163
  * System theme preference store
@@ -188,13 +188,15 @@ if (typeof window !== 'undefined') {
188
188
  }
189
189
  }
190
190
  /**
191
- * Theme preference (internal - exported via core/themeStore)
191
+ * Theme preference store
192
+ * Derived from themeSettings for convenient access
192
193
  */
193
- const theme = derived(themeSettings, ($theme) => $theme.preference);
194
+ export const theme = derived(themeSettings, ($theme) => $theme.preference);
194
195
  /**
195
- * Resolved theme (internal - exported via core/themeStore)
196
+ * Resolved theme - the actual theme applied ('light' or 'dark')
197
+ * When preference is 'auto', resolves based on system preference
196
198
  */
197
- const resolvedTheme = derived([themeSettings, systemTheme], ([$themeSettings, $systemTheme]) => {
199
+ export const resolvedTheme = derived([themeSettings, systemTheme], ([$themeSettings, $systemTheme]) => {
198
200
  if ($themeSettings.preference === 'auto') {
199
201
  return $systemTheme;
200
202
  }
@@ -288,18 +290,21 @@ export function getSettings() {
288
290
  return get(settingsStore);
289
291
  }
290
292
  // =========================================================================
291
- // Theme-Specific Functions (backward compatible with themeStore)
293
+ // Theme Actions
292
294
  // =========================================================================
293
295
  /**
294
- * Set the theme preference (internal - exported via core/themeStore)
296
+ * Set the theme preference
297
+ *
298
+ * @param newTheme - The new theme preference ('light', 'dark', or 'auto')
295
299
  */
296
- function setTheme(newTheme) {
300
+ export function setTheme(newTheme) {
297
301
  updateSettings({ theme: { preference: newTheme } });
298
302
  }
299
303
  /**
300
- * Toggle between light and dark themes (internal - exported via core/themeStore)
304
+ * Toggle between light and dark themes
305
+ * If currently 'auto', switches to the opposite of system preference
301
306
  */
302
- function toggleTheme() {
307
+ export function toggleTheme() {
303
308
  const currentTheme = get(theme);
304
309
  const currentResolved = get(resolvedTheme);
305
310
  if (currentTheme === 'auto') {
@@ -310,9 +315,9 @@ function toggleTheme() {
310
315
  }
311
316
  }
312
317
  /**
313
- * Cycle through theme options (internal - exported via core/themeStore)
318
+ * Cycle through theme options: light -> dark -> auto -> light
314
319
  */
315
- function cycleTheme() {
320
+ export function cycleTheme() {
316
321
  const currentTheme = get(theme);
317
322
  switch (currentTheme) {
318
323
  case 'light':
@@ -338,9 +343,14 @@ function applyTheme(resolved) {
338
343
  document.documentElement.setAttribute('data-theme', resolved);
339
344
  }
340
345
  /**
341
- * Initialize the theme system (internal - exported via core/themeStore)
346
+ * Initialize the theme system
347
+ * Should be called once on app startup
348
+ *
349
+ * This function:
350
+ * 1. Applies the current resolved theme to the document
351
+ * 2. Sets up reactivity to apply theme changes
342
352
  */
343
- function initializeTheme() {
353
+ export function initializeTheme() {
344
354
  const resolved = get(resolvedTheme);
345
355
  applyTheme(resolved);
346
356
  // Subscribe to resolved theme changes and apply them
@@ -348,6 +358,18 @@ function initializeTheme() {
348
358
  applyTheme(theme);
349
359
  });
350
360
  }
361
+ /**
362
+ * Check if theme system is initialized
363
+ * Useful for SSR scenarios
364
+ *
365
+ * @returns true if running in browser and theme is applied
366
+ */
367
+ export function isThemeInitialized() {
368
+ if (typeof document === 'undefined') {
369
+ return false;
370
+ }
371
+ return document.documentElement.hasAttribute('data-theme');
372
+ }
351
373
  // =========================================================================
352
374
  // API Sync Functions
353
375
  // =========================================================================
@@ -511,6 +511,8 @@ export interface NodeMetadata {
511
511
  color?: string;
512
512
  /** Badge label displayed in the node header (e.g., "TOOL", "API", "LLM"). Overridable per-instance via config.instanceBadge. */
513
513
  badge?: string;
514
+ /** Port dataType to expose on tool nodes. Defaults to 'tool'. Set to another type (e.g., 'trigger') to show that port instead. */
515
+ portDataType?: string;
514
516
  inputs: NodePort[];
515
517
  outputs: NodePort[];
516
518
  configSchema?: ConfigSchema;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Fetch authentication utilities
3
+ *
4
+ * Shared logic for building HTTP headers with authentication.
5
+ * Used by components that make authenticated API requests (e.g., FormAutocomplete).
6
+ *
7
+ * @module utils/fetchWithAuth
8
+ */
9
+ import type { AuthProvider } from '../types/auth.js';
10
+ /**
11
+ * Build fetch headers with optional authentication
12
+ *
13
+ * Constructs standard JSON request headers and merges in authentication
14
+ * headers from the provided AuthProvider, if available.
15
+ *
16
+ * @param authProvider - Optional auth provider to get headers from
17
+ * @returns Promise resolving to a complete headers object
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const headers = await buildFetchHeaders(authProvider);
22
+ * const response = await fetch(url, { headers });
23
+ * ```
24
+ */
25
+ export declare function buildFetchHeaders(authProvider?: AuthProvider): Promise<Record<string, string>>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Fetch authentication utilities
3
+ *
4
+ * Shared logic for building HTTP headers with authentication.
5
+ * Used by components that make authenticated API requests (e.g., FormAutocomplete).
6
+ *
7
+ * @module utils/fetchWithAuth
8
+ */
9
+ /**
10
+ * Build fetch headers with optional authentication
11
+ *
12
+ * Constructs standard JSON request headers and merges in authentication
13
+ * headers from the provided AuthProvider, if available.
14
+ *
15
+ * @param authProvider - Optional auth provider to get headers from
16
+ * @returns Promise resolving to a complete headers object
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const headers = await buildFetchHeaders(authProvider);
21
+ * const response = await fetch(url, { headers });
22
+ * ```
23
+ */
24
+ export async function buildFetchHeaders(authProvider) {
25
+ const headers = {
26
+ Accept: 'application/json',
27
+ 'Content-Type': 'application/json'
28
+ };
29
+ if (authProvider) {
30
+ const authHeaders = await authProvider.getAuthHeaders();
31
+ Object.assign(headers, authHeaders);
32
+ }
33
+ return headers;
34
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@d34dman/flowdrop",
3
3
  "license": "MIT",
4
4
  "private": false,
5
- "version": "0.0.52",
5
+ "version": "0.0.53",
6
6
  "scripts": {
7
7
  "dev": "vite dev",
8
8
  "build": "vite build && npm run prepack",
@@ -1,68 +0,0 @@
1
- /**
2
- * Theme Store for FlowDrop
3
- *
4
- * @deprecated This module is deprecated. Use `settingsStore` instead.
5
- *
6
- * The theme functionality has been moved to the unified settings system.
7
- * Import from settingsStore for new code:
8
- *
9
- * ```typescript
10
- * import { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme } from "./settingsStore.js";
11
- * ```
12
- *
13
- * This module re-exports from settingsStore for backward compatibility.
14
- *
15
- * Provides reactive theme state management with:
16
- * - Three theme modes: 'light', 'dark', 'auto' (follows system preference)
17
- * - localStorage persistence
18
- * - System preference detection via matchMedia
19
- * - Automatic data-theme attribute application
20
- *
21
- * @module stores/themeStore
22
- */
23
- /** Theme preference options */
24
- export type ThemePreference = 'light' | 'dark' | 'auto';
25
- /** Resolved theme (actual applied theme, never 'auto') */
26
- export type ResolvedTheme = 'light' | 'dark';
27
- /**
28
- * User's theme preference store
29
- * Can be 'light', 'dark', or 'auto'
30
- */
31
- export declare const theme: import("svelte/store").Writable<ThemePreference>;
32
- /**
33
- * Resolved theme store
34
- * Always returns the actual theme being applied ('light' or 'dark')
35
- * When theme is 'auto', this reflects the system preference
36
- */
37
- export declare const resolvedTheme: import("svelte/store").Readable<ResolvedTheme>;
38
- /**
39
- * Set the theme preference
40
- *
41
- * @param newTheme - The new theme preference ('light', 'dark', or 'auto')
42
- */
43
- export declare function setTheme(newTheme: ThemePreference): void;
44
- /**
45
- * Toggle between light and dark themes
46
- * If currently 'auto', switches to the opposite of system preference
47
- */
48
- export declare function toggleTheme(): void;
49
- /**
50
- * Cycle through theme options: light -> dark -> auto -> light
51
- */
52
- export declare function cycleTheme(): void;
53
- /**
54
- * Initialize the theme system
55
- * Should be called once on app startup
56
- *
57
- * This function:
58
- * 1. Applies the current resolved theme to the document
59
- * 2. Sets up reactivity to apply theme changes
60
- */
61
- export declare function initializeTheme(): void;
62
- /**
63
- * Check if theme system is initialized
64
- * Useful for SSR scenarios
65
- *
66
- * @returns true if running in browser and theme is applied
67
- */
68
- export declare function isThemeInitialized(): boolean;
@@ -1,213 +0,0 @@
1
- /**
2
- * Theme Store for FlowDrop
3
- *
4
- * @deprecated This module is deprecated. Use `settingsStore` instead.
5
- *
6
- * The theme functionality has been moved to the unified settings system.
7
- * Import from settingsStore for new code:
8
- *
9
- * ```typescript
10
- * import { theme, resolvedTheme, setTheme, toggleTheme, cycleTheme } from "./settingsStore.js";
11
- * ```
12
- *
13
- * This module re-exports from settingsStore for backward compatibility.
14
- *
15
- * Provides reactive theme state management with:
16
- * - Three theme modes: 'light', 'dark', 'auto' (follows system preference)
17
- * - localStorage persistence
18
- * - System preference detection via matchMedia
19
- * - Automatic data-theme attribute application
20
- *
21
- * @module stores/themeStore
22
- */
23
- import { writable, derived, get } from 'svelte/store';
24
- /** localStorage key for persisting theme preference */
25
- const STORAGE_KEY = 'flowdrop-theme';
26
- /** Default theme preference when none is stored */
27
- const DEFAULT_THEME = 'auto';
28
- // =========================================================================
29
- // System Preference Detection
30
- // =========================================================================
31
- /**
32
- * Get the system's color scheme preference
33
- *
34
- * @returns 'dark' if system prefers dark mode, 'light' otherwise
35
- */
36
- function getSystemTheme() {
37
- if (typeof window === 'undefined') {
38
- return 'light';
39
- }
40
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
41
- }
42
- /**
43
- * Store for system theme preference
44
- * Updates when system preference changes
45
- */
46
- const systemTheme = writable(typeof window !== 'undefined' ? getSystemTheme() : 'light');
47
- // Listen for system theme changes
48
- if (typeof window !== 'undefined') {
49
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
50
- /**
51
- * Handler for system theme preference changes
52
- */
53
- const handleSystemThemeChange = (event) => {
54
- systemTheme.set(event.matches ? 'dark' : 'light');
55
- };
56
- // Modern browsers use addEventListener
57
- if (mediaQuery.addEventListener) {
58
- mediaQuery.addEventListener('change', handleSystemThemeChange);
59
- }
60
- else {
61
- // Fallback for older browsers
62
- mediaQuery.addListener(handleSystemThemeChange);
63
- }
64
- }
65
- // =========================================================================
66
- // Theme Preference Store
67
- // =========================================================================
68
- /**
69
- * Load saved theme preference from localStorage
70
- *
71
- * @returns Saved theme preference or default
72
- */
73
- function loadSavedTheme() {
74
- if (typeof window === 'undefined') {
75
- return DEFAULT_THEME;
76
- }
77
- try {
78
- const saved = localStorage.getItem(STORAGE_KEY);
79
- if (saved === 'light' || saved === 'dark' || saved === 'auto') {
80
- return saved;
81
- }
82
- }
83
- catch {
84
- // localStorage may be unavailable (e.g., private browsing)
85
- console.warn('Failed to load theme from localStorage');
86
- }
87
- return DEFAULT_THEME;
88
- }
89
- /**
90
- * Save theme preference to localStorage
91
- *
92
- * @param theme - Theme preference to save
93
- */
94
- function saveTheme(theme) {
95
- if (typeof window === 'undefined') {
96
- return;
97
- }
98
- try {
99
- localStorage.setItem(STORAGE_KEY, theme);
100
- }
101
- catch {
102
- // localStorage may be unavailable
103
- console.warn('Failed to save theme to localStorage');
104
- }
105
- }
106
- /**
107
- * User's theme preference store
108
- * Can be 'light', 'dark', or 'auto'
109
- */
110
- export const theme = writable(loadSavedTheme());
111
- // =========================================================================
112
- // Resolved Theme Store
113
- // =========================================================================
114
- /**
115
- * Resolved theme store
116
- * Always returns the actual theme being applied ('light' or 'dark')
117
- * When theme is 'auto', this reflects the system preference
118
- */
119
- export const resolvedTheme = derived([theme, systemTheme], ([$theme, $systemTheme]) => {
120
- if ($theme === 'auto') {
121
- return $systemTheme;
122
- }
123
- return $theme;
124
- });
125
- // =========================================================================
126
- // Theme Actions
127
- // =========================================================================
128
- /**
129
- * Apply theme to the document
130
- * Sets the data-theme attribute on the document element
131
- *
132
- * @param resolved - The resolved theme to apply
133
- */
134
- function applyTheme(resolved) {
135
- if (typeof document === 'undefined') {
136
- return;
137
- }
138
- document.documentElement.setAttribute('data-theme', resolved);
139
- }
140
- /**
141
- * Set the theme preference
142
- *
143
- * @param newTheme - The new theme preference ('light', 'dark', or 'auto')
144
- */
145
- export function setTheme(newTheme) {
146
- theme.set(newTheme);
147
- saveTheme(newTheme);
148
- }
149
- /**
150
- * Toggle between light and dark themes
151
- * If currently 'auto', switches to the opposite of system preference
152
- */
153
- export function toggleTheme() {
154
- const currentTheme = get(theme);
155
- const currentResolved = get(resolvedTheme);
156
- if (currentTheme === 'auto') {
157
- // Switch to opposite of system preference
158
- setTheme(currentResolved === 'dark' ? 'light' : 'dark');
159
- }
160
- else {
161
- // Toggle between light and dark
162
- setTheme(currentTheme === 'dark' ? 'light' : 'dark');
163
- }
164
- }
165
- /**
166
- * Cycle through theme options: light -> dark -> auto -> light
167
- */
168
- export function cycleTheme() {
169
- const currentTheme = get(theme);
170
- switch (currentTheme) {
171
- case 'light':
172
- setTheme('dark');
173
- break;
174
- case 'dark':
175
- setTheme('auto');
176
- break;
177
- case 'auto':
178
- setTheme('light');
179
- break;
180
- }
181
- }
182
- // =========================================================================
183
- // Initialization
184
- // =========================================================================
185
- /**
186
- * Initialize the theme system
187
- * Should be called once on app startup
188
- *
189
- * This function:
190
- * 1. Applies the current resolved theme to the document
191
- * 2. Sets up reactivity to apply theme changes
192
- */
193
- export function initializeTheme() {
194
- // Apply initial theme
195
- const resolved = get(resolvedTheme);
196
- applyTheme(resolved);
197
- // Subscribe to resolved theme changes and apply them
198
- resolvedTheme.subscribe((resolved) => {
199
- applyTheme(resolved);
200
- });
201
- }
202
- /**
203
- * Check if theme system is initialized
204
- * Useful for SSR scenarios
205
- *
206
- * @returns true if running in browser and theme is applied
207
- */
208
- export function isThemeInitialized() {
209
- if (typeof document === 'undefined') {
210
- return false;
211
- }
212
- return document.documentElement.hasAttribute('data-theme');
213
- }