@particle-academy/fancy-flow 0.20.0 → 0.22.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.
Files changed (60) hide show
  1. package/dist/{capabilities-DUhAc-EJ.d.ts → capabilities-85Mjn4uW.d.ts} +1 -1
  2. package/dist/{capabilities-ChE3Xi8R.d.cts → capabilities-exVeADXz.d.cts} +1 -1
  3. package/dist/{chunk-L4AX73Q6.js → chunk-KDHLKBL2.js} +11 -5
  4. package/dist/chunk-KDHLKBL2.js.map +1 -0
  5. package/dist/{chunk-KI4RI2N3.js → chunk-O7F6SGV4.js} +4 -4
  6. package/dist/{chunk-KI4RI2N3.js.map → chunk-O7F6SGV4.js.map} +1 -1
  7. package/dist/{chunk-D6W5FMCT.js → chunk-OHVSEHDC.js} +23 -5
  8. package/dist/chunk-OHVSEHDC.js.map +1 -0
  9. package/dist/{chunk-U5F22BHV.js → chunk-YXRLIV7A.js} +4 -2
  10. package/dist/chunk-YXRLIV7A.js.map +1 -0
  11. package/dist/{chunk-AW2QUEL2.js → chunk-YZTBRXBZ.js} +66 -8
  12. package/dist/chunk-YZTBRXBZ.js.map +1 -0
  13. package/dist/{chunk-XETAGLH6.js → chunk-ZB4HHQMR.js} +130 -32
  14. package/dist/chunk-ZB4HHQMR.js.map +1 -0
  15. package/dist/engine.cjs +8 -2
  16. package/dist/engine.cjs.map +1 -1
  17. package/dist/engine.d.cts +3 -3
  18. package/dist/engine.d.ts +3 -3
  19. package/dist/engine.js +3 -3
  20. package/dist/index.cjs +3553 -3278
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +25 -8
  23. package/dist/index.d.ts +25 -8
  24. package/dist/index.js +200 -107
  25. package/dist/index.js.map +1 -1
  26. package/dist/llm/vercel-ai.d.cts +2 -2
  27. package/dist/llm/vercel-ai.d.ts +2 -2
  28. package/dist/registry/index.d.cts +5 -5
  29. package/dist/registry/index.d.ts +5 -5
  30. package/dist/registry.cjs +197 -33
  31. package/dist/registry.cjs.map +1 -1
  32. package/dist/registry.js +4 -4
  33. package/dist/{run-flow-ChBi6q-W.d.ts → run-flow-9U-gaYVF.d.ts} +1 -1
  34. package/dist/{run-flow-VKm57Hcj.d.cts → run-flow-AXzOR-Wq.d.cts} +1 -1
  35. package/dist/runtime/index.d.cts +3 -3
  36. package/dist/runtime/index.d.ts +3 -3
  37. package/dist/runtime.cjs +8 -2
  38. package/dist/runtime.cjs.map +1 -1
  39. package/dist/runtime.js +4 -4
  40. package/dist/schema/index.d.cts +21 -2
  41. package/dist/schema/index.d.ts +21 -2
  42. package/dist/schema.cjs +20 -1
  43. package/dist/schema.cjs.map +1 -1
  44. package/dist/schema.js +2 -2
  45. package/dist/styles.css +61 -0
  46. package/dist/styles.css.map +1 -1
  47. package/dist/{types-NerkPtHS.d.ts → types-CFEtuRWJ.d.ts} +33 -3
  48. package/dist/{types-sOmpCitB.d.cts → types-CMSrWVYM.d.cts} +1 -1
  49. package/dist/{types-sOmpCitB.d.ts → types-CMSrWVYM.d.ts} +1 -1
  50. package/dist/{types-D_jOR3Z2.d.cts → types-DneUoivl.d.cts} +33 -3
  51. package/dist/ux.cjs.map +1 -1
  52. package/dist/ux.d.cts +3 -3
  53. package/dist/ux.d.ts +3 -3
  54. package/dist/ux.js +1 -1
  55. package/package.json +23 -3
  56. package/dist/chunk-AW2QUEL2.js.map +0 -1
  57. package/dist/chunk-D6W5FMCT.js.map +0 -1
  58. package/dist/chunk-L4AX73Q6.js.map +0 -1
  59. package/dist/chunk-U5F22BHV.js.map +0 -1
  60. package/dist/chunk-XETAGLH6.js.map +0 -1
package/dist/index.d.cts CHANGED
@@ -3,17 +3,17 @@ import { ReactNode, CSSProperties, ComponentType } from 'react';
3
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
- import { b as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-sOmpCitB.cjs';
7
- export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, a 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-sOmpCitB.cjs';
6
+ import { a as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-CMSrWVYM.cjs';
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
- export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, workflowToBlob } from './schema/index.cjs';
10
- import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-D_jOR3Z2.cjs';
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';
9
+ export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './schema/index.cjs';
10
+ import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-DneUoivl.cjs';
11
+ export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-DneUoivl.cjs';
12
12
  import { FlowRunFeedEntry } from './runtime/index.cjs';
13
13
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.cjs';
14
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-VKm57Hcj.cjs';
14
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-AXzOR-Wq.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
- import './capabilities-ChE3Xi8R.cjs';
16
+ import './capabilities-exVeADXz.cjs';
17
17
 
18
18
  type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" | "height"> & {
19
19
  nodes: FlowNode[];
@@ -136,6 +136,12 @@ type FlowEditorApi = {
136
136
  alignSelected: (edge: AlignEdge) => void;
137
137
  /** Evenly distribute the multi-selection's gaps along an axis (needs 3+). */
138
138
  distributeSelected: (axis: "h" | "v") => void;
139
+ /** Add a swimlane, stacked with existing lanes. Returns the new lane id. */
140
+ addLane: (orientation?: "horizontal" | "vertical", title?: string) => string | null;
141
+ /** Put a node inside a lane (sets parentId + a lane-relative position). */
142
+ assignToLane: (nodeId: string, laneId: string) => void;
143
+ /** Remove a node from its lane (restores its absolute position). */
144
+ removeFromLane: (nodeId: string) => void;
139
145
  /** Copy the current selection to the editor clipboard. */
140
146
  copy: () => void;
141
147
  /** Copy then delete the current selection. */
@@ -192,6 +198,8 @@ type FlowEditorBuiltins = {
192
198
  delete?: boolean;
193
199
  /** Undo/redo toolbar buttons. Default true. */
194
200
  history?: boolean;
201
+ /** "Add lane" toolbar button. Default true. */
202
+ addLane?: boolean;
195
203
  /** Right-click a node for Delete / Duplicate. Default true. */
196
204
  contextMenu?: boolean;
197
205
  /** Right-click a connection for Label / Delete. Default true. */
@@ -426,6 +434,15 @@ declare const NoteNode: react.MemoExoticComponent<typeof NoteNodeInner>;
426
434
  declare function SubgraphNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
427
435
  declare const SubgraphNode: react.MemoExoticComponent<typeof SubgraphNodeInner>;
428
436
 
437
+ /**
438
+ * LaneNode — a resizable swimlane band. A portless container: child nodes are
439
+ * parented into it (`parentId` + `extent:'parent'`) and render on top, so this
440
+ * node is just the titled background. Wired as the `@particle-academy/lane`
441
+ * kind's `component`, so `buildNodeTypes` uses it instead of the default card.
442
+ */
443
+ declare function LaneNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
444
+ declare const LaneNode: react.MemoExoticComponent<typeof LaneNodeInner>;
445
+
429
446
  type NodeShellProps = {
430
447
  /** Required: the xyflow node this is rendering. */
431
448
  node: NodeProps<FlowNode>;
@@ -522,4 +539,4 @@ type FlowRunFeedProps = {
522
539
  /** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
523
540
  declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
524
541
 
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 };
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 };
package/dist/index.d.ts CHANGED
@@ -3,17 +3,17 @@ import { ReactNode, CSSProperties, ComponentType } from 'react';
3
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
- import { b as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-sOmpCitB.js';
7
- export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, a 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-sOmpCitB.js';
6
+ import { a as FlowNode, F as FlowGraph, e as NodeRunStatus, E as ExecutorRegistry } from './types-CMSrWVYM.js';
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
- export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, workflowToBlob } from './schema/index.js';
10
- import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-NerkPtHS.js';
11
- export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-NerkPtHS.js';
9
+ export { ImportIssue, ImportOptions, ImportResult, WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, WorkflowSchemaEdge, WorkflowSchemaNode, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './schema/index.js';
10
+ import { N as NodeCategory, a as NodeKindDefinition, C as ConfigField } from './types-CFEtuRWJ.js';
11
+ export { D as DocumentConfigField, K as KeyValueConfigField, P as PortSpec, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField } from './types-CFEtuRWJ.js';
12
12
  import { FlowRunFeedEntry } from './runtime/index.js';
13
13
  export { HistoryController, UseFlowHistoryReturn, UseFlowRunOptions, UseFlowRunReturn, UseFlowStateReturn, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './runtime/index.js';
14
- export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-ChBi6q-W.js';
14
+ export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-9U-gaYVF.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
- import './capabilities-DUhAc-EJ.js';
16
+ import './capabilities-85Mjn4uW.js';
17
17
 
18
18
  type FlowCanvasProps = Omit<ReactFlowProps<FlowNode, Edge>, "nodes" | "edges" | "height"> & {
19
19
  nodes: FlowNode[];
@@ -136,6 +136,12 @@ type FlowEditorApi = {
136
136
  alignSelected: (edge: AlignEdge) => void;
137
137
  /** Evenly distribute the multi-selection's gaps along an axis (needs 3+). */
138
138
  distributeSelected: (axis: "h" | "v") => void;
139
+ /** Add a swimlane, stacked with existing lanes. Returns the new lane id. */
140
+ addLane: (orientation?: "horizontal" | "vertical", title?: string) => string | null;
141
+ /** Put a node inside a lane (sets parentId + a lane-relative position). */
142
+ assignToLane: (nodeId: string, laneId: string) => void;
143
+ /** Remove a node from its lane (restores its absolute position). */
144
+ removeFromLane: (nodeId: string) => void;
139
145
  /** Copy the current selection to the editor clipboard. */
140
146
  copy: () => void;
141
147
  /** Copy then delete the current selection. */
@@ -192,6 +198,8 @@ type FlowEditorBuiltins = {
192
198
  delete?: boolean;
193
199
  /** Undo/redo toolbar buttons. Default true. */
194
200
  history?: boolean;
201
+ /** "Add lane" toolbar button. Default true. */
202
+ addLane?: boolean;
195
203
  /** Right-click a node for Delete / Duplicate. Default true. */
196
204
  contextMenu?: boolean;
197
205
  /** Right-click a connection for Label / Delete. Default true. */
@@ -426,6 +434,15 @@ declare const NoteNode: react.MemoExoticComponent<typeof NoteNodeInner>;
426
434
  declare function SubgraphNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
427
435
  declare const SubgraphNode: react.MemoExoticComponent<typeof SubgraphNodeInner>;
428
436
 
437
+ /**
438
+ * LaneNode — a resizable swimlane band. A portless container: child nodes are
439
+ * parented into it (`parentId` + `extent:'parent'`) and render on top, so this
440
+ * node is just the titled background. Wired as the `@particle-academy/lane`
441
+ * kind's `component`, so `buildNodeTypes` uses it instead of the default card.
442
+ */
443
+ declare function LaneNodeInner(props: NodeProps<FlowNode>): react.JSX.Element;
444
+ declare const LaneNode: react.MemoExoticComponent<typeof LaneNodeInner>;
445
+
429
446
  type NodeShellProps = {
430
447
  /** Required: the xyflow node this is rendering. */
431
448
  node: NodeProps<FlowNode>;
@@ -522,4 +539,4 @@ type FlowRunFeedProps = {
522
539
  /** FlowRunFeed — scrolling log panel. Auto-scrolls to bottom on new entries. */
523
540
  declare function FlowRunFeed({ entries, className, style }: FlowRunFeedProps): react.JSX.Element;
524
541
 
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 };
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 };
package/dist/index.js CHANGED
@@ -1,15 +1,15 @@
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';
1
+ import { useFlowState, useFlowRun, useFlowHistory, applyStatusesToNodes } from './chunk-O7F6SGV4.js';
2
+ export { applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-O7F6SGV4.js';
3
+ import { buildNodeTypes, registerBuiltinKinds, createConnectionValidator } from './chunk-YZTBRXBZ.js';
4
+ export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds } from './chunk-YZTBRXBZ.js';
5
+ import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle, reconnectEdge, index, BackgroundVariant, Background, Controls, MiniMap } from './chunk-ZB4HHQMR.js';
6
6
  export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
7
- export { runFlow } from './chunk-L4AX73Q6.js';
8
- import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-D6W5FMCT.js';
9
- export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, workflowToBlob } from './chunk-D6W5FMCT.js';
7
+ export { runFlow } from './chunk-KDHLKBL2.js';
8
+ import { workflowToBlob, importWorkflow, exportWorkflow } from './chunk-OHVSEHDC.js';
9
+ export { WORKFLOW_SCHEMA_URL, WORKFLOW_SCHEMA_VERSION, exportWorkflow, importWorkflow, migrateSchema, workflowToBlob } from './chunk-OHVSEHDC.js';
10
10
  export { resolveNodePorts, resolvePortSpec } from './chunk-TITD5W4Y.js';
11
- import { onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, validateConfig, categoryAccent } from './chunk-U5F22BHV.js';
12
- export { categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerNodeKind, validateConfig } from './chunk-U5F22BHV.js';
11
+ import { onNodeKindsChanged, listNodeKinds, getNodeKind, defaultConfigFor, validateConfig, categoryAccent } from './chunk-YXRLIV7A.js';
12
+ export { categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerNodeKind, validateConfig } from './chunk-YXRLIV7A.js';
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';
@@ -139,6 +139,140 @@ var defaultNodeTypes = {
139
139
  note: NoteNode,
140
140
  subgraph: SubgraphNode
141
141
  };
142
+
143
+ // src/components/FlowEditor/graph-ops.ts
144
+ function removeNodes(graph, ids) {
145
+ if (ids.length === 0) return graph;
146
+ const doomed = new Set(ids);
147
+ return {
148
+ nodes: graph.nodes.filter((n) => !doomed.has(n.id)),
149
+ edges: graph.edges.filter((e) => !doomed.has(e.source) && !doomed.has(e.target))
150
+ };
151
+ }
152
+ function removeEdges(edges, ids) {
153
+ if (ids.length === 0) return edges;
154
+ const doomed = new Set(ids);
155
+ return edges.filter((e) => !doomed.has(e.id));
156
+ }
157
+ function setEdgeLabel(edges, id, label) {
158
+ const next = label?.trim();
159
+ return edges.map((e) => {
160
+ if (e.id !== id) return e;
161
+ if (!next) {
162
+ const { label: _drop, ...rest } = e;
163
+ return rest;
164
+ }
165
+ return { ...e, label: next };
166
+ });
167
+ }
168
+ function duplicateNode(node, id, offset = 40) {
169
+ return {
170
+ ...node,
171
+ id,
172
+ position: { x: node.position.x + offset, y: node.position.y + offset },
173
+ // Deep copy so the clone's config edits don't mutate the original.
174
+ data: JSON.parse(JSON.stringify(node.data ?? {}))
175
+ };
176
+ }
177
+ function cloneSubgraph(nodes, edges, opts) {
178
+ const offset = opts.offset ?? 40;
179
+ const idMap = /* @__PURE__ */ new Map();
180
+ for (const n of nodes) idMap.set(n.id, opts.makeId());
181
+ const clonedNodes = nodes.map((n) => {
182
+ const cloned = duplicateNode(n, idMap.get(n.id), offset);
183
+ const parentId = n.parentId;
184
+ if (parentId && idMap.has(parentId)) cloned.parentId = idMap.get(parentId);
185
+ else if (parentId) delete cloned.parentId;
186
+ return cloned;
187
+ });
188
+ 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) }));
189
+ return { nodes: clonedNodes, edges: clonedEdges, idMap };
190
+ }
191
+ function reconnectEdge2(edges, oldEdge, newConnection) {
192
+ return reconnectEdge(oldEdge, newConnection, edges, { shouldReplaceId: false });
193
+ }
194
+ var nodeW = (n) => n.width ?? n.measured?.width ?? 0;
195
+ var nodeH = (n) => n.height ?? n.measured?.height ?? 0;
196
+ function alignNodes(nodes, edge) {
197
+ if (nodes.length < 2) return nodes;
198
+ const minL = Math.min(...nodes.map((n) => n.position.x));
199
+ const maxR = Math.max(...nodes.map((n) => n.position.x + nodeW(n)));
200
+ const minT = Math.min(...nodes.map((n) => n.position.y));
201
+ const maxB = Math.max(...nodes.map((n) => n.position.y + nodeH(n)));
202
+ const cx = (minL + maxR) / 2;
203
+ const cy = (minT + maxB) / 2;
204
+ return nodes.map((n) => {
205
+ let { x, y } = n.position;
206
+ if (edge === "left") x = minL;
207
+ else if (edge === "right") x = maxR - nodeW(n);
208
+ else if (edge === "hcenter") x = cx - nodeW(n) / 2;
209
+ else if (edge === "top") y = minT;
210
+ else if (edge === "bottom") y = maxB - nodeH(n);
211
+ else if (edge === "vcenter") y = cy - nodeH(n) / 2;
212
+ return { ...n, position: { x, y } };
213
+ });
214
+ }
215
+ function sortNodesParentFirst(nodes) {
216
+ const byId = new Map(nodes.map((n) => [n.id, n]));
217
+ const seen = /* @__PURE__ */ new Set();
218
+ const out = [];
219
+ const visit = (n) => {
220
+ if (seen.has(n.id)) return;
221
+ const pid = n.parentId;
222
+ if (pid && byId.has(pid) && !seen.has(pid)) visit(byId.get(pid));
223
+ seen.add(n.id);
224
+ out.push(n);
225
+ };
226
+ for (const n of nodes) visit(n);
227
+ return out;
228
+ }
229
+ function absolutePosition(node, nodes) {
230
+ const pid = node.parentId;
231
+ if (!pid) return node.position;
232
+ const parent = nodes.find((n) => n.id === pid);
233
+ return parent ? { x: node.position.x + parent.position.x, y: node.position.y + parent.position.y } : node.position;
234
+ }
235
+ function assignToLane(nodes, nodeId, laneId) {
236
+ if (nodeId === laneId) return nodes;
237
+ const node = nodes.find((n) => n.id === nodeId);
238
+ const lane = nodes.find((n) => n.id === laneId);
239
+ if (!node || !lane) return nodes;
240
+ const abs = absolutePosition(node, nodes);
241
+ const laneAbs = absolutePosition(lane, nodes);
242
+ const rel = { x: abs.x - laneAbs.x, y: abs.y - laneAbs.y };
243
+ return nodes.map((n) => n.id === nodeId ? { ...n, parentId: laneId, extent: "parent", position: rel } : n);
244
+ }
245
+ function removeFromLane(nodes, nodeId) {
246
+ const node = nodes.find((n) => n.id === nodeId);
247
+ if (!node || !node.parentId) return nodes;
248
+ const abs = absolutePosition(node, nodes);
249
+ return nodes.map((n) => {
250
+ if (n.id !== nodeId) return n;
251
+ const { parentId: _p, extent: _e, ...rest } = n;
252
+ return { ...rest, position: abs };
253
+ });
254
+ }
255
+ function distributeNodes(nodes, axis) {
256
+ if (nodes.length < 3) return nodes;
257
+ const size = (n) => axis === "h" ? nodeW(n) : nodeH(n);
258
+ const coord = (n) => axis === "h" ? n.position.x : n.position.y;
259
+ const sorted = [...nodes].sort((a, b) => coord(a) - coord(b));
260
+ const start = coord(sorted[0]);
261
+ const last = sorted[sorted.length - 1];
262
+ const end = coord(last) + size(last);
263
+ const totalSize = sorted.reduce((s, n) => s + size(n), 0);
264
+ const gap = (end - start - totalSize) / (sorted.length - 1);
265
+ const posById = /* @__PURE__ */ new Map();
266
+ let cursor = start;
267
+ for (const n of sorted) {
268
+ posById.set(n.id, cursor);
269
+ cursor += size(n) + gap;
270
+ }
271
+ return nodes.map((n) => {
272
+ const p = posById.get(n.id);
273
+ return { ...n, position: axis === "h" ? { ...n.position, x: p } : { ...n.position, y: p } };
274
+ });
275
+ }
142
276
  var DEFAULT_FIT_VIEW = { padding: 0.2 };
143
277
  var DEFAULT_EDGE_OPTIONS = {
144
278
  type: "smoothstep",
@@ -178,12 +312,13 @@ function FlowCanvas({
178
312
  () => edgeTypes ? { ...edgeTypes } : void 0,
179
313
  [edgeTypes]
180
314
  );
315
+ const orderedNodes = useMemo(() => sortNodesParentFirst(nodes), [nodes]);
181
316
  return /* @__PURE__ */ jsxs("div", { className: ["ff-canvas", className ?? ""].filter(Boolean).join(" "), style: { height, ...style }, children: [
182
317
  toolbar && /* @__PURE__ */ jsx("div", { className: "ff-canvas__toolbar", children: toolbar }),
183
318
  /* @__PURE__ */ jsx("div", { className: "ff-canvas__surface", children: /* @__PURE__ */ jsxs(
184
319
  index,
185
320
  {
186
- nodes,
321
+ nodes: orderedNodes,
187
322
  edges,
188
323
  nodeTypes: mergedNodeTypes,
189
324
  edgeTypes: mergedEdgeTypes,
@@ -204,7 +339,7 @@ function FlowCanvas({
204
339
  ) })
205
340
  ] });
206
341
  }
207
- var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "custom"];
342
+ var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "custom"];
208
343
  var CATEGORY_LABELS = {
209
344
  trigger: "Triggers",
210
345
  logic: "Logic",
@@ -213,6 +348,7 @@ var CATEGORY_LABELS = {
213
348
  io: "Connectors",
214
349
  human: "Human",
215
350
  output: "Output",
351
+ layout: "Layout",
216
352
  custom: "Custom"
217
353
  };
218
354
  function NodePalette({ categories, onPick, className, style }) {
@@ -804,100 +940,6 @@ function formatTime(at) {
804
940
  const d = new Date(at);
805
941
  return `${d.getMinutes().toString().padStart(2, "0")}:${d.getSeconds().toString().padStart(2, "0")}.${Math.floor(d.getMilliseconds() / 100)}`;
806
942
  }
807
-
808
- // src/components/FlowEditor/graph-ops.ts
809
- function removeNodes(graph, ids) {
810
- if (ids.length === 0) return graph;
811
- const doomed = new Set(ids);
812
- return {
813
- nodes: graph.nodes.filter((n) => !doomed.has(n.id)),
814
- edges: graph.edges.filter((e) => !doomed.has(e.source) && !doomed.has(e.target))
815
- };
816
- }
817
- function removeEdges(edges, ids) {
818
- if (ids.length === 0) return edges;
819
- const doomed = new Set(ids);
820
- return edges.filter((e) => !doomed.has(e.id));
821
- }
822
- function setEdgeLabel(edges, id, label) {
823
- const next = label?.trim();
824
- return edges.map((e) => {
825
- if (e.id !== id) return e;
826
- if (!next) {
827
- const { label: _drop, ...rest } = e;
828
- return rest;
829
- }
830
- return { ...e, label: next };
831
- });
832
- }
833
- function duplicateNode(node, id, offset = 40) {
834
- return {
835
- ...node,
836
- id,
837
- position: { x: node.position.x + offset, y: node.position.y + offset },
838
- // Deep copy so the clone's config edits don't mutate the original.
839
- data: JSON.parse(JSON.stringify(node.data ?? {}))
840
- };
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
- }
901
943
  var FlowEditorContext = createContext(null);
902
944
  var FlowEditorProvider = FlowEditorContext.Provider;
903
945
  function useFlowEditor() {
@@ -1128,6 +1170,49 @@ function FlowEditorInner({
1128
1170
  (oldEdge, conn) => flow.setEdges((eds) => reconnectEdge2(eds, oldEdge, conn)),
1129
1171
  [flow]
1130
1172
  );
1173
+ const isLaneNode = useCallback(
1174
+ (n) => getNodeKind(n.data?.kind ?? n.type)?.category === "layout",
1175
+ []
1176
+ );
1177
+ const addLane = useCallback(
1178
+ (orientation = "horizontal", title) => {
1179
+ const vertical = orientation === "vertical";
1180
+ const lanes = flow.nodes.filter(isLaneNode);
1181
+ const w = vertical ? 280 : 680;
1182
+ const h = vertical ? 480 : 168;
1183
+ const end = lanes.reduce(
1184
+ (m, l) => Math.max(m, vertical ? l.position.x + (l.width ?? w) : l.position.y + (l.height ?? h)),
1185
+ 0
1186
+ );
1187
+ const id = newNodeId();
1188
+ const name = title ?? `Lane ${lanes.length + 1}`;
1189
+ const node = {
1190
+ id,
1191
+ type: "@particle-academy/lane",
1192
+ position: vertical ? { x: lanes.length ? end + 12 : 0, y: 0 } : { x: 0, y: lanes.length ? end + 12 : 0 },
1193
+ width: w,
1194
+ height: h,
1195
+ data: { kind: "@particle-academy/lane", label: name, config: { title: name, orientation } }
1196
+ };
1197
+ flow.setNodes((all) => [...all, node]);
1198
+ setSelectedId(id);
1199
+ return id;
1200
+ },
1201
+ [flow, isLaneNode]
1202
+ );
1203
+ const handleNodeDragStop = useCallback(
1204
+ (_e, node) => {
1205
+ if (isLaneNode(node)) return;
1206
+ const overLane = rf.getIntersectingNodes(node).find((n) => isLaneNode(n));
1207
+ const currentParent = node.parentId;
1208
+ if (overLane && overLane.id !== currentParent) {
1209
+ flow.setNodes((all) => assignToLane(all, node.id, overLane.id));
1210
+ } else if (!overLane && currentParent) {
1211
+ flow.setNodes((all) => removeFromLane(all, node.id));
1212
+ }
1213
+ },
1214
+ [rf, flow, isLaneNode]
1215
+ );
1131
1216
  useEffect(() => {
1132
1217
  const onKey = (e) => {
1133
1218
  const t = e.target;
@@ -1191,6 +1276,9 @@ function FlowEditorInner({
1191
1276
  deleteNodes(selectedIds);
1192
1277
  },
1193
1278
  paste: pasteClipboard,
1279
+ addLane,
1280
+ assignToLane: (nodeId, laneId) => flow.setNodes((all) => assignToLane(all, nodeId, laneId)),
1281
+ removeFromLane: (nodeId) => flow.setNodes((all) => removeFromLane(all, nodeId)),
1194
1282
  run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, executors),
1195
1283
  cancel: runner.cancel,
1196
1284
  reset: runner.reset,
@@ -1203,7 +1291,7 @@ function FlowEditorInner({
1203
1291
  canUndo: hist.canUndo,
1204
1292
  canRedo: hist.canRedo
1205
1293
  };
1206
- }, [flow, selectedId, selected, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, executors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, hist, rf]);
1294
+ }, [flow, selectedId, selected, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, executors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, addLane, hist, rf]);
1207
1295
  useImperativeHandle(apiRef, () => api, [api]);
1208
1296
  const dropHandlers = paletteDropHandlers((kindName, evt) => {
1209
1297
  const point = rf.screenToFlowPosition({ x: evt.clientX, y: evt.clientY });
@@ -1239,6 +1327,10 @@ function FlowEditorInner({
1239
1327
  }
1240
1328
  )
1241
1329
  ] }),
1330
+ builtins.addLane !== false && /* @__PURE__ */ jsxs(Fragment, { children: [
1331
+ /* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
1332
+ /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "add-lane", title: "Add a swimlane", onClick: () => api.addLane(), children: "\u25A4 Lane" })
1333
+ ] }),
1242
1334
  (builtins.export !== false || builtins.import !== false) && /* @__PURE__ */ jsx("span", { className: "ff-editor__sep" }),
1243
1335
  builtins.export !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "export", onClick: api.exportWorkflow, children: "\u2193 Export" }),
1244
1336
  builtins.import !== false && /* @__PURE__ */ jsx("button", { className: "ff-editor__btn", "data-action": "import", onClick: api.importWorkflow, children: "\u2191 Import" }),
@@ -1266,6 +1358,7 @@ function FlowEditorInner({
1266
1358
  onReconnect,
1267
1359
  edgesReconnectable: true,
1268
1360
  onNodeDragStart: hist.onNodeDragStart,
1361
+ onNodeDragStop: handleNodeDragStop,
1269
1362
  onNodeClick: handleNodeClick,
1270
1363
  onNodeContextMenu: builtins.contextMenu === false ? void 0 : handleNodeContextMenu,
1271
1364
  onEdgeClick: handleEdgeClick,