@particle-academy/fancy-flow 0.22.0 → 0.24.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-ZB4HHQMR.js → chunk-OWENS2H5.js} +11 -3
- package/dist/chunk-OWENS2H5.js.map +1 -0
- package/dist/{chunk-YZTBRXBZ.js → chunk-QAA4CA22.js} +8 -4
- package/dist/chunk-QAA4CA22.js.map +1 -0
- package/dist/{chunk-O7F6SGV4.js → chunk-ZS6DVGB3.js} +17 -5
- package/dist/chunk-ZS6DVGB3.js.map +1 -0
- package/dist/engine.js +1 -1
- package/dist/index.cjs +2409 -525
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -5
- package/dist/index.d.ts +15 -5
- package/dist/index.js +182 -37
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.cts +30 -0
- package/dist/layout/index.d.ts +30 -0
- package/dist/layout.cjs +1693 -0
- package/dist/layout.cjs.map +1 -0
- package/dist/layout.js +1691 -0
- package/dist/layout.js.map +1 -0
- package/dist/registry/index.d.cts +2 -2
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry.cjs +4 -0
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -3
- package/dist/runtime/index.d.cts +13 -1
- package/dist/runtime/index.d.ts +13 -1
- package/dist/runtime.cjs +13 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +2 -2
- package/dist/styles.css +20 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-DneUoivl.d.cts → types-BekE5JTG.d.cts} +7 -0
- package/dist/{types-CFEtuRWJ.d.ts → types-DwRpXXNy.d.ts} +7 -0
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/package.json +12 -1
- package/dist/chunk-O7F6SGV4.js.map +0 -1
- package/dist/chunk-YZTBRXBZ.js.map +0 -1
- package/dist/chunk-ZB4HHQMR.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -7,10 +7,12 @@ import { a as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistr
|
|
|
7
7
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, b as FlowEdge, c as FlowNodeData, d as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-CMSrWVYM.cjs';
|
|
8
8
|
import { WorkflowSchema, WorkflowMetadata } from './schema/index.cjs';
|
|
9
9
|
export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './schema/index.cjs';
|
|
10
|
-
import {
|
|
11
|
-
export {
|
|
10
|
+
import { AutoLayoutOptions } from './layout/index.cjs';
|
|
11
|
+
export { AutoLayoutDirection } from './layout/index.cjs';
|
|
12
|
+
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-BekE5JTG.cjs';
|
|
13
|
+
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-BekE5JTG.cjs';
|
|
12
14
|
import { FlowRunFeedEntry } from './runtime/index.cjs';
|
|
13
|
-
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
|
|
15
|
+
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
|
|
14
16
|
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-AXzOR-Wq.cjs';
|
|
15
17
|
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
18
|
import './capabilities-exVeADXz.cjs';
|
|
@@ -34,6 +36,8 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
34
36
|
* disable. An `isValidConnection` you pass yourself always takes precedence.
|
|
35
37
|
*/
|
|
36
38
|
validateConnections?: boolean | ConnectionValidatorOptions;
|
|
39
|
+
/** Show alignment guide lines while dragging, and snap to them. Default off. */
|
|
40
|
+
showHelperLines?: boolean;
|
|
37
41
|
/** Optional toolbar / palette etc. rendered above the canvas. */
|
|
38
42
|
toolbar?: ReactNode;
|
|
39
43
|
className?: string;
|
|
@@ -48,7 +52,7 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
48
52
|
* (xyflow's standard pattern). The surrounding `useFlowState` hook in
|
|
49
53
|
* `runtime/use-flow-state.ts` is a convenience that wires those for you.
|
|
50
54
|
*/
|
|
51
|
-
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
55
|
+
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, colorMode, showHelperLines, onNodesChange, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
58
|
* Clone a set of nodes AND the edges internal to that set, remapping every id.
|
|
@@ -142,6 +146,10 @@ type FlowEditorApi = {
|
|
|
142
146
|
assignToLane: (nodeId: string, laneId: string) => void;
|
|
143
147
|
/** Remove a node from its lane (restores its absolute position). */
|
|
144
148
|
removeFromLane: (nodeId: string) => void;
|
|
149
|
+
/** Auto-arrange the graph (or a lane's children) into a tidy DAG layout. */
|
|
150
|
+
autoLayout: (options?: AutoLayoutOptions) => void;
|
|
151
|
+
/** Tidy just one lane's children. */
|
|
152
|
+
tidyLane: (laneId: string) => void;
|
|
145
153
|
/** Copy the current selection to the editor clipboard. */
|
|
146
154
|
copy: () => void;
|
|
147
155
|
/** Copy then delete the current selection. */
|
|
@@ -200,6 +208,8 @@ type FlowEditorBuiltins = {
|
|
|
200
208
|
history?: boolean;
|
|
201
209
|
/** "Add lane" toolbar button. Default true. */
|
|
202
210
|
addLane?: boolean;
|
|
211
|
+
/** "Tidy" (auto-layout) toolbar button. Default true. */
|
|
212
|
+
autoLayout?: boolean;
|
|
203
213
|
/** Right-click a node for Delete / Duplicate. Default true. */
|
|
204
214
|
contextMenu?: boolean;
|
|
205
215
|
/** Right-click a connection for Label / Delete. Default true. */
|
|
@@ -539,4 +549,4 @@ type FlowRunFeedProps = {
|
|
|
539
549
|
/** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
|
|
540
550
|
declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
|
|
541
551
|
|
|
542
|
-
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, LaneNode, 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 };
|
|
552
|
+
export { ActionNode, type AlignEdge, AutoLayoutOptions, 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, LaneNode, 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
|
@@ -7,10 +7,12 @@ import { a as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistr
|
|
|
7
7
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, b as FlowEdge, c as FlowNodeData, d as FlowNodeKind, N as NodeExecutor, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, R as RunEvent, S as SubgraphNodeData, T as TriggerNodeData } from './types-CMSrWVYM.js';
|
|
8
8
|
import { WorkflowSchema, WorkflowMetadata } from './schema/index.js';
|
|
9
9
|
export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './schema/index.js';
|
|
10
|
-
import {
|
|
11
|
-
export {
|
|
10
|
+
import { AutoLayoutOptions } from './layout/index.js';
|
|
11
|
+
export { AutoLayoutDirection } from './layout/index.js';
|
|
12
|
+
import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-DwRpXXNy.js';
|
|
13
|
+
export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-DwRpXXNy.js';
|
|
12
14
|
import { FlowRunFeedEntry } from './runtime/index.js';
|
|
13
|
-
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
|
|
15
|
+
export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
|
|
14
16
|
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-9U-gaYVF.js';
|
|
15
17
|
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
18
|
import './capabilities-85Mjn4uW.js';
|
|
@@ -34,6 +36,8 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
34
36
|
* disable. An `isValidConnection` you pass yourself always takes precedence.
|
|
35
37
|
*/
|
|
36
38
|
validateConnections?: boolean | ConnectionValidatorOptions;
|
|
39
|
+
/** Show alignment guide lines while dragging, and snap to them. Default off. */
|
|
40
|
+
showHelperLines?: boolean;
|
|
37
41
|
/** Optional toolbar / palette etc. rendered above the canvas. */
|
|
38
42
|
toolbar?: ReactNode;
|
|
39
43
|
className?: string;
|
|
@@ -48,7 +52,7 @@ type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" |
|
|
|
48
52
|
* (xyflow's standard pattern). The surrounding `useFlowState` hook in
|
|
49
53
|
* `runtime/use-flow-state.ts` is a convenience that wires those for you.
|
|
50
54
|
*/
|
|
51
|
-
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
55
|
+
declare function FlowCanvas({ nodes, edges, background, showControls, showMinimap, height, validateConnections, isValidConnection, colorMode, showHelperLines, onNodesChange, toolbar, nodeTypes, edgeTypes, className, style, ...rest }: FlowCanvasProps): react.JSX.Element;
|
|
52
56
|
|
|
53
57
|
/**
|
|
54
58
|
* Clone a set of nodes AND the edges internal to that set, remapping every id.
|
|
@@ -142,6 +146,10 @@ type FlowEditorApi = {
|
|
|
142
146
|
assignToLane: (nodeId: string, laneId: string) => void;
|
|
143
147
|
/** Remove a node from its lane (restores its absolute position). */
|
|
144
148
|
removeFromLane: (nodeId: string) => void;
|
|
149
|
+
/** Auto-arrange the graph (or a lane's children) into a tidy DAG layout. */
|
|
150
|
+
autoLayout: (options?: AutoLayoutOptions) => void;
|
|
151
|
+
/** Tidy just one lane's children. */
|
|
152
|
+
tidyLane: (laneId: string) => void;
|
|
145
153
|
/** Copy the current selection to the editor clipboard. */
|
|
146
154
|
copy: () => void;
|
|
147
155
|
/** Copy then delete the current selection. */
|
|
@@ -200,6 +208,8 @@ type FlowEditorBuiltins = {
|
|
|
200
208
|
history?: boolean;
|
|
201
209
|
/** "Add lane" toolbar button. Default true. */
|
|
202
210
|
addLane?: boolean;
|
|
211
|
+
/** "Tidy" (auto-layout) toolbar button. Default true. */
|
|
212
|
+
autoLayout?: boolean;
|
|
203
213
|
/** Right-click a node for Delete / Duplicate. Default true. */
|
|
204
214
|
contextMenu?: boolean;
|
|
205
215
|
/** Right-click a connection for Label / Delete. Default true. */
|
|
@@ -539,4 +549,4 @@ type FlowRunFeedProps = {
|
|
|
539
549
|
/** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
|
|
540
550
|
declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
|
|
541
551
|
|
|
542
|
-
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, LaneNode, 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 };
|
|
552
|
+
export { ActionNode, type AlignEdge, AutoLayoutOptions, 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, LaneNode, 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,9 +1,10 @@
|
|
|
1
|
-
import { useFlowState, useFlowRun, useFlowHistory, applyStatusesToNodes } from './chunk-
|
|
2
|
-
export { applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-
|
|
3
|
-
import { buildNodeTypes, registerBuiltinKinds, createConnectionValidator } from './chunk-
|
|
4
|
-
export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds } from './chunk-
|
|
5
|
-
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap } from './chunk-
|
|
1
|
+
import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-ZS6DVGB3.js';
|
|
2
|
+
export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-ZS6DVGB3.js';
|
|
3
|
+
import { buildNodeTypes, registerBuiltinKinds, createConnectionValidator } from './chunk-QAA4CA22.js';
|
|
4
|
+
export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds } from './chunk-QAA4CA22.js';
|
|
5
|
+
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap, ViewportPortal } from './chunk-OWENS2H5.js';
|
|
6
6
|
export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
|
|
7
|
+
import './chunk-USL4FMFU.js';
|
|
7
8
|
export { runFlow } from './chunk-KDHLKBL2.js';
|
|
8
9
|
import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-OHVSEHDC.js';
|
|
9
10
|
export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './chunk-OHVSEHDC.js';
|
|
@@ -12,7 +13,6 @@ import { onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, valid
|
|
|
12
13
|
export { categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerNodeKind, validateConfig } from './chunk-YXRLIV7A.js';
|
|
13
14
|
import { getRichInputAdapter } from './chunk-F5RPRB7A.js';
|
|
14
15
|
export { RichInputPreview, getRichInputAdapter, isRichInputEnabled, onRichInputAdapterChanged, registerRichInputAdapter } from './chunk-F5RPRB7A.js';
|
|
15
|
-
import './chunk-USL4FMFU.js';
|
|
16
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
|
|
|
@@ -273,6 +273,97 @@ function distributeNodes(nodes, axis) {
|
|
|
273
273
|
return { ...n, position: axis === "h" ? { ...n.position, x: p } : { ...n.position, y: p } };
|
|
274
274
|
});
|
|
275
275
|
}
|
|
276
|
+
|
|
277
|
+
// src/components/canvas/helper-lines.ts
|
|
278
|
+
var w = (n) => n.width ?? n.measured?.width ?? 0;
|
|
279
|
+
var h = (n) => n.height ?? n.measured?.height ?? 0;
|
|
280
|
+
function getHelperLines(change, nodes, distance = 6) {
|
|
281
|
+
const result = { snapPosition: { x: void 0, y: void 0 } };
|
|
282
|
+
const a = nodes.find((n) => n.id === change.id);
|
|
283
|
+
if (!a || !change.position) return result;
|
|
284
|
+
const A = {
|
|
285
|
+
left: change.position.x,
|
|
286
|
+
right: change.position.x + w(a),
|
|
287
|
+
top: change.position.y,
|
|
288
|
+
bottom: change.position.y + h(a),
|
|
289
|
+
width: w(a),
|
|
290
|
+
height: h(a)
|
|
291
|
+
};
|
|
292
|
+
let vDist = distance;
|
|
293
|
+
let hDist = distance;
|
|
294
|
+
for (const b of nodes) {
|
|
295
|
+
if (b.id === a.id) continue;
|
|
296
|
+
const B = { left: b.position.x, right: b.position.x + w(b), top: b.position.y, bottom: b.position.y + h(b) };
|
|
297
|
+
const ll = Math.abs(A.left - B.left);
|
|
298
|
+
if (ll < vDist) {
|
|
299
|
+
result.snapPosition.x = B.left;
|
|
300
|
+
result.vertical = B.left;
|
|
301
|
+
vDist = ll;
|
|
302
|
+
}
|
|
303
|
+
const rr = Math.abs(A.right - B.right);
|
|
304
|
+
if (rr < vDist) {
|
|
305
|
+
result.snapPosition.x = B.right - A.width;
|
|
306
|
+
result.vertical = B.right;
|
|
307
|
+
vDist = rr;
|
|
308
|
+
}
|
|
309
|
+
const lr = Math.abs(A.left - B.right);
|
|
310
|
+
if (lr < vDist) {
|
|
311
|
+
result.snapPosition.x = B.right;
|
|
312
|
+
result.vertical = B.right;
|
|
313
|
+
vDist = lr;
|
|
314
|
+
}
|
|
315
|
+
const rl = Math.abs(A.right - B.left);
|
|
316
|
+
if (rl < vDist) {
|
|
317
|
+
result.snapPosition.x = B.left - A.width;
|
|
318
|
+
result.vertical = B.left;
|
|
319
|
+
vDist = rl;
|
|
320
|
+
}
|
|
321
|
+
const tt = Math.abs(A.top - B.top);
|
|
322
|
+
if (tt < hDist) {
|
|
323
|
+
result.snapPosition.y = B.top;
|
|
324
|
+
result.horizontal = B.top;
|
|
325
|
+
hDist = tt;
|
|
326
|
+
}
|
|
327
|
+
const bb = Math.abs(A.bottom - B.bottom);
|
|
328
|
+
if (bb < hDist) {
|
|
329
|
+
result.snapPosition.y = B.bottom - A.height;
|
|
330
|
+
result.horizontal = B.bottom;
|
|
331
|
+
hDist = bb;
|
|
332
|
+
}
|
|
333
|
+
const tb = Math.abs(A.top - B.bottom);
|
|
334
|
+
if (tb < hDist) {
|
|
335
|
+
result.snapPosition.y = B.bottom;
|
|
336
|
+
result.horizontal = B.bottom;
|
|
337
|
+
hDist = tb;
|
|
338
|
+
}
|
|
339
|
+
const bt = Math.abs(A.bottom - B.top);
|
|
340
|
+
if (bt < hDist) {
|
|
341
|
+
result.snapPosition.y = B.top - A.height;
|
|
342
|
+
result.horizontal = B.top;
|
|
343
|
+
hDist = bt;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
function HelperLines({ horizontal, vertical }) {
|
|
349
|
+
if (horizontal === void 0 && vertical === void 0) return null;
|
|
350
|
+
return /* @__PURE__ */ jsxs(ViewportPortal, { children: [
|
|
351
|
+
vertical !== void 0 && /* @__PURE__ */ jsx(
|
|
352
|
+
"div",
|
|
353
|
+
{
|
|
354
|
+
className: "ff-helper-line",
|
|
355
|
+
style: { position: "absolute", transform: `translateX(${vertical}px)`, top: -5e3, height: 1e4, width: 1, pointerEvents: "none" }
|
|
356
|
+
}
|
|
357
|
+
),
|
|
358
|
+
horizontal !== void 0 && /* @__PURE__ */ jsx(
|
|
359
|
+
"div",
|
|
360
|
+
{
|
|
361
|
+
className: "ff-helper-line",
|
|
362
|
+
style: { position: "absolute", transform: `translateY(${horizontal}px)`, left: -5e3, width: 1e4, height: 1, pointerEvents: "none" }
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
] });
|
|
366
|
+
}
|
|
276
367
|
var DEFAULT_FIT_VIEW = { padding: 0.2 };
|
|
277
368
|
var DEFAULT_EDGE_OPTIONS = {
|
|
278
369
|
type: "smoothstep",
|
|
@@ -287,6 +378,9 @@ function FlowCanvas({
|
|
|
287
378
|
height = 600,
|
|
288
379
|
validateConnections = true,
|
|
289
380
|
isValidConnection,
|
|
381
|
+
colorMode,
|
|
382
|
+
showHelperLines = false,
|
|
383
|
+
onNodesChange,
|
|
290
384
|
toolbar,
|
|
291
385
|
nodeTypes,
|
|
292
386
|
edgeTypes,
|
|
@@ -313,31 +407,66 @@ function FlowCanvas({
|
|
|
313
407
|
[edgeTypes]
|
|
314
408
|
);
|
|
315
409
|
const orderedNodes = useMemo(() => sortNodesParentFirst(nodes), [nodes]);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
zoomActivationKeyCode: "Shift",
|
|
330
|
-
preventScrolling: false,
|
|
331
|
-
isValidConnection: resolvedIsValidConnection,
|
|
332
|
-
...rest,
|
|
333
|
-
children: [
|
|
334
|
-
background !== "none" && /* @__PURE__ */ jsx(Background, { variant: background, gap: 20, size: 1, color: "rgba(0,0,0,0.18)" }),
|
|
335
|
-
showControls && /* @__PURE__ */ jsx(Controls, { className: "ff-controls", position: "bottom-right" }),
|
|
336
|
-
showMinimap && /* @__PURE__ */ jsx(MiniMap, { className: "ff-minimap", pannable: true, zoomable: true })
|
|
337
|
-
]
|
|
410
|
+
const [helperLines, setHelperLines] = useState({});
|
|
411
|
+
const handleNodesChange = useCallback(
|
|
412
|
+
(changes) => {
|
|
413
|
+
if (showHelperLines) {
|
|
414
|
+
const pos = changes.filter((c) => c.type === "position" && c.position);
|
|
415
|
+
if (pos.length === 1 && pos[0].dragging) {
|
|
416
|
+
const lines = getHelperLines(pos[0], nodesRef.current);
|
|
417
|
+
if (lines.snapPosition.x !== void 0) pos[0].position.x = lines.snapPosition.x;
|
|
418
|
+
if (lines.snapPosition.y !== void 0) pos[0].position.y = lines.snapPosition.y;
|
|
419
|
+
setHelperLines({ horizontal: lines.horizontal, vertical: lines.vertical });
|
|
420
|
+
} else {
|
|
421
|
+
setHelperLines({});
|
|
422
|
+
}
|
|
338
423
|
}
|
|
339
|
-
|
|
340
|
-
|
|
424
|
+
onNodesChange?.(changes);
|
|
425
|
+
},
|
|
426
|
+
[showHelperLines, onNodesChange]
|
|
427
|
+
);
|
|
428
|
+
return /* @__PURE__ */ jsxs(
|
|
429
|
+
"div",
|
|
430
|
+
{
|
|
431
|
+
className: [
|
|
432
|
+
"ff-canvas",
|
|
433
|
+
// colorMode drives BOTH react-flow's chrome (below) and our `ff-` styles
|
|
434
|
+
// via the shared `.dark` class / light opt-out — one theme signal.
|
|
435
|
+
colorMode === "dark" ? "dark" : "",
|
|
436
|
+
colorMode === "light" ? "ff-canvas--light" : "",
|
|
437
|
+
className ?? ""
|
|
438
|
+
].filter(Boolean).join(" "),
|
|
439
|
+
style: { height, ...style },
|
|
440
|
+
children: [
|
|
441
|
+
toolbar && /* @__PURE__ */ jsx("div", { className: "ff-canvas__toolbar", children: toolbar }),
|
|
442
|
+
/* @__PURE__ */ jsx("div", { className: "ff-canvas__surface", children: /* @__PURE__ */ jsxs(
|
|
443
|
+
index,
|
|
444
|
+
{
|
|
445
|
+
nodes: orderedNodes,
|
|
446
|
+
edges,
|
|
447
|
+
onNodesChange: handleNodesChange,
|
|
448
|
+
colorMode,
|
|
449
|
+
nodeTypes: mergedNodeTypes,
|
|
450
|
+
edgeTypes: mergedEdgeTypes,
|
|
451
|
+
fitView: true,
|
|
452
|
+
fitViewOptions: DEFAULT_FIT_VIEW,
|
|
453
|
+
defaultEdgeOptions: DEFAULT_EDGE_OPTIONS,
|
|
454
|
+
proOptions: { hideAttribution: true },
|
|
455
|
+
zoomActivationKeyCode: "Shift",
|
|
456
|
+
preventScrolling: false,
|
|
457
|
+
isValidConnection: resolvedIsValidConnection,
|
|
458
|
+
...rest,
|
|
459
|
+
children: [
|
|
460
|
+
background !== "none" && /* @__PURE__ */ jsx(Background, { variant: background, gap: 20, size: 1, color: "rgba(0,0,0,0.18)" }),
|
|
461
|
+
showControls && /* @__PURE__ */ jsx(Controls, { className: "ff-controls", position: "bottom-right" }),
|
|
462
|
+
showMinimap && /* @__PURE__ */ jsx(MiniMap, { className: "ff-minimap", pannable: true, zoomable: true }),
|
|
463
|
+
showHelperLines && /* @__PURE__ */ jsx(HelperLines, { horizontal: helperLines.horizontal, vertical: helperLines.vertical })
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
) })
|
|
467
|
+
]
|
|
468
|
+
}
|
|
469
|
+
);
|
|
341
470
|
}
|
|
342
471
|
var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "custom"];
|
|
343
472
|
var CATEGORY_LABELS = {
|
|
@@ -993,8 +1122,11 @@ function FlowEditorInner({
|
|
|
993
1122
|
useEffect(() => onNodeKindsChanged(() => force((n) => n + 1)), []);
|
|
994
1123
|
const nodeTypes = useMemo(() => buildNodeTypes(), [listNodeKinds().length]);
|
|
995
1124
|
const renderedNodes = useMemo(
|
|
996
|
-
() =>
|
|
997
|
-
|
|
1125
|
+
() => applyOutputsToNodes(
|
|
1126
|
+
applyStatusesToNodes(flow.nodes, runner.statuses, runner.statusText),
|
|
1127
|
+
runner.outputs
|
|
1128
|
+
),
|
|
1129
|
+
[flow.nodes, runner.statuses, runner.statusText, runner.outputs]
|
|
998
1130
|
);
|
|
999
1131
|
const [selectedId, setSelectedId] = useState(null);
|
|
1000
1132
|
const selected = useMemo(() => flow.nodes.find((n) => n.id === selectedId) ?? null, [flow.nodes, selectedId]);
|
|
@@ -1178,10 +1310,10 @@ function FlowEditorInner({
|
|
|
1178
1310
|
(orientation = "horizontal", title) => {
|
|
1179
1311
|
const vertical = orientation === "vertical";
|
|
1180
1312
|
const lanes = flow.nodes.filter(isLaneNode);
|
|
1181
|
-
const
|
|
1182
|
-
const
|
|
1313
|
+
const w2 = vertical ? 280 : 680;
|
|
1314
|
+
const h2 = vertical ? 480 : 168;
|
|
1183
1315
|
const end = lanes.reduce(
|
|
1184
|
-
(m, l) => Math.max(m, vertical ? l.position.x + (l.width ??
|
|
1316
|
+
(m, l) => Math.max(m, vertical ? l.position.x + (l.width ?? w2) : l.position.y + (l.height ?? h2)),
|
|
1185
1317
|
0
|
|
1186
1318
|
);
|
|
1187
1319
|
const id = newNodeId();
|
|
@@ -1190,8 +1322,8 @@ function FlowEditorInner({
|
|
|
1190
1322
|
id,
|
|
1191
1323
|
type: "@particle-academy/lane",
|
|
1192
1324
|
position: vertical ? { x: lanes.length ? end + 12 : 0, y: 0 } : { x: 0, y: lanes.length ? end + 12 : 0 },
|
|
1193
|
-
width:
|
|
1194
|
-
height:
|
|
1325
|
+
width: w2,
|
|
1326
|
+
height: h2,
|
|
1195
1327
|
data: { kind: "@particle-academy/lane", label: name, config: { title: name, orientation } }
|
|
1196
1328
|
};
|
|
1197
1329
|
flow.setNodes((all) => [...all, node]);
|
|
@@ -1279,6 +1411,18 @@ function FlowEditorInner({
|
|
|
1279
1411
|
addLane,
|
|
1280
1412
|
assignToLane: (nodeId, laneId) => flow.setNodes((all) => assignToLane(all, nodeId, laneId)),
|
|
1281
1413
|
removeFromLane: (nodeId) => flow.setNodes((all) => removeFromLane(all, nodeId)),
|
|
1414
|
+
// dagre is loaded lazily so it stays out of the eager bundle — consumers
|
|
1415
|
+
// who never tidy pay nothing for it.
|
|
1416
|
+
autoLayout: (opts) => {
|
|
1417
|
+
void import('./layout.js').then(
|
|
1418
|
+
({ autoLayout }) => flow.setGraph({ nodes: autoLayout({ nodes: flow.nodes, edges: flow.edges }, opts), edges: flow.edges })
|
|
1419
|
+
);
|
|
1420
|
+
},
|
|
1421
|
+
tidyLane: (laneId) => {
|
|
1422
|
+
void import('./layout.js').then(
|
|
1423
|
+
({ autoLayout }) => flow.setGraph({ nodes: autoLayout({ nodes: flow.nodes, edges: flow.edges }, { scope: laneId }), edges: flow.edges })
|
|
1424
|
+
);
|
|
1425
|
+
},
|
|
1282
1426
|
run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, executors),
|
|
1283
1427
|
cancel: runner.cancel,
|
|
1284
1428
|
reset: runner.reset,
|
|
@@ -1331,6 +1475,7 @@ function FlowEditorInner({
|
|
|
1331
1475
|
/* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
|
|
1332
1476
|
/* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "add-lane", title: "Add a swimlane", onClick: () => api.addLane(), children: "\u25A4 Lane" })
|
|
1333
1477
|
] }),
|
|
1478
|
+
builtins.autoLayout !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "auto-layout", title: "Tidy \u2014 auto-arrange", onClick: () => api.autoLayout(), children: "\u2922 Tidy" }),
|
|
1334
1479
|
(builtins.export !== false || builtins.import !== false) && /* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
|
|
1335
1480
|
builtins.export !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "export", onClick: api.exportWorkflow, children: "\u2193 Export" }),
|
|
1336
1481
|
builtins.import !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "import", onClick: api.importWorkflow, children: "\u2191 Import" }),
|