@elabs-ai/components-flow 4.1.0 → 5.0.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 (268) hide show
  1. package/README.md +2 -1
  2. package/dist/canvas-shell/canvas-shell.js +13 -0
  3. package/dist/canvas-shell/canvas-shell.js.map +1 -0
  4. package/dist/canvas-shell/clamped-fit-offset.js +8 -0
  5. package/dist/canvas-shell/clamped-fit-offset.js.map +1 -0
  6. package/dist/canvas-shell/index.js +14 -0
  7. package/dist/canvas-shell/index.js.map +1 -0
  8. package/dist/canvas-shell/use-measured-nodes.js +8 -0
  9. package/dist/canvas-shell/use-measured-nodes.js.map +1 -0
  10. package/dist/chunk-3BTU2VWZ.js +42 -0
  11. package/dist/chunk-3BTU2VWZ.js.map +1 -0
  12. package/dist/chunk-3LK6OOXE.js +2 -0
  13. package/dist/chunk-3LK6OOXE.js.map +1 -0
  14. package/dist/chunk-3M3FQAD4.js +105 -0
  15. package/dist/chunk-3M3FQAD4.js.map +1 -0
  16. package/dist/chunk-3Y5TCGC3.js +2 -0
  17. package/dist/chunk-3Y5TCGC3.js.map +1 -0
  18. package/dist/chunk-4VYHZM3S.js +108 -0
  19. package/dist/chunk-4VYHZM3S.js.map +1 -0
  20. package/dist/chunk-5TVVM4GR.js +45 -0
  21. package/dist/chunk-5TVVM4GR.js.map +1 -0
  22. package/dist/chunk-5XT73MQQ.js +2 -0
  23. package/dist/chunk-5XT73MQQ.js.map +1 -0
  24. package/dist/chunk-6LQD5Y5R.js +24 -0
  25. package/dist/chunk-6LQD5Y5R.js.map +1 -0
  26. package/dist/chunk-6SR26QY2.js +18 -0
  27. package/dist/chunk-6SR26QY2.js.map +1 -0
  28. package/dist/chunk-75JJK6BH.js +157 -0
  29. package/dist/chunk-75JJK6BH.js.map +1 -0
  30. package/dist/chunk-A66JBW4X.js +2 -0
  31. package/dist/chunk-A66JBW4X.js.map +1 -0
  32. package/dist/chunk-A7AFM3LI.js +72 -0
  33. package/dist/chunk-A7AFM3LI.js.map +1 -0
  34. package/dist/chunk-ADHLQF56.js +2 -0
  35. package/dist/chunk-ADHLQF56.js.map +1 -0
  36. package/dist/chunk-ATO4LXJF.js +75 -0
  37. package/dist/chunk-ATO4LXJF.js.map +1 -0
  38. package/dist/chunk-ATXVXTMZ.js +227 -0
  39. package/dist/chunk-ATXVXTMZ.js.map +1 -0
  40. package/dist/chunk-B6IUPZU7.js +2 -0
  41. package/dist/chunk-B6IUPZU7.js.map +1 -0
  42. package/dist/chunk-BJ7UMCD3.js +53 -0
  43. package/dist/chunk-BJ7UMCD3.js.map +1 -0
  44. package/dist/chunk-BWGYV4JZ.js +2 -0
  45. package/dist/chunk-BWGYV4JZ.js.map +1 -0
  46. package/dist/chunk-CWNO63LQ.js +109 -0
  47. package/dist/chunk-CWNO63LQ.js.map +1 -0
  48. package/dist/chunk-DHPUXKCZ.js +76 -0
  49. package/dist/chunk-DHPUXKCZ.js.map +1 -0
  50. package/dist/chunk-DNX7G3KA.js +185 -0
  51. package/dist/chunk-DNX7G3KA.js.map +1 -0
  52. package/dist/chunk-DNZRAJAQ.js +41 -0
  53. package/dist/chunk-DNZRAJAQ.js.map +1 -0
  54. package/dist/chunk-ELCLRVD4.js +2 -0
  55. package/dist/chunk-ELCLRVD4.js.map +1 -0
  56. package/dist/chunk-ELOADZSX.js +2 -0
  57. package/dist/chunk-ELOADZSX.js.map +1 -0
  58. package/dist/chunk-EUNV2MR5.js +21 -0
  59. package/dist/chunk-EUNV2MR5.js.map +1 -0
  60. package/dist/chunk-FAFQ5KLB.js +43 -0
  61. package/dist/chunk-FAFQ5KLB.js.map +1 -0
  62. package/dist/chunk-FIRO6Y3R.js +128 -0
  63. package/dist/chunk-FIRO6Y3R.js.map +1 -0
  64. package/dist/chunk-GE73WQUK.js +2 -0
  65. package/dist/chunk-GE73WQUK.js.map +1 -0
  66. package/dist/chunk-GIARAOU6.js +39 -0
  67. package/dist/chunk-GIARAOU6.js.map +1 -0
  68. package/dist/chunk-HTLTCCFA.js +73 -0
  69. package/dist/chunk-HTLTCCFA.js.map +1 -0
  70. package/dist/chunk-I53W3CFN.js +56 -0
  71. package/dist/chunk-I53W3CFN.js.map +1 -0
  72. package/dist/chunk-IJVFPUXO.js +2 -0
  73. package/dist/chunk-IJVFPUXO.js.map +1 -0
  74. package/dist/chunk-JUPMOISE.js +51 -0
  75. package/dist/chunk-JUPMOISE.js.map +1 -0
  76. package/dist/chunk-K5RPSVO6.js +167 -0
  77. package/dist/chunk-K5RPSVO6.js.map +1 -0
  78. package/dist/chunk-K6U27YJ6.js +78 -0
  79. package/dist/chunk-K6U27YJ6.js.map +1 -0
  80. package/dist/chunk-KDVMGMAK.js +2 -0
  81. package/dist/chunk-KDVMGMAK.js.map +1 -0
  82. package/dist/chunk-LQOZCOK3.js +74 -0
  83. package/dist/chunk-LQOZCOK3.js.map +1 -0
  84. package/dist/chunk-NAQWNX2A.js +102 -0
  85. package/dist/chunk-NAQWNX2A.js.map +1 -0
  86. package/dist/chunk-NVJVWGUS.js +152 -0
  87. package/dist/chunk-NVJVWGUS.js.map +1 -0
  88. package/dist/chunk-O2F3R3PV.js +2 -0
  89. package/dist/chunk-O2F3R3PV.js.map +1 -0
  90. package/dist/chunk-OMFQHMA6.js +2 -0
  91. package/dist/chunk-OMFQHMA6.js.map +1 -0
  92. package/dist/chunk-PE6JXVOX.js +2 -0
  93. package/dist/chunk-PE6JXVOX.js.map +1 -0
  94. package/dist/chunk-ROJEIIA4.js +52 -0
  95. package/dist/chunk-ROJEIIA4.js.map +1 -0
  96. package/dist/chunk-ROYAQKJA.js +9 -0
  97. package/dist/chunk-ROYAQKJA.js.map +1 -0
  98. package/dist/chunk-SHCZK4DO.js +45 -0
  99. package/dist/chunk-SHCZK4DO.js.map +1 -0
  100. package/dist/chunk-SXXJV6YJ.js +79 -0
  101. package/dist/chunk-SXXJV6YJ.js.map +1 -0
  102. package/dist/chunk-T7S7NJFD.js +287 -0
  103. package/dist/chunk-T7S7NJFD.js.map +1 -0
  104. package/dist/chunk-TEZ3OXAY.js +63 -0
  105. package/dist/chunk-TEZ3OXAY.js.map +1 -0
  106. package/dist/chunk-TGWPM2CG.js +199 -0
  107. package/dist/chunk-TGWPM2CG.js.map +1 -0
  108. package/dist/chunk-TSB6RE7L.js +2 -0
  109. package/dist/chunk-TSB6RE7L.js.map +1 -0
  110. package/dist/chunk-UPV2MT4O.js +2 -0
  111. package/dist/chunk-UPV2MT4O.js.map +1 -0
  112. package/dist/chunk-UZJZY4A5.js +2 -0
  113. package/dist/chunk-UZJZY4A5.js.map +1 -0
  114. package/dist/chunk-W3TQMUGX.js +2 -0
  115. package/dist/chunk-W3TQMUGX.js.map +1 -0
  116. package/dist/chunk-WN46RIOC.js +29 -0
  117. package/dist/chunk-WN46RIOC.js.map +1 -0
  118. package/dist/chunk-XZFE2YYH.js +70 -0
  119. package/dist/chunk-XZFE2YYH.js.map +1 -0
  120. package/dist/chunk-YG3KXTZW.js +2 -0
  121. package/dist/chunk-YG3KXTZW.js.map +1 -0
  122. package/dist/chunk-ZATVF42E.js +65 -0
  123. package/dist/chunk-ZATVF42E.js.map +1 -0
  124. package/dist/chunk-ZXZTLQIX.js +19 -0
  125. package/dist/chunk-ZXZTLQIX.js.map +1 -0
  126. package/dist/flow-button-edge/flow-button-edge.js +10 -0
  127. package/dist/flow-button-edge/flow-button-edge.js.map +1 -0
  128. package/dist/flow-button-edge/index.js +11 -0
  129. package/dist/flow-button-edge/index.js.map +1 -0
  130. package/dist/flow-edge/flow-edge.js +10 -0
  131. package/dist/flow-edge/flow-edge.js.map +1 -0
  132. package/dist/flow-edge/index.js +11 -0
  133. package/dist/flow-edge/index.js.map +1 -0
  134. package/dist/flow-edge-path/flow-edge-path.js +12 -0
  135. package/dist/flow-edge-path/flow-edge-path.js.map +1 -0
  136. package/dist/flow-edge-path/index.js +13 -0
  137. package/dist/flow-edge-path/index.js.map +1 -0
  138. package/dist/flow-edge-tokens/edge-path-sampler.js +10 -0
  139. package/dist/flow-edge-tokens/edge-path-sampler.js.map +1 -0
  140. package/dist/flow-edge-tokens/flow-edge-tokens.js +12 -0
  141. package/dist/flow-edge-tokens/flow-edge-tokens.js.map +1 -0
  142. package/dist/flow-edge-tokens/index.js +17 -0
  143. package/dist/flow-edge-tokens/index.js.map +1 -0
  144. package/dist/flow-floating-edge/floating-edge-geometry.js +8 -0
  145. package/dist/flow-floating-edge/floating-edge-geometry.js.map +1 -0
  146. package/dist/flow-floating-edge/flow-floating-edge.js +11 -0
  147. package/dist/flow-floating-edge/flow-floating-edge.js.map +1 -0
  148. package/dist/flow-floating-edge/index.js +15 -0
  149. package/dist/flow-floating-edge/index.js.map +1 -0
  150. package/dist/flow-group-node/flow-group-node.js +12 -0
  151. package/dist/flow-group-node/flow-group-node.js.map +1 -0
  152. package/dist/flow-group-node/index.js +13 -0
  153. package/dist/flow-group-node/index.js.map +1 -0
  154. package/dist/flow-handle/flow-handle-anchor.js +8 -0
  155. package/dist/flow-handle/flow-handle-anchor.js.map +1 -0
  156. package/dist/flow-handle/index.js +9 -0
  157. package/dist/flow-handle/index.js.map +1 -0
  158. package/dist/flow-layout/backbone.js +8 -0
  159. package/dist/flow-layout/backbone.js.map +1 -0
  160. package/dist/flow-layout/flow-layout.js +10 -0
  161. package/dist/flow-layout/flow-layout.js.map +1 -0
  162. package/dist/flow-layout/index.js +21 -0
  163. package/dist/flow-layout/index.js.map +1 -0
  164. package/dist/flow-layout/layout-flow-elk.js +9 -0
  165. package/dist/flow-layout/layout-flow-elk.js.map +1 -0
  166. package/dist/flow-layout/layout-graph.js +9 -0
  167. package/dist/flow-layout/layout-graph.js.map +1 -0
  168. package/dist/flow-layout/use-auto-layout.js +10 -0
  169. package/dist/flow-layout/use-auto-layout.js.map +1 -0
  170. package/dist/flow-layout/use-flow-layout.js +9 -0
  171. package/dist/flow-layout/use-flow-layout.js.map +1 -0
  172. package/dist/flow-mini-map/flow-mini-map.js +8 -0
  173. package/dist/flow-mini-map/flow-mini-map.js.map +1 -0
  174. package/dist/flow-mini-map/index.js +9 -0
  175. package/dist/flow-mini-map/index.js.map +1 -0
  176. package/dist/flow-node/flow-node.js +11 -0
  177. package/dist/flow-node/flow-node.js.map +1 -0
  178. package/dist/flow-node/index.js +12 -0
  179. package/dist/flow-node/index.js.map +1 -0
  180. package/dist/flow-placeholder-node/flow-placeholder-node.js +9 -0
  181. package/dist/flow-placeholder-node/flow-placeholder-node.js.map +1 -0
  182. package/dist/flow-placeholder-node/index.js +10 -0
  183. package/dist/flow-placeholder-node/index.js.map +1 -0
  184. package/dist/flow-self-loop-edge/flow-self-loop-edge.js +20 -0
  185. package/dist/flow-self-loop-edge/flow-self-loop-edge.js.map +1 -0
  186. package/dist/flow-self-loop-edge/index.js +28 -0
  187. package/dist/flow-self-loop-edge/index.js.map +1 -0
  188. package/dist/flow-self-loop-edge/self-loop-geometry.js +12 -0
  189. package/dist/flow-self-loop-edge/self-loop-geometry.js.map +1 -0
  190. package/dist/flow-smart-edge/flow-smart-edge.js +11 -0
  191. package/dist/flow-smart-edge/flow-smart-edge.js.map +1 -0
  192. package/dist/flow-smart-edge/index.js +25 -0
  193. package/dist/flow-smart-edge/index.js.map +1 -0
  194. package/dist/flow-smart-edge/smart-edge-geometry.js +18 -0
  195. package/dist/flow-smart-edge/smart-edge-geometry.js.map +1 -0
  196. package/dist/flow-weighted-edge/back-edge-geometry.js +8 -0
  197. package/dist/flow-weighted-edge/back-edge-geometry.js.map +1 -0
  198. package/dist/flow-weighted-edge/edge-aria.js +10 -0
  199. package/dist/flow-weighted-edge/edge-aria.js.map +1 -0
  200. package/dist/flow-weighted-edge/edge-label-pill.js +8 -0
  201. package/dist/flow-weighted-edge/edge-label-pill.js.map +1 -0
  202. package/dist/flow-weighted-edge/flow-weighted-edge.js +16 -0
  203. package/dist/flow-weighted-edge/flow-weighted-edge.js.map +1 -0
  204. package/dist/flow-weighted-edge/index.js +34 -0
  205. package/dist/flow-weighted-edge/index.js.map +1 -0
  206. package/dist/flow-weighted-edge/weight-scale.js +10 -0
  207. package/dist/flow-weighted-edge/weight-scale.js.map +1 -0
  208. package/dist/helper-lines/get-helper-lines.js +8 -0
  209. package/dist/helper-lines/get-helper-lines.js.map +1 -0
  210. package/dist/helper-lines/helper-lines.js +9 -0
  211. package/dist/helper-lines/helper-lines.js.map +1 -0
  212. package/dist/helper-lines/index.js +17 -0
  213. package/dist/helper-lines/index.js.map +1 -0
  214. package/dist/helper-lines/use-helper-lines.js +10 -0
  215. package/dist/helper-lines/use-helper-lines.js.map +1 -0
  216. package/dist/index.d.ts +241 -2
  217. package/dist/index.js +167 -2306
  218. package/dist/index.js.map +1 -1
  219. package/dist/inspector-panel/index.js +9 -0
  220. package/dist/inspector-panel/index.js.map +1 -0
  221. package/dist/inspector-panel/inspector-panel.js +8 -0
  222. package/dist/inspector-panel/inspector-panel.js.map +1 -0
  223. package/dist/legend/index.js +10 -0
  224. package/dist/legend/index.js.map +1 -0
  225. package/dist/legend/legend.js +9 -0
  226. package/dist/legend/legend.js.map +1 -0
  227. package/dist/use-flow-groups/group-operations.js +20 -0
  228. package/dist/use-flow-groups/group-operations.js.map +1 -0
  229. package/dist/use-flow-groups/index.js +25 -0
  230. package/dist/use-flow-groups/index.js.map +1 -0
  231. package/dist/use-flow-groups/use-flow-groups.js +9 -0
  232. package/dist/use-flow-groups/use-flow-groups.js.map +1 -0
  233. package/dist/zoom-controls/index.js +9 -0
  234. package/dist/zoom-controls/index.js.map +1 -0
  235. package/dist/zoom-controls/zoom-controls.js +8 -0
  236. package/dist/zoom-controls/zoom-controls.js.map +1 -0
  237. package/package.json +14 -7
  238. package/src/__contract__/inspector-panel.contract.test.tsx +49 -0
  239. package/src/__contract__/legend.contract.test.tsx +49 -0
  240. package/src/canvas-shell/canvas-shell.stories.tsx +1 -0
  241. package/src/canvas-shell/canvas-shell.tsx +51 -4
  242. package/src/canvas-shell/clamped-fit-offset.test.ts +25 -0
  243. package/src/canvas-shell/clamped-fit-offset.ts +30 -0
  244. package/src/custom-nodes/custom-nodes.stories.tsx +718 -0
  245. package/src/flow-edge-tokens/edge-path-sampler.test.ts +108 -0
  246. package/src/flow-edge-tokens/edge-path-sampler.ts +362 -0
  247. package/src/flow-edge-tokens/flow-edge-tokens.stories.tsx +312 -0
  248. package/src/flow-edge-tokens/flow-edge-tokens.test.tsx +115 -0
  249. package/src/flow-edge-tokens/flow-edge-tokens.tsx +94 -0
  250. package/src/flow-edge-tokens/index.ts +12 -0
  251. package/src/flow-layout/backbone.test.ts +88 -0
  252. package/src/flow-layout/backbone.ts +58 -0
  253. package/src/flow-layout/layout-flow-elk.stories.tsx +154 -0
  254. package/src/flow-layout/layout-flow-elk.test.ts +152 -0
  255. package/src/flow-layout/layout-flow-elk.ts +399 -0
  256. package/src/flow-mini-map/flow-mini-map.stories.tsx +20 -0
  257. package/src/flow-node/flow-node.stories.tsx +40 -1
  258. package/src/flow-self-loop-edge/flow-self-loop-edge.test.tsx +38 -0
  259. package/src/flow-self-loop-edge/flow-self-loop-edge.tsx +7 -0
  260. package/src/flow-weighted-edge/edge-label-pill.test.tsx +20 -0
  261. package/src/flow-weighted-edge/flow-weighted-edge.stories.tsx +1 -0
  262. package/src/flow-weighted-edge/flow-weighted-edge.test.tsx +40 -0
  263. package/src/flow-weighted-edge/flow-weighted-edge.tsx +7 -0
  264. package/src/index.ts +13 -0
  265. package/src/inspector-panel/inspector-panel.tsx +9 -6
  266. package/src/legend/legend.test.tsx +11 -0
  267. package/src/legend/legend.tsx +15 -7
  268. package/src/templates-flow-workspace.stories.tsx +7 -2
package/dist/index.js CHANGED
@@ -1,2318 +1,174 @@
1
1
  "use client";
2
-
3
- // src/canvas-shell/canvas-shell.tsx
4
- import { useEffect } from "react";
2
+ import "./chunk-3LK6OOXE.js";
5
3
  import {
6
- Background,
7
- ReactFlow,
8
- ReactFlowProvider,
9
- useNodesInitialized,
10
- useReactFlow as useReactFlow2,
11
- useStoreApi
12
- } from "@xyflow/react";
13
- import { cn as cn2 } from "@elabs-ai/components-ui/lib/cn";
14
-
15
- // src/helper-lines/helper-lines.tsx
16
- import { forwardRef } from "react";
17
- import { useViewport } from "@xyflow/react";
18
- import { cn } from "@elabs-ai/components-ui/lib/cn";
19
- import { jsx, jsxs } from "react/jsx-runtime";
20
- var HelperLines = forwardRef(function HelperLines2({ horizontal, vertical, className, ...props }, ref) {
21
- const { x, y, zoom } = useViewport();
22
- if (horizontal == null && vertical == null) return null;
23
- const screenY = horizontal == null ? null : y + horizontal * zoom;
24
- const screenX = vertical == null ? null : x + vertical * zoom;
25
- return /* @__PURE__ */ jsxs(
26
- "svg",
27
- {
28
- ref,
29
- "aria-hidden": "true",
30
- width: "100%",
31
- height: "100%",
32
- className: cn("pointer-events-none absolute inset-0 size-full", className),
33
- ...props,
34
- children: [
35
- screenY != null ? /* @__PURE__ */ jsx(
36
- "line",
37
- {
38
- x1: 0,
39
- x2: "100%",
40
- y1: screenY,
41
- y2: screenY,
42
- stroke: "var(--flow-helper-line, var(--primary))",
43
- strokeWidth: 1
44
- }
45
- ) : null,
46
- screenX != null ? /* @__PURE__ */ jsx(
47
- "line",
48
- {
49
- x1: screenX,
50
- x2: screenX,
51
- y1: 0,
52
- y2: "100%",
53
- stroke: "var(--flow-helper-line, var(--primary))",
54
- strokeWidth: 1
55
- }
56
- ) : null
57
- ]
58
- }
59
- );
60
- });
61
-
62
- // src/helper-lines/use-helper-lines.ts
63
- import { useCallback, useState } from "react";
64
- import { useReactFlow } from "@xyflow/react";
65
-
66
- // src/helper-lines/get-helper-lines.ts
67
- var ANCHORS = [0, 0.5, 1];
68
- function getHelperLines(dragged, others, threshold = 5) {
69
- const result = {};
70
- let bestVerticalDist = threshold;
71
- let bestHorizontalDist = threshold;
72
- const draggedX = ANCHORS.map((a) => ({ a, pos: dragged.x + dragged.width * a }));
73
- const draggedY = ANCHORS.map((a) => ({ a, pos: dragged.y + dragged.height * a }));
74
- for (const other of others) {
75
- const otherX = ANCHORS.map((a) => other.x + other.width * a);
76
- const otherY = ANCHORS.map((a) => other.y + other.height * a);
77
- for (const d of draggedX) {
78
- for (const oPos of otherX) {
79
- const dx = Math.abs(d.pos - oPos);
80
- if (dx < bestVerticalDist) {
81
- bestVerticalDist = dx;
82
- result.vertical = oPos;
83
- result.snapX = oPos - dragged.width * d.a;
84
- }
85
- }
86
- }
87
- for (const d of draggedY) {
88
- for (const oPos of otherY) {
89
- const dy = Math.abs(d.pos - oPos);
90
- if (dy < bestHorizontalDist) {
91
- bestHorizontalDist = dy;
92
- result.horizontal = oPos;
93
- result.snapY = oPos - dragged.height * d.a;
94
- }
95
- }
96
- }
97
- }
98
- return result;
99
- }
100
-
101
- // src/helper-lines/use-helper-lines.ts
102
- function nodeRect(node) {
103
- return {
104
- x: node.position.x,
105
- y: node.position.y,
106
- width: node.measured?.width ?? node.width ?? 0,
107
- height: node.measured?.height ?? node.height ?? 0
108
- };
109
- }
110
- function useHelperLines(onNodesChange, options = {}) {
111
- const { threshold = 5 } = options;
112
- const { getNodes } = useReactFlow();
113
- const [helperLineHorizontal, setHelperLineHorizontal] = useState();
114
- const [helperLineVertical, setHelperLineVertical] = useState();
115
- const handleNodesChange = useCallback(
116
- (changes) => {
117
- let horizontal;
118
- let vertical;
119
- const dragChanges = changes.filter(
120
- (c) => c.type === "position" && c.dragging === true && c.position != null
121
- );
122
- if (dragChanges.length === 1) {
123
- const change = dragChanges[0];
124
- const nodes = getNodes();
125
- const dragged = change ? nodes.find((n) => n.id === change.id) : void 0;
126
- if (change && change.position && dragged) {
127
- const draggedRect = {
128
- x: change.position.x,
129
- y: change.position.y,
130
- width: dragged.measured?.width ?? dragged.width ?? 0,
131
- height: dragged.measured?.height ?? dragged.height ?? 0
132
- };
133
- const others = nodes.filter((n) => n.id !== change.id).map(nodeRect);
134
- const {
135
- snapX,
136
- snapY,
137
- vertical: v,
138
- horizontal: h
139
- } = getHelperLines(draggedRect, others, threshold);
140
- if (snapX !== void 0) change.position.x = snapX;
141
- if (snapY !== void 0) change.position.y = snapY;
142
- vertical = v;
143
- horizontal = h;
144
- }
145
- }
146
- setHelperLineHorizontal(horizontal);
147
- setHelperLineVertical(vertical);
148
- onNodesChange?.(changes);
149
- },
150
- [getNodes, onNodesChange, threshold]
151
- );
152
- return { onNodesChange: handleNodesChange, helperLineHorizontal, helperLineVertical };
153
- }
154
-
155
- // src/canvas-shell/use-measured-nodes.ts
156
- import { useCallback as useCallback2, useMemo, useRef, useState as useState2 } from "react";
157
- function useMeasuredNodes(nodes, onNodesChange) {
158
- const measured = useRef(/* @__PURE__ */ new Map());
159
- const [version, setVersion] = useState2(0);
160
- const handleNodesChange = useCallback2(
161
- (changes) => {
162
- let touched = false;
163
- for (const change of changes) {
164
- if (change.type !== "dimensions" || !change.dimensions) continue;
165
- const previous = measured.current.get(change.id);
166
- if (previous?.width === change.dimensions.width && previous.height === change.dimensions.height) {
167
- continue;
168
- }
169
- measured.current.set(change.id, { ...change.dimensions });
170
- touched = true;
171
- }
172
- onNodesChange?.(changes);
173
- if (touched) setVersion((n) => n + 1);
174
- },
175
- [onNodesChange]
176
- );
177
- const mergedNodes = useMemo(() => {
178
- if (!nodes) return nodes;
179
- void version;
180
- let changed = false;
181
- const next = nodes.map((node) => {
182
- const dimensions = measured.current.get(node.id);
183
- if (!dimensions) return node;
184
- if (node.measured?.width === dimensions.width && node.measured?.height === dimensions.height) {
185
- return node;
186
- }
187
- changed = true;
188
- return { ...node, measured: dimensions };
189
- });
190
- return changed ? next : nodes;
191
- }, [nodes, version]);
192
- return {
193
- nodes: mergedNodes,
194
- // Only take the handler over when there is something to measure INTO. An uncontrolled
195
- // canvas is left exactly as it was.
196
- onNodesChange: nodes ? handleNodesChange : onNodesChange
197
- };
198
- }
199
-
200
- // src/canvas-shell/canvas-shell.tsx
201
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
202
- var DEFAULT_ARIA_LABEL_CONFIG = {
203
- "node.a11yDescription.default": "Press Enter or Space to select this node. Press Delete to remove it, Escape to cancel.",
204
- "node.a11yDescription.keyboardDisabled": "Press Enter or Space to select this node. Then use the arrow keys to move it. Press Delete to remove it, Escape to cancel.",
205
- "edge.a11yDescription.default": "Press Enter or Space to select this edge. Press Delete to remove it, Escape to cancel.",
206
- "controls.ariaLabel": "Canvas controls",
207
- "controls.zoomIn.ariaLabel": "Zoom in",
208
- "controls.zoomOut.ariaLabel": "Zoom out",
209
- "controls.fitView.ariaLabel": "Fit view",
210
- "controls.interactive.ariaLabel": "Toggle interactivity",
211
- "minimap.ariaLabel": "Minimap",
212
- "handle.ariaLabel": "Connection handle"
213
- };
214
- function CanvasShell({
215
- helperLines = false,
216
- ...props
217
- }) {
218
- if (helperLines) {
219
- return /* @__PURE__ */ jsx2(ReactFlowProvider, { children: /* @__PURE__ */ jsx2(CanvasShellWithHelperLines, { ...props }) });
220
- }
221
- return /* @__PURE__ */ jsx2(CanvasShellBase, { ...props });
222
- }
223
- function CanvasShellBase({
224
- background = true,
225
- children,
226
- className,
227
- ariaLabelConfig,
228
- nodes,
229
- onNodesChange,
230
- fitViewKey,
231
- fitViewKeyOptions,
232
- ...props
233
- }) {
234
- const measured = useMeasuredNodes(nodes, onNodesChange);
235
- return /* @__PURE__ */ jsx2("div", { className: cn2("h-full w-full bg-canvas", className), children: /* @__PURE__ */ jsxs2(
236
- ReactFlow,
237
- {
238
- fitView: true,
239
- proOptions: { hideAttribution: true },
240
- ariaLabelConfig: { ...DEFAULT_ARIA_LABEL_CONFIG, ...ariaLabelConfig },
241
- nodes: measured.nodes,
242
- onNodesChange: measured.onNodesChange,
243
- ...props,
244
- children: [
245
- background ? /* @__PURE__ */ jsx2(Background, { gap: 20, size: 1, color: "var(--canvas-grid)" }) : null,
246
- fitViewKey === void 0 ? null : /* @__PURE__ */ jsx2(FitViewOnKey, { fitViewKey, options: fitViewKeyOptions }),
247
- children
248
- ]
249
- }
250
- ) });
251
- }
252
- function FitViewOnKey({
253
- fitViewKey,
254
- options
255
- }) {
256
- const { fitView, getNodes, getNodesBounds: getNodesBounds2, setViewport } = useReactFlow2();
257
- const store = useStoreApi();
258
- const nodesInitialized = useNodesInitialized();
259
- useEffect(() => {
260
- if (!nodesInitialized) return;
261
- void fitView(options).then(
262
- () => anchorToStartWhenClamped(store, setViewport, getNodesBounds2, getNodes(), options)
263
- );
264
- }, [fitViewKey, nodesInitialized, fitView, getNodes, getNodesBounds2, setViewport, store]);
265
- return null;
266
- }
267
- function anchorToStartWhenClamped(store, setViewport, getNodesBounds2, nodes, options) {
268
- if (nodes.length === 0) return;
269
- const { width, height, transform, panZoom } = store.getState();
270
- const [x, y, zoom] = transform;
271
- if (!width || !height || !zoom) return;
272
- const bounds = getNodesBounds2(nodes);
273
- const padding = typeof options?.padding === "number" ? options.padding : 0.1;
274
- const padX = width * padding;
275
- const padY = height * padding;
276
- const overflowsX = bounds.width * zoom > width - padX * 2 + 1;
277
- const overflowsY = bounds.height * zoom > height - padY * 2 + 1;
278
- if (!overflowsX && !overflowsY) return;
279
- const next = {
280
- x: overflowsX ? padX - bounds.x * zoom : x,
281
- y: overflowsY ? padY - bounds.y * zoom : y,
282
- zoom
283
- };
284
- if (!panZoom) return;
285
- void setViewport(next);
286
- }
287
- function CanvasShellWithHelperLines({
288
- onNodesChange,
289
- children,
290
- ...props
291
- }) {
292
- const {
293
- onNodesChange: wrappedOnNodesChange,
294
- helperLineHorizontal,
295
- helperLineVertical
296
- } = useHelperLines(onNodesChange);
297
- return /* @__PURE__ */ jsxs2(CanvasShellBase, { ...props, onNodesChange: wrappedOnNodesChange, children: [
298
- children,
299
- /* @__PURE__ */ jsx2(HelperLines, { horizontal: helperLineHorizontal, vertical: helperLineVertical })
300
- ] });
301
- }
302
-
303
- // src/flow-handle/flow-handle-anchor.ts
304
- var FLOW_HANDLE_ANCHOR_CLASS = "transition-none";
305
-
306
- // src/flow-node/flow-node.tsx
307
- import "react";
308
- import { Handle, Position } from "@xyflow/react";
309
- import { Star } from "lucide-react";
310
- import { STATUS_TONE_ICONS } from "@elabs-ai/components-ui";
311
- import { cn as cn3 } from "@elabs-ai/components-ui/lib/cn";
312
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
313
- var FLOW_ALL_SIDE_HANDLES = {
314
- source: ["top", "right", "bottom", "left"],
315
- target: ["top", "right", "bottom", "left"]
316
- };
317
- var toneRing = {
318
- default: "border-border",
319
- accent: "border-primary",
320
- success: "border-success",
321
- warning: "border-warning",
322
- destructive: "border-destructive"
323
- };
324
- var toneIcon = {
325
- accent: Star,
326
- success: STATUS_TONE_ICONS.success,
327
- warning: STATUS_TONE_ICONS.warning,
328
- destructive: STATUS_TONE_ICONS.destructive
329
- };
330
- var toneIconColor = {
331
- accent: "text-primary",
332
- success: "text-success-text",
333
- warning: "text-warning-text",
334
- destructive: "text-destructive-text"
335
- };
336
- var toneLabel = {
337
- accent: "Highlighted",
338
- success: "Success",
339
- warning: "Warning",
340
- destructive: "Destructive"
341
- };
342
- var sidePosition = {
343
- top: Position.Top,
344
- right: Position.Right,
345
- bottom: Position.Bottom,
346
- left: Position.Left
347
- };
348
- var handleClassName = `!size-2 !border-2 !border-flow-edge !bg-flow-node ${FLOW_HANDLE_ANCHOR_CLASS}`;
349
- function FlowNode({
350
- data,
351
- selected,
352
- sourcePosition,
353
- targetPosition
354
- }) {
355
- const tone = data.tone ?? "default";
356
- const ToneIcon = toneIcon[tone];
357
- return /* @__PURE__ */ jsxs3(
358
- "div",
359
- {
360
- "data-slot": "flow-node",
361
- "data-tone": tone,
362
- className: cn3(
363
- "min-w-44 rounded-lg border bg-flow-node px-3 py-2 text-flow-node-foreground shadow-sm transition-[box-shadow,border-color] duration-fast ease-standard",
364
- toneRing[tone],
365
- selected && "ring-2 ring-ring",
366
- "[[data-id]:focus-visible_&]:focus-ring-static"
367
- ),
368
- children: [
369
- data.handles ? /* @__PURE__ */ jsxs3(Fragment, { children: [
370
- (data.handles.target ?? []).map((side) => /* @__PURE__ */ jsx3(
371
- Handle,
372
- {
373
- id: side,
374
- type: "target",
375
- position: sidePosition[side],
376
- className: handleClassName
377
- },
378
- `target-${side}`
379
- )),
380
- (data.handles.source ?? []).map((side) => /* @__PURE__ */ jsx3(
381
- Handle,
382
- {
383
- id: side,
384
- type: "source",
385
- position: sidePosition[side],
386
- className: handleClassName
387
- },
388
- `source-${side}`
389
- ))
390
- ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
391
- /* @__PURE__ */ jsx3(
392
- Handle,
393
- {
394
- type: "target",
395
- position: targetPosition ?? Position.Top,
396
- className: handleClassName
397
- }
398
- ),
399
- /* @__PURE__ */ jsx3(
400
- Handle,
401
- {
402
- type: "source",
403
- position: sourcePosition ?? Position.Bottom,
404
- className: handleClassName
405
- }
406
- )
407
- ] }),
408
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
409
- data.icon ? /* @__PURE__ */ jsx3("span", { className: "[&_svg]:size-4 text-muted-foreground", children: data.icon }) : null,
410
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
411
- data.kind ? /* @__PURE__ */ jsx3("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: data.kind }) : null,
412
- /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-medium", children: data.title }),
413
- data.subtitle ? /* @__PURE__ */ jsx3("div", { className: "truncate text-xs text-muted-foreground", children: data.subtitle }) : null
414
- ] }),
415
- ToneIcon ? /* @__PURE__ */ jsx3(ToneIcon, { "aria-hidden": "true", className: cn3("size-3.5 shrink-0", toneIconColor[tone]) }) : null
416
- ] }),
417
- data.footer ? /* @__PURE__ */ jsx3("div", { className: "mt-2", children: data.footer }) : null,
418
- toneLabel[tone] ? /* @__PURE__ */ jsx3("span", { className: "sr-only", children: toneLabel[tone] }) : null
419
- ]
420
- }
421
- );
422
- }
423
-
424
- // src/flow-edge/flow-edge.tsx
425
- import { getBezierPath } from "@xyflow/react";
426
-
427
- // src/flow-edge-path/flow-edge-path.tsx
428
- import { BaseEdge } from "@xyflow/react";
429
- import { cn as cn4 } from "@elabs-ai/components-ui/lib/cn";
430
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
431
- var FLOW_EDGE_FOCUS_RING_WIDTH = 3;
432
- var FLOW_EDGE_FOCUS_CONTOUR_WIDTH = 6;
433
- function FlowEdgePath({
434
- path,
435
- stroke,
436
- strokeWidth,
437
- strokeDasharray,
438
- strokeOpacity,
439
- markerEnd,
440
- markerStart,
441
- interactionWidth,
442
- className,
443
- style,
444
- ...props
445
- }) {
446
- const focusDash = strokeDasharray ?? "none";
447
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
448
- /* @__PURE__ */ jsx4(
449
- "path",
450
- {
451
- d: path,
452
- fill: "none",
453
- "aria-hidden": "true",
454
- "data-slot": "flow-edge-focus-contour",
455
- className: "pointer-events-none stroke-foreground opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100",
456
- strokeWidth: strokeWidth + FLOW_EDGE_FOCUS_CONTOUR_WIDTH,
457
- strokeDasharray: focusDash,
458
- strokeLinecap: "round"
459
- }
460
- ),
461
- /* @__PURE__ */ jsx4(
462
- "path",
463
- {
464
- d: path,
465
- fill: "none",
466
- "aria-hidden": "true",
467
- "data-slot": "flow-edge-focus-ring",
468
- className: "pointer-events-none stroke-ring opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100",
469
- strokeWidth: strokeWidth + FLOW_EDGE_FOCUS_RING_WIDTH,
470
- strokeDasharray: focusDash,
471
- strokeLinecap: "round"
472
- }
473
- ),
474
- /* @__PURE__ */ jsx4(
475
- BaseEdge,
476
- {
477
- ...props,
478
- path,
479
- markerEnd,
480
- markerStart,
481
- interactionWidth,
482
- className: cn4(
483
- "transition-[stroke,stroke-width] duration-fast ease-standard motion-reduce:transition-none",
484
- className
485
- ),
486
- style: {
487
- stroke,
488
- strokeWidth,
489
- ...strokeDasharray ? { strokeDasharray } : null,
490
- ...strokeOpacity !== void 0 ? { strokeOpacity } : null,
491
- ...style
492
- }
493
- }
494
- )
495
- ] });
496
- }
497
-
498
- // src/flow-edge/flow-edge.tsx
499
- import { jsx as jsx5 } from "react/jsx-runtime";
500
- function FlowEdge({
501
- id,
502
- sourceX,
503
- sourceY,
504
- targetX,
505
- targetY,
506
- sourcePosition,
507
- targetPosition,
508
- markerEnd,
509
- style
510
- }) {
511
- const [edgePath] = getBezierPath({
512
- sourceX,
513
- sourceY,
514
- targetX,
515
- targetY,
516
- sourcePosition,
517
- targetPosition
518
- });
519
- return /* @__PURE__ */ jsx5(
520
- FlowEdgePath,
521
- {
522
- id,
523
- path: edgePath,
524
- markerEnd,
525
- "data-slot": "flow-edge",
526
- stroke: "var(--flow-edge)",
527
- strokeWidth: 1.5,
528
- style
529
- }
530
- );
531
- }
532
-
533
- // src/flow-smart-edge/flow-smart-edge.tsx
4
+ ZoomControls
5
+ } from "./chunk-ATO4LXJF.js";
6
+ import "./chunk-GE73WQUK.js";
7
+ import "./chunk-IJVFPUXO.js";
534
8
  import {
535
- getBezierPath as getBezierPath2,
536
- useInternalNode
537
- } from "@xyflow/react";
538
-
539
- // src/flow-smart-edge/smart-edge-geometry.ts
540
- import { Position as Position2 } from "@xyflow/react";
541
- var HANDLE_SIDES = ["top", "right", "bottom", "left"];
542
- var sideToPosition = {
543
- top: Position2.Top,
544
- right: Position2.Right,
545
- bottom: Position2.Bottom,
546
- left: Position2.Left
547
- };
548
- var positionToSide = {
549
- [Position2.Top]: "top",
550
- [Position2.Right]: "right",
551
- [Position2.Bottom]: "bottom",
552
- [Position2.Left]: "left"
553
- };
554
- function pickClosestAnchors(sources, targets) {
555
- if (!sources.length || !targets.length) return void 0;
556
- let best;
557
- let bestDist = Infinity;
558
- for (const source of sources) {
559
- for (const target of targets) {
560
- const dx = target.x - source.x;
561
- const dy = target.y - source.y;
562
- const dist = dx * dx + dy * dy;
563
- if (dist < bestDist) {
564
- bestDist = dist;
565
- best = { source, target };
566
- }
567
- }
568
- }
569
- return best;
570
- }
571
- function handlePoint(rect, side) {
572
- switch (side) {
573
- case "top":
574
- return { x: rect.x + rect.width / 2, y: rect.y };
575
- case "bottom":
576
- return { x: rect.x + rect.width / 2, y: rect.y + rect.height };
577
- case "left":
578
- return { x: rect.x, y: rect.y + rect.height / 2 };
579
- case "right":
580
- return { x: rect.x + rect.width, y: rect.y + rect.height / 2 };
581
- }
582
- }
583
- function pickClosestHandles(source, sourceSides, target, targetSides) {
584
- const srcSides = sourceSides.length ? sourceSides : HANDLE_SIDES;
585
- const tgtSides = targetSides.length ? targetSides : HANDLE_SIDES;
586
- let best;
587
- let bestDist = Infinity;
588
- for (const s of srcSides) {
589
- const sp = handlePoint(source, s);
590
- for (const t of tgtSides) {
591
- const tp = handlePoint(target, t);
592
- const dx = tp.x - sp.x;
593
- const dy = tp.y - sp.y;
594
- const dist = dx * dx + dy * dy;
595
- if (dist < bestDist) {
596
- bestDist = dist;
597
- best = { sourceSide: s, targetSide: t, sx: sp.x, sy: sp.y, tx: tp.x, ty: tp.y };
598
- }
599
- }
600
- }
601
- return best;
602
- }
603
-
604
- // src/flow-smart-edge/flow-smart-edge.tsx
605
- import { jsx as jsx6 } from "react/jsx-runtime";
606
- function toRect(node) {
607
- return {
608
- x: node.internals.positionAbsolute.x,
609
- y: node.internals.positionAbsolute.y,
610
- width: node.measured.width ?? 0,
611
- height: node.measured.height ?? 0
612
- };
613
- }
614
- function anchorsOf(node, kind, handleId) {
615
- const bounds = node.internals.handleBounds?.[kind] ?? [];
616
- const origin = node.internals.positionAbsolute;
617
- return bounds.filter((handle) => handleId ? handle.id === handleId : true).map((handle) => ({
618
- id: handle.id ?? null,
619
- x: origin.x + handle.x + handle.width / 2,
620
- y: origin.y + handle.y + handle.height / 2,
621
- side: positionToSide[handle.position]
622
- }));
623
- }
624
- function declaredSides(node, kind) {
625
- const userNode = node.internals.userNode;
626
- const data = userNode.data;
627
- const configured = data?.handles?.[kind];
628
- if (configured?.length) return [...configured];
629
- const position = kind === "target" ? userNode.targetPosition : userNode.sourcePosition;
630
- if (position) return [positionToSide[position]];
631
- return [kind === "target" ? "top" : "bottom"];
632
- }
633
- function FlowSmartEdge({
634
- id,
635
- source,
636
- target,
637
- sourceHandleId,
638
- targetHandleId,
639
- markerEnd,
640
- style
641
- }) {
642
- const sourceNode = useInternalNode(source);
643
- const targetNode = useInternalNode(target);
644
- if (!sourceNode || !targetNode) return null;
645
- const picked = pickClosestAnchors(
646
- anchorsOf(sourceNode, "source", sourceHandleId),
647
- anchorsOf(targetNode, "target", targetHandleId)
648
- );
649
- let sx;
650
- let sy;
651
- let tx;
652
- let ty;
653
- let sourcePosition;
654
- let targetPosition;
655
- if (picked) {
656
- ({ x: sx, y: sy } = picked.source);
657
- ({ x: tx, y: ty } = picked.target);
658
- sourcePosition = sideToPosition[picked.source.side];
659
- targetPosition = sideToPosition[picked.target.side];
660
- } else {
661
- const fallback = pickClosestHandles(
662
- toRect(sourceNode),
663
- declaredSides(sourceNode, "source"),
664
- toRect(targetNode),
665
- declaredSides(targetNode, "target")
666
- );
667
- ({ sx, sy, tx, ty } = fallback);
668
- sourcePosition = sideToPosition[fallback.sourceSide];
669
- targetPosition = sideToPosition[fallback.targetSide];
670
- }
671
- const [edgePath] = getBezierPath2({
672
- sourceX: sx,
673
- sourceY: sy,
674
- sourcePosition,
675
- targetX: tx,
676
- targetY: ty,
677
- targetPosition
678
- });
679
- return /* @__PURE__ */ jsx6(
680
- FlowEdgePath,
681
- {
682
- id,
683
- path: edgePath,
684
- markerEnd,
685
- "data-slot": "flow-smart-edge",
686
- stroke: "var(--flow-edge)",
687
- strokeWidth: 1.5,
688
- style
689
- }
690
- );
691
- }
692
-
693
- // src/flow-floating-edge/flow-floating-edge.tsx
694
- import { getBezierPath as getBezierPath3, useInternalNode as useInternalNode2 } from "@xyflow/react";
695
-
696
- // src/flow-floating-edge/floating-edge-geometry.ts
697
- import { Position as Position3 } from "@xyflow/react";
698
- function getNodeIntersection(intersectionNode, targetNode) {
699
- const w = (intersectionNode.measured.width ?? 0) / 2;
700
- const h = (intersectionNode.measured.height ?? 0) / 2;
701
- const pos = intersectionNode.internals.positionAbsolute;
702
- const targetPos = targetNode.internals.positionAbsolute;
703
- const x2 = pos.x + w;
704
- const y2 = pos.y + h;
705
- const x1 = targetPos.x + (targetNode.measured.width ?? 0) / 2;
706
- const y1 = targetPos.y + (targetNode.measured.height ?? 0) / 2;
707
- const safeW = w || 1;
708
- const safeH = h || 1;
709
- const xx1 = (x1 - x2) / (2 * safeW) - (y1 - y2) / (2 * safeH);
710
- const yy1 = (x1 - x2) / (2 * safeW) + (y1 - y2) / (2 * safeH);
711
- const a = 1 / (Math.abs(xx1) + Math.abs(yy1) || 1);
712
- const xx3 = a * xx1;
713
- const yy3 = a * yy1;
714
- const x = safeW * (xx3 + yy3) + x2;
715
- const y = safeH * (-xx3 + yy3) + y2;
716
- return { x, y };
717
- }
718
- function clampToBorder(point, node) {
719
- const x0 = node.internals.positionAbsolute.x;
720
- const y0 = node.internals.positionAbsolute.y;
721
- const w = node.measured.width ?? 0;
722
- const h = node.measured.height ?? 0;
723
- if (w <= 0 || h <= 0) return point;
724
- const cx = Math.min(Math.max(point.x, x0), x0 + w);
725
- const cy = Math.min(Math.max(point.y, y0), y0 + h);
726
- const dLeft = cx - x0;
727
- const dRight = x0 + w - cx;
728
- const dTop = cy - y0;
729
- const dBottom = y0 + h - cy;
730
- const nearest = Math.min(dLeft, dRight, dTop, dBottom);
731
- if (nearest === dLeft) return { x: x0, y: cy };
732
- if (nearest === dRight) return { x: x0 + w, y: cy };
733
- if (nearest === dTop) return { x: cx, y: y0 };
734
- return { x: cx, y: y0 + h };
735
- }
736
- function getEdgeBorderPosition(node, point) {
737
- const nx = Math.round(node.internals.positionAbsolute.x);
738
- const ny = Math.round(node.internals.positionAbsolute.y);
739
- const px = Math.round(point.x);
740
- const py = Math.round(point.y);
741
- const w = node.measured.width ?? 0;
742
- const h = node.measured.height ?? 0;
743
- if (px <= nx + 1) return Position3.Left;
744
- if (px >= nx + w - 1) return Position3.Right;
745
- if (py <= ny + 1) return Position3.Top;
746
- if (py >= ny + h - 1) return Position3.Bottom;
747
- return Position3.Top;
748
- }
749
- function getEdgeParams(source, target) {
750
- const sourceIntersection = clampToBorder(getNodeIntersection(source, target), source);
751
- const targetIntersection = clampToBorder(getNodeIntersection(target, source), target);
752
- return {
753
- sx: sourceIntersection.x,
754
- sy: sourceIntersection.y,
755
- tx: targetIntersection.x,
756
- ty: targetIntersection.y,
757
- sourcePos: getEdgeBorderPosition(source, sourceIntersection),
758
- targetPos: getEdgeBorderPosition(target, targetIntersection)
759
- };
760
- }
761
-
762
- // src/flow-floating-edge/flow-floating-edge.tsx
763
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
764
- var ANCHOR_RADIUS = 4;
765
- function FlowFloatingEdge({ id, source, target, markerEnd, style, data }) {
766
- const sourceNode = useInternalNode2(source);
767
- const targetNode = useInternalNode2(target);
768
- if (!sourceNode || !targetNode) return null;
769
- const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
770
- const [edgePath] = getBezierPath3({
771
- sourceX: sx,
772
- sourceY: sy,
773
- sourcePosition: sourcePos,
774
- targetX: tx,
775
- targetY: ty,
776
- targetPosition: targetPos
777
- });
778
- const showAnchors = data?.anchors !== false;
779
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
780
- /* @__PURE__ */ jsx7(
781
- FlowEdgePath,
782
- {
783
- id,
784
- path: edgePath,
785
- markerEnd,
786
- "data-slot": "flow-floating-edge",
787
- stroke: "var(--flow-edge)",
788
- strokeWidth: 1.5,
789
- style
790
- }
791
- ),
792
- showAnchors ? /* @__PURE__ */ jsxs5(
793
- "g",
794
- {
795
- className: "brand-floating-edge__anchors",
796
- fill: "var(--flow-node)",
797
- stroke: "var(--flow-edge)",
798
- children: [
799
- /* @__PURE__ */ jsx7(
800
- "circle",
801
- {
802
- cx: sx,
803
- cy: sy,
804
- r: ANCHOR_RADIUS,
805
- strokeWidth: 2,
806
- style: { pointerEvents: "none" },
807
- "aria-hidden": "true"
808
- }
809
- ),
810
- /* @__PURE__ */ jsx7(
811
- "circle",
812
- {
813
- cx: tx,
814
- cy: ty,
815
- r: ANCHOR_RADIUS,
816
- strokeWidth: 2,
817
- style: { pointerEvents: "none" },
818
- "aria-hidden": "true"
819
- }
820
- )
821
- ]
822
- }
823
- ) : null
824
- ] });
825
- }
826
-
827
- // src/flow-placeholder-node/flow-placeholder-node.tsx
828
- import { Handle as Handle2, Position as Position4 } from "@xyflow/react";
829
- import { Plus } from "lucide-react";
830
- import { cn as cn5 } from "@elabs-ai/components-ui/lib/cn";
831
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
832
- function FlowPlaceholderNode({ data }) {
833
- const label = data.label ?? "Add node";
834
- return /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
835
- /* @__PURE__ */ jsx8(
836
- Handle2,
837
- {
838
- type: "target",
839
- position: Position4.Top,
840
- className: `!size-2 !border-2 !border-flow-edge !bg-flow-node ${FLOW_HANDLE_ANCHOR_CLASS}`
841
- }
842
- ),
843
- /* @__PURE__ */ jsxs6(
844
- "button",
845
- {
846
- type: "button",
847
- "aria-label": label,
848
- onClick: () => data.onActivate?.(),
849
- className: cn5(
850
- "flex min-w-44 items-center justify-center gap-1.5 rounded-lg border border-dashed border-flow-group-border bg-flow-group px-3 py-2 text-muted-foreground",
851
- "transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground",
852
- "focus-ring"
853
- ),
854
- children: [
855
- /* @__PURE__ */ jsx8(Plus, { className: "size-4", "aria-hidden": "true" }),
856
- /* @__PURE__ */ jsx8("span", { className: "text-body", children: label })
857
- ]
858
- }
859
- )
860
- ] });
861
- }
862
-
863
- // src/flow-button-edge/flow-button-edge.tsx
864
- import { EdgeLabelRenderer, getBezierPath as getBezierPath4 } from "@xyflow/react";
865
- import { Plus as Plus2 } from "lucide-react";
866
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
867
- function FlowButtonEdge({
868
- id,
869
- sourceX,
870
- sourceY,
871
- targetX,
872
- targetY,
873
- sourcePosition,
874
- targetPosition,
875
- markerEnd,
876
- style,
877
- data
878
- }) {
879
- const [edgePath, labelX, labelY] = getBezierPath4({
880
- sourceX,
881
- sourceY,
882
- sourcePosition,
883
- targetX,
884
- targetY,
885
- targetPosition
886
- });
887
- const label = data?.label ?? "Insert node on edge";
888
- return /* @__PURE__ */ jsxs7(Fragment4, { children: [
889
- /* @__PURE__ */ jsx9(
890
- FlowEdgePath,
891
- {
892
- id,
893
- path: edgePath,
894
- markerEnd,
895
- "data-slot": "flow-button-edge",
896
- stroke: "var(--flow-edge)",
897
- strokeWidth: 1.5,
898
- style
899
- }
900
- ),
901
- /* @__PURE__ */ jsx9(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx9(
902
- "div",
903
- {
904
- style: {
905
- position: "absolute",
906
- transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`
907
- },
908
- className: "nodrag nopan pointer-events-auto",
909
- children: /* @__PURE__ */ jsx9(
910
- "button",
911
- {
912
- type: "button",
913
- "aria-label": label,
914
- onClick: () => data?.onInsert?.(),
915
- className: "flex size-5 items-center justify-center rounded-full border border-flow-group-border bg-flow-node text-flow-node-foreground shadow-sm transition-colors duration-fast ease-standard hover:bg-accent hover:text-accent-foreground focus-ring",
916
- children: /* @__PURE__ */ jsx9(Plus2, { className: "size-3", "aria-hidden": "true" })
917
- }
918
- )
919
- }
920
- ) })
921
- ] });
922
- }
923
-
924
- // src/flow-layout/flow-layout.ts
925
- import dagre from "@dagrejs/dagre";
926
- import { Position as Position5 } from "@xyflow/react";
927
- var HANDLE_BY_DIRECTION = {
928
- TB: { source: Position5.Bottom, target: Position5.Top },
929
- BT: { source: Position5.Top, target: Position5.Bottom },
930
- LR: { source: Position5.Right, target: Position5.Left },
931
- RL: { source: Position5.Left, target: Position5.Right }
932
- };
933
- var DEFAULT_NODE_WIDTH = 172;
934
- var DEFAULT_NODE_HEIGHT = 40;
935
- function nodeSize(node) {
936
- return {
937
- width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH,
938
- height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT
939
- };
940
- }
941
- function layoutFlow(nodes, edges, options = {}) {
942
- const { direction = "TB", nodeSpacing = 48, rankSpacing = 72 } = options;
943
- const graph = new dagre.graphlib.Graph();
944
- graph.setDefaultEdgeLabel(() => ({}));
945
- graph.setGraph({ rankdir: direction, nodesep: nodeSpacing, ranksep: rankSpacing });
946
- for (const node of nodes) {
947
- const { width, height } = nodeSize(node);
948
- graph.setNode(node.id, { width, height });
949
- }
950
- const selfLoops = [];
951
- for (const edge of edges) {
952
- if (edge.source === edge.target) {
953
- selfLoops.push(edge.id);
954
- continue;
955
- }
956
- graph.setEdge(edge.source, edge.target);
957
- }
958
- dagre.layout(graph);
959
- const backEdges = collectBackEdges(graph, edges);
960
- const handles = HANDLE_BY_DIRECTION[direction];
961
- const layoutedNodes = nodes.map((node) => {
962
- const dagreNode = graph.node(node.id);
963
- const { width, height } = nodeSize(node);
964
- return {
965
- ...node,
966
- // Move the anchors with the layout direction so `FlowNode` puts its
967
- // handles on the sides the edges actually leave/enter.
968
- sourcePosition: handles.source,
969
- targetPosition: handles.target,
970
- position: {
971
- x: dagreNode.x - width / 2,
972
- y: dagreNode.y - height / 2
973
- }
974
- };
975
- });
976
- return { nodes: layoutedNodes, edges, backEdges, selfLoops };
977
- }
978
- function collectBackEdges(graph, edges) {
979
- const backEdges = [];
980
- for (const edge of edges) {
981
- if (edge.source === edge.target) continue;
982
- const sourceRank = graph.node(edge.source)?.rank;
983
- const targetRank = graph.node(edge.target)?.rank;
984
- if (typeof sourceRank !== "number" || typeof targetRank !== "number") continue;
985
- if (sourceRank >= targetRank) backEdges.push(edge.id);
986
- }
987
- return backEdges;
988
- }
989
-
990
- // src/flow-layout/use-flow-layout.ts
991
- import { useCallback as useCallback3, useRef as useRef2, useState as useState3 } from "react";
992
- import { useNodesInitialized as useNodesInitialized2, useReactFlow as useReactFlow3 } from "@xyflow/react";
993
- function useFlowLayout() {
994
- const { getNodes, getEdges, setNodes, fitView } = useReactFlow3();
995
- const nodesInitialized = useNodesInitialized2();
996
- const [layouting, setLayouting] = useState3(false);
997
- const runningRef = useRef2(false);
998
- const layout = useCallback3(
999
- (direction, options) => {
1000
- if (!nodesInitialized || runningRef.current) return;
1001
- runningRef.current = true;
1002
- setLayouting(true);
1003
- try {
1004
- const { nodes: layoutedNodes } = layoutFlow(getNodes(), getEdges(), {
1005
- ...options,
1006
- direction
1007
- });
1008
- setNodes(layoutedNodes);
1009
- requestAnimationFrame(() => fitView());
1010
- } finally {
1011
- runningRef.current = false;
1012
- setLayouting(false);
1013
- }
1014
- },
1015
- [nodesInitialized, getNodes, getEdges, setNodes, fitView]
1016
- );
1017
- return { layout, ready: nodesInitialized, layouting };
1018
- }
1019
-
1020
- // src/flow-layout/layout-graph.ts
9
+ InspectorPanel
10
+ } from "./chunk-NAQWNX2A.js";
11
+ import "./chunk-UPV2MT4O.js";
1021
12
  import {
1022
- forceCenter,
1023
- forceCollide,
1024
- forceLink,
1025
- forceManyBody,
1026
- forceSimulation
1027
- } from "d3-force";
1028
- var DEFAULT_NODE_WIDTH2 = 172;
1029
- var DEFAULT_NODE_HEIGHT2 = 40;
1030
- var DEFAULT_RING_RADIUS = 180;
1031
- var DEFAULT_FORCE_ITERATIONS = 300;
1032
- var DEFAULT_GRID_SPACING = { x: 200, y: 120 };
1033
- var FORCE_CHARGE_STRENGTH = -300;
1034
- var FORCE_LINK_DISTANCE = 150;
1035
- function resolveNodeSize(node, nodeSize2) {
1036
- if (nodeSize2) return nodeSize2(node.id);
1037
- return {
1038
- width: node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH2,
1039
- height: node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT2
1040
- };
1041
- }
1042
- function layoutGraph(nodes, edges, options) {
1043
- if (nodes.length === 0) return [];
1044
- switch (options.algorithm) {
1045
- case "layered-lr":
1046
- return layoutLayered(nodes, edges, options, "LR");
1047
- case "layered-tb":
1048
- return layoutLayered(nodes, edges, options, "TB");
1049
- case "concentric":
1050
- return layoutConcentric(nodes, edges, options);
1051
- case "force":
1052
- return layoutForce(nodes, edges, options);
1053
- case "grid":
1054
- return layoutGrid(nodes, options);
1055
- default: {
1056
- const exhaustiveCheck = options.algorithm;
1057
- throw new Error(`layoutGraph: unknown algorithm "${String(exhaustiveCheck)}"`);
1058
- }
1059
- }
1060
- }
1061
- function layoutLayered(nodes, edges, options, direction) {
1062
- const { spacing, nodeSize: nodeSize2 } = options;
1063
- const sizedNodes = nodeSize2 ? nodes.map((node) => {
1064
- const { width, height } = nodeSize2(node.id);
1065
- return { ...node, width, height, measured: { width, height } };
1066
- }) : nodes;
1067
- const horizontalRanks = direction === "LR" || direction === "RL";
1068
- const { nodes: laidOut } = layoutFlow(sizedNodes, edges, {
1069
- direction,
1070
- nodeSpacing: horizontalRanks ? spacing?.y : spacing?.x,
1071
- rankSpacing: horizontalRanks ? spacing?.x : spacing?.y
1072
- });
1073
- return nodes.map((node, i) => ({
1074
- ...node,
1075
- sourcePosition: laidOut[i].sourcePosition,
1076
- targetPosition: laidOut[i].targetPosition,
1077
- position: laidOut[i].position
1078
- }));
1079
- }
1080
- function resolveCenterId(centerId, nodeIds, adjacency) {
1081
- if (centerId && nodeIds.includes(centerId)) return centerId;
1082
- let best = nodeIds[0];
1083
- let bestDegree = -1;
1084
- for (const id of nodeIds) {
1085
- const degree = adjacency.get(id)?.size ?? 0;
1086
- if (degree > bestDegree) {
1087
- bestDegree = degree;
1088
- best = id;
1089
- }
1090
- }
1091
- return best;
1092
- }
1093
- function layoutConcentric(nodes, edges, options) {
1094
- const ringRadius = options.ringRadius ?? DEFAULT_RING_RADIUS;
1095
- const nodeIds = nodes.map((node) => node.id);
1096
- const idSet = new Set(nodeIds);
1097
- const adjacency = /* @__PURE__ */ new Map();
1098
- for (const id of nodeIds) adjacency.set(id, /* @__PURE__ */ new Set());
1099
- for (const edge of edges) {
1100
- if (!idSet.has(edge.source) || !idSet.has(edge.target)) continue;
1101
- adjacency.get(edge.source).add(edge.target);
1102
- adjacency.get(edge.target).add(edge.source);
1103
- }
1104
- const centerId = resolveCenterId(options.centerId, nodeIds, adjacency);
1105
- const hopOf = /* @__PURE__ */ new Map();
1106
- hopOf.set(centerId, 0);
1107
- const queue = [centerId];
1108
- let maxHop = 0;
1109
- while (queue.length > 0) {
1110
- const current = queue.shift();
1111
- const currentHop = hopOf.get(current);
1112
- for (const neighbor of adjacency.get(current) ?? []) {
1113
- if (hopOf.has(neighbor)) continue;
1114
- const hop = currentHop + 1;
1115
- hopOf.set(neighbor, hop);
1116
- maxHop = Math.max(maxHop, hop);
1117
- queue.push(neighbor);
1118
- }
1119
- }
1120
- const leftoverHop = maxHop + 1;
1121
- for (const id of nodeIds) {
1122
- if (!hopOf.has(id)) hopOf.set(id, leftoverHop);
1123
- }
1124
- const rings = /* @__PURE__ */ new Map();
1125
- for (const id of nodeIds) {
1126
- const hop = hopOf.get(id);
1127
- if (hop === 0) continue;
1128
- if (!rings.has(hop)) rings.set(hop, []);
1129
- rings.get(hop).push(id);
1130
- }
1131
- const positionById = /* @__PURE__ */ new Map();
1132
- positionById.set(centerId, { x: 0, y: 0 });
1133
- for (const [hop, ids] of rings) {
1134
- const count = ids.length;
1135
- const angleStep = 2 * Math.PI / count;
1136
- const ringOffset = (hop - 1) * (angleStep / 2);
1137
- const radius = hop * ringRadius;
1138
- ids.forEach((id, i) => {
1139
- const angle = ringOffset + i * angleStep;
1140
- positionById.set(id, { x: radius * Math.cos(angle), y: radius * Math.sin(angle) });
1141
- });
1142
- }
1143
- return nodes.map((node) => ({
1144
- ...node,
1145
- position: positionById.get(node.id) ?? { x: 0, y: 0 }
1146
- }));
1147
- }
1148
- function layoutForce(nodes, edges, options) {
1149
- const iterations = options.iterations ?? DEFAULT_FORCE_ITERATIONS;
1150
- const nodeIds = new Set(nodes.map((node) => node.id));
1151
- const seedRadius = Math.max(nodes.length * 20, 100);
1152
- const simNodes = nodes.map((node, i) => {
1153
- const angle = 2 * Math.PI * i / Math.max(nodes.length, 1);
1154
- return { id: node.id, x: seedRadius * Math.cos(angle), y: seedRadius * Math.sin(angle) };
1155
- });
1156
- const simLinks = edges.filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)).map((edge) => ({ source: edge.source, target: edge.target }));
1157
- const collisionById = new Map(
1158
- nodes.map((node) => {
1159
- const { width, height } = resolveNodeSize(node, options.nodeSize);
1160
- return [node.id, Math.max(width, height) / 2];
1161
- })
1162
- );
1163
- const simulation = forceSimulation(simNodes).force("charge", forceManyBody().strength(FORCE_CHARGE_STRENGTH)).force(
1164
- "link",
1165
- forceLink(simLinks).id((d) => d.id).distance(FORCE_LINK_DISTANCE)
1166
- ).force("center", forceCenter(0, 0)).force(
1167
- "collide",
1168
- forceCollide((d) => collisionById.get(d.id) ?? DEFAULT_NODE_WIDTH2 / 2)
1169
- ).stop();
1170
- for (let i = 0; i < iterations; i++) {
1171
- simulation.tick();
1172
- }
1173
- const positionById = new Map(simNodes.map((n) => [n.id, { x: n.x ?? 0, y: n.y ?? 0 }]));
1174
- return nodes.map((node) => ({
1175
- ...node,
1176
- position: positionById.get(node.id) ?? { x: 0, y: 0 }
1177
- }));
1178
- }
1179
- function layoutGrid(nodes, options) {
1180
- const spacing = options.spacing ?? DEFAULT_GRID_SPACING;
1181
- const cols = Math.max(1, Math.ceil(Math.sqrt(nodes.length)));
1182
- const handles = HANDLE_BY_DIRECTION.TB;
1183
- return nodes.map((node, i) => {
1184
- const row = Math.floor(i / cols);
1185
- const col = i % cols;
1186
- return {
1187
- ...node,
1188
- sourcePosition: handles.source,
1189
- targetPosition: handles.target,
1190
- position: { x: col * spacing.x, y: row * spacing.y }
1191
- };
1192
- });
1193
- }
1194
-
1195
- // src/flow-layout/use-auto-layout.ts
1196
- import { useMemo as useMemo2 } from "react";
1197
- function useAutoLayout(nodes, edges, options) {
1198
- return useMemo2(
1199
- () => layoutGraph(nodes, edges, options),
1200
- [nodes, edges, options]
1201
- );
1202
- }
1203
-
1204
- // src/flow-mini-map/flow-mini-map.tsx
1205
- import { MiniMap } from "@xyflow/react";
1206
- import { cn as cn6 } from "@elabs-ai/components-ui/lib/cn";
1207
- import { jsx as jsx10 } from "react/jsx-runtime";
1208
- function FlowMiniMap({
1209
- className,
1210
- nodeColor = "var(--flow-minimap-node)",
1211
- nodeStrokeColor = "var(--flow-minimap-node)",
1212
- maskColor = "var(--flow-minimap-mask)",
1213
- ...props
1214
- }) {
1215
- return /* @__PURE__ */ jsx10(
1216
- MiniMap,
1217
- {
1218
- className: cn6("!rounded-lg !bg-surface-elevated !shadow-ring-sm", className),
1219
- nodeColor,
1220
- nodeStrokeColor,
1221
- maskColor,
1222
- ...props
1223
- }
1224
- );
1225
- }
1226
-
1227
- // src/inspector-panel/inspector-panel.tsx
1228
- import "react";
1229
- import { Reveal, useCollapsiblePanel } from "@elabs-ai/components-ui";
1230
- import { cn as cn7 } from "@elabs-ai/components-ui/lib/cn";
1231
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1232
- var INSPECTOR_PANEL_WIDTH = "18rem";
1233
- function InspectorPanel({
1234
- title = "Inspector",
1235
- children,
1236
- onClose,
1237
- emptyMessage = "Select a node to see its details.",
1238
- hasSelection = true,
1239
- selectionKey,
1240
- open,
1241
- defaultOpen = true,
1242
- onOpenChange,
1243
- side = "right",
1244
- width = INSPECTOR_PANEL_WIDTH,
1245
- className
1246
- }) {
1247
- const panel = useCollapsiblePanel({
1248
- side,
1249
- open,
1250
- defaultOpen,
1251
- onOpenChange,
1252
- widthClassName: "w-(--inspector-panel-width)",
1253
- spacerCollapsedClassName: "group-data-[state=collapsed]:w-0",
1254
- containerSlideClassNames: {
1255
- left: "left-0 group-data-[state=collapsed]:left-[calc(var(--inspector-panel-width)*-1)]",
1256
- right: "right-0 group-data-[state=collapsed]:right-[calc(var(--inspector-panel-width)*-1)]"
1257
- }
1258
- });
1259
- return /* @__PURE__ */ jsxs8(
1260
- "div",
1261
- {
1262
- className: cn7("group relative h-full overflow-hidden", className),
1263
- style: { "--inspector-panel-width": width },
1264
- inert: panel.open ? void 0 : true,
1265
- ...panel.attrs,
1266
- children: [
1267
- /* @__PURE__ */ jsx11("div", { "data-slot": "inspector-panel-gap", className: panel.spacerClassName }),
1268
- /* @__PURE__ */ jsx11(
1269
- "div",
1270
- {
1271
- "data-slot": "inspector-panel-container",
1272
- className: cn7(panel.containerClassName, "absolute inset-y-0 flex h-full border-s"),
1273
- children: /* @__PURE__ */ jsxs8("div", { className: "flex h-full w-full flex-col bg-card", children: [
1274
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
1275
- /* @__PURE__ */ jsx11("h3", { className: "text-body font-semibold text-foreground", children: title }),
1276
- onClose ? /* @__PURE__ */ jsx11(
1277
- "button",
1278
- {
1279
- type: "button",
1280
- "aria-label": "Close inspector",
1281
- onClick: onClose,
1282
- className: "rounded-sm p-1 text-muted-foreground hover:text-foreground focus-ring",
1283
- children: /* @__PURE__ */ jsx11(
1284
- "svg",
1285
- {
1286
- width: "14",
1287
- height: "14",
1288
- viewBox: "0 0 24 24",
1289
- fill: "none",
1290
- stroke: "currentColor",
1291
- strokeWidth: "2",
1292
- strokeLinecap: "round",
1293
- strokeLinejoin: "round",
1294
- "aria-hidden": "true",
1295
- children: /* @__PURE__ */ jsx11("path", { d: "M18 6 6 18M6 6l12 12" })
1296
- }
1297
- )
1298
- }
1299
- ) : null
1300
- ] }),
1301
- hasSelection ? /* @__PURE__ */ jsx11(
1302
- Reveal,
1303
- {
1304
- appear: "fade",
1305
- speed: "fast",
1306
- tabIndex: 0,
1307
- className: "focus-ring flex-1 overflow-y-auto p-4 text-body",
1308
- children
1309
- },
1310
- selectionKey
1311
- ) : /* @__PURE__ */ jsx11("div", { tabIndex: 0, className: "focus-ring flex-1 overflow-y-auto p-4 text-body", children: /* @__PURE__ */ jsx11("p", { className: "text-muted-foreground", children: emptyMessage }) })
1312
- ] })
1313
- }
1314
- )
1315
- ]
1316
- }
1317
- );
1318
- }
1319
-
1320
- // src/legend/legend.tsx
1321
- import { cn as cn8 } from "@elabs-ai/components-ui/lib/cn";
1322
-
1323
- // src/flow-weighted-edge/weight-scale.ts
1324
- var DEFAULT_EDGE_WIDTH_RANGE = [1.5, 8];
1325
- var DEFAULT_SCALE_GROUP = "__default__";
1326
- function computeEdgeWeightScale(edges, opts = {}) {
1327
- const [minWidth, maxWidth] = opts.widthRange ?? DEFAULT_EDGE_WIDTH_RANGE;
1328
- const result = /* @__PURE__ */ new Map();
1329
- const groups = /* @__PURE__ */ new Map();
1330
- for (const edge of edges) {
1331
- const weight = edge.data?.weight;
1332
- if (weight === void 0) {
1333
- result.set(edge.id, minWidth);
1334
- continue;
1335
- }
1336
- const group = edge.data?.scaleGroup ?? DEFAULT_SCALE_GROUP;
1337
- if (opts.scaleGroup !== void 0 && group !== opts.scaleGroup) continue;
1338
- const list = groups.get(group);
1339
- if (list) list.push({ id: edge.id, weight });
1340
- else groups.set(group, [{ id: edge.id, weight }]);
1341
- }
1342
- for (const list of groups.values()) {
1343
- let min = Infinity;
1344
- let max = -Infinity;
1345
- for (const { weight } of list) {
1346
- if (weight < min) min = weight;
1347
- if (weight > max) max = weight;
1348
- }
1349
- const span = max - min;
1350
- for (const { id, weight } of list) {
1351
- const width = span === 0 ? (minWidth + maxWidth) / 2 : minWidth + (weight - min) / span * (maxWidth - minWidth);
1352
- result.set(id, width);
1353
- }
1354
- }
1355
- return result;
1356
- }
1357
-
1358
- // src/legend/legend.tsx
1359
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1360
- var defaultFormat = (value) => value.toLocaleString();
1361
- function Legend(props) {
1362
- if (props.variant === "scale") {
1363
- return /* @__PURE__ */ jsx12(LegendScale, { ...props });
1364
- }
1365
- return /* @__PURE__ */ jsx12(LegendCategorical, { ...props });
1366
- }
1367
- function LegendCategorical({ items, title, className }) {
1368
- return /* @__PURE__ */ jsxs9(
1369
- "div",
1370
- {
1371
- className: cn8(
1372
- "rounded-lg bg-surface-elevated/90 p-3 text-xs shadow-ring-sm backdrop-blur",
1373
- className
1374
- ),
1375
- children: [
1376
- title ? /* @__PURE__ */ jsx12("div", { className: "mb-1.5 font-medium text-foreground", children: title }) : null,
1377
- /* @__PURE__ */ jsx12("ul", { className: "space-y-1", children: items.map((item) => /* @__PURE__ */ jsxs9("li", { className: "flex items-center gap-2 text-muted-foreground", children: [
1378
- /* @__PURE__ */ jsx12(
1379
- "span",
1380
- {
1381
- "aria-hidden": "true",
1382
- className: "size-2.5 shrink-0 rounded-full",
1383
- style: { backgroundColor: item.color }
1384
- }
1385
- ),
1386
- item.label
1387
- ] }, item.label)) })
1388
- ]
1389
- }
1390
- );
1391
- }
1392
- function computeWidthSamples(domain, ticks) {
1393
- const [min, max] = domain;
1394
- const values = ticks === "minmedmax" ? [min, (min + max) / 2, max] : [min, max];
1395
- const likeEdges = values.map((weight, index) => ({
1396
- id: String(index),
1397
- data: { weight }
1398
- }));
1399
- const widths = computeEdgeWeightScale(likeEdges);
1400
- return values.map((value, index) => ({ value, width: widths.get(String(index)) ?? 0 }));
1401
- }
1402
- var COLOR_STOP_FRACTIONS = [0, 0.25, 0.5, 0.75, 1];
1403
- function scaleKindLabel(kind) {
1404
- return kind === "width" ? "width" : "color";
1405
- }
1406
- function LegendScale({
1407
- kind,
1408
- domain,
1409
- format = defaultFormat,
1410
- ticks = "minmax",
1411
- title,
1412
- className
1413
- }) {
1414
- const [min, max] = domain;
1415
- const ariaLabel = `Edge ${scaleKindLabel(kind)} scale, ${format(min)} to ${format(max)}, minimum to maximum`;
1416
- return /* @__PURE__ */ jsxs9(
1417
- "div",
1418
- {
1419
- "aria-label": ariaLabel,
1420
- className: cn8(
1421
- "flex flex-col gap-2 rounded-lg bg-surface-elevated/90 p-3 shadow-ring-sm backdrop-blur",
1422
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1423
- className
1424
- ),
1425
- role: "group",
1426
- tabIndex: 0,
1427
- children: [
1428
- title ? /* @__PURE__ */ jsx12("div", { className: "text-body font-medium text-foreground", children: title }) : null,
1429
- kind === "width" ? /* @__PURE__ */ jsx12(LegendScaleWidth, { domain, format, ticks }) : /* @__PURE__ */ jsx12(LegendScaleColor, { domain, format })
1430
- ]
1431
- }
1432
- );
1433
- }
1434
- function LegendScaleWidth({
1435
- domain,
1436
- format,
1437
- ticks
1438
- }) {
1439
- const samples = computeWidthSamples(domain, ticks);
1440
- return /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: samples.map((sample, index) => (
1441
- // Index disambiguates a zero-width domain, where every sample shares
1442
- // the same value (and therefore the same width).
1443
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-3", children: [
1444
- /* @__PURE__ */ jsx12("svg", { "aria-hidden": "true", className: "shrink-0", height: 16, overflow: "visible", width: 40, children: /* @__PURE__ */ jsx12(
1445
- "line",
1446
- {
1447
- stroke: "var(--flow-edge)",
1448
- strokeLinecap: "round",
1449
- strokeWidth: sample.width,
1450
- x1: 2,
1451
- x2: 38,
1452
- y1: 8,
1453
- y2: 8
1454
- }
1455
- ) }),
1456
- /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate text-meta tabular-nums text-muted-foreground", children: format(sample.value) })
1457
- ] }, `${index}-${sample.value}`)
1458
- )) });
1459
- }
1460
- function LegendScaleColor({
1461
- domain,
1462
- format
1463
- }) {
1464
- const [min, max] = domain;
1465
- const span = max - min;
1466
- const stops = COLOR_STOP_FRACTIONS.map((fraction) => ({
1467
- fraction,
1468
- value: min + fraction * span
1469
- }));
1470
- const gradient = `linear-gradient(to right, ${stops.map(({ fraction }) => {
1471
- const percent = Math.round(fraction * 100);
1472
- return `color-mix(in oklch, var(--flow-edge-strong) ${percent}%, var(--flow-edge-weak)) ${percent}%`;
1473
- }).join(", ")})`;
1474
- return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1.5", children: [
1475
- /* @__PURE__ */ jsx12(
1476
- "div",
1477
- {
1478
- "aria-hidden": "true",
1479
- className: "h-3 w-full rounded-full",
1480
- style: { backgroundImage: gradient }
1481
- }
1482
- ),
1483
- /* @__PURE__ */ jsx12("div", { className: "flex justify-between gap-1", children: stops.map((stop, index) => /* @__PURE__ */ jsx12(
1484
- "span",
1485
- {
1486
- className: cn8(
1487
- "min-w-0 truncate text-meta tabular-nums text-muted-foreground",
1488
- index === 0 && "text-start",
1489
- index === stops.length - 1 && "text-end"
1490
- ),
1491
- children: format(stop.value)
1492
- },
1493
- `${index}-${stop.value}`
1494
- )) })
1495
- ] });
1496
- }
1497
-
1498
- // src/zoom-controls/zoom-controls.tsx
1499
- import { Panel, useReactFlow as useReactFlow4 } from "@xyflow/react";
1500
- import { cn as cn9 } from "@elabs-ai/components-ui/lib/cn";
1501
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1502
- function ControlButton({
1503
- label,
1504
- onClick,
1505
- children
1506
- }) {
1507
- return /* @__PURE__ */ jsx13(
1508
- "button",
1509
- {
1510
- type: "button",
1511
- "aria-label": label,
1512
- onClick,
1513
- className: "flex size-8 items-center justify-center text-foreground transition-colors duration-fast hover:bg-surface-muted focus-ring [&_svg]:size-4",
1514
- children
1515
- }
1516
- );
1517
- }
1518
- function ZoomControls({ position = "bottom-right", className }) {
1519
- const { zoomIn, zoomOut, fitView } = useReactFlow4();
1520
- return /* @__PURE__ */ jsx13(Panel, { position, children: /* @__PURE__ */ jsxs10(
1521
- "div",
1522
- {
1523
- className: cn9(
1524
- "flex divide-x overflow-hidden rounded-lg bg-surface-elevated shadow-ring-sm",
1525
- className
1526
- ),
1527
- children: [
1528
- /* @__PURE__ */ jsx13(ControlButton, { label: "Zoom in", onClick: () => zoomIn(), children: /* @__PURE__ */ jsx13(
1529
- "svg",
1530
- {
1531
- viewBox: "0 0 24 24",
1532
- fill: "none",
1533
- stroke: "currentColor",
1534
- strokeWidth: "2",
1535
- strokeLinecap: "round",
1536
- children: /* @__PURE__ */ jsx13("path", { d: "M12 5v14M5 12h14" })
1537
- }
1538
- ) }),
1539
- /* @__PURE__ */ jsx13(ControlButton, { label: "Zoom out", onClick: () => zoomOut(), children: /* @__PURE__ */ jsx13(
1540
- "svg",
1541
- {
1542
- viewBox: "0 0 24 24",
1543
- fill: "none",
1544
- stroke: "currentColor",
1545
- strokeWidth: "2",
1546
- strokeLinecap: "round",
1547
- children: /* @__PURE__ */ jsx13("path", { d: "M5 12h14" })
1548
- }
1549
- ) }),
1550
- /* @__PURE__ */ jsx13(ControlButton, { label: "Fit view", onClick: () => fitView(), children: /* @__PURE__ */ jsx13(
1551
- "svg",
1552
- {
1553
- viewBox: "0 0 24 24",
1554
- fill: "none",
1555
- stroke: "currentColor",
1556
- strokeWidth: "2",
1557
- strokeLinecap: "round",
1558
- strokeLinejoin: "round",
1559
- children: /* @__PURE__ */ jsx13("path", { d: "M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M16 21h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" })
1560
- }
1561
- ) })
1562
- ]
1563
- }
1564
- ) });
1565
- }
1566
-
1567
- // src/flow-group-node/flow-group-node.tsx
1568
- import "react";
1569
- import { Handle as Handle3, NodeResizer, Position as Position6, useNodes } from "@xyflow/react";
1570
- import { ChevronDown, ChevronRight } from "lucide-react";
1571
- import { cn as cn10 } from "@elabs-ai/components-ui/lib/cn";
1572
-
1573
- // src/use-flow-groups/use-flow-groups.ts
1574
- import { useCallback as useCallback4 } from "react";
1575
- import { useReactFlow as useReactFlow5 } from "@xyflow/react";
1576
-
1577
- // src/use-flow-groups/group-operations.ts
1578
- import { getNodesBounds } from "@xyflow/react";
1579
- var FLOW_GROUP_NODE_TYPE = "group";
1580
- var DEFAULT_GROUP_PADDING = 28;
1581
- var GROUP_HEADER_OFFSET = 44;
1582
- var OVERVIEW_WIDTH = 220;
1583
- var OVERVIEW_HEIGHT = 48;
1584
- var COLLAPSED_STATE_KEY = "__flowGroupCollapsedState";
1585
- function isFlowGroupProxyEdge(edge) {
1586
- return Boolean(edge.data?.__flowGroupProxy);
1587
- }
1588
- function proxyEdgeId(groupId, originalEdgeId) {
1589
- return `flow-group-proxy__${groupId}__${originalEdgeId}`;
1590
- }
1591
- function readCollapsedState(node) {
1592
- return node.data?.[COLLAPSED_STATE_KEY];
1593
- }
1594
- function collectDescendantIds(nodes, groupId) {
1595
- const childrenByParent = /* @__PURE__ */ new Map();
1596
- for (const n of nodes) {
1597
- if (n.parentId) {
1598
- const arr = childrenByParent.get(n.parentId);
1599
- if (arr) arr.push(n);
1600
- else childrenByParent.set(n.parentId, [n]);
1601
- }
1602
- }
1603
- const out = /* @__PURE__ */ new Set();
1604
- const stack = [...childrenByParent.get(groupId) ?? []];
1605
- while (stack.length > 0) {
1606
- const n = stack.pop();
1607
- if (out.has(n.id)) continue;
1608
- out.add(n.id);
1609
- const kids = childrenByParent.get(n.id);
1610
- if (kids) stack.push(...kids);
1611
- }
1612
- return out;
1613
- }
1614
- function groupNodes(nodes, edges, nodeIds, options) {
1615
- const idSet = new Set(nodeIds);
1616
- const children = nodes.filter((n) => idSet.has(n.id));
1617
- if (children.length === 0) return { nodes, edges };
1618
- const bounds = getNodesBounds(children);
1619
- const padding = options.padding ?? DEFAULT_GROUP_PADDING;
1620
- const originX = bounds.x - padding;
1621
- const originY = bounds.y - padding - GROUP_HEADER_OFFSET;
1622
- const data = {
1623
- title: options.title ?? "Group",
1624
- childCount: children.length,
1625
- ...options.tone ? { tone: options.tone } : {}
1626
- };
1627
- const groupNode = {
1628
- id: options.groupId,
1629
- type: FLOW_GROUP_NODE_TYPE,
1630
- position: { x: originX, y: originY },
1631
- width: bounds.width + padding * 2,
1632
- height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,
1633
- data
1634
- };
1635
- const reparented = /* @__PURE__ */ new Map();
1636
- for (const child of children) {
1637
- reparented.set(child.id, {
1638
- ...child,
1639
- parentId: options.groupId,
1640
- extent: "parent",
1641
- position: { x: child.position.x - originX, y: child.position.y - originY }
1642
- });
1643
- }
1644
- let earliest = nodes.length;
1645
- for (let i = 0; i < nodes.length; i++) {
1646
- if (idSet.has(nodes[i].id)) {
1647
- earliest = i;
1648
- break;
1649
- }
1650
- }
1651
- const mapped = nodes.map((n) => reparented.get(n.id) ?? n);
1652
- const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];
1653
- return { nodes: resultNodes, edges };
1654
- }
1655
- function ungroup(nodes, edges, groupId) {
1656
- const group = nodes.find((n) => n.id === groupId);
1657
- if (!group) return { nodes, edges };
1658
- const base = group.data?.collapsed ? expandGroup(nodes, edges, groupId) : { nodes, edges };
1659
- const liveGroup = base.nodes.find((n) => n.id === groupId);
1660
- const gx = liveGroup.position.x;
1661
- const gy = liveGroup.position.y;
1662
- const resultNodes = base.nodes.filter((n) => n.id !== groupId).map((n) => {
1663
- if (n.parentId === groupId) {
1664
- const {
1665
- parentId: _parentId,
1666
- extent: _extent,
1667
- ...rest
1668
- } = n;
1669
- return {
1670
- ...rest,
1671
- position: { x: n.position.x + gx, y: n.position.y + gy }
1672
- };
1673
- }
1674
- return n;
1675
- });
1676
- return { nodes: resultNodes, edges: base.edges };
1677
- }
1678
- function collapseGroup(nodes, edges, groupId) {
1679
- const group = nodes.find((n) => n.id === groupId);
1680
- if (!group) return { nodes, edges };
1681
- if (group.data?.collapsed) return { nodes, edges };
1682
- const descendantIds = collectDescendantIds(nodes, groupId);
1683
- const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));
1684
- const directChildCount = nodes.filter((n) => n.parentId === groupId).length;
1685
- const isInside = (id) => descendantIds.has(id);
1686
- const reroutedEdges = [];
1687
- const proxyEdges = [];
1688
- for (const edge of edges) {
1689
- const sourceInside = isInside(edge.source);
1690
- const targetInside = isInside(edge.target);
1691
- if (sourceInside === targetInside) continue;
1692
- reroutedEdges.push(edge);
1693
- const proxyData = {
1694
- ...edge.data ?? {},
1695
- __flowGroupProxy: true,
1696
- groupId
1697
- };
1698
- proxyEdges.push({
1699
- ...edge,
1700
- id: proxyEdgeId(groupId, edge.id),
1701
- source: sourceInside ? groupId : edge.source,
1702
- target: targetInside ? groupId : edge.target,
1703
- sourceHandle: void 0,
1704
- targetHandle: void 0,
1705
- data: proxyData
1706
- });
1707
- }
1708
- const collapsedState = {
1709
- groupSnapshot: group,
1710
- descendantSnapshots,
1711
- reroutedEdges
1712
- };
1713
- const resultNodes = nodes.map((n) => {
1714
- if (n.id === groupId) {
1715
- return {
1716
- ...n,
1717
- width: OVERVIEW_WIDTH,
1718
- height: OVERVIEW_HEIGHT,
1719
- data: {
1720
- ...n.data,
1721
- collapsed: true,
1722
- childCount: directChildCount,
1723
- [COLLAPSED_STATE_KEY]: collapsedState
1724
- }
1725
- };
1726
- }
1727
- if (descendantIds.has(n.id)) {
1728
- return { ...n, hidden: true };
1729
- }
1730
- return n;
1731
- });
1732
- const reroutedIds = new Set(reroutedEdges.map((e) => e.id));
1733
- const resultEdges = edges.map(
1734
- (e) => reroutedIds.has(e.id) ? { ...e, hidden: true } : e
1735
- );
1736
- resultEdges.push(...proxyEdges);
1737
- return { nodes: resultNodes, edges: resultEdges };
1738
- }
1739
- function expandGroup(nodes, edges, groupId) {
1740
- const group = nodes.find((n) => n.id === groupId);
1741
- const state = group ? readCollapsedState(group) : void 0;
1742
- if (!group || !state) return { nodes, edges };
1743
- const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));
1744
- const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));
1745
- const resultNodes = nodes.map((n) => {
1746
- if (n.id === groupId) return state.groupSnapshot;
1747
- const snap = snapshotById.get(n.id);
1748
- return snap ? snap : n;
1749
- });
1750
- const resultEdges = edges.filter(
1751
- (e) => !(isFlowGroupProxyEdge(e) && e.data?.groupId === groupId)
1752
- ).map((e) => {
1753
- const original = reroutedById.get(e.id);
1754
- return original ? original : e;
1755
- });
1756
- return { nodes: resultNodes, edges: resultEdges };
1757
- }
1758
- function toggleGroupCollapsed(nodes, edges, groupId) {
1759
- const group = nodes.find((n) => n.id === groupId);
1760
- if (!group) return { nodes, edges };
1761
- return group.data?.collapsed ? expandGroup(nodes, edges, groupId) : collapseGroup(nodes, edges, groupId);
1762
- }
1763
-
1764
- // src/use-flow-groups/use-flow-groups.ts
1765
- function generateGroupId() {
1766
- return `flow-group-${Math.random().toString(36).slice(2, 9)}`;
1767
- }
1768
- function useFlowGroups() {
1769
- const { getNodes, getEdges, setNodes, setEdges } = useReactFlow5();
1770
- const apply = useCallback4(
1771
- (result) => {
1772
- setNodes(result.nodes);
1773
- setEdges(result.edges);
1774
- },
1775
- [setNodes, setEdges]
1776
- );
1777
- const groupNodes2 = useCallback4(
1778
- (nodeIds, options) => {
1779
- if (nodeIds.length === 0) return void 0;
1780
- const groupId = options?.groupId ?? generateGroupId();
1781
- const result = groupNodes(getNodes(), getEdges(), nodeIds, {
1782
- ...options,
1783
- groupId
1784
- });
1785
- apply(result);
1786
- return groupId;
1787
- },
1788
- [getNodes, getEdges, apply]
1789
- );
1790
- const groupSelection = useCallback4(
1791
- (options) => {
1792
- const selectedIds = getNodes().filter((n) => n.selected && !n.parentId).map((n) => n.id);
1793
- return groupNodes2(selectedIds, options);
1794
- },
1795
- [getNodes, groupNodes2]
1796
- );
1797
- const ungroup2 = useCallback4(
1798
- (groupId) => apply(ungroup(getNodes(), getEdges(), groupId)),
1799
- [getNodes, getEdges, apply]
1800
- );
1801
- const collapseGroup2 = useCallback4(
1802
- (groupId) => apply(collapseGroup(getNodes(), getEdges(), groupId)),
1803
- [getNodes, getEdges, apply]
1804
- );
1805
- const expandGroup2 = useCallback4(
1806
- (groupId) => apply(expandGroup(getNodes(), getEdges(), groupId)),
1807
- [getNodes, getEdges, apply]
1808
- );
1809
- const toggleCollapse = useCallback4(
1810
- (groupId) => apply(toggleGroupCollapsed(getNodes(), getEdges(), groupId)),
1811
- [getNodes, getEdges, apply]
1812
- );
1813
- return {
1814
- groupNodes: groupNodes2,
1815
- groupSelection,
1816
- ungroup: ungroup2,
1817
- collapseGroup: collapseGroup2,
1818
- expandGroup: expandGroup2,
1819
- toggleCollapse
1820
- };
1821
- }
1822
-
1823
- // src/flow-group-node/flow-group-node.tsx
1824
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1825
- var toneMark = {
1826
- default: "text-muted-foreground",
1827
- accent: "text-primary-text",
1828
- success: "text-success",
1829
- warning: "text-warning",
1830
- destructive: "text-destructive"
1831
- };
1832
- var toneInk = {
1833
- default: "text-muted-foreground",
1834
- accent: "text-primary-text",
1835
- success: "text-success-text",
1836
- warning: "text-warning-text",
1837
- destructive: "text-destructive-text"
1838
- };
1839
- var handleClassName2 = `!size-2 !border-2 !border-flow-group-border !bg-flow-group ${FLOW_HANDLE_ANCHOR_CLASS}`;
1840
- function FlowGroupNode({ id, data, selected }) {
1841
- const { toggleCollapse } = useFlowGroups();
1842
- const nodes = useNodes();
1843
- const collapsed = data.collapsed ?? false;
1844
- const tone = data.tone ?? "default";
1845
- const liveChildCount = nodes.filter((n) => n.parentId === id).length;
1846
- const childCount = liveChildCount || data.childCount || 0;
1847
- return /* @__PURE__ */ jsxs11(
1848
- "div",
1849
- {
1850
- className: cn10(
1851
- "flex h-full w-full flex-col rounded-lg border bg-flow-group/60 text-foreground",
1852
- "border-flow-group-border transition-[box-shadow,border-color] duration-fast ease-standard motion-reduce:transition-none",
1853
- collapsed ? "shadow-sm" : "shadow-none",
1854
- selected && "ring-2 ring-ring"
1855
- ),
1856
- children: [
1857
- !collapsed && selected ? /* @__PURE__ */ jsx14(
1858
- NodeResizer,
1859
- {
1860
- minWidth: 160,
1861
- minHeight: 96,
1862
- lineClassName: "!border-flow-group-border",
1863
- handleClassName: handleClassName2
1864
- }
1865
- ) : null,
1866
- /* @__PURE__ */ jsx14(Handle3, { type: "target", position: Position6.Top, className: handleClassName2 }),
1867
- /* @__PURE__ */ jsx14(Handle3, { type: "source", position: Position6.Bottom, className: handleClassName2 }),
1868
- /* @__PURE__ */ jsxs11(
1869
- "div",
1870
- {
1871
- className: cn10(
1872
- "flex items-center gap-2 px-3 py-2",
1873
- !collapsed && "border-b border-flow-group-border"
1874
- ),
1875
- children: [
1876
- /* @__PURE__ */ jsx14(
1877
- "button",
1878
- {
1879
- type: "button",
1880
- onClick: () => toggleCollapse(id),
1881
- "aria-label": collapsed ? `Expand group ${data.title}` : `Collapse group ${data.title}`,
1882
- "aria-expanded": !collapsed,
1883
- className: cn10(
1884
- "-ml-1 grid size-5 shrink-0 place-items-center rounded",
1885
- "text-muted-foreground hover:bg-muted hover:text-foreground",
1886
- "focus-ring"
1887
- ),
1888
- children: collapsed ? /* @__PURE__ */ jsx14(ChevronRight, { "aria-hidden": "true", className: "size-4" }) : /* @__PURE__ */ jsx14(ChevronDown, { "aria-hidden": "true", className: "size-4" })
1889
- }
1890
- ),
1891
- data.icon ? /* @__PURE__ */ jsx14("span", { className: cn10("[&_svg]:size-4", toneMark[tone]), children: data.icon }) : null,
1892
- /* @__PURE__ */ jsx14("span", { className: "min-w-0 flex-1 truncate text-body font-medium", children: data.title }),
1893
- /* @__PURE__ */ jsx14(
1894
- "span",
1895
- {
1896
- className: cn10(
1897
- "shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-meta font-medium tabular-nums",
1898
- toneInk[tone]
1899
- ),
1900
- "aria-label": `${childCount} ${childCount === 1 ? "node" : "nodes"}`,
1901
- children: childCount
1902
- }
1903
- )
1904
- ]
1905
- }
1906
- )
1907
- ]
1908
- }
1909
- );
1910
- }
13
+ Legend
14
+ } from "./chunk-NVJVWGUS.js";
15
+ import "./chunk-3Y5TCGC3.js";
16
+ import {
17
+ FlowPlaceholderNode
18
+ } from "./chunk-5TVVM4GR.js";
19
+ import "./chunk-BWGYV4JZ.js";
20
+ import {
21
+ FlowSelfLoopEdge
22
+ } from "./chunk-CWNO63LQ.js";
23
+ import "./chunk-W3TQMUGX.js";
24
+ import {
25
+ buildWeightedEdgeAriaLabel,
26
+ withWeightedEdgeAria
27
+ } from "./chunk-FAFQ5KLB.js";
28
+ import {
29
+ FlowWeightedEdge
30
+ } from "./chunk-K5RPSVO6.js";
31
+ import {
32
+ backEdgeDetour
33
+ } from "./chunk-ZXZTLQIX.js";
34
+ import {
35
+ EdgeLabelPill
36
+ } from "./chunk-I53W3CFN.js";
37
+ import {
38
+ DEFAULT_EDGE_WIDTH_RANGE,
39
+ computeEdgeWeightScale
40
+ } from "./chunk-3BTU2VWZ.js";
41
+ import {
42
+ DEFAULT_LOOP_RADIUS,
43
+ selfLoopHandleArc,
44
+ selfLoopPath
45
+ } from "./chunk-TEZ3OXAY.js";
46
+ import "./chunk-UZJZY4A5.js";
47
+ import {
48
+ FlowSmartEdge
49
+ } from "./chunk-4VYHZM3S.js";
50
+ import {
51
+ HANDLE_SIDES,
52
+ handlePoint,
53
+ pickClosestAnchors,
54
+ pickClosestHandles,
55
+ positionToSide,
56
+ sideToPosition
57
+ } from "./chunk-DHPUXKCZ.js";
58
+ import {
59
+ layoutFlowElk
60
+ } from "./chunk-ATXVXTMZ.js";
61
+ import "./chunk-O2F3R3PV.js";
62
+ import {
63
+ FlowMiniMap
64
+ } from "./chunk-WN46RIOC.js";
65
+ import "./chunk-ADHLQF56.js";
66
+ import {
67
+ FLOW_ALL_SIDE_HANDLES,
68
+ FlowNode
69
+ } from "./chunk-FIRO6Y3R.js";
70
+ import "./chunk-A66JBW4X.js";
71
+ import "./chunk-TSB6RE7L.js";
72
+ import {
73
+ FlowGroupNode
74
+ } from "./chunk-3M3FQAD4.js";
75
+ import "./chunk-ELOADZSX.js";
76
+ import {
77
+ useFlowGroups
78
+ } from "./chunk-LQOZCOK3.js";
79
+ import {
80
+ FLOW_GROUP_NODE_TYPE,
81
+ collapseGroup,
82
+ expandGroup,
83
+ groupNodes,
84
+ isFlowGroupProxyEdge,
85
+ toggleGroupCollapsed,
86
+ ungroup
87
+ } from "./chunk-TGWPM2CG.js";
88
+ import "./chunk-5XT73MQQ.js";
89
+ import {
90
+ FLOW_HANDLE_ANCHOR_CLASS
91
+ } from "./chunk-ROYAQKJA.js";
92
+ import {
93
+ pinBackbone
94
+ } from "./chunk-6LQD5Y5R.js";
95
+ import "./chunk-PE6JXVOX.js";
96
+ import {
97
+ useAutoLayout
98
+ } from "./chunk-6SR26QY2.js";
99
+ import {
100
+ layoutGraph
101
+ } from "./chunk-DNX7G3KA.js";
102
+ import {
103
+ useFlowLayout
104
+ } from "./chunk-GIARAOU6.js";
105
+ import {
106
+ layoutFlow
107
+ } from "./chunk-HTLTCCFA.js";
108
+ import "./chunk-YG3KXTZW.js";
109
+ import {
110
+ DEFAULT_EDGE_TOKEN_RADIUS,
111
+ FlowEdgeTokens
112
+ } from "./chunk-ROJEIIA4.js";
113
+ import {
114
+ createEdgePathSampler,
115
+ flattenEdgePath
116
+ } from "./chunk-T7S7NJFD.js";
117
+ import "./chunk-ELCLRVD4.js";
118
+ import {
119
+ FlowEdge
120
+ } from "./chunk-SHCZK4DO.js";
121
+ import {
122
+ FlowFloatingEdge
123
+ } from "./chunk-K6U27YJ6.js";
124
+ import {
125
+ getEdgeParams
126
+ } from "./chunk-A7AFM3LI.js";
127
+ import "./chunk-OMFQHMA6.js";
128
+ import {
129
+ CanvasShell
130
+ } from "./chunk-75JJK6BH.js";
131
+ import {
132
+ useHelperLines
133
+ } from "./chunk-ZATVF42E.js";
134
+ import {
135
+ getHelperLines
136
+ } from "./chunk-DNZRAJAQ.js";
137
+ import {
138
+ HelperLines
139
+ } from "./chunk-BJ7UMCD3.js";
140
+ import "./chunk-EUNV2MR5.js";
141
+ import "./chunk-JUPMOISE.js";
142
+ import "./chunk-B6IUPZU7.js";
143
+ import {
144
+ FlowButtonEdge
145
+ } from "./chunk-XZFE2YYH.js";
146
+ import "./chunk-KDVMGMAK.js";
147
+ import {
148
+ FLOW_EDGE_FOCUS_CONTOUR_WIDTH,
149
+ FLOW_EDGE_FOCUS_RING_WIDTH,
150
+ FlowEdgePath
151
+ } from "./chunk-SXXJV6YJ.js";
1911
152
 
1912
153
  // src/index.ts
1913
154
  import {
1914
- Background as Background2,
155
+ Background,
1915
156
  Controls,
1916
- MiniMap as MiniMap2,
1917
- Panel as Panel2,
1918
- Position as Position8,
1919
- ReactFlow as ReactFlow2,
1920
- ReactFlowProvider as ReactFlowProvider2,
1921
- useReactFlow as useReactFlow6,
157
+ MiniMap,
158
+ Panel,
159
+ Position,
160
+ ReactFlow,
161
+ ReactFlowProvider,
162
+ useReactFlow,
1922
163
  useNodesState,
1923
164
  useEdgesState,
1924
165
  addEdge
1925
166
  } from "@xyflow/react";
1926
-
1927
- // src/flow-weighted-edge/flow-weighted-edge.tsx
1928
- import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
1929
- import {
1930
- Position as Position7,
1931
- getBezierPath as getBezierPath5,
1932
- getSmoothStepPath,
1933
- useEdges,
1934
- useStore
1935
- } from "@xyflow/react";
1936
- import { resolveTokenColor } from "@elabs-ai/components-tokens";
1937
-
1938
- // src/flow-weighted-edge/edge-label-pill.tsx
1939
- import { EdgeLabelRenderer as EdgeLabelRenderer2 } from "@xyflow/react";
1940
- import { cn as cn11 } from "@elabs-ai/components-ui/lib/cn";
1941
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1942
- function EdgeLabelPill({
1943
- label,
1944
- secondaryLabel,
1945
- x,
1946
- y,
1947
- selected,
1948
- className,
1949
- ...props
1950
- }) {
1951
- if (!label && !secondaryLabel) return null;
1952
- const accessibleName = [label, secondaryLabel].filter(Boolean).join(" \xB7 ");
1953
- return /* @__PURE__ */ jsx15(EdgeLabelRenderer2, { children: /* @__PURE__ */ jsx15(
1954
- "div",
1955
- {
1956
- style: {
1957
- position: "absolute",
1958
- transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`,
1959
- pointerEvents: "none"
1960
- },
1961
- className: "nodrag nopan",
1962
- "data-slot": "edge-label-pill-anchor",
1963
- children: /* @__PURE__ */ jsxs12(
1964
- "button",
1965
- {
1966
- type: "button",
1967
- "aria-label": accessibleName,
1968
- "data-slot": "edge-label-pill",
1969
- ...props,
1970
- className: cn11(
1971
- "pointer-events-auto flex items-center gap-1 rounded-full border bg-flow-node px-2 py-0.5",
1972
- "text-meta font-medium text-flow-node-foreground shadow-sm",
1973
- "transition-colors duration-fast ease-standard",
1974
- "focus-ring",
1975
- selected ? "border-ring" : "border-flow-group-border",
1976
- className
1977
- ),
1978
- children: [
1979
- label ? /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: label }) : null,
1980
- secondaryLabel ? /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", className: "text-flow-node-foreground/70 tabular-nums", children: secondaryLabel }) : null
1981
- ]
1982
- }
1983
- )
1984
- }
1985
- ) });
1986
- }
1987
-
1988
- // src/flow-weighted-edge/back-edge-geometry.ts
1989
- function backEdgeDetour(rects, axis, span, clearance) {
1990
- const [lo, hi] = span[0] <= span[1] ? span : [span[1], span[0]];
1991
- let far = Number.NEGATIVE_INFINITY;
1992
- for (const rect of rects) {
1993
- if (!(rect.width > 0) || !(rect.height > 0)) continue;
1994
- const [near, beyond] = axis === "vertical" ? [rect.y, rect.y + rect.height] : [rect.x, rect.x + rect.width];
1995
- if (beyond < lo || near > hi) continue;
1996
- far = Math.max(far, axis === "vertical" ? rect.x + rect.width : rect.y + rect.height);
1997
- }
1998
- return Number.isFinite(far) ? far + clearance : null;
1999
- }
2000
-
2001
- // src/flow-weighted-edge/flow-weighted-edge.tsx
2002
- import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
2003
- var NO_NODES = [];
2004
- var FALLBACK_WEAK = "#6085a1";
2005
- var FALLBACK_STRONG = "#496d89";
2006
- var BACK_EDGE_DASHARRAY = "6 4";
2007
- var BACK_EDGE_OPACITY = 0.7;
2008
- var BACK_EDGE_CLEARANCE = 40;
2009
- var DEFAULT_BACK_EDGE_LABEL = "Back edge \u2014 runs against the process direction";
2010
- function clamp01(t) {
2011
- return Math.min(1, Math.max(0, t));
2012
- }
2013
- function hexToRgb(hex) {
2014
- const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(hex);
2015
- if (!m) return [0, 0, 0];
2016
- return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
2017
- }
2018
- function toHexByte(n) {
2019
- return Math.round(Math.min(255, Math.max(0, n))).toString(16).padStart(2, "0");
2020
- }
2021
- function mixHex(a, b, t) {
2022
- const [ar, ag, ab] = hexToRgb(a);
2023
- const [br, bg, bb] = hexToRgb(b);
2024
- const r = ar + (br - ar) * t;
2025
- const g = ag + (bg - ag) * t;
2026
- const bl = ab + (bb - ab) * t;
2027
- return `#${toHexByte(r)}${toHexByte(g)}${toHexByte(bl)}`;
2028
- }
2029
- function resolveValueStrokeColor(value, domain) {
2030
- if (value === void 0 || !domain) return void 0;
2031
- const [lo, hi] = domain;
2032
- const t = hi === lo ? 1 : clamp01((value - lo) / (hi - lo));
2033
- const weak = resolveTokenColor("--flow-edge-weak", { fallback: FALLBACK_WEAK });
2034
- const strong = resolveTokenColor("--flow-edge-strong", { fallback: FALLBACK_STRONG });
2035
- return mixHex(weak, strong, t);
2036
- }
2037
- function FlowWeightedEdge({
2038
- id,
2039
- sourceX,
2040
- sourceY,
2041
- targetX,
2042
- targetY,
2043
- sourcePosition,
2044
- targetPosition,
2045
- markerEnd,
2046
- style,
2047
- selected,
2048
- data
2049
- }) {
2050
- const edges = useEdges();
2051
- const widthByEdgeId = useMemo3(
2052
- () => computeEdgeWeightScale(edges),
2053
- [edges]
2054
- );
2055
- const variant = data?.variant ?? "forward";
2056
- const isBack = variant === "back";
2057
- const nodes = useStore(
2058
- useCallback5((s) => isBack ? s.nodes : NO_NODES, [isBack])
2059
- );
2060
- const pathType = data?.path ?? "bezier";
2061
- const axis = targetPosition === Position7.Top || targetPosition === Position7.Bottom ? "vertical" : "horizontal";
2062
- const rects = useMemo3(() => {
2063
- if (!isBack) return [];
2064
- const out = [];
2065
- for (const node of nodes) {
2066
- if (node.parentId) continue;
2067
- const width = node.measured?.width ?? node.width;
2068
- const height = node.measured?.height ?? node.height;
2069
- if (!width || !height) continue;
2070
- out.push({ x: node.position.x, y: node.position.y, width, height });
2071
- }
2072
- return out;
2073
- }, [isBack, nodes]);
2074
- const detour = isBack ? backEdgeDetour(
2075
- rects,
2076
- axis,
2077
- axis === "vertical" ? [sourceY, targetY] : [sourceX, targetX],
2078
- BACK_EDGE_CLEARANCE
2079
- ) : null;
2080
- const [edgePath, labelX, labelY] = isBack ? getSmoothStepPath({
2081
- sourceX,
2082
- sourceY,
2083
- sourcePosition,
2084
- targetX,
2085
- targetY,
2086
- targetPosition,
2087
- offset: BACK_EDGE_CLEARANCE,
2088
- ...detour === null ? {} : axis === "vertical" ? { centerX: detour } : { centerY: detour }
2089
- }) : pathType === "smoothstep" ? getSmoothStepPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }) : getBezierPath5({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
2090
- const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];
2091
- const valueColor = useMemo3(
2092
- () => resolveValueStrokeColor(data?.value, data?.valueDomain),
2093
- [data?.value, data?.valueDomain]
2094
- );
2095
- const stroke = selected ? "var(--ring)" : valueColor ?? "var(--flow-edge)";
2096
- const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;
2097
- const edge = /* @__PURE__ */ jsx16(
2098
- FlowEdgePath,
2099
- {
2100
- id,
2101
- path: edgePath,
2102
- markerEnd,
2103
- "data-slot": "flow-weighted-edge",
2104
- "data-variant": variant,
2105
- "data-weight": data?.weight,
2106
- "data-value": data?.value,
2107
- stroke,
2108
- strokeWidth,
2109
- strokeDasharray: isBack ? BACK_EDGE_DASHARRAY : void 0,
2110
- strokeOpacity: isBack ? BACK_EDGE_OPACITY : void 0,
2111
- style
2112
- }
2113
- );
2114
- return /* @__PURE__ */ jsxs13(Fragment5, { children: [
2115
- isBack ? (
2116
- // A `data-variant` is invisible to assistive technology, so the back
2117
- // edge's meaning is also published as a named graphic. Only the back
2118
- // variant is wrapped — a forward edge's DOM is unchanged from before
2119
- // this prop existed.
2120
- /* @__PURE__ */ jsx16("g", { role: "img", "aria-label": data?.variantLabel ?? DEFAULT_BACK_EDGE_LABEL, children: edge })
2121
- ) : edge,
2122
- /* @__PURE__ */ jsx16(
2123
- EdgeLabelPill,
2124
- {
2125
- label: data?.label,
2126
- secondaryLabel: data?.secondaryLabel,
2127
- x: labelX,
2128
- y: labelY,
2129
- selected,
2130
- ...data?.labelProps
2131
- }
2132
- )
2133
- ] });
2134
- }
2135
-
2136
- // src/flow-weighted-edge/edge-aria.ts
2137
- var DEFAULT_WEIGHT_LABEL = "weight";
2138
- var DEFAULT_VALUE_LABEL = "value";
2139
- function defaultNameOf(nodeId) {
2140
- return nodeId;
2141
- }
2142
- function defaultFormatNumber(n) {
2143
- return String(n);
2144
- }
2145
- function buildWeightedEdgeAriaLabel(edge, opts = {}) {
2146
- if (edge.ariaLabel) return edge.ariaLabel;
2147
- const { weight, value, label, secondaryLabel } = edge.data ?? {};
2148
- if (weight === void 0 && value === void 0 && !label && !secondaryLabel) {
2149
- return void 0;
2150
- }
2151
- const {
2152
- weightLabel = DEFAULT_WEIGHT_LABEL,
2153
- valueLabel = DEFAULT_VALUE_LABEL,
2154
- nameOf = defaultNameOf,
2155
- formatNumber = defaultFormatNumber
2156
- } = opts;
2157
- const parts = [`Edge from ${nameOf(edge.source)} to ${nameOf(edge.target)}`];
2158
- if (weight !== void 0) parts.push(`${weightLabel} ${formatNumber(weight)}`);
2159
- if (value !== void 0) parts.push(`${valueLabel} ${formatNumber(value)}`);
2160
- const pillText = [label, secondaryLabel].filter(Boolean).join(" ");
2161
- if (pillText) parts.push(pillText);
2162
- return parts.join(", ");
2163
- }
2164
- function withWeightedEdgeAria(edges, opts) {
2165
- return edges.map((edge) => {
2166
- if (edge.ariaLabel) return edge;
2167
- const ariaLabel = buildWeightedEdgeAriaLabel(edge, opts);
2168
- return ariaLabel === void 0 ? edge : { ...edge, ariaLabel };
2169
- });
2170
- }
2171
-
2172
- // src/flow-self-loop-edge/flow-self-loop-edge.tsx
2173
- import { useMemo as useMemo4 } from "react";
2174
- import { useEdges as useEdges2, useInternalNode as useInternalNode3 } from "@xyflow/react";
2175
-
2176
- // src/flow-self-loop-edge/self-loop-geometry.ts
2177
- var DEFAULT_LOOP_RADIUS = 28;
2178
- var CONTROL_REACH_X = 1.2;
2179
- var CONTROL_REACH_Y = 2.4;
2180
- var APEX_FACTOR = 3 / 4 * CONTROL_REACH_Y;
2181
- function selfLoopPath(anchor, loopRadius) {
2182
- const cx = Number.isFinite(anchor.centerX) ? anchor.centerX : 0;
2183
- const ay = Number.isFinite(anchor.topY) ? anchor.topY : 0;
2184
- const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
2185
- const startX = cx + r;
2186
- const endX = cx - r;
2187
- const controlY = ay - r * CONTROL_REACH_Y;
2188
- return {
2189
- path: `M ${startX},${ay} C ${startX + r * CONTROL_REACH_X},${controlY} ${endX - r * CONTROL_REACH_X},${controlY} ${endX},${ay}`,
2190
- labelX: cx,
2191
- labelY: ay - r * APEX_FACTOR
2192
- };
2193
- }
2194
- function outward(x, y, centerX, centerY, fallback) {
2195
- const dx = x - centerX;
2196
- const dy = y - centerY;
2197
- const length = Math.hypot(dx, dy);
2198
- if (!Number.isFinite(length) || length < 1e-6) return fallback;
2199
- return [dx / length, dy / length];
2200
- }
2201
- function selfLoopHandleArc(anchor, loopRadius) {
2202
- const num = (value) => Number.isFinite(value) ? value : 0;
2203
- const sx = num(anchor.sourceX);
2204
- const sy = num(anchor.sourceY);
2205
- const tx = num(anchor.targetX);
2206
- const ty = num(anchor.targetY);
2207
- const cx = num(anchor.centerX);
2208
- const cy = num(anchor.centerY);
2209
- const width = Math.max(0, num(anchor.width));
2210
- const height = Math.max(0, num(anchor.height));
2211
- const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
2212
- const [osx, osy] = outward(sx, sy, cx, cy, [0, 1]);
2213
- const [otx, oty] = outward(tx, ty, cx, cy, [-osx, -osy]);
2214
- const lx = osy;
2215
- const ly = -osx;
2216
- const clearance = Math.abs(lx) * (width / 2) + Math.abs(ly) * (height / 2) + r;
2217
- const reach = 4 / 3 * clearance;
2218
- const c1x = sx + osx * r + lx * reach;
2219
- const c1y = sy + osy * r + ly * reach;
2220
- const c2x = tx + otx * r + lx * reach;
2221
- const c2y = ty + oty * r + ly * reach;
2222
- return {
2223
- path: `M ${sx},${sy} C ${c1x},${c1y} ${c2x},${c2y} ${tx},${ty}`,
2224
- // A cubic's midpoint is (P0 + 3·C1 + 3·C2 + P3) / 8 — the arc's apex, which `reach`
2225
- // above places `loopRadius` clear of the card, so the label sits off the node.
2226
- labelX: (sx + 3 * c1x + 3 * c2x + tx) / 8,
2227
- labelY: (sy + 3 * c1y + 3 * c2y + ty) / 8
2228
- };
2229
- }
2230
-
2231
- // src/flow-self-loop-edge/flow-self-loop-edge.tsx
2232
- import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
2233
- function nodeName(data, fallback) {
2234
- if (data && typeof data === "object" && "title" in data) {
2235
- const title = data.title;
2236
- if (typeof title === "string" && title.length > 0) return title;
2237
- }
2238
- return fallback;
2239
- }
2240
- function FlowSelfLoopEdge({
2241
- id,
2242
- source,
2243
- sourceX,
2244
- sourceY,
2245
- targetX,
2246
- targetY,
2247
- markerEnd,
2248
- style,
2249
- selected,
2250
- data
2251
- }) {
2252
- const edges = useEdges2();
2253
- const widthByEdgeId = useMemo4(
2254
- () => computeEdgeWeightScale(edges),
2255
- [edges]
2256
- );
2257
- const node = useInternalNode3(source);
2258
- const measuredWidth = node?.measured?.width;
2259
- const measuredHeight = node?.measured?.height;
2260
- const loopRadius = data?.loopRadius ?? DEFAULT_LOOP_RADIUS;
2261
- const { path, labelX, labelY } = useMemo4(() => {
2262
- if (node && measuredWidth && measuredHeight) {
2263
- return selfLoopHandleArc(
2264
- {
2265
- sourceX,
2266
- sourceY,
2267
- targetX,
2268
- targetY,
2269
- centerX: node.internals.positionAbsolute.x + measuredWidth / 2,
2270
- centerY: node.internals.positionAbsolute.y + measuredHeight / 2,
2271
- width: measuredWidth,
2272
- height: measuredHeight
2273
- },
2274
- loopRadius
2275
- );
2276
- }
2277
- return selfLoopPath(
2278
- { centerX: (sourceX + targetX) / 2, topY: Math.min(sourceY, targetY) },
2279
- loopRadius
2280
- );
2281
- }, [node, measuredWidth, measuredHeight, sourceX, sourceY, targetX, targetY, loopRadius]);
2282
- const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];
2283
- const stroke = selected ? "var(--ring)" : "var(--flow-edge)";
2284
- const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;
2285
- const accessibleName = data?.loopLabel ?? `Self-loop on ${nodeName(node?.data, source)} \u2014 this step repeats`;
2286
- return /* @__PURE__ */ jsxs14(Fragment6, { children: [
2287
- /* @__PURE__ */ jsx17("g", { role: "img", "aria-label": accessibleName, children: /* @__PURE__ */ jsx17(
2288
- FlowEdgePath,
2289
- {
2290
- id,
2291
- path,
2292
- markerEnd,
2293
- "data-slot": "flow-self-loop-edge",
2294
- stroke,
2295
- strokeWidth,
2296
- style: { fill: "none", ...style }
2297
- }
2298
- ) }),
2299
- /* @__PURE__ */ jsx17(
2300
- EdgeLabelPill,
2301
- {
2302
- label: data?.label,
2303
- secondaryLabel: data?.secondaryLabel,
2304
- x: labelX,
2305
- y: labelY,
2306
- selected,
2307
- ...data?.labelProps
2308
- }
2309
- )
2310
- ] });
2311
- }
2312
167
  export {
2313
- Background2 as Background,
168
+ Background,
2314
169
  CanvasShell,
2315
170
  Controls,
171
+ DEFAULT_EDGE_TOKEN_RADIUS,
2316
172
  DEFAULT_EDGE_WIDTH_RANGE,
2317
173
  DEFAULT_LOOP_RADIUS,
2318
174
  EdgeLabelPill,
@@ -2324,6 +180,7 @@ export {
2324
180
  FlowButtonEdge,
2325
181
  FlowEdge,
2326
182
  FlowEdgePath,
183
+ FlowEdgeTokens,
2327
184
  FlowFloatingEdge,
2328
185
  FlowGroupNode,
2329
186
  FlowMiniMap,
@@ -2336,27 +193,31 @@ export {
2336
193
  HelperLines,
2337
194
  InspectorPanel,
2338
195
  Legend,
2339
- MiniMap2 as MiniMap,
2340
- Panel2 as Panel,
2341
- Position8 as Position,
2342
- ReactFlow2 as ReactFlow,
2343
- ReactFlowProvider2 as ReactFlowProvider,
196
+ MiniMap,
197
+ Panel,
198
+ Position,
199
+ ReactFlow,
200
+ ReactFlowProvider,
2344
201
  ZoomControls,
2345
202
  addEdge,
2346
203
  backEdgeDetour,
2347
204
  buildWeightedEdgeAriaLabel,
2348
205
  collapseGroup,
2349
206
  computeEdgeWeightScale,
207
+ createEdgePathSampler,
2350
208
  expandGroup,
209
+ flattenEdgePath,
2351
210
  getEdgeParams,
2352
211
  getHelperLines,
2353
212
  groupNodes,
2354
213
  handlePoint,
2355
214
  isFlowGroupProxyEdge,
2356
215
  layoutFlow,
216
+ layoutFlowElk,
2357
217
  layoutGraph,
2358
218
  pickClosestAnchors,
2359
219
  pickClosestHandles,
220
+ pinBackbone,
2360
221
  positionToSide,
2361
222
  selfLoopHandleArc,
2362
223
  selfLoopPath,
@@ -2369,7 +230,7 @@ export {
2369
230
  useFlowLayout,
2370
231
  useHelperLines,
2371
232
  useNodesState,
2372
- useReactFlow6 as useReactFlow,
233
+ useReactFlow,
2373
234
  withWeightedEdgeAria
2374
235
  };
2375
236
  //# sourceMappingURL=index.js.map