@d34dman/flowdrop 0.0.43 → 0.0.44
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.
- package/README.md +8 -8
- package/dist/api/enhanced-client.d.ts +3 -1
- package/dist/api/enhanced-client.js +35 -5
- package/dist/components/App.svelte +68 -34
- package/dist/components/ConfigForm.svelte +169 -142
- package/dist/components/ConfigForm.svelte.d.ts +4 -2
- package/dist/components/ConfigPanel.svelte +42 -15
- package/dist/components/LogsSidebar.svelte +20 -19
- package/dist/components/Navbar.svelte +150 -80
- package/dist/components/Navbar.svelte.d.ts +8 -0
- package/dist/components/NodeSidebar.svelte +330 -217
- package/dist/components/PipelineStatus.svelte +6 -1
- package/dist/components/ReadOnlyDetails.svelte +14 -14
- package/dist/components/SchemaForm.svelte +49 -30
- package/dist/components/SchemaForm.svelte.d.ts +11 -1
- package/dist/components/SettingsModal.svelte +279 -0
- package/dist/components/SettingsModal.svelte.d.ts +23 -0
- package/dist/components/SettingsPanel.svelte +615 -0
- package/dist/components/SettingsPanel.svelte.d.ts +21 -0
- package/dist/components/ThemeToggle.svelte +186 -0
- package/dist/components/ThemeToggle.svelte.d.ts +14 -0
- package/dist/components/WorkflowEditor.svelte +110 -36
- package/dist/components/form/FormArray.svelte +81 -81
- package/dist/components/form/FormAutocomplete.svelte +1014 -0
- package/dist/components/form/FormAutocomplete.svelte.d.ts +25 -0
- package/dist/components/form/FormCheckboxGroup.svelte +16 -16
- package/dist/components/form/FormCodeEditor.svelte +26 -26
- package/dist/components/form/FormField.svelte +52 -21
- package/dist/components/form/FormFieldLight.svelte +19 -19
- package/dist/components/form/FormFieldWrapper.svelte +4 -4
- package/dist/components/form/FormMarkdownEditor.svelte +124 -57
- package/dist/components/form/FormNumberField.svelte +13 -13
- package/dist/components/form/FormRangeField.svelte +16 -16
- package/dist/components/form/FormSelect.svelte +15 -15
- package/dist/components/form/FormTemplateEditor.svelte +34 -34
- package/dist/components/form/FormTextField.svelte +13 -13
- package/dist/components/form/FormTextarea.svelte +13 -13
- package/dist/components/form/FormToggle.svelte +8 -8
- package/dist/components/form/index.d.ts +1 -0
- package/dist/components/form/index.js +1 -0
- package/dist/components/form/types.d.ts +133 -8
- package/dist/components/form/types.js +50 -1
- package/dist/components/interrupt/ChoicePrompt.svelte +45 -38
- package/dist/components/interrupt/ConfirmationPrompt.svelte +35 -35
- package/dist/components/interrupt/FormPrompt.svelte +27 -20
- package/dist/components/interrupt/InterruptBubble.svelte +50 -50
- package/dist/components/interrupt/TextInputPrompt.svelte +39 -32
- package/dist/components/layouts/MainLayout.svelte +233 -34
- package/dist/components/layouts/MainLayout.svelte.d.ts +12 -0
- package/dist/components/nodes/GatewayNode.svelte +102 -73
- package/dist/components/nodes/IdeaNode.svelte +53 -52
- package/dist/components/nodes/NotesNode.svelte +120 -88
- package/dist/components/nodes/SimpleNode.svelte +67 -47
- package/dist/components/nodes/SquareNode.svelte +86 -49
- package/dist/components/nodes/TerminalNode.svelte +122 -72
- package/dist/components/nodes/ToolNode.svelte +96 -65
- package/dist/components/nodes/WorkflowNode.svelte +91 -67
- package/dist/components/playground/ChatPanel.svelte +76 -76
- package/dist/components/playground/ExecutionLogs.svelte +71 -69
- package/dist/components/playground/InputCollector.svelte +59 -59
- package/dist/components/playground/MessageBubble.svelte +111 -112
- package/dist/components/playground/Playground.svelte +184 -138
- package/dist/components/playground/PlaygroundModal.svelte +18 -19
- package/dist/components/playground/SessionManager.svelte +68 -67
- package/dist/config/defaultPortConfig.js +22 -22
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/form/fieldRegistry.d.ts +17 -1
- package/dist/form/fieldRegistry.js +18 -2
- package/dist/form/index.d.ts +20 -2
- package/dist/form/index.js +19 -1
- package/dist/helpers/workflowEditorHelper.js +23 -11
- package/dist/index.d.ts +5 -0
- package/dist/index.js +13 -0
- package/dist/services/autoSaveService.d.ts +112 -0
- package/dist/services/autoSaveService.js +223 -0
- package/dist/services/settingsService.d.ts +92 -0
- package/dist/services/settingsService.js +202 -0
- package/dist/services/toastService.d.ts +9 -0
- package/dist/services/toastService.js +30 -1
- package/dist/stores/settingsStore.d.ts +128 -0
- package/dist/stores/settingsStore.js +488 -0
- package/dist/stores/themeStore.d.ts +68 -0
- package/dist/stores/themeStore.js +215 -0
- package/dist/styles/base.css +298 -621
- package/dist/styles/toast.css +33 -0
- package/dist/styles/tokens.css +366 -0
- package/dist/types/index.d.ts +78 -0
- package/dist/types/index.js +2 -0
- package/dist/types/playground.d.ts +12 -0
- package/dist/types/settings.d.ts +185 -0
- package/dist/types/settings.js +101 -0
- package/dist/utils/colors.d.ts +100 -7
- package/dist/utils/colors.js +228 -67
- package/package.json +3 -3
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Settings Panel Component
|
|
3
|
+
|
|
4
|
+
A comprehensive settings panel with tabbed categories for configuring
|
|
5
|
+
FlowDrop preferences. Uses SchemaForm for dynamic form generation.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Tabbed interface for settings categories (Theme, Editor, UI, Behavior, API)
|
|
9
|
+
- Real-time settings updates via settingsStore
|
|
10
|
+
- Optional API sync with "Sync to Cloud" button
|
|
11
|
+
- Reset to defaults functionality
|
|
12
|
+
|
|
13
|
+
@example
|
|
14
|
+
```svelte
|
|
15
|
+
<script>
|
|
16
|
+
import { SettingsPanel } from "@d34dman/flowdrop";
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<SettingsPanel
|
|
20
|
+
showSyncButton={true}
|
|
21
|
+
onClose={() => console.log("Closed")}
|
|
22
|
+
/>
|
|
23
|
+
```
|
|
24
|
+
-->
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import Icon from '@iconify/svelte';
|
|
28
|
+
import { SchemaForm } from '../form/index.js';
|
|
29
|
+
import type { ConfigSchema } from '../types/index.js';
|
|
30
|
+
import type { SettingsCategory } from '../types/settings.js';
|
|
31
|
+
import {
|
|
32
|
+
SETTINGS_CATEGORIES,
|
|
33
|
+
SETTINGS_CATEGORY_LABELS,
|
|
34
|
+
SETTINGS_CATEGORY_ICONS
|
|
35
|
+
} from '../types/settings.js';
|
|
36
|
+
import {
|
|
37
|
+
settingsStore,
|
|
38
|
+
updateSettings,
|
|
39
|
+
resetSettings,
|
|
40
|
+
syncSettingsToApi,
|
|
41
|
+
syncStatusStore
|
|
42
|
+
} from '../stores/settingsStore.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Props interface for SettingsPanel component
|
|
46
|
+
*/
|
|
47
|
+
interface Props {
|
|
48
|
+
/** Categories to display (defaults to all) */
|
|
49
|
+
categories?: SettingsCategory[];
|
|
50
|
+
/** Show the "Sync to Cloud" button */
|
|
51
|
+
showSyncButton?: boolean;
|
|
52
|
+
/** Show the reset button */
|
|
53
|
+
showResetButton?: boolean;
|
|
54
|
+
/** Callback when settings change */
|
|
55
|
+
onSettingsChange?: (category: SettingsCategory, values: Record<string, unknown>) => void;
|
|
56
|
+
/** Callback when close is requested */
|
|
57
|
+
onClose?: () => void;
|
|
58
|
+
/** Custom CSS class */
|
|
59
|
+
class?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const {
|
|
63
|
+
categories = SETTINGS_CATEGORIES,
|
|
64
|
+
showSyncButton = true,
|
|
65
|
+
showResetButton = true,
|
|
66
|
+
onSettingsChange,
|
|
67
|
+
onClose,
|
|
68
|
+
class: className = ''
|
|
69
|
+
}: Props = $props();
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Currently active tab
|
|
73
|
+
*/
|
|
74
|
+
let activeTab = $state<SettingsCategory>(categories[0] ?? 'theme');
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether sync is in progress
|
|
78
|
+
*/
|
|
79
|
+
let isSyncing = $derived($syncStatusStore.status === 'syncing');
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* JSON Schema definitions for each settings category
|
|
83
|
+
*/
|
|
84
|
+
const schemas: Record<SettingsCategory, ConfigSchema> = {
|
|
85
|
+
theme: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
preference: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
title: 'Theme Preference',
|
|
91
|
+
description: 'Choose your preferred color scheme',
|
|
92
|
+
enum: ['light', 'dark', 'auto'],
|
|
93
|
+
enumLabels: ['Light', 'Dark', 'Auto (System)'],
|
|
94
|
+
default: 'auto'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
editor: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
showGrid: {
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
title: 'Show Grid',
|
|
104
|
+
description: 'Display grid lines on the canvas',
|
|
105
|
+
default: true
|
|
106
|
+
},
|
|
107
|
+
snapToGrid: {
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
title: 'Snap to Grid',
|
|
110
|
+
description: 'Snap nodes to grid when dragging',
|
|
111
|
+
default: true
|
|
112
|
+
},
|
|
113
|
+
gridSize: {
|
|
114
|
+
type: 'number',
|
|
115
|
+
title: 'Grid Size',
|
|
116
|
+
description: 'Grid cell size in pixels',
|
|
117
|
+
minimum: 5,
|
|
118
|
+
maximum: 50,
|
|
119
|
+
default: 20
|
|
120
|
+
},
|
|
121
|
+
showMinimap: {
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
title: 'Show Minimap',
|
|
124
|
+
description: 'Display navigation minimap',
|
|
125
|
+
default: true
|
|
126
|
+
},
|
|
127
|
+
defaultZoom: {
|
|
128
|
+
type: 'number',
|
|
129
|
+
title: 'Default Zoom',
|
|
130
|
+
description: 'Initial zoom level (1 = 100%)',
|
|
131
|
+
minimum: 0.25,
|
|
132
|
+
maximum: 2,
|
|
133
|
+
default: 1
|
|
134
|
+
},
|
|
135
|
+
fitViewOnLoad: {
|
|
136
|
+
type: 'boolean',
|
|
137
|
+
title: 'Fit View on Load',
|
|
138
|
+
description: 'Automatically fit workflow to view when loading',
|
|
139
|
+
default: true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
ui: {
|
|
144
|
+
type: 'object',
|
|
145
|
+
properties: {
|
|
146
|
+
sidebarWidth: {
|
|
147
|
+
type: 'number',
|
|
148
|
+
title: 'Sidebar Width',
|
|
149
|
+
description: 'Width of the node sidebar in pixels',
|
|
150
|
+
minimum: 200,
|
|
151
|
+
maximum: 500,
|
|
152
|
+
default: 280
|
|
153
|
+
},
|
|
154
|
+
sidebarCollapsed: {
|
|
155
|
+
type: 'boolean',
|
|
156
|
+
title: 'Sidebar Collapsed',
|
|
157
|
+
description: 'Start with sidebar collapsed',
|
|
158
|
+
default: false
|
|
159
|
+
},
|
|
160
|
+
compactMode: {
|
|
161
|
+
type: 'boolean',
|
|
162
|
+
title: 'Compact Mode',
|
|
163
|
+
description: 'Use compact UI with smaller spacing',
|
|
164
|
+
default: false
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
behavior: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
autoSave: {
|
|
172
|
+
type: 'boolean',
|
|
173
|
+
title: 'Auto Save',
|
|
174
|
+
description: 'Automatically save changes',
|
|
175
|
+
default: false
|
|
176
|
+
},
|
|
177
|
+
autoSaveInterval: {
|
|
178
|
+
type: 'number',
|
|
179
|
+
title: 'Auto Save Interval',
|
|
180
|
+
description: 'Time between auto-saves in milliseconds',
|
|
181
|
+
minimum: 5000,
|
|
182
|
+
maximum: 300000,
|
|
183
|
+
default: 30000
|
|
184
|
+
},
|
|
185
|
+
undoHistoryLimit: {
|
|
186
|
+
type: 'number',
|
|
187
|
+
title: 'Undo History Limit',
|
|
188
|
+
description: 'Maximum number of undo steps',
|
|
189
|
+
minimum: 10,
|
|
190
|
+
maximum: 200,
|
|
191
|
+
default: 50
|
|
192
|
+
},
|
|
193
|
+
confirmDelete: {
|
|
194
|
+
type: 'boolean',
|
|
195
|
+
title: 'Confirm Delete',
|
|
196
|
+
description: 'Show confirmation before deleting nodes',
|
|
197
|
+
default: true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
api: {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: {
|
|
204
|
+
timeout: {
|
|
205
|
+
type: 'number',
|
|
206
|
+
title: 'Request Timeout',
|
|
207
|
+
description: 'API request timeout in milliseconds',
|
|
208
|
+
minimum: 5000,
|
|
209
|
+
maximum: 120000,
|
|
210
|
+
default: 30000
|
|
211
|
+
},
|
|
212
|
+
retryEnabled: {
|
|
213
|
+
type: 'boolean',
|
|
214
|
+
title: 'Enable Retry',
|
|
215
|
+
description: 'Automatically retry failed requests',
|
|
216
|
+
default: true
|
|
217
|
+
},
|
|
218
|
+
retryAttempts: {
|
|
219
|
+
type: 'number',
|
|
220
|
+
title: 'Retry Attempts',
|
|
221
|
+
description: 'Maximum number of retry attempts',
|
|
222
|
+
minimum: 1,
|
|
223
|
+
maximum: 10,
|
|
224
|
+
default: 3
|
|
225
|
+
},
|
|
226
|
+
cacheEnabled: {
|
|
227
|
+
type: 'boolean',
|
|
228
|
+
title: 'Enable Caching',
|
|
229
|
+
description: 'Cache API responses for better performance',
|
|
230
|
+
default: true
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get current values for a category from the store
|
|
238
|
+
*/
|
|
239
|
+
function getCategoryValues(category: SettingsCategory): Record<string, unknown> {
|
|
240
|
+
const settings = $settingsStore;
|
|
241
|
+
const categorySettings = settings[category];
|
|
242
|
+
// Convert to Record<string, unknown> for SchemaForm compatibility
|
|
243
|
+
return Object.fromEntries(Object.entries(categorySettings));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Handle form value changes
|
|
248
|
+
*/
|
|
249
|
+
function handleChange(category: SettingsCategory, values: Record<string, unknown>): void {
|
|
250
|
+
// Update the store
|
|
251
|
+
updateSettings({ [category]: values });
|
|
252
|
+
|
|
253
|
+
// Notify parent if callback provided
|
|
254
|
+
if (onSettingsChange) {
|
|
255
|
+
onSettingsChange(category, values);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Handle sync to cloud button click
|
|
261
|
+
*/
|
|
262
|
+
async function handleSync(): Promise<void> {
|
|
263
|
+
try {
|
|
264
|
+
await syncSettingsToApi();
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error('Failed to sync settings:', error);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Handle reset button click
|
|
272
|
+
*/
|
|
273
|
+
function handleReset(): void {
|
|
274
|
+
if (confirm(`Reset ${SETTINGS_CATEGORY_LABELS[activeTab]} settings to defaults?`)) {
|
|
275
|
+
resetSettings([activeTab]);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Handle reset all button click
|
|
281
|
+
*/
|
|
282
|
+
function handleResetAll(): void {
|
|
283
|
+
if (confirm('Reset all settings to defaults?')) {
|
|
284
|
+
resetSettings();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Handle tab keyboard navigation
|
|
290
|
+
*/
|
|
291
|
+
function handleTabKeydown(event: KeyboardEvent, index: number): void {
|
|
292
|
+
const tabs = categories;
|
|
293
|
+
let newIndex = index;
|
|
294
|
+
|
|
295
|
+
switch (event.key) {
|
|
296
|
+
case 'ArrowLeft':
|
|
297
|
+
newIndex = index > 0 ? index - 1 : tabs.length - 1;
|
|
298
|
+
break;
|
|
299
|
+
case 'ArrowRight':
|
|
300
|
+
newIndex = index < tabs.length - 1 ? index + 1 : 0;
|
|
301
|
+
break;
|
|
302
|
+
case 'Home':
|
|
303
|
+
newIndex = 0;
|
|
304
|
+
break;
|
|
305
|
+
case 'End':
|
|
306
|
+
newIndex = tabs.length - 1;
|
|
307
|
+
break;
|
|
308
|
+
default:
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
event.preventDefault();
|
|
313
|
+
activeTab = tabs[newIndex];
|
|
314
|
+
|
|
315
|
+
// Focus the new tab
|
|
316
|
+
const tabElement = document.querySelector(
|
|
317
|
+
`[data-tab="${tabs[newIndex]}"]`
|
|
318
|
+
) as HTMLElement | null;
|
|
319
|
+
tabElement?.focus();
|
|
320
|
+
}
|
|
321
|
+
</script>
|
|
322
|
+
|
|
323
|
+
<div class="flowdrop-settings-panel {className}">
|
|
324
|
+
<!-- Tab Navigation -->
|
|
325
|
+
<div class="flowdrop-settings-panel__tabs" role="tablist" aria-label="Settings categories">
|
|
326
|
+
{#each categories as category, index (category)}
|
|
327
|
+
<button
|
|
328
|
+
class="flowdrop-settings-panel__tab"
|
|
329
|
+
class:flowdrop-settings-panel__tab--active={activeTab === category}
|
|
330
|
+
role="tab"
|
|
331
|
+
aria-selected={activeTab === category}
|
|
332
|
+
aria-controls="panel-{category}"
|
|
333
|
+
data-tab={category}
|
|
334
|
+
tabindex={activeTab === category ? 0 : -1}
|
|
335
|
+
onclick={() => (activeTab = category)}
|
|
336
|
+
onkeydown={(e) => handleTabKeydown(e, index)}
|
|
337
|
+
>
|
|
338
|
+
<Icon icon={SETTINGS_CATEGORY_ICONS[category]} class="flowdrop-settings-panel__tab-icon" />
|
|
339
|
+
<span class="flowdrop-settings-panel__tab-label">{SETTINGS_CATEGORY_LABELS[category]}</span>
|
|
340
|
+
</button>
|
|
341
|
+
{/each}
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<!-- Tab Panels -->
|
|
345
|
+
<div class="flowdrop-settings-panel__content">
|
|
346
|
+
{#each categories as category (category)}
|
|
347
|
+
<div
|
|
348
|
+
id="panel-{category}"
|
|
349
|
+
class="flowdrop-settings-panel__panel"
|
|
350
|
+
class:flowdrop-settings-panel__panel--active={activeTab === category}
|
|
351
|
+
role="tabpanel"
|
|
352
|
+
aria-labelledby="tab-{category}"
|
|
353
|
+
hidden={activeTab !== category}
|
|
354
|
+
>
|
|
355
|
+
{#if activeTab === category}
|
|
356
|
+
<SchemaForm
|
|
357
|
+
schema={schemas[category]}
|
|
358
|
+
values={getCategoryValues(category)}
|
|
359
|
+
onChange={(values) => handleChange(category, values)}
|
|
360
|
+
showActions={false}
|
|
361
|
+
/>
|
|
362
|
+
{/if}
|
|
363
|
+
</div>
|
|
364
|
+
{/each}
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<!-- Footer Actions -->
|
|
368
|
+
<div class="flowdrop-settings-panel__footer">
|
|
369
|
+
<div class="flowdrop-settings-panel__footer-start">
|
|
370
|
+
{#if showResetButton}
|
|
371
|
+
<button
|
|
372
|
+
class="flowdrop-settings-panel__btn flowdrop-settings-panel__btn--outline"
|
|
373
|
+
onclick={handleReset}
|
|
374
|
+
title="Reset current category to defaults"
|
|
375
|
+
>
|
|
376
|
+
<Icon icon="mdi:refresh" />
|
|
377
|
+
<span>Reset</span>
|
|
378
|
+
</button>
|
|
379
|
+
<button
|
|
380
|
+
class="flowdrop-settings-panel__btn flowdrop-settings-panel__btn--ghost"
|
|
381
|
+
onclick={handleResetAll}
|
|
382
|
+
title="Reset all settings to defaults"
|
|
383
|
+
>
|
|
384
|
+
Reset All
|
|
385
|
+
</button>
|
|
386
|
+
{/if}
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div class="flowdrop-settings-panel__footer-end">
|
|
390
|
+
{#if showSyncButton}
|
|
391
|
+
<button
|
|
392
|
+
class="flowdrop-settings-panel__btn flowdrop-settings-panel__btn--secondary"
|
|
393
|
+
onclick={handleSync}
|
|
394
|
+
disabled={isSyncing}
|
|
395
|
+
title="Sync settings to cloud"
|
|
396
|
+
>
|
|
397
|
+
{#if isSyncing}
|
|
398
|
+
<Icon icon="mdi:loading" class="flowdrop-settings-panel__spin" />
|
|
399
|
+
<span>Syncing...</span>
|
|
400
|
+
{:else}
|
|
401
|
+
<Icon icon="mdi:cloud-upload" />
|
|
402
|
+
<span>Sync to Cloud</span>
|
|
403
|
+
{/if}
|
|
404
|
+
</button>
|
|
405
|
+
{/if}
|
|
406
|
+
|
|
407
|
+
{#if onClose}
|
|
408
|
+
<button
|
|
409
|
+
class="flowdrop-settings-panel__btn flowdrop-settings-panel__btn--primary"
|
|
410
|
+
onclick={onClose}
|
|
411
|
+
>
|
|
412
|
+
<span>Close</span>
|
|
413
|
+
</button>
|
|
414
|
+
{/if}
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
<!-- Sync Status Indicator -->
|
|
419
|
+
{#if $syncStatusStore.error}
|
|
420
|
+
<div class="flowdrop-settings-panel__error">
|
|
421
|
+
<Icon icon="mdi:alert-circle" />
|
|
422
|
+
<span>{$syncStatusStore.error}</span>
|
|
423
|
+
</div>
|
|
424
|
+
{:else if $syncStatusStore.status === 'synced' && $syncStatusStore.lastSyncedAt}
|
|
425
|
+
<div class="flowdrop-settings-panel__synced">
|
|
426
|
+
<Icon icon="mdi:check-circle" />
|
|
427
|
+
<span>Synced {new Date($syncStatusStore.lastSyncedAt).toLocaleTimeString()}</span>
|
|
428
|
+
</div>
|
|
429
|
+
{/if}
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<style>
|
|
433
|
+
.flowdrop-settings-panel {
|
|
434
|
+
display: flex;
|
|
435
|
+
flex-direction: column;
|
|
436
|
+
height: 100%;
|
|
437
|
+
background-color: var(--fd-background);
|
|
438
|
+
color: var(--fd-foreground);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* Tabs */
|
|
442
|
+
.flowdrop-settings-panel__tabs {
|
|
443
|
+
display: flex;
|
|
444
|
+
gap: var(--fd-space-1);
|
|
445
|
+
padding: var(--fd-space-3);
|
|
446
|
+
border-bottom: 1px solid var(--fd-border);
|
|
447
|
+
overflow-x: auto;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.flowdrop-settings-panel__tab {
|
|
451
|
+
display: flex;
|
|
452
|
+
align-items: center;
|
|
453
|
+
gap: var(--fd-space-2);
|
|
454
|
+
padding: var(--fd-space-2) var(--fd-space-3);
|
|
455
|
+
border: none;
|
|
456
|
+
border-radius: var(--fd-radius-md);
|
|
457
|
+
background-color: transparent;
|
|
458
|
+
color: var(--fd-muted-foreground);
|
|
459
|
+
font-size: var(--fd-text-sm);
|
|
460
|
+
font-weight: 500;
|
|
461
|
+
cursor: pointer;
|
|
462
|
+
transition: all var(--fd-transition-fast);
|
|
463
|
+
white-space: nowrap;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.flowdrop-settings-panel__tab:hover {
|
|
467
|
+
background-color: var(--fd-muted);
|
|
468
|
+
color: var(--fd-foreground);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.flowdrop-settings-panel__tab--active {
|
|
472
|
+
background-color: var(--fd-primary);
|
|
473
|
+
color: var(--fd-primary-foreground);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.flowdrop-settings-panel__tab--active:hover {
|
|
477
|
+
background-color: var(--fd-primary);
|
|
478
|
+
color: var(--fd-primary-foreground);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.flowdrop-settings-panel__tab:focus {
|
|
482
|
+
outline: none;
|
|
483
|
+
box-shadow: 0 0 0 2px var(--fd-ring);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
:global(.flowdrop-settings-panel__tab-icon) {
|
|
487
|
+
font-size: var(--fd-text-base);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Content */
|
|
491
|
+
.flowdrop-settings-panel__content {
|
|
492
|
+
flex: 1;
|
|
493
|
+
overflow-y: auto;
|
|
494
|
+
padding: var(--fd-space-4);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.flowdrop-settings-panel__panel {
|
|
498
|
+
display: none;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.flowdrop-settings-panel__panel--active {
|
|
502
|
+
display: block;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* Footer */
|
|
506
|
+
.flowdrop-settings-panel__footer {
|
|
507
|
+
display: flex;
|
|
508
|
+
justify-content: space-between;
|
|
509
|
+
align-items: center;
|
|
510
|
+
padding: var(--fd-space-3) var(--fd-space-4);
|
|
511
|
+
border-top: 1px solid var(--fd-border);
|
|
512
|
+
gap: var(--fd-space-3);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.flowdrop-settings-panel__footer-start,
|
|
516
|
+
.flowdrop-settings-panel__footer-end {
|
|
517
|
+
display: flex;
|
|
518
|
+
gap: var(--fd-space-2);
|
|
519
|
+
align-items: center;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Buttons */
|
|
523
|
+
.flowdrop-settings-panel__btn {
|
|
524
|
+
display: inline-flex;
|
|
525
|
+
align-items: center;
|
|
526
|
+
gap: var(--fd-space-2);
|
|
527
|
+
padding: var(--fd-space-2) var(--fd-space-3);
|
|
528
|
+
border-radius: var(--fd-radius-md);
|
|
529
|
+
font-size: var(--fd-text-sm);
|
|
530
|
+
font-weight: 500;
|
|
531
|
+
cursor: pointer;
|
|
532
|
+
transition: all var(--fd-transition-fast);
|
|
533
|
+
border: 1px solid transparent;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.flowdrop-settings-panel__btn:disabled {
|
|
537
|
+
opacity: 0.5;
|
|
538
|
+
cursor: not-allowed;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.flowdrop-settings-panel__btn--primary {
|
|
542
|
+
background-color: var(--fd-primary);
|
|
543
|
+
color: var(--fd-primary-foreground);
|
|
544
|
+
border-color: var(--fd-primary);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.flowdrop-settings-panel__btn--primary:hover:not(:disabled) {
|
|
548
|
+
opacity: 0.9;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.flowdrop-settings-panel__btn--secondary {
|
|
552
|
+
background-color: var(--fd-secondary);
|
|
553
|
+
color: var(--fd-secondary-foreground);
|
|
554
|
+
border-color: var(--fd-border);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.flowdrop-settings-panel__btn--secondary:hover:not(:disabled) {
|
|
558
|
+
background-color: var(--fd-muted);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.flowdrop-settings-panel__btn--outline {
|
|
562
|
+
background-color: transparent;
|
|
563
|
+
color: var(--fd-foreground);
|
|
564
|
+
border-color: var(--fd-border);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.flowdrop-settings-panel__btn--outline:hover:not(:disabled) {
|
|
568
|
+
background-color: var(--fd-muted);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.flowdrop-settings-panel__btn--ghost {
|
|
572
|
+
background-color: transparent;
|
|
573
|
+
color: var(--fd-muted-foreground);
|
|
574
|
+
border-color: transparent;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.flowdrop-settings-panel__btn--ghost:hover:not(:disabled) {
|
|
578
|
+
background-color: var(--fd-muted);
|
|
579
|
+
color: var(--fd-foreground);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* Status Indicators */
|
|
583
|
+
.flowdrop-settings-panel__error,
|
|
584
|
+
.flowdrop-settings-panel__synced {
|
|
585
|
+
display: flex;
|
|
586
|
+
align-items: center;
|
|
587
|
+
gap: var(--fd-space-2);
|
|
588
|
+
padding: var(--fd-space-2) var(--fd-space-4);
|
|
589
|
+
font-size: var(--fd-text-xs);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.flowdrop-settings-panel__error {
|
|
593
|
+
background-color: var(--fd-destructive);
|
|
594
|
+
color: var(--fd-destructive-foreground);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.flowdrop-settings-panel__synced {
|
|
598
|
+
background-color: var(--fd-success, #22c55e);
|
|
599
|
+
color: white;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/* Spin Animation */
|
|
603
|
+
:global(.flowdrop-settings-panel__spin) {
|
|
604
|
+
animation: flowdrop-spin 1s linear infinite;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
@keyframes flowdrop-spin {
|
|
608
|
+
from {
|
|
609
|
+
transform: rotate(0deg);
|
|
610
|
+
}
|
|
611
|
+
to {
|
|
612
|
+
transform: rotate(360deg);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SettingsCategory } from '../types/settings.js';
|
|
2
|
+
/**
|
|
3
|
+
* Props interface for SettingsPanel component
|
|
4
|
+
*/
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Categories to display (defaults to all) */
|
|
7
|
+
categories?: SettingsCategory[];
|
|
8
|
+
/** Show the "Sync to Cloud" button */
|
|
9
|
+
showSyncButton?: boolean;
|
|
10
|
+
/** Show the reset button */
|
|
11
|
+
showResetButton?: boolean;
|
|
12
|
+
/** Callback when settings change */
|
|
13
|
+
onSettingsChange?: (category: SettingsCategory, values: Record<string, unknown>) => void;
|
|
14
|
+
/** Callback when close is requested */
|
|
15
|
+
onClose?: () => void;
|
|
16
|
+
/** Custom CSS class */
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const SettingsPanel: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type SettingsPanel = ReturnType<typeof SettingsPanel>;
|
|
21
|
+
export default SettingsPanel;
|