@flowdrop/flowdrop 1.14.0 → 2.0.0-beta.1
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/CHANGELOG.md +475 -0
- package/MIGRATION-2.0.md +472 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +162 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +110 -66
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +16 -67
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +45 -40
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +118 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
- package/dist/components/chat/AIChatPanel.svelte +93 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +29 -13
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +4 -2
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +16 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +292 -0
- package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +65 -6
- package/dist/components/playground/PipelinePanel.svelte +11 -5
- package/dist/components/playground/PipelineTableView.svelte +186 -44
- package/dist/components/playground/Playground.svelte +95 -92
- package/dist/components/playground/Playground.svelte.d.ts +2 -0
- package/dist/components/playground/PlaygroundApp.svelte +6 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundModal.svelte +13 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundStudio.svelte +34 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
- package/dist/config/endpoints.d.ts +0 -7
- package/dist/config/endpoints.js +2 -10
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/display/index.d.ts +0 -2
- package/dist/display/index.js +0 -6
- package/dist/editor/index.d.ts +19 -20
- package/dist/editor/index.js +25 -35
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +17 -13
- package/dist/form/full.js +22 -27
- package/dist/form/index.d.ts +3 -3
- package/dist/form/index.js +3 -3
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +7 -3
- package/dist/helpers/proximityConnect.js +19 -6
- package/dist/helpers/workflowEditorHelper.d.ts +12 -5
- package/dist/helpers/workflowEditorHelper.js +27 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +227 -164
- package/dist/playground/index.d.ts +2 -3
- package/dist/playground/index.js +2 -30
- package/dist/playground/mount.d.ts +15 -0
- package/dist/playground/mount.js +46 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodes.d.ts +1 -26
- package/dist/registry/builtinNodes.js +14 -50
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +53 -6
- package/dist/services/agentSpecExecutionService.js +0 -1
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +5 -22
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/chatService.d.ts +8 -4
- package/dist/services/chatService.js +15 -15
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +14 -9
- package/dist/services/interruptService.js +27 -27
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +14 -9
- package/dist/services/playgroundService.js +31 -30
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +45 -0
- package/dist/stores/apiContext.js +65 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +70 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -216
- package/dist/stores/playgroundStore.svelte.js +515 -572
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +157 -53
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/formMerge.d.ts +36 -0
- package/dist/utils/formMerge.js +70 -0
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -19
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +6 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
|
@@ -21,12 +21,7 @@
|
|
|
21
21
|
getEditorSettings,
|
|
22
22
|
getBehaviorSettings
|
|
23
23
|
} from '../stores/settingsStore.svelte.js';
|
|
24
|
-
import type {
|
|
25
|
-
WorkflowNode as WorkflowNodeType,
|
|
26
|
-
NodeMetadata,
|
|
27
|
-
Workflow,
|
|
28
|
-
WorkflowEdge
|
|
29
|
-
} from '../types/index.js';
|
|
24
|
+
import type { WorkflowNode as WorkflowNodeType, Workflow, WorkflowEdge } from '../types/index.js';
|
|
30
25
|
import CanvasBanner from './CanvasBanner.svelte';
|
|
31
26
|
import CanvasController from './CanvasController.svelte';
|
|
32
27
|
import FlowDropZone from './FlowDropZone.svelte';
|
|
@@ -36,8 +31,8 @@
|
|
|
36
31
|
import ConnectionLine from './ConnectionLine.svelte';
|
|
37
32
|
import FlowDropEdge from './FlowDropEdge.svelte';
|
|
38
33
|
import { m } from '../messages/index.js';
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
34
|
+
import { provideInstance } from '../stores/getInstance.svelte.js';
|
|
35
|
+
import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
|
|
41
36
|
import UniversalNode from './UniversalNode.svelte';
|
|
42
37
|
import {
|
|
43
38
|
EdgeStylingHelper,
|
|
@@ -57,34 +52,50 @@
|
|
|
57
52
|
type ProximityEdgeCandidate
|
|
58
53
|
} from '../helpers/proximityConnect.js';
|
|
59
54
|
import PortCoordinateTracker from './PortCoordinateTracker.svelte';
|
|
60
|
-
import { getPortCoordinateSnapshot } from '../stores/portCoordinateStore.svelte.js';
|
|
61
55
|
import { logger } from '../utils/logger.js';
|
|
62
56
|
import { validateWorkflowData } from '../utils/validation.js';
|
|
63
57
|
import { createEditorStateMachine } from '../stores/editorStateMachine.svelte.js';
|
|
64
58
|
import Icon from '@iconify/svelte';
|
|
59
|
+
import { DEV } from 'esm-env';
|
|
65
60
|
|
|
66
61
|
interface Props {
|
|
67
|
-
nodes?: NodeMetadata[];
|
|
68
62
|
endpointConfig?: EndpointConfig;
|
|
69
|
-
height?: string | number;
|
|
70
|
-
width?: string | number;
|
|
71
|
-
isConfigSidebarOpen?: boolean;
|
|
72
|
-
selectedNodeForConfig?: WorkflowNodeType | null;
|
|
73
63
|
openConfigSidebar?: (node: WorkflowNodeType) => void;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Editor interaction mode. `'edit'` allows node drag/connect/select and
|
|
66
|
+
* proximity-connect; `'readonly'` and `'locked'` disable all canvas
|
|
67
|
+
* editing (they behave identically today — see App's `mode` prop for the
|
|
68
|
+
* full matrix). Replaces the former `readOnly` + `lockWorkflow` booleans.
|
|
69
|
+
* @default 'edit'
|
|
70
|
+
*/
|
|
71
|
+
mode?: 'edit' | 'readonly' | 'locked';
|
|
79
72
|
// Pipeline ID for fetching node execution info from jobs
|
|
80
73
|
pipelineId?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Increments to force a re-fetch of node execution info from the server.
|
|
76
|
+
* Used by parents (e.g. PipelineStatus) to push poll ticks / chat-message
|
|
77
|
+
* arrivals down to the canvas without owning a separate status channel.
|
|
78
|
+
*/
|
|
79
|
+
refreshTrigger?: number;
|
|
81
80
|
// Console toggle
|
|
82
81
|
consoleOpen?: boolean;
|
|
83
82
|
onToggleConsole?: () => void;
|
|
83
|
+
/** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
|
|
84
|
+
instance?: FlowDropInstance;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
let props: Props = $props();
|
|
87
88
|
|
|
89
|
+
// Resolve (and provide to children) the per-instance state container.
|
|
90
|
+
// Must run during component init — provideInstance reads/sets Svelte context.
|
|
91
|
+
// The instance never changes for a mounted component, so capturing it once is correct.
|
|
92
|
+
// svelte-ignore state_referenced_locally
|
|
93
|
+
const fd = provideInstance(props.instance);
|
|
94
|
+
|
|
95
|
+
// `mode` is the public API; the canvas only needs to know whether editing is
|
|
96
|
+
// enabled. 'readonly' and 'locked' both disable interaction identically.
|
|
97
|
+
const canvasEditable = $derived((props.mode ?? 'edit') === 'edit');
|
|
98
|
+
|
|
88
99
|
// ---------------------------------------------------------------------------
|
|
89
100
|
// Editor State Machine
|
|
90
101
|
// Centralizes reactive guards — replaces scattered boolean flags
|
|
@@ -93,7 +104,7 @@
|
|
|
93
104
|
const machine = createEditorStateMachine();
|
|
94
105
|
|
|
95
106
|
// Dev-mode transition logging
|
|
96
|
-
if (
|
|
107
|
+
if (DEV) {
|
|
97
108
|
machine.onTransition((from, event, to) => {
|
|
98
109
|
logger.debug(`[EditorFSM] ${from} --${event}--> ${to}`);
|
|
99
110
|
});
|
|
@@ -122,7 +133,7 @@
|
|
|
122
133
|
* Key for SvelteFlow component — changes when workflow ID changes.
|
|
123
134
|
* Forces SvelteFlow to remount with fresh state, allowing fitView to work correctly.
|
|
124
135
|
*/
|
|
125
|
-
let svelteFlowKey = $derived(
|
|
136
|
+
let svelteFlowKey = $derived(fd.workflow.current?.id ?? 'default');
|
|
126
137
|
|
|
127
138
|
/**
|
|
128
139
|
* Derive snap grid configuration from editor settings
|
|
@@ -164,14 +175,14 @@
|
|
|
164
175
|
// Helper: sync current flowNodes/flowEdges back to the global store
|
|
165
176
|
// ---------------------------------------------------------------------------
|
|
166
177
|
function syncFlowToStore(): void {
|
|
167
|
-
const storeValue = untrack(() =>
|
|
178
|
+
const storeValue = untrack(() => fd.workflow.current);
|
|
168
179
|
if (!storeValue) return;
|
|
169
180
|
const updatedWorkflow = WorkflowOperationsHelper.updateWorkflow(
|
|
170
181
|
storeValue,
|
|
171
182
|
flowNodes,
|
|
172
183
|
flowEdges
|
|
173
184
|
);
|
|
174
|
-
|
|
185
|
+
fd.workflow.updateWorkflow(updatedWorkflow);
|
|
175
186
|
}
|
|
176
187
|
|
|
177
188
|
// ---------------------------------------------------------------------------
|
|
@@ -182,7 +193,7 @@
|
|
|
182
193
|
let previousSyncedWorkflowId: string | null = null;
|
|
183
194
|
|
|
184
195
|
$effect(() => {
|
|
185
|
-
const storeValue =
|
|
196
|
+
const storeValue = fd.workflow.current;
|
|
186
197
|
|
|
187
198
|
// Suppressed during operations — handlers write to flowNodes directly
|
|
188
199
|
if (untrack(() => machine.permissions.suppressEffect)) return;
|
|
@@ -228,7 +239,7 @@
|
|
|
228
239
|
let previousExecPipelineId: string | undefined = undefined;
|
|
229
240
|
|
|
230
241
|
$effect(() => {
|
|
231
|
-
const storeValue =
|
|
242
|
+
const storeValue = fd.workflow.current;
|
|
232
243
|
const pipelineId = props.pipelineId;
|
|
233
244
|
|
|
234
245
|
if (!storeValue || !pipelineId) return;
|
|
@@ -241,15 +252,12 @@
|
|
|
241
252
|
previousExecWorkflowId = storeValue.id;
|
|
242
253
|
previousExecPipelineId = pipelineId;
|
|
243
254
|
|
|
244
|
-
// Cancel any pending timeout
|
|
255
|
+
// Cancel any pending idle/timeout schedule. In-flight fetches are
|
|
256
|
+
// cancelled by loadNodeExecutionInfo() itself when it's re-entered.
|
|
245
257
|
if (loadExecutionInfoTimeout) {
|
|
246
258
|
clearTimeout(loadExecutionInfoTimeout);
|
|
247
259
|
loadExecutionInfoTimeout = null;
|
|
248
260
|
}
|
|
249
|
-
if (executionInfoAbortController) {
|
|
250
|
-
executionInfoAbortController.abort();
|
|
251
|
-
executionInfoAbortController = null;
|
|
252
|
-
}
|
|
253
261
|
|
|
254
262
|
// Schedule loading with requestIdleCallback (falls back to setTimeout)
|
|
255
263
|
if (typeof requestIdleCallback !== 'undefined') {
|
|
@@ -266,44 +274,26 @@
|
|
|
266
274
|
}
|
|
267
275
|
});
|
|
268
276
|
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
277
|
+
// Re-fetch node execution info when the parent bumps refreshTrigger
|
|
278
|
+
// (poll ticks, chat-message arrivals, manual refresh). loadNodeExecutionInfo()
|
|
279
|
+
// is the single writer of executionInfo on flowNodes — no parallel channel.
|
|
280
|
+
// svelte-ignore state_referenced_locally
|
|
281
|
+
let _prevExecRefreshTrigger = props.refreshTrigger ?? 0;
|
|
272
282
|
$effect(() => {
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const rawStatus = statuses[node.id];
|
|
278
|
-
if (!rawStatus) return node;
|
|
279
|
-
|
|
280
|
-
const existing = node.data.executionInfo ?? {
|
|
281
|
-
status: 'idle' as const,
|
|
282
|
-
executionCount: 0,
|
|
283
|
-
isExecuting: false
|
|
284
|
-
};
|
|
285
|
-
return {
|
|
286
|
-
...node,
|
|
287
|
-
data: {
|
|
288
|
-
...node.data,
|
|
289
|
-
executionInfo: {
|
|
290
|
-
...existing,
|
|
291
|
-
status: rawStatus === 'error' ? ('failed' as const) : rawStatus,
|
|
292
|
-
isExecuting: rawStatus === 'running'
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
});
|
|
283
|
+
const t = props.refreshTrigger ?? 0;
|
|
284
|
+
if (t === 0 || t === _prevExecRefreshTrigger) return;
|
|
285
|
+
_prevExecRefreshTrigger = t;
|
|
286
|
+
loadNodeExecutionInfo();
|
|
297
287
|
});
|
|
298
288
|
|
|
299
289
|
// ---------------------------------------------------------------------------
|
|
300
290
|
// History restore callback
|
|
301
291
|
// ---------------------------------------------------------------------------
|
|
302
292
|
$effect(() => {
|
|
303
|
-
setOnRestoreCallback((restoredWorkflow: Workflow) => {
|
|
293
|
+
fd.historyBindings.setOnRestoreCallback((restoredWorkflow: Workflow) => {
|
|
304
294
|
machine.send('START_RESTORE');
|
|
305
295
|
// Update the store (effect is suppressed during 'restoring')
|
|
306
|
-
|
|
296
|
+
fd.workflow.restoreFromHistory(restoredWorkflow);
|
|
307
297
|
// Derive flowNodes/flowEdges directly for immediate visual update
|
|
308
298
|
const derived = buildFlowNodesFromStore(restoredWorkflow);
|
|
309
299
|
flowNodes = derived.nodes;
|
|
@@ -314,26 +304,38 @@
|
|
|
314
304
|
});
|
|
315
305
|
|
|
316
306
|
return () => {
|
|
317
|
-
|
|
307
|
+
// Reinstate the container's default wiring rather than nulling it: the
|
|
308
|
+
// default instance survives unmount, and a bare null would make legacy
|
|
309
|
+
// historyActions.undo() silently restore nothing afterwards.
|
|
310
|
+
fd.historyBindings.setOnRestoreCallback((restored: Workflow) =>
|
|
311
|
+
fd.workflow.restoreFromHistory(restored)
|
|
312
|
+
);
|
|
318
313
|
};
|
|
319
314
|
});
|
|
320
315
|
|
|
321
316
|
/**
|
|
322
|
-
* Load node execution information for all nodes in the workflow
|
|
317
|
+
* Load node execution information for all nodes in the workflow.
|
|
318
|
+
* Cancels any in-flight fetch so concurrent callers (pipelineId change
|
|
319
|
+
* and refreshTrigger bumps) can't race on flowNodes.
|
|
323
320
|
*/
|
|
324
321
|
async function loadNodeExecutionInfo(): Promise<void> {
|
|
325
|
-
const workflow = untrack(() =>
|
|
322
|
+
const workflow = untrack(() => fd.workflow.current);
|
|
326
323
|
if (!workflow?.nodes || !props.pipelineId) return;
|
|
327
324
|
|
|
328
|
-
|
|
329
|
-
executionInfoAbortController
|
|
325
|
+
if (executionInfoAbortController) {
|
|
326
|
+
executionInfoAbortController.abort();
|
|
327
|
+
}
|
|
328
|
+
const controller = new AbortController();
|
|
329
|
+
executionInfoAbortController = controller;
|
|
330
330
|
|
|
331
|
+
try {
|
|
331
332
|
const executionInfo = await NodeOperationsHelper.loadNodeExecutionInfo(
|
|
333
|
+
fd.api,
|
|
332
334
|
workflow,
|
|
333
335
|
props.pipelineId
|
|
334
336
|
);
|
|
335
337
|
|
|
336
|
-
if (
|
|
338
|
+
if (controller.signal.aborted) return;
|
|
337
339
|
|
|
338
340
|
const defaultExecutionInfo: NodeExecutionInfo = {
|
|
339
341
|
status: 'idle' as const,
|
|
@@ -350,7 +352,9 @@
|
|
|
350
352
|
}
|
|
351
353
|
}));
|
|
352
354
|
|
|
353
|
-
executionInfoAbortController
|
|
355
|
+
if (executionInfoAbortController === controller) {
|
|
356
|
+
executionInfoAbortController = null;
|
|
357
|
+
}
|
|
354
358
|
} catch (error) {
|
|
355
359
|
if (error instanceof Error && error.name !== 'AbortError') {
|
|
356
360
|
logger.error('Failed to load node execution info:', error);
|
|
@@ -401,12 +405,7 @@
|
|
|
401
405
|
nodes: WorkflowNodeType[];
|
|
402
406
|
event: MouseEvent | TouchEvent;
|
|
403
407
|
}): void {
|
|
404
|
-
if (
|
|
405
|
-
!getEditorSettings().proximityConnect ||
|
|
406
|
-
!targetNode ||
|
|
407
|
-
props.readOnly ||
|
|
408
|
-
props.lockWorkflow
|
|
409
|
-
) {
|
|
408
|
+
if (!getEditorSettings().proximityConnect || !targetNode || !canvasEditable) {
|
|
410
409
|
if (currentProximityCandidates.length > 0) {
|
|
411
410
|
flowEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
|
|
412
411
|
currentProximityCandidates = [];
|
|
@@ -422,16 +421,18 @@
|
|
|
422
421
|
const baseEdges = ProximityConnectHelper.removePreviewEdges(flowEdges);
|
|
423
422
|
|
|
424
423
|
// Find the best compatible edge using port-to-port distance
|
|
425
|
-
const portCoordinates =
|
|
424
|
+
const portCoordinates = fd.portCoordinates.coordinates;
|
|
426
425
|
const candidates =
|
|
427
426
|
portCoordinates.size > 0
|
|
428
427
|
? ProximityConnectHelper.findCompatibleEdgesByPortCoordinates(
|
|
428
|
+
fd.portCompatibility,
|
|
429
429
|
targetNode.id,
|
|
430
430
|
portCoordinates,
|
|
431
431
|
baseEdges,
|
|
432
432
|
getEditorSettings().proximityConnectDistance
|
|
433
433
|
)
|
|
434
434
|
: ProximityConnectHelper.findCompatibleEdges(
|
|
435
|
+
fd.portCompatibility,
|
|
435
436
|
targetNode,
|
|
436
437
|
flowNodes,
|
|
437
438
|
baseEdges,
|
|
@@ -478,9 +479,9 @@
|
|
|
478
479
|
syncFlowToStore();
|
|
479
480
|
|
|
480
481
|
// Push history AFTER the drag completed
|
|
481
|
-
const storeValue =
|
|
482
|
+
const storeValue = fd.workflow.current;
|
|
482
483
|
if (storeValue) {
|
|
483
|
-
|
|
484
|
+
fd.workflow.pushHistory('Move node', storeValue);
|
|
484
485
|
}
|
|
485
486
|
|
|
486
487
|
// Transition to idle — sync effect is now unblocked
|
|
@@ -488,14 +489,11 @@
|
|
|
488
489
|
}
|
|
489
490
|
|
|
490
491
|
/**
|
|
491
|
-
* Handle new connections between nodes
|
|
492
|
+
* Handle new connections between nodes.
|
|
493
|
+
* The connection details aren't needed — SvelteFlow auto-creates the edge
|
|
494
|
+
* via bind:edges; this only drives the state machine and history snapshot.
|
|
492
495
|
*/
|
|
493
|
-
async function handleConnect(
|
|
494
|
-
source: string;
|
|
495
|
-
target: string;
|
|
496
|
-
sourceHandle?: string;
|
|
497
|
-
targetHandle?: string;
|
|
498
|
-
}): Promise<void> {
|
|
496
|
+
async function handleConnect(): Promise<void> {
|
|
499
497
|
machine.send('START_CONNECT');
|
|
500
498
|
|
|
501
499
|
// SvelteFlow auto-creates the edge via bind:edges — wait for DOM update
|
|
@@ -507,9 +505,9 @@
|
|
|
507
505
|
// Sync to store
|
|
508
506
|
syncFlowToStore();
|
|
509
507
|
|
|
510
|
-
const storeValue =
|
|
508
|
+
const storeValue = fd.workflow.current;
|
|
511
509
|
if (storeValue) {
|
|
512
|
-
|
|
510
|
+
fd.workflow.pushHistory('Add connection', storeValue);
|
|
513
511
|
}
|
|
514
512
|
|
|
515
513
|
machine.send('CONNECTION_MADE');
|
|
@@ -585,9 +583,9 @@
|
|
|
585
583
|
} else if (edgeCount > 0) {
|
|
586
584
|
description = `Delete ${edgeCount} connection${edgeCount > 1 ? 's' : ''}`;
|
|
587
585
|
}
|
|
588
|
-
const storeValue =
|
|
586
|
+
const storeValue = fd.workflow.current;
|
|
589
587
|
if (storeValue) {
|
|
590
|
-
|
|
588
|
+
fd.workflow.pushHistory(description, storeValue);
|
|
591
589
|
}
|
|
592
590
|
|
|
593
591
|
machine.send('DELETE_COMPLETE');
|
|
@@ -598,7 +596,7 @@
|
|
|
598
596
|
// Configure endpoints when props change
|
|
599
597
|
$effect(() => {
|
|
600
598
|
if (props.endpointConfig) {
|
|
601
|
-
ConfigurationHelper.configureEndpoints(props.endpointConfig);
|
|
599
|
+
ConfigurationHelper.configureEndpoints(fd.api, props.endpointConfig);
|
|
602
600
|
}
|
|
603
601
|
});
|
|
604
602
|
|
|
@@ -635,9 +633,9 @@
|
|
|
635
633
|
|
|
636
634
|
await tick();
|
|
637
635
|
|
|
638
|
-
const storeValue =
|
|
636
|
+
const storeValue = fd.workflow.current;
|
|
639
637
|
if (storeValue) {
|
|
640
|
-
|
|
638
|
+
fd.workflow.pushHistory('Add node', storeValue);
|
|
641
639
|
}
|
|
642
640
|
} else {
|
|
643
641
|
logger.warn('Failed to create node from drop data');
|
|
@@ -667,7 +665,7 @@
|
|
|
667
665
|
logger.warn('Workflow file drop validation failed:', validation.error);
|
|
668
666
|
return;
|
|
669
667
|
}
|
|
670
|
-
|
|
668
|
+
fd.workflow.initialize(data as Workflow);
|
|
671
669
|
} catch (error) {
|
|
672
670
|
const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
|
|
673
671
|
logger.error('Workflow file drop import failed:', errorObj);
|
|
@@ -693,7 +691,7 @@
|
|
|
693
691
|
/**
|
|
694
692
|
* Update a node's data in the local editor state.
|
|
695
693
|
* Called by App.svelte AFTER it has already updated the global store via
|
|
696
|
-
*
|
|
694
|
+
* fd.workflow.updateNode(). We only need to update flowNodes for
|
|
697
695
|
* immediate visual feedback — no store sync needed.
|
|
698
696
|
*
|
|
699
697
|
* @param nodeId - The ID of the node to update
|
|
@@ -774,15 +772,15 @@
|
|
|
774
772
|
* - Ctrl+Z (or Cmd+Z on Mac): Undo
|
|
775
773
|
* - Ctrl+Shift+Z (or Cmd+Shift+Z): Redo
|
|
776
774
|
* - Ctrl+Y (or Cmd+Y): Redo (Windows convention)
|
|
775
|
+
*
|
|
776
|
+
* Also suppresses WebKit's legacy default action for Backspace outside
|
|
777
|
+
* editable content — history back-navigation. SvelteFlow's KeyHandler
|
|
778
|
+
* deletes the selected elements on Backspace but never preventDefaults,
|
|
779
|
+
* so in embedded WebKit the page navigates away mid-delete. Scoped to
|
|
780
|
+
* keydowns originating inside the flow canvas so host-page behavior
|
|
781
|
+
* outside the editor is untouched.
|
|
777
782
|
*/
|
|
778
783
|
function handleKeydown(event: KeyboardEvent): void {
|
|
779
|
-
// Check for Ctrl (Windows/Linux) or Cmd (Mac)
|
|
780
|
-
const isModifierPressed = event.ctrlKey || event.metaKey;
|
|
781
|
-
|
|
782
|
-
if (!isModifierPressed) {
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
784
|
// Don't handle shortcuts if user is typing in an input, textarea, or contenteditable
|
|
787
785
|
const target = event.target as HTMLElement;
|
|
788
786
|
const isInputElement =
|
|
@@ -792,17 +790,31 @@
|
|
|
792
790
|
return;
|
|
793
791
|
}
|
|
794
792
|
|
|
793
|
+
// Backspace/Delete on a canvas element: let SvelteFlow handle the
|
|
794
|
+
// deletion, but block the browser default (WebKit navigates back).
|
|
795
|
+
if ((event.key === 'Backspace' || event.key === 'Delete') && target.closest('.svelte-flow')) {
|
|
796
|
+
event.preventDefault();
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Check for Ctrl (Windows/Linux) or Cmd (Mac)
|
|
801
|
+
const isModifierPressed = event.ctrlKey || event.metaKey;
|
|
802
|
+
|
|
803
|
+
if (!isModifierPressed) {
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
795
807
|
// Undo: Ctrl+Z (without Shift)
|
|
796
808
|
if (event.key === 'z' && !event.shiftKey) {
|
|
797
809
|
event.preventDefault();
|
|
798
|
-
|
|
810
|
+
fd.historyBindings.undo();
|
|
799
811
|
return;
|
|
800
812
|
}
|
|
801
813
|
|
|
802
814
|
// Redo: Ctrl+Shift+Z or Ctrl+Y
|
|
803
815
|
if ((event.key === 'z' && event.shiftKey) || event.key === 'y') {
|
|
804
816
|
event.preventDefault();
|
|
805
|
-
|
|
817
|
+
fd.historyBindings.redo();
|
|
806
818
|
return;
|
|
807
819
|
}
|
|
808
820
|
}
|
|
@@ -832,18 +844,13 @@
|
|
|
832
844
|
<FlowDropZone ondrop={handleNodeDrop} onfiledrop={handleWorkflowFileDrop}>
|
|
833
845
|
{#key svelteFlowKey}
|
|
834
846
|
<SvelteFlow
|
|
847
|
+
id={fd.id}
|
|
835
848
|
bind:nodes={flowNodes}
|
|
836
849
|
bind:edges={flowEdges}
|
|
837
850
|
{nodeTypes}
|
|
838
851
|
{edgeTypes}
|
|
839
852
|
{defaultEdgeOptions}
|
|
840
|
-
onconnect={(
|
|
841
|
-
void handleConnect({
|
|
842
|
-
source: connection.source,
|
|
843
|
-
target: connection.target,
|
|
844
|
-
sourceHandle: connection.sourceHandle ?? undefined,
|
|
845
|
-
targetHandle: connection.targetHandle ?? undefined
|
|
846
|
-
})}
|
|
853
|
+
onconnect={() => void handleConnect()}
|
|
847
854
|
onbeforedelete={handleBeforeDelete}
|
|
848
855
|
ondelete={handleNodesDelete}
|
|
849
856
|
onnodedragstart={handleNodeDragStart}
|
|
@@ -859,12 +866,12 @@
|
|
|
859
866
|
{initialViewport}
|
|
860
867
|
colorMode={getResolvedTheme() as ColorMode}
|
|
861
868
|
fitView={getEditorSettings().fitViewOnLoad}
|
|
862
|
-
nodesDraggable={
|
|
863
|
-
nodesConnectable={
|
|
864
|
-
elementsSelectable={
|
|
869
|
+
nodesDraggable={canvasEditable}
|
|
870
|
+
nodesConnectable={canvasEditable}
|
|
871
|
+
elementsSelectable={canvasEditable}
|
|
865
872
|
>
|
|
866
873
|
<Controls />
|
|
867
|
-
{#if
|
|
874
|
+
{#if canvasEditable && props.onToggleConsole}
|
|
868
875
|
<button
|
|
869
876
|
class="flowdrop-console-toggle"
|
|
870
877
|
class:flowdrop-console-toggle--active={props.consoleOpen}
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import '@xyflow/svelte/dist/style.css';
|
|
2
|
-
import type { WorkflowNode as WorkflowNodeType
|
|
2
|
+
import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
|
|
3
3
|
import type { EndpointConfig } from '../config/endpoints.js';
|
|
4
|
+
import type { FlowDropInstance } from '../stores/instanceContainer.svelte.js';
|
|
4
5
|
interface Props {
|
|
5
|
-
nodes?: NodeMetadata[];
|
|
6
6
|
endpointConfig?: EndpointConfig;
|
|
7
|
-
height?: string | number;
|
|
8
|
-
width?: string | number;
|
|
9
|
-
isConfigSidebarOpen?: boolean;
|
|
10
|
-
selectedNodeForConfig?: WorkflowNodeType | null;
|
|
11
7
|
openConfigSidebar?: (node: WorkflowNodeType) => void;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Editor interaction mode. `'edit'` allows node drag/connect/select and
|
|
10
|
+
* proximity-connect; `'readonly'` and `'locked'` disable all canvas
|
|
11
|
+
* editing (they behave identically today — see App's `mode` prop for the
|
|
12
|
+
* full matrix). Replaces the former `readOnly` + `lockWorkflow` booleans.
|
|
13
|
+
* @default 'edit'
|
|
14
|
+
*/
|
|
15
|
+
mode?: 'edit' | 'readonly' | 'locked';
|
|
16
16
|
pipelineId?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Increments to force a re-fetch of node execution info from the server.
|
|
19
|
+
* Used by parents (e.g. PipelineStatus) to push poll ticks / chat-message
|
|
20
|
+
* arrivals down to the canvas without owning a separate status channel.
|
|
21
|
+
*/
|
|
22
|
+
refreshTrigger?: number;
|
|
17
23
|
consoleOpen?: boolean;
|
|
18
24
|
onToggleConsole?: () => void;
|
|
25
|
+
/** Per-instance state container (created by mount functions). Defaults to the page-default instance. */
|
|
26
|
+
instance?: FlowDropInstance;
|
|
19
27
|
}
|
|
20
28
|
declare const WorkflowEditor: import("svelte").Component<Props, {
|
|
21
29
|
updateNodeData: (nodeId: string, dataUpdates: Partial<WorkflowNodeType["data"]>) => void;
|