@particle-academy/fancy-flow 0.18.0 → 0.20.0
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/dist/{chunk-FSC64BPD.js → chunk-AW2QUEL2.js} +3 -3
- package/dist/{chunk-FSC64BPD.js.map → chunk-AW2QUEL2.js.map} +1 -1
- package/dist/{chunk-MFMRTRPO.js → chunk-KI4RI2N3.js} +115 -6
- package/dist/chunk-KI4RI2N3.js.map +1 -0
- package/dist/{chunk-NF6NPY5N.js → chunk-XETAGLH6.js} +31 -3
- package/dist/chunk-XETAGLH6.js.map +1 -0
- package/dist/index.cjs +379 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.js +242 -33
- package/dist/index.js.map +1 -1
- package/dist/registry.js +2 -2
- package/dist/runtime/index.d.cts +61 -1
- package/dist/runtime/index.d.ts +61 -1
- package/dist/runtime.cjs +112 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +2 -2
- package/package.json +6 -3
- package/dist/chunk-MFMRTRPO.js.map +0 -1
- package/dist/chunk-NF6NPY5N.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
|
-
import { ReactFlowProps, Edge, BackgroundVariant, NodeProps, NodeTypes } from '@xyflow/react';
|
|
3
|
+
import { ReactFlowProps, Edge, BackgroundVariant, Connection, NodeProps, NodeTypes } from '@xyflow/react';
|
|
4
4
|
import { ConnectionValidatorOptions } from './registry/index.cjs';
|
|
5
5
|
export { ANY_PORT_TYPE, BUILTIN_KINDS, PortCompatibility, RegistryNode, RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, createConnectionValidator, defaultConfigFor, defaultPortCompatibility, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig } from './registry/index.cjs';
|
|
6
6
|
import { b as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-sOmpCitB.cjs';
|
|
@@ -10,7 +10,7 @@ export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW
|
|
|
10
10
|
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-D_jOR3Z2.cjs';
|
|
11
11
|
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-D_jOR3Z2.cjs';
|
|
12
12
|
import { FlowRunFeedEntry } from './runtime/index.cjs';
|
|
13
|
-
export { UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, useFlowRun, useFlowState } from './runtime/index.cjs';
|
|
13
|
+
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
|
|
14
14
|
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-VKm57Hcj.cjs';
|
|
15
15
|
export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.cjs';
|
|
16
16
|
import './capabilities-ChE3Xi8R.cjs';
|
|
@@ -50,6 +50,33 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
50
50
|
*/
|
|
51
51
|
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Clone a set of nodes AND the edges internal to that set, remapping every id.
|
|
55
|
+
* This is the piece `duplicateNode` lacks: a copy/paste (or duplicate-selection)
|
|
56
|
+
* that preserves the wiring *between* the copied nodes. Edges with an endpoint
|
|
57
|
+
* outside the set are dropped (they'd dangle). A `parentId` pointing inside the
|
|
58
|
+
* set is remapped; one pointing outside is detached.
|
|
59
|
+
*/
|
|
60
|
+
declare function cloneSubgraph(nodes: FlowNode[], edges: Edge[], opts: {
|
|
61
|
+
makeId: () => string;
|
|
62
|
+
offset?: number;
|
|
63
|
+
}): {
|
|
64
|
+
nodes: FlowNode[];
|
|
65
|
+
edges: Edge[];
|
|
66
|
+
idMap: Map<string, string>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Rewire one edge's endpoint(s) — wraps xyflow's `reconnectEdge`. Keeps the
|
|
70
|
+
* edge's id (`shouldReplaceId: false`) so its label and identity survive the
|
|
71
|
+
* rewire instead of being regenerated from the new endpoints.
|
|
72
|
+
*/
|
|
73
|
+
declare function reconnectEdge(edges: Edge[], oldEdge: Edge, newConnection: Connection): Edge[];
|
|
74
|
+
type AlignEdge = "left" | "hcenter" | "right" | "top" | "vcenter" | "bottom";
|
|
75
|
+
/** Align a selection to a shared edge/center of its bounding box. */
|
|
76
|
+
declare function alignNodes(nodes: FlowNode[], edge: AlignEdge): FlowNode[];
|
|
77
|
+
/** Evenly distribute a selection's gaps along an axis (needs 3+ nodes). */
|
|
78
|
+
declare function distributeNodes(nodes: FlowNode[], axis: "h" | "v"): FlowNode[];
|
|
79
|
+
|
|
53
80
|
/**
|
|
54
81
|
* Everything the editor knows and can do, handed to every extension point.
|
|
55
82
|
*
|
|
@@ -71,6 +98,10 @@ type FlowEditorApi = {
|
|
|
71
98
|
selectedEdgeId: string | null;
|
|
72
99
|
/** Currently selected edge, or null. */
|
|
73
100
|
selectedEdge: Edge | null;
|
|
101
|
+
/** All multi-selected node ids (xyflow box / shift selection). */
|
|
102
|
+
selectedIds: string[];
|
|
103
|
+
/** All multi-selected nodes. */
|
|
104
|
+
selectedNodes: FlowNode[];
|
|
74
105
|
/** True while a run is in flight. */
|
|
75
106
|
running: boolean;
|
|
76
107
|
/** Per-node run status, keyed by node id. */
|
|
@@ -99,6 +130,21 @@ type FlowEditorApi = {
|
|
|
99
130
|
duplicateNode: (id: string) => string | null;
|
|
100
131
|
/** Replace the whole graph. */
|
|
101
132
|
setGraph: (graph: FlowGraph) => void;
|
|
133
|
+
/** Duplicate the current multi-selection, preserving edges between them. */
|
|
134
|
+
duplicateSelected: () => void;
|
|
135
|
+
/** Align the multi-selection to a shared edge/center of its bounding box. */
|
|
136
|
+
alignSelected: (edge: AlignEdge) => void;
|
|
137
|
+
/** Evenly distribute the multi-selection's gaps along an axis (needs 3+). */
|
|
138
|
+
distributeSelected: (axis: "h" | "v") => void;
|
|
139
|
+
/** Copy the current selection to the editor clipboard. */
|
|
140
|
+
copy: () => void;
|
|
141
|
+
/** Copy then delete the current selection. */
|
|
142
|
+
cut: () => void;
|
|
143
|
+
/** Paste the clipboard (optionally at a flow position) and select the result. */
|
|
144
|
+
paste: (at?: {
|
|
145
|
+
x: number;
|
|
146
|
+
y: number;
|
|
147
|
+
}) => void;
|
|
102
148
|
run: () => void;
|
|
103
149
|
cancel: () => void;
|
|
104
150
|
reset: () => void;
|
|
@@ -109,6 +155,16 @@ type FlowEditorApi = {
|
|
|
109
155
|
/** Open a file picker and load a workflow. */
|
|
110
156
|
importWorkflow: () => void;
|
|
111
157
|
fitView: () => void;
|
|
158
|
+
/** Undo the last committing edit (add / delete / connect / config / drag /
|
|
159
|
+
* import). Transient interactions (a drag in progress, selection) are not
|
|
160
|
+
* their own steps. */
|
|
161
|
+
undo: () => void;
|
|
162
|
+
/** Redo the last undone edit. */
|
|
163
|
+
redo: () => void;
|
|
164
|
+
/** True when there is an edit to undo. */
|
|
165
|
+
canUndo: boolean;
|
|
166
|
+
/** True when there is an undone edit to redo. */
|
|
167
|
+
canRedo: boolean;
|
|
112
168
|
};
|
|
113
169
|
/**
|
|
114
170
|
* A declarative toolbar button. JSON-friendly on purpose — a host (or an
|
|
@@ -134,6 +190,8 @@ type FlowEditorAction = {
|
|
|
134
190
|
type FlowEditorBuiltins = {
|
|
135
191
|
run?: boolean;
|
|
136
192
|
delete?: boolean;
|
|
193
|
+
/** Undo/redo toolbar buttons. Default true. */
|
|
194
|
+
history?: boolean;
|
|
137
195
|
/** Right-click a node for Delete / Duplicate. Default true. */
|
|
138
196
|
contextMenu?: boolean;
|
|
139
197
|
/** Right-click a connection for Label / Delete. Default true. */
|
|
@@ -208,6 +266,17 @@ type FlowEditorProps = {
|
|
|
208
266
|
onDelete?: (ids: string[]) => void;
|
|
209
267
|
/** Called after connections are broken, with the deleted edge ids. */
|
|
210
268
|
onEdgeDelete?: (ids: string[]) => void;
|
|
269
|
+
/**
|
|
270
|
+
* Stage destructive edits for human confirmation. When set, every delete path
|
|
271
|
+
* — keyboard, panel, context menu, and `api.deleteNodes`/`deleteEdges` —
|
|
272
|
+
* calls this first; return false to veto. Default: delete immediately. This
|
|
273
|
+
* realizes the component contract's "agents propose, humans confirm" on the
|
|
274
|
+
* canvas.
|
|
275
|
+
*/
|
|
276
|
+
confirmDelete?: (targets: {
|
|
277
|
+
nodes: FlowNode[];
|
|
278
|
+
edges: Edge[];
|
|
279
|
+
}) => boolean | Promise<boolean>;
|
|
211
280
|
className?: string;
|
|
212
281
|
style?: CSSProperties;
|
|
213
282
|
};
|
|
@@ -453,4 +522,4 @@ type FlowRunFeedProps = {
|
|
|
453
522
|
/** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
|
|
454
523
|
declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
|
|
455
524
|
|
|
456
|
-
export { ActionNode, ConfigField, ConfigFieldRenderer, type ConfigFieldRendererProps, ConnectionValidatorOptions, DecisionNode, ExecutorRegistry, FlowCanvas, type FlowCanvasProps, FlowEditor, type FlowEditorAction, type FlowEditorApi, type FlowEditorBuiltins, type FlowEditorProps, type FlowEditorSlots, FlowGraph, FlowNode, type FlowNodeRenderProps, FlowRunControls, type FlowRunControlsProps, FlowRunFeed, FlowRunFeedEntry, type FlowRunFeedProps, NodeCategory, NodeConfigPanel, type NodeConfigPanelProps, NodeKindDefinition, NodePalette, type NodePaletteProps, NodePort, type NodePortProps, type NodePortSide, type NodePortType, NodeRunStatus, NodeShell, type NodeShellProps, NoteNode, OutputNode, SubgraphNode, TriggerNode, WorkflowMetadata, WorkflowSchema, defaultNodeTypes, defineNode, paletteDropHandlers, useFlowEditor, useFlowEditorOptional };
|
|
525
|
+
export { ActionNode, type AlignEdge, ConfigField, ConfigFieldRenderer, type ConfigFieldRendererProps, ConnectionValidatorOptions, DecisionNode, ExecutorRegistry, FlowCanvas, type FlowCanvasProps, FlowEditor, type FlowEditorAction, type FlowEditorApi, type FlowEditorBuiltins, type FlowEditorProps, type FlowEditorSlots, FlowGraph, FlowNode, type FlowNodeRenderProps, FlowRunControls, type FlowRunControlsProps, FlowRunFeed, FlowRunFeedEntry, type FlowRunFeedProps, NodeCategory, NodeConfigPanel, type NodeConfigPanelProps, NodeKindDefinition, NodePalette, type NodePaletteProps, NodePort, type NodePortProps, type NodePortSide, type NodePortType, NodeRunStatus, NodeShell, type NodeShellProps, NoteNode, OutputNode, SubgraphNode, TriggerNode, WorkflowMetadata, WorkflowSchema, alignNodes, cloneSubgraph, defaultNodeTypes, defineNode, distributeNodes, paletteDropHandlers, reconnectEdge, useFlowEditor, useFlowEditorOptional };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, ComponentType } from 'react';
|
|
3
|
-
import { ReactFlowProps, Edge, BackgroundVariant, NodeProps, NodeTypes } from '@xyflow/react';
|
|
3
|
+
import { ReactFlowProps, Edge, BackgroundVariant, Connection, NodeProps, NodeTypes } from '@xyflow/react';
|
|
4
4
|
import { ConnectionValidatorOptions } from './registry/index.js';
|
|
5
5
|
export { ANY_PORT_TYPE, BUILTIN_KINDS, PortCompatibility, RegistryNode, RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, createConnectionValidator, defaultConfigFor, defaultPortCompatibility, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig } from './registry/index.js';
|
|
6
6
|
import { b as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-sOmpCitB.js';
|
|
@@ -10,7 +10,7 @@ export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW
|
|
|
10
10
|
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-NerkPtHS.js';
|
|
11
11
|
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-NerkPtHS.js';
|
|
12
12
|
import { FlowRunFeedEntry } from './runtime/index.js';
|
|
13
|
-
export { UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, useFlowRun, useFlowState } from './runtime/index.js';
|
|
13
|
+
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
|
|
14
14
|
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-ChBi6q-W.js';
|
|
15
15
|
export { L as LEGACY_PAUSE_PREFIXES, a as PAUSE_PREFIX, P as PauseAwaiting, b as PauseSignal, d as decodePause, e as encodePause, i as isPause, p as pauseForHuman } from './pause-9iT4tCEV.js';
|
|
16
16
|
import './capabilities-DUhAc-EJ.js';
|
|
@@ -50,6 +50,33 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
50
50
|
*/
|
|
51
51
|
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Clone a set of nodes AND the edges internal to that set, remapping every id.
|
|
55
|
+
* This is the piece `duplicateNode` lacks: a copy/paste (or duplicate-selection)
|
|
56
|
+
* that preserves the wiring *between* the copied nodes. Edges with an endpoint
|
|
57
|
+
* outside the set are dropped (they'd dangle). A `parentId` pointing inside the
|
|
58
|
+
* set is remapped; one pointing outside is detached.
|
|
59
|
+
*/
|
|
60
|
+
declare function cloneSubgraph(nodes: FlowNode[], edges: Edge[], opts: {
|
|
61
|
+
makeId: () => string;
|
|
62
|
+
offset?: number;
|
|
63
|
+
}): {
|
|
64
|
+
nodes: FlowNode[];
|
|
65
|
+
edges: Edge[];
|
|
66
|
+
idMap: Map<string, string>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Rewire one edge's endpoint(s) — wraps xyflow's `reconnectEdge`. Keeps the
|
|
70
|
+
* edge's id (`shouldReplaceId: false`) so its label and identity survive the
|
|
71
|
+
* rewire instead of being regenerated from the new endpoints.
|
|
72
|
+
*/
|
|
73
|
+
declare function reconnectEdge(edges: Edge[], oldEdge: Edge, newConnection: Connection): Edge[];
|
|
74
|
+
type AlignEdge = "left" | "hcenter" | "right" | "top" | "vcenter" | "bottom";
|
|
75
|
+
/** Align a selection to a shared edge/center of its bounding box. */
|
|
76
|
+
declare function alignNodes(nodes: FlowNode[], edge: AlignEdge): FlowNode[];
|
|
77
|
+
/** Evenly distribute a selection's gaps along an axis (needs 3+ nodes). */
|
|
78
|
+
declare function distributeNodes(nodes: FlowNode[], axis: "h" | "v"): FlowNode[];
|
|
79
|
+
|
|
53
80
|
/**
|
|
54
81
|
* Everything the editor knows and can do, handed to every extension point.
|
|
55
82
|
*
|
|
@@ -71,6 +98,10 @@ type FlowEditorApi = {
|
|
|
71
98
|
selectedEdgeId: string | null;
|
|
72
99
|
/** Currently selected edge, or null. */
|
|
73
100
|
selectedEdge: Edge | null;
|
|
101
|
+
/** All multi-selected node ids (xyflow box / shift selection). */
|
|
102
|
+
selectedIds: string[];
|
|
103
|
+
/** All multi-selected nodes. */
|
|
104
|
+
selectedNodes: FlowNode[];
|
|
74
105
|
/** True while a run is in flight. */
|
|
75
106
|
running: boolean;
|
|
76
107
|
/** Per-node run status, keyed by node id. */
|
|
@@ -99,6 +130,21 @@ type FlowEditorApi = {
|
|
|
99
130
|
duplicateNode: (id: string) => string | null;
|
|
100
131
|
/** Replace the whole graph. */
|
|
101
132
|
setGraph: (graph: FlowGraph) => void;
|
|
133
|
+
/** Duplicate the current multi-selection, preserving edges between them. */
|
|
134
|
+
duplicateSelected: () => void;
|
|
135
|
+
/** Align the multi-selection to a shared edge/center of its bounding box. */
|
|
136
|
+
alignSelected: (edge: AlignEdge) => void;
|
|
137
|
+
/** Evenly distribute the multi-selection's gaps along an axis (needs 3+). */
|
|
138
|
+
distributeSelected: (axis: "h" | "v") => void;
|
|
139
|
+
/** Copy the current selection to the editor clipboard. */
|
|
140
|
+
copy: () => void;
|
|
141
|
+
/** Copy then delete the current selection. */
|
|
142
|
+
cut: () => void;
|
|
143
|
+
/** Paste the clipboard (optionally at a flow position) and select the result. */
|
|
144
|
+
paste: (at?: {
|
|
145
|
+
x: number;
|
|
146
|
+
y: number;
|
|
147
|
+
}) => void;
|
|
102
148
|
run: () => void;
|
|
103
149
|
cancel: () => void;
|
|
104
150
|
reset: () => void;
|
|
@@ -109,6 +155,16 @@ type FlowEditorApi = {
|
|
|
109
155
|
/** Open a file picker and load a workflow. */
|
|
110
156
|
importWorkflow: () => void;
|
|
111
157
|
fitView: () => void;
|
|
158
|
+
/** Undo the last committing edit (add / delete / connect / config / drag /
|
|
159
|
+
* import). Transient interactions (a drag in progress, selection) are not
|
|
160
|
+
* their own steps. */
|
|
161
|
+
undo: () => void;
|
|
162
|
+
/** Redo the last undone edit. */
|
|
163
|
+
redo: () => void;
|
|
164
|
+
/** True when there is an edit to undo. */
|
|
165
|
+
canUndo: boolean;
|
|
166
|
+
/** True when there is an undone edit to redo. */
|
|
167
|
+
canRedo: boolean;
|
|
112
168
|
};
|
|
113
169
|
/**
|
|
114
170
|
* A declarative toolbar button. JSON-friendly on purpose — a host (or an
|
|
@@ -134,6 +190,8 @@ type FlowEditorAction = {
|
|
|
134
190
|
type FlowEditorBuiltins = {
|
|
135
191
|
run?: boolean;
|
|
136
192
|
delete?: boolean;
|
|
193
|
+
/** Undo/redo toolbar buttons. Default true. */
|
|
194
|
+
history?: boolean;
|
|
137
195
|
/** Right-click a node for Delete / Duplicate. Default true. */
|
|
138
196
|
contextMenu?: boolean;
|
|
139
197
|
/** Right-click a connection for Label / Delete. Default true. */
|
|
@@ -208,6 +266,17 @@ type FlowEditorProps = {
|
|
|
208
266
|
onDelete?: (ids: string[]) => void;
|
|
209
267
|
/** Called after connections are broken, with the deleted edge ids. */
|
|
210
268
|
onEdgeDelete?: (ids: string[]) => void;
|
|
269
|
+
/**
|
|
270
|
+
* Stage destructive edits for human confirmation. When set, every delete path
|
|
271
|
+
* — keyboard, panel, context menu, and `api.deleteNodes`/`deleteEdges` —
|
|
272
|
+
* calls this first; return false to veto. Default: delete immediately. This
|
|
273
|
+
* realizes the component contract's "agents propose, humans confirm" on the
|
|
274
|
+
* canvas.
|
|
275
|
+
*/
|
|
276
|
+
confirmDelete?: (targets: {
|
|
277
|
+
nodes: FlowNode[];
|
|
278
|
+
edges: Edge[];
|
|
279
|
+
}) => boolean | Promise<boolean>;
|
|
211
280
|
className?: string;
|
|
212
281
|
style?: CSSProperties;
|
|
213
282
|
};
|
|
@@ -453,4 +522,4 @@ type FlowRunFeedProps = {
|
|
|
453
522
|
/** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
|
|
454
523
|
declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
|
|
455
524
|
|
|
456
|
-
export { ActionNode, ConfigField, ConfigFieldRenderer, type ConfigFieldRendererProps, ConnectionValidatorOptions, DecisionNode, ExecutorRegistry, FlowCanvas, type FlowCanvasProps, FlowEditor, type FlowEditorAction, type FlowEditorApi, type FlowEditorBuiltins, type FlowEditorProps, type FlowEditorSlots, FlowGraph, FlowNode, type FlowNodeRenderProps, FlowRunControls, type FlowRunControlsProps, FlowRunFeed, FlowRunFeedEntry, type FlowRunFeedProps, NodeCategory, NodeConfigPanel, type NodeConfigPanelProps, NodeKindDefinition, NodePalette, type NodePaletteProps, NodePort, type NodePortProps, type NodePortSide, type NodePortType, NodeRunStatus, NodeShell, type NodeShellProps, NoteNode, OutputNode, SubgraphNode, TriggerNode, WorkflowMetadata, WorkflowSchema, defaultNodeTypes, defineNode, paletteDropHandlers, useFlowEditor, useFlowEditorOptional };
|
|
525
|
+
export { ActionNode, type AlignEdge, ConfigField, ConfigFieldRenderer, type ConfigFieldRendererProps, ConnectionValidatorOptions, DecisionNode, ExecutorRegistry, FlowCanvas, type FlowCanvasProps, FlowEditor, type FlowEditorAction, type FlowEditorApi, type FlowEditorBuiltins, type FlowEditorProps, type FlowEditorSlots, FlowGraph, FlowNode, type FlowNodeRenderProps, FlowRunControls, type FlowRunControlsProps, FlowRunFeed, FlowRunFeedEntry, type FlowRunFeedProps, NodeCategory, NodeConfigPanel, type NodeConfigPanelProps, NodeKindDefinition, NodePalette, type NodePaletteProps, NodePort, type NodePortProps, type NodePortSide, type NodePortType, NodeRunStatus, NodeShell, type NodeShellProps, NoteNode, OutputNode, SubgraphNode, TriggerNode, WorkflowMetadata, WorkflowSchema, alignNodes, cloneSubgraph, defaultNodeTypes, defineNode, distributeNodes, paletteDropHandlers, reconnectEdge, useFlowEditor, useFlowEditorOptional };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useFlowState, useFlowRun, applyStatusesToNodes } from './chunk-
|
|
2
|
-
export { applyStatusesToNodes, useFlowRun, useFlowState } from './chunk-
|
|
3
|
-
import { buildNodeTypes, registerBuiltinKinds, createConnectionValidator } from './chunk-
|
|
4
|
-
export { ANY_PORT_TYPE, BUILTIN_KINDS, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds } from './chunk-
|
|
5
|
-
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, index, BackgroundVariant, Background, Controls, MiniMap } from './chunk-
|
|
1
|
+
import { useFlowState, useFlowRun, useFlowHistory, applyStatusesToNodes } from './chunk-KI4RI2N3.js';
|
|
2
|
+
export { applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-KI4RI2N3.js';
|
|
3
|
+
import { buildNodeTypes, registerBuiltinKinds, createConnectionValidator } from './chunk-AW2QUEL2.js';
|
|
4
|
+
export { ANY_PORT_TYPE, BUILTIN_KINDS, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds } from './chunk-AW2QUEL2.js';
|
|
5
|
+
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap } from './chunk-XETAGLH6.js';
|
|
6
6
|
export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
|
|
7
7
|
export { runFlow } from './chunk-L4AX73Q6.js';
|
|
8
8
|
import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-D6W5FMCT.js';
|
|
@@ -13,7 +13,7 @@ export { categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKin
|
|
|
13
13
|
import { getRichInputAdapter } from './chunk-F5RPRB7A.js';
|
|
14
14
|
export { RichInputPreview, getRichInputAdapter, isRichInputEnabled, onRichInputAdapterChanged, registerRichInputAdapter } from './chunk-F5RPRB7A.js';
|
|
15
15
|
import './chunk-USL4FMFU.js';
|
|
16
|
-
import { memo, createContext, forwardRef, useState, useEffect, useMemo, useCallback,
|
|
16
|
+
import { memo, createContext, forwardRef, useState, useEffect, useMemo, useCallback, useRef, useImperativeHandle, useContext } from 'react';
|
|
17
17
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
18
18
|
|
|
19
19
|
function NodeShellInner({
|
|
@@ -839,6 +839,65 @@ function duplicateNode(node, id, offset = 40) {
|
|
|
839
839
|
data: JSON.parse(JSON.stringify(node.data ?? {}))
|
|
840
840
|
};
|
|
841
841
|
}
|
|
842
|
+
function cloneSubgraph(nodes, edges, opts) {
|
|
843
|
+
const offset = opts.offset ?? 40;
|
|
844
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
845
|
+
for (const n of nodes) idMap.set(n.id, opts.makeId());
|
|
846
|
+
const clonedNodes = nodes.map((n) => {
|
|
847
|
+
const cloned = duplicateNode(n, idMap.get(n.id), offset);
|
|
848
|
+
const parentId = n.parentId;
|
|
849
|
+
if (parentId && idMap.has(parentId)) cloned.parentId = idMap.get(parentId);
|
|
850
|
+
else if (parentId) delete cloned.parentId;
|
|
851
|
+
return cloned;
|
|
852
|
+
});
|
|
853
|
+
const clonedEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((e) => ({ ...e, id: opts.makeId(), source: idMap.get(e.source), target: idMap.get(e.target) }));
|
|
854
|
+
return { nodes: clonedNodes, edges: clonedEdges, idMap };
|
|
855
|
+
}
|
|
856
|
+
function reconnectEdge2(edges, oldEdge, newConnection) {
|
|
857
|
+
return reconnectEdge(oldEdge, newConnection, edges, { shouldReplaceId: false });
|
|
858
|
+
}
|
|
859
|
+
var nodeW = (n) => n.width ?? n.measured?.width ?? 0;
|
|
860
|
+
var nodeH = (n) => n.height ?? n.measured?.height ?? 0;
|
|
861
|
+
function alignNodes(nodes, edge) {
|
|
862
|
+
if (nodes.length < 2) return nodes;
|
|
863
|
+
const minL = Math.min(...nodes.map((n) => n.position.x));
|
|
864
|
+
const maxR = Math.max(...nodes.map((n) => n.position.x + nodeW(n)));
|
|
865
|
+
const minT = Math.min(...nodes.map((n) => n.position.y));
|
|
866
|
+
const maxB = Math.max(...nodes.map((n) => n.position.y + nodeH(n)));
|
|
867
|
+
const cx = (minL + maxR) / 2;
|
|
868
|
+
const cy = (minT + maxB) / 2;
|
|
869
|
+
return nodes.map((n) => {
|
|
870
|
+
let { x, y } = n.position;
|
|
871
|
+
if (edge === "left") x = minL;
|
|
872
|
+
else if (edge === "right") x = maxR - nodeW(n);
|
|
873
|
+
else if (edge === "hcenter") x = cx - nodeW(n) / 2;
|
|
874
|
+
else if (edge === "top") y = minT;
|
|
875
|
+
else if (edge === "bottom") y = maxB - nodeH(n);
|
|
876
|
+
else if (edge === "vcenter") y = cy - nodeH(n) / 2;
|
|
877
|
+
return { ...n, position: { x, y } };
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
function distributeNodes(nodes, axis) {
|
|
881
|
+
if (nodes.length < 3) return nodes;
|
|
882
|
+
const size = (n) => axis === "h" ? nodeW(n) : nodeH(n);
|
|
883
|
+
const coord = (n) => axis === "h" ? n.position.x : n.position.y;
|
|
884
|
+
const sorted = [...nodes].sort((a, b) => coord(a) - coord(b));
|
|
885
|
+
const start = coord(sorted[0]);
|
|
886
|
+
const last = sorted[sorted.length - 1];
|
|
887
|
+
const end = coord(last) + size(last);
|
|
888
|
+
const totalSize = sorted.reduce((s, n) => s + size(n), 0);
|
|
889
|
+
const gap = (end - start - totalSize) / (sorted.length - 1);
|
|
890
|
+
const posById = /* @__PURE__ */ new Map();
|
|
891
|
+
let cursor = start;
|
|
892
|
+
for (const n of sorted) {
|
|
893
|
+
posById.set(n.id, cursor);
|
|
894
|
+
cursor += size(n) + gap;
|
|
895
|
+
}
|
|
896
|
+
return nodes.map((n) => {
|
|
897
|
+
const p = posById.get(n.id);
|
|
898
|
+
return { ...n, position: axis === "h" ? { ...n.position, x: p } : { ...n.position, y: p } };
|
|
899
|
+
});
|
|
900
|
+
}
|
|
842
901
|
var FlowEditorContext = createContext(null);
|
|
843
902
|
var FlowEditorProvider = FlowEditorContext.Provider;
|
|
844
903
|
function useFlowEditor() {
|
|
@@ -878,13 +937,16 @@ function FlowEditorInner({
|
|
|
878
937
|
onSelectionChange,
|
|
879
938
|
onDelete,
|
|
880
939
|
onEdgeDelete,
|
|
940
|
+
confirmDelete,
|
|
881
941
|
apiRef
|
|
882
942
|
}) {
|
|
883
943
|
const internal = useFlowState(initial);
|
|
884
944
|
const runner = useFlowRun();
|
|
885
945
|
const rf = useReactFlow();
|
|
886
946
|
const controlled = value !== void 0;
|
|
887
|
-
const
|
|
947
|
+
const baseFlow = controlled ? makeControlledFlowAdapter(value, onChange) : internal;
|
|
948
|
+
const hist = useFlowHistory(baseFlow);
|
|
949
|
+
const flow = hist.flow;
|
|
888
950
|
const [, force] = useState(0);
|
|
889
951
|
useEffect(() => onNodeKindsChanged(() => force((n) => n + 1)), []);
|
|
890
952
|
const nodeTypes = useMemo(() => buildNodeTypes(), [listNodeKinds().length]);
|
|
@@ -937,6 +999,23 @@ function FlowEditorInner({
|
|
|
937
999
|
window.removeEventListener("keydown", onKey);
|
|
938
1000
|
};
|
|
939
1001
|
}, [menu, labelEdit]);
|
|
1002
|
+
useEffect(() => {
|
|
1003
|
+
const onKey = (e) => {
|
|
1004
|
+
const t = e.target;
|
|
1005
|
+
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
|
|
1006
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1007
|
+
const key = e.key.toLowerCase();
|
|
1008
|
+
if (key === "z" && !e.shiftKey) {
|
|
1009
|
+
e.preventDefault();
|
|
1010
|
+
hist.undo();
|
|
1011
|
+
} else if (key === "z" && e.shiftKey || key === "y") {
|
|
1012
|
+
e.preventDefault();
|
|
1013
|
+
hist.redo();
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
window.addEventListener("keydown", onKey);
|
|
1017
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1018
|
+
}, [hist]);
|
|
940
1019
|
useEffect(() => {
|
|
941
1020
|
if (!controlled) onChange?.({ nodes: flow.nodes, edges: flow.edges });
|
|
942
1021
|
}, [flow.nodes, flow.edges, onChange, controlled]);
|
|
@@ -961,16 +1040,116 @@ function FlowEditorInner({
|
|
|
961
1040
|
[flow]
|
|
962
1041
|
);
|
|
963
1042
|
const deleteNodes = useCallback(
|
|
964
|
-
(ids) => {
|
|
1043
|
+
async (ids) => {
|
|
965
1044
|
if (ids.length === 0) return;
|
|
1045
|
+
const targets = flow.nodes.filter((n) => ids.includes(n.id));
|
|
1046
|
+
if (targets.length === 0) return;
|
|
1047
|
+
if (confirmDelete) {
|
|
1048
|
+
const attached = flow.edges.filter((e) => ids.includes(e.source) || ids.includes(e.target));
|
|
1049
|
+
if (!await confirmDelete({ nodes: targets, edges: attached })) return;
|
|
1050
|
+
}
|
|
966
1051
|
const doomed = new Set(ids);
|
|
967
|
-
flow.
|
|
968
|
-
flow.setEdges((all) => removeNodes({ nodes: [], edges: all }, ids).edges);
|
|
1052
|
+
flow.setGraph(removeNodes({ nodes: flow.nodes, edges: flow.edges }, ids));
|
|
969
1053
|
setSelectedId((cur) => cur !== null && doomed.has(cur) ? null : cur);
|
|
970
1054
|
onDelete?.(ids);
|
|
971
1055
|
},
|
|
972
|
-
[flow, onDelete]
|
|
1056
|
+
[flow, onDelete, confirmDelete]
|
|
1057
|
+
);
|
|
1058
|
+
const deleteEdges = useCallback(
|
|
1059
|
+
async (ids) => {
|
|
1060
|
+
if (ids.length === 0) return;
|
|
1061
|
+
const targets = flow.edges.filter((e) => ids.includes(e.id));
|
|
1062
|
+
if (targets.length === 0) return;
|
|
1063
|
+
if (confirmDelete && !await confirmDelete({ nodes: [], edges: targets })) return;
|
|
1064
|
+
flow.setEdges((all) => removeEdges(all, ids));
|
|
1065
|
+
setSelectedEdgeId((cur) => cur !== null && ids.includes(cur) ? null : cur);
|
|
1066
|
+
onEdgeDelete?.(ids);
|
|
1067
|
+
},
|
|
1068
|
+
[flow, onEdgeDelete, confirmDelete]
|
|
1069
|
+
);
|
|
1070
|
+
const selectedNodes = useMemo(() => flow.nodes.filter((n) => n.selected), [flow.nodes]);
|
|
1071
|
+
const selectedIds = useMemo(() => selectedNodes.map((n) => n.id), [selectedNodes]);
|
|
1072
|
+
const clipboard = useRef(null);
|
|
1073
|
+
const copySelection = useCallback(() => {
|
|
1074
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
1075
|
+
if (sel.length === 0) return;
|
|
1076
|
+
const ids = new Set(sel.map((n) => n.id));
|
|
1077
|
+
const edges = flow.edges.filter((e) => ids.has(e.source) && ids.has(e.target));
|
|
1078
|
+
clipboard.current = { nodes: sel.map((n) => ({ ...n })), edges: edges.map((e) => ({ ...e })) };
|
|
1079
|
+
}, [flow]);
|
|
1080
|
+
const pasteClipboard = useCallback(
|
|
1081
|
+
(at) => {
|
|
1082
|
+
const clip = clipboard.current;
|
|
1083
|
+
if (!clip || clip.nodes.length === 0) return;
|
|
1084
|
+
const { nodes: clones, edges: cloneEdges } = cloneSubgraph(clip.nodes, clip.edges, { makeId: newNodeId, offset: 40 });
|
|
1085
|
+
let placed = clones;
|
|
1086
|
+
if (at) {
|
|
1087
|
+
const minX = Math.min(...clones.map((n) => n.position.x));
|
|
1088
|
+
const minY = Math.min(...clones.map((n) => n.position.y));
|
|
1089
|
+
placed = clones.map((n) => ({ ...n, position: { x: n.position.x - minX + at.x, y: n.position.y - minY + at.y } }));
|
|
1090
|
+
}
|
|
1091
|
+
flow.setGraph({
|
|
1092
|
+
nodes: [...flow.nodes.map((n) => ({ ...n, selected: false })), ...placed.map((n) => ({ ...n, selected: true }))],
|
|
1093
|
+
edges: [...flow.edges, ...cloneEdges]
|
|
1094
|
+
});
|
|
1095
|
+
},
|
|
1096
|
+
[flow]
|
|
1097
|
+
);
|
|
1098
|
+
const duplicateSelected = useCallback(() => {
|
|
1099
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
1100
|
+
if (sel.length === 0) return;
|
|
1101
|
+
const ids = new Set(sel.map((n) => n.id));
|
|
1102
|
+
const internal2 = flow.edges.filter((e) => ids.has(e.source) && ids.has(e.target));
|
|
1103
|
+
const { nodes: clones, edges: cloneEdges } = cloneSubgraph(sel, internal2, { makeId: newNodeId, offset: 40 });
|
|
1104
|
+
flow.setGraph({
|
|
1105
|
+
nodes: [...flow.nodes.map((n) => ({ ...n, selected: false })), ...clones.map((n) => ({ ...n, selected: true }))],
|
|
1106
|
+
edges: [...flow.edges, ...cloneEdges]
|
|
1107
|
+
});
|
|
1108
|
+
}, [flow]);
|
|
1109
|
+
const alignSelected = useCallback(
|
|
1110
|
+
(edge) => {
|
|
1111
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
1112
|
+
if (sel.length < 2) return;
|
|
1113
|
+
const byId = new Map(alignNodes(sel, edge).map((n) => [n.id, n]));
|
|
1114
|
+
flow.setNodes((all) => all.map((n) => byId.get(n.id) ?? n));
|
|
1115
|
+
},
|
|
1116
|
+
[flow]
|
|
973
1117
|
);
|
|
1118
|
+
const distributeSelected = useCallback(
|
|
1119
|
+
(axis) => {
|
|
1120
|
+
const sel = flow.nodes.filter((n) => n.selected);
|
|
1121
|
+
if (sel.length < 3) return;
|
|
1122
|
+
const byId = new Map(distributeNodes(sel, axis).map((n) => [n.id, n]));
|
|
1123
|
+
flow.setNodes((all) => all.map((n) => byId.get(n.id) ?? n));
|
|
1124
|
+
},
|
|
1125
|
+
[flow]
|
|
1126
|
+
);
|
|
1127
|
+
const onReconnect = useCallback(
|
|
1128
|
+
(oldEdge, conn) => flow.setEdges((eds) => reconnectEdge2(eds, oldEdge, conn)),
|
|
1129
|
+
[flow]
|
|
1130
|
+
);
|
|
1131
|
+
useEffect(() => {
|
|
1132
|
+
const onKey = (e) => {
|
|
1133
|
+
const t = e.target;
|
|
1134
|
+
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
|
|
1135
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
1136
|
+
const key = e.key.toLowerCase();
|
|
1137
|
+
if (key === "c") copySelection();
|
|
1138
|
+
else if (key === "x") {
|
|
1139
|
+
e.preventDefault();
|
|
1140
|
+
copySelection();
|
|
1141
|
+
deleteNodes(selectedIds);
|
|
1142
|
+
} else if (key === "v") {
|
|
1143
|
+
e.preventDefault();
|
|
1144
|
+
pasteClipboard();
|
|
1145
|
+
} else if (key === "d") {
|
|
1146
|
+
e.preventDefault();
|
|
1147
|
+
duplicateSelected();
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
window.addEventListener("keydown", onKey);
|
|
1151
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
1152
|
+
}, [copySelection, pasteClipboard, duplicateSelected, deleteNodes, selectedIds]);
|
|
974
1153
|
const api = useMemo(() => {
|
|
975
1154
|
const toWorkflow = () => exportWorkflow({ nodes: flow.nodes, edges: flow.edges }, metadata);
|
|
976
1155
|
return {
|
|
@@ -981,6 +1160,8 @@ function FlowEditorInner({
|
|
|
981
1160
|
selected,
|
|
982
1161
|
selectedEdgeId,
|
|
983
1162
|
selectedEdge,
|
|
1163
|
+
selectedIds,
|
|
1164
|
+
selectedNodes,
|
|
984
1165
|
running: runner.running,
|
|
985
1166
|
statuses: runner.statuses,
|
|
986
1167
|
select: setSelectedId,
|
|
@@ -989,18 +1170,8 @@ function FlowEditorInner({
|
|
|
989
1170
|
updateNode: (next) => flow.setNodes((all) => all.map((x) => x.id === next.id ? next : x)),
|
|
990
1171
|
deleteNodes,
|
|
991
1172
|
deleteSelected: () => deleteNodes(selectedId ? [selectedId] : []),
|
|
992
|
-
deleteEdges
|
|
993
|
-
|
|
994
|
-
flow.setEdges((all) => removeEdges(all, ids));
|
|
995
|
-
setSelectedEdgeId((cur) => cur !== null && ids.includes(cur) ? null : cur);
|
|
996
|
-
onEdgeDelete?.(ids);
|
|
997
|
-
},
|
|
998
|
-
deleteSelectedEdge: () => {
|
|
999
|
-
if (!selectedEdgeId) return;
|
|
1000
|
-
flow.setEdges((all) => removeEdges(all, [selectedEdgeId]));
|
|
1001
|
-
setSelectedEdgeId(null);
|
|
1002
|
-
onEdgeDelete?.([selectedEdgeId]);
|
|
1003
|
-
},
|
|
1173
|
+
deleteEdges,
|
|
1174
|
+
deleteSelectedEdge: () => deleteEdges(selectedEdgeId ? [selectedEdgeId] : []),
|
|
1004
1175
|
setEdgeLabel: (id, label) => flow.setEdges((all) => setEdgeLabel(all, id, label)),
|
|
1005
1176
|
duplicateNode: (id) => {
|
|
1006
1177
|
const src = flow.nodes.find((n) => n.id === id);
|
|
@@ -1010,22 +1181,29 @@ function FlowEditorInner({
|
|
|
1010
1181
|
setSelectedId(copy.id);
|
|
1011
1182
|
return copy.id;
|
|
1012
1183
|
},
|
|
1013
|
-
setGraph: (graph) =>
|
|
1014
|
-
|
|
1015
|
-
|
|
1184
|
+
setGraph: (graph) => flow.setGraph(graph),
|
|
1185
|
+
duplicateSelected,
|
|
1186
|
+
alignSelected,
|
|
1187
|
+
distributeSelected,
|
|
1188
|
+
copy: copySelection,
|
|
1189
|
+
cut: () => {
|
|
1190
|
+
copySelection();
|
|
1191
|
+
deleteNodes(selectedIds);
|
|
1016
1192
|
},
|
|
1193
|
+
paste: pasteClipboard,
|
|
1017
1194
|
run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, executors),
|
|
1018
1195
|
cancel: runner.cancel,
|
|
1019
1196
|
reset: runner.reset,
|
|
1020
1197
|
toWorkflow,
|
|
1021
1198
|
exportWorkflow: () => downloadWorkflow(toWorkflow(), metadata),
|
|
1022
|
-
importWorkflow: () => pickWorkflow((graph) =>
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1199
|
+
importWorkflow: () => pickWorkflow((graph) => flow.setGraph(graph)),
|
|
1200
|
+
fitView: () => rf.fitView({ padding: 0.2 }),
|
|
1201
|
+
undo: hist.undo,
|
|
1202
|
+
redo: hist.redo,
|
|
1203
|
+
canUndo: hist.canUndo,
|
|
1204
|
+
canRedo: hist.canRedo
|
|
1027
1205
|
};
|
|
1028
|
-
}, [flow, selectedId, selected, runner, executors, metadata, addNode, deleteNodes, rf]);
|
|
1206
|
+
}, [flow, selectedId, selected, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, executors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, hist, rf]);
|
|
1029
1207
|
useImperativeHandle(apiRef, () => api, [api]);
|
|
1030
1208
|
const dropHandlers = paletteDropHandlers((kindName, evt) => {
|
|
1031
1209
|
const point = rf.screenToFlowPosition({ x: evt.clientX, y: evt.clientY });
|
|
@@ -1036,6 +1214,31 @@ function FlowEditorInner({
|
|
|
1036
1214
|
const toolbar = slots.toolbar ? slots.toolbar(api) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1037
1215
|
startActions.map((a) => renderAction(a, api)),
|
|
1038
1216
|
builtins.run !== false && /* @__PURE__ */ jsx(FlowRunControls, { running: api.running, onRun: api.run, onCancel: api.cancel, onReset: api.reset }),
|
|
1217
|
+
builtins.history !== false && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1218
|
+
/* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
|
|
1219
|
+
/* @__PURE__ */ jsx(
|
|
1220
|
+
"button",
|
|
1221
|
+
{
|
|
1222
|
+
className: "ff-editor__btn",
|
|
1223
|
+
"data-action": "undo",
|
|
1224
|
+
title: "Undo (Ctrl+Z)",
|
|
1225
|
+
disabled: !api.canUndo,
|
|
1226
|
+
onClick: api.undo,
|
|
1227
|
+
children: "\u21B6 Undo"
|
|
1228
|
+
}
|
|
1229
|
+
),
|
|
1230
|
+
/* @__PURE__ */ jsx(
|
|
1231
|
+
"button",
|
|
1232
|
+
{
|
|
1233
|
+
className: "ff-editor__btn",
|
|
1234
|
+
"data-action": "redo",
|
|
1235
|
+
title: "Redo (Ctrl+Shift+Z)",
|
|
1236
|
+
disabled: !api.canRedo,
|
|
1237
|
+
onClick: api.redo,
|
|
1238
|
+
children: "\u21B7 Redo"
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] }),
|
|
1039
1242
|
(builtins.export !== false || builtins.import !== false) && /* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
|
|
1040
1243
|
builtins.export !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "export", onClick: api.exportWorkflow, children: "\u2193 Export" }),
|
|
1041
1244
|
builtins.import !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "import", onClick: api.importWorkflow, children: "\u2191 Import" }),
|
|
@@ -1060,12 +1263,16 @@ function FlowEditorInner({
|
|
|
1060
1263
|
onNodesChange: flow.onNodesChange,
|
|
1061
1264
|
onEdgesChange: flow.onEdgesChange,
|
|
1062
1265
|
onConnect: flow.onConnect,
|
|
1266
|
+
onReconnect,
|
|
1267
|
+
edgesReconnectable: true,
|
|
1268
|
+
onNodeDragStart: hist.onNodeDragStart,
|
|
1063
1269
|
onNodeClick: handleNodeClick,
|
|
1064
1270
|
onNodeContextMenu: builtins.contextMenu === false ? void 0 : handleNodeContextMenu,
|
|
1065
1271
|
onEdgeClick: handleEdgeClick,
|
|
1066
1272
|
onEdgeContextMenu: builtins.edgeContextMenu === false ? void 0 : handleEdgeContextMenu,
|
|
1067
1273
|
onEdgesDelete: (deleted) => onEdgeDelete?.(deleted.map((e) => e.id)),
|
|
1068
1274
|
onNodesDelete: (deleted) => onDelete?.(deleted.map((n) => n.id)),
|
|
1275
|
+
onBeforeDelete: confirmDelete ? async ({ nodes, edges }) => confirmDelete({ nodes, edges }) : void 0,
|
|
1069
1276
|
deleteKeyCode: ["Delete", "Backspace"],
|
|
1070
1277
|
height: "100%",
|
|
1071
1278
|
toolbar,
|
|
@@ -1236,6 +1443,8 @@ function makeControlledFlowAdapter(value, onChange) {
|
|
|
1236
1443
|
const nextEdges = typeof next === "function" ? next(value.edges) : next;
|
|
1237
1444
|
apply({ nodes: value.nodes, edges: nextEdges });
|
|
1238
1445
|
},
|
|
1446
|
+
// Atomic both-at-once commit — the reason this exists (see UseFlowStateReturn).
|
|
1447
|
+
setGraph: (graph) => apply(graph),
|
|
1239
1448
|
onNodesChange: (changes) => {
|
|
1240
1449
|
apply({ nodes: applyNodeChanges(changes, value.nodes), edges: value.edges });
|
|
1241
1450
|
},
|
|
@@ -1322,6 +1531,6 @@ function NodePort({ side, type, id, style, title, className }) {
|
|
|
1322
1531
|
// src/index.ts
|
|
1323
1532
|
registerBuiltinKinds();
|
|
1324
1533
|
|
|
1325
|
-
export { ActionNode, ConfigFieldRenderer, DecisionNode, FlowCanvas, FlowEditor, FlowRunControls, FlowRunFeed, NodeConfigPanel, NodePalette, NodePort, NodeShell, NoteNode, OutputNode, SubgraphNode, TriggerNode, defaultNodeTypes, defineNode, paletteDropHandlers, useFlowEditor, useFlowEditorOptional };
|
|
1534
|
+
export { ActionNode, ConfigFieldRenderer, DecisionNode, FlowCanvas, FlowEditor, FlowRunControls, FlowRunFeed, NodeConfigPanel, NodePalette, NodePort, NodeShell, NoteNode, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, defineNode, distributeNodes, paletteDropHandlers, reconnectEdge2 as reconnectEdge, useFlowEditor, useFlowEditorOptional };
|
|
1326
1535
|
//# sourceMappingURL=index.js.map
|
|
1327
1536
|
//# sourceMappingURL=index.js.map
|