@d34dman/flowdrop 0.0.61 → 0.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -1
- package/dist/api/client.d.ts +4 -0
- package/dist/api/client.js +6 -1
- package/dist/api/enhanced-client.js +7 -6
- package/dist/components/App.svelte +143 -219
- package/dist/components/CanvasBanner.stories.svelte +25 -0
- package/dist/components/CanvasBanner.stories.svelte.d.ts +27 -0
- package/dist/components/CanvasBanner.svelte +2 -2
- package/dist/components/ConfigForm.svelte +37 -36
- package/dist/components/ConfigPanel.stories.svelte +38 -0
- package/dist/components/ConfigPanel.stories.svelte.d.ts +27 -0
- package/dist/components/ConfigPanel.svelte +2 -2
- package/dist/components/ConnectionLine.svelte +2 -2
- package/dist/components/FlowDropZone.svelte +18 -2
- package/dist/components/FlowDropZone.svelte.d.ts +2 -0
- package/dist/components/LoadingSpinner.stories.svelte +30 -0
- package/dist/components/LoadingSpinner.stories.svelte.d.ts +27 -0
- package/dist/components/Logo.stories.svelte +22 -0
- package/dist/components/Logo.stories.svelte.d.ts +27 -0
- package/dist/components/Logo.svelte +33 -13
- package/dist/components/Logo.svelte.d.ts +1 -1
- package/dist/components/MarkdownDisplay.stories.svelte +21 -0
- package/dist/components/MarkdownDisplay.stories.svelte.d.ts +27 -0
- package/dist/components/MarkdownDisplay.svelte +4 -3
- package/dist/components/Navbar.stories.svelte +41 -0
- package/dist/components/Navbar.stories.svelte.d.ts +27 -0
- package/dist/components/Navbar.svelte +4 -4
- package/dist/components/NodeSidebar.svelte +12 -12
- package/dist/components/NodeStatusOverlay.stories.svelte +74 -0
- package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +27 -0
- package/dist/components/PipelineStatus.svelte +11 -4
- package/dist/components/PortCoordinateTracker.svelte +1 -1
- package/dist/components/SchemaForm.stories.svelte +101 -0
- package/dist/components/SchemaForm.stories.svelte.d.ts +27 -0
- package/dist/components/SchemaForm.svelte +17 -12
- package/dist/components/SettingsModal.svelte +3 -3
- package/dist/components/SettingsPanel.svelte +23 -22
- package/dist/components/StatusIcon.stories.svelte +60 -0
- package/dist/components/StatusIcon.stories.svelte.d.ts +27 -0
- package/dist/components/StatusIcon.svelte +7 -0
- package/dist/components/StatusLabel.stories.svelte +17 -0
- package/dist/components/StatusLabel.stories.svelte.d.ts +27 -0
- package/dist/components/ThemeToggle.stories.svelte +25 -0
- package/dist/components/ThemeToggle.stories.svelte.d.ts +27 -0
- package/dist/components/ThemeToggle.svelte +8 -8
- package/dist/components/UniversalNode.svelte +1 -1
- package/dist/components/WorkflowEditor.svelte +298 -294
- package/dist/components/form/FormAutocomplete.svelte +20 -19
- package/dist/components/form/FormCheckboxGroup.stories.svelte +28 -0
- package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormField.svelte +3 -3
- package/dist/components/form/FormFieldLight.svelte +2 -2
- package/dist/components/form/FormFieldWrapper.stories.svelte +31 -0
- package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormFieldset.svelte +7 -7
- package/dist/components/form/FormNumberField.stories.svelte +33 -0
- package/dist/components/form/FormNumberField.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormRangeField.stories.svelte +31 -0
- package/dist/components/form/FormRangeField.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormSelect.stories.svelte +50 -0
- package/dist/components/form/FormSelect.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormTemplateEditor.svelte +2 -1
- package/dist/components/form/FormTextField.stories.svelte +30 -0
- package/dist/components/form/FormTextField.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormTextarea.stories.svelte +31 -0
- package/dist/components/form/FormTextarea.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormToggle.stories.svelte +30 -0
- package/dist/components/form/FormToggle.stories.svelte.d.ts +27 -0
- package/dist/components/form/FormUISchemaRenderer.svelte +1 -1
- package/dist/components/form/types.d.ts +15 -47
- package/dist/components/interrupt/ChoicePrompt.stories.svelte +43 -0
- package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +27 -0
- package/dist/components/interrupt/ChoicePrompt.svelte +24 -24
- package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +49 -0
- package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +27 -0
- package/dist/components/interrupt/ConfirmationPrompt.svelte +19 -19
- package/dist/components/interrupt/FormPrompt.svelte +15 -15
- package/dist/components/interrupt/InterruptBubble.svelte +202 -236
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +1 -1
- package/dist/components/interrupt/ReviewPrompt.stories.svelte +46 -0
- package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +27 -0
- package/dist/components/interrupt/ReviewPrompt.svelte +842 -0
- package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +23 -0
- package/dist/components/interrupt/TextInputPrompt.stories.svelte +34 -0
- package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +27 -0
- package/dist/components/interrupt/TextInputPrompt.svelte +21 -21
- package/dist/components/nodes/GatewayNode.stories.svelte +76 -0
- package/dist/components/nodes/GatewayNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -17
- package/dist/components/nodes/IdeaNode.stories.svelte +48 -0
- package/dist/components/nodes/IdeaNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/IdeaNode.svelte +10 -26
- package/dist/components/nodes/NotesNode.stories.svelte +69 -0
- package/dist/components/nodes/NotesNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/NotesNode.svelte +8 -8
- package/dist/components/nodes/SimpleNode.stories.svelte +101 -0
- package/dist/components/nodes/SimpleNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/SimpleNode.svelte +16 -24
- package/dist/components/nodes/SquareNode.stories.svelte +56 -0
- package/dist/components/nodes/SquareNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/SquareNode.svelte +13 -21
- package/dist/components/nodes/TerminalNode.stories.svelte +25 -0
- package/dist/components/nodes/TerminalNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/TerminalNode.svelte +6 -6
- package/dist/components/nodes/ToolNode.stories.svelte +71 -0
- package/dist/components/nodes/ToolNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/ToolNode.svelte +7 -15
- package/dist/components/nodes/WorkflowNode.stories.svelte +50 -0
- package/dist/components/nodes/WorkflowNode.stories.svelte.d.ts +26 -0
- package/dist/components/nodes/WorkflowNode.svelte +13 -13
- package/dist/components/playground/ChatPanel.svelte +48 -48
- package/dist/components/playground/ExecutionLogs.svelte +23 -23
- package/dist/components/playground/InputCollector.svelte +24 -24
- package/dist/components/playground/MessageBubble.stories.svelte +49 -0
- package/dist/components/playground/MessageBubble.stories.svelte.d.ts +27 -0
- package/dist/components/playground/MessageBubble.svelte +49 -46
- package/dist/components/playground/Playground.svelte +194 -129
- package/dist/components/playground/PlaygroundModal.svelte +5 -5
- package/dist/components/playground/SessionManager.svelte +26 -26
- package/dist/config/constants.d.ts +22 -0
- package/dist/config/constants.js +22 -0
- package/dist/config/endpoints.d.ts +19 -0
- package/dist/config/runtimeConfig.js +2 -1
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.js +9 -1
- package/dist/editor/index.d.ts +13 -9
- package/dist/editor/index.js +15 -11
- package/dist/form/code.d.ts +2 -1
- package/dist/form/code.js +1 -3
- package/dist/form/markdown.d.ts +2 -1
- package/dist/form/markdown.js +1 -3
- package/dist/helpers/workflowEditorHelper.js +13 -9
- package/dist/mocks/app-forms.js +1 -0
- package/dist/mocks/app-navigation.js +3 -1
- package/dist/mocks/app-stores.d.ts +4 -4
- package/dist/playground/index.d.ts +4 -3
- package/dist/playground/index.js +12 -10
- package/dist/playground/mount.js +6 -13
- package/dist/services/agentSpecExecutionService.js +2 -1
- package/dist/services/api.js +10 -18
- package/dist/services/apiVariableService.js +2 -1
- package/dist/services/autoSaveService.d.ts +3 -3
- package/dist/services/autoSaveService.js +21 -17
- package/dist/services/categoriesApi.js +13 -5
- package/dist/services/draftStorage.js +5 -4
- package/dist/services/dynamicSchemaService.js +4 -4
- package/dist/services/globalSave.d.ts +60 -11
- package/dist/services/globalSave.js +160 -83
- package/dist/services/historyService.d.ts +2 -1
- package/dist/services/historyService.js +7 -3
- package/dist/services/interruptService.js +9 -8
- package/dist/services/nodeExecutionService.js +14 -6
- package/dist/services/playgroundService.js +2 -1
- package/dist/services/portConfigApi.js +11 -7
- package/dist/services/toastService.d.ts +1 -1
- package/dist/services/toastService.js +6 -5
- package/dist/services/variableService.js +3 -2
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.js +1 -1
- package/dist/stores/{categoriesStore.d.ts → categoriesStore.svelte.d.ts} +3 -3
- package/dist/stores/{categoriesStore.js → categoriesStore.svelte.js} +15 -18
- package/dist/stores/editorStateMachine.svelte.d.ts +42 -0
- package/dist/stores/editorStateMachine.svelte.js +132 -0
- package/dist/stores/{historyStore.d.ts → historyStore.svelte.d.ts} +18 -15
- package/dist/stores/{historyStore.js → historyStore.svelte.js} +40 -21
- package/dist/stores/{interruptStore.d.ts → interruptStore.svelte.d.ts} +16 -15
- package/dist/stores/{interruptStore.js → interruptStore.svelte.js} +85 -94
- package/dist/stores/{playgroundStore.d.ts → playgroundStore.svelte.d.ts} +41 -33
- package/dist/stores/{playgroundStore.js → playgroundStore.svelte.js} +164 -84
- package/dist/stores/{portCoordinateStore.d.ts → portCoordinateStore.svelte.d.ts} +10 -4
- package/dist/stores/{portCoordinateStore.js → portCoordinateStore.svelte.js} +38 -35
- package/dist/stores/{settingsStore.d.ts → settingsStore.svelte.d.ts} +45 -28
- package/dist/stores/{settingsStore.js → settingsStore.svelte.js} +169 -128
- package/dist/stores/{workflowStore.d.ts → workflowStore.svelte.d.ts} +101 -65
- package/dist/stores/{workflowStore.js → workflowStore.svelte.js} +285 -239
- package/dist/stories/CanvasDecorator.svelte +50 -0
- package/dist/stories/CanvasDecorator.svelte.d.ts +8 -0
- package/dist/stories/NodeDecorator.svelte +74 -0
- package/dist/stories/NodeDecorator.svelte.d.ts +8 -0
- package/dist/stories/utils.d.ts +93 -0
- package/dist/stories/utils.js +122 -0
- package/dist/styles/base.css +114 -61
- package/dist/styles/toast.css +2 -2
- package/dist/styles/tokens.css +250 -185
- package/dist/svelte-app.d.ts +0 -6
- package/dist/svelte-app.js +13 -31
- package/dist/types/index.d.ts +2 -0
- package/dist/types/interrupt.d.ts +89 -5
- package/dist/types/interrupt.js +13 -1
- package/dist/types/playground.d.ts +5 -0
- package/dist/types/settings.js +1 -1
- package/dist/utils/colors.js +4 -4
- package/dist/utils/connections.js +33 -8
- package/dist/utils/icons.js +1 -1
- package/dist/utils/logger.d.ts +47 -0
- package/dist/utils/logger.js +72 -0
- package/dist/utils/nodeWrapper.js +1 -1
- package/dist/utils/sanitize.d.ts +19 -0
- package/dist/utils/sanitize.js +31 -0
- package/dist/utils/validation.d.ts +29 -0
- package/dist/utils/validation.js +39 -0
- package/package.json +243 -232
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Global Save Service
|
|
3
|
-
* Provides save and export functionality that can be accessed from anywhere in the app
|
|
4
|
-
* This
|
|
3
|
+
* Provides save and export functionality that can be accessed from anywhere in the app.
|
|
4
|
+
* This is the single source of truth for save/export logic.
|
|
5
|
+
*
|
|
6
|
+
* App.svelte delegates to globalSaveWorkflow() / globalExportWorkflow() rather than
|
|
7
|
+
* reimplementing the logic, ensuring "blur active element" flushing and metadata
|
|
8
|
+
* construction happen in exactly one place.
|
|
5
9
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
10
|
+
import { tick } from 'svelte';
|
|
11
|
+
import { getWorkflowStore, workflowActions, markAsSaved as storeMarkAsSaved } from '../stores/workflowStore.svelte.js';
|
|
8
12
|
import { workflowApi, setEndpointConfig } from './api.js';
|
|
9
13
|
import { createEndpointConfig } from '../config/endpoints.js';
|
|
10
14
|
import { v4 as uuidv4 } from 'uuid';
|
|
15
|
+
import { DEFAULT_WORKFLOW_FORMAT } from '../types/index.js';
|
|
11
16
|
import { apiToasts, workflowToasts, dismissToast } from './toastService.js';
|
|
17
|
+
import { DEFAULT_FEATURES } from '../types/events.js';
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Internal helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
12
21
|
/**
|
|
13
|
-
* Ensure API configuration is initialized
|
|
22
|
+
* Ensure API configuration is initialized.
|
|
14
23
|
* This is needed when the global save function is called from the layout component
|
|
15
|
-
* which doesn't initialize the API configuration like the App component does
|
|
24
|
+
* which doesn't initialize the API configuration like the App component does.
|
|
16
25
|
*/
|
|
17
26
|
async function ensureApiConfiguration() {
|
|
18
|
-
// Check if we need to initialize the API configuration
|
|
19
|
-
// We'll check if the endpointConfig is already set by importing the api module
|
|
20
27
|
try {
|
|
21
|
-
// Import the api module to check if endpointConfig is already set
|
|
22
28
|
const { getEndpointConfig } = await import('./api.js');
|
|
23
|
-
// Try to get the current configuration
|
|
24
29
|
const currentConfig = getEndpointConfig();
|
|
25
30
|
if (currentConfig && currentConfig.baseUrl) {
|
|
26
31
|
return;
|
|
@@ -29,25 +34,15 @@ async function ensureApiConfiguration() {
|
|
|
29
34
|
catch {
|
|
30
35
|
// Could not check existing API configuration, initializing
|
|
31
36
|
}
|
|
32
|
-
// API configuration is not initialized
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (typeof window !== 'undefined') {
|
|
37
|
-
if (window.location.hostname === 'localhost' && window.location.port === '5173') {
|
|
38
|
-
return '/api/flowdrop';
|
|
39
|
-
}
|
|
40
|
-
// Otherwise, use the current domain
|
|
41
|
-
return `${window.location.protocol}//${window.location.host}/api/flowdrop`;
|
|
42
|
-
}
|
|
43
|
-
// Fallback to relative path
|
|
44
|
-
return '/api/flowdrop';
|
|
45
|
-
})();
|
|
37
|
+
// API configuration is not initialized — derive URL from window.location when available
|
|
38
|
+
const apiBaseUrl = typeof window !== 'undefined'
|
|
39
|
+
? `${window.location.protocol}//${window.location.host}/api/flowdrop`
|
|
40
|
+
: '/api/flowdrop';
|
|
46
41
|
const config = createEndpointConfig(apiBaseUrl, {
|
|
47
42
|
auth: {
|
|
48
|
-
type: 'none'
|
|
43
|
+
type: 'none'
|
|
49
44
|
},
|
|
50
|
-
timeout: 10000,
|
|
45
|
+
timeout: 10000,
|
|
51
46
|
retry: {
|
|
52
47
|
enabled: true,
|
|
53
48
|
maxAttempts: 2,
|
|
@@ -58,89 +53,182 @@ async function ensureApiConfiguration() {
|
|
|
58
53
|
setEndpointConfig(config);
|
|
59
54
|
}
|
|
60
55
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
56
|
+
* Flush any pending form changes by blurring the active element.
|
|
57
|
+
* This ensures focusout handlers (like ConfigForm's handleFormBlur)
|
|
58
|
+
* sync local state to the global store before we read it.
|
|
59
|
+
*
|
|
60
|
+
* Must be called once, in this file only, so the logic lives in exactly one place.
|
|
63
61
|
*/
|
|
64
|
-
|
|
65
|
-
// Flush any pending form changes by blurring the active element.
|
|
66
|
-
// This ensures focusout handlers (like ConfigForm's handleFormBlur)
|
|
67
|
-
// sync local state to the global store before we read it.
|
|
62
|
+
async function flushPendingFormChanges() {
|
|
68
63
|
if (typeof document !== 'undefined' && document.activeElement instanceof HTMLElement) {
|
|
69
64
|
document.activeElement.blur();
|
|
70
65
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
// Wait for any pending DOM / Svelte reactive updates before reading the store
|
|
67
|
+
await tick();
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Public API
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
/**
|
|
73
|
+
* Save the current workflow to the backend.
|
|
74
|
+
*
|
|
75
|
+
* This is the single source of truth for save logic. App.svelte delegates
|
|
76
|
+
* to this function rather than reimplementing the steps.
|
|
77
|
+
*
|
|
78
|
+
* Steps performed:
|
|
79
|
+
* 1. Flush pending form changes (blur active element + tick)
|
|
80
|
+
* 2. Optionally call onBeforeSave — return false cancels the save
|
|
81
|
+
* 3. Build the canonical Workflow object (preserving metadata, format, etc.)
|
|
82
|
+
* 4. Persist via enhanced apiClient or legacy workflowApi
|
|
83
|
+
* 5. Update the store if the server assigned a new ID
|
|
84
|
+
* 6. Call onMarkAsSaved / onAfterSave hooks
|
|
85
|
+
* 7. Show toast notifications (respecting features.showToasts)
|
|
86
|
+
*/
|
|
87
|
+
export async function globalSaveWorkflow(options = {}) {
|
|
88
|
+
const { apiClient, eventHandlers, onMarkAsSaved } = options;
|
|
89
|
+
const features = { ...DEFAULT_FEATURES, ...options.features };
|
|
90
|
+
// Step 1 — Flush pending form changes (single location for this logic)
|
|
91
|
+
await flushPendingFormChanges();
|
|
92
|
+
// Get current workflow from global store after flush
|
|
93
|
+
const currentWorkflow = getWorkflowStore();
|
|
94
|
+
if (!currentWorkflow) {
|
|
95
|
+
if (features.showToasts) {
|
|
82
96
|
apiToasts.error('Save workflow', 'No workflow to save');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
// Determine the workflow ID
|
|
86
|
-
let workflowId;
|
|
87
|
-
if (currentWorkflow.id) {
|
|
88
|
-
workflowId = currentWorkflow.id;
|
|
89
97
|
}
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Step 2 — Allow the parent to cancel the save
|
|
101
|
+
if (eventHandlers?.onBeforeSave) {
|
|
102
|
+
const shouldContinue = await eventHandlers.onBeforeSave(currentWorkflow);
|
|
103
|
+
if (shouldContinue === false) {
|
|
104
|
+
return;
|
|
92
105
|
}
|
|
93
|
-
|
|
106
|
+
}
|
|
107
|
+
const loadingToast = features.showToasts ? apiToasts.loading('Saving workflow') : null;
|
|
108
|
+
try {
|
|
109
|
+
// Ensure API configuration is initialised (needed when called outside App.svelte)
|
|
110
|
+
await ensureApiConfiguration();
|
|
111
|
+
// Step 3 — Build the canonical workflow object.
|
|
112
|
+
// Preserve all existing metadata fields (format, tags, etc.) so nothing is dropped.
|
|
113
|
+
const workflowId = currentWorkflow.id || uuidv4();
|
|
94
114
|
const finalWorkflow = {
|
|
95
115
|
id: workflowId,
|
|
96
116
|
name: currentWorkflow.name || 'Untitled Workflow',
|
|
117
|
+
description: currentWorkflow.description || '',
|
|
97
118
|
nodes: currentWorkflow.nodes || [],
|
|
98
119
|
edges: currentWorkflow.edges || [],
|
|
99
120
|
metadata: {
|
|
100
|
-
|
|
121
|
+
...currentWorkflow.metadata,
|
|
122
|
+
version: currentWorkflow.metadata?.version || '1.0.0',
|
|
123
|
+
format: currentWorkflow.metadata?.format || DEFAULT_WORKFLOW_FORMAT,
|
|
101
124
|
createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
|
|
102
125
|
updatedAt: new Date().toISOString()
|
|
103
126
|
}
|
|
104
127
|
};
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
// Step 4 — Persist
|
|
129
|
+
let savedWorkflow;
|
|
130
|
+
if (apiClient) {
|
|
131
|
+
// Enhanced client path — detects existing workflows by non-UUID ID
|
|
132
|
+
const isExistingWorkflow = finalWorkflow.id.length > 0 &&
|
|
133
|
+
!finalWorkflow.id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
|
|
134
|
+
if (isExistingWorkflow) {
|
|
135
|
+
savedWorkflow = await apiClient.updateWorkflow(finalWorkflow.id, finalWorkflow);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
savedWorkflow = await apiClient.saveWorkflow(finalWorkflow);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Legacy path
|
|
143
|
+
savedWorkflow = await workflowApi.saveWorkflow(finalWorkflow);
|
|
144
|
+
}
|
|
145
|
+
// Step 5 — If the server assigned a new ID, sync the store
|
|
146
|
+
if (savedWorkflow.id && savedWorkflow.id !== finalWorkflow.id) {
|
|
147
|
+
workflowActions.batchUpdate({
|
|
148
|
+
nodes: finalWorkflow.nodes,
|
|
149
|
+
edges: finalWorkflow.edges,
|
|
150
|
+
name: finalWorkflow.name,
|
|
151
|
+
metadata: {
|
|
152
|
+
...finalWorkflow.metadata,
|
|
153
|
+
...savedWorkflow.metadata
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// Step 6a — Mark dirty state as clean
|
|
158
|
+
if (onMarkAsSaved) {
|
|
159
|
+
onMarkAsSaved();
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Fallback: call the store's own markAsSaved if no callback was provided
|
|
163
|
+
storeMarkAsSaved();
|
|
164
|
+
}
|
|
165
|
+
// Show success toast
|
|
107
166
|
if (loadingToast)
|
|
108
167
|
dismissToast(loadingToast);
|
|
109
|
-
|
|
168
|
+
if (features.showToasts) {
|
|
169
|
+
workflowToasts.saved(finalWorkflow.name);
|
|
170
|
+
}
|
|
171
|
+
// Step 6b — After-save hook
|
|
172
|
+
if (eventHandlers?.onAfterSave) {
|
|
173
|
+
await eventHandlers.onAfterSave(savedWorkflow);
|
|
174
|
+
}
|
|
110
175
|
}
|
|
111
176
|
catch (error) {
|
|
112
|
-
// Dismiss loading toast and show error toast
|
|
113
177
|
if (loadingToast)
|
|
114
178
|
dismissToast(loadingToast);
|
|
115
|
-
|
|
179
|
+
const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
|
|
180
|
+
// onSaveError hook
|
|
181
|
+
const currentWorkflowForError = getWorkflowStore();
|
|
182
|
+
if (eventHandlers?.onSaveError && currentWorkflowForError) {
|
|
183
|
+
await eventHandlers.onSaveError(errorObj, currentWorkflowForError);
|
|
184
|
+
}
|
|
185
|
+
// onApiError hook — return true suppresses the default toast
|
|
186
|
+
let suppressToast = false;
|
|
187
|
+
if (eventHandlers?.onApiError) {
|
|
188
|
+
suppressToast = eventHandlers.onApiError(errorObj, 'save') === true;
|
|
189
|
+
}
|
|
190
|
+
if (features.showToasts && !suppressToast) {
|
|
191
|
+
apiToasts.error('Save workflow', errorObj.message);
|
|
192
|
+
}
|
|
116
193
|
throw error;
|
|
117
194
|
}
|
|
118
195
|
}
|
|
119
196
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
197
|
+
* Export the current workflow as a downloadable JSON file.
|
|
198
|
+
*
|
|
199
|
+
* This is the single source of truth for export logic. App.svelte delegates
|
|
200
|
+
* to this function rather than reimplementing the steps.
|
|
201
|
+
*
|
|
202
|
+
* Preserves all metadata fields (format, tags, etc.) consistently with save.
|
|
122
203
|
*/
|
|
123
|
-
export async function globalExportWorkflow() {
|
|
204
|
+
export async function globalExportWorkflow(options = {}) {
|
|
205
|
+
const features = { ...DEFAULT_FEATURES, ...options.features };
|
|
124
206
|
try {
|
|
125
|
-
//
|
|
126
|
-
|
|
207
|
+
// Flush pending changes before exporting (same discipline as save)
|
|
208
|
+
await flushPendingFormChanges();
|
|
209
|
+
const currentWorkflow = getWorkflowStore();
|
|
127
210
|
if (!currentWorkflow) {
|
|
128
|
-
|
|
211
|
+
if (features.showToasts) {
|
|
212
|
+
apiToasts.error('Export workflow', 'No workflow to export');
|
|
213
|
+
}
|
|
129
214
|
return;
|
|
130
215
|
}
|
|
131
|
-
//
|
|
216
|
+
// Build the canonical export object — preserve all metadata fields
|
|
132
217
|
const finalWorkflow = {
|
|
133
218
|
id: currentWorkflow.id || 'untitled-workflow',
|
|
134
219
|
name: currentWorkflow.name || 'Untitled Workflow',
|
|
220
|
+
description: currentWorkflow.description || '',
|
|
135
221
|
nodes: currentWorkflow.nodes || [],
|
|
136
222
|
edges: currentWorkflow.edges || [],
|
|
137
223
|
metadata: {
|
|
138
|
-
|
|
224
|
+
...currentWorkflow.metadata,
|
|
225
|
+
version: currentWorkflow.metadata?.version || '1.0.0',
|
|
226
|
+
format: currentWorkflow.metadata?.format || DEFAULT_WORKFLOW_FORMAT,
|
|
139
227
|
createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
|
|
140
228
|
updatedAt: new Date().toISOString()
|
|
141
229
|
}
|
|
142
230
|
};
|
|
143
|
-
//
|
|
231
|
+
// Trigger browser download
|
|
144
232
|
const dataStr = JSON.stringify(finalWorkflow, null, 2);
|
|
145
233
|
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
146
234
|
const url = URL.createObjectURL(dataBlob);
|
|
@@ -149,23 +237,12 @@ export async function globalExportWorkflow() {
|
|
|
149
237
|
link.download = `${finalWorkflow.name}.json`;
|
|
150
238
|
link.click();
|
|
151
239
|
URL.revokeObjectURL(url);
|
|
152
|
-
|
|
153
|
-
|
|
240
|
+
if (features.showToasts) {
|
|
241
|
+
workflowToasts.exported(finalWorkflow.name);
|
|
242
|
+
}
|
|
154
243
|
}
|
|
155
244
|
catch (error) {
|
|
156
|
-
|
|
157
|
-
apiToasts.error('Export workflow',
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Initialize global save functions on window object for external access
|
|
162
|
-
* This allows the functions to be called from anywhere in the application
|
|
163
|
-
*/
|
|
164
|
-
export function initializeGlobalSave() {
|
|
165
|
-
if (typeof window !== 'undefined') {
|
|
166
|
-
// @ts-expect-error - Adding to window for external access
|
|
167
|
-
window.flowdropGlobalSave = globalSaveWorkflow;
|
|
168
|
-
// @ts-expect-error - Adding to window for external access
|
|
169
|
-
window.flowdropGlobalExport = globalExportWorkflow;
|
|
245
|
+
const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
|
|
246
|
+
apiToasts.error('Export workflow', errorObj.message);
|
|
170
247
|
}
|
|
171
248
|
}
|
|
@@ -182,7 +182,8 @@ export declare class HistoryService {
|
|
|
182
182
|
*/
|
|
183
183
|
private pushInternal;
|
|
184
184
|
/**
|
|
185
|
-
* Trim history to stay within maxEntries limit
|
|
185
|
+
* Trim history to stay within maxEntries limit.
|
|
186
|
+
* A maxEntries of 0 means unlimited (no trimming).
|
|
186
187
|
*/
|
|
187
188
|
private trimHistory;
|
|
188
189
|
/**
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module services/historyService
|
|
8
8
|
*/
|
|
9
9
|
import { DEFAULT_BEHAVIOR_SETTINGS } from '../types/settings.js';
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
10
11
|
// =========================================================================
|
|
11
12
|
// History Service Class
|
|
12
13
|
// =========================================================================
|
|
@@ -153,7 +154,7 @@ export class HistoryService {
|
|
|
153
154
|
*/
|
|
154
155
|
startTransaction(workflow, description) {
|
|
155
156
|
if (this.inTransaction) {
|
|
156
|
-
|
|
157
|
+
logger.warn('HistoryService: Transaction already in progress, ignoring startTransaction');
|
|
157
158
|
return;
|
|
158
159
|
}
|
|
159
160
|
this.inTransaction = true;
|
|
@@ -167,7 +168,7 @@ export class HistoryService {
|
|
|
167
168
|
*/
|
|
168
169
|
commitTransaction() {
|
|
169
170
|
if (!this.inTransaction || !this.transactionSnapshot) {
|
|
170
|
-
|
|
171
|
+
logger.warn('HistoryService: No transaction in progress, ignoring commitTransaction');
|
|
171
172
|
return;
|
|
172
173
|
}
|
|
173
174
|
// Push the snapshot captured at transaction start
|
|
@@ -278,9 +279,12 @@ export class HistoryService {
|
|
|
278
279
|
this.notifyChange();
|
|
279
280
|
}
|
|
280
281
|
/**
|
|
281
|
-
* Trim history to stay within maxEntries limit
|
|
282
|
+
* Trim history to stay within maxEntries limit.
|
|
283
|
+
* A maxEntries of 0 means unlimited (no trimming).
|
|
282
284
|
*/
|
|
283
285
|
trimHistory() {
|
|
286
|
+
if (this.maxEntries <= 0)
|
|
287
|
+
return;
|
|
284
288
|
while (this.undoStack.length > this.maxEntries) {
|
|
285
289
|
this.undoStack.shift();
|
|
286
290
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { defaultInterruptPollingConfig } from '../types/interrupt.js';
|
|
11
11
|
import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
|
|
12
12
|
import { getEndpointConfig } from './api.js';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
13
14
|
/**
|
|
14
15
|
* Interrupt Service class
|
|
15
16
|
*
|
|
@@ -28,7 +29,7 @@ export class InterruptService {
|
|
|
28
29
|
*/
|
|
29
30
|
constructor() {
|
|
30
31
|
this.pollingConfig = { ...defaultInterruptPollingConfig };
|
|
31
|
-
this.currentBackoff = this.pollingConfig.interval ??
|
|
32
|
+
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* Get the singleton instance of InterruptService
|
|
@@ -48,7 +49,7 @@ export class InterruptService {
|
|
|
48
49
|
*/
|
|
49
50
|
setPollingConfig(config) {
|
|
50
51
|
this.pollingConfig = { ...this.pollingConfig, ...config };
|
|
51
|
-
this.currentBackoff = this.pollingConfig.interval ??
|
|
52
|
+
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Get the current polling configuration
|
|
@@ -212,13 +213,13 @@ export class InterruptService {
|
|
|
212
213
|
*/
|
|
213
214
|
startPolling(sessionId, callback) {
|
|
214
215
|
if (!this.pollingConfig.enabled) {
|
|
215
|
-
|
|
216
|
+
logger.warn('[InterruptService] Polling is disabled. Enable via setPollingConfig().');
|
|
216
217
|
return;
|
|
217
218
|
}
|
|
218
219
|
// Stop any existing polling
|
|
219
220
|
this.stopPolling();
|
|
220
221
|
this.pollingSessionId = sessionId;
|
|
221
|
-
this.currentBackoff = this.pollingConfig.interval ??
|
|
222
|
+
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
222
223
|
const poll = async () => {
|
|
223
224
|
if (this.pollingSessionId !== sessionId) {
|
|
224
225
|
return;
|
|
@@ -227,14 +228,14 @@ export class InterruptService {
|
|
|
227
228
|
const interrupts = await this.listSessionInterrupts(sessionId);
|
|
228
229
|
const pendingInterrupts = interrupts.filter((i) => i.status === 'pending');
|
|
229
230
|
// Reset backoff on successful request
|
|
230
|
-
this.currentBackoff = this.pollingConfig.interval ??
|
|
231
|
+
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
231
232
|
// Call the callback with pending interrupts
|
|
232
233
|
callback(pendingInterrupts);
|
|
233
234
|
}
|
|
234
235
|
catch (error) {
|
|
235
|
-
|
|
236
|
+
logger.error('[InterruptService] Polling error:', error);
|
|
236
237
|
// Exponential backoff on error
|
|
237
|
-
const maxBackoff = this.pollingConfig.maxBackoff ??
|
|
238
|
+
const maxBackoff = this.pollingConfig.maxBackoff ?? defaultInterruptPollingConfig.maxBackoff;
|
|
238
239
|
this.currentBackoff = Math.min(this.currentBackoff * 2, maxBackoff);
|
|
239
240
|
}
|
|
240
241
|
// Schedule next poll
|
|
@@ -254,7 +255,7 @@ export class InterruptService {
|
|
|
254
255
|
this.pollingInterval = null;
|
|
255
256
|
}
|
|
256
257
|
this.pollingSessionId = null;
|
|
257
|
-
this.currentBackoff = this.pollingConfig.interval ??
|
|
258
|
+
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
258
259
|
}
|
|
259
260
|
/**
|
|
260
261
|
* Check if polling is active
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { getEndpointConfig } from './api.js';
|
|
6
6
|
import { buildEndpointUrl } from '../config/endpoints.js';
|
|
7
|
+
import { NODE_EXECUTION_CACHE_TIMEOUT_MS, PIPELINE_API_UNAVAILABLE_DURATION_MS } from '../config/constants.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
7
9
|
/**
|
|
8
10
|
* Service for managing node execution information
|
|
9
11
|
*/
|
|
10
12
|
export class NodeExecutionService {
|
|
11
13
|
static instance;
|
|
12
14
|
cache = new Map();
|
|
13
|
-
cacheTimeout =
|
|
15
|
+
cacheTimeout = NODE_EXECUTION_CACHE_TIMEOUT_MS;
|
|
14
16
|
lastFetch = 0;
|
|
15
17
|
apiUnavailable = false;
|
|
16
18
|
apiUnavailableUntil = 0;
|
|
@@ -30,6 +32,8 @@ export class NodeExecutionService {
|
|
|
30
32
|
}
|
|
31
33
|
try {
|
|
32
34
|
const endpointConfig = getEndpointConfig();
|
|
35
|
+
if (!endpointConfig)
|
|
36
|
+
throw new Error('Endpoint config not available');
|
|
33
37
|
const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.pipelines.get, {
|
|
34
38
|
id: pipelineId
|
|
35
39
|
});
|
|
@@ -62,7 +66,7 @@ export class NodeExecutionService {
|
|
|
62
66
|
return executionInfo;
|
|
63
67
|
}
|
|
64
68
|
catch (error) {
|
|
65
|
-
|
|
69
|
+
logger.error('Failed to fetch node execution info:', error);
|
|
66
70
|
return null;
|
|
67
71
|
}
|
|
68
72
|
}
|
|
@@ -87,6 +91,8 @@ export class NodeExecutionService {
|
|
|
87
91
|
}
|
|
88
92
|
try {
|
|
89
93
|
const endpointConfig = getEndpointConfig();
|
|
94
|
+
if (!endpointConfig)
|
|
95
|
+
throw new Error('Endpoint config not available');
|
|
90
96
|
const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.pipelines.get, {
|
|
91
97
|
id: pipelineId
|
|
92
98
|
});
|
|
@@ -95,9 +101,9 @@ export class NodeExecutionService {
|
|
|
95
101
|
// If the endpoint returns 404, it means the pipeline API is not available
|
|
96
102
|
// Mark API as unavailable for 5 minutes to prevent repeated calls
|
|
97
103
|
if (response.status === 404) {
|
|
98
|
-
|
|
104
|
+
logger.warn(`Pipeline API endpoint not available for pipeline ${pipelineId}`);
|
|
99
105
|
this.apiUnavailable = true;
|
|
100
|
-
this.apiUnavailableUntil = Date.now() +
|
|
106
|
+
this.apiUnavailableUntil = Date.now() + PIPELINE_API_UNAVAILABLE_DURATION_MS;
|
|
101
107
|
const defaultExecutionInfo = {};
|
|
102
108
|
nodeIds.forEach((nodeId) => {
|
|
103
109
|
defaultExecutionInfo[nodeId] = {
|
|
@@ -140,7 +146,7 @@ export class NodeExecutionService {
|
|
|
140
146
|
return executionInfoMap;
|
|
141
147
|
}
|
|
142
148
|
catch (error) {
|
|
143
|
-
|
|
149
|
+
logger.error('Failed to fetch multiple node execution info:', error);
|
|
144
150
|
// Return default values instead of empty object to prevent repeated calls
|
|
145
151
|
const defaultExecutionInfo = {};
|
|
146
152
|
nodeIds.forEach((nodeId) => {
|
|
@@ -159,6 +165,8 @@ export class NodeExecutionService {
|
|
|
159
165
|
async getAllNodeExecutionCounts() {
|
|
160
166
|
try {
|
|
161
167
|
const endpointConfig = getEndpointConfig();
|
|
168
|
+
if (!endpointConfig)
|
|
169
|
+
throw new Error('Endpoint config not available');
|
|
162
170
|
const url = buildEndpointUrl(endpointConfig, '/node-execution-counts');
|
|
163
171
|
const response = await fetch(url);
|
|
164
172
|
if (!response.ok) {
|
|
@@ -171,7 +179,7 @@ export class NodeExecutionService {
|
|
|
171
179
|
return {};
|
|
172
180
|
}
|
|
173
181
|
catch (error) {
|
|
174
|
-
|
|
182
|
+
logger.error('Failed to fetch all node execution counts:', error);
|
|
175
183
|
return {};
|
|
176
184
|
}
|
|
177
185
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { defaultShouldStopPolling } from '../types/playground.js';
|
|
10
10
|
import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
|
|
11
11
|
import { getEndpointConfig } from './api.js';
|
|
12
|
+
import { logger } from '../utils/logger.js';
|
|
12
13
|
/**
|
|
13
14
|
* Default polling interval in milliseconds
|
|
14
15
|
*/
|
|
@@ -273,7 +274,7 @@ export class PlaygroundService {
|
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
276
|
catch (error) {
|
|
276
|
-
|
|
277
|
+
logger.error('Polling error:', error);
|
|
277
278
|
// Exponential backoff on error
|
|
278
279
|
this.currentBackoff = Math.min(this.currentBackoff * 2, MAX_POLLING_BACKOFF);
|
|
279
280
|
}
|
|
@@ -4,26 +4,30 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { buildEndpointUrl } from '../config/endpoints.js';
|
|
6
6
|
import { DEFAULT_PORT_CONFIG } from '../config/defaultPortConfig.js';
|
|
7
|
-
import {
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
8
|
/**
|
|
9
9
|
* Fetch port configuration from API
|
|
10
10
|
*/
|
|
11
11
|
export async function fetchPortConfig(endpointConfig) {
|
|
12
12
|
try {
|
|
13
13
|
const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.portConfig);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
headers: { 'Content-Type': 'application/json' }
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
19
|
+
}
|
|
20
|
+
const data = await response.json();
|
|
21
|
+
const portConfig = data.data ?? data;
|
|
18
22
|
// Validate the configuration has required fields
|
|
19
23
|
if (!portConfig.dataTypes || !Array.isArray(portConfig.dataTypes)) {
|
|
20
|
-
|
|
24
|
+
logger.warn('Invalid port config received from API, using default');
|
|
21
25
|
return DEFAULT_PORT_CONFIG;
|
|
22
26
|
}
|
|
23
27
|
return portConfig;
|
|
24
28
|
}
|
|
25
29
|
catch (error) {
|
|
26
|
-
|
|
30
|
+
logger.error('Error fetching port configuration:', error);
|
|
27
31
|
return DEFAULT_PORT_CONFIG;
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -57,7 +57,7 @@ export declare function dismissAllToasts(): void;
|
|
|
57
57
|
export declare function showPromise<T>(promise: Promise<T>, { loading, success, error, options }: {
|
|
58
58
|
loading: string;
|
|
59
59
|
success: string | ((data: T) => string);
|
|
60
|
-
error: string | ((error:
|
|
60
|
+
error: string | ((error: unknown) => string);
|
|
61
61
|
options?: ToastOptions;
|
|
62
62
|
}): Promise<T>;
|
|
63
63
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Provides consistent toast notifications across the FlowDrop application
|
|
5
5
|
*/
|
|
6
6
|
import { toast } from 'svelte-5-french-toast';
|
|
7
|
+
import { TOAST_DURATION } from '../config/constants.js';
|
|
7
8
|
/**
|
|
8
9
|
* Default toast options themed with FlowDrop design tokens.
|
|
9
10
|
* Use with <Toaster toastOptions={flowdropToastOptions} containerClassName="flowdrop-toaster" />
|
|
@@ -38,7 +39,7 @@ export const FLOWDROP_TOASTER_CLASS = 'flowdrop-toaster';
|
|
|
38
39
|
*/
|
|
39
40
|
export function showSuccess(message, options) {
|
|
40
41
|
return toast.success(message, {
|
|
41
|
-
duration: options?.duration ||
|
|
42
|
+
duration: options?.duration || TOAST_DURATION.SUCCESS,
|
|
42
43
|
position: options?.position || 'bottom-center'
|
|
43
44
|
});
|
|
44
45
|
}
|
|
@@ -47,7 +48,7 @@ export function showSuccess(message, options) {
|
|
|
47
48
|
*/
|
|
48
49
|
export function showError(message, options) {
|
|
49
50
|
return toast.error(message, {
|
|
50
|
-
duration: options?.duration ||
|
|
51
|
+
duration: options?.duration || TOAST_DURATION.ERROR,
|
|
51
52
|
position: options?.position || 'bottom-center'
|
|
52
53
|
});
|
|
53
54
|
}
|
|
@@ -56,7 +57,7 @@ export function showError(message, options) {
|
|
|
56
57
|
*/
|
|
57
58
|
export function showWarning(message, options) {
|
|
58
59
|
return toast.error(message, {
|
|
59
|
-
duration: options?.duration ||
|
|
60
|
+
duration: options?.duration || TOAST_DURATION.WARNING,
|
|
60
61
|
position: options?.position || 'bottom-center'
|
|
61
62
|
});
|
|
62
63
|
}
|
|
@@ -65,7 +66,7 @@ export function showWarning(message, options) {
|
|
|
65
66
|
*/
|
|
66
67
|
export function showInfo(message, options) {
|
|
67
68
|
return toast.success(message, {
|
|
68
|
-
duration: options?.duration ||
|
|
69
|
+
duration: options?.duration || TOAST_DURATION.INFO,
|
|
69
70
|
position: options?.position || 'bottom-center'
|
|
70
71
|
});
|
|
71
72
|
}
|
|
@@ -106,7 +107,7 @@ export function showPromise(promise, { loading, success, error, options }) {
|
|
|
106
107
|
*/
|
|
107
108
|
export function showConfirmation(message, options) {
|
|
108
109
|
return toast(message, {
|
|
109
|
-
duration: options?.duration ||
|
|
110
|
+
duration: options?.duration || TOAST_DURATION.CONFIRMATION,
|
|
110
111
|
position: options?.position || 'bottom-center'
|
|
111
112
|
});
|
|
112
113
|
}
|