@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
@@ -1,6 +1,6 @@
1
1
  import { LanguageModel } from 'ai';
2
- import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-ChE3Xi8R.cjs';
3
- import '../types-sOmpCitB.cjs';
2
+ import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-exVeADXz.cjs';
3
+ import '../types-CMSrWVYM.cjs';
4
4
  import '@xyflow/react';
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import { LanguageModel } from 'ai';
2
- import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-DUhAc-EJ.js';
3
- import '../types-sOmpCitB.js';
2
+ import { L as LlmRouteRequest, a as LlmClient } from '../capabilities-85Mjn4uW.js';
3
+ import '../types-CMSrWVYM.js';
4
4
  import '@xyflow/react';
5
5
 
6
6
  /**
@@ -1,9 +1,9 @@
1
- import { a as NodeKindDefinition, P as PortSpec } from '../types-D_jOR3Z2.cjs';
2
- export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-D_jOR3Z2.cjs';
3
- import { P as PortDescriptor, b as FlowNode, N as NodeExecutor } from '../types-sOmpCitB.cjs';
1
+ import { a as NodeKindDefinition, P as PortSpec } from '../types-DneUoivl.cjs';
2
+ export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-DneUoivl.cjs';
3
+ import { P as PortDescriptor, a as FlowNode, N as NodeExecutor } from '../types-CMSrWVYM.cjs';
4
4
  import { Connection, Edge, NodeProps, NodeTypes } from '@xyflow/react';
5
- import { b as LlmRoute } from '../capabilities-ChE3Xi8R.cjs';
6
- export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-ChE3Xi8R.cjs';
5
+ import { b as LlmRoute } from '../capabilities-exVeADXz.cjs';
6
+ export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-exVeADXz.cjs';
7
7
  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';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode } from 'react';
@@ -1,9 +1,9 @@
1
- import { a as NodeKindDefinition, P as PortSpec } from '../types-NerkPtHS.js';
2
- export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-NerkPtHS.js';
3
- import { P as PortDescriptor, b as FlowNode, N as NodeExecutor } from '../types-sOmpCitB.js';
1
+ import { a as NodeKindDefinition, P as PortSpec } from '../types-CFEtuRWJ.js';
2
+ export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-CFEtuRWJ.js';
3
+ import { P as PortDescriptor, a as FlowNode, N as NodeExecutor } from '../types-CMSrWVYM.js';
4
4
  import { Connection, Edge, NodeProps, NodeTypes } from '@xyflow/react';
5
- import { b as LlmRoute } from '../capabilities-DUhAc-EJ.js';
6
- export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-DUhAc-EJ.js';
5
+ import { b as LlmRoute } from '../capabilities-85Mjn4uW.js';
6
+ export { C as CapabilityId, a as LlmClient, c as LlmRouteChoice, L as LlmRouteRequest, W as WorkflowResolution, d as WorkflowResolutionFailure, e as WorkflowResolver, f as capabilityStatus, g as getLlmClient, h as getWorkflowResolver, i as isResolutionFailure, r as registerLlmClient, j as registerWorkflowResolver } from '../capabilities-85Mjn4uW.js';
7
7
  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';
8
8
  import * as react from 'react';
9
9
  import { ComponentType, ReactNode } from 'react';
package/dist/registry.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var ReactExports = require('react');
5
- require('react-dom');
5
+ var reactDom = require('react-dom');
6
6
 
7
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
8
 
@@ -179,6 +179,8 @@ function categoryAccent(category) {
179
179
  return "#ec4899";
180
180
  case "output":
181
181
  return "#a855f7";
182
+ case "layout":
183
+ return "#64748b";
182
184
  default:
183
185
  return "#71717a";
184
186
  }
@@ -321,8 +323,14 @@ async function runFlow(graph, executors, onEvent = () => {
321
323
  continue;
322
324
  }
323
325
  }
324
- if (node.type === "note") {
325
- onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "annotation" });
326
+ const visualKind = getNodeKind(node.type ?? "");
327
+ if (node.type === "note" || visualKind?.category === "layout") {
328
+ onEvent({
329
+ type: "node-status",
330
+ nodeId: node.id,
331
+ status: "idle",
332
+ text: visualKind?.category === "layout" ? "lane" : "annotation"
333
+ });
326
334
  continue;
327
335
  }
328
336
  onEvent({ type: "node-status", nodeId: node.id, status: "running" });
@@ -733,9 +741,9 @@ function creator_default(name) {
733
741
  // node_modules/d3-selection/src/selector.js
734
742
  function none() {
735
743
  }
736
- function selector_default(selector) {
737
- return selector == null ? none : function() {
738
- return this.querySelector(selector);
744
+ function selector_default(selector2) {
745
+ return selector2 == null ? none : function() {
746
+ return this.querySelector(selector2);
739
747
  };
740
748
  }
741
749
 
@@ -762,9 +770,9 @@ function array(x) {
762
770
  function empty() {
763
771
  return [];
764
772
  }
765
- function selectorAll_default(selector) {
766
- return selector == null ? empty : function() {
767
- return this.querySelectorAll(selector);
773
+ function selectorAll_default(selector2) {
774
+ return selector2 == null ? empty : function() {
775
+ return this.querySelectorAll(selector2);
768
776
  };
769
777
  }
770
778
 
@@ -789,14 +797,14 @@ function selectAll_default(select) {
789
797
  }
790
798
 
791
799
  // node_modules/d3-selection/src/matcher.js
792
- function matcher_default(selector) {
800
+ function matcher_default(selector2) {
793
801
  return function() {
794
- return this.matches(selector);
802
+ return this.matches(selector2);
795
803
  };
796
804
  }
797
- function childMatcher(selector) {
805
+ function childMatcher(selector2) {
798
806
  return function(node) {
799
- return node.matches(selector);
807
+ return node.matches(selector2);
800
808
  };
801
809
  }
802
810
 
@@ -865,11 +873,11 @@ EnterNode.prototype = {
865
873
  insertBefore: function(child, next) {
866
874
  return this._parent.insertBefore(child, next);
867
875
  },
868
- querySelector: function(selector) {
869
- return this._parent.querySelector(selector);
876
+ querySelector: function(selector2) {
877
+ return this._parent.querySelector(selector2);
870
878
  },
871
- querySelectorAll: function(selector) {
872
- return this._parent.querySelectorAll(selector);
879
+ querySelectorAll: function(selector2) {
880
+ return this._parent.querySelectorAll(selector2);
873
881
  }
874
882
  };
875
883
 
@@ -1480,8 +1488,8 @@ Selection.prototype = selection.prototype = {
1480
1488
  var selection_default = selection;
1481
1489
 
1482
1490
  // node_modules/d3-selection/src/select.js
1483
- function select_default2(selector) {
1484
- return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root);
1491
+ function select_default2(selector2) {
1492
+ return typeof selector2 === "string" ? new Selection([[document.querySelector(selector2)]], [document.documentElement]) : new Selection([[selector2]], root);
1485
1493
  }
1486
1494
 
1487
1495
  // node_modules/d3-selection/src/sourceEvent.js
@@ -4473,6 +4481,40 @@ function createMarkerIds(edges, { id: id2, defaultColor, defaultMarkerStart, def
4473
4481
  return markers;
4474
4482
  }, []).sort((a, b) => a.id.localeCompare(b.id));
4475
4483
  }
4484
+ function getNodeToolbarTransform(nodeRect, viewport, position, offset, align) {
4485
+ let alignmentOffset = 0.5;
4486
+ if (align === "start") {
4487
+ alignmentOffset = 0;
4488
+ } else if (align === "end") {
4489
+ alignmentOffset = 1;
4490
+ }
4491
+ let pos = [
4492
+ (nodeRect.x + nodeRect.width * alignmentOffset) * viewport.zoom + viewport.x,
4493
+ nodeRect.y * viewport.zoom + viewport.y - offset
4494
+ ];
4495
+ let shift = [-100 * alignmentOffset, -100];
4496
+ switch (position) {
4497
+ case Position.Right:
4498
+ pos = [
4499
+ (nodeRect.x + nodeRect.width) * viewport.zoom + viewport.x + offset,
4500
+ (nodeRect.y + nodeRect.height * alignmentOffset) * viewport.zoom + viewport.y
4501
+ ];
4502
+ shift = [0, -100 * alignmentOffset];
4503
+ break;
4504
+ case Position.Bottom:
4505
+ pos[1] = (nodeRect.y + nodeRect.height) * viewport.zoom + viewport.y + offset;
4506
+ shift[1] = 0;
4507
+ break;
4508
+ case Position.Left:
4509
+ pos = [
4510
+ nodeRect.x * viewport.zoom + viewport.x - offset,
4511
+ (nodeRect.y + nodeRect.height * alignmentOffset) * viewport.zoom + viewport.y
4512
+ ];
4513
+ shift = [-100, -100 * alignmentOffset];
4514
+ break;
4515
+ }
4516
+ return `translate(${pos[0]}px, ${pos[1]}px) translate(${shift[0]}%, ${shift[1]}%)`;
4517
+ }
4476
4518
  var SELECTED_NODE_Z = 1e3;
4477
4519
  var ROOT_PARENT_Z_INCREMENT = 10;
4478
4520
  var defaultOptions = {
@@ -4840,10 +4882,10 @@ function isParentSelected(node, nodeLookup) {
4840
4882
  }
4841
4883
  return isParentSelected(parentNode, nodeLookup);
4842
4884
  }
4843
- function hasSelector(target, selector, domNode) {
4885
+ function hasSelector(target, selector2, domNode) {
4844
4886
  let current = target;
4845
4887
  do {
4846
- if (current?.matches?.(selector))
4888
+ if (current?.matches?.(selector2))
4847
4889
  return true;
4848
4890
  if (current === domNode)
4849
4891
  return false;
@@ -5801,6 +5843,8 @@ var ResizeControlVariant;
5801
5843
  ResizeControlVariant2["Line"] = "line";
5802
5844
  ResizeControlVariant2["Handle"] = "handle";
5803
5845
  })(ResizeControlVariant || (ResizeControlVariant = {}));
5846
+ var XY_RESIZER_HANDLE_POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"];
5847
+ var XY_RESIZER_LINE_POSITIONS = ["top", "right", "bottom", "left"];
5804
5848
  function getResizeDirection({ width, prevWidth, height, prevHeight, affectsX, affectsY }) {
5805
5849
  const deltaWidth = width - prevWidth;
5806
5850
  const deltaHeight = height - prevHeight;
@@ -6157,7 +6201,7 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
6157
6201
  destroy
6158
6202
  };
6159
6203
  }
6160
- function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
6204
+ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector2, isEqual) {
6161
6205
  const instRef = ReactExports.useRef(null);
6162
6206
  let inst;
6163
6207
  if (instRef.current === null) {
@@ -6174,7 +6218,7 @@ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnaps
6174
6218
  if (!hasMemo) {
6175
6219
  hasMemo = true;
6176
6220
  memoizedSnapshot = nextSnapshot;
6177
- const nextSelection2 = selector(nextSnapshot);
6221
+ const nextSelection2 = selector2(nextSnapshot);
6178
6222
  if (isEqual !== void 0 && inst.hasValue) {
6179
6223
  const currentSelection = inst.value;
6180
6224
  if (isEqual(currentSelection, nextSelection2)) {
@@ -6190,7 +6234,7 @@ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnaps
6190
6234
  if (Object.is(prevSnapshot, nextSnapshot)) {
6191
6235
  return prevSelection;
6192
6236
  }
6193
- const nextSelection = selector(nextSnapshot);
6237
+ const nextSelection = selector2(nextSnapshot);
6194
6238
  if (isEqual !== void 0 && isEqual(prevSelection, nextSelection)) {
6195
6239
  memoizedSnapshot = nextSnapshot;
6196
6240
  return prevSelection;
@@ -6203,7 +6247,7 @@ function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnaps
6203
6247
  const getSnapshotWithSelector = () => memoizedSelector(getSnapshot());
6204
6248
  const getServerSnapshotWithSelector = maybeGetServerSnapshot === null ? void 0 : () => memoizedSelector(maybeGetServerSnapshot());
6205
6249
  return [getSnapshotWithSelector, getServerSnapshotWithSelector];
6206
- }, [getSnapshot, getServerSnapshot, selector, isEqual]);
6250
+ }, [getSnapshot, getServerSnapshot, selector2, isEqual]);
6207
6251
  const value = ReactExports.useSyncExternalStore(subscribe, getSelection, getServerSelection);
6208
6252
  ReactExports.useEffect(() => {
6209
6253
  inst.hasValue = true;
@@ -6250,12 +6294,12 @@ var createStore = (createState) => createState ? createStoreImpl(createState) :
6250
6294
  var { useDebugValue: useDebugValue2 } = ReactExports__default.default;
6251
6295
  var { useSyncExternalStoreWithSelector: useSyncExternalStoreWithSelector2 } = use_sync_external_store_with_selector_default;
6252
6296
  var identity3 = (arg) => arg;
6253
- function useStoreWithEqualityFn(api, selector = identity3, equalityFn) {
6297
+ function useStoreWithEqualityFn(api, selector2 = identity3, equalityFn) {
6254
6298
  const slice = useSyncExternalStoreWithSelector2(
6255
6299
  api.subscribe,
6256
6300
  api.getState,
6257
6301
  api.getServerState || api.getInitialState,
6258
- selector,
6302
+ selector2,
6259
6303
  equalityFn
6260
6304
  );
6261
6305
  useDebugValue2(slice);
@@ -6263,7 +6307,7 @@ function useStoreWithEqualityFn(api, selector = identity3, equalityFn) {
6263
6307
  }
6264
6308
  var createWithEqualityFnImpl = (createState, defaultEqualityFn) => {
6265
6309
  const api = createStore(createState);
6266
- const useBoundStoreWithEqualityFn = (selector, equalityFn = defaultEqualityFn) => useStoreWithEqualityFn(api, selector, equalityFn);
6310
+ const useBoundStoreWithEqualityFn = (selector2, equalityFn = defaultEqualityFn) => useStoreWithEqualityFn(api, selector2, equalityFn);
6267
6311
  Object.assign(useBoundStoreWithEqualityFn, api);
6268
6312
  return useBoundStoreWithEqualityFn;
6269
6313
  };
@@ -6309,12 +6353,12 @@ function shallow$1(objA, objB) {
6309
6353
  var StoreContext = ReactExports.createContext(null);
6310
6354
  var Provider$1 = StoreContext.Provider;
6311
6355
  var zustandErrorMessage = errorMessages["error001"]("react");
6312
- function useStore(selector, equalityFn) {
6356
+ function useStore(selector2, equalityFn) {
6313
6357
  const store = ReactExports.useContext(StoreContext);
6314
6358
  if (store === null) {
6315
6359
  throw new Error(zustandErrorMessage);
6316
6360
  }
6317
- return useStoreWithEqualityFn(store, selector, equalityFn);
6361
+ return useStoreWithEqualityFn(store, selector2, equalityFn);
6318
6362
  }
6319
6363
  function useStoreApi() {
6320
6364
  const store = ReactExports.useContext(StoreContext);
@@ -9506,7 +9550,69 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
9506
9550
  ...color2 && { [isHandleControl ? "backgroundColor" : "borderColor"]: color2 }
9507
9551
  }, children: children2 });
9508
9552
  }
9509
- ReactExports.memo(ResizeControl);
9553
+ var NodeResizeControl = ReactExports.memo(ResizeControl);
9554
+ function NodeResizer({ nodeId, isVisible = true, handleClassName, handleStyle, lineClassName, lineStyle, color: color2, minWidth = 10, minHeight = 10, maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, autoScale = true, shouldResize, onResizeStart, onResize, onResizeEnd }) {
9555
+ if (!isVisible) {
9556
+ return null;
9557
+ }
9558
+ return jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [XY_RESIZER_LINE_POSITIONS.map((position) => jsxRuntime.jsx(NodeResizeControl, { className: lineClassName, style: lineStyle, nodeId, position, variant: ResizeControlVariant.Line, color: color2, minWidth, minHeight, maxWidth, maxHeight, onResizeStart, keepAspectRatio, autoScale, shouldResize, onResize, onResizeEnd }, position)), XY_RESIZER_HANDLE_POSITIONS.map((position) => jsxRuntime.jsx(NodeResizeControl, { className: handleClassName, style: handleStyle, nodeId, position, color: color2, minWidth, minHeight, maxWidth, maxHeight, onResizeStart, keepAspectRatio, autoScale, shouldResize, onResize, onResizeEnd }, position))] });
9559
+ }
9560
+ var selector = (state) => state.domNode?.querySelector(".react-flow__renderer");
9561
+ function NodeToolbarPortal({ children: children2 }) {
9562
+ const wrapperRef = useStore(selector);
9563
+ if (!wrapperRef) {
9564
+ return null;
9565
+ }
9566
+ return reactDom.createPortal(children2, wrapperRef);
9567
+ }
9568
+ var nodeEqualityFn = (a, b) => a?.internals.positionAbsolute.x !== b?.internals.positionAbsolute.x || a?.internals.positionAbsolute.y !== b?.internals.positionAbsolute.y || a?.measured.width !== b?.measured.width || a?.measured.height !== b?.measured.height || a?.selected !== b?.selected || a?.internals.z !== b?.internals.z;
9569
+ var nodesEqualityFn = (a, b) => {
9570
+ if (a.size !== b.size) {
9571
+ return false;
9572
+ }
9573
+ for (const [key, node] of a) {
9574
+ if (nodeEqualityFn(node, b.get(key))) {
9575
+ return false;
9576
+ }
9577
+ }
9578
+ return true;
9579
+ };
9580
+ var storeSelector = (state) => ({
9581
+ x: state.transform[0],
9582
+ y: state.transform[1],
9583
+ zoom: state.transform[2],
9584
+ selectedNodesCount: state.nodes.filter((node) => node.selected).length
9585
+ });
9586
+ function NodeToolbar({ nodeId, children: children2, className, style: style2, isVisible, position = Position.Top, offset = 10, align = "center", ...rest }) {
9587
+ const contextNodeId = useNodeId();
9588
+ const nodesSelector = ReactExports.useCallback((state) => {
9589
+ const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ""];
9590
+ const internalNodes = nodeIds.reduce((res, id2) => {
9591
+ const node = state.nodeLookup.get(id2);
9592
+ if (node) {
9593
+ res.set(node.id, node);
9594
+ }
9595
+ return res;
9596
+ }, /* @__PURE__ */ new Map());
9597
+ return internalNodes;
9598
+ }, [nodeId, contextNodeId]);
9599
+ const nodes = useStore(nodesSelector, nodesEqualityFn);
9600
+ const { x, y, zoom, selectedNodesCount } = useStore(storeSelector, shallow$1);
9601
+ const isActive = typeof isVisible === "boolean" ? isVisible : nodes.size === 1 && nodes.values().next().value?.selected && selectedNodesCount === 1;
9602
+ if (!isActive || !nodes.size) {
9603
+ return null;
9604
+ }
9605
+ const nodeRect = getInternalNodesBounds(nodes);
9606
+ const nodesArray = Array.from(nodes.values());
9607
+ const zIndex = Math.max(...nodesArray.map((node) => node.internals.z + 1));
9608
+ const wrapperStyle2 = {
9609
+ position: "absolute",
9610
+ transform: getNodeToolbarTransform(nodeRect, { x, y, zoom }, position, offset, align),
9611
+ zIndex,
9612
+ ...style2
9613
+ };
9614
+ return jsxRuntime.jsx(NodeToolbarPortal, { children: jsxRuntime.jsx("div", { style: wrapperStyle2, className: cc(["react-flow__node-toolbar", className]), ...rest, "data-id": nodesArray.reduce((acc, node) => `${acc}${node.id} `, "").trim(), children: children2 }) });
9615
+ }
9510
9616
  function RegistryNodeInner(props) {
9511
9617
  const kindName = props.data.kind ?? props.type;
9512
9618
  const kind = ReactExports.useMemo(() => getNodeKind(kindName), [kindName]);
@@ -9538,10 +9644,20 @@ function RegistryNodeInner(props) {
9538
9644
  "ff-node",
9539
9645
  `ff-node--status-${status}`,
9540
9646
  `ff-node--cat-${kind.category}`,
9541
- props.selected ? "ff-node--selected" : ""
9647
+ props.selected ? "ff-node--selected" : "",
9648
+ kind.resizable ? "ff-node--resizable" : ""
9542
9649
  ].filter(Boolean).join(" "),
9543
9650
  style: { borderColor: props.selected ? accent : void 0 },
9544
9651
  children: [
9652
+ kind.resizable && /* @__PURE__ */ jsxRuntime.jsx(
9653
+ NodeResizer,
9654
+ {
9655
+ isVisible: props.selected ?? false,
9656
+ color: accent,
9657
+ ...typeof kind.resizable === "object" ? kind.resizable : {}
9658
+ }
9659
+ ),
9660
+ kind.toolbar && /* @__PURE__ */ jsxRuntime.jsx(NodeToolbar, { isVisible: props.selected ?? false, children: kind.toolbar({ nodeId: props.id, config, selected: props.selected ?? false }) }),
9545
9661
  /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "ff-node__header", style: { background: accent }, children: [
9546
9662
  kind.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__icon", "aria-hidden": true, children: kind.icon }),
9547
9663
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-node__tag", children: kind.label.toUpperCase() }),
@@ -9637,6 +9753,27 @@ function previewValue(v) {
9637
9753
  }
9638
9754
  return "\u2026";
9639
9755
  }
9756
+ function LaneNodeInner(props) {
9757
+ const data = props.data;
9758
+ const config = data.config ?? {};
9759
+ const title = config.title ?? data.label ?? "Lane";
9760
+ const orientation = config.orientation ?? "horizontal";
9761
+ const accent = config.color ?? data.color ?? categoryAccent("layout");
9762
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9763
+ "div",
9764
+ {
9765
+ className: ["ff-lane", `ff-lane--${orientation}`, props.selected ? "ff-lane--selected" : ""].filter(Boolean).join(" "),
9766
+ style: { borderColor: props.selected ? accent : void 0 },
9767
+ children: [
9768
+ /* @__PURE__ */ jsxRuntime.jsx(NodeResizer, { isVisible: props.selected ?? false, color: accent, minWidth: 160, minHeight: 72 }),
9769
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-lane__header", style: { background: accent }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-lane__title", children: title }) })
9770
+ ]
9771
+ }
9772
+ );
9773
+ }
9774
+ var LaneNode = ReactExports.memo(LaneNodeInner);
9775
+
9776
+ // src/registry/builtin.ts
9640
9777
  function casePorts(cases) {
9641
9778
  const byPort = /* @__PURE__ */ new Map();
9642
9779
  if (cases && typeof cases === "object" && !Array.isArray(cases)) {
@@ -10377,6 +10514,32 @@ var KINDS = [
10377
10514
  },
10378
10515
  { type: "expression", key: "message", label: "Message", required: true, example: "{{ $json }}" }
10379
10516
  ]
10517
+ },
10518
+ {
10519
+ // Swimlane — a portless, resizable container. Child nodes are parented into
10520
+ // it; it never runs (the runtime skips the `layout` category). Uses its own
10521
+ // renderer (LaneNode) instead of the default card.
10522
+ name: "@particle-academy/lane",
10523
+ aliases: ["lane", "@fancy/lane"],
10524
+ category: "layout",
10525
+ label: "Lane",
10526
+ description: "A resizable swimlane \u2014 drop nodes into it to group them.",
10527
+ icon: "\u25A4",
10528
+ inputs: [],
10529
+ outputs: [],
10530
+ configSchema: [
10531
+ { type: "text", key: "title", label: "Title", default: "Lane" },
10532
+ {
10533
+ type: "select",
10534
+ key: "orientation",
10535
+ label: "Orientation",
10536
+ default: "horizontal",
10537
+ options: [{ value: "horizontal", label: "Row" }, { value: "vertical", label: "Column" }]
10538
+ }
10539
+ ],
10540
+ defaultConfig: { title: "Lane", orientation: "horizontal" },
10541
+ resizable: { minWidth: 160, minHeight: 72 },
10542
+ component: LaneNode
10380
10543
  }
10381
10544
  ];
10382
10545
  function registerBuiltinKinds() {
@@ -10388,7 +10551,8 @@ var BUILTIN_KINDS = KINDS;
10388
10551
  function buildNodeTypes() {
10389
10552
  const map = {};
10390
10553
  for (const k of listNodeKinds()) {
10391
- for (const id2 of kindIds(k)) map[id2] = RegistryNode;
10554
+ const renderer = k.component ?? RegistryNode;
10555
+ for (const id2 of kindIds(k)) map[id2] = renderer;
10392
10556
  }
10393
10557
  return map;
10394
10558
  }