@d34dman/flowdrop 0.0.45 → 0.0.46
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 +2 -2
- package/dist/components/ConfigForm.svelte +4 -20
- package/dist/components/Navbar.svelte +6 -7
- package/dist/components/SchemaForm.svelte +2 -10
- package/dist/components/WorkflowEditor.svelte +132 -4
- package/dist/components/form/FormAutocomplete.svelte +5 -9
- package/dist/components/form/FormCheckboxGroup.svelte +11 -1
- package/dist/components/form/FormCheckboxGroup.svelte.d.ts +2 -0
- package/dist/components/form/FormCodeEditor.svelte +16 -7
- package/dist/components/form/FormCodeEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormField.svelte +20 -1
- package/dist/components/form/FormMarkdownEditor.svelte +29 -19
- package/dist/components/form/FormMarkdownEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormNumberField.svelte +4 -0
- package/dist/components/form/FormNumberField.svelte.d.ts +2 -0
- package/dist/components/form/FormRangeField.svelte +4 -0
- package/dist/components/form/FormRangeField.svelte.d.ts +2 -0
- package/dist/components/form/FormSelect.svelte +4 -0
- package/dist/components/form/FormSelect.svelte.d.ts +2 -0
- package/dist/components/form/FormTemplateEditor.svelte +16 -7
- package/dist/components/form/FormTemplateEditor.svelte.d.ts +2 -0
- package/dist/components/form/FormTextField.svelte +4 -0
- package/dist/components/form/FormTextField.svelte.d.ts +2 -0
- package/dist/components/form/FormTextarea.svelte +4 -0
- package/dist/components/form/FormTextarea.svelte.d.ts +2 -0
- package/dist/components/form/FormToggle.svelte +4 -0
- package/dist/components/form/FormToggle.svelte.d.ts +2 -0
- package/dist/components/form/types.d.ts +5 -0
- package/dist/components/form/types.js +1 -1
- package/dist/components/layouts/MainLayout.svelte +5 -2
- package/dist/components/nodes/GatewayNode.svelte +0 -8
- package/dist/components/nodes/SimpleNode.svelte +2 -3
- package/dist/components/nodes/WorkflowNode.svelte +0 -8
- package/dist/components/playground/Playground.svelte +43 -38
- package/dist/editor/index.d.ts +3 -1
- package/dist/editor/index.js +5 -1
- package/dist/helpers/workflowEditorHelper.js +1 -2
- package/dist/services/autoSaveService.js +5 -5
- package/dist/services/historyService.d.ts +207 -0
- package/dist/services/historyService.js +317 -0
- package/dist/services/settingsService.d.ts +2 -2
- package/dist/services/settingsService.js +15 -21
- package/dist/services/toastService.d.ts +1 -1
- package/dist/services/toastService.js +10 -10
- package/dist/stores/historyStore.d.ts +133 -0
- package/dist/stores/historyStore.js +188 -0
- package/dist/stores/settingsStore.d.ts +1 -1
- package/dist/stores/settingsStore.js +40 -42
- package/dist/stores/themeStore.d.ts +2 -2
- package/dist/stores/themeStore.js +30 -32
- package/dist/stores/workflowStore.d.ts +52 -2
- package/dist/stores/workflowStore.js +102 -2
- package/dist/styles/base.css +28 -8
- package/dist/styles/toast.css +3 -1
- package/dist/styles/tokens.css +2 -2
- package/dist/types/settings.d.ts +3 -3
- package/dist/types/settings.js +13 -19
- package/dist/utils/colors.js +17 -17
- package/package.json +2 -2
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @module stores/themeStore
|
|
22
22
|
*/
|
|
23
|
-
import { writable, derived, get } from
|
|
23
|
+
import { writable, derived, get } from 'svelte/store';
|
|
24
24
|
/** localStorage key for persisting theme preference */
|
|
25
|
-
const STORAGE_KEY =
|
|
25
|
+
const STORAGE_KEY = 'flowdrop-theme';
|
|
26
26
|
/** Default theme preference when none is stored */
|
|
27
|
-
const DEFAULT_THEME =
|
|
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 ===
|
|
38
|
-
return
|
|
37
|
+
if (typeof window === 'undefined') {
|
|
38
|
+
return 'light';
|
|
39
39
|
}
|
|
40
|
-
return window.matchMedia(
|
|
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 !==
|
|
46
|
+
const systemTheme = writable(typeof window !== 'undefined' ? getSystemTheme() : 'light');
|
|
49
47
|
// Listen for system theme changes
|
|
50
|
-
if (typeof window !==
|
|
51
|
-
const mediaQuery = window.matchMedia(
|
|
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 ?
|
|
54
|
+
systemTheme.set(event.matches ? 'dark' : 'light');
|
|
57
55
|
};
|
|
58
56
|
// Modern browsers use addEventListener
|
|
59
57
|
if (mediaQuery.addEventListener) {
|
|
60
|
-
mediaQuery.addEventListener(
|
|
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 ===
|
|
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 ===
|
|
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(
|
|
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 ===
|
|
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(
|
|
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 ===
|
|
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 ===
|
|
135
|
+
if (typeof document === 'undefined') {
|
|
138
136
|
return;
|
|
139
137
|
}
|
|
140
|
-
document.documentElement.setAttribute(
|
|
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 ===
|
|
156
|
+
if (currentTheme === 'auto') {
|
|
159
157
|
// Switch to opposite of system preference
|
|
160
|
-
setTheme(currentResolved ===
|
|
158
|
+
setTheme(currentResolved === 'dark' ? 'light' : 'dark');
|
|
161
159
|
}
|
|
162
160
|
else {
|
|
163
161
|
// Toggle between light and dark
|
|
164
|
-
setTheme(currentTheme ===
|
|
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
|
|
174
|
-
setTheme(
|
|
171
|
+
case 'light':
|
|
172
|
+
setTheme('dark');
|
|
175
173
|
break;
|
|
176
|
-
case
|
|
177
|
-
setTheme(
|
|
174
|
+
case 'dark':
|
|
175
|
+
setTheme('auto');
|
|
178
176
|
break;
|
|
179
|
-
case
|
|
180
|
-
setTheme(
|
|
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 ===
|
|
209
|
+
if (typeof document === 'undefined') {
|
|
212
210
|
return false;
|
|
213
211
|
}
|
|
214
|
-
return document.documentElement.hasAttribute(
|
|
212
|
+
return document.documentElement.hasAttribute('data-theme');
|
|
215
213
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workflow Store for FlowDrop
|
|
3
3
|
*
|
|
4
|
-
* Provides global state management for workflows with dirty state tracking
|
|
4
|
+
* Provides global state management for workflows with dirty state tracking
|
|
5
|
+
* and undo/redo history integration.
|
|
5
6
|
*
|
|
6
7
|
* @module stores/workflowStore
|
|
7
8
|
*/
|
|
@@ -40,6 +41,28 @@ export declare function markAsSaved(): void;
|
|
|
40
41
|
* @returns true if there are unsaved changes
|
|
41
42
|
*/
|
|
42
43
|
export declare function isDirty(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Enable or disable history recording
|
|
46
|
+
*
|
|
47
|
+
* Useful for bulk operations where you don't want individual history entries.
|
|
48
|
+
*
|
|
49
|
+
* @param enabled - Whether history should be recorded
|
|
50
|
+
*/
|
|
51
|
+
export declare function setHistoryEnabled(enabled: boolean): void;
|
|
52
|
+
/**
|
|
53
|
+
* Check if history recording is enabled
|
|
54
|
+
*
|
|
55
|
+
* @returns true if history is being recorded
|
|
56
|
+
*/
|
|
57
|
+
export declare function isHistoryEnabled(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Set the restoring from history flag
|
|
60
|
+
*
|
|
61
|
+
* Used internally by the history store when performing undo/redo.
|
|
62
|
+
*
|
|
63
|
+
* @param restoring - Whether we're currently restoring from history
|
|
64
|
+
*/
|
|
65
|
+
export declare function setRestoringFromHistory(restoring: boolean): void;
|
|
43
66
|
/**
|
|
44
67
|
* Get the current workflow
|
|
45
68
|
*
|
|
@@ -74,13 +97,22 @@ export declare const workflowActions: {
|
|
|
74
97
|
/**
|
|
75
98
|
* Initialize workflow (from load or new)
|
|
76
99
|
*
|
|
77
|
-
* This sets the initial saved snapshot
|
|
100
|
+
* This sets the initial saved snapshot, clears dirty state, and initializes history.
|
|
78
101
|
*/
|
|
79
102
|
initialize: (workflow: Workflow) => void;
|
|
80
103
|
/**
|
|
81
104
|
* Update the entire workflow
|
|
105
|
+
*
|
|
106
|
+
* Note: This is typically called from SvelteFlow sync and should not push to history
|
|
107
|
+
* for every small change. History is pushed by specific actions or drag handlers.
|
|
82
108
|
*/
|
|
83
109
|
updateWorkflow: (workflow: Workflow) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Restore workflow from history (undo/redo)
|
|
112
|
+
*
|
|
113
|
+
* This bypasses history recording to prevent recursive loops.
|
|
114
|
+
*/
|
|
115
|
+
restoreFromHistory: (workflow: Workflow) => void;
|
|
84
116
|
/**
|
|
85
117
|
* Update nodes
|
|
86
118
|
*/
|
|
@@ -99,6 +131,9 @@ export declare const workflowActions: {
|
|
|
99
131
|
addNode: (node: WorkflowNode) => void;
|
|
100
132
|
/**
|
|
101
133
|
* Remove a node
|
|
134
|
+
*
|
|
135
|
+
* This is an atomic operation that also removes connected edges.
|
|
136
|
+
* A single undo will restore both the node and its edges.
|
|
102
137
|
*/
|
|
103
138
|
removeNode: (nodeId: string) => void;
|
|
104
139
|
/**
|
|
@@ -111,10 +146,14 @@ export declare const workflowActions: {
|
|
|
111
146
|
removeEdge: (edgeId: string) => void;
|
|
112
147
|
/**
|
|
113
148
|
* Update a specific node
|
|
149
|
+
*
|
|
150
|
+
* Used for config changes. Pushes to history for undo support.
|
|
114
151
|
*/
|
|
115
152
|
updateNode: (nodeId: string, updates: Partial<WorkflowNode>) => void;
|
|
116
153
|
/**
|
|
117
154
|
* Clear the workflow
|
|
155
|
+
*
|
|
156
|
+
* Resets the workflow and clears history.
|
|
118
157
|
*/
|
|
119
158
|
clear: () => void;
|
|
120
159
|
/**
|
|
@@ -125,6 +164,7 @@ export declare const workflowActions: {
|
|
|
125
164
|
* Batch update nodes and edges
|
|
126
165
|
*
|
|
127
166
|
* Useful for complex operations that update multiple things at once.
|
|
167
|
+
* Creates a single history entry for the entire batch.
|
|
128
168
|
*/
|
|
129
169
|
batchUpdate: (updates: {
|
|
130
170
|
nodes?: WorkflowNode[];
|
|
@@ -133,6 +173,16 @@ export declare const workflowActions: {
|
|
|
133
173
|
description?: string;
|
|
134
174
|
metadata?: Partial<Workflow["metadata"]>;
|
|
135
175
|
}) => void;
|
|
176
|
+
/**
|
|
177
|
+
* Push current state to history manually
|
|
178
|
+
*
|
|
179
|
+
* Use this before operations that modify the workflow through other means
|
|
180
|
+
* (e.g., drag operations handled by SvelteFlow directly).
|
|
181
|
+
*
|
|
182
|
+
* @param description - Description of the upcoming change
|
|
183
|
+
* @param workflow - Optional workflow to push (uses store state if not provided)
|
|
184
|
+
*/
|
|
185
|
+
pushHistory: (description?: string, workflow?: Workflow) => void;
|
|
136
186
|
};
|
|
137
187
|
/** Derived store for workflow changes (useful for triggering saves) */
|
|
138
188
|
export declare const workflowChanged: import("svelte/store").Readable<{
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workflow Store for FlowDrop
|
|
3
3
|
*
|
|
4
|
-
* Provides global state management for workflows with dirty state tracking
|
|
4
|
+
* Provides global state management for workflows with dirty state tracking
|
|
5
|
+
* and undo/redo history integration.
|
|
5
6
|
*
|
|
6
7
|
* @module stores/workflowStore
|
|
7
8
|
*/
|
|
8
9
|
import { writable, derived, get } from 'svelte/store';
|
|
10
|
+
import { historyService } from '../services/historyService.js';
|
|
9
11
|
// =========================================================================
|
|
10
12
|
// Core Workflow Store
|
|
11
13
|
// =========================================================================
|
|
@@ -39,6 +41,18 @@ let onDirtyStateChangeCallback = null;
|
|
|
39
41
|
* Set by the App component to notify parent application.
|
|
40
42
|
*/
|
|
41
43
|
let onWorkflowChangeCallback = null;
|
|
44
|
+
/**
|
|
45
|
+
* Flag to track if we're currently restoring from history (undo/redo)
|
|
46
|
+
*
|
|
47
|
+
* When true, prevents pushing to history to avoid recursive loops.
|
|
48
|
+
*/
|
|
49
|
+
let isRestoringFromHistory = false;
|
|
50
|
+
/**
|
|
51
|
+
* Flag to track if history recording is enabled
|
|
52
|
+
*
|
|
53
|
+
* Can be disabled for bulk operations or when history is not needed.
|
|
54
|
+
*/
|
|
55
|
+
let historyEnabled = true;
|
|
42
56
|
/**
|
|
43
57
|
* Set the dirty state change callback
|
|
44
58
|
*
|
|
@@ -136,6 +150,49 @@ export function markAsSaved() {
|
|
|
136
150
|
export function isDirty() {
|
|
137
151
|
return get(isDirtyStore);
|
|
138
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Enable or disable history recording
|
|
155
|
+
*
|
|
156
|
+
* Useful for bulk operations where you don't want individual history entries.
|
|
157
|
+
*
|
|
158
|
+
* @param enabled - Whether history should be recorded
|
|
159
|
+
*/
|
|
160
|
+
export function setHistoryEnabled(enabled) {
|
|
161
|
+
historyEnabled = enabled;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if history recording is enabled
|
|
165
|
+
*
|
|
166
|
+
* @returns true if history is being recorded
|
|
167
|
+
*/
|
|
168
|
+
export function isHistoryEnabled() {
|
|
169
|
+
return historyEnabled;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Set the restoring from history flag
|
|
173
|
+
*
|
|
174
|
+
* Used internally by the history store when performing undo/redo.
|
|
175
|
+
*
|
|
176
|
+
* @param restoring - Whether we're currently restoring from history
|
|
177
|
+
*/
|
|
178
|
+
export function setRestoringFromHistory(restoring) {
|
|
179
|
+
isRestoringFromHistory = restoring;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Push current state to history before making changes
|
|
183
|
+
*
|
|
184
|
+
* @param description - Description of the change about to be made
|
|
185
|
+
* @param workflow - Optional workflow to push (uses store if not provided)
|
|
186
|
+
*/
|
|
187
|
+
function pushToHistory(description, workflow) {
|
|
188
|
+
if (!historyEnabled || isRestoringFromHistory) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const workflowToPush = workflow ?? get(workflowStore);
|
|
192
|
+
if (workflowToPush) {
|
|
193
|
+
historyService.push(workflowToPush, { description });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
139
196
|
/**
|
|
140
197
|
* Get the current workflow
|
|
141
198
|
*
|
|
@@ -218,7 +275,7 @@ export const workflowActions = {
|
|
|
218
275
|
/**
|
|
219
276
|
* Initialize workflow (from load or new)
|
|
220
277
|
*
|
|
221
|
-
* This sets the initial saved snapshot
|
|
278
|
+
* This sets the initial saved snapshot, clears dirty state, and initializes history.
|
|
222
279
|
*/
|
|
223
280
|
initialize: (workflow) => {
|
|
224
281
|
workflowStore.set(workflow);
|
|
@@ -228,14 +285,30 @@ export const workflowActions = {
|
|
|
228
285
|
if (onDirtyStateChangeCallback) {
|
|
229
286
|
onDirtyStateChangeCallback(false);
|
|
230
287
|
}
|
|
288
|
+
// Initialize history with the loaded workflow
|
|
289
|
+
historyService.initialize(workflow);
|
|
231
290
|
},
|
|
232
291
|
/**
|
|
233
292
|
* Update the entire workflow
|
|
293
|
+
*
|
|
294
|
+
* Note: This is typically called from SvelteFlow sync and should not push to history
|
|
295
|
+
* for every small change. History is pushed by specific actions or drag handlers.
|
|
234
296
|
*/
|
|
235
297
|
updateWorkflow: (workflow) => {
|
|
236
298
|
workflowStore.set(workflow);
|
|
237
299
|
notifyWorkflowChange('metadata');
|
|
238
300
|
},
|
|
301
|
+
/**
|
|
302
|
+
* Restore workflow from history (undo/redo)
|
|
303
|
+
*
|
|
304
|
+
* This bypasses history recording to prevent recursive loops.
|
|
305
|
+
*/
|
|
306
|
+
restoreFromHistory: (workflow) => {
|
|
307
|
+
isRestoringFromHistory = true;
|
|
308
|
+
workflowStore.set(workflow);
|
|
309
|
+
notifyWorkflowChange('metadata');
|
|
310
|
+
isRestoringFromHistory = false;
|
|
311
|
+
},
|
|
239
312
|
/**
|
|
240
313
|
* Update nodes
|
|
241
314
|
*/
|
|
@@ -310,6 +383,7 @@ export const workflowActions = {
|
|
|
310
383
|
* Add a node
|
|
311
384
|
*/
|
|
312
385
|
addNode: (node) => {
|
|
386
|
+
pushToHistory('Add node');
|
|
313
387
|
workflowStore.update(($workflow) => {
|
|
314
388
|
if (!$workflow)
|
|
315
389
|
return null;
|
|
@@ -326,8 +400,12 @@ export const workflowActions = {
|
|
|
326
400
|
},
|
|
327
401
|
/**
|
|
328
402
|
* Remove a node
|
|
403
|
+
*
|
|
404
|
+
* This is an atomic operation that also removes connected edges.
|
|
405
|
+
* A single undo will restore both the node and its edges.
|
|
329
406
|
*/
|
|
330
407
|
removeNode: (nodeId) => {
|
|
408
|
+
pushToHistory('Delete node');
|
|
331
409
|
workflowStore.update(($workflow) => {
|
|
332
410
|
if (!$workflow)
|
|
333
411
|
return null;
|
|
@@ -347,6 +425,7 @@ export const workflowActions = {
|
|
|
347
425
|
* Add an edge
|
|
348
426
|
*/
|
|
349
427
|
addEdge: (edge) => {
|
|
428
|
+
pushToHistory('Add connection');
|
|
350
429
|
workflowStore.update(($workflow) => {
|
|
351
430
|
if (!$workflow)
|
|
352
431
|
return null;
|
|
@@ -365,6 +444,7 @@ export const workflowActions = {
|
|
|
365
444
|
* Remove an edge
|
|
366
445
|
*/
|
|
367
446
|
removeEdge: (edgeId) => {
|
|
447
|
+
pushToHistory('Delete connection');
|
|
368
448
|
workflowStore.update(($workflow) => {
|
|
369
449
|
if (!$workflow)
|
|
370
450
|
return null;
|
|
@@ -381,8 +461,11 @@ export const workflowActions = {
|
|
|
381
461
|
},
|
|
382
462
|
/**
|
|
383
463
|
* Update a specific node
|
|
464
|
+
*
|
|
465
|
+
* Used for config changes. Pushes to history for undo support.
|
|
384
466
|
*/
|
|
385
467
|
updateNode: (nodeId, updates) => {
|
|
468
|
+
pushToHistory('Update node config');
|
|
386
469
|
workflowStore.update(($workflow) => {
|
|
387
470
|
if (!$workflow)
|
|
388
471
|
return null;
|
|
@@ -399,11 +482,14 @@ export const workflowActions = {
|
|
|
399
482
|
},
|
|
400
483
|
/**
|
|
401
484
|
* Clear the workflow
|
|
485
|
+
*
|
|
486
|
+
* Resets the workflow and clears history.
|
|
402
487
|
*/
|
|
403
488
|
clear: () => {
|
|
404
489
|
workflowStore.set(null);
|
|
405
490
|
savedSnapshot = null;
|
|
406
491
|
isDirtyStore.set(false);
|
|
492
|
+
historyService.clear();
|
|
407
493
|
if (onDirtyStateChangeCallback) {
|
|
408
494
|
onDirtyStateChangeCallback(false);
|
|
409
495
|
}
|
|
@@ -430,8 +516,10 @@ export const workflowActions = {
|
|
|
430
516
|
* Batch update nodes and edges
|
|
431
517
|
*
|
|
432
518
|
* Useful for complex operations that update multiple things at once.
|
|
519
|
+
* Creates a single history entry for the entire batch.
|
|
433
520
|
*/
|
|
434
521
|
batchUpdate: (updates) => {
|
|
522
|
+
pushToHistory('Batch update');
|
|
435
523
|
workflowStore.update(($workflow) => {
|
|
436
524
|
if (!$workflow)
|
|
437
525
|
return null;
|
|
@@ -449,6 +537,18 @@ export const workflowActions = {
|
|
|
449
537
|
};
|
|
450
538
|
});
|
|
451
539
|
notifyWorkflowChange('metadata');
|
|
540
|
+
},
|
|
541
|
+
/**
|
|
542
|
+
* Push current state to history manually
|
|
543
|
+
*
|
|
544
|
+
* Use this before operations that modify the workflow through other means
|
|
545
|
+
* (e.g., drag operations handled by SvelteFlow directly).
|
|
546
|
+
*
|
|
547
|
+
* @param description - Description of the upcoming change
|
|
548
|
+
* @param workflow - Optional workflow to push (uses store state if not provided)
|
|
549
|
+
*/
|
|
550
|
+
pushHistory: (description, workflow) => {
|
|
551
|
+
pushToHistory(description, workflow);
|
|
452
552
|
}
|
|
453
553
|
};
|
|
454
554
|
// =========================================================================
|
package/dist/styles/base.css
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
:global(.svelte-flow__handle::before) {
|
|
52
|
-
content:
|
|
52
|
+
content: '';
|
|
53
53
|
position: absolute;
|
|
54
54
|
inset: 0;
|
|
55
55
|
margin: auto;
|
|
@@ -914,7 +914,11 @@
|
|
|
914
914
|
========================================================================= */
|
|
915
915
|
|
|
916
916
|
/* Interrupt state: Pending (awaiting user response) */
|
|
917
|
-
--fd-interrupt-pending-bg: linear-gradient(
|
|
917
|
+
--fd-interrupt-pending-bg: linear-gradient(
|
|
918
|
+
135deg,
|
|
919
|
+
var(--fd-warning-muted) 0%,
|
|
920
|
+
var(--fd-warning-muted) 100%
|
|
921
|
+
);
|
|
918
922
|
--fd-interrupt-pending-border: var(--fd-warning);
|
|
919
923
|
--fd-interrupt-pending-shadow: rgba(245, 158, 11, 0.15);
|
|
920
924
|
--fd-interrupt-pending-avatar: var(--fd-warning);
|
|
@@ -922,7 +926,11 @@
|
|
|
922
926
|
--fd-interrupt-pending-text-light: var(--fd-warning-hover);
|
|
923
927
|
|
|
924
928
|
/* Interrupt state: Completed (response received - neutral) */
|
|
925
|
-
--fd-interrupt-completed-bg: linear-gradient(
|
|
929
|
+
--fd-interrupt-completed-bg: linear-gradient(
|
|
930
|
+
135deg,
|
|
931
|
+
var(--fd-info-muted) 0%,
|
|
932
|
+
var(--fd-primary-muted) 100%
|
|
933
|
+
);
|
|
926
934
|
--fd-interrupt-completed-border: var(--fd-primary);
|
|
927
935
|
--fd-interrupt-completed-shadow: rgba(59, 130, 246, 0.15);
|
|
928
936
|
--fd-interrupt-completed-avatar: var(--fd-primary);
|
|
@@ -938,7 +946,11 @@
|
|
|
938
946
|
--fd-interrupt-cancelled-text-light: var(--fd-muted-foreground);
|
|
939
947
|
|
|
940
948
|
/* Interrupt state: Error */
|
|
941
|
-
--fd-interrupt-error-bg: linear-gradient(
|
|
949
|
+
--fd-interrupt-error-bg: linear-gradient(
|
|
950
|
+
135deg,
|
|
951
|
+
var(--fd-error-muted) 0%,
|
|
952
|
+
var(--fd-error-muted) 100%
|
|
953
|
+
);
|
|
942
954
|
--fd-interrupt-error-border: var(--fd-error);
|
|
943
955
|
--fd-interrupt-error-shadow: rgba(239, 68, 68, 0.15);
|
|
944
956
|
--fd-interrupt-error-avatar: var(--fd-error);
|
|
@@ -953,8 +965,16 @@
|
|
|
953
965
|
--fd-interrupt-prompt-border-error: rgba(239, 68, 68, 0.2);
|
|
954
966
|
|
|
955
967
|
/* Interrupt button tokens */
|
|
956
|
-
--fd-interrupt-btn-primary-bg: linear-gradient(
|
|
957
|
-
|
|
968
|
+
--fd-interrupt-btn-primary-bg: linear-gradient(
|
|
969
|
+
135deg,
|
|
970
|
+
var(--fd-primary) 0%,
|
|
971
|
+
var(--fd-primary-hover) 100%
|
|
972
|
+
);
|
|
973
|
+
--fd-interrupt-btn-primary-bg-hover: linear-gradient(
|
|
974
|
+
135deg,
|
|
975
|
+
var(--fd-primary-hover) 0%,
|
|
976
|
+
var(--fd-primary-hover) 100%
|
|
977
|
+
);
|
|
958
978
|
--fd-interrupt-btn-primary-shadow: rgba(59, 130, 246, 0.3);
|
|
959
979
|
--fd-interrupt-btn-secondary-bg: var(--fd-muted);
|
|
960
980
|
--fd-interrupt-btn-secondary-border: var(--fd-border);
|
|
@@ -987,7 +1007,7 @@
|
|
|
987
1007
|
}
|
|
988
1008
|
|
|
989
1009
|
/* Dark mode overrides for interrupt component tokens */
|
|
990
|
-
[data-theme=
|
|
1010
|
+
[data-theme='dark'] {
|
|
991
1011
|
/* Interrupt prompt inner styling - dark background for dark mode */
|
|
992
1012
|
--fd-interrupt-prompt-bg: rgba(30, 30, 35, 0.95);
|
|
993
1013
|
--fd-interrupt-prompt-border-pending: rgba(251, 191, 36, 0.25);
|
|
@@ -1216,4 +1236,4 @@
|
|
|
1216
1236
|
|
|
1217
1237
|
.markdown-display--large h3 {
|
|
1218
1238
|
font-size: 1.25rem;
|
|
1219
|
-
}
|
|
1239
|
+
}
|
package/dist/styles/toast.css
CHANGED
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
max-width: 350px;
|
|
29
29
|
font-size: var(--fd-text-sm);
|
|
30
30
|
line-height: 1.3;
|
|
31
|
-
transition:
|
|
31
|
+
transition:
|
|
32
|
+
background var(--fd-transition-fast),
|
|
33
|
+
border-color var(--fd-transition-fast),
|
|
32
34
|
box-shadow var(--fd-transition-fast);
|
|
33
35
|
}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
Designed for softer contrast and reduced eye strain
|
|
285
285
|
========================================================================= */
|
|
286
286
|
|
|
287
|
-
[data-theme=
|
|
287
|
+
[data-theme='dark'] {
|
|
288
288
|
/* ----- SURFACES (Backgrounds) - Softer contrast ----- */
|
|
289
289
|
--fd-background: #1a1a1e;
|
|
290
290
|
--fd-foreground: var(--_gray-2);
|
|
@@ -399,4 +399,4 @@
|
|
|
399
399
|
|
|
400
400
|
/* ----- LAYOUT BACKGROUND (darker gradient for dark mode) ----- */
|
|
401
401
|
--fd-layout-background: linear-gradient(135deg, #141418 0%, #1a1a2e 50%, #16162a 100%);
|
|
402
|
-
}
|
|
402
|
+
}
|
package/dist/types/settings.d.ts
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
* - 'dark': Force dark theme
|
|
13
13
|
* - 'auto': Follow system preference
|
|
14
14
|
*/
|
|
15
|
-
export type ThemePreference =
|
|
15
|
+
export type ThemePreference = 'light' | 'dark' | 'auto';
|
|
16
16
|
/**
|
|
17
17
|
* Resolved theme (actual applied theme, never 'auto')
|
|
18
18
|
*/
|
|
19
|
-
export type ResolvedTheme =
|
|
19
|
+
export type ResolvedTheme = 'light' | 'dark';
|
|
20
20
|
/**
|
|
21
21
|
* Theme-related settings
|
|
22
22
|
*/
|
|
@@ -167,7 +167,7 @@ export declare const SETTINGS_STORAGE_KEY = "flowdrop-settings";
|
|
|
167
167
|
/**
|
|
168
168
|
* API sync status
|
|
169
169
|
*/
|
|
170
|
-
export type SyncStatus =
|
|
170
|
+
export type SyncStatus = 'idle' | 'syncing' | 'synced' | 'error';
|
|
171
171
|
/**
|
|
172
172
|
* Settings store state including sync metadata
|
|
173
173
|
*/
|
package/dist/types/settings.js
CHANGED
|
@@ -9,32 +9,26 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* All available settings categories
|
|
11
11
|
*/
|
|
12
|
-
export const SETTINGS_CATEGORIES = [
|
|
13
|
-
"theme",
|
|
14
|
-
"editor",
|
|
15
|
-
"ui",
|
|
16
|
-
"behavior",
|
|
17
|
-
"api"
|
|
18
|
-
];
|
|
12
|
+
export const SETTINGS_CATEGORIES = ['theme', 'editor', 'ui', 'behavior', 'api'];
|
|
19
13
|
/**
|
|
20
14
|
* Human-readable labels for settings categories
|
|
21
15
|
*/
|
|
22
16
|
export const SETTINGS_CATEGORY_LABELS = {
|
|
23
|
-
theme:
|
|
24
|
-
editor:
|
|
25
|
-
ui:
|
|
26
|
-
behavior:
|
|
27
|
-
api:
|
|
17
|
+
theme: 'Theme',
|
|
18
|
+
editor: 'Editor',
|
|
19
|
+
ui: 'UI',
|
|
20
|
+
behavior: 'Behavior',
|
|
21
|
+
api: 'API'
|
|
28
22
|
};
|
|
29
23
|
/**
|
|
30
24
|
* Icons for settings categories (Iconify icon names)
|
|
31
25
|
*/
|
|
32
26
|
export const SETTINGS_CATEGORY_ICONS = {
|
|
33
|
-
theme:
|
|
34
|
-
editor:
|
|
35
|
-
ui:
|
|
36
|
-
behavior:
|
|
37
|
-
api:
|
|
27
|
+
theme: 'mdi:palette',
|
|
28
|
+
editor: 'mdi:grid',
|
|
29
|
+
ui: 'mdi:view-dashboard',
|
|
30
|
+
behavior: 'mdi:cog-play',
|
|
31
|
+
api: 'mdi:api'
|
|
38
32
|
};
|
|
39
33
|
// =========================================================================
|
|
40
34
|
// Default Settings
|
|
@@ -43,7 +37,7 @@ export const SETTINGS_CATEGORY_ICONS = {
|
|
|
43
37
|
* Default theme settings
|
|
44
38
|
*/
|
|
45
39
|
export const DEFAULT_THEME_SETTINGS = {
|
|
46
|
-
preference:
|
|
40
|
+
preference: 'auto'
|
|
47
41
|
};
|
|
48
42
|
/**
|
|
49
43
|
* Default editor settings
|
|
@@ -98,4 +92,4 @@ export const DEFAULT_SETTINGS = {
|
|
|
98
92
|
/**
|
|
99
93
|
* localStorage key for persisting settings
|
|
100
94
|
*/
|
|
101
|
-
export const SETTINGS_STORAGE_KEY =
|
|
95
|
+
export const SETTINGS_STORAGE_KEY = 'flowdrop-settings';
|