@octaviaflow/core 3.0.18-beta.2 → 3.0.18-beta.20

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 (170) hide show
  1. package/dist/chunk-2NGC7AI3.js +2637 -0
  2. package/dist/chunk-2NGC7AI3.js.map +1 -0
  3. package/dist/chunk-2O6K5PLY.js +2637 -0
  4. package/dist/chunk-2O6K5PLY.js.map +1 -0
  5. package/dist/chunk-2ZIOFEIS.js +3001 -0
  6. package/dist/chunk-2ZIOFEIS.js.map +1 -0
  7. package/dist/chunk-3DWB2PUF.js +2991 -0
  8. package/dist/chunk-3DWB2PUF.js.map +1 -0
  9. package/dist/chunk-4ZALUTZS.js +2936 -0
  10. package/dist/chunk-4ZALUTZS.js.map +1 -0
  11. package/dist/chunk-5ARKSRED.js +2951 -0
  12. package/dist/chunk-5ARKSRED.js.map +1 -0
  13. package/dist/chunk-5OSGSJMM.js +2981 -0
  14. package/dist/chunk-5OSGSJMM.js.map +1 -0
  15. package/dist/chunk-5YQQMEF3.js +2981 -0
  16. package/dist/chunk-5YQQMEF3.js.map +1 -0
  17. package/dist/chunk-6QCJK7H7.js +2991 -0
  18. package/dist/chunk-6QCJK7H7.js.map +1 -0
  19. package/dist/chunk-76TP67ED.js +2984 -0
  20. package/dist/chunk-76TP67ED.js.map +1 -0
  21. package/dist/chunk-A6KMO4JV.js +2949 -0
  22. package/dist/chunk-A6KMO4JV.js.map +1 -0
  23. package/dist/chunk-B7FTWSTM.js +2938 -0
  24. package/dist/chunk-B7FTWSTM.js.map +1 -0
  25. package/dist/chunk-BCO6M26F.js +2940 -0
  26. package/dist/chunk-BCO6M26F.js.map +1 -0
  27. package/dist/chunk-C3UD2AZ5.js +2637 -0
  28. package/dist/chunk-C3UD2AZ5.js.map +1 -0
  29. package/dist/chunk-CEUP4NK2.js +2850 -0
  30. package/dist/chunk-CEUP4NK2.js.map +1 -0
  31. package/dist/chunk-DBNSBJO7.js +2993 -0
  32. package/dist/chunk-DBNSBJO7.js.map +1 -0
  33. package/dist/chunk-ECIHUVU7.js +2986 -0
  34. package/dist/chunk-ECIHUVU7.js.map +1 -0
  35. package/dist/chunk-EERNYLFL.js +2860 -0
  36. package/dist/chunk-EERNYLFL.js.map +1 -0
  37. package/dist/chunk-EKFDJX4G.js +2872 -0
  38. package/dist/chunk-EKFDJX4G.js.map +1 -0
  39. package/dist/chunk-GJA3GJUZ.js +2844 -0
  40. package/dist/chunk-GJA3GJUZ.js.map +1 -0
  41. package/dist/chunk-HDOTOZNA.js +2936 -0
  42. package/dist/chunk-HDOTOZNA.js.map +1 -0
  43. package/dist/chunk-IOKUV7FD.js +2658 -0
  44. package/dist/chunk-IOKUV7FD.js.map +1 -0
  45. package/dist/chunk-IUIICQU5.js +2946 -0
  46. package/dist/chunk-IUIICQU5.js.map +1 -0
  47. package/dist/chunk-J2UYZI6D.js +2946 -0
  48. package/dist/chunk-J2UYZI6D.js.map +1 -0
  49. package/dist/chunk-J7YASALS.js +2859 -0
  50. package/dist/chunk-J7YASALS.js.map +1 -0
  51. package/dist/chunk-JIEUYBQT.js +2658 -0
  52. package/dist/chunk-JIEUYBQT.js.map +1 -0
  53. package/dist/chunk-K2H7JLQW.js +2952 -0
  54. package/dist/chunk-K2H7JLQW.js.map +1 -0
  55. package/dist/chunk-KUXYBP66.js +2953 -0
  56. package/dist/chunk-KUXYBP66.js.map +1 -0
  57. package/dist/chunk-KYMYNYFV.js +2656 -0
  58. package/dist/chunk-KYMYNYFV.js.map +1 -0
  59. package/dist/chunk-MMXL343D.js +2974 -0
  60. package/dist/chunk-MMXL343D.js.map +1 -0
  61. package/dist/chunk-MXJL3EPE.js +2986 -0
  62. package/dist/chunk-MXJL3EPE.js.map +1 -0
  63. package/dist/chunk-MYZ25B2R.js +2995 -0
  64. package/dist/chunk-MYZ25B2R.js.map +1 -0
  65. package/dist/chunk-NTMEYB7B.js +2949 -0
  66. package/dist/chunk-NTMEYB7B.js.map +1 -0
  67. package/dist/chunk-PVJXX6GP.js +2640 -0
  68. package/dist/chunk-PVJXX6GP.js.map +1 -0
  69. package/dist/chunk-Q6ERDPQR.js +2981 -0
  70. package/dist/chunk-Q6ERDPQR.js.map +1 -0
  71. package/dist/chunk-S2SSBMWJ.js +2658 -0
  72. package/dist/chunk-S2SSBMWJ.js.map +1 -0
  73. package/dist/chunk-SLVDAZSX.js +2946 -0
  74. package/dist/chunk-SLVDAZSX.js.map +1 -0
  75. package/dist/chunk-UQBPYONV.js +2991 -0
  76. package/dist/chunk-UQBPYONV.js.map +1 -0
  77. package/dist/chunk-UXMNBS22.js +2955 -0
  78. package/dist/chunk-UXMNBS22.js.map +1 -0
  79. package/dist/chunk-WEGED7TA.js +2991 -0
  80. package/dist/chunk-WEGED7TA.js.map +1 -0
  81. package/dist/chunk-WEPTBLWX.js +2847 -0
  82. package/dist/chunk-WEPTBLWX.js.map +1 -0
  83. package/dist/chunk-WG4ZQMPS.js +2844 -0
  84. package/dist/chunk-WG4ZQMPS.js.map +1 -0
  85. package/dist/chunk-XEPEBHAW.js +2808 -0
  86. package/dist/chunk-XEPEBHAW.js.map +1 -0
  87. package/dist/chunk-XG2OYFX6.js +2925 -0
  88. package/dist/chunk-XG2OYFX6.js.map +1 -0
  89. package/dist/chunk-ZAMJEU42.js +2992 -0
  90. package/dist/chunk-ZAMJEU42.js.map +1 -0
  91. package/dist/chunk-ZRAM6FXB.js +2949 -0
  92. package/dist/chunk-ZRAM6FXB.js.map +1 -0
  93. package/dist/components/CsvViewer/CsvViewer.d.ts +51 -0
  94. package/dist/components/CsvViewer/CsvViewer.d.ts.map +1 -0
  95. package/dist/components/CsvViewer/index.d.ts +2 -0
  96. package/dist/components/CsvViewer/index.d.ts.map +1 -0
  97. package/dist/components/DataTable/DataTable.d.ts +19 -1
  98. package/dist/components/DataTable/DataTable.d.ts.map +1 -1
  99. package/dist/components/DropdownMenu/DropdownMenu.d.ts +12 -2
  100. package/dist/components/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  101. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts +8 -2
  102. package/dist/components/ExecutionConsole/ExecutionConsole.d.ts.map +1 -1
  103. package/dist/components/FlowMinimap/FlowMinimap.d.ts +17 -1
  104. package/dist/components/FlowMinimap/FlowMinimap.d.ts.map +1 -1
  105. package/dist/components/FlowToolbar/FlowToolbar.d.ts +16 -10
  106. package/dist/components/FlowToolbar/FlowToolbar.d.ts.map +1 -1
  107. package/dist/components/JsonViewer/JsonViewer.d.ts +42 -7
  108. package/dist/components/JsonViewer/JsonViewer.d.ts.map +1 -1
  109. package/dist/components/JsonViewer/index.d.ts +1 -1
  110. package/dist/components/JsonViewer/index.d.ts.map +1 -1
  111. package/dist/components/Select/Select.d.ts.map +1 -1
  112. package/dist/components/WorkflowHeader/WorkflowHeader.d.ts +130 -0
  113. package/dist/components/WorkflowHeader/WorkflowHeader.d.ts.map +1 -0
  114. package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts +69 -0
  115. package/dist/components/WorkflowHeader/WorkflowHeaderExpanded.d.ts.map +1 -0
  116. package/dist/components/WorkflowHeader/index.d.ts +3 -0
  117. package/dist/components/WorkflowHeader/index.d.ts.map +1 -0
  118. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts +40 -0
  119. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCentered.d.ts.map +1 -0
  120. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts +39 -0
  121. package/dist/components/WorkflowHeader/misc/WorkflowHeaderCommand.d.ts.map +1 -0
  122. package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts +44 -0
  123. package/dist/components/WorkflowHeader/misc/WorkflowHeaderMinimal.d.ts.map +1 -0
  124. package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts +45 -0
  125. package/dist/components/WorkflowHeader/misc/WorkflowHeaderRail.d.ts.map +1 -0
  126. package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts +48 -0
  127. package/dist/components/WorkflowHeader/misc/WorkflowHeaderStudio.d.ts.map +1 -0
  128. package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts +52 -0
  129. package/dist/components/WorkflowHeader/misc/WorkflowHeaderTiered.d.ts.map +1 -0
  130. package/dist/components/XmlViewer/XmlViewer.d.ts +26 -1
  131. package/dist/components/XmlViewer/XmlViewer.d.ts.map +1 -1
  132. package/dist/components/XmlViewer/index.d.ts +1 -1
  133. package/dist/components/XmlViewer/index.d.ts.map +1 -1
  134. package/dist/components/YamlViewer/YamlViewer.d.ts +26 -1
  135. package/dist/components/YamlViewer/YamlViewer.d.ts.map +1 -1
  136. package/dist/components/YamlViewer/index.d.ts +1 -1
  137. package/dist/components/YamlViewer/index.d.ts.map +1 -1
  138. package/dist/hooks/useRelativeTime.d.ts +28 -0
  139. package/dist/hooks/useRelativeTime.d.ts.map +1 -0
  140. package/dist/hooks/useWorkflowRuntime.d.ts +20 -0
  141. package/dist/hooks/useWorkflowRuntime.d.ts.map +1 -0
  142. package/dist/index.cjs +4918 -3536
  143. package/dist/index.cjs.map +1 -1
  144. package/dist/index.d.ts +7 -3
  145. package/dist/index.d.ts.map +1 -1
  146. package/dist/index.js +4317 -3305
  147. package/dist/index.js.map +1 -1
  148. package/dist/styles.css +1 -1
  149. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts +27 -9
  150. package/dist/workflow/components/ConfigPanel/ConfigPanel.d.ts.map +1 -1
  151. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts +49 -1
  152. package/dist/workflow/components/FlowCanvas/FlowCanvas.d.ts.map +1 -1
  153. package/dist/workflow/components/FlowEdge/FlowEdge.d.ts.map +1 -1
  154. package/dist/workflow/components/Handle/Handle.d.ts +9 -1
  155. package/dist/workflow/components/Handle/Handle.d.ts.map +1 -1
  156. package/dist/workflow/components/Handle/handleRegistry.d.ts +19 -0
  157. package/dist/workflow/components/Handle/handleRegistry.d.ts.map +1 -1
  158. package/dist/workflow/components/kinds/index.d.ts +4 -0
  159. package/dist/workflow/components/kinds/index.d.ts.map +1 -1
  160. package/dist/workflow/index.d.ts +1 -1
  161. package/dist/workflow/index.d.ts.map +1 -1
  162. package/dist/workflow/store/selectors.d.ts +12 -0
  163. package/dist/workflow/store/selectors.d.ts.map +1 -1
  164. package/dist/workflow/utils/parenting.d.ts +5 -3
  165. package/dist/workflow/utils/parenting.d.ts.map +1 -1
  166. package/dist/workflow.cjs +694 -452
  167. package/dist/workflow.cjs.map +1 -1
  168. package/dist/workflow.js +86 -265
  169. package/dist/workflow.js.map +1 -1
  170. package/package.json +7 -3
package/dist/workflow.cjs CHANGED
@@ -77,7 +77,8 @@ __export(workflow_exports, {
77
77
  useNodeData: () => useNodeData,
78
78
  useNodes: () => useNodes,
79
79
  useSelection: () => useSelection,
80
- useViewport: () => useViewport
80
+ useViewport: () => useViewport,
81
+ useViewportOrNull: () => useViewportOrNull
81
82
  });
82
83
  module.exports = __toCommonJS(workflow_exports);
83
84
 
@@ -646,6 +647,23 @@ function useEdges() {
646
647
  function useViewport() {
647
648
  return useFlowSelector((s) => s.viewport);
648
649
  }
650
+ var VIEWPORT_OR_NULL_NO_STORE_SUBSCRIBE = (_cb) => () => {
651
+ };
652
+ var VIEWPORT_OR_NULL_NO_STORE_SNAPSHOT = () => null;
653
+ function useViewportOrNull() {
654
+ const store = (0, import_react5.useContext)(FlowStoreContext);
655
+ const { sub, snap } = (0, import_react5.useMemo)(
656
+ () => store ? {
657
+ sub: store.subscribe,
658
+ snap: () => store.getSnapshot().viewport
659
+ } : {
660
+ sub: VIEWPORT_OR_NULL_NO_STORE_SUBSCRIBE,
661
+ snap: VIEWPORT_OR_NULL_NO_STORE_SNAPSHOT
662
+ },
663
+ [store]
664
+ );
665
+ return (0, import_react5.useSyncExternalStore)(sub, snap, snap);
666
+ }
649
667
  function useNodeById(id) {
650
668
  return useFlowSelector((s) => s.nodes.find((n) => n.id === id));
651
669
  }
@@ -708,26 +726,27 @@ var SAVE_STATE_DOT = {
708
726
  function ConfigPanel({
709
727
  open = true,
710
728
  title,
711
- kindLabel,
712
729
  icon,
730
+ headerActions,
713
731
  banner,
714
732
  tabs,
715
733
  activeTab: controlledActive,
716
734
  defaultActiveTab,
717
735
  onTabChange,
718
- saveState = "clean",
736
+ saveState,
719
737
  onTitleChange,
720
738
  onClose,
721
739
  onSave,
722
740
  onCancel,
741
+ padding = 12,
723
742
  resizable = true,
724
- defaultWidth = 360,
725
- minWidth = 280,
743
+ defaultWidth = 400,
744
+ minWidth = 400,
726
745
  maxWidth = 720,
727
746
  maxWidthContainerInset = 80,
728
747
  variant = "pinned",
729
748
  children,
730
- footer,
749
+ footerActions,
731
750
  className,
732
751
  style
733
752
  }) {
@@ -824,55 +843,53 @@ function ConfigPanel({
824
843
  "aria-hidden": "true"
825
844
  }
826
845
  ),
827
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "ods-flow-config-panel__header", children: [
828
- kindLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ods-flow-config-panel__breadcrumb", children: kindLabel }),
829
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ods-flow-config-panel__title-row", children: [
830
- icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ods-flow-config-panel__icon", children: icon }),
831
- editingTitle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
832
- "input",
833
- {
834
- className: "ods-flow-config-panel__title-input",
835
- value: draftTitle,
836
- autoFocus: true,
837
- onChange: (e) => setDraftTitle(e.target.value),
838
- onBlur: () => commitTitle(draftTitle),
839
- onKeyDown: titleKeyDown,
840
- "aria-label": "Edit title"
841
- }
842
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
843
- "button",
844
- {
845
- type: "button",
846
- className: "ods-flow-config-panel__title",
847
- onDoubleClick: () => {
848
- if (!onTitleChange) return;
849
- setDraftTitle(title ?? "");
850
- setEditingTitle(true);
851
- },
852
- title: onTitleChange ? "Double-click to rename" : void 0,
853
- children: title ?? "Untitled"
854
- }
855
- ),
856
- onClose && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
857
- "button",
858
- {
859
- type: "button",
860
- className: "ods-flow-config-panel__close",
861
- onClick: onClose,
862
- "aria-label": "Close panel",
863
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
864
- "path",
865
- {
866
- d: "M3 3l8 8M11 3l-8 8",
867
- stroke: "currentColor",
868
- strokeWidth: "1.5",
869
- strokeLinecap: "round"
870
- }
871
- ) })
872
- }
873
- )
874
- ] })
875
- ] }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", { className: "ods-flow-config-panel__header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ods-flow-config-panel__title-row", children: [
847
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ods-flow-config-panel__icon", children: icon }),
848
+ editingTitle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
849
+ "input",
850
+ {
851
+ className: "ods-flow-config-panel__title-input",
852
+ value: draftTitle,
853
+ autoFocus: true,
854
+ onChange: (e) => setDraftTitle(e.target.value),
855
+ onBlur: () => commitTitle(draftTitle),
856
+ onKeyDown: titleKeyDown,
857
+ "aria-label": "Edit title"
858
+ }
859
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
860
+ "button",
861
+ {
862
+ type: "button",
863
+ className: "ods-flow-config-panel__title",
864
+ onDoubleClick: () => {
865
+ if (!onTitleChange) return;
866
+ setDraftTitle(title ?? "");
867
+ setEditingTitle(true);
868
+ },
869
+ title: onTitleChange ? "Double-click to rename" : void 0,
870
+ children: title ?? "Untitled"
871
+ }
872
+ ),
873
+ headerActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ods-flow-config-panel__header-actions", children: headerActions }),
874
+ onClose && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
875
+ "button",
876
+ {
877
+ type: "button",
878
+ className: "ods-flow-config-panel__close",
879
+ onClick: onClose,
880
+ "aria-label": "Close panel",
881
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
882
+ "path",
883
+ {
884
+ d: "M3 3l8 8M11 3l-8 8",
885
+ stroke: "currentColor",
886
+ strokeWidth: "1.5",
887
+ strokeLinecap: "round"
888
+ }
889
+ ) })
890
+ }
891
+ )
892
+ ] }) }),
876
893
  tabs && tabs.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { className: "ods-flow-config-panel__tabs", role: "tablist", children: tabs.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
877
894
  "button",
878
895
  {
@@ -893,9 +910,17 @@ function ConfigPanel({
893
910
  t.id
894
911
  )) }),
895
912
  banner && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ods-flow-config-panel__banner", children: banner }),
896
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ods-flow-config-panel__body", role: "tabpanel", children: tabs ? activeTab?.content : children }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
914
+ "div",
915
+ {
916
+ className: "ods-flow-config-panel__body",
917
+ role: "tabpanel",
918
+ style: { padding },
919
+ children: tabs ? activeTab?.content : children
920
+ }
921
+ ),
897
922
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("footer", { className: "ods-flow-config-panel__footer", children: [
898
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
923
+ saveState && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
899
924
  "span",
900
925
  {
901
926
  className: cn(
@@ -915,7 +940,7 @@ function ConfigPanel({
915
940
  ]
916
941
  }
917
942
  ),
918
- footer ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ods-flow-config-panel__footer-actions", children: [
943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ods-flow-config-panel__footer-actions", children: footerActions ?? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
919
944
  onCancel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
920
945
  "button",
921
946
  {
@@ -931,11 +956,11 @@ function ConfigPanel({
931
956
  type: "button",
932
957
  className: "ods-flow-config-panel__btn ods-flow-config-panel__btn--primary",
933
958
  onClick: onSave,
934
- disabled: saveState === "saving" || saveState === "clean",
959
+ disabled: saveState === "saving",
935
960
  children: saveState === "saving" ? "Saving\u2026" : "Save"
936
961
  }
937
962
  )
938
- ] })
963
+ ] }) })
939
964
  ] })
940
965
  ]
941
966
  }
@@ -943,7 +968,7 @@ function ConfigPanel({
943
968
  }
944
969
 
945
970
  // src/workflow/components/FlowCanvas/FlowCanvas.tsx
946
- var import_react14 = require("react");
971
+ var import_react15 = require("react");
947
972
 
948
973
  // src/workflow/store/createFlowStore.ts
949
974
  var DEFAULT_VIEWPORT = { x: 0, y: 0, zoom: 1 };
@@ -1126,7 +1151,7 @@ function findContainingGroup(point, nodes, exclude = []) {
1126
1151
  for (let i = nodes.length - 1; i >= 0; i--) {
1127
1152
  const n = nodes[i];
1128
1153
  if (exclude.includes(n.id)) continue;
1129
- if (n.type !== "group") continue;
1154
+ if (n.type !== "group" && n.type !== "forEach") continue;
1130
1155
  if (n.data && typeof n.data === "object" && n.data.collapsed) {
1131
1156
  continue;
1132
1157
  }
@@ -1336,6 +1361,9 @@ function createHandleRegistry() {
1336
1361
  resolve(nodeId, type, handleId) {
1337
1362
  return map.get(key(nodeId, type, handleId));
1338
1363
  },
1364
+ all() {
1365
+ return Array.from(map.values());
1366
+ },
1339
1367
  subscribe(listener) {
1340
1368
  listeners.add(listener);
1341
1369
  return () => {
@@ -1376,6 +1404,8 @@ function FlowEdgeImpl({
1376
1404
  const targetDesc = registry.resolve(targetNode.id, "target", targetHandleId);
1377
1405
  const sourceSide = sourceDesc?.side ?? sourceNode.sourcePosition ?? "bottom";
1378
1406
  const targetSide = targetDesc?.side ?? targetNode.targetPosition ?? "top";
1407
+ const sourceRoute = sourceDesc?.routeSide ?? sourceSide;
1408
+ const targetRoute = targetDesc?.routeSide ?? targetSide;
1379
1409
  const sourceIndex = sourceDesc?.index ?? 0;
1380
1410
  const sourceTotal = sourceDesc?.total ?? 1;
1381
1411
  const targetIndex = targetDesc?.index ?? 0;
@@ -1383,14 +1413,14 @@ function FlowEdgeImpl({
1383
1413
  const rawStart = handleCentre(sourceNode, sourceSide, sourceIndex, sourceTotal);
1384
1414
  const rawEnd = handleCentre(targetNode, targetSide, targetIndex, targetTotal);
1385
1415
  const HANDLE_GAP = 8;
1386
- const start = offsetAlongSide2(rawStart, sourceSide, HANDLE_GAP);
1387
- const end = offsetAlongSide2(rawEnd, targetSide, HANDLE_GAP);
1416
+ const start = offsetAlongSide2(rawStart, sourceRoute, HANDLE_GAP);
1417
+ const end = offsetAlongSide2(rawEnd, targetRoute, HANDLE_GAP);
1388
1418
  const routing = edge.routing ?? "bezier";
1389
- const { d, midX, midY } = buildEdgePath(routing, start, sourceSide, end, targetSide, {
1419
+ const { d, midX, midY } = buildEdgePath(routing, start, sourceRoute, end, targetRoute, {
1390
1420
  curvature,
1391
1421
  borderRadius
1392
1422
  });
1393
- const { d: hitD } = buildEdgePath(routing, rawStart, sourceSide, rawEnd, targetSide, {
1423
+ const { d: hitD } = buildEdgePath(routing, rawStart, sourceRoute, rawEnd, targetRoute, {
1394
1424
  curvature,
1395
1425
  borderRadius
1396
1426
  });
@@ -1437,6 +1467,12 @@ function FlowEdgeImpl({
1437
1467
  e.stopPropagation();
1438
1468
  onSelect?.(edge.id);
1439
1469
  },
1470
+ onDoubleClick: (e) => {
1471
+ if (!onLabelChange) return;
1472
+ e.stopPropagation();
1473
+ setDraftLabel(edge.label ?? "");
1474
+ setEditing(true);
1475
+ },
1440
1476
  children: [
1441
1477
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1442
1478
  "marker",
@@ -1708,6 +1744,7 @@ var DEFAULT_HANDLE_ID = "default";
1708
1744
  function Handle({
1709
1745
  type,
1710
1746
  position,
1747
+ routeSide,
1711
1748
  id = DEFAULT_HANDLE_ID,
1712
1749
  isConnectable = true,
1713
1750
  isConnectableStart,
@@ -1730,11 +1767,14 @@ function Handle({
1730
1767
  handleId: id,
1731
1768
  type,
1732
1769
  side: position,
1770
+ routeSide: routeSide ?? position,
1733
1771
  index,
1734
- total
1772
+ total,
1773
+ canStart,
1774
+ canEnd
1735
1775
  });
1736
1776
  return dispose;
1737
- }, [registry, node.id, id, type, position, index, total]);
1777
+ }, [registry, node.id, id, type, position, routeSide, index, total, canStart, canEnd]);
1738
1778
  const handlePointerDown = (e) => {
1739
1779
  if (!canStart) return;
1740
1780
  e.stopPropagation();
@@ -1791,10 +1831,155 @@ function handleSideStyle(side, index, total) {
1791
1831
  }
1792
1832
  }
1793
1833
 
1794
- // src/workflow/components/kinds/BaseNode.tsx
1795
- var import_icons = require("@octaviaflow/icons");
1834
+ // src/workflow/components/NodeResizer/NodeResizer.tsx
1796
1835
  var import_react13 = require("react");
1797
1836
  var import_jsx_runtime5 = require("react/jsx-runtime");
1837
+ function NodeResizer({
1838
+ isVisible,
1839
+ minWidth = 80,
1840
+ minHeight = 60,
1841
+ maxWidth,
1842
+ maxHeight,
1843
+ keepAspectRatio = false,
1844
+ onResize,
1845
+ onResizeEnd,
1846
+ color
1847
+ }) {
1848
+ const { node, selected } = useFlowNodeContext();
1849
+ const viewport = useViewport();
1850
+ const flow = useFlow();
1851
+ const dragRef = (0, import_react13.useRef)(null);
1852
+ const show = isVisible ?? selected;
1853
+ if (!show) return null;
1854
+ const beginResize = (e, corner) => {
1855
+ e.preventDefault();
1856
+ e.stopPropagation();
1857
+ e.target.setPointerCapture(e.pointerId);
1858
+ const w = node.width ?? DEFAULT_NODE_WIDTH;
1859
+ const h = node.height ?? DEFAULT_NODE_HEIGHT;
1860
+ dragRef.current = {
1861
+ pointerId: e.pointerId,
1862
+ corner,
1863
+ startClientX: e.clientX,
1864
+ startClientY: e.clientY,
1865
+ startWidth: w,
1866
+ startHeight: h,
1867
+ startX: node.position.x,
1868
+ startY: node.position.y,
1869
+ aspect: w / Math.max(1, h)
1870
+ };
1871
+ };
1872
+ const onMove = (e) => {
1873
+ const drag = dragRef.current;
1874
+ if (!drag || drag.pointerId !== e.pointerId) return;
1875
+ const dx = (e.clientX - drag.startClientX) / viewport.zoom;
1876
+ const dy = (e.clientY - drag.startClientY) / viewport.zoom;
1877
+ let nextW = drag.startWidth;
1878
+ let nextH = drag.startHeight;
1879
+ let nextX = drag.startX;
1880
+ let nextY = drag.startY;
1881
+ switch (drag.corner) {
1882
+ case "se":
1883
+ nextW = drag.startWidth + dx;
1884
+ nextH = drag.startHeight + dy;
1885
+ break;
1886
+ case "sw":
1887
+ nextW = drag.startWidth - dx;
1888
+ nextH = drag.startHeight + dy;
1889
+ nextX = drag.startX + dx;
1890
+ break;
1891
+ case "ne":
1892
+ nextW = drag.startWidth + dx;
1893
+ nextH = drag.startHeight - dy;
1894
+ nextY = drag.startY + dy;
1895
+ break;
1896
+ case "nw":
1897
+ nextW = drag.startWidth - dx;
1898
+ nextH = drag.startHeight - dy;
1899
+ nextX = drag.startX + dx;
1900
+ nextY = drag.startY + dy;
1901
+ break;
1902
+ }
1903
+ if (keepAspectRatio) {
1904
+ nextH = nextW / drag.aspect;
1905
+ if (drag.corner === "nw" || drag.corner === "ne") {
1906
+ nextY = drag.startY + (drag.startHeight - nextH);
1907
+ }
1908
+ }
1909
+ nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);
1910
+ nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);
1911
+ flow.updateNode(node.id, {
1912
+ width: nextW,
1913
+ height: nextH,
1914
+ position: { x: nextX, y: nextY }
1915
+ });
1916
+ onResize?.({ width: nextW, height: nextH });
1917
+ };
1918
+ const onUp = (e) => {
1919
+ if (dragRef.current?.pointerId === e.pointerId) {
1920
+ const cur = flow.getNode(node.id);
1921
+ if (cur) {
1922
+ onResizeEnd?.({
1923
+ width: cur.width ?? DEFAULT_NODE_WIDTH,
1924
+ height: cur.height ?? DEFAULT_NODE_HEIGHT
1925
+ });
1926
+ }
1927
+ dragRef.current = null;
1928
+ }
1929
+ };
1930
+ const handleColor = color ?? "var(--ods-accent)";
1931
+ const handleStyle = (corner) => {
1932
+ const base = {
1933
+ position: "absolute",
1934
+ width: 12,
1935
+ height: 12,
1936
+ background: "var(--ods-surface-canvas)",
1937
+ border: `2px solid ${handleColor}`,
1938
+ borderRadius: 2,
1939
+ cursor: cursorFor(corner),
1940
+ touchAction: "none",
1941
+ // Place each handle so its CENTRE sits on the corresponding corner.
1942
+ transform: "translate(-50%, -50%)"
1943
+ };
1944
+ switch (corner) {
1945
+ case "nw":
1946
+ return { ...base, top: 0, left: 0 };
1947
+ case "ne":
1948
+ return { ...base, top: 0, left: "100%" };
1949
+ case "sw":
1950
+ return { ...base, top: "100%", left: 0 };
1951
+ case "se":
1952
+ return { ...base, top: "100%", left: "100%" };
1953
+ }
1954
+ };
1955
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn("ods-node-resizer"), "data-flow-no-drag": "true", children: ["nw", "ne", "sw", "se"].map((corner) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1956
+ "div",
1957
+ {
1958
+ style: handleStyle(corner),
1959
+ onPointerDown: (e) => beginResize(e, corner),
1960
+ onPointerMove: onMove,
1961
+ onPointerUp: onUp,
1962
+ onPointerCancel: onUp,
1963
+ "aria-label": `Resize ${corner}`
1964
+ },
1965
+ corner
1966
+ )) });
1967
+ }
1968
+ function cursorFor(corner) {
1969
+ switch (corner) {
1970
+ case "nw":
1971
+ case "se":
1972
+ return "nwse-resize";
1973
+ case "ne":
1974
+ case "sw":
1975
+ return "nesw-resize";
1976
+ }
1977
+ }
1978
+
1979
+ // src/workflow/components/kinds/BaseNode.tsx
1980
+ var import_icons = require("@octaviaflow/icons");
1981
+ var import_react14 = require("react");
1982
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1798
1983
  function BaseNode({
1799
1984
  kind,
1800
1985
  kindIcon,
@@ -1810,10 +1995,10 @@ function BaseNode({
1810
1995
  className,
1811
1996
  children
1812
1997
  }) {
1813
- const ctx = (0, import_react13.useContext)(FlowNodeContext);
1814
- const bridge = (0, import_react13.useContext)(FlowNodeBridgeContext);
1998
+ const ctx = (0, import_react14.useContext)(FlowNodeContext);
1999
+ const bridge = (0, import_react14.useContext)(FlowNodeBridgeContext);
1815
2000
  const deleteHandler = onDelete === false ? void 0 : onDelete ?? (ctx && bridge ? () => bridge.deleteNode(ctx.id) : void 0);
1816
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2001
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1817
2002
  "div",
1818
2003
  {
1819
2004
  className: cn(
@@ -1823,31 +2008,31 @@ function BaseNode({
1823
2008
  className
1824
2009
  ),
1825
2010
  children: [
1826
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ods-flow-base-node__pill", children: [
1827
- kindIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ods-flow-base-node__pill-icon", "aria-hidden": "true", children: kindIcon }),
1828
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ods-flow-base-node__pill-label", children: kind })
2011
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-base-node__pill", children: [
2012
+ kindIcon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-base-node__pill-icon", "aria-hidden": "true", children: kindIcon }),
2013
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-base-node__pill-label", children: kind })
1829
2014
  ] }),
1830
- status && status !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2015
+ status && status !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1831
2016
  "span",
1832
2017
  {
1833
2018
  className: cn("ods-flow-base-node__status", `ods-flow-base-node__status--${status}`),
1834
2019
  "aria-hidden": "true"
1835
2020
  }
1836
2021
  ),
1837
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ods-flow-base-node__body", children: [
1838
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ods-flow-base-node__content", children: [
1839
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ods-flow-base-node__bubble", "aria-hidden": "true", children: icon }),
1840
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ods-flow-base-node__content-text", children: [
1841
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ods-flow-base-node__content-title", children: title }),
1842
- (chip !== void 0 || description !== void 0 || valueChip !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ods-flow-base-node__content-info", children: [
1843
- chip !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ods-flow-base-node__chip", children: chip }),
1844
- description !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ods-flow-base-node__description", children: description }),
1845
- valueChip !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ods-flow-base-node__value-chip", children: valueChip })
2022
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-base-node__body", children: [
2023
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-base-node__content", children: [
2024
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ods-flow-base-node__bubble", "aria-hidden": "true", children: icon }),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-base-node__content-text", children: [
2026
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ods-flow-base-node__content-title", children: title }),
2027
+ (chip !== void 0 || description !== void 0 || valueChip !== void 0) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-base-node__content-info", children: [
2028
+ chip !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-base-node__chip", children: chip }),
2029
+ description !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-base-node__description", children: description }),
2030
+ valueChip !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-base-node__value-chip", children: valueChip })
1846
2031
  ] })
1847
2032
  ] })
1848
2033
  ] }),
1849
- footer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ods-flow-base-node__footer", children: footer }),
1850
- deleteHandler && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2034
+ footer && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ods-flow-base-node__footer", children: footer }),
2035
+ deleteHandler && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1851
2036
  "button",
1852
2037
  {
1853
2038
  type: "button",
@@ -1859,7 +2044,7 @@ function BaseNode({
1859
2044
  "aria-label": "Delete node",
1860
2045
  "data-flow-no-drag": "true",
1861
2046
  title: "Delete node",
1862
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_icons.TrashCanIcon, { size: 16, "aria-hidden": true })
2047
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons.TrashCanIcon, { size: 16, "aria-hidden": true })
1863
2048
  }
1864
2049
  )
1865
2050
  ] }),
@@ -1870,12 +2055,12 @@ function BaseNode({
1870
2055
  }
1871
2056
 
1872
2057
  // src/workflow/components/kinds/index.tsx
1873
- var import_jsx_runtime6 = require("react/jsx-runtime");
2058
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1874
2059
  var ActionNode = ({
1875
2060
  node
1876
2061
  }) => {
1877
2062
  const d = node.data ?? {};
1878
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2063
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1879
2064
  BaseNode,
1880
2065
  {
1881
2066
  kind: d.kind ?? "ACTION",
@@ -1887,8 +2072,8 @@ var ActionNode = ({
1887
2072
  status: d.status,
1888
2073
  accent: "green",
1889
2074
  children: [
1890
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
1891
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2075
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
1892
2077
  ]
1893
2078
  }
1894
2079
  );
@@ -1897,7 +2082,7 @@ var TriggerNode = ({
1897
2082
  node
1898
2083
  }) => {
1899
2084
  const d = node.data ?? {};
1900
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2085
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1901
2086
  BaseNode,
1902
2087
  {
1903
2088
  kind: d.kind ?? "TRIGGER",
@@ -1908,7 +2093,7 @@ var TriggerNode = ({
1908
2093
  valueChip: d.valueChip,
1909
2094
  status: d.status,
1910
2095
  accent: "green",
1911
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2096
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
1912
2097
  }
1913
2098
  );
1914
2099
  };
@@ -1920,7 +2105,7 @@ var ConditionNode = ({
1920
2105
  { id: "true", label: "true" },
1921
2106
  { id: "false", label: "false" }
1922
2107
  ];
1923
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1924
2109
  BaseNode,
1925
2110
  {
1926
2111
  kind: d.kind ?? "CONDITION",
@@ -1932,8 +2117,8 @@ var ConditionNode = ({
1932
2117
  status: d.status,
1933
2118
  accent: "amber",
1934
2119
  children: [
1935
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
1936
- branches.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2120
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2121
+ branches.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1937
2122
  Handle,
1938
2123
  {
1939
2124
  type: "source",
@@ -1954,24 +2139,27 @@ var GroupNode = ({
1954
2139
  }) => {
1955
2140
  const d = node.data ?? {};
1956
2141
  const collapsed = !!d.collapsed;
2142
+ const disabled = !!d.disabled;
1957
2143
  const hiddenCount = d.hiddenCount;
1958
2144
  const bridge = useFlowNodeBridge();
1959
2145
  const onChevronClick = (e) => {
1960
2146
  e.stopPropagation();
1961
2147
  bridge.toggleNodeCollapse(node.id);
1962
2148
  };
1963
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2149
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1964
2150
  "div",
1965
2151
  {
1966
2152
  className: "ods-flow-group",
1967
2153
  "data-collapsed": collapsed ? "true" : "false",
2154
+ "data-disabled": disabled ? "true" : void 0,
1968
2155
  style: {
1969
2156
  width: node.width ?? 360,
1970
2157
  height: collapsed ? 36 : node.height ?? 200
1971
2158
  },
1972
2159
  children: [
1973
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-group__header", "data-flow-no-drag": "false", children: [
1974
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2160
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NodeResizer, { minWidth: 240, minHeight: 120 }),
2161
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ods-flow-group__header", "data-flow-no-drag": "false", children: [
2162
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1975
2163
  "button",
1976
2164
  {
1977
2165
  type: "button",
@@ -1984,15 +2172,16 @@ var GroupNode = ({
1984
2172
  children: collapsed ? "\u25B8" : "\u25BE"
1985
2173
  }
1986
2174
  ),
1987
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-group__title", children: d.title ?? "Group" }),
1988
- d.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-group__subtitle", children: d.subtitle }),
1989
- collapsed && hiddenCount !== void 0 && hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ods-flow-group__count", "aria-label": `${hiddenCount} hidden steps`, children: [
2175
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ods-flow-group__title", children: d.title ?? "Group" }),
2176
+ d.subtitle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ods-flow-group__subtitle", children: d.subtitle }),
2177
+ disabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ods-flow-group__disabled-badge", "aria-label": "Disabled subflow", children: "Disabled" }),
2178
+ collapsed && hiddenCount !== void 0 && hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "ods-flow-group__count", "aria-label": `${hiddenCount} hidden steps`, children: [
1990
2179
  hiddenCount,
1991
2180
  " steps"
1992
2181
  ] })
1993
2182
  ] }),
1994
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top", id: "__group_in" }),
1995
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom", id: "__group_out" })
2183
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top", id: "__group_in" }),
2184
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom", id: "__group_out" })
1996
2185
  ]
1997
2186
  }
1998
2187
  );
@@ -2003,24 +2192,27 @@ var ForEachNode = ({
2003
2192
  const d = node.data ?? {};
2004
2193
  const iteratorExpr = d.iterator ?? d.description ?? "items[]";
2005
2194
  const collapsed = !!d.collapsed;
2195
+ const disabled = !!d.disabled;
2006
2196
  const hiddenCount = d.hiddenCount;
2007
2197
  const bridge = useFlowNodeBridge();
2008
2198
  const onChevronClick = (e) => {
2009
2199
  e.stopPropagation();
2010
2200
  bridge.toggleNodeCollapse(node.id);
2011
2201
  };
2012
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2202
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2013
2203
  "div",
2014
2204
  {
2015
2205
  className: "ods-flow-foreach",
2016
2206
  "data-collapsed": collapsed ? "true" : "false",
2207
+ "data-disabled": disabled ? "true" : void 0,
2017
2208
  style: {
2018
2209
  width: node.width ?? 420,
2019
2210
  height: collapsed ? 40 : node.height ?? 260
2020
2211
  },
2021
2212
  children: [
2022
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ods-flow-foreach__header", children: [
2023
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2213
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NodeResizer, { minWidth: 240, minHeight: 120 }),
2214
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ods-flow-foreach__header", children: [
2215
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2024
2216
  "button",
2025
2217
  {
2026
2218
  type: "button",
@@ -2033,24 +2225,57 @@ var ForEachNode = ({
2033
2225
  children: collapsed ? "\u25B8" : "\u25BE"
2034
2226
  }
2035
2227
  ),
2036
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-foreach__icon", "aria-hidden": "true", children: "\u21BB" }),
2037
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ods-flow-foreach__title", children: d.title ?? "For each" }),
2038
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: "ods-flow-foreach__iterator", children: iteratorExpr }),
2039
- collapsed && hiddenCount !== void 0 && hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ods-flow-foreach__count", "aria-label": `${hiddenCount} hidden steps`, children: [
2228
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ods-flow-foreach__icon", "aria-hidden": "true", children: "\u21BB" }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ods-flow-foreach__title", children: d.title ?? "For each" }),
2230
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "ods-flow-foreach__iterator", children: iteratorExpr }),
2231
+ disabled && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2232
+ "span",
2233
+ {
2234
+ className: "ods-flow-foreach__disabled-badge",
2235
+ "aria-label": "Disabled subflow",
2236
+ children: "Disabled"
2237
+ }
2238
+ ),
2239
+ collapsed && hiddenCount !== void 0 && hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "ods-flow-foreach__count", "aria-label": `${hiddenCount} hidden steps`, children: [
2040
2240
  hiddenCount,
2041
2241
  " steps"
2042
2242
  ] })
2043
2243
  ] }),
2044
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top", id: "__group_in" }),
2045
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom", id: "__group_out" }),
2046
- !collapsed && // Expanded mode full 3-handle iterator surface (in / item / done).
2047
- // `in` shares the top edge with `__group_in`; consumers wiring an
2048
- // explicit `targetHandle: "in"` get the labelled, visible variant.
2049
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2050
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top", id: "in", label: "in" }),
2051
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "right", id: "item", label: "item" }),
2052
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom", id: "done", label: "done" })
2053
- ] })
2244
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top", id: "__group_in", label: !collapsed ? "in" : void 0 }),
2245
+ collapsed ? (
2246
+ // Collapsed: single bottom exit so the container behaves like a
2247
+ // regular action in the chain.
2248
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom", id: "__group_out" })
2249
+ ) : (
2250
+ // Expanded: two bottom outputs spread across the bottom edge.
2251
+ // Index/total tell the Handle to position them at 33% and 66%
2252
+ // along the edge (per `handleSideStyle`). `each` is left of
2253
+ // centre, `__group_out` (labelled "done") is right of centre.
2254
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2255
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2256
+ Handle,
2257
+ {
2258
+ type: "source",
2259
+ position: "bottom",
2260
+ id: "each",
2261
+ label: "each",
2262
+ index: 0,
2263
+ total: 2
2264
+ }
2265
+ ),
2266
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2267
+ Handle,
2268
+ {
2269
+ type: "source",
2270
+ position: "bottom",
2271
+ id: "__group_out",
2272
+ label: "done",
2273
+ index: 1,
2274
+ total: 2
2275
+ }
2276
+ )
2277
+ ] })
2278
+ )
2054
2279
  ]
2055
2280
  }
2056
2281
  );
@@ -2059,7 +2284,7 @@ var OutputNode = ({
2059
2284
  node
2060
2285
  }) => {
2061
2286
  const d = node.data ?? {};
2062
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2287
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2063
2288
  BaseNode,
2064
2289
  {
2065
2290
  kind: d.kind ?? "OUTPUT",
@@ -2069,7 +2294,7 @@ var OutputNode = ({
2069
2294
  description: d.description ?? d.subtitle,
2070
2295
  status: d.status,
2071
2296
  accent: "green",
2072
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" })
2297
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" })
2073
2298
  }
2074
2299
  );
2075
2300
  };
@@ -2077,7 +2302,7 @@ var ErrorNode = ({
2077
2302
  node
2078
2303
  }) => {
2079
2304
  const d = node.data ?? {};
2080
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2305
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2081
2306
  BaseNode,
2082
2307
  {
2083
2308
  kind: d.kind ?? "ERROR",
@@ -2088,8 +2313,8 @@ var ErrorNode = ({
2088
2313
  status: d.status ?? "error",
2089
2314
  accent: "red",
2090
2315
  children: [
2091
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
2092
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2316
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2317
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
2093
2318
  ]
2094
2319
  }
2095
2320
  );
@@ -2100,7 +2325,7 @@ var WaitNode = ({
2100
2325
  const d = node.data ?? {};
2101
2326
  const waitMs = d.waitMs;
2102
2327
  const durationChip = waitMs ? `${Math.round(waitMs / 100) / 10}s` : void 0;
2103
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2328
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2104
2329
  BaseNode,
2105
2330
  {
2106
2331
  kind: d.kind ?? "WAIT",
@@ -2111,8 +2336,8 @@ var WaitNode = ({
2111
2336
  status: d.status,
2112
2337
  accent: "violet",
2113
2338
  children: [
2114
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
2115
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2339
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2340
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
2116
2341
  ]
2117
2342
  }
2118
2343
  );
@@ -2125,7 +2350,7 @@ var ParallelNode = ({
2125
2350
  { id: "a", label: "a" },
2126
2351
  { id: "b", label: "b" }
2127
2352
  ];
2128
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2353
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2129
2354
  BaseNode,
2130
2355
  {
2131
2356
  kind: d.kind ?? "PARALLEL",
@@ -2136,8 +2361,8 @@ var ParallelNode = ({
2136
2361
  status: d.status,
2137
2362
  accent: "blue",
2138
2363
  children: [
2139
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
2140
- branches.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2364
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2365
+ branches.map((b, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2141
2366
  Handle,
2142
2367
  {
2143
2368
  type: "source",
@@ -2157,7 +2382,7 @@ var StickyNode = ({
2157
2382
  node
2158
2383
  }) => {
2159
2384
  const d = node.data ?? {};
2160
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2385
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2161
2386
  "div",
2162
2387
  {
2163
2388
  className: "ods-flow-sticky",
@@ -2166,8 +2391,8 @@ var StickyNode = ({
2166
2391
  minHeight: node.height ?? 120
2167
2392
  },
2168
2393
  children: [
2169
- d.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ods-flow-sticky__title", children: d.title }),
2170
- d.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ods-flow-sticky__body", children: d.description })
2394
+ d.title && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ods-flow-sticky__title", children: d.title }),
2395
+ d.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ods-flow-sticky__body", children: d.description })
2171
2396
  ]
2172
2397
  }
2173
2398
  );
@@ -2176,7 +2401,7 @@ var WebhookNode = ({
2176
2401
  node
2177
2402
  }) => {
2178
2403
  const d = node.data ?? {};
2179
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2404
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2180
2405
  BaseNode,
2181
2406
  {
2182
2407
  kind: d.kind ?? "WEBHOOK",
@@ -2187,7 +2412,7 @@ var WebhookNode = ({
2187
2412
  valueChip: d.valueChip,
2188
2413
  status: d.status,
2189
2414
  accent: "blue",
2190
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2415
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
2191
2416
  }
2192
2417
  );
2193
2418
  };
@@ -2195,7 +2420,7 @@ var HttpRequestNode = ({
2195
2420
  node
2196
2421
  }) => {
2197
2422
  const d = node.data ?? {};
2198
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2423
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2199
2424
  BaseNode,
2200
2425
  {
2201
2426
  kind: d.kind ?? "HTTP",
@@ -2207,8 +2432,8 @@ var HttpRequestNode = ({
2207
2432
  status: d.status,
2208
2433
  accent: "blue",
2209
2434
  children: [
2210
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "target", position: "top" }),
2211
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Handle, { type: "source", position: "bottom" })
2435
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "target", position: "top" }),
2436
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Handle, { type: "source", position: "bottom" })
2212
2437
  ]
2213
2438
  }
2214
2439
  );
@@ -2229,52 +2454,60 @@ var DEFAULT_NODE_KINDS = {
2229
2454
  };
2230
2455
 
2231
2456
  // src/workflow/components/FlowCanvas/FlowCanvas.tsx
2232
- var import_jsx_runtime7 = require("react/jsx-runtime");
2457
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2233
2458
  var DEFAULT_VIEWPORT2 = { x: 0, y: 0, zoom: 1 };
2234
- function FlowCanvas({
2235
- nodes,
2236
- edges,
2237
- onNodesChange,
2238
- onEdgesChange,
2239
- viewport: controlledViewport,
2240
- defaultViewport = DEFAULT_VIEWPORT2,
2241
- onViewportChange,
2242
- minZoom = 0.25,
2243
- maxZoom = 2,
2244
- nodeKinds,
2245
- onConnect,
2246
- onConnectStart,
2247
- onConnectEnd,
2248
- isValidConnection,
2249
- onSelectionChange,
2250
- onPaneClick,
2251
- onNodeClick,
2252
- onEdgeClick,
2253
- onEdgeLabelChange,
2254
- onInit,
2255
- onBeforeDelete,
2256
- onNodeContextMenu,
2257
- onEdgeContextMenu,
2258
- onPaneContextMenu,
2259
- nodesDraggable = true,
2260
- nodesConnectable = true,
2261
- panOnDrag: panOnDragProp,
2262
- zoomOnScroll: zoomOnScrollProp,
2263
- preset = "mouse",
2264
- paneClickDistance = 4,
2265
- paneClickClearsSelection = true,
2266
- background = "dots",
2267
- gridSize = 20,
2268
- snapToGrid = false,
2269
- nodeCollisionGap = -1,
2270
- subflowCollisionGap,
2271
- height = "100%",
2272
- width = "100%",
2273
- className,
2274
- style,
2275
- children,
2276
- emptyState
2277
- }) {
2459
+ function FlowCanvas(props) {
2460
+ const viewportPropProvided = props.viewport !== void 0 || props.defaultViewport !== void 0;
2461
+ const {
2462
+ nodes,
2463
+ edges,
2464
+ onNodesChange,
2465
+ onEdgesChange,
2466
+ viewport: controlledViewport,
2467
+ defaultViewport = DEFAULT_VIEWPORT2,
2468
+ onViewportChange,
2469
+ minZoom = 0.25,
2470
+ maxZoom = 2,
2471
+ fitViewOnInit,
2472
+ nodeKinds,
2473
+ onConnect,
2474
+ onConnectStart,
2475
+ onConnectEnd,
2476
+ isValidConnection,
2477
+ onSelectionChange,
2478
+ onPaneClick,
2479
+ onNodeClick,
2480
+ onEdgeClick,
2481
+ onEdgeLabelChange,
2482
+ onInit,
2483
+ onBeforeDelete,
2484
+ onNodeContextMenu,
2485
+ onEdgeContextMenu,
2486
+ onPaneContextMenu,
2487
+ nodesDraggable = true,
2488
+ nodesConnectable = true,
2489
+ panOnDrag: panOnDragProp,
2490
+ zoomOnScroll: zoomOnScrollProp,
2491
+ preset = "mouse",
2492
+ paneClickDistance = 4,
2493
+ paneClickClearsSelection = true,
2494
+ background = "dots",
2495
+ gridSize = 20,
2496
+ snapToGrid = false,
2497
+ nodeCollisionGap = -1,
2498
+ subflowCollisionGap,
2499
+ reparentOnDrag = false,
2500
+ autoResizeContainers = false,
2501
+ containerAutoResizePadding = 32,
2502
+ containerMinWidth = 320,
2503
+ containerMinHeight = 200,
2504
+ height = "100%",
2505
+ width = "100%",
2506
+ className,
2507
+ style,
2508
+ children,
2509
+ emptyState
2510
+ } = props;
2278
2511
  const presetDefaults = {
2279
2512
  mouse: { panOnDrag: true, zoomOnScroll: true },
2280
2513
  trackpad: { panOnDrag: true, zoomOnScroll: true },
@@ -2282,46 +2515,46 @@ function FlowCanvas({
2282
2515
  };
2283
2516
  const panOnDrag = panOnDragProp ?? presetDefaults[preset].panOnDrag;
2284
2517
  const zoomOnScroll = zoomOnScrollProp ?? presetDefaults[preset].zoomOnScroll;
2285
- const store = (0, import_react14.useState)(
2518
+ const store = (0, import_react15.useState)(
2286
2519
  () => createFlowStore({
2287
2520
  initialNodes: nodes,
2288
2521
  initialEdges: edges,
2289
2522
  initialViewport: controlledViewport ?? defaultViewport
2290
2523
  })
2291
2524
  )[0];
2292
- const handleRegistry = (0, import_react14.useState)(() => createHandleRegistry())[0];
2293
- const [handleVersion, setHandleVersion] = (0, import_react14.useState)(0);
2294
- (0, import_react14.useEffect)(() => {
2525
+ const handleRegistry = (0, import_react15.useState)(() => createHandleRegistry())[0];
2526
+ const [handleVersion, setHandleVersion] = (0, import_react15.useState)(0);
2527
+ (0, import_react15.useEffect)(() => {
2295
2528
  const unsub = handleRegistry.subscribe(() => {
2296
2529
  setHandleVersion((v) => v + 1);
2297
2530
  });
2298
2531
  return unsub;
2299
2532
  }, [handleRegistry]);
2300
- const kinds = (0, import_react14.useMemo)(() => buildNodeKindRegistry(DEFAULT_NODE_KINDS, nodeKinds), [nodeKinds]);
2301
- const containerRef = (0, import_react14.useRef)(null);
2302
- (0, import_react14.useEffect)(() => store.setNodes(nodes), [store, nodes]);
2303
- (0, import_react14.useEffect)(() => store.setEdges(edges), [store, edges]);
2304
- const [uncontrolledVp, setUncontrolledVp] = (0, import_react14.useState)(controlledViewport ?? defaultViewport);
2533
+ const kinds = (0, import_react15.useMemo)(() => buildNodeKindRegistry(DEFAULT_NODE_KINDS, nodeKinds), [nodeKinds]);
2534
+ const containerRef = (0, import_react15.useRef)(null);
2535
+ (0, import_react15.useEffect)(() => store.setNodes(nodes), [store, nodes]);
2536
+ (0, import_react15.useEffect)(() => store.setEdges(edges), [store, edges]);
2537
+ const [uncontrolledVp, setUncontrolledVp] = (0, import_react15.useState)(controlledViewport ?? defaultViewport);
2305
2538
  const viewport = controlledViewport ?? uncontrolledVp;
2306
- (0, import_react14.useEffect)(() => store.setViewport(viewport), [store, viewport]);
2307
- const setViewport = (0, import_react14.useCallback)(
2539
+ (0, import_react15.useEffect)(() => store.setViewport(viewport), [store, viewport]);
2540
+ const setViewport = (0, import_react15.useCallback)(
2308
2541
  (next) => {
2309
2542
  if (controlledViewport === void 0) setUncontrolledVp(next);
2310
2543
  onViewportChange?.(next);
2311
2544
  },
2312
2545
  [controlledViewport, onViewportChange]
2313
2546
  );
2314
- const selectedNodeIds = (0, import_react14.useMemo)(() => {
2547
+ const selectedNodeIds = (0, import_react15.useMemo)(() => {
2315
2548
  const s = /* @__PURE__ */ new Set();
2316
2549
  for (const n of nodes) if (n.selected) s.add(n.id);
2317
2550
  return s;
2318
2551
  }, [nodes]);
2319
- const selectedEdgeIds = (0, import_react14.useMemo)(() => {
2552
+ const selectedEdgeIds = (0, import_react15.useMemo)(() => {
2320
2553
  const s = /* @__PURE__ */ new Set();
2321
2554
  for (const e of edges) if (e.selected) s.add(e.id);
2322
2555
  return s;
2323
2556
  }, [edges]);
2324
- (0, import_react14.useEffect)(() => {
2557
+ (0, import_react15.useEffect)(() => {
2325
2558
  store.setSelection(selectedNodeIds, selectedEdgeIds);
2326
2559
  if (onSelectionChange) {
2327
2560
  onSelectionChange({
@@ -2330,7 +2563,7 @@ function FlowCanvas({
2330
2563
  });
2331
2564
  }
2332
2565
  }, [store, selectedNodeIds, selectedEdgeIds, nodes, edges, onSelectionChange]);
2333
- const selectNode = (0, import_react14.useCallback)(
2566
+ const selectNode = (0, import_react15.useCallback)(
2334
2567
  (id, additive) => {
2335
2568
  const next = [];
2336
2569
  const nextEdges = [];
@@ -2353,14 +2586,14 @@ function FlowCanvas({
2353
2586
  },
2354
2587
  [nodes, edges, selectedNodeIds, onNodesChange, onEdgesChange]
2355
2588
  );
2356
- const notifyNodeClick = (0, import_react14.useCallback)(
2589
+ const notifyNodeClick = (0, import_react15.useCallback)(
2357
2590
  (id) => {
2358
2591
  const node = nodes.find((n) => n.id === id);
2359
2592
  if (node) onNodeClick?.(node);
2360
2593
  },
2361
2594
  [nodes, onNodeClick]
2362
2595
  );
2363
- const selectEdge = (0, import_react14.useCallback)(
2596
+ const selectEdge = (0, import_react15.useCallback)(
2364
2597
  (id, additive) => {
2365
2598
  const next = [];
2366
2599
  const nextNodes = [];
@@ -2385,7 +2618,7 @@ function FlowCanvas({
2385
2618
  },
2386
2619
  [nodes, edges, selectedEdgeIds, onEdgesChange, onNodesChange, onEdgeClick]
2387
2620
  );
2388
- const clearSelection = (0, import_react14.useCallback)(() => {
2621
+ const clearSelection = (0, import_react15.useCallback)(() => {
2389
2622
  const ns = [];
2390
2623
  const es = [];
2391
2624
  for (const n of nodes) if (n.selected) ns.push(change.node.select(n.id, false));
@@ -2393,9 +2626,9 @@ function FlowCanvas({
2393
2626
  if (ns.length) onNodesChange?.(ns);
2394
2627
  if (es.length) onEdgesChange?.(es);
2395
2628
  }, [nodes, edges, onNodesChange, onEdgesChange]);
2396
- const dragRef = (0, import_react14.useRef)(null);
2397
- const [draggingId, setDraggingId] = (0, import_react14.useState)(null);
2398
- const beginNodeDrag = (0, import_react14.useCallback)(
2629
+ const dragRef = (0, import_react15.useRef)(null);
2630
+ const [draggingId, setDraggingId] = (0, import_react15.useState)(null);
2631
+ const beginNodeDrag = (0, import_react15.useCallback)(
2399
2632
  (nodeId, pointerId, clientX, clientY, altKey = false) => {
2400
2633
  if (!nodesDraggable) return;
2401
2634
  const node = nodes.find((n) => n.id === nodeId);
@@ -2404,6 +2637,9 @@ function FlowCanvas({
2404
2637
  id: d.id,
2405
2638
  startPosition: d.position
2406
2639
  }));
2640
+ const evaluateReparent = altKey || reparentOnDragRef.current;
2641
+ const hasParent = !!node.parentId;
2642
+ const skipClamp = altKey || reparentOnDragRef.current && !hasParent;
2407
2643
  dragRef.current = {
2408
2644
  pointerId,
2409
2645
  nodeId,
@@ -2411,7 +2647,8 @@ function FlowCanvas({
2411
2647
  startClientY: clientY,
2412
2648
  startPosition: node.position,
2413
2649
  descendants: kids,
2414
- altDetach: altKey && !!node.parentId,
2650
+ skipClamp,
2651
+ evaluateReparent,
2415
2652
  rafScheduled: false,
2416
2653
  nextDelta: null
2417
2654
  };
@@ -2420,12 +2657,12 @@ function FlowCanvas({
2420
2657
  },
2421
2658
  [nodes, nodesDraggable, selectNode]
2422
2659
  );
2423
- const [conn, setConn] = (0, import_react14.useState)(null);
2424
- const connRef = (0, import_react14.useRef)(null);
2425
- (0, import_react14.useEffect)(() => {
2660
+ const [conn, setConn] = (0, import_react15.useState)(null);
2661
+ const connRef = (0, import_react15.useRef)(null);
2662
+ (0, import_react15.useEffect)(() => {
2426
2663
  connRef.current = conn;
2427
2664
  }, [conn]);
2428
- const beginConnection = (0, import_react14.useCallback)(
2665
+ const beginConnection = (0, import_react15.useCallback)(
2429
2666
  (nodeId, handleId, handleType, pointerId, clientX, clientY) => {
2430
2667
  if (!nodesConnectable) return;
2431
2668
  const node = nodes.find((n) => n.id === nodeId);
@@ -2458,63 +2695,83 @@ function FlowCanvas({
2458
2695
  },
2459
2696
  [nodes, nodesConnectable, handleRegistry, viewport, store, onConnectStart]
2460
2697
  );
2461
- const viewportRef = (0, import_react14.useRef)(viewport);
2462
- const nodesRef = (0, import_react14.useRef)(nodes);
2463
- const edgesRef = (0, import_react14.useRef)(edges);
2464
- const onNodesChangeRefForInstance = (0, import_react14.useRef)(onNodesChange);
2465
- const onEdgesChangeRefForInstance = (0, import_react14.useRef)(onEdgesChange);
2466
- const onBeforeDeleteRef = (0, import_react14.useRef)(onBeforeDelete);
2467
- const snapToGridRef = (0, import_react14.useRef)(snapToGrid);
2468
- const gridSizeRef = (0, import_react14.useRef)(gridSize);
2469
- const nodeCollisionGapRef = (0, import_react14.useRef)(nodeCollisionGap);
2470
- const subflowCollisionGapRef = (0, import_react14.useRef)(subflowCollisionGap ?? nodeCollisionGap);
2471
- (0, import_react14.useEffect)(() => {
2698
+ const viewportRef = (0, import_react15.useRef)(viewport);
2699
+ const nodesRef = (0, import_react15.useRef)(nodes);
2700
+ const edgesRef = (0, import_react15.useRef)(edges);
2701
+ const onNodesChangeRefForInstance = (0, import_react15.useRef)(onNodesChange);
2702
+ const onEdgesChangeRefForInstance = (0, import_react15.useRef)(onEdgesChange);
2703
+ const onBeforeDeleteRef = (0, import_react15.useRef)(onBeforeDelete);
2704
+ const snapToGridRef = (0, import_react15.useRef)(snapToGrid);
2705
+ const gridSizeRef = (0, import_react15.useRef)(gridSize);
2706
+ const nodeCollisionGapRef = (0, import_react15.useRef)(nodeCollisionGap);
2707
+ const subflowCollisionGapRef = (0, import_react15.useRef)(subflowCollisionGap ?? nodeCollisionGap);
2708
+ const reparentOnDragRef = (0, import_react15.useRef)(reparentOnDrag);
2709
+ const autoResizeContainersRef = (0, import_react15.useRef)(autoResizeContainers);
2710
+ const containerAutoResizePaddingRef = (0, import_react15.useRef)(containerAutoResizePadding);
2711
+ const containerMinWidthRef = (0, import_react15.useRef)(containerMinWidth);
2712
+ const containerMinHeightRef = (0, import_react15.useRef)(containerMinHeight);
2713
+ (0, import_react15.useEffect)(() => {
2472
2714
  edgesRef.current = edges;
2473
2715
  }, [edges]);
2474
- (0, import_react14.useEffect)(() => {
2716
+ (0, import_react15.useEffect)(() => {
2475
2717
  onNodesChangeRefForInstance.current = onNodesChange;
2476
2718
  }, [onNodesChange]);
2477
- (0, import_react14.useEffect)(() => {
2719
+ (0, import_react15.useEffect)(() => {
2478
2720
  onEdgesChangeRefForInstance.current = onEdgesChange;
2479
2721
  }, [onEdgesChange]);
2480
- (0, import_react14.useEffect)(() => {
2722
+ (0, import_react15.useEffect)(() => {
2481
2723
  onBeforeDeleteRef.current = onBeforeDelete;
2482
2724
  }, [onBeforeDelete]);
2483
- (0, import_react14.useEffect)(() => {
2725
+ (0, import_react15.useEffect)(() => {
2484
2726
  snapToGridRef.current = snapToGrid;
2485
2727
  }, [snapToGrid]);
2486
- (0, import_react14.useEffect)(() => {
2728
+ (0, import_react15.useEffect)(() => {
2487
2729
  gridSizeRef.current = gridSize;
2488
2730
  }, [gridSize]);
2489
- (0, import_react14.useEffect)(() => {
2731
+ (0, import_react15.useEffect)(() => {
2490
2732
  nodeCollisionGapRef.current = nodeCollisionGap;
2491
2733
  }, [nodeCollisionGap]);
2492
- (0, import_react14.useEffect)(() => {
2734
+ (0, import_react15.useEffect)(() => {
2493
2735
  subflowCollisionGapRef.current = subflowCollisionGap ?? nodeCollisionGap;
2494
2736
  }, [subflowCollisionGap, nodeCollisionGap]);
2495
- const onNodesChangeRef = (0, import_react14.useRef)(onNodesChange);
2496
- const onConnectRef = (0, import_react14.useRef)(onConnect);
2497
- const onConnectEndRef = (0, import_react14.useRef)(onConnectEnd);
2498
- const isValidConnectionRef = (0, import_react14.useRef)(isValidConnection);
2499
- (0, import_react14.useEffect)(() => {
2737
+ (0, import_react15.useEffect)(() => {
2738
+ reparentOnDragRef.current = reparentOnDrag;
2739
+ }, [reparentOnDrag]);
2740
+ (0, import_react15.useEffect)(() => {
2741
+ autoResizeContainersRef.current = autoResizeContainers;
2742
+ }, [autoResizeContainers]);
2743
+ (0, import_react15.useEffect)(() => {
2744
+ containerAutoResizePaddingRef.current = containerAutoResizePadding;
2745
+ }, [containerAutoResizePadding]);
2746
+ (0, import_react15.useEffect)(() => {
2747
+ containerMinWidthRef.current = containerMinWidth;
2748
+ }, [containerMinWidth]);
2749
+ (0, import_react15.useEffect)(() => {
2750
+ containerMinHeightRef.current = containerMinHeight;
2751
+ }, [containerMinHeight]);
2752
+ const onNodesChangeRef = (0, import_react15.useRef)(onNodesChange);
2753
+ const onConnectRef = (0, import_react15.useRef)(onConnect);
2754
+ const onConnectEndRef = (0, import_react15.useRef)(onConnectEnd);
2755
+ const isValidConnectionRef = (0, import_react15.useRef)(isValidConnection);
2756
+ (0, import_react15.useEffect)(() => {
2500
2757
  viewportRef.current = viewport;
2501
2758
  }, [viewport]);
2502
- (0, import_react14.useEffect)(() => {
2759
+ (0, import_react15.useEffect)(() => {
2503
2760
  nodesRef.current = nodes;
2504
2761
  }, [nodes]);
2505
- (0, import_react14.useEffect)(() => {
2762
+ (0, import_react15.useEffect)(() => {
2506
2763
  onNodesChangeRef.current = onNodesChange;
2507
2764
  }, [onNodesChange]);
2508
- (0, import_react14.useEffect)(() => {
2765
+ (0, import_react15.useEffect)(() => {
2509
2766
  onConnectRef.current = onConnect;
2510
2767
  }, [onConnect]);
2511
- (0, import_react14.useEffect)(() => {
2768
+ (0, import_react15.useEffect)(() => {
2512
2769
  onConnectEndRef.current = onConnectEnd;
2513
2770
  }, [onConnectEnd]);
2514
- (0, import_react14.useEffect)(() => {
2771
+ (0, import_react15.useEffect)(() => {
2515
2772
  isValidConnectionRef.current = isValidConnection;
2516
2773
  }, [isValidConnection]);
2517
- (0, import_react14.useEffect)(() => {
2774
+ (0, import_react15.useEffect)(() => {
2518
2775
  const onPointerMove = (e) => {
2519
2776
  const vp = viewportRef.current;
2520
2777
  const drag = dragRef.current;
@@ -2536,7 +2793,7 @@ function FlowCanvas({
2536
2793
  x: d.startPosition.x + delta.dx,
2537
2794
  y: d.startPosition.y + delta.dy
2538
2795
  };
2539
- const clamped = d.altDetach ? proposed : clampToParentExtent(dragNode, proposed, nodesRef.current);
2796
+ const clamped = d.skipClamp ? proposed : clampToParentExtent(dragNode, proposed, nodesRef.current);
2540
2797
  const isContainer = dragNode.type === "group" || dragNode.type === "forEach";
2541
2798
  const gap = isContainer ? subflowCollisionGapRef.current : nodeCollisionGapRef.current;
2542
2799
  const excludeIds = /* @__PURE__ */ new Set([d.nodeId, ...d.descendants.map((kid) => kid.id)]);
@@ -2588,7 +2845,7 @@ function FlowCanvas({
2588
2845
  y: Math.round(proposed.y / g) * g
2589
2846
  };
2590
2847
  }
2591
- const clamped = drag.altDetach ? proposed : clampToParentExtent(dragNode, proposed, nodesRef.current);
2848
+ const clamped = drag.skipClamp ? proposed : clampToParentExtent(dragNode, proposed, nodesRef.current);
2592
2849
  const isContainer = dragNode.type === "group" || dragNode.type === "forEach";
2593
2850
  const gap = isContainer ? subflowCollisionGapRef.current : nodeCollisionGapRef.current;
2594
2851
  const excludeIds = /* @__PURE__ */ new Set([
@@ -2611,7 +2868,7 @@ function FlowCanvas({
2611
2868
  )
2612
2869
  );
2613
2870
  }
2614
- if (drag.altDetach && dragNode.parentId) {
2871
+ if (drag.evaluateReparent) {
2615
2872
  const targetGroup = findContainingGroup(
2616
2873
  {
2617
2874
  x: finalPos.x + (dragNode.width ?? 0) / 2,
@@ -2621,14 +2878,30 @@ function FlowCanvas({
2621
2878
  [drag.nodeId, ...drag.descendants.map((d) => d.id)]
2622
2879
  );
2623
2880
  const nextParentId = targetGroup?.id;
2624
- const updated = {
2625
- ...dragNode,
2626
- position: finalPos,
2627
- parentId: nextParentId,
2628
- // Preserve extent only when staying in a group.
2629
- extent: nextParentId ? dragNode.extent : void 0
2630
- };
2631
- changes.push(change.node.replace(drag.nodeId, updated));
2881
+ if (nextParentId !== dragNode.parentId) {
2882
+ const updated = {
2883
+ ...dragNode,
2884
+ position: finalPos,
2885
+ parentId: nextParentId,
2886
+ // Adopting into a container always pins the child with
2887
+ // `extent: 'parent'` so it's clamped from the next drag
2888
+ // on. Detaching clears it.
2889
+ extent: nextParentId ? "parent" : void 0
2890
+ };
2891
+ changes.push(change.node.replace(drag.nodeId, updated));
2892
+ }
2893
+ }
2894
+ if (autoResizeContainersRef.current) {
2895
+ const containerChanges = computeContainerAutoResize(
2896
+ nodesRef.current,
2897
+ // Projected children: apply the position + reparent
2898
+ // changes we just built before measuring bboxes.
2899
+ changes,
2900
+ containerAutoResizePaddingRef.current,
2901
+ containerMinWidthRef.current,
2902
+ containerMinHeightRef.current
2903
+ );
2904
+ for (const c2 of containerChanges) changes.push(c2);
2632
2905
  }
2633
2906
  onNodesChangeRef.current?.(changes);
2634
2907
  }
@@ -2637,38 +2910,54 @@ function FlowCanvas({
2637
2910
  }
2638
2911
  const c = connRef.current;
2639
2912
  if (c && c.pointerId === e.pointerId) {
2640
- const target = e.target;
2641
- const handleEl = target?.closest("[data-handle-id]");
2913
+ const rect = containerRef.current?.getBoundingClientRect();
2914
+ const flowPos = rect ? screenToFlow({ x: e.clientX - rect.left, y: e.clientY - rect.top }, vp) : { x: 0, y: 0 };
2915
+ const snapRadius = 26 / Math.max(0.1, vp.zoom);
2916
+ let bestDesc = null;
2917
+ let bestDist = snapRadius;
2918
+ for (const desc of handleRegistry.all()) {
2919
+ if (desc.nodeId === c.from.nodeId) continue;
2920
+ if (desc.type === c.from.handleType) continue;
2921
+ if (!desc.canEnd) continue;
2922
+ const targetNode = nodesRef.current.find((n) => n.id === desc.nodeId);
2923
+ if (!targetNode || targetNode.hidden) continue;
2924
+ const centre = handleCentre(targetNode, desc.side, desc.index, desc.total);
2925
+ const dist = Math.hypot(centre.x - flowPos.x, centre.y - flowPos.y);
2926
+ if (dist < bestDist) {
2927
+ bestDist = dist;
2928
+ bestDesc = desc;
2929
+ }
2930
+ }
2642
2931
  let connection = null;
2643
2932
  let connectedTo;
2644
- if (handleEl) {
2645
- const targetNodeId = handleEl.dataset.handleNodeId;
2646
- const targetHandleId = handleEl.dataset.handleId;
2647
- const targetType = handleEl.dataset.handleType;
2648
- const connectableEnd = handleEl.dataset.handleConnectableEnd === "true";
2649
- if (connectableEnd && (targetNodeId !== c.from.nodeId || targetHandleId !== c.from.handleId) && targetType !== c.from.handleType) {
2650
- const source = c.from.handleType === "source" ? c.from : { nodeId: targetNodeId, handleId: targetHandleId, handleType: "source" };
2651
- const target2 = c.from.handleType === "target" ? c.from : { nodeId: targetNodeId, handleId: targetHandleId, handleType: "target" };
2652
- connection = {
2653
- source: source.nodeId,
2654
- sourceHandle: source.handleId,
2655
- target: target2.nodeId,
2656
- targetHandle: target2.handleId
2657
- };
2658
- connectedTo = {
2659
- nodeId: targetNodeId,
2660
- handleId: targetHandleId,
2661
- handleType: targetType
2662
- };
2663
- const validator = isValidConnectionRef.current;
2664
- if (validator && !validator(connection)) {
2665
- connection = null;
2666
- connectedTo = void 0;
2667
- }
2933
+ if (bestDesc) {
2934
+ const source = c.from.handleType === "source" ? c.from : {
2935
+ nodeId: bestDesc.nodeId,
2936
+ handleId: bestDesc.handleId,
2937
+ handleType: "source"
2938
+ };
2939
+ const target2 = c.from.handleType === "target" ? c.from : {
2940
+ nodeId: bestDesc.nodeId,
2941
+ handleId: bestDesc.handleId,
2942
+ handleType: "target"
2943
+ };
2944
+ connection = {
2945
+ source: source.nodeId,
2946
+ sourceHandle: source.handleId,
2947
+ target: target2.nodeId,
2948
+ targetHandle: target2.handleId
2949
+ };
2950
+ connectedTo = {
2951
+ nodeId: bestDesc.nodeId,
2952
+ handleId: bestDesc.handleId,
2953
+ handleType: bestDesc.type
2954
+ };
2955
+ const validator = isValidConnectionRef.current;
2956
+ if (validator && !validator(connection)) {
2957
+ connection = null;
2958
+ connectedTo = void 0;
2668
2959
  }
2669
2960
  }
2670
- const rect = containerRef.current?.getBoundingClientRect();
2671
- const flowPos = rect ? screenToFlow({ x: e.clientX - rect.left, y: e.clientY - rect.top }, vp) : { x: 0, y: 0 };
2672
2961
  const endState = {
2673
2962
  cancelled: !connection,
2674
2963
  position: { x: e.clientX, y: e.clientY },
@@ -2701,7 +2990,26 @@ function FlowCanvas({
2701
2990
  window.removeEventListener("pointercancel", onPointerCancel);
2702
2991
  };
2703
2992
  }, [store]);
2704
- const panRef = (0, import_react14.useRef)(null);
2993
+ (0, import_react15.useEffect)(() => {
2994
+ if (!autoResizeContainers || draggingId) return;
2995
+ const resizeChanges = computeContainerAutoResize(
2996
+ nodes,
2997
+ [],
2998
+ containerAutoResizePadding,
2999
+ containerMinWidth,
3000
+ containerMinHeight
3001
+ );
3002
+ if (resizeChanges.length > 0) onNodesChange?.(resizeChanges);
3003
+ }, [
3004
+ nodes,
3005
+ draggingId,
3006
+ autoResizeContainers,
3007
+ containerAutoResizePadding,
3008
+ containerMinWidth,
3009
+ containerMinHeight,
3010
+ onNodesChange
3011
+ ]);
3012
+ const panRef = (0, import_react15.useRef)(null);
2705
3013
  const onCanvasPointerDown = (e) => {
2706
3014
  if (e.button !== 0) return;
2707
3015
  const t = e.target;
@@ -2726,7 +3034,7 @@ function FlowCanvas({
2726
3034
  moved: false
2727
3035
  };
2728
3036
  };
2729
- (0, import_react14.useEffect)(() => {
3037
+ (0, import_react15.useEffect)(() => {
2730
3038
  const onMove = (e) => {
2731
3039
  const pan = panRef.current;
2732
3040
  if (!pan || pan.pointerId !== e.pointerId) return;
@@ -2782,7 +3090,7 @@ function FlowCanvas({
2782
3090
  y: py - (py - viewport.y) * k
2783
3091
  });
2784
3092
  };
2785
- const dispatch = (0, import_react14.useCallback)(
3093
+ const dispatch = (0, import_react15.useCallback)(
2786
3094
  (a) => {
2787
3095
  if (a.type === "connection/start") {
2788
3096
  beginConnection(a.nodeId, a.handleId, a.handleType, a.pointerId, a.clientX, a.clientY);
@@ -2790,7 +3098,7 @@ function FlowCanvas({
2790
3098
  },
2791
3099
  [beginConnection]
2792
3100
  );
2793
- const reportDimensions = (0, import_react14.useCallback)(
3101
+ const reportDimensions = (0, import_react15.useCallback)(
2794
3102
  (nodeId, width2, height2) => {
2795
3103
  const node = nodes.find((n) => n.id === nodeId);
2796
3104
  if (!node) return;
@@ -2799,7 +3107,7 @@ function FlowCanvas({
2799
3107
  },
2800
3108
  [nodes, onNodesChange]
2801
3109
  );
2802
- const toggleNodeCollapseImpl = (0, import_react14.useCallback)(
3110
+ const toggleNodeCollapseImpl = (0, import_react15.useCallback)(
2803
3111
  (nodeId) => {
2804
3112
  const node = nodes.find((n) => n.id === nodeId);
2805
3113
  if (!node) return;
@@ -2812,7 +3120,7 @@ function FlowCanvas({
2812
3120
  },
2813
3121
  [nodes, onNodesChange]
2814
3122
  );
2815
- const deleteNodeImpl = (0, import_react14.useCallback)(
3123
+ const deleteNodeImpl = (0, import_react15.useCallback)(
2816
3124
  (nodeId) => {
2817
3125
  const incidentEdgeIds = edgesRef.current.filter((e) => e.source === nodeId || e.target === nodeId).map((e) => e.id);
2818
3126
  if (incidentEdgeIds.length > 0) {
@@ -2822,7 +3130,7 @@ function FlowCanvas({
2822
3130
  },
2823
3131
  [onNodesChange, onEdgesChange]
2824
3132
  );
2825
- const instance = (0, import_react14.useMemo)(
3133
+ const instance = (0, import_react15.useMemo)(
2826
3134
  () => ({
2827
3135
  // viewport
2828
3136
  getViewport: () => viewportRef.current,
@@ -2963,13 +3271,29 @@ function FlowCanvas({
2963
3271
  }),
2964
3272
  [setViewport, minZoom, maxZoom]
2965
3273
  );
2966
- const initFiredRef = (0, import_react14.useRef)(false);
2967
- (0, import_react14.useEffect)(() => {
3274
+ const initFiredRef = (0, import_react15.useRef)(false);
3275
+ (0, import_react15.useEffect)(() => {
2968
3276
  if (initFiredRef.current) return;
2969
3277
  initFiredRef.current = true;
2970
3278
  onInit?.(instance);
2971
3279
  }, [instance, onInit]);
2972
- (0, import_react14.useEffect)(() => {
3280
+ const fitOnInitFiredRef = (0, import_react15.useRef)(false);
3281
+ (0, import_react15.useEffect)(() => {
3282
+ if (fitOnInitFiredRef.current) return;
3283
+ const opt = fitViewOnInit;
3284
+ const shouldFit = opt === false ? false : opt !== void 0 ? true : !viewportPropProvided;
3285
+ if (!shouldFit) return;
3286
+ if (nodes.length === 0) return;
3287
+ const rect = containerRef.current?.getBoundingClientRect();
3288
+ if (!rect || rect.width === 0 || rect.height === 0) return;
3289
+ fitOnInitFiredRef.current = true;
3290
+ const fitOpts = typeof opt === "object" && opt !== null ? opt : void 0;
3291
+ const raf = requestAnimationFrame(() => {
3292
+ void instance.fitView(fitOpts);
3293
+ });
3294
+ return () => cancelAnimationFrame(raf);
3295
+ }, [fitViewOnInit, viewportPropProvided, nodes.length, instance]);
3296
+ (0, import_react15.useEffect)(() => {
2973
3297
  const onKey = (e) => {
2974
3298
  if (e.key !== "Backspace" && e.key !== "Delete") return;
2975
3299
  const target = e.target;
@@ -2990,7 +3314,7 @@ function FlowCanvas({
2990
3314
  window.addEventListener("keydown", onKey);
2991
3315
  return () => window.removeEventListener("keydown", onKey);
2992
3316
  }, [instance, store]);
2993
- const bridge = (0, import_react14.useMemo)(
3317
+ const bridge = (0, import_react15.useMemo)(
2994
3318
  () => ({
2995
3319
  beginNodeDrag,
2996
3320
  selectNode,
@@ -3010,16 +3334,16 @@ function FlowCanvas({
3010
3334
  toggleNodeCollapseImpl
3011
3335
  ]
3012
3336
  );
3013
- const [panGesture, setPanGesture] = (0, import_react14.useState)(false);
3337
+ const [panGesture, setPanGesture] = (0, import_react15.useState)(false);
3014
3338
  const isEmpty = nodes.length === 0 && edges.length === 0;
3015
3339
  const isConnecting = conn !== null;
3016
- const visibleNodes = (0, import_react14.useMemo)(() => nodes.filter((n) => !n.hidden), [nodes]);
3017
- const visibleEdges = (0, import_react14.useMemo)(() => {
3340
+ const visibleNodes = (0, import_react15.useMemo)(() => nodes.filter((n) => !n.hidden), [nodes]);
3341
+ const visibleEdges = (0, import_react15.useMemo)(() => {
3018
3342
  if (visibleNodes.length === nodes.length) return edges;
3019
3343
  const visibleIds = new Set(visibleNodes.map((n) => n.id));
3020
3344
  return edges.filter((e) => visibleIds.has(e.source) && visibleIds.has(e.target));
3021
3345
  }, [edges, nodes, visibleNodes]);
3022
- const orderedNodes = (0, import_react14.useMemo)(() => {
3346
+ const orderedNodes = (0, import_react15.useMemo)(() => {
3023
3347
  const isContainer = (n) => n.type === "group" || n.type === "forEach";
3024
3348
  const depth = (n) => {
3025
3349
  let d = 0;
@@ -3038,7 +3362,7 @@ function FlowCanvas({
3038
3362
  containers.sort((a, b) => depth(a) - depth(b));
3039
3363
  return [...containers, ...others];
3040
3364
  }, [visibleNodes]);
3041
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FlowStoreContext.Provider, { value: store, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FlowInstanceContext.Provider, { value: instance, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HandleRegistryContext.Provider, { value: handleRegistry, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FlowDispatchContext.Provider, { value: dispatch, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FlowNodeBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3365
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FlowStoreContext.Provider, { value: store, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FlowInstanceContext.Provider, { value: instance, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(HandleRegistryContext.Provider, { value: handleRegistry, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FlowDispatchContext.Provider, { value: dispatch, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(FlowNodeBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3042
3366
  "div",
3043
3367
  {
3044
3368
  ref: containerRef,
@@ -3083,7 +3407,7 @@ function FlowCanvas({
3083
3407
  },
3084
3408
  "data-empty": isEmpty ? "true" : void 0,
3085
3409
  children: [
3086
- background !== "none" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3410
+ background !== "none" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3087
3411
  "div",
3088
3412
  {
3089
3413
  className: cn(
@@ -3097,7 +3421,7 @@ function FlowCanvas({
3097
3421
  }
3098
3422
  }
3099
3423
  ),
3100
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3424
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3101
3425
  "div",
3102
3426
  {
3103
3427
  className: "ods-flow-canvas-v2__viewport",
@@ -3108,7 +3432,7 @@ function FlowCanvas({
3108
3432
  transformOrigin: "0 0"
3109
3433
  },
3110
3434
  children: [
3111
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3435
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3112
3436
  EdgesLayer,
3113
3437
  {
3114
3438
  edges: visibleEdges,
@@ -3123,7 +3447,7 @@ function FlowCanvas({
3123
3447
  orderedNodes.map((node) => {
3124
3448
  const Kind = kinds[node.type] ?? kinds.action;
3125
3449
  if (!Kind) return null;
3126
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3450
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3127
3451
  FlowNode,
3128
3452
  {
3129
3453
  node,
@@ -3138,7 +3462,7 @@ function FlowCanvas({
3138
3462
  ]
3139
3463
  }
3140
3464
  ),
3141
- isEmpty && emptyState && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ods-flow-canvas-v2__empty", children: emptyState }),
3465
+ isEmpty && emptyState && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ods-flow-canvas-v2__empty", children: emptyState }),
3142
3466
  children
3143
3467
  ]
3144
3468
  }
@@ -3147,7 +3471,69 @@ function FlowCanvas({
3147
3471
  function onEdgesChangeRef(id, cb) {
3148
3472
  cb?.([change.edge.remove(id)]);
3149
3473
  }
3150
- var EdgesLayer = (0, import_react14.memo)(function EdgesLayer2({
3474
+ var CONTAINER_DROP_HEADROOM_W = 220;
3475
+ var CONTAINER_DROP_HEADROOM_H = 2 * 128;
3476
+ function computeContainerAutoResize(nodes, pending, padding, minWidth, minHeight) {
3477
+ const projected = /* @__PURE__ */ new Map();
3478
+ for (const n of nodes) projected.set(n.id, n);
3479
+ for (const c of pending) {
3480
+ if (c.type === "position" && c.position) {
3481
+ const cur = projected.get(c.id);
3482
+ if (cur) projected.set(c.id, { ...cur, position: c.position });
3483
+ } else if (c.type === "replace" && c.item) {
3484
+ projected.set(c.id, { ...projected.get(c.id), ...c.item });
3485
+ } else if (c.type === "add" && c.item) {
3486
+ projected.set(c.item.id, c.item);
3487
+ }
3488
+ }
3489
+ const HEADER = 56;
3490
+ const out = [];
3491
+ for (const parent of projected.values()) {
3492
+ if (parent.type !== "group" && parent.type !== "forEach") continue;
3493
+ const children = [];
3494
+ for (const n of projected.values()) {
3495
+ if (n.parentId === parent.id) children.push(n);
3496
+ }
3497
+ if (children.length === 0) {
3498
+ const curW2 = parent.width ?? 480;
3499
+ const curH2 = parent.height ?? 240;
3500
+ if (curW2 !== minWidth || curH2 !== minHeight) {
3501
+ out.push(change.node.dimensions(parent.id, { width: minWidth, height: minHeight }));
3502
+ }
3503
+ continue;
3504
+ }
3505
+ let minX = Number.POSITIVE_INFINITY;
3506
+ let minY = Number.POSITIVE_INFINITY;
3507
+ let maxX = Number.NEGATIVE_INFINITY;
3508
+ let maxY = Number.NEGATIVE_INFINITY;
3509
+ for (const c of children) {
3510
+ const cw = c.width ?? 240;
3511
+ const ch = c.height ?? 96;
3512
+ if (c.position.x < minX) minX = c.position.x;
3513
+ if (c.position.y < minY) minY = c.position.y;
3514
+ if (c.position.x + cw > maxX) maxX = c.position.x + cw;
3515
+ if (c.position.y + ch > maxY) maxY = c.position.y + ch;
3516
+ }
3517
+ const targetX = minX - padding;
3518
+ const targetY = minY - padding - HEADER;
3519
+ const curW = parent.width ?? 480;
3520
+ const curH = parent.height ?? 240;
3521
+ const fitW = maxX - minX + padding * 2 + CONTAINER_DROP_HEADROOM_W;
3522
+ const fitH = maxY - minY + padding * 2 + HEADER + CONTAINER_DROP_HEADROOM_H;
3523
+ const nextW = Math.max(minWidth, fitW);
3524
+ const nextH = Math.max(minHeight, fitH);
3525
+ const positionDirty = parent.position.x !== targetX || parent.position.y !== targetY;
3526
+ const sizeDirty = curW !== nextW || curH !== nextH;
3527
+ if (positionDirty) {
3528
+ out.push(change.node.position(parent.id, { x: targetX, y: targetY }, false));
3529
+ }
3530
+ if (sizeDirty) {
3531
+ out.push(change.node.dimensions(parent.id, { width: nextW, height: nextH }));
3532
+ }
3533
+ }
3534
+ return out;
3535
+ }
3536
+ var EdgesLayer = (0, import_react15.memo)(function EdgesLayer2({
3151
3537
  edges,
3152
3538
  nodes,
3153
3539
  onSelect,
@@ -3156,7 +3542,7 @@ var EdgesLayer = (0, import_react14.memo)(function EdgesLayer2({
3156
3542
  ghost,
3157
3543
  handleVersion: _handleVersion
3158
3544
  }) {
3159
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3545
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3160
3546
  "svg",
3161
3547
  {
3162
3548
  className: "ods-flow-canvas-v2__edges",
@@ -3164,7 +3550,7 @@ var EdgesLayer = (0, import_react14.memo)(function EdgesLayer2({
3164
3550
  width: "100%",
3165
3551
  height: "100%",
3166
3552
  children: [
3167
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { style: { pointerEvents: "auto" }, children: edges.map((edge) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3553
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("g", { style: { pointerEvents: "auto" }, children: edges.map((edge) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3168
3554
  FlowEdge,
3169
3555
  {
3170
3556
  edge,
@@ -3176,7 +3562,7 @@ var EdgesLayer = (0, import_react14.memo)(function EdgesLayer2({
3176
3562
  },
3177
3563
  edge.id
3178
3564
  )) }),
3179
- ghost && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3565
+ ghost && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3180
3566
  "path",
3181
3567
  {
3182
3568
  d: `M ${ghost.start.x} ${ghost.start.y} L ${ghost.end.x} ${ghost.end.y}`,
@@ -3191,151 +3577,6 @@ var EdgesLayer = (0, import_react14.memo)(function EdgesLayer2({
3191
3577
  );
3192
3578
  });
3193
3579
 
3194
- // src/workflow/components/NodeResizer/NodeResizer.tsx
3195
- var import_react15 = require("react");
3196
- var import_jsx_runtime8 = require("react/jsx-runtime");
3197
- function NodeResizer({
3198
- isVisible,
3199
- minWidth = 80,
3200
- minHeight = 60,
3201
- maxWidth,
3202
- maxHeight,
3203
- keepAspectRatio = false,
3204
- onResize,
3205
- onResizeEnd,
3206
- color
3207
- }) {
3208
- const { node, selected } = useFlowNodeContext();
3209
- const viewport = useViewport();
3210
- const flow = useFlow();
3211
- const dragRef = (0, import_react15.useRef)(null);
3212
- const show = isVisible ?? selected;
3213
- if (!show) return null;
3214
- const beginResize = (e, corner) => {
3215
- e.preventDefault();
3216
- e.stopPropagation();
3217
- e.target.setPointerCapture(e.pointerId);
3218
- const w = node.width ?? DEFAULT_NODE_WIDTH;
3219
- const h = node.height ?? DEFAULT_NODE_HEIGHT;
3220
- dragRef.current = {
3221
- pointerId: e.pointerId,
3222
- corner,
3223
- startClientX: e.clientX,
3224
- startClientY: e.clientY,
3225
- startWidth: w,
3226
- startHeight: h,
3227
- startX: node.position.x,
3228
- startY: node.position.y,
3229
- aspect: w / Math.max(1, h)
3230
- };
3231
- };
3232
- const onMove = (e) => {
3233
- const drag = dragRef.current;
3234
- if (!drag || drag.pointerId !== e.pointerId) return;
3235
- const dx = (e.clientX - drag.startClientX) / viewport.zoom;
3236
- const dy = (e.clientY - drag.startClientY) / viewport.zoom;
3237
- let nextW = drag.startWidth;
3238
- let nextH = drag.startHeight;
3239
- let nextX = drag.startX;
3240
- let nextY = drag.startY;
3241
- switch (drag.corner) {
3242
- case "se":
3243
- nextW = drag.startWidth + dx;
3244
- nextH = drag.startHeight + dy;
3245
- break;
3246
- case "sw":
3247
- nextW = drag.startWidth - dx;
3248
- nextH = drag.startHeight + dy;
3249
- nextX = drag.startX + dx;
3250
- break;
3251
- case "ne":
3252
- nextW = drag.startWidth + dx;
3253
- nextH = drag.startHeight - dy;
3254
- nextY = drag.startY + dy;
3255
- break;
3256
- case "nw":
3257
- nextW = drag.startWidth - dx;
3258
- nextH = drag.startHeight - dy;
3259
- nextX = drag.startX + dx;
3260
- nextY = drag.startY + dy;
3261
- break;
3262
- }
3263
- if (keepAspectRatio) {
3264
- nextH = nextW / drag.aspect;
3265
- if (drag.corner === "nw" || drag.corner === "ne") {
3266
- nextY = drag.startY + (drag.startHeight - nextH);
3267
- }
3268
- }
3269
- nextW = Math.max(minWidth, maxWidth ? Math.min(maxWidth, nextW) : nextW);
3270
- nextH = Math.max(minHeight, maxHeight ? Math.min(maxHeight, nextH) : nextH);
3271
- flow.updateNode(node.id, {
3272
- width: nextW,
3273
- height: nextH,
3274
- position: { x: nextX, y: nextY }
3275
- });
3276
- onResize?.({ width: nextW, height: nextH });
3277
- };
3278
- const onUp = (e) => {
3279
- if (dragRef.current?.pointerId === e.pointerId) {
3280
- const cur = flow.getNode(node.id);
3281
- if (cur) {
3282
- onResizeEnd?.({
3283
- width: cur.width ?? DEFAULT_NODE_WIDTH,
3284
- height: cur.height ?? DEFAULT_NODE_HEIGHT
3285
- });
3286
- }
3287
- dragRef.current = null;
3288
- }
3289
- };
3290
- const handleColor = color ?? "var(--ods-accent)";
3291
- const handleStyle = (corner) => {
3292
- const base = {
3293
- position: "absolute",
3294
- width: 12,
3295
- height: 12,
3296
- background: "var(--ods-surface-canvas)",
3297
- border: `2px solid ${handleColor}`,
3298
- borderRadius: 2,
3299
- cursor: cursorFor(corner),
3300
- touchAction: "none",
3301
- // Place each handle so its CENTRE sits on the corresponding corner.
3302
- transform: "translate(-50%, -50%)"
3303
- };
3304
- switch (corner) {
3305
- case "nw":
3306
- return { ...base, top: 0, left: 0 };
3307
- case "ne":
3308
- return { ...base, top: 0, left: "100%" };
3309
- case "sw":
3310
- return { ...base, top: "100%", left: 0 };
3311
- case "se":
3312
- return { ...base, top: "100%", left: "100%" };
3313
- }
3314
- };
3315
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn("ods-node-resizer"), "data-flow-no-drag": "true", children: ["nw", "ne", "sw", "se"].map((corner) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3316
- "div",
3317
- {
3318
- style: handleStyle(corner),
3319
- onPointerDown: (e) => beginResize(e, corner),
3320
- onPointerMove: onMove,
3321
- onPointerUp: onUp,
3322
- onPointerCancel: onUp,
3323
- "aria-label": `Resize ${corner}`
3324
- },
3325
- corner
3326
- )) });
3327
- }
3328
- function cursorFor(corner) {
3329
- switch (corner) {
3330
- case "nw":
3331
- case "se":
3332
- return "nwse-resize";
3333
- case "ne":
3334
- case "sw":
3335
- return "nesw-resize";
3336
- }
3337
- }
3338
-
3339
3580
  // src/workflow/components/NodeToolbar/NodeToolbar.tsx
3340
3581
  var import_jsx_runtime9 = require("react/jsx-runtime");
3341
3582
  function NodeToolbar({
@@ -3564,6 +3805,7 @@ function toggleGroupCollapse(groupId, nodes) {
3564
3805
  useNodeData,
3565
3806
  useNodes,
3566
3807
  useSelection,
3567
- useViewport
3808
+ useViewport,
3809
+ useViewportOrNull
3568
3810
  });
3569
3811
  //# sourceMappingURL=workflow.cjs.map