@elabs-ai/components-flow 4.2.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.
- package/README.md +2 -1
- package/dist/canvas-shell/canvas-shell.js +13 -0
- package/dist/canvas-shell/canvas-shell.js.map +1 -0
- package/dist/canvas-shell/clamped-fit-offset.js +8 -0
- package/dist/canvas-shell/clamped-fit-offset.js.map +1 -0
- package/dist/canvas-shell/index.js +14 -0
- package/dist/canvas-shell/index.js.map +1 -0
- package/dist/canvas-shell/use-measured-nodes.js +8 -0
- package/dist/canvas-shell/use-measured-nodes.js.map +1 -0
- package/dist/chunk-3BTU2VWZ.js +42 -0
- package/dist/chunk-3BTU2VWZ.js.map +1 -0
- package/dist/chunk-3LK6OOXE.js +2 -0
- package/dist/chunk-3LK6OOXE.js.map +1 -0
- package/dist/chunk-3M3FQAD4.js +105 -0
- package/dist/chunk-3M3FQAD4.js.map +1 -0
- package/dist/chunk-3Y5TCGC3.js +2 -0
- package/dist/chunk-3Y5TCGC3.js.map +1 -0
- package/dist/chunk-4VYHZM3S.js +108 -0
- package/dist/chunk-4VYHZM3S.js.map +1 -0
- package/dist/chunk-5TVVM4GR.js +45 -0
- package/dist/chunk-5TVVM4GR.js.map +1 -0
- package/dist/chunk-5XT73MQQ.js +2 -0
- package/dist/chunk-5XT73MQQ.js.map +1 -0
- package/dist/chunk-6LQD5Y5R.js +24 -0
- package/dist/chunk-6LQD5Y5R.js.map +1 -0
- package/dist/chunk-6SR26QY2.js +18 -0
- package/dist/chunk-6SR26QY2.js.map +1 -0
- package/dist/chunk-75JJK6BH.js +157 -0
- package/dist/chunk-75JJK6BH.js.map +1 -0
- package/dist/chunk-A66JBW4X.js +2 -0
- package/dist/chunk-A66JBW4X.js.map +1 -0
- package/dist/chunk-A7AFM3LI.js +72 -0
- package/dist/chunk-A7AFM3LI.js.map +1 -0
- package/dist/chunk-ADHLQF56.js +2 -0
- package/dist/chunk-ADHLQF56.js.map +1 -0
- package/dist/chunk-ATO4LXJF.js +75 -0
- package/dist/chunk-ATO4LXJF.js.map +1 -0
- package/dist/chunk-ATXVXTMZ.js +227 -0
- package/dist/chunk-ATXVXTMZ.js.map +1 -0
- package/dist/chunk-B6IUPZU7.js +2 -0
- package/dist/chunk-B6IUPZU7.js.map +1 -0
- package/dist/chunk-BJ7UMCD3.js +53 -0
- package/dist/chunk-BJ7UMCD3.js.map +1 -0
- package/dist/chunk-BWGYV4JZ.js +2 -0
- package/dist/chunk-BWGYV4JZ.js.map +1 -0
- package/dist/chunk-CWNO63LQ.js +109 -0
- package/dist/chunk-CWNO63LQ.js.map +1 -0
- package/dist/chunk-DHPUXKCZ.js +76 -0
- package/dist/chunk-DHPUXKCZ.js.map +1 -0
- package/dist/chunk-DNX7G3KA.js +185 -0
- package/dist/chunk-DNX7G3KA.js.map +1 -0
- package/dist/chunk-DNZRAJAQ.js +41 -0
- package/dist/chunk-DNZRAJAQ.js.map +1 -0
- package/dist/chunk-ELCLRVD4.js +2 -0
- package/dist/chunk-ELCLRVD4.js.map +1 -0
- package/dist/chunk-ELOADZSX.js +2 -0
- package/dist/chunk-ELOADZSX.js.map +1 -0
- package/dist/chunk-EUNV2MR5.js +21 -0
- package/dist/chunk-EUNV2MR5.js.map +1 -0
- package/dist/chunk-FAFQ5KLB.js +43 -0
- package/dist/chunk-FAFQ5KLB.js.map +1 -0
- package/dist/chunk-FIRO6Y3R.js +128 -0
- package/dist/chunk-FIRO6Y3R.js.map +1 -0
- package/dist/chunk-GE73WQUK.js +2 -0
- package/dist/chunk-GE73WQUK.js.map +1 -0
- package/dist/chunk-GIARAOU6.js +39 -0
- package/dist/chunk-GIARAOU6.js.map +1 -0
- package/dist/chunk-HTLTCCFA.js +73 -0
- package/dist/chunk-HTLTCCFA.js.map +1 -0
- package/dist/chunk-I53W3CFN.js +56 -0
- package/dist/chunk-I53W3CFN.js.map +1 -0
- package/dist/chunk-IJVFPUXO.js +2 -0
- package/dist/chunk-IJVFPUXO.js.map +1 -0
- package/dist/chunk-JUPMOISE.js +51 -0
- package/dist/chunk-JUPMOISE.js.map +1 -0
- package/dist/chunk-K5RPSVO6.js +167 -0
- package/dist/chunk-K5RPSVO6.js.map +1 -0
- package/dist/chunk-K6U27YJ6.js +78 -0
- package/dist/chunk-K6U27YJ6.js.map +1 -0
- package/dist/chunk-KDVMGMAK.js +2 -0
- package/dist/chunk-KDVMGMAK.js.map +1 -0
- package/dist/chunk-LQOZCOK3.js +74 -0
- package/dist/chunk-LQOZCOK3.js.map +1 -0
- package/dist/chunk-NAQWNX2A.js +102 -0
- package/dist/chunk-NAQWNX2A.js.map +1 -0
- package/dist/chunk-NVJVWGUS.js +152 -0
- package/dist/chunk-NVJVWGUS.js.map +1 -0
- package/dist/chunk-O2F3R3PV.js +2 -0
- package/dist/chunk-O2F3R3PV.js.map +1 -0
- package/dist/chunk-OMFQHMA6.js +2 -0
- package/dist/chunk-OMFQHMA6.js.map +1 -0
- package/dist/chunk-PE6JXVOX.js +2 -0
- package/dist/chunk-PE6JXVOX.js.map +1 -0
- package/dist/chunk-ROJEIIA4.js +52 -0
- package/dist/chunk-ROJEIIA4.js.map +1 -0
- package/dist/chunk-ROYAQKJA.js +9 -0
- package/dist/chunk-ROYAQKJA.js.map +1 -0
- package/dist/chunk-SHCZK4DO.js +45 -0
- package/dist/chunk-SHCZK4DO.js.map +1 -0
- package/dist/chunk-SXXJV6YJ.js +79 -0
- package/dist/chunk-SXXJV6YJ.js.map +1 -0
- package/dist/chunk-T7S7NJFD.js +287 -0
- package/dist/chunk-T7S7NJFD.js.map +1 -0
- package/dist/chunk-TEZ3OXAY.js +63 -0
- package/dist/chunk-TEZ3OXAY.js.map +1 -0
- package/dist/chunk-TGWPM2CG.js +199 -0
- package/dist/chunk-TGWPM2CG.js.map +1 -0
- package/dist/chunk-TSB6RE7L.js +2 -0
- package/dist/chunk-TSB6RE7L.js.map +1 -0
- package/dist/chunk-UPV2MT4O.js +2 -0
- package/dist/chunk-UPV2MT4O.js.map +1 -0
- package/dist/chunk-UZJZY4A5.js +2 -0
- package/dist/chunk-UZJZY4A5.js.map +1 -0
- package/dist/chunk-W3TQMUGX.js +2 -0
- package/dist/chunk-W3TQMUGX.js.map +1 -0
- package/dist/chunk-WN46RIOC.js +29 -0
- package/dist/chunk-WN46RIOC.js.map +1 -0
- package/dist/chunk-XZFE2YYH.js +70 -0
- package/dist/chunk-XZFE2YYH.js.map +1 -0
- package/dist/chunk-YG3KXTZW.js +2 -0
- package/dist/chunk-YG3KXTZW.js.map +1 -0
- package/dist/chunk-ZATVF42E.js +65 -0
- package/dist/chunk-ZATVF42E.js.map +1 -0
- package/dist/chunk-ZXZTLQIX.js +19 -0
- package/dist/chunk-ZXZTLQIX.js.map +1 -0
- package/dist/flow-button-edge/flow-button-edge.js +10 -0
- package/dist/flow-button-edge/flow-button-edge.js.map +1 -0
- package/dist/flow-button-edge/index.js +11 -0
- package/dist/flow-button-edge/index.js.map +1 -0
- package/dist/flow-edge/flow-edge.js +10 -0
- package/dist/flow-edge/flow-edge.js.map +1 -0
- package/dist/flow-edge/index.js +11 -0
- package/dist/flow-edge/index.js.map +1 -0
- package/dist/flow-edge-path/flow-edge-path.js +12 -0
- package/dist/flow-edge-path/flow-edge-path.js.map +1 -0
- package/dist/flow-edge-path/index.js +13 -0
- package/dist/flow-edge-path/index.js.map +1 -0
- package/dist/flow-edge-tokens/edge-path-sampler.js +10 -0
- package/dist/flow-edge-tokens/edge-path-sampler.js.map +1 -0
- package/dist/flow-edge-tokens/flow-edge-tokens.js +12 -0
- package/dist/flow-edge-tokens/flow-edge-tokens.js.map +1 -0
- package/dist/flow-edge-tokens/index.js +17 -0
- package/dist/flow-edge-tokens/index.js.map +1 -0
- package/dist/flow-floating-edge/floating-edge-geometry.js +8 -0
- package/dist/flow-floating-edge/floating-edge-geometry.js.map +1 -0
- package/dist/flow-floating-edge/flow-floating-edge.js +11 -0
- package/dist/flow-floating-edge/flow-floating-edge.js.map +1 -0
- package/dist/flow-floating-edge/index.js +15 -0
- package/dist/flow-floating-edge/index.js.map +1 -0
- package/dist/flow-group-node/flow-group-node.js +12 -0
- package/dist/flow-group-node/flow-group-node.js.map +1 -0
- package/dist/flow-group-node/index.js +13 -0
- package/dist/flow-group-node/index.js.map +1 -0
- package/dist/flow-handle/flow-handle-anchor.js +8 -0
- package/dist/flow-handle/flow-handle-anchor.js.map +1 -0
- package/dist/flow-handle/index.js +9 -0
- package/dist/flow-handle/index.js.map +1 -0
- package/dist/flow-layout/backbone.js +8 -0
- package/dist/flow-layout/backbone.js.map +1 -0
- package/dist/flow-layout/flow-layout.js +10 -0
- package/dist/flow-layout/flow-layout.js.map +1 -0
- package/dist/flow-layout/index.js +21 -0
- package/dist/flow-layout/index.js.map +1 -0
- package/dist/flow-layout/layout-flow-elk.js +9 -0
- package/dist/flow-layout/layout-flow-elk.js.map +1 -0
- package/dist/flow-layout/layout-graph.js +9 -0
- package/dist/flow-layout/layout-graph.js.map +1 -0
- package/dist/flow-layout/use-auto-layout.js +10 -0
- package/dist/flow-layout/use-auto-layout.js.map +1 -0
- package/dist/flow-layout/use-flow-layout.js +9 -0
- package/dist/flow-layout/use-flow-layout.js.map +1 -0
- package/dist/flow-mini-map/flow-mini-map.js +8 -0
- package/dist/flow-mini-map/flow-mini-map.js.map +1 -0
- package/dist/flow-mini-map/index.js +9 -0
- package/dist/flow-mini-map/index.js.map +1 -0
- package/dist/flow-node/flow-node.js +11 -0
- package/dist/flow-node/flow-node.js.map +1 -0
- package/dist/flow-node/index.js +12 -0
- package/dist/flow-node/index.js.map +1 -0
- package/dist/flow-placeholder-node/flow-placeholder-node.js +9 -0
- package/dist/flow-placeholder-node/flow-placeholder-node.js.map +1 -0
- package/dist/flow-placeholder-node/index.js +10 -0
- package/dist/flow-placeholder-node/index.js.map +1 -0
- package/dist/flow-self-loop-edge/flow-self-loop-edge.js +20 -0
- package/dist/flow-self-loop-edge/flow-self-loop-edge.js.map +1 -0
- package/dist/flow-self-loop-edge/index.js +28 -0
- package/dist/flow-self-loop-edge/index.js.map +1 -0
- package/dist/flow-self-loop-edge/self-loop-geometry.js +12 -0
- package/dist/flow-self-loop-edge/self-loop-geometry.js.map +1 -0
- package/dist/flow-smart-edge/flow-smart-edge.js +11 -0
- package/dist/flow-smart-edge/flow-smart-edge.js.map +1 -0
- package/dist/flow-smart-edge/index.js +25 -0
- package/dist/flow-smart-edge/index.js.map +1 -0
- package/dist/flow-smart-edge/smart-edge-geometry.js +18 -0
- package/dist/flow-smart-edge/smart-edge-geometry.js.map +1 -0
- package/dist/flow-weighted-edge/back-edge-geometry.js +8 -0
- package/dist/flow-weighted-edge/back-edge-geometry.js.map +1 -0
- package/dist/flow-weighted-edge/edge-aria.js +10 -0
- package/dist/flow-weighted-edge/edge-aria.js.map +1 -0
- package/dist/flow-weighted-edge/edge-label-pill.js +8 -0
- package/dist/flow-weighted-edge/edge-label-pill.js.map +1 -0
- package/dist/flow-weighted-edge/flow-weighted-edge.js +16 -0
- package/dist/flow-weighted-edge/flow-weighted-edge.js.map +1 -0
- package/dist/flow-weighted-edge/index.js +34 -0
- package/dist/flow-weighted-edge/index.js.map +1 -0
- package/dist/flow-weighted-edge/weight-scale.js +10 -0
- package/dist/flow-weighted-edge/weight-scale.js.map +1 -0
- package/dist/helper-lines/get-helper-lines.js +8 -0
- package/dist/helper-lines/get-helper-lines.js.map +1 -0
- package/dist/helper-lines/helper-lines.js +9 -0
- package/dist/helper-lines/helper-lines.js.map +1 -0
- package/dist/helper-lines/index.js +17 -0
- package/dist/helper-lines/index.js.map +1 -0
- package/dist/helper-lines/use-helper-lines.js +10 -0
- package/dist/helper-lines/use-helper-lines.js.map +1 -0
- package/dist/index.d.ts +236 -1
- package/dist/index.js +167 -2311
- package/dist/index.js.map +1 -1
- package/dist/inspector-panel/index.js +9 -0
- package/dist/inspector-panel/index.js.map +1 -0
- package/dist/inspector-panel/inspector-panel.js +8 -0
- package/dist/inspector-panel/inspector-panel.js.map +1 -0
- package/dist/legend/index.js +10 -0
- package/dist/legend/index.js.map +1 -0
- package/dist/legend/legend.js +9 -0
- package/dist/legend/legend.js.map +1 -0
- package/dist/use-flow-groups/group-operations.js +20 -0
- package/dist/use-flow-groups/group-operations.js.map +1 -0
- package/dist/use-flow-groups/index.js +25 -0
- package/dist/use-flow-groups/index.js.map +1 -0
- package/dist/use-flow-groups/use-flow-groups.js +9 -0
- package/dist/use-flow-groups/use-flow-groups.js.map +1 -0
- package/dist/zoom-controls/index.js +9 -0
- package/dist/zoom-controls/index.js.map +1 -0
- package/dist/zoom-controls/zoom-controls.js +8 -0
- package/dist/zoom-controls/zoom-controls.js.map +1 -0
- package/package.json +13 -6
- package/src/canvas-shell/canvas-shell.stories.tsx +1 -0
- package/src/canvas-shell/canvas-shell.tsx +51 -4
- package/src/canvas-shell/clamped-fit-offset.test.ts +25 -0
- package/src/canvas-shell/clamped-fit-offset.ts +30 -0
- package/src/custom-nodes/custom-nodes.stories.tsx +718 -0
- package/src/flow-edge-tokens/edge-path-sampler.test.ts +108 -0
- package/src/flow-edge-tokens/edge-path-sampler.ts +362 -0
- package/src/flow-edge-tokens/flow-edge-tokens.stories.tsx +312 -0
- package/src/flow-edge-tokens/flow-edge-tokens.test.tsx +115 -0
- package/src/flow-edge-tokens/flow-edge-tokens.tsx +94 -0
- package/src/flow-edge-tokens/index.ts +12 -0
- package/src/flow-layout/backbone.test.ts +88 -0
- package/src/flow-layout/backbone.ts +58 -0
- package/src/flow-layout/layout-flow-elk.stories.tsx +154 -0
- package/src/flow-layout/layout-flow-elk.test.ts +152 -0
- package/src/flow-layout/layout-flow-elk.ts +399 -0
- package/src/flow-mini-map/flow-mini-map.stories.tsx +10 -7
- package/src/flow-node/flow-node.stories.tsx +40 -1
- package/src/flow-self-loop-edge/flow-self-loop-edge.test.tsx +38 -0
- package/src/flow-self-loop-edge/flow-self-loop-edge.tsx +7 -0
- package/src/flow-weighted-edge/edge-label-pill.test.tsx +20 -0
- package/src/flow-weighted-edge/flow-weighted-edge.stories.tsx +1 -0
- package/src/flow-weighted-edge/flow-weighted-edge.test.tsx +40 -0
- package/src/flow-weighted-edge/flow-weighted-edge.tsx +7 -0
- package/src/index.ts +13 -0
- package/src/templates-flow-workspace.stories.tsx +7 -2
package/dist/index.js
CHANGED
|
@@ -1,2323 +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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
} from "
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
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, useLocale } 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,
|
|
1235
|
-
children,
|
|
1236
|
-
onClose,
|
|
1237
|
-
emptyMessage,
|
|
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 { t } = useLocale();
|
|
1248
|
-
const resolvedTitle = title ?? t("flow.inspectorPanel.title");
|
|
1249
|
-
const resolvedEmptyMessage = emptyMessage ?? t("flow.inspectorPanel.emptyMessage");
|
|
1250
|
-
const panel = useCollapsiblePanel({
|
|
1251
|
-
side,
|
|
1252
|
-
open,
|
|
1253
|
-
defaultOpen,
|
|
1254
|
-
onOpenChange,
|
|
1255
|
-
widthClassName: "w-(--inspector-panel-width)",
|
|
1256
|
-
spacerCollapsedClassName: "group-data-[state=collapsed]:w-0",
|
|
1257
|
-
containerSlideClassNames: {
|
|
1258
|
-
left: "left-0 group-data-[state=collapsed]:left-[calc(var(--inspector-panel-width)*-1)]",
|
|
1259
|
-
right: "right-0 group-data-[state=collapsed]:right-[calc(var(--inspector-panel-width)*-1)]"
|
|
1260
|
-
}
|
|
1261
|
-
});
|
|
1262
|
-
return /* @__PURE__ */ jsxs8(
|
|
1263
|
-
"div",
|
|
1264
|
-
{
|
|
1265
|
-
className: cn7("group relative h-full overflow-hidden", className),
|
|
1266
|
-
style: { "--inspector-panel-width": width },
|
|
1267
|
-
inert: panel.open ? void 0 : true,
|
|
1268
|
-
...panel.attrs,
|
|
1269
|
-
children: [
|
|
1270
|
-
/* @__PURE__ */ jsx11("div", { "data-slot": "inspector-panel-gap", className: panel.spacerClassName }),
|
|
1271
|
-
/* @__PURE__ */ jsx11(
|
|
1272
|
-
"div",
|
|
1273
|
-
{
|
|
1274
|
-
"data-slot": "inspector-panel-container",
|
|
1275
|
-
className: cn7(panel.containerClassName, "absolute inset-y-0 flex h-full border-s"),
|
|
1276
|
-
children: /* @__PURE__ */ jsxs8("div", { className: "flex h-full w-full flex-col bg-card", children: [
|
|
1277
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
|
|
1278
|
-
/* @__PURE__ */ jsx11("h3", { className: "text-body font-semibold text-foreground", children: resolvedTitle }),
|
|
1279
|
-
onClose ? /* @__PURE__ */ jsx11(
|
|
1280
|
-
"button",
|
|
1281
|
-
{
|
|
1282
|
-
type: "button",
|
|
1283
|
-
"aria-label": t("flow.inspectorPanel.close"),
|
|
1284
|
-
onClick: onClose,
|
|
1285
|
-
className: "rounded-sm p-1 text-muted-foreground hover:text-foreground focus-ring",
|
|
1286
|
-
children: /* @__PURE__ */ jsx11(
|
|
1287
|
-
"svg",
|
|
1288
|
-
{
|
|
1289
|
-
width: "14",
|
|
1290
|
-
height: "14",
|
|
1291
|
-
viewBox: "0 0 24 24",
|
|
1292
|
-
fill: "none",
|
|
1293
|
-
stroke: "currentColor",
|
|
1294
|
-
strokeWidth: "2",
|
|
1295
|
-
strokeLinecap: "round",
|
|
1296
|
-
strokeLinejoin: "round",
|
|
1297
|
-
"aria-hidden": "true",
|
|
1298
|
-
children: /* @__PURE__ */ jsx11("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
1299
|
-
}
|
|
1300
|
-
)
|
|
1301
|
-
}
|
|
1302
|
-
) : null
|
|
1303
|
-
] }),
|
|
1304
|
-
hasSelection ? /* @__PURE__ */ jsx11(
|
|
1305
|
-
Reveal,
|
|
1306
|
-
{
|
|
1307
|
-
appear: "fade",
|
|
1308
|
-
speed: "fast",
|
|
1309
|
-
tabIndex: 0,
|
|
1310
|
-
className: "focus-ring flex-1 overflow-y-auto p-4 text-body",
|
|
1311
|
-
children
|
|
1312
|
-
},
|
|
1313
|
-
selectionKey
|
|
1314
|
-
) : /* @__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: resolvedEmptyMessage }) })
|
|
1315
|
-
] })
|
|
1316
|
-
}
|
|
1317
|
-
)
|
|
1318
|
-
]
|
|
1319
|
-
}
|
|
1320
|
-
);
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
// src/legend/legend.tsx
|
|
1324
|
-
import { useLocale as useLocale2 } from "@elabs-ai/components-ui";
|
|
1325
|
-
import { cn as cn8 } from "@elabs-ai/components-ui/lib/cn";
|
|
1326
|
-
|
|
1327
|
-
// src/flow-weighted-edge/weight-scale.ts
|
|
1328
|
-
var DEFAULT_EDGE_WIDTH_RANGE = [1.5, 8];
|
|
1329
|
-
var DEFAULT_SCALE_GROUP = "__default__";
|
|
1330
|
-
function computeEdgeWeightScale(edges, opts = {}) {
|
|
1331
|
-
const [minWidth, maxWidth] = opts.widthRange ?? DEFAULT_EDGE_WIDTH_RANGE;
|
|
1332
|
-
const result = /* @__PURE__ */ new Map();
|
|
1333
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1334
|
-
for (const edge of edges) {
|
|
1335
|
-
const weight = edge.data?.weight;
|
|
1336
|
-
if (weight === void 0) {
|
|
1337
|
-
result.set(edge.id, minWidth);
|
|
1338
|
-
continue;
|
|
1339
|
-
}
|
|
1340
|
-
const group = edge.data?.scaleGroup ?? DEFAULT_SCALE_GROUP;
|
|
1341
|
-
if (opts.scaleGroup !== void 0 && group !== opts.scaleGroup) continue;
|
|
1342
|
-
const list = groups.get(group);
|
|
1343
|
-
if (list) list.push({ id: edge.id, weight });
|
|
1344
|
-
else groups.set(group, [{ id: edge.id, weight }]);
|
|
1345
|
-
}
|
|
1346
|
-
for (const list of groups.values()) {
|
|
1347
|
-
let min = Infinity;
|
|
1348
|
-
let max = -Infinity;
|
|
1349
|
-
for (const { weight } of list) {
|
|
1350
|
-
if (weight < min) min = weight;
|
|
1351
|
-
if (weight > max) max = weight;
|
|
1352
|
-
}
|
|
1353
|
-
const span = max - min;
|
|
1354
|
-
for (const { id, weight } of list) {
|
|
1355
|
-
const width = span === 0 ? (minWidth + maxWidth) / 2 : minWidth + (weight - min) / span * (maxWidth - minWidth);
|
|
1356
|
-
result.set(id, width);
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
return result;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
// src/legend/legend.tsx
|
|
1363
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1364
|
-
function Legend(props) {
|
|
1365
|
-
if (props.variant === "scale") {
|
|
1366
|
-
return /* @__PURE__ */ jsx12(LegendScale, { ...props });
|
|
1367
|
-
}
|
|
1368
|
-
return /* @__PURE__ */ jsx12(LegendCategorical, { ...props });
|
|
1369
|
-
}
|
|
1370
|
-
function LegendCategorical({ items, title, className }) {
|
|
1371
|
-
return /* @__PURE__ */ jsxs9(
|
|
1372
|
-
"div",
|
|
1373
|
-
{
|
|
1374
|
-
className: cn8(
|
|
1375
|
-
"rounded-lg bg-surface-elevated/90 p-3 text-xs shadow-ring-sm backdrop-blur",
|
|
1376
|
-
className
|
|
1377
|
-
),
|
|
1378
|
-
children: [
|
|
1379
|
-
title ? /* @__PURE__ */ jsx12("div", { className: "mb-1.5 font-medium text-foreground", children: title }) : null,
|
|
1380
|
-
/* @__PURE__ */ jsx12("ul", { className: "space-y-1", children: items.map((item) => /* @__PURE__ */ jsxs9("li", { className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
1381
|
-
/* @__PURE__ */ jsx12(
|
|
1382
|
-
"span",
|
|
1383
|
-
{
|
|
1384
|
-
"aria-hidden": "true",
|
|
1385
|
-
className: "size-2.5 shrink-0 rounded-full",
|
|
1386
|
-
style: { backgroundColor: item.color }
|
|
1387
|
-
}
|
|
1388
|
-
),
|
|
1389
|
-
item.label
|
|
1390
|
-
] }, item.label)) })
|
|
1391
|
-
]
|
|
1392
|
-
}
|
|
1393
|
-
);
|
|
1394
|
-
}
|
|
1395
|
-
function computeWidthSamples(domain, ticks) {
|
|
1396
|
-
const [min, max] = domain;
|
|
1397
|
-
const values = ticks === "minmedmax" ? [min, (min + max) / 2, max] : [min, max];
|
|
1398
|
-
const likeEdges = values.map((weight, index) => ({
|
|
1399
|
-
id: String(index),
|
|
1400
|
-
data: { weight }
|
|
1401
|
-
}));
|
|
1402
|
-
const widths = computeEdgeWeightScale(likeEdges);
|
|
1403
|
-
return values.map((value, index) => ({ value, width: widths.get(String(index)) ?? 0 }));
|
|
1404
|
-
}
|
|
1405
|
-
var COLOR_STOP_FRACTIONS = [0, 0.25, 0.5, 0.75, 1];
|
|
1406
|
-
function scaleKindLabel(kind) {
|
|
1407
|
-
return kind === "width" ? "width" : "color";
|
|
1408
|
-
}
|
|
1409
|
-
function LegendScale({
|
|
1410
|
-
kind,
|
|
1411
|
-
domain,
|
|
1412
|
-
format,
|
|
1413
|
-
ticks = "minmax",
|
|
1414
|
-
title,
|
|
1415
|
-
className
|
|
1416
|
-
}) {
|
|
1417
|
-
const { formatNumber } = useLocale2();
|
|
1418
|
-
const format_ = format ?? formatNumber;
|
|
1419
|
-
const [min, max] = domain;
|
|
1420
|
-
const ariaLabel = `Edge ${scaleKindLabel(kind)} scale, ${format_(min)} to ${format_(max)}, minimum to maximum`;
|
|
1421
|
-
return /* @__PURE__ */ jsxs9(
|
|
1422
|
-
"div",
|
|
1423
|
-
{
|
|
1424
|
-
"aria-label": ariaLabel,
|
|
1425
|
-
className: cn8(
|
|
1426
|
-
"flex flex-col gap-2 rounded-lg bg-surface-elevated/90 p-3 shadow-ring-sm backdrop-blur",
|
|
1427
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1428
|
-
className
|
|
1429
|
-
),
|
|
1430
|
-
role: "group",
|
|
1431
|
-
tabIndex: 0,
|
|
1432
|
-
children: [
|
|
1433
|
-
title ? /* @__PURE__ */ jsx12("div", { className: "text-body font-medium text-foreground", children: title }) : null,
|
|
1434
|
-
kind === "width" ? /* @__PURE__ */ jsx12(LegendScaleWidth, { domain, format: format_, ticks }) : /* @__PURE__ */ jsx12(LegendScaleColor, { domain, format: format_ })
|
|
1435
|
-
]
|
|
1436
|
-
}
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
function LegendScaleWidth({
|
|
1440
|
-
domain,
|
|
1441
|
-
format,
|
|
1442
|
-
ticks
|
|
1443
|
-
}) {
|
|
1444
|
-
const samples = computeWidthSamples(domain, ticks);
|
|
1445
|
-
return /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: samples.map((sample, index) => (
|
|
1446
|
-
// Index disambiguates a zero-width domain, where every sample shares
|
|
1447
|
-
// the same value (and therefore the same width).
|
|
1448
|
-
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-3", children: [
|
|
1449
|
-
/* @__PURE__ */ jsx12("svg", { "aria-hidden": "true", className: "shrink-0", height: 16, overflow: "visible", width: 40, children: /* @__PURE__ */ jsx12(
|
|
1450
|
-
"line",
|
|
1451
|
-
{
|
|
1452
|
-
stroke: "var(--flow-edge)",
|
|
1453
|
-
strokeLinecap: "round",
|
|
1454
|
-
strokeWidth: sample.width,
|
|
1455
|
-
x1: 2,
|
|
1456
|
-
x2: 38,
|
|
1457
|
-
y1: 8,
|
|
1458
|
-
y2: 8
|
|
1459
|
-
}
|
|
1460
|
-
) }),
|
|
1461
|
-
/* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate text-meta tabular-nums text-muted-foreground", children: format(sample.value) })
|
|
1462
|
-
] }, `${index}-${sample.value}`)
|
|
1463
|
-
)) });
|
|
1464
|
-
}
|
|
1465
|
-
function LegendScaleColor({
|
|
1466
|
-
domain,
|
|
1467
|
-
format
|
|
1468
|
-
}) {
|
|
1469
|
-
const [min, max] = domain;
|
|
1470
|
-
const span = max - min;
|
|
1471
|
-
const stops = COLOR_STOP_FRACTIONS.map((fraction) => ({
|
|
1472
|
-
fraction,
|
|
1473
|
-
value: min + fraction * span
|
|
1474
|
-
}));
|
|
1475
|
-
const gradient = `linear-gradient(to right, ${stops.map(({ fraction }) => {
|
|
1476
|
-
const percent = Math.round(fraction * 100);
|
|
1477
|
-
return `color-mix(in oklch, var(--flow-edge-strong) ${percent}%, var(--flow-edge-weak)) ${percent}%`;
|
|
1478
|
-
}).join(", ")})`;
|
|
1479
|
-
return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-1.5", children: [
|
|
1480
|
-
/* @__PURE__ */ jsx12(
|
|
1481
|
-
"div",
|
|
1482
|
-
{
|
|
1483
|
-
"aria-hidden": "true",
|
|
1484
|
-
className: "h-3 w-full rounded-full",
|
|
1485
|
-
style: { backgroundImage: gradient }
|
|
1486
|
-
}
|
|
1487
|
-
),
|
|
1488
|
-
/* @__PURE__ */ jsx12("div", { className: "flex justify-between gap-1", children: stops.map((stop, index) => /* @__PURE__ */ jsx12(
|
|
1489
|
-
"span",
|
|
1490
|
-
{
|
|
1491
|
-
className: cn8(
|
|
1492
|
-
"min-w-0 truncate text-meta tabular-nums text-muted-foreground",
|
|
1493
|
-
index === 0 && "text-start",
|
|
1494
|
-
index === stops.length - 1 && "text-end"
|
|
1495
|
-
),
|
|
1496
|
-
children: format(stop.value)
|
|
1497
|
-
},
|
|
1498
|
-
`${index}-${stop.value}`
|
|
1499
|
-
)) })
|
|
1500
|
-
] });
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
// src/zoom-controls/zoom-controls.tsx
|
|
1504
|
-
import { Panel, useReactFlow as useReactFlow4 } from "@xyflow/react";
|
|
1505
|
-
import { cn as cn9 } from "@elabs-ai/components-ui/lib/cn";
|
|
1506
|
-
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1507
|
-
function ControlButton({
|
|
1508
|
-
label,
|
|
1509
|
-
onClick,
|
|
1510
|
-
children
|
|
1511
|
-
}) {
|
|
1512
|
-
return /* @__PURE__ */ jsx13(
|
|
1513
|
-
"button",
|
|
1514
|
-
{
|
|
1515
|
-
type: "button",
|
|
1516
|
-
"aria-label": label,
|
|
1517
|
-
onClick,
|
|
1518
|
-
className: "flex size-8 items-center justify-center text-foreground transition-colors duration-fast hover:bg-surface-muted focus-ring [&_svg]:size-4",
|
|
1519
|
-
children
|
|
1520
|
-
}
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
function ZoomControls({ position = "bottom-right", className }) {
|
|
1524
|
-
const { zoomIn, zoomOut, fitView } = useReactFlow4();
|
|
1525
|
-
return /* @__PURE__ */ jsx13(Panel, { position, children: /* @__PURE__ */ jsxs10(
|
|
1526
|
-
"div",
|
|
1527
|
-
{
|
|
1528
|
-
className: cn9(
|
|
1529
|
-
"flex divide-x overflow-hidden rounded-lg bg-surface-elevated shadow-ring-sm",
|
|
1530
|
-
className
|
|
1531
|
-
),
|
|
1532
|
-
children: [
|
|
1533
|
-
/* @__PURE__ */ jsx13(ControlButton, { label: "Zoom in", onClick: () => zoomIn(), children: /* @__PURE__ */ jsx13(
|
|
1534
|
-
"svg",
|
|
1535
|
-
{
|
|
1536
|
-
viewBox: "0 0 24 24",
|
|
1537
|
-
fill: "none",
|
|
1538
|
-
stroke: "currentColor",
|
|
1539
|
-
strokeWidth: "2",
|
|
1540
|
-
strokeLinecap: "round",
|
|
1541
|
-
children: /* @__PURE__ */ jsx13("path", { d: "M12 5v14M5 12h14" })
|
|
1542
|
-
}
|
|
1543
|
-
) }),
|
|
1544
|
-
/* @__PURE__ */ jsx13(ControlButton, { label: "Zoom out", onClick: () => zoomOut(), children: /* @__PURE__ */ jsx13(
|
|
1545
|
-
"svg",
|
|
1546
|
-
{
|
|
1547
|
-
viewBox: "0 0 24 24",
|
|
1548
|
-
fill: "none",
|
|
1549
|
-
stroke: "currentColor",
|
|
1550
|
-
strokeWidth: "2",
|
|
1551
|
-
strokeLinecap: "round",
|
|
1552
|
-
children: /* @__PURE__ */ jsx13("path", { d: "M5 12h14" })
|
|
1553
|
-
}
|
|
1554
|
-
) }),
|
|
1555
|
-
/* @__PURE__ */ jsx13(ControlButton, { label: "Fit view", onClick: () => fitView(), children: /* @__PURE__ */ jsx13(
|
|
1556
|
-
"svg",
|
|
1557
|
-
{
|
|
1558
|
-
viewBox: "0 0 24 24",
|
|
1559
|
-
fill: "none",
|
|
1560
|
-
stroke: "currentColor",
|
|
1561
|
-
strokeWidth: "2",
|
|
1562
|
-
strokeLinecap: "round",
|
|
1563
|
-
strokeLinejoin: "round",
|
|
1564
|
-
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" })
|
|
1565
|
-
}
|
|
1566
|
-
) })
|
|
1567
|
-
]
|
|
1568
|
-
}
|
|
1569
|
-
) });
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
// src/flow-group-node/flow-group-node.tsx
|
|
1573
|
-
import "react";
|
|
1574
|
-
import { Handle as Handle3, NodeResizer, Position as Position6, useNodes } from "@xyflow/react";
|
|
1575
|
-
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
1576
|
-
import { cn as cn10 } from "@elabs-ai/components-ui/lib/cn";
|
|
1577
|
-
|
|
1578
|
-
// src/use-flow-groups/use-flow-groups.ts
|
|
1579
|
-
import { useCallback as useCallback4 } from "react";
|
|
1580
|
-
import { useReactFlow as useReactFlow5 } from "@xyflow/react";
|
|
1581
|
-
|
|
1582
|
-
// src/use-flow-groups/group-operations.ts
|
|
1583
|
-
import { getNodesBounds } from "@xyflow/react";
|
|
1584
|
-
var FLOW_GROUP_NODE_TYPE = "group";
|
|
1585
|
-
var DEFAULT_GROUP_PADDING = 28;
|
|
1586
|
-
var GROUP_HEADER_OFFSET = 44;
|
|
1587
|
-
var OVERVIEW_WIDTH = 220;
|
|
1588
|
-
var OVERVIEW_HEIGHT = 48;
|
|
1589
|
-
var COLLAPSED_STATE_KEY = "__flowGroupCollapsedState";
|
|
1590
|
-
function isFlowGroupProxyEdge(edge) {
|
|
1591
|
-
return Boolean(edge.data?.__flowGroupProxy);
|
|
1592
|
-
}
|
|
1593
|
-
function proxyEdgeId(groupId, originalEdgeId) {
|
|
1594
|
-
return `flow-group-proxy__${groupId}__${originalEdgeId}`;
|
|
1595
|
-
}
|
|
1596
|
-
function readCollapsedState(node) {
|
|
1597
|
-
return node.data?.[COLLAPSED_STATE_KEY];
|
|
1598
|
-
}
|
|
1599
|
-
function collectDescendantIds(nodes, groupId) {
|
|
1600
|
-
const childrenByParent = /* @__PURE__ */ new Map();
|
|
1601
|
-
for (const n of nodes) {
|
|
1602
|
-
if (n.parentId) {
|
|
1603
|
-
const arr = childrenByParent.get(n.parentId);
|
|
1604
|
-
if (arr) arr.push(n);
|
|
1605
|
-
else childrenByParent.set(n.parentId, [n]);
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
const out = /* @__PURE__ */ new Set();
|
|
1609
|
-
const stack = [...childrenByParent.get(groupId) ?? []];
|
|
1610
|
-
while (stack.length > 0) {
|
|
1611
|
-
const n = stack.pop();
|
|
1612
|
-
if (out.has(n.id)) continue;
|
|
1613
|
-
out.add(n.id);
|
|
1614
|
-
const kids = childrenByParent.get(n.id);
|
|
1615
|
-
if (kids) stack.push(...kids);
|
|
1616
|
-
}
|
|
1617
|
-
return out;
|
|
1618
|
-
}
|
|
1619
|
-
function groupNodes(nodes, edges, nodeIds, options) {
|
|
1620
|
-
const idSet = new Set(nodeIds);
|
|
1621
|
-
const children = nodes.filter((n) => idSet.has(n.id));
|
|
1622
|
-
if (children.length === 0) return { nodes, edges };
|
|
1623
|
-
const bounds = getNodesBounds(children);
|
|
1624
|
-
const padding = options.padding ?? DEFAULT_GROUP_PADDING;
|
|
1625
|
-
const originX = bounds.x - padding;
|
|
1626
|
-
const originY = bounds.y - padding - GROUP_HEADER_OFFSET;
|
|
1627
|
-
const data = {
|
|
1628
|
-
title: options.title ?? "Group",
|
|
1629
|
-
childCount: children.length,
|
|
1630
|
-
...options.tone ? { tone: options.tone } : {}
|
|
1631
|
-
};
|
|
1632
|
-
const groupNode = {
|
|
1633
|
-
id: options.groupId,
|
|
1634
|
-
type: FLOW_GROUP_NODE_TYPE,
|
|
1635
|
-
position: { x: originX, y: originY },
|
|
1636
|
-
width: bounds.width + padding * 2,
|
|
1637
|
-
height: bounds.height + padding * 2 + GROUP_HEADER_OFFSET,
|
|
1638
|
-
data
|
|
1639
|
-
};
|
|
1640
|
-
const reparented = /* @__PURE__ */ new Map();
|
|
1641
|
-
for (const child of children) {
|
|
1642
|
-
reparented.set(child.id, {
|
|
1643
|
-
...child,
|
|
1644
|
-
parentId: options.groupId,
|
|
1645
|
-
extent: "parent",
|
|
1646
|
-
position: { x: child.position.x - originX, y: child.position.y - originY }
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
let earliest = nodes.length;
|
|
1650
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
1651
|
-
if (idSet.has(nodes[i].id)) {
|
|
1652
|
-
earliest = i;
|
|
1653
|
-
break;
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
const mapped = nodes.map((n) => reparented.get(n.id) ?? n);
|
|
1657
|
-
const resultNodes = [...mapped.slice(0, earliest), groupNode, ...mapped.slice(earliest)];
|
|
1658
|
-
return { nodes: resultNodes, edges };
|
|
1659
|
-
}
|
|
1660
|
-
function ungroup(nodes, edges, groupId) {
|
|
1661
|
-
const group = nodes.find((n) => n.id === groupId);
|
|
1662
|
-
if (!group) return { nodes, edges };
|
|
1663
|
-
const base = group.data?.collapsed ? expandGroup(nodes, edges, groupId) : { nodes, edges };
|
|
1664
|
-
const liveGroup = base.nodes.find((n) => n.id === groupId);
|
|
1665
|
-
const gx = liveGroup.position.x;
|
|
1666
|
-
const gy = liveGroup.position.y;
|
|
1667
|
-
const resultNodes = base.nodes.filter((n) => n.id !== groupId).map((n) => {
|
|
1668
|
-
if (n.parentId === groupId) {
|
|
1669
|
-
const {
|
|
1670
|
-
parentId: _parentId,
|
|
1671
|
-
extent: _extent,
|
|
1672
|
-
...rest
|
|
1673
|
-
} = n;
|
|
1674
|
-
return {
|
|
1675
|
-
...rest,
|
|
1676
|
-
position: { x: n.position.x + gx, y: n.position.y + gy }
|
|
1677
|
-
};
|
|
1678
|
-
}
|
|
1679
|
-
return n;
|
|
1680
|
-
});
|
|
1681
|
-
return { nodes: resultNodes, edges: base.edges };
|
|
1682
|
-
}
|
|
1683
|
-
function collapseGroup(nodes, edges, groupId) {
|
|
1684
|
-
const group = nodes.find((n) => n.id === groupId);
|
|
1685
|
-
if (!group) return { nodes, edges };
|
|
1686
|
-
if (group.data?.collapsed) return { nodes, edges };
|
|
1687
|
-
const descendantIds = collectDescendantIds(nodes, groupId);
|
|
1688
|
-
const descendantSnapshots = nodes.filter((n) => descendantIds.has(n.id));
|
|
1689
|
-
const directChildCount = nodes.filter((n) => n.parentId === groupId).length;
|
|
1690
|
-
const isInside = (id) => descendantIds.has(id);
|
|
1691
|
-
const reroutedEdges = [];
|
|
1692
|
-
const proxyEdges = [];
|
|
1693
|
-
for (const edge of edges) {
|
|
1694
|
-
const sourceInside = isInside(edge.source);
|
|
1695
|
-
const targetInside = isInside(edge.target);
|
|
1696
|
-
if (sourceInside === targetInside) continue;
|
|
1697
|
-
reroutedEdges.push(edge);
|
|
1698
|
-
const proxyData = {
|
|
1699
|
-
...edge.data ?? {},
|
|
1700
|
-
__flowGroupProxy: true,
|
|
1701
|
-
groupId
|
|
1702
|
-
};
|
|
1703
|
-
proxyEdges.push({
|
|
1704
|
-
...edge,
|
|
1705
|
-
id: proxyEdgeId(groupId, edge.id),
|
|
1706
|
-
source: sourceInside ? groupId : edge.source,
|
|
1707
|
-
target: targetInside ? groupId : edge.target,
|
|
1708
|
-
sourceHandle: void 0,
|
|
1709
|
-
targetHandle: void 0,
|
|
1710
|
-
data: proxyData
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
const collapsedState = {
|
|
1714
|
-
groupSnapshot: group,
|
|
1715
|
-
descendantSnapshots,
|
|
1716
|
-
reroutedEdges
|
|
1717
|
-
};
|
|
1718
|
-
const resultNodes = nodes.map((n) => {
|
|
1719
|
-
if (n.id === groupId) {
|
|
1720
|
-
return {
|
|
1721
|
-
...n,
|
|
1722
|
-
width: OVERVIEW_WIDTH,
|
|
1723
|
-
height: OVERVIEW_HEIGHT,
|
|
1724
|
-
data: {
|
|
1725
|
-
...n.data,
|
|
1726
|
-
collapsed: true,
|
|
1727
|
-
childCount: directChildCount,
|
|
1728
|
-
[COLLAPSED_STATE_KEY]: collapsedState
|
|
1729
|
-
}
|
|
1730
|
-
};
|
|
1731
|
-
}
|
|
1732
|
-
if (descendantIds.has(n.id)) {
|
|
1733
|
-
return { ...n, hidden: true };
|
|
1734
|
-
}
|
|
1735
|
-
return n;
|
|
1736
|
-
});
|
|
1737
|
-
const reroutedIds = new Set(reroutedEdges.map((e) => e.id));
|
|
1738
|
-
const resultEdges = edges.map(
|
|
1739
|
-
(e) => reroutedIds.has(e.id) ? { ...e, hidden: true } : e
|
|
1740
|
-
);
|
|
1741
|
-
resultEdges.push(...proxyEdges);
|
|
1742
|
-
return { nodes: resultNodes, edges: resultEdges };
|
|
1743
|
-
}
|
|
1744
|
-
function expandGroup(nodes, edges, groupId) {
|
|
1745
|
-
const group = nodes.find((n) => n.id === groupId);
|
|
1746
|
-
const state = group ? readCollapsedState(group) : void 0;
|
|
1747
|
-
if (!group || !state) return { nodes, edges };
|
|
1748
|
-
const snapshotById = new Map(state.descendantSnapshots.map((n) => [n.id, n]));
|
|
1749
|
-
const reroutedById = new Map(state.reroutedEdges.map((e) => [e.id, e]));
|
|
1750
|
-
const resultNodes = nodes.map((n) => {
|
|
1751
|
-
if (n.id === groupId) return state.groupSnapshot;
|
|
1752
|
-
const snap = snapshotById.get(n.id);
|
|
1753
|
-
return snap ? snap : n;
|
|
1754
|
-
});
|
|
1755
|
-
const resultEdges = edges.filter(
|
|
1756
|
-
(e) => !(isFlowGroupProxyEdge(e) && e.data?.groupId === groupId)
|
|
1757
|
-
).map((e) => {
|
|
1758
|
-
const original = reroutedById.get(e.id);
|
|
1759
|
-
return original ? original : e;
|
|
1760
|
-
});
|
|
1761
|
-
return { nodes: resultNodes, edges: resultEdges };
|
|
1762
|
-
}
|
|
1763
|
-
function toggleGroupCollapsed(nodes, edges, groupId) {
|
|
1764
|
-
const group = nodes.find((n) => n.id === groupId);
|
|
1765
|
-
if (!group) return { nodes, edges };
|
|
1766
|
-
return group.data?.collapsed ? expandGroup(nodes, edges, groupId) : collapseGroup(nodes, edges, groupId);
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
// src/use-flow-groups/use-flow-groups.ts
|
|
1770
|
-
function generateGroupId() {
|
|
1771
|
-
return `flow-group-${Math.random().toString(36).slice(2, 9)}`;
|
|
1772
|
-
}
|
|
1773
|
-
function useFlowGroups() {
|
|
1774
|
-
const { getNodes, getEdges, setNodes, setEdges } = useReactFlow5();
|
|
1775
|
-
const apply = useCallback4(
|
|
1776
|
-
(result) => {
|
|
1777
|
-
setNodes(result.nodes);
|
|
1778
|
-
setEdges(result.edges);
|
|
1779
|
-
},
|
|
1780
|
-
[setNodes, setEdges]
|
|
1781
|
-
);
|
|
1782
|
-
const groupNodes2 = useCallback4(
|
|
1783
|
-
(nodeIds, options) => {
|
|
1784
|
-
if (nodeIds.length === 0) return void 0;
|
|
1785
|
-
const groupId = options?.groupId ?? generateGroupId();
|
|
1786
|
-
const result = groupNodes(getNodes(), getEdges(), nodeIds, {
|
|
1787
|
-
...options,
|
|
1788
|
-
groupId
|
|
1789
|
-
});
|
|
1790
|
-
apply(result);
|
|
1791
|
-
return groupId;
|
|
1792
|
-
},
|
|
1793
|
-
[getNodes, getEdges, apply]
|
|
1794
|
-
);
|
|
1795
|
-
const groupSelection = useCallback4(
|
|
1796
|
-
(options) => {
|
|
1797
|
-
const selectedIds = getNodes().filter((n) => n.selected && !n.parentId).map((n) => n.id);
|
|
1798
|
-
return groupNodes2(selectedIds, options);
|
|
1799
|
-
},
|
|
1800
|
-
[getNodes, groupNodes2]
|
|
1801
|
-
);
|
|
1802
|
-
const ungroup2 = useCallback4(
|
|
1803
|
-
(groupId) => apply(ungroup(getNodes(), getEdges(), groupId)),
|
|
1804
|
-
[getNodes, getEdges, apply]
|
|
1805
|
-
);
|
|
1806
|
-
const collapseGroup2 = useCallback4(
|
|
1807
|
-
(groupId) => apply(collapseGroup(getNodes(), getEdges(), groupId)),
|
|
1808
|
-
[getNodes, getEdges, apply]
|
|
1809
|
-
);
|
|
1810
|
-
const expandGroup2 = useCallback4(
|
|
1811
|
-
(groupId) => apply(expandGroup(getNodes(), getEdges(), groupId)),
|
|
1812
|
-
[getNodes, getEdges, apply]
|
|
1813
|
-
);
|
|
1814
|
-
const toggleCollapse = useCallback4(
|
|
1815
|
-
(groupId) => apply(toggleGroupCollapsed(getNodes(), getEdges(), groupId)),
|
|
1816
|
-
[getNodes, getEdges, apply]
|
|
1817
|
-
);
|
|
1818
|
-
return {
|
|
1819
|
-
groupNodes: groupNodes2,
|
|
1820
|
-
groupSelection,
|
|
1821
|
-
ungroup: ungroup2,
|
|
1822
|
-
collapseGroup: collapseGroup2,
|
|
1823
|
-
expandGroup: expandGroup2,
|
|
1824
|
-
toggleCollapse
|
|
1825
|
-
};
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
// src/flow-group-node/flow-group-node.tsx
|
|
1829
|
-
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1830
|
-
var toneMark = {
|
|
1831
|
-
default: "text-muted-foreground",
|
|
1832
|
-
accent: "text-primary-text",
|
|
1833
|
-
success: "text-success",
|
|
1834
|
-
warning: "text-warning",
|
|
1835
|
-
destructive: "text-destructive"
|
|
1836
|
-
};
|
|
1837
|
-
var toneInk = {
|
|
1838
|
-
default: "text-muted-foreground",
|
|
1839
|
-
accent: "text-primary-text",
|
|
1840
|
-
success: "text-success-text",
|
|
1841
|
-
warning: "text-warning-text",
|
|
1842
|
-
destructive: "text-destructive-text"
|
|
1843
|
-
};
|
|
1844
|
-
var handleClassName2 = `!size-2 !border-2 !border-flow-group-border !bg-flow-group ${FLOW_HANDLE_ANCHOR_CLASS}`;
|
|
1845
|
-
function FlowGroupNode({ id, data, selected }) {
|
|
1846
|
-
const { toggleCollapse } = useFlowGroups();
|
|
1847
|
-
const nodes = useNodes();
|
|
1848
|
-
const collapsed = data.collapsed ?? false;
|
|
1849
|
-
const tone = data.tone ?? "default";
|
|
1850
|
-
const liveChildCount = nodes.filter((n) => n.parentId === id).length;
|
|
1851
|
-
const childCount = liveChildCount || data.childCount || 0;
|
|
1852
|
-
return /* @__PURE__ */ jsxs11(
|
|
1853
|
-
"div",
|
|
1854
|
-
{
|
|
1855
|
-
className: cn10(
|
|
1856
|
-
"flex h-full w-full flex-col rounded-lg border bg-flow-group/60 text-foreground",
|
|
1857
|
-
"border-flow-group-border transition-[box-shadow,border-color] duration-fast ease-standard motion-reduce:transition-none",
|
|
1858
|
-
collapsed ? "shadow-sm" : "shadow-none",
|
|
1859
|
-
selected && "ring-2 ring-ring"
|
|
1860
|
-
),
|
|
1861
|
-
children: [
|
|
1862
|
-
!collapsed && selected ? /* @__PURE__ */ jsx14(
|
|
1863
|
-
NodeResizer,
|
|
1864
|
-
{
|
|
1865
|
-
minWidth: 160,
|
|
1866
|
-
minHeight: 96,
|
|
1867
|
-
lineClassName: "!border-flow-group-border",
|
|
1868
|
-
handleClassName: handleClassName2
|
|
1869
|
-
}
|
|
1870
|
-
) : null,
|
|
1871
|
-
/* @__PURE__ */ jsx14(Handle3, { type: "target", position: Position6.Top, className: handleClassName2 }),
|
|
1872
|
-
/* @__PURE__ */ jsx14(Handle3, { type: "source", position: Position6.Bottom, className: handleClassName2 }),
|
|
1873
|
-
/* @__PURE__ */ jsxs11(
|
|
1874
|
-
"div",
|
|
1875
|
-
{
|
|
1876
|
-
className: cn10(
|
|
1877
|
-
"flex items-center gap-2 px-3 py-2",
|
|
1878
|
-
!collapsed && "border-b border-flow-group-border"
|
|
1879
|
-
),
|
|
1880
|
-
children: [
|
|
1881
|
-
/* @__PURE__ */ jsx14(
|
|
1882
|
-
"button",
|
|
1883
|
-
{
|
|
1884
|
-
type: "button",
|
|
1885
|
-
onClick: () => toggleCollapse(id),
|
|
1886
|
-
"aria-label": collapsed ? `Expand group ${data.title}` : `Collapse group ${data.title}`,
|
|
1887
|
-
"aria-expanded": !collapsed,
|
|
1888
|
-
className: cn10(
|
|
1889
|
-
"-ml-1 grid size-5 shrink-0 place-items-center rounded",
|
|
1890
|
-
"text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1891
|
-
"focus-ring"
|
|
1892
|
-
),
|
|
1893
|
-
children: collapsed ? /* @__PURE__ */ jsx14(ChevronRight, { "aria-hidden": "true", className: "size-4" }) : /* @__PURE__ */ jsx14(ChevronDown, { "aria-hidden": "true", className: "size-4" })
|
|
1894
|
-
}
|
|
1895
|
-
),
|
|
1896
|
-
data.icon ? /* @__PURE__ */ jsx14("span", { className: cn10("[&_svg]:size-4", toneMark[tone]), children: data.icon }) : null,
|
|
1897
|
-
/* @__PURE__ */ jsx14("span", { className: "min-w-0 flex-1 truncate text-body font-medium", children: data.title }),
|
|
1898
|
-
/* @__PURE__ */ jsx14(
|
|
1899
|
-
"span",
|
|
1900
|
-
{
|
|
1901
|
-
className: cn10(
|
|
1902
|
-
"shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-meta font-medium tabular-nums",
|
|
1903
|
-
toneInk[tone]
|
|
1904
|
-
),
|
|
1905
|
-
"aria-label": `${childCount} ${childCount === 1 ? "node" : "nodes"}`,
|
|
1906
|
-
children: childCount
|
|
1907
|
-
}
|
|
1908
|
-
)
|
|
1909
|
-
]
|
|
1910
|
-
}
|
|
1911
|
-
)
|
|
1912
|
-
]
|
|
1913
|
-
}
|
|
1914
|
-
);
|
|
1915
|
-
}
|
|
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";
|
|
1916
152
|
|
|
1917
153
|
// src/index.ts
|
|
1918
154
|
import {
|
|
1919
|
-
Background
|
|
155
|
+
Background,
|
|
1920
156
|
Controls,
|
|
1921
|
-
MiniMap
|
|
1922
|
-
Panel
|
|
1923
|
-
Position
|
|
1924
|
-
ReactFlow
|
|
1925
|
-
ReactFlowProvider
|
|
1926
|
-
useReactFlow
|
|
157
|
+
MiniMap,
|
|
158
|
+
Panel,
|
|
159
|
+
Position,
|
|
160
|
+
ReactFlow,
|
|
161
|
+
ReactFlowProvider,
|
|
162
|
+
useReactFlow,
|
|
1927
163
|
useNodesState,
|
|
1928
164
|
useEdgesState,
|
|
1929
165
|
addEdge
|
|
1930
166
|
} from "@xyflow/react";
|
|
1931
|
-
|
|
1932
|
-
// src/flow-weighted-edge/flow-weighted-edge.tsx
|
|
1933
|
-
import { useCallback as useCallback5, useMemo as useMemo3 } from "react";
|
|
1934
|
-
import {
|
|
1935
|
-
Position as Position7,
|
|
1936
|
-
getBezierPath as getBezierPath5,
|
|
1937
|
-
getSmoothStepPath,
|
|
1938
|
-
useEdges,
|
|
1939
|
-
useStore
|
|
1940
|
-
} from "@xyflow/react";
|
|
1941
|
-
import { resolveTokenColor } from "@elabs-ai/components-tokens";
|
|
1942
|
-
|
|
1943
|
-
// src/flow-weighted-edge/edge-label-pill.tsx
|
|
1944
|
-
import { EdgeLabelRenderer as EdgeLabelRenderer2 } from "@xyflow/react";
|
|
1945
|
-
import { cn as cn11 } from "@elabs-ai/components-ui/lib/cn";
|
|
1946
|
-
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1947
|
-
function EdgeLabelPill({
|
|
1948
|
-
label,
|
|
1949
|
-
secondaryLabel,
|
|
1950
|
-
x,
|
|
1951
|
-
y,
|
|
1952
|
-
selected,
|
|
1953
|
-
className,
|
|
1954
|
-
...props
|
|
1955
|
-
}) {
|
|
1956
|
-
if (!label && !secondaryLabel) return null;
|
|
1957
|
-
const accessibleName = [label, secondaryLabel].filter(Boolean).join(" \xB7 ");
|
|
1958
|
-
return /* @__PURE__ */ jsx15(EdgeLabelRenderer2, { children: /* @__PURE__ */ jsx15(
|
|
1959
|
-
"div",
|
|
1960
|
-
{
|
|
1961
|
-
style: {
|
|
1962
|
-
position: "absolute",
|
|
1963
|
-
transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`,
|
|
1964
|
-
pointerEvents: "none"
|
|
1965
|
-
},
|
|
1966
|
-
className: "nodrag nopan",
|
|
1967
|
-
"data-slot": "edge-label-pill-anchor",
|
|
1968
|
-
children: /* @__PURE__ */ jsxs12(
|
|
1969
|
-
"button",
|
|
1970
|
-
{
|
|
1971
|
-
type: "button",
|
|
1972
|
-
"aria-label": accessibleName,
|
|
1973
|
-
"data-slot": "edge-label-pill",
|
|
1974
|
-
...props,
|
|
1975
|
-
className: cn11(
|
|
1976
|
-
"pointer-events-auto flex items-center gap-1 rounded-full border bg-flow-node px-2 py-0.5",
|
|
1977
|
-
"text-meta font-medium text-flow-node-foreground shadow-sm",
|
|
1978
|
-
"transition-colors duration-fast ease-standard",
|
|
1979
|
-
"focus-ring",
|
|
1980
|
-
selected ? "border-ring" : "border-flow-group-border",
|
|
1981
|
-
className
|
|
1982
|
-
),
|
|
1983
|
-
children: [
|
|
1984
|
-
label ? /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: label }) : null,
|
|
1985
|
-
secondaryLabel ? /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", className: "text-flow-node-foreground/70 tabular-nums", children: secondaryLabel }) : null
|
|
1986
|
-
]
|
|
1987
|
-
}
|
|
1988
|
-
)
|
|
1989
|
-
}
|
|
1990
|
-
) });
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// src/flow-weighted-edge/back-edge-geometry.ts
|
|
1994
|
-
function backEdgeDetour(rects, axis, span, clearance) {
|
|
1995
|
-
const [lo, hi] = span[0] <= span[1] ? span : [span[1], span[0]];
|
|
1996
|
-
let far = Number.NEGATIVE_INFINITY;
|
|
1997
|
-
for (const rect of rects) {
|
|
1998
|
-
if (!(rect.width > 0) || !(rect.height > 0)) continue;
|
|
1999
|
-
const [near, beyond] = axis === "vertical" ? [rect.y, rect.y + rect.height] : [rect.x, rect.x + rect.width];
|
|
2000
|
-
if (beyond < lo || near > hi) continue;
|
|
2001
|
-
far = Math.max(far, axis === "vertical" ? rect.x + rect.width : rect.y + rect.height);
|
|
2002
|
-
}
|
|
2003
|
-
return Number.isFinite(far) ? far + clearance : null;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
// src/flow-weighted-edge/flow-weighted-edge.tsx
|
|
2007
|
-
import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2008
|
-
var NO_NODES = [];
|
|
2009
|
-
var FALLBACK_WEAK = "#6085a1";
|
|
2010
|
-
var FALLBACK_STRONG = "#496d89";
|
|
2011
|
-
var BACK_EDGE_DASHARRAY = "6 4";
|
|
2012
|
-
var BACK_EDGE_OPACITY = 0.7;
|
|
2013
|
-
var BACK_EDGE_CLEARANCE = 40;
|
|
2014
|
-
var DEFAULT_BACK_EDGE_LABEL = "Back edge \u2014 runs against the process direction";
|
|
2015
|
-
function clamp01(t) {
|
|
2016
|
-
return Math.min(1, Math.max(0, t));
|
|
2017
|
-
}
|
|
2018
|
-
function hexToRgb(hex) {
|
|
2019
|
-
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i.exec(hex);
|
|
2020
|
-
if (!m) return [0, 0, 0];
|
|
2021
|
-
return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
|
|
2022
|
-
}
|
|
2023
|
-
function toHexByte(n) {
|
|
2024
|
-
return Math.round(Math.min(255, Math.max(0, n))).toString(16).padStart(2, "0");
|
|
2025
|
-
}
|
|
2026
|
-
function mixHex(a, b, t) {
|
|
2027
|
-
const [ar, ag, ab] = hexToRgb(a);
|
|
2028
|
-
const [br, bg, bb] = hexToRgb(b);
|
|
2029
|
-
const r = ar + (br - ar) * t;
|
|
2030
|
-
const g = ag + (bg - ag) * t;
|
|
2031
|
-
const bl = ab + (bb - ab) * t;
|
|
2032
|
-
return `#${toHexByte(r)}${toHexByte(g)}${toHexByte(bl)}`;
|
|
2033
|
-
}
|
|
2034
|
-
function resolveValueStrokeColor(value, domain) {
|
|
2035
|
-
if (value === void 0 || !domain) return void 0;
|
|
2036
|
-
const [lo, hi] = domain;
|
|
2037
|
-
const t = hi === lo ? 1 : clamp01((value - lo) / (hi - lo));
|
|
2038
|
-
const weak = resolveTokenColor("--flow-edge-weak", { fallback: FALLBACK_WEAK });
|
|
2039
|
-
const strong = resolveTokenColor("--flow-edge-strong", { fallback: FALLBACK_STRONG });
|
|
2040
|
-
return mixHex(weak, strong, t);
|
|
2041
|
-
}
|
|
2042
|
-
function FlowWeightedEdge({
|
|
2043
|
-
id,
|
|
2044
|
-
sourceX,
|
|
2045
|
-
sourceY,
|
|
2046
|
-
targetX,
|
|
2047
|
-
targetY,
|
|
2048
|
-
sourcePosition,
|
|
2049
|
-
targetPosition,
|
|
2050
|
-
markerEnd,
|
|
2051
|
-
style,
|
|
2052
|
-
selected,
|
|
2053
|
-
data
|
|
2054
|
-
}) {
|
|
2055
|
-
const edges = useEdges();
|
|
2056
|
-
const widthByEdgeId = useMemo3(
|
|
2057
|
-
() => computeEdgeWeightScale(edges),
|
|
2058
|
-
[edges]
|
|
2059
|
-
);
|
|
2060
|
-
const variant = data?.variant ?? "forward";
|
|
2061
|
-
const isBack = variant === "back";
|
|
2062
|
-
const nodes = useStore(
|
|
2063
|
-
useCallback5((s) => isBack ? s.nodes : NO_NODES, [isBack])
|
|
2064
|
-
);
|
|
2065
|
-
const pathType = data?.path ?? "bezier";
|
|
2066
|
-
const axis = targetPosition === Position7.Top || targetPosition === Position7.Bottom ? "vertical" : "horizontal";
|
|
2067
|
-
const rects = useMemo3(() => {
|
|
2068
|
-
if (!isBack) return [];
|
|
2069
|
-
const out = [];
|
|
2070
|
-
for (const node of nodes) {
|
|
2071
|
-
if (node.parentId) continue;
|
|
2072
|
-
const width = node.measured?.width ?? node.width;
|
|
2073
|
-
const height = node.measured?.height ?? node.height;
|
|
2074
|
-
if (!width || !height) continue;
|
|
2075
|
-
out.push({ x: node.position.x, y: node.position.y, width, height });
|
|
2076
|
-
}
|
|
2077
|
-
return out;
|
|
2078
|
-
}, [isBack, nodes]);
|
|
2079
|
-
const detour = isBack ? backEdgeDetour(
|
|
2080
|
-
rects,
|
|
2081
|
-
axis,
|
|
2082
|
-
axis === "vertical" ? [sourceY, targetY] : [sourceX, targetX],
|
|
2083
|
-
BACK_EDGE_CLEARANCE
|
|
2084
|
-
) : null;
|
|
2085
|
-
const [edgePath, labelX, labelY] = isBack ? getSmoothStepPath({
|
|
2086
|
-
sourceX,
|
|
2087
|
-
sourceY,
|
|
2088
|
-
sourcePosition,
|
|
2089
|
-
targetX,
|
|
2090
|
-
targetY,
|
|
2091
|
-
targetPosition,
|
|
2092
|
-
offset: BACK_EDGE_CLEARANCE,
|
|
2093
|
-
...detour === null ? {} : axis === "vertical" ? { centerX: detour } : { centerY: detour }
|
|
2094
|
-
}) : pathType === "smoothstep" ? getSmoothStepPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }) : getBezierPath5({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
|
|
2095
|
-
const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];
|
|
2096
|
-
const valueColor = useMemo3(
|
|
2097
|
-
() => resolveValueStrokeColor(data?.value, data?.valueDomain),
|
|
2098
|
-
[data?.value, data?.valueDomain]
|
|
2099
|
-
);
|
|
2100
|
-
const stroke = selected ? "var(--ring)" : valueColor ?? "var(--flow-edge)";
|
|
2101
|
-
const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;
|
|
2102
|
-
const edge = /* @__PURE__ */ jsx16(
|
|
2103
|
-
FlowEdgePath,
|
|
2104
|
-
{
|
|
2105
|
-
id,
|
|
2106
|
-
path: edgePath,
|
|
2107
|
-
markerEnd,
|
|
2108
|
-
"data-slot": "flow-weighted-edge",
|
|
2109
|
-
"data-variant": variant,
|
|
2110
|
-
"data-weight": data?.weight,
|
|
2111
|
-
"data-value": data?.value,
|
|
2112
|
-
stroke,
|
|
2113
|
-
strokeWidth,
|
|
2114
|
-
strokeDasharray: isBack ? BACK_EDGE_DASHARRAY : void 0,
|
|
2115
|
-
strokeOpacity: isBack ? BACK_EDGE_OPACITY : void 0,
|
|
2116
|
-
style
|
|
2117
|
-
}
|
|
2118
|
-
);
|
|
2119
|
-
return /* @__PURE__ */ jsxs13(Fragment5, { children: [
|
|
2120
|
-
isBack ? (
|
|
2121
|
-
// A `data-variant` is invisible to assistive technology, so the back
|
|
2122
|
-
// edge's meaning is also published as a named graphic. Only the back
|
|
2123
|
-
// variant is wrapped — a forward edge's DOM is unchanged from before
|
|
2124
|
-
// this prop existed.
|
|
2125
|
-
/* @__PURE__ */ jsx16("g", { role: "img", "aria-label": data?.variantLabel ?? DEFAULT_BACK_EDGE_LABEL, children: edge })
|
|
2126
|
-
) : edge,
|
|
2127
|
-
/* @__PURE__ */ jsx16(
|
|
2128
|
-
EdgeLabelPill,
|
|
2129
|
-
{
|
|
2130
|
-
label: data?.label,
|
|
2131
|
-
secondaryLabel: data?.secondaryLabel,
|
|
2132
|
-
x: labelX,
|
|
2133
|
-
y: labelY,
|
|
2134
|
-
selected,
|
|
2135
|
-
...data?.labelProps
|
|
2136
|
-
}
|
|
2137
|
-
)
|
|
2138
|
-
] });
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
// src/flow-weighted-edge/edge-aria.ts
|
|
2142
|
-
var DEFAULT_WEIGHT_LABEL = "weight";
|
|
2143
|
-
var DEFAULT_VALUE_LABEL = "value";
|
|
2144
|
-
function defaultNameOf(nodeId) {
|
|
2145
|
-
return nodeId;
|
|
2146
|
-
}
|
|
2147
|
-
function defaultFormatNumber(n) {
|
|
2148
|
-
return String(n);
|
|
2149
|
-
}
|
|
2150
|
-
function buildWeightedEdgeAriaLabel(edge, opts = {}) {
|
|
2151
|
-
if (edge.ariaLabel) return edge.ariaLabel;
|
|
2152
|
-
const { weight, value, label, secondaryLabel } = edge.data ?? {};
|
|
2153
|
-
if (weight === void 0 && value === void 0 && !label && !secondaryLabel) {
|
|
2154
|
-
return void 0;
|
|
2155
|
-
}
|
|
2156
|
-
const {
|
|
2157
|
-
weightLabel = DEFAULT_WEIGHT_LABEL,
|
|
2158
|
-
valueLabel = DEFAULT_VALUE_LABEL,
|
|
2159
|
-
nameOf = defaultNameOf,
|
|
2160
|
-
formatNumber = defaultFormatNumber
|
|
2161
|
-
} = opts;
|
|
2162
|
-
const parts = [`Edge from ${nameOf(edge.source)} to ${nameOf(edge.target)}`];
|
|
2163
|
-
if (weight !== void 0) parts.push(`${weightLabel} ${formatNumber(weight)}`);
|
|
2164
|
-
if (value !== void 0) parts.push(`${valueLabel} ${formatNumber(value)}`);
|
|
2165
|
-
const pillText = [label, secondaryLabel].filter(Boolean).join(" ");
|
|
2166
|
-
if (pillText) parts.push(pillText);
|
|
2167
|
-
return parts.join(", ");
|
|
2168
|
-
}
|
|
2169
|
-
function withWeightedEdgeAria(edges, opts) {
|
|
2170
|
-
return edges.map((edge) => {
|
|
2171
|
-
if (edge.ariaLabel) return edge;
|
|
2172
|
-
const ariaLabel = buildWeightedEdgeAriaLabel(edge, opts);
|
|
2173
|
-
return ariaLabel === void 0 ? edge : { ...edge, ariaLabel };
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
// src/flow-self-loop-edge/flow-self-loop-edge.tsx
|
|
2178
|
-
import { useMemo as useMemo4 } from "react";
|
|
2179
|
-
import { useEdges as useEdges2, useInternalNode as useInternalNode3 } from "@xyflow/react";
|
|
2180
|
-
|
|
2181
|
-
// src/flow-self-loop-edge/self-loop-geometry.ts
|
|
2182
|
-
var DEFAULT_LOOP_RADIUS = 28;
|
|
2183
|
-
var CONTROL_REACH_X = 1.2;
|
|
2184
|
-
var CONTROL_REACH_Y = 2.4;
|
|
2185
|
-
var APEX_FACTOR = 3 / 4 * CONTROL_REACH_Y;
|
|
2186
|
-
function selfLoopPath(anchor, loopRadius) {
|
|
2187
|
-
const cx = Number.isFinite(anchor.centerX) ? anchor.centerX : 0;
|
|
2188
|
-
const ay = Number.isFinite(anchor.topY) ? anchor.topY : 0;
|
|
2189
|
-
const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
|
|
2190
|
-
const startX = cx + r;
|
|
2191
|
-
const endX = cx - r;
|
|
2192
|
-
const controlY = ay - r * CONTROL_REACH_Y;
|
|
2193
|
-
return {
|
|
2194
|
-
path: `M ${startX},${ay} C ${startX + r * CONTROL_REACH_X},${controlY} ${endX - r * CONTROL_REACH_X},${controlY} ${endX},${ay}`,
|
|
2195
|
-
labelX: cx,
|
|
2196
|
-
labelY: ay - r * APEX_FACTOR
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
function outward(x, y, centerX, centerY, fallback) {
|
|
2200
|
-
const dx = x - centerX;
|
|
2201
|
-
const dy = y - centerY;
|
|
2202
|
-
const length = Math.hypot(dx, dy);
|
|
2203
|
-
if (!Number.isFinite(length) || length < 1e-6) return fallback;
|
|
2204
|
-
return [dx / length, dy / length];
|
|
2205
|
-
}
|
|
2206
|
-
function selfLoopHandleArc(anchor, loopRadius) {
|
|
2207
|
-
const num = (value) => Number.isFinite(value) ? value : 0;
|
|
2208
|
-
const sx = num(anchor.sourceX);
|
|
2209
|
-
const sy = num(anchor.sourceY);
|
|
2210
|
-
const tx = num(anchor.targetX);
|
|
2211
|
-
const ty = num(anchor.targetY);
|
|
2212
|
-
const cx = num(anchor.centerX);
|
|
2213
|
-
const cy = num(anchor.centerY);
|
|
2214
|
-
const width = Math.max(0, num(anchor.width));
|
|
2215
|
-
const height = Math.max(0, num(anchor.height));
|
|
2216
|
-
const r = Number.isFinite(loopRadius) && loopRadius > 0 ? loopRadius : DEFAULT_LOOP_RADIUS;
|
|
2217
|
-
const [osx, osy] = outward(sx, sy, cx, cy, [0, 1]);
|
|
2218
|
-
const [otx, oty] = outward(tx, ty, cx, cy, [-osx, -osy]);
|
|
2219
|
-
const lx = osy;
|
|
2220
|
-
const ly = -osx;
|
|
2221
|
-
const clearance = Math.abs(lx) * (width / 2) + Math.abs(ly) * (height / 2) + r;
|
|
2222
|
-
const reach = 4 / 3 * clearance;
|
|
2223
|
-
const c1x = sx + osx * r + lx * reach;
|
|
2224
|
-
const c1y = sy + osy * r + ly * reach;
|
|
2225
|
-
const c2x = tx + otx * r + lx * reach;
|
|
2226
|
-
const c2y = ty + oty * r + ly * reach;
|
|
2227
|
-
return {
|
|
2228
|
-
path: `M ${sx},${sy} C ${c1x},${c1y} ${c2x},${c2y} ${tx},${ty}`,
|
|
2229
|
-
// A cubic's midpoint is (P0 + 3·C1 + 3·C2 + P3) / 8 — the arc's apex, which `reach`
|
|
2230
|
-
// above places `loopRadius` clear of the card, so the label sits off the node.
|
|
2231
|
-
labelX: (sx + 3 * c1x + 3 * c2x + tx) / 8,
|
|
2232
|
-
labelY: (sy + 3 * c1y + 3 * c2y + ty) / 8
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
|
|
2236
|
-
// src/flow-self-loop-edge/flow-self-loop-edge.tsx
|
|
2237
|
-
import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2238
|
-
function nodeName(data, fallback) {
|
|
2239
|
-
if (data && typeof data === "object" && "title" in data) {
|
|
2240
|
-
const title = data.title;
|
|
2241
|
-
if (typeof title === "string" && title.length > 0) return title;
|
|
2242
|
-
}
|
|
2243
|
-
return fallback;
|
|
2244
|
-
}
|
|
2245
|
-
function FlowSelfLoopEdge({
|
|
2246
|
-
id,
|
|
2247
|
-
source,
|
|
2248
|
-
sourceX,
|
|
2249
|
-
sourceY,
|
|
2250
|
-
targetX,
|
|
2251
|
-
targetY,
|
|
2252
|
-
markerEnd,
|
|
2253
|
-
style,
|
|
2254
|
-
selected,
|
|
2255
|
-
data
|
|
2256
|
-
}) {
|
|
2257
|
-
const edges = useEdges2();
|
|
2258
|
-
const widthByEdgeId = useMemo4(
|
|
2259
|
-
() => computeEdgeWeightScale(edges),
|
|
2260
|
-
[edges]
|
|
2261
|
-
);
|
|
2262
|
-
const node = useInternalNode3(source);
|
|
2263
|
-
const measuredWidth = node?.measured?.width;
|
|
2264
|
-
const measuredHeight = node?.measured?.height;
|
|
2265
|
-
const loopRadius = data?.loopRadius ?? DEFAULT_LOOP_RADIUS;
|
|
2266
|
-
const { path, labelX, labelY } = useMemo4(() => {
|
|
2267
|
-
if (node && measuredWidth && measuredHeight) {
|
|
2268
|
-
return selfLoopHandleArc(
|
|
2269
|
-
{
|
|
2270
|
-
sourceX,
|
|
2271
|
-
sourceY,
|
|
2272
|
-
targetX,
|
|
2273
|
-
targetY,
|
|
2274
|
-
centerX: node.internals.positionAbsolute.x + measuredWidth / 2,
|
|
2275
|
-
centerY: node.internals.positionAbsolute.y + measuredHeight / 2,
|
|
2276
|
-
width: measuredWidth,
|
|
2277
|
-
height: measuredHeight
|
|
2278
|
-
},
|
|
2279
|
-
loopRadius
|
|
2280
|
-
);
|
|
2281
|
-
}
|
|
2282
|
-
return selfLoopPath(
|
|
2283
|
-
{ centerX: (sourceX + targetX) / 2, topY: Math.min(sourceY, targetY) },
|
|
2284
|
-
loopRadius
|
|
2285
|
-
);
|
|
2286
|
-
}, [node, measuredWidth, measuredHeight, sourceX, sourceY, targetX, targetY, loopRadius]);
|
|
2287
|
-
const scaledWidth = widthByEdgeId.get(id) ?? DEFAULT_EDGE_WIDTH_RANGE[0];
|
|
2288
|
-
const stroke = selected ? "var(--ring)" : "var(--flow-edge)";
|
|
2289
|
-
const strokeWidth = selected ? scaledWidth + 1.5 : scaledWidth;
|
|
2290
|
-
const accessibleName = data?.loopLabel ?? `Self-loop on ${nodeName(node?.data, source)} \u2014 this step repeats`;
|
|
2291
|
-
return /* @__PURE__ */ jsxs14(Fragment6, { children: [
|
|
2292
|
-
/* @__PURE__ */ jsx17("g", { role: "img", "aria-label": accessibleName, children: /* @__PURE__ */ jsx17(
|
|
2293
|
-
FlowEdgePath,
|
|
2294
|
-
{
|
|
2295
|
-
id,
|
|
2296
|
-
path,
|
|
2297
|
-
markerEnd,
|
|
2298
|
-
"data-slot": "flow-self-loop-edge",
|
|
2299
|
-
stroke,
|
|
2300
|
-
strokeWidth,
|
|
2301
|
-
style: { fill: "none", ...style }
|
|
2302
|
-
}
|
|
2303
|
-
) }),
|
|
2304
|
-
/* @__PURE__ */ jsx17(
|
|
2305
|
-
EdgeLabelPill,
|
|
2306
|
-
{
|
|
2307
|
-
label: data?.label,
|
|
2308
|
-
secondaryLabel: data?.secondaryLabel,
|
|
2309
|
-
x: labelX,
|
|
2310
|
-
y: labelY,
|
|
2311
|
-
selected,
|
|
2312
|
-
...data?.labelProps
|
|
2313
|
-
}
|
|
2314
|
-
)
|
|
2315
|
-
] });
|
|
2316
|
-
}
|
|
2317
167
|
export {
|
|
2318
|
-
|
|
168
|
+
Background,
|
|
2319
169
|
CanvasShell,
|
|
2320
170
|
Controls,
|
|
171
|
+
DEFAULT_EDGE_TOKEN_RADIUS,
|
|
2321
172
|
DEFAULT_EDGE_WIDTH_RANGE,
|
|
2322
173
|
DEFAULT_LOOP_RADIUS,
|
|
2323
174
|
EdgeLabelPill,
|
|
@@ -2329,6 +180,7 @@ export {
|
|
|
2329
180
|
FlowButtonEdge,
|
|
2330
181
|
FlowEdge,
|
|
2331
182
|
FlowEdgePath,
|
|
183
|
+
FlowEdgeTokens,
|
|
2332
184
|
FlowFloatingEdge,
|
|
2333
185
|
FlowGroupNode,
|
|
2334
186
|
FlowMiniMap,
|
|
@@ -2341,27 +193,31 @@ export {
|
|
|
2341
193
|
HelperLines,
|
|
2342
194
|
InspectorPanel,
|
|
2343
195
|
Legend,
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
196
|
+
MiniMap,
|
|
197
|
+
Panel,
|
|
198
|
+
Position,
|
|
199
|
+
ReactFlow,
|
|
200
|
+
ReactFlowProvider,
|
|
2349
201
|
ZoomControls,
|
|
2350
202
|
addEdge,
|
|
2351
203
|
backEdgeDetour,
|
|
2352
204
|
buildWeightedEdgeAriaLabel,
|
|
2353
205
|
collapseGroup,
|
|
2354
206
|
computeEdgeWeightScale,
|
|
207
|
+
createEdgePathSampler,
|
|
2355
208
|
expandGroup,
|
|
209
|
+
flattenEdgePath,
|
|
2356
210
|
getEdgeParams,
|
|
2357
211
|
getHelperLines,
|
|
2358
212
|
groupNodes,
|
|
2359
213
|
handlePoint,
|
|
2360
214
|
isFlowGroupProxyEdge,
|
|
2361
215
|
layoutFlow,
|
|
216
|
+
layoutFlowElk,
|
|
2362
217
|
layoutGraph,
|
|
2363
218
|
pickClosestAnchors,
|
|
2364
219
|
pickClosestHandles,
|
|
220
|
+
pinBackbone,
|
|
2365
221
|
positionToSide,
|
|
2366
222
|
selfLoopHandleArc,
|
|
2367
223
|
selfLoopPath,
|
|
@@ -2374,7 +230,7 @@ export {
|
|
|
2374
230
|
useFlowLayout,
|
|
2375
231
|
useHelperLines,
|
|
2376
232
|
useNodesState,
|
|
2377
|
-
|
|
233
|
+
useReactFlow,
|
|
2378
234
|
withWeightedEdgeAria
|
|
2379
235
|
};
|
|
2380
236
|
//# sourceMappingURL=index.js.map
|