footprint-explainable-ui 0.6.0 → 0.7.2
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 +158 -401
- package/dist/flowchart.cjs +721 -485
- package/dist/flowchart.cjs.map +1 -1
- package/dist/flowchart.d.cts +68 -15
- package/dist/flowchart.d.ts +68 -15
- package/dist/flowchart.js +723 -488
- package/dist/flowchart.js.map +1 -1
- package/dist/index.cjs +1158 -436
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -31
- package/dist/index.d.ts +115 -31
- package/dist/index.js +1159 -442
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/flowchart.js
CHANGED
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
} from "@xyflow/react";
|
|
10
10
|
|
|
11
11
|
// src/components/StageNode/StageNode.tsx
|
|
12
|
-
import { memo } from "react";
|
|
12
|
+
import { memo, useEffect as useEffect2, useRef } from "react";
|
|
13
13
|
import { Handle, Position } from "@xyflow/react";
|
|
14
14
|
|
|
15
15
|
// src/theme/ThemeProvider.tsx
|
|
16
16
|
import { createContext, useContext } from "react";
|
|
17
17
|
|
|
18
18
|
// src/theme/tokens.ts
|
|
19
|
-
var
|
|
19
|
+
var rawDefaults = {
|
|
20
20
|
colors: {
|
|
21
21
|
primary: "#6366f1",
|
|
22
22
|
success: "#22c55e",
|
|
@@ -36,6 +36,26 @@ var defaultTokens = {
|
|
|
36
36
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
+
var defaultTokens = {
|
|
40
|
+
colors: {
|
|
41
|
+
primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
|
|
42
|
+
success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
|
|
43
|
+
error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
|
|
44
|
+
warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
|
|
45
|
+
bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
|
|
46
|
+
bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
|
|
47
|
+
bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
|
|
48
|
+
textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
|
|
49
|
+
textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
|
|
50
|
+
textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
|
|
51
|
+
border: `var(--fp-border, ${rawDefaults.colors.border})`
|
|
52
|
+
},
|
|
53
|
+
radius: `var(--fp-radius, ${rawDefaults.radius})`,
|
|
54
|
+
fontFamily: {
|
|
55
|
+
sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
|
|
56
|
+
mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
|
|
57
|
+
}
|
|
58
|
+
};
|
|
39
59
|
|
|
40
60
|
// src/theme/ThemeProvider.tsx
|
|
41
61
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -77,10 +97,169 @@ import { useState, useEffect } from "react";
|
|
|
77
97
|
|
|
78
98
|
// src/components/StageNode/StageNode.tsx
|
|
79
99
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
100
|
+
var KEYFRAMES_ID = "fp-stage-node-keyframes";
|
|
101
|
+
var KEYFRAMES_CSS = `
|
|
102
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
103
|
+
@keyframes fp-pulse {
|
|
104
|
+
0%, 100% { opacity: 0.4; transform: scale(1); }
|
|
105
|
+
50% { opacity: 0.15; transform: scale(1.06); }
|
|
106
|
+
}
|
|
107
|
+
@keyframes fp-blink {
|
|
108
|
+
0%, 100% { opacity: 1; }
|
|
109
|
+
50% { opacity: 0.3; }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
@media (prefers-reduced-motion: reduce) {
|
|
113
|
+
@keyframes fp-pulse { 0%, 100% { opacity: 0.3; } }
|
|
114
|
+
@keyframes fp-blink { 0%, 100% { opacity: 1; } }
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
var ICON_SIZE = 16;
|
|
118
|
+
function StageIcon({ type, color }) {
|
|
119
|
+
const s = ICON_SIZE;
|
|
120
|
+
const props = { width: s, height: s, viewBox: `0 0 ${s} ${s}`, fill: "none", style: { flexShrink: 0 } };
|
|
121
|
+
switch (type) {
|
|
122
|
+
// LLM / AI call — brain/sparkle
|
|
123
|
+
case "llm":
|
|
124
|
+
case "ai":
|
|
125
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
126
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "6", stroke: color, strokeWidth: "1.5" }),
|
|
127
|
+
/* @__PURE__ */ jsx2("path", { d: "M5.5 8C5.5 6.5 6.5 5 8 5S10.5 6.5 10.5 8", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" }),
|
|
128
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "9.5", r: "1", fill: color }),
|
|
129
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "3.5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
130
|
+
/* @__PURE__ */ jsx2("line", { x1: "12.5", y1: "4", x2: "11.2", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
131
|
+
/* @__PURE__ */ jsx2("line", { x1: "3.5", y1: "4", x2: "4.8", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
|
|
132
|
+
] });
|
|
133
|
+
// Tool / function call — gear
|
|
134
|
+
case "tool":
|
|
135
|
+
case "function":
|
|
136
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
137
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "3", stroke: color, strokeWidth: "1.5" }),
|
|
138
|
+
[0, 45, 90, 135, 180, 225, 270, 315].map((angle) => {
|
|
139
|
+
const rad = angle * Math.PI / 180;
|
|
140
|
+
const x1 = 8 + Math.cos(rad) * 4.5;
|
|
141
|
+
const y1 = 8 + Math.sin(rad) * 4.5;
|
|
142
|
+
const x2 = 8 + Math.cos(rad) * 6;
|
|
143
|
+
const y2 = 8 + Math.sin(rad) * 6;
|
|
144
|
+
return /* @__PURE__ */ jsx2("line", { x1, y1, x2, y2, stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }, angle);
|
|
145
|
+
})
|
|
146
|
+
] });
|
|
147
|
+
// RAG / retrieval — magnifying glass + doc
|
|
148
|
+
case "rag":
|
|
149
|
+
case "search":
|
|
150
|
+
case "retrieval":
|
|
151
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
152
|
+
/* @__PURE__ */ jsx2("circle", { cx: "7", cy: "7", r: "4", stroke: color, strokeWidth: "1.5" }),
|
|
153
|
+
/* @__PURE__ */ jsx2("line", { x1: "10", y1: "10", x2: "13.5", y2: "13.5", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
154
|
+
/* @__PURE__ */ jsx2("line", { x1: "5.5", y1: "6", x2: "8.5", y2: "6", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
155
|
+
/* @__PURE__ */ jsx2("line", { x1: "5.5", y1: "8", x2: "7.5", y2: "8", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
|
|
156
|
+
] });
|
|
157
|
+
// Parse / process — diamond with arrows
|
|
158
|
+
case "parse":
|
|
159
|
+
case "process":
|
|
160
|
+
case "transform":
|
|
161
|
+
return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: color, strokeWidth: "1.5", transform: "rotate(45 8 8)" }) });
|
|
162
|
+
// Start / seed — play triangle
|
|
163
|
+
case "start":
|
|
164
|
+
case "seed":
|
|
165
|
+
case "init":
|
|
166
|
+
return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("path", { d: "M5 3.5L12.5 8L5 12.5V3.5Z", fill: color, opacity: "0.8" }) });
|
|
167
|
+
// End / finalize — stop square
|
|
168
|
+
case "end":
|
|
169
|
+
case "finalize":
|
|
170
|
+
case "output":
|
|
171
|
+
return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: color, opacity: "0.8" }) });
|
|
172
|
+
// Agent — person silhouette
|
|
173
|
+
case "agent":
|
|
174
|
+
case "orchestrator":
|
|
175
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
176
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "5", r: "2.5", stroke: color, strokeWidth: "1.5" }),
|
|
177
|
+
/* @__PURE__ */ jsx2("path", { d: "M3.5 14C3.5 11 5.5 9 8 9S12.5 11 12.5 14", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
178
|
+
] });
|
|
179
|
+
// Swarm — multi-agent
|
|
180
|
+
case "swarm":
|
|
181
|
+
case "multi-agent":
|
|
182
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
183
|
+
/* @__PURE__ */ jsx2("circle", { cx: "5", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
184
|
+
/* @__PURE__ */ jsx2("circle", { cx: "11", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
185
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "11", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
186
|
+
/* @__PURE__ */ jsx2("line", { x1: "5", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" }),
|
|
187
|
+
/* @__PURE__ */ jsx2("line", { x1: "11", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" })
|
|
188
|
+
] });
|
|
189
|
+
// Guard / guardrail — shield
|
|
190
|
+
case "guard":
|
|
191
|
+
case "guardrail":
|
|
192
|
+
case "validate":
|
|
193
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
194
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 2L3 5V9C3 11.5 5 13.5 8 14.5C11 13.5 13 11.5 13 9V5L8 2Z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }),
|
|
195
|
+
/* @__PURE__ */ jsx2("path", { d: "M6 8L7.5 9.5L10 6.5", stroke: color, strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
196
|
+
] });
|
|
197
|
+
// Stream — wave
|
|
198
|
+
case "stream":
|
|
199
|
+
case "streaming":
|
|
200
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
201
|
+
/* @__PURE__ */ jsx2("path", { d: "M2 8C4 5 6 11 8 8S12 5 14 8", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
|
|
202
|
+
/* @__PURE__ */ jsx2("path", { d: "M2 11C4 8 6 14 8 11S12 8 14 11", stroke: color, strokeWidth: "1", strokeLinecap: "round", fill: "none", opacity: "0.5" })
|
|
203
|
+
] });
|
|
204
|
+
// Memory / state — database cylinder
|
|
205
|
+
case "memory":
|
|
206
|
+
case "state":
|
|
207
|
+
case "db":
|
|
208
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
209
|
+
/* @__PURE__ */ jsx2("ellipse", { cx: "8", cy: "4.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" }),
|
|
210
|
+
/* @__PURE__ */ jsx2("line", { x1: "3", y1: "4.5", x2: "3", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
|
|
211
|
+
/* @__PURE__ */ jsx2("line", { x1: "13", y1: "4.5", x2: "13", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
|
|
212
|
+
/* @__PURE__ */ jsx2("ellipse", { cx: "8", cy: "11.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" })
|
|
213
|
+
] });
|
|
214
|
+
// Loop — circular arrow
|
|
215
|
+
case "loop":
|
|
216
|
+
case "retry":
|
|
217
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
218
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 8A4 4 0 1 1 8 4", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
|
|
219
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 1.5L10.5 4L8 6.5", stroke: color, strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })
|
|
220
|
+
] });
|
|
221
|
+
// Lazy / service — cloud (deferred resolution, loaded on demand)
|
|
222
|
+
case "lazy":
|
|
223
|
+
case "service":
|
|
224
|
+
case "cloud":
|
|
225
|
+
return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
|
|
226
|
+
"path",
|
|
227
|
+
{
|
|
228
|
+
d: "M4.5 12C2.8 12 1.5 10.7 1.5 9C1.5 7.5 2.5 6.3 3.8 6C4 4 5.8 2.5 8 2.5C9.8 2.5 11.3 3.5 11.9 5C13.9 5.2 15.5 6.8 15.5 8.8C15.5 10.8 13.9 12.5 11.8 12.5H4.5",
|
|
229
|
+
stroke: color,
|
|
230
|
+
strokeWidth: "1.3",
|
|
231
|
+
strokeLinecap: "round",
|
|
232
|
+
fill: "none"
|
|
233
|
+
}
|
|
234
|
+
) });
|
|
235
|
+
// Decision — diamond (already handled by isDecider shape)
|
|
236
|
+
case "decision":
|
|
237
|
+
case "router":
|
|
238
|
+
return /* @__PURE__ */ jsxs("svg", { ...props, children: [
|
|
239
|
+
/* @__PURE__ */ jsx2("path", { d: "M8 2L14 8L8 14L2 8Z", stroke: color, strokeWidth: "1.5", fill: "none" }),
|
|
240
|
+
/* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "1.5", fill: color })
|
|
241
|
+
] });
|
|
242
|
+
default:
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
80
246
|
var StageNode = memo(function StageNode2({
|
|
81
247
|
data
|
|
82
248
|
}) {
|
|
83
|
-
const { label, active, done, error, linked, stepNumbers, dimmed, isSubflow, description } = data;
|
|
249
|
+
const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description } = data;
|
|
250
|
+
const effectiveIcon = icon || (isLazy ? "lazy" : void 0);
|
|
251
|
+
const isLazyUnresolved = isLazy && !done && !active;
|
|
252
|
+
const injectedRef = useRef(false);
|
|
253
|
+
useEffect2(() => {
|
|
254
|
+
if (injectedRef.current) return;
|
|
255
|
+
if (typeof document !== "undefined" && !document.getElementById(KEYFRAMES_ID)) {
|
|
256
|
+
const styleEl = document.createElement("style");
|
|
257
|
+
styleEl.id = KEYFRAMES_ID;
|
|
258
|
+
styleEl.textContent = KEYFRAMES_CSS;
|
|
259
|
+
document.head.appendChild(styleEl);
|
|
260
|
+
}
|
|
261
|
+
injectedRef.current = true;
|
|
262
|
+
}, []);
|
|
84
263
|
const isOnPath = active || done;
|
|
85
264
|
const bg = active ? theme.primary : done ? theme.success : error ? theme.error : theme.bgSecondary;
|
|
86
265
|
const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : theme.border;
|
|
@@ -142,7 +321,8 @@ var StageNode = memo(function StageNode2({
|
|
|
142
321
|
style: {
|
|
143
322
|
position: "absolute",
|
|
144
323
|
inset: -6,
|
|
145
|
-
borderRadius: `calc(${theme.radius} + 4px)`,
|
|
324
|
+
borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
|
|
325
|
+
transform: isDecider ? "rotate(45deg)" : void 0,
|
|
146
326
|
border: `2px solid ${theme.primary}`,
|
|
147
327
|
opacity: 0.4,
|
|
148
328
|
animation: "fp-pulse 2s ease-in-out infinite"
|
|
@@ -155,107 +335,175 @@ var StageNode = memo(function StageNode2({
|
|
|
155
335
|
style: {
|
|
156
336
|
position: "absolute",
|
|
157
337
|
inset: -6,
|
|
158
|
-
borderRadius: `calc(${theme.radius} + 4px)`,
|
|
338
|
+
borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
|
|
339
|
+
transform: isDecider ? "rotate(45deg)" : void 0,
|
|
159
340
|
border: `2px solid ${theme.primary}`,
|
|
160
341
|
opacity: 0.3,
|
|
161
342
|
animation: "fp-pulse 1.5s ease-out infinite"
|
|
162
343
|
}
|
|
163
344
|
}
|
|
164
345
|
),
|
|
165
|
-
/* @__PURE__ */
|
|
346
|
+
isDecider ? /* @__PURE__ */ jsx2(
|
|
166
347
|
"div",
|
|
167
348
|
{
|
|
168
349
|
style: {
|
|
169
350
|
background: bg,
|
|
170
|
-
border: `2px solid ${borderColor}`,
|
|
171
|
-
borderRadius:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
flexDirection: "column",
|
|
175
|
-
alignItems: "center",
|
|
176
|
-
gap: description ? 2 : 0,
|
|
351
|
+
border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
|
|
352
|
+
borderRadius: 4,
|
|
353
|
+
transform: "rotate(45deg)",
|
|
354
|
+
padding: 20,
|
|
177
355
|
boxShadow: shadow,
|
|
178
356
|
transition: "all 0.3s ease",
|
|
179
|
-
|
|
180
|
-
|
|
357
|
+
display: "flex",
|
|
358
|
+
alignItems: "center",
|
|
181
359
|
justifyContent: "center"
|
|
182
360
|
},
|
|
183
|
-
children:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
361
|
+
children: /* @__PURE__ */ jsxs(
|
|
362
|
+
"div",
|
|
363
|
+
{
|
|
364
|
+
style: {
|
|
365
|
+
transform: "rotate(-45deg)",
|
|
366
|
+
display: "flex",
|
|
367
|
+
flexDirection: "column",
|
|
368
|
+
alignItems: "center",
|
|
369
|
+
gap: 2,
|
|
370
|
+
fontFamily: theme.fontSans
|
|
371
|
+
},
|
|
372
|
+
children: [
|
|
373
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [
|
|
374
|
+
icon && /* @__PURE__ */ jsx2(StageIcon, { type: icon, color: textColor }),
|
|
375
|
+
!icon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u25C7" }),
|
|
376
|
+
/* @__PURE__ */ jsx2(
|
|
377
|
+
"span",
|
|
378
|
+
{
|
|
379
|
+
style: {
|
|
380
|
+
fontSize: 12,
|
|
381
|
+
fontWeight: 600,
|
|
382
|
+
color: textColor,
|
|
383
|
+
whiteSpace: "nowrap"
|
|
384
|
+
},
|
|
385
|
+
children: label
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
] }),
|
|
389
|
+
description && /* @__PURE__ */ jsx2(
|
|
390
|
+
"span",
|
|
391
|
+
{
|
|
392
|
+
style: {
|
|
393
|
+
fontSize: 9,
|
|
394
|
+
fontWeight: 400,
|
|
395
|
+
color: textColor,
|
|
396
|
+
opacity: 0.7,
|
|
397
|
+
whiteSpace: "nowrap",
|
|
398
|
+
overflow: "hidden",
|
|
399
|
+
textOverflow: "ellipsis",
|
|
400
|
+
maxWidth: 130
|
|
401
|
+
},
|
|
402
|
+
children: description
|
|
196
403
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
404
|
+
)
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
) : (
|
|
410
|
+
/* Standard rectangular node */
|
|
411
|
+
/* @__PURE__ */ jsxs(
|
|
412
|
+
"div",
|
|
413
|
+
{
|
|
414
|
+
style: {
|
|
415
|
+
background: bg,
|
|
416
|
+
border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
|
|
417
|
+
borderRadius: theme.radius,
|
|
418
|
+
padding: description ? "8px 16px" : "10px 20px",
|
|
419
|
+
display: "flex",
|
|
420
|
+
flexDirection: "column",
|
|
421
|
+
alignItems: "center",
|
|
422
|
+
gap: description ? 2 : 0,
|
|
423
|
+
boxShadow: shadow,
|
|
424
|
+
transition: "all 0.3s ease",
|
|
425
|
+
fontFamily: theme.fontSans,
|
|
426
|
+
minWidth: 100,
|
|
427
|
+
justifyContent: "center"
|
|
428
|
+
},
|
|
429
|
+
children: [
|
|
430
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
431
|
+
effectiveIcon && /* @__PURE__ */ jsx2(StageIcon, { type: effectiveIcon, color: textColor }),
|
|
432
|
+
done && !effectiveIcon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
|
|
433
|
+
active && !effectiveIcon && /* @__PURE__ */ jsx2(
|
|
434
|
+
"span",
|
|
435
|
+
{
|
|
436
|
+
style: {
|
|
437
|
+
width: 8,
|
|
438
|
+
height: 8,
|
|
439
|
+
borderRadius: "50%",
|
|
440
|
+
background: "#fff",
|
|
441
|
+
animation: "fp-blink 1s ease-in-out infinite",
|
|
442
|
+
flexShrink: 0
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
),
|
|
446
|
+
error && !effectiveIcon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
|
|
447
|
+
/* @__PURE__ */ jsx2(
|
|
448
|
+
"span",
|
|
449
|
+
{
|
|
450
|
+
style: {
|
|
451
|
+
fontSize: 13,
|
|
452
|
+
fontWeight: 500,
|
|
453
|
+
color: textColor,
|
|
454
|
+
whiteSpace: "nowrap"
|
|
455
|
+
},
|
|
456
|
+
children: label
|
|
457
|
+
}
|
|
458
|
+
),
|
|
459
|
+
isSubflow && /* @__PURE__ */ jsx2(
|
|
460
|
+
"span",
|
|
461
|
+
{
|
|
462
|
+
style: {
|
|
463
|
+
display: "inline-flex",
|
|
464
|
+
alignItems: "center",
|
|
465
|
+
justifyContent: "center",
|
|
466
|
+
width: 16,
|
|
467
|
+
height: 16,
|
|
468
|
+
borderRadius: 3,
|
|
469
|
+
border: `1.5px solid ${textColor}`,
|
|
470
|
+
position: "relative",
|
|
471
|
+
opacity: 0.7,
|
|
472
|
+
flexShrink: 0
|
|
473
|
+
},
|
|
474
|
+
children: /* @__PURE__ */ jsx2(
|
|
475
|
+
"span",
|
|
476
|
+
{
|
|
477
|
+
style: {
|
|
478
|
+
width: 8,
|
|
479
|
+
height: 8,
|
|
480
|
+
borderRadius: 2,
|
|
481
|
+
border: `1px solid ${textColor}`
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
)
|
|
487
|
+
] }),
|
|
488
|
+
description && /* @__PURE__ */ jsx2(
|
|
201
489
|
"span",
|
|
202
490
|
{
|
|
203
491
|
style: {
|
|
204
|
-
fontSize:
|
|
205
|
-
fontWeight:
|
|
492
|
+
fontSize: 10,
|
|
493
|
+
fontWeight: 400,
|
|
206
494
|
color: textColor,
|
|
207
|
-
whiteSpace: "nowrap"
|
|
208
|
-
},
|
|
209
|
-
children: label
|
|
210
|
-
}
|
|
211
|
-
),
|
|
212
|
-
isSubflow && /* @__PURE__ */ jsx2(
|
|
213
|
-
"span",
|
|
214
|
-
{
|
|
215
|
-
style: {
|
|
216
|
-
display: "inline-flex",
|
|
217
|
-
alignItems: "center",
|
|
218
|
-
justifyContent: "center",
|
|
219
|
-
width: 16,
|
|
220
|
-
height: 16,
|
|
221
|
-
borderRadius: 3,
|
|
222
|
-
border: `1.5px solid ${textColor}`,
|
|
223
|
-
position: "relative",
|
|
224
495
|
opacity: 0.7,
|
|
225
|
-
|
|
496
|
+
whiteSpace: "nowrap",
|
|
497
|
+
overflow: "hidden",
|
|
498
|
+
textOverflow: "ellipsis",
|
|
499
|
+
maxWidth: 160
|
|
226
500
|
},
|
|
227
|
-
children:
|
|
228
|
-
"span",
|
|
229
|
-
{
|
|
230
|
-
style: {
|
|
231
|
-
width: 8,
|
|
232
|
-
height: 8,
|
|
233
|
-
borderRadius: 2,
|
|
234
|
-
border: `1px solid ${textColor}`
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
)
|
|
501
|
+
children: description
|
|
238
502
|
}
|
|
239
503
|
)
|
|
240
|
-
]
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
{
|
|
244
|
-
style: {
|
|
245
|
-
fontSize: 10,
|
|
246
|
-
fontWeight: 400,
|
|
247
|
-
color: textColor,
|
|
248
|
-
opacity: 0.7,
|
|
249
|
-
whiteSpace: "nowrap",
|
|
250
|
-
overflow: "hidden",
|
|
251
|
-
textOverflow: "ellipsis",
|
|
252
|
-
maxWidth: 160
|
|
253
|
-
},
|
|
254
|
-
children: description
|
|
255
|
-
}
|
|
256
|
-
)
|
|
257
|
-
]
|
|
258
|
-
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
)
|
|
259
507
|
)
|
|
260
508
|
]
|
|
261
509
|
}
|
|
@@ -396,23 +644,24 @@ function FlowchartView({
|
|
|
396
644
|
}
|
|
397
645
|
|
|
398
646
|
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
399
|
-
import {
|
|
647
|
+
import { useMemo as useMemo2, useCallback as useCallback2, useEffect as useEffect3 } from "react";
|
|
400
648
|
import {
|
|
401
649
|
ReactFlow as ReactFlow2,
|
|
402
650
|
Background as Background2,
|
|
403
|
-
BackgroundVariant as BackgroundVariant2
|
|
651
|
+
BackgroundVariant as BackgroundVariant2,
|
|
652
|
+
useReactFlow
|
|
404
653
|
} from "@xyflow/react";
|
|
405
654
|
|
|
406
655
|
// src/components/FlowchartView/specToReactFlow.ts
|
|
407
656
|
var DEFAULT_COLORS = {
|
|
408
|
-
edgeDefault:
|
|
409
|
-
edgeExecuted:
|
|
410
|
-
edgeActive:
|
|
411
|
-
edgeLoop:
|
|
412
|
-
labelDefault:
|
|
413
|
-
labelExecuted:
|
|
414
|
-
labelLoop:
|
|
415
|
-
pathGlow: `${
|
|
657
|
+
edgeDefault: rawDefaults.colors.textMuted,
|
|
658
|
+
edgeExecuted: rawDefaults.colors.success,
|
|
659
|
+
edgeActive: rawDefaults.colors.primary,
|
|
660
|
+
edgeLoop: rawDefaults.colors.warning,
|
|
661
|
+
labelDefault: rawDefaults.colors.textSecondary,
|
|
662
|
+
labelExecuted: rawDefaults.colors.success,
|
|
663
|
+
labelLoop: rawDefaults.colors.warning,
|
|
664
|
+
pathGlow: `${rawDefaults.colors.success}4D`
|
|
416
665
|
// ~30% opacity hex
|
|
417
666
|
};
|
|
418
667
|
var Y_STEP = 100;
|
|
@@ -420,122 +669,33 @@ var X_SPREAD = 200;
|
|
|
420
669
|
function nid(n) {
|
|
421
670
|
return n.name || n.id || `spec-${Math.random()}`;
|
|
422
671
|
}
|
|
423
|
-
function
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const c = state.colors;
|
|
427
|
-
const executed = o && o.executedStages.has(source) && o.executedStages.has(target);
|
|
428
|
-
const isLeadingEdge = o && source === o.activeStage && !o.doneStages.has(target);
|
|
429
|
-
if (isLoop) {
|
|
430
|
-
let loopExecuted = false;
|
|
431
|
-
if (o?.executionOrder) {
|
|
432
|
-
const lastSourceIdx = o.executionOrder.lastIndexOf(source);
|
|
433
|
-
if (lastSourceIdx >= 0) {
|
|
434
|
-
loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(target);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
state.edges.push({
|
|
438
|
-
id: `se${state.edgeCounter}`,
|
|
439
|
-
source,
|
|
440
|
-
target,
|
|
441
|
-
sourceHandle: "loop-source",
|
|
442
|
-
targetHandle: "loop-target",
|
|
443
|
-
label: label ?? "loop",
|
|
444
|
-
type: "smoothstep",
|
|
445
|
-
pathOptions: { offset: 40, borderRadius: 16 },
|
|
446
|
-
style: {
|
|
447
|
-
stroke: c.edgeLoop,
|
|
448
|
-
strokeWidth: loopExecuted ? 3 : 2,
|
|
449
|
-
strokeDasharray: "6 3",
|
|
450
|
-
opacity: o && !loopExecuted ? 0.35 : 1
|
|
451
|
-
},
|
|
452
|
-
labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
|
|
453
|
-
animated: loopExecuted,
|
|
454
|
-
zIndex: 2
|
|
455
|
-
});
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
if (executed) {
|
|
459
|
-
state.edges.push({
|
|
460
|
-
id: `se${state.edgeCounter}-glow`,
|
|
461
|
-
source,
|
|
462
|
-
target,
|
|
463
|
-
style: {
|
|
464
|
-
stroke: c.pathGlow,
|
|
465
|
-
strokeWidth: 8,
|
|
466
|
-
opacity: 0.4
|
|
467
|
-
},
|
|
468
|
-
zIndex: 0,
|
|
469
|
-
selectable: false,
|
|
470
|
-
focusable: false
|
|
471
|
-
});
|
|
472
|
-
state.edges.push({
|
|
473
|
-
id: `se${state.edgeCounter}`,
|
|
474
|
-
source,
|
|
475
|
-
target,
|
|
476
|
-
label,
|
|
477
|
-
style: {
|
|
478
|
-
stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
|
|
479
|
-
strokeWidth: 3.5
|
|
480
|
-
},
|
|
481
|
-
labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
|
|
482
|
-
animated: !!isLeadingEdge,
|
|
483
|
-
zIndex: 1
|
|
484
|
-
});
|
|
485
|
-
} else {
|
|
486
|
-
state.edges.push({
|
|
487
|
-
id: `se${state.edgeCounter}`,
|
|
488
|
-
source,
|
|
489
|
-
target,
|
|
490
|
-
label,
|
|
491
|
-
style: {
|
|
492
|
-
stroke: c.edgeDefault,
|
|
493
|
-
strokeWidth: 1.5,
|
|
494
|
-
opacity: o ? 0.3 : 1
|
|
495
|
-
},
|
|
496
|
-
labelStyle: { fontSize: 10, fill: c.labelDefault }
|
|
497
|
-
});
|
|
672
|
+
function registerNode(state, node) {
|
|
673
|
+
if (node.id && node.name) {
|
|
674
|
+
state.idToName.set(node.id, node.name);
|
|
498
675
|
}
|
|
499
676
|
}
|
|
500
|
-
function
|
|
677
|
+
function walkLayout(node, state, x, y) {
|
|
678
|
+
if (!node) return { lastIds: [], bottomY: y };
|
|
679
|
+
registerNode(state, node);
|
|
501
680
|
const id = nid(node);
|
|
502
681
|
if (state.seen.has(id)) {
|
|
503
682
|
return { lastIds: [id], bottomY: y };
|
|
504
683
|
}
|
|
505
684
|
state.seen.add(id);
|
|
506
|
-
const isDecider = node.type === "decider" || node.hasDecider;
|
|
685
|
+
const isDecider = node.type === "decider" || !!node.hasDecider;
|
|
507
686
|
const isFork = node.type === "fork";
|
|
508
|
-
const o = state.overlay;
|
|
509
|
-
const isDone = o ? o.doneStages.has(id) : false;
|
|
510
|
-
const isActive = o ? o.activeStage === id : false;
|
|
511
|
-
const wasExecuted = o ? o.executedStages.has(id) : false;
|
|
512
|
-
const dimmed = o && !wasExecuted;
|
|
513
|
-
let stepNumbers;
|
|
514
|
-
if (o?.executionOrder) {
|
|
515
|
-
const nums = [];
|
|
516
|
-
for (let i = 0; i < o.executionOrder.length; i++) {
|
|
517
|
-
if (o.executionOrder[i] === id) nums.push(i + 1);
|
|
518
|
-
}
|
|
519
|
-
if (nums.length > 0) stepNumbers = nums;
|
|
520
|
-
}
|
|
521
687
|
state.nodes.push({
|
|
522
688
|
id,
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
dimmed,
|
|
534
|
-
stepNumbers,
|
|
535
|
-
isSubflow: !!node.isSubflowRoot
|
|
536
|
-
},
|
|
537
|
-
type: "stage",
|
|
538
|
-
style: dimmed ? { opacity: 0.35 } : void 0
|
|
689
|
+
x,
|
|
690
|
+
y,
|
|
691
|
+
label: node.name,
|
|
692
|
+
isDecider,
|
|
693
|
+
isFork,
|
|
694
|
+
description: node.description,
|
|
695
|
+
icon: node.icon,
|
|
696
|
+
subflowId: node.subflowId,
|
|
697
|
+
isSubflow: !!node.isSubflowRoot,
|
|
698
|
+
isLazy: node.isLazy
|
|
539
699
|
});
|
|
540
700
|
let lastIds = [id];
|
|
541
701
|
let bottomY = y;
|
|
@@ -546,120 +706,257 @@ function walk(node, state, x, y) {
|
|
|
546
706
|
const childResults = [];
|
|
547
707
|
for (let i = 0; i < node.children.length; i++) {
|
|
548
708
|
const child = node.children[i];
|
|
709
|
+
if (!child) continue;
|
|
549
710
|
const childX = startX + i * X_SPREAD;
|
|
550
711
|
const edgeLabel = node.branchIds?.[i];
|
|
551
|
-
|
|
552
|
-
|
|
712
|
+
state.edgeCounter++;
|
|
713
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: nid(child), label: edgeLabel, isLoop: false });
|
|
714
|
+
const result = walkLayout(child, state, childX, childY);
|
|
553
715
|
childResults.push(result);
|
|
554
716
|
}
|
|
555
717
|
lastIds = childResults.flatMap((r) => r.lastIds);
|
|
556
718
|
bottomY = Math.max(...childResults.map((r) => r.bottomY));
|
|
557
719
|
}
|
|
558
720
|
if (node.loopTarget) {
|
|
559
|
-
|
|
721
|
+
const resolvedTarget = state.idToName.get(node.loopTarget) ?? node.loopTarget;
|
|
722
|
+
state.edgeCounter++;
|
|
723
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: resolvedTarget, label: "loop", isLoop: true });
|
|
560
724
|
}
|
|
561
725
|
if (node.next) {
|
|
726
|
+
const rawNextId = nid(node.next);
|
|
727
|
+
const resolvedNextId = state.idToName.get(rawNextId) ?? rawNextId;
|
|
728
|
+
const isLoopRef = node.loopTarget && state.seen.has(resolvedNextId);
|
|
729
|
+
if (isLoopRef) {
|
|
730
|
+
return { lastIds, bottomY };
|
|
731
|
+
}
|
|
562
732
|
const nextY = bottomY + Y_STEP;
|
|
563
|
-
const nextId = nid(node.next);
|
|
564
733
|
for (const lid of lastIds) {
|
|
565
|
-
|
|
566
|
-
|
|
734
|
+
state.edgeCounter++;
|
|
735
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, isLoop: false });
|
|
567
736
|
}
|
|
568
|
-
|
|
569
|
-
return result;
|
|
737
|
+
return walkLayout(node.next, state, x, nextY);
|
|
570
738
|
}
|
|
571
739
|
return { lastIds, bottomY };
|
|
572
740
|
}
|
|
573
|
-
function
|
|
741
|
+
function specToLayout(spec) {
|
|
574
742
|
const state = {
|
|
575
743
|
nodes: [],
|
|
576
744
|
edges: [],
|
|
577
745
|
edgeCounter: 0,
|
|
578
746
|
seen: /* @__PURE__ */ new Set(),
|
|
579
|
-
|
|
580
|
-
colors: { ...DEFAULT_COLORS, ...colors }
|
|
747
|
+
idToName: /* @__PURE__ */ new Map()
|
|
581
748
|
};
|
|
582
|
-
|
|
583
|
-
return { nodes: state.nodes, edges: state.edges };
|
|
749
|
+
walkLayout(spec, state, 300, 0);
|
|
750
|
+
return { nodes: state.nodes, edges: state.edges, idToName: state.idToName };
|
|
584
751
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
function collectSubflows(node) {
|
|
599
|
-
if (node.isSubflowRoot && node.subflowStructure) {
|
|
600
|
-
const id = node.name || node.id || "";
|
|
601
|
-
map.set(id, node);
|
|
752
|
+
function applyOverlay(layout, overlay, colors) {
|
|
753
|
+
const c = { ...DEFAULT_COLORS, ...colors };
|
|
754
|
+
const o = overlay ?? null;
|
|
755
|
+
const nodes = layout.nodes.map((ln) => {
|
|
756
|
+
const isDone = o ? o.doneStages.has(ln.id) : false;
|
|
757
|
+
const isActive = o ? o.activeStage === ln.id : false;
|
|
758
|
+
const wasExecuted = o ? o.executedStages.has(ln.id) : false;
|
|
759
|
+
const dimmed = o && !wasExecuted;
|
|
760
|
+
let stepNumbers;
|
|
761
|
+
if (o?.executionOrder) {
|
|
762
|
+
const nums = [];
|
|
763
|
+
for (let i = 0; i < o.executionOrder.length; i++) {
|
|
764
|
+
if (o.executionOrder[i] === ln.id) nums.push(i + 1);
|
|
602
765
|
}
|
|
603
|
-
if (
|
|
604
|
-
if (node.next) collectSubflows(node.next);
|
|
766
|
+
if (nums.length > 0) stepNumbers = nums;
|
|
605
767
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
768
|
+
return {
|
|
769
|
+
id: ln.id,
|
|
770
|
+
position: { x: ln.x, y: ln.y },
|
|
771
|
+
data: {
|
|
772
|
+
label: ln.label,
|
|
773
|
+
active: isActive,
|
|
774
|
+
done: isDone,
|
|
775
|
+
error: false,
|
|
776
|
+
isDecider: ln.isDecider,
|
|
777
|
+
isFork: ln.isFork,
|
|
778
|
+
description: ln.description,
|
|
779
|
+
icon: ln.icon,
|
|
780
|
+
subflowId: ln.subflowId,
|
|
781
|
+
dimmed,
|
|
782
|
+
stepNumbers,
|
|
783
|
+
isSubflow: ln.isSubflow,
|
|
784
|
+
isLazy: ln.isLazy
|
|
785
|
+
},
|
|
786
|
+
type: "stage",
|
|
787
|
+
style: dimmed ? { opacity: 0.35 } : void 0
|
|
614
788
|
};
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
description: subflowNode.description
|
|
789
|
+
});
|
|
790
|
+
const edges = [];
|
|
791
|
+
for (const le of layout.edges) {
|
|
792
|
+
const executed = o && o.executedStages.has(le.source) && o.executedStages.has(le.target);
|
|
793
|
+
const isLeadingEdge = o && le.source === o.activeStage && !o.doneStages.has(le.target);
|
|
794
|
+
if (le.isLoop) {
|
|
795
|
+
let loopExecuted = false;
|
|
796
|
+
if (o?.executionOrder) {
|
|
797
|
+
const lastSourceIdx = o.executionOrder.lastIndexOf(le.source);
|
|
798
|
+
if (lastSourceIdx >= 0) {
|
|
799
|
+
loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(le.target);
|
|
627
800
|
}
|
|
628
|
-
]);
|
|
629
|
-
return true;
|
|
630
|
-
},
|
|
631
|
-
[subflowMap]
|
|
632
|
-
);
|
|
633
|
-
const navigateTo = useCallback2(
|
|
634
|
-
(level) => {
|
|
635
|
-
if (level === 0) {
|
|
636
|
-
setStack([]);
|
|
637
|
-
} else {
|
|
638
|
-
setStack((prev) => prev.slice(0, level));
|
|
639
801
|
}
|
|
802
|
+
edges.push({
|
|
803
|
+
id: le.id,
|
|
804
|
+
source: le.source,
|
|
805
|
+
target: le.target,
|
|
806
|
+
sourceHandle: "loop-source",
|
|
807
|
+
targetHandle: "loop-target",
|
|
808
|
+
label: le.label ?? "loop",
|
|
809
|
+
type: "smoothstep",
|
|
810
|
+
pathOptions: { offset: 40, borderRadius: 16 },
|
|
811
|
+
style: {
|
|
812
|
+
stroke: c.edgeLoop,
|
|
813
|
+
strokeWidth: loopExecuted ? 3 : 2,
|
|
814
|
+
strokeDasharray: "6 3",
|
|
815
|
+
opacity: o && !loopExecuted ? 0.35 : 1
|
|
816
|
+
},
|
|
817
|
+
labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
|
|
818
|
+
animated: loopExecuted,
|
|
819
|
+
zIndex: 2
|
|
820
|
+
});
|
|
821
|
+
} else if (executed) {
|
|
822
|
+
edges.push({
|
|
823
|
+
id: `${le.id}-glow`,
|
|
824
|
+
source: le.source,
|
|
825
|
+
target: le.target,
|
|
826
|
+
style: { stroke: c.pathGlow, strokeWidth: 8, opacity: 0.4 },
|
|
827
|
+
zIndex: 0,
|
|
828
|
+
selectable: false,
|
|
829
|
+
focusable: false
|
|
830
|
+
});
|
|
831
|
+
edges.push({
|
|
832
|
+
id: le.id,
|
|
833
|
+
source: le.source,
|
|
834
|
+
target: le.target,
|
|
835
|
+
label: le.label,
|
|
836
|
+
style: {
|
|
837
|
+
stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
|
|
838
|
+
strokeWidth: 3.5
|
|
839
|
+
},
|
|
840
|
+
labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
|
|
841
|
+
animated: !!isLeadingEdge,
|
|
842
|
+
zIndex: 1
|
|
843
|
+
});
|
|
844
|
+
} else {
|
|
845
|
+
edges.push({
|
|
846
|
+
id: le.id,
|
|
847
|
+
source: le.source,
|
|
848
|
+
target: le.target,
|
|
849
|
+
label: le.label,
|
|
850
|
+
style: {
|
|
851
|
+
stroke: c.edgeDefault,
|
|
852
|
+
strokeWidth: 1.5,
|
|
853
|
+
opacity: o ? 0.3 : 1
|
|
854
|
+
},
|
|
855
|
+
labelStyle: { fontSize: 10, fill: c.labelDefault }
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return { nodes, edges };
|
|
860
|
+
}
|
|
861
|
+
function specToReactFlow(spec, overlay, colors) {
|
|
862
|
+
const layout = specToLayout(spec);
|
|
863
|
+
return applyOverlay(layout, overlay, colors);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
867
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
868
|
+
var defaultNodeTypes = { stage: StageNode };
|
|
869
|
+
function FitViewOnResize() {
|
|
870
|
+
const { fitView } = useReactFlow();
|
|
871
|
+
useEffect3(() => {
|
|
872
|
+
const handler = () => {
|
|
873
|
+
requestAnimationFrame(() => fitView({ padding: 0.3 }));
|
|
874
|
+
};
|
|
875
|
+
window.addEventListener("resize", handler);
|
|
876
|
+
return () => window.removeEventListener("resize", handler);
|
|
877
|
+
}, [fitView]);
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
function TracedFlowchartView({
|
|
881
|
+
spec,
|
|
882
|
+
snapshots,
|
|
883
|
+
snapshotIndex = 0,
|
|
884
|
+
onNodeClick,
|
|
885
|
+
nodeTypes: customNodeTypes,
|
|
886
|
+
unstyled = false,
|
|
887
|
+
className,
|
|
888
|
+
style
|
|
889
|
+
}) {
|
|
890
|
+
const nodeTypes2 = customNodeTypes ?? defaultNodeTypes;
|
|
891
|
+
const overlay = useMemo2(() => {
|
|
892
|
+
if (!snapshots || snapshots.length === 0) return void 0;
|
|
893
|
+
const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
|
|
894
|
+
const doneStages = new Set(
|
|
895
|
+
snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
|
|
896
|
+
);
|
|
897
|
+
const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
|
|
898
|
+
const executedStages = /* @__PURE__ */ new Set([...doneStages]);
|
|
899
|
+
if (activeStage) executedStages.add(activeStage);
|
|
900
|
+
return { doneStages, activeStage, executedStages, executionOrder };
|
|
901
|
+
}, [snapshots, snapshotIndex]);
|
|
902
|
+
const layout = useMemo2(() => {
|
|
903
|
+
if (!spec) return null;
|
|
904
|
+
return specToLayout(spec);
|
|
905
|
+
}, [spec]);
|
|
906
|
+
const { nodes, edges } = useMemo2(() => {
|
|
907
|
+
if (!layout) return { nodes: [], edges: [] };
|
|
908
|
+
return applyOverlay(layout, overlay);
|
|
909
|
+
}, [layout, overlay]);
|
|
910
|
+
const handleNodeClick = useCallback2(
|
|
911
|
+
(_, node) => {
|
|
912
|
+
if (!onNodeClick) return;
|
|
913
|
+
onNodeClick(node.id);
|
|
640
914
|
},
|
|
641
|
-
[]
|
|
915
|
+
[onNodeClick]
|
|
916
|
+
);
|
|
917
|
+
return /* @__PURE__ */ jsx4(
|
|
918
|
+
"div",
|
|
919
|
+
{
|
|
920
|
+
className,
|
|
921
|
+
style: { width: "100%", height: "100%", ...style },
|
|
922
|
+
"data-fp": "traced-flowchart",
|
|
923
|
+
children: /* @__PURE__ */ jsxs2(
|
|
924
|
+
ReactFlow2,
|
|
925
|
+
{
|
|
926
|
+
nodes,
|
|
927
|
+
edges,
|
|
928
|
+
onNodeClick: handleNodeClick,
|
|
929
|
+
nodeTypes: nodeTypes2,
|
|
930
|
+
fitView: true,
|
|
931
|
+
fitViewOptions: { padding: 0.3 },
|
|
932
|
+
proOptions: { hideAttribution: true },
|
|
933
|
+
panOnDrag: false,
|
|
934
|
+
zoomOnScroll: false,
|
|
935
|
+
zoomOnPinch: false,
|
|
936
|
+
zoomOnDoubleClick: false,
|
|
937
|
+
preventScrolling: false,
|
|
938
|
+
nodesDraggable: false,
|
|
939
|
+
nodesConnectable: false,
|
|
940
|
+
elementsSelectable: !!onNodeClick,
|
|
941
|
+
children: [
|
|
942
|
+
/* @__PURE__ */ jsx4(FitViewOnResize, {}),
|
|
943
|
+
!unstyled && /* @__PURE__ */ jsx4(Background2, { variant: BackgroundVariant2.Dots, gap: 16, size: 1 })
|
|
944
|
+
]
|
|
945
|
+
}
|
|
946
|
+
)
|
|
947
|
+
}
|
|
642
948
|
);
|
|
643
|
-
return {
|
|
644
|
-
breadcrumbs,
|
|
645
|
-
nodes,
|
|
646
|
-
edges,
|
|
647
|
-
handleNodeClick,
|
|
648
|
-
navigateTo,
|
|
649
|
-
isInSubflow: stack.length > 0,
|
|
650
|
-
currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
|
|
651
|
-
};
|
|
652
949
|
}
|
|
653
950
|
|
|
654
951
|
// src/components/FlowchartView/SubflowBreadcrumb.tsx
|
|
655
952
|
import { memo as memo2 } from "react";
|
|
656
|
-
import { jsx as
|
|
953
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
657
954
|
var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
658
955
|
breadcrumbs,
|
|
659
956
|
onNavigate
|
|
660
957
|
}) {
|
|
661
958
|
if (breadcrumbs.length <= 1) return null;
|
|
662
|
-
return /* @__PURE__ */
|
|
959
|
+
return /* @__PURE__ */ jsx5(
|
|
663
960
|
"div",
|
|
664
961
|
{
|
|
665
962
|
style: {
|
|
@@ -676,10 +973,10 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
|
676
973
|
},
|
|
677
974
|
children: breadcrumbs.map((crumb, i) => {
|
|
678
975
|
const isLast = i === breadcrumbs.length - 1;
|
|
679
|
-
return /* @__PURE__ */
|
|
680
|
-
i > 0 && /* @__PURE__ */
|
|
681
|
-
isLast ? /* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
976
|
+
return /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
977
|
+
i > 0 && /* @__PURE__ */ jsx5("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
|
|
978
|
+
isLast ? /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
979
|
+
/* @__PURE__ */ jsx5(
|
|
683
980
|
"span",
|
|
684
981
|
{
|
|
685
982
|
style: {
|
|
@@ -689,7 +986,7 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
|
689
986
|
children: crumb.label
|
|
690
987
|
}
|
|
691
988
|
),
|
|
692
|
-
crumb.description && /* @__PURE__ */
|
|
989
|
+
crumb.description && /* @__PURE__ */ jsxs3(
|
|
693
990
|
"span",
|
|
694
991
|
{
|
|
695
992
|
style: {
|
|
@@ -703,7 +1000,7 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
|
703
1000
|
]
|
|
704
1001
|
}
|
|
705
1002
|
)
|
|
706
|
-
] }) : /* @__PURE__ */
|
|
1003
|
+
] }) : /* @__PURE__ */ jsx5(
|
|
707
1004
|
"button",
|
|
708
1005
|
{
|
|
709
1006
|
onClick: () => onNavigate(i),
|
|
@@ -734,13 +1031,83 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
|
734
1031
|
);
|
|
735
1032
|
});
|
|
736
1033
|
|
|
1034
|
+
// src/components/FlowchartView/useSubflowNavigation.ts
|
|
1035
|
+
import { useState as useState2, useCallback as useCallback3, useMemo as useMemo3 } from "react";
|
|
1036
|
+
function useSubflowNavigation(rootSpec, overlay, colors) {
|
|
1037
|
+
const [stack, setStack] = useState2([]);
|
|
1038
|
+
const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
|
|
1039
|
+
const { nodes, edges } = useMemo3(() => {
|
|
1040
|
+
if (!currentSpec) return { nodes: [], edges: [] };
|
|
1041
|
+
return specToReactFlow(currentSpec, overlay, colors);
|
|
1042
|
+
}, [currentSpec, overlay, colors]);
|
|
1043
|
+
const subflowMap = useMemo3(() => {
|
|
1044
|
+
const map = /* @__PURE__ */ new Map();
|
|
1045
|
+
if (!currentSpec) return map;
|
|
1046
|
+
function collectSubflows(node) {
|
|
1047
|
+
if (node.isSubflowRoot && node.subflowStructure) {
|
|
1048
|
+
const id = node.name || node.id || "";
|
|
1049
|
+
map.set(id, node);
|
|
1050
|
+
}
|
|
1051
|
+
if (node.children) node.children.forEach(collectSubflows);
|
|
1052
|
+
if (node.next) collectSubflows(node.next);
|
|
1053
|
+
}
|
|
1054
|
+
collectSubflows(currentSpec);
|
|
1055
|
+
return map;
|
|
1056
|
+
}, [currentSpec]);
|
|
1057
|
+
const breadcrumbs = useMemo3(() => {
|
|
1058
|
+
const root = {
|
|
1059
|
+
label: rootSpec?.name || "Flowchart",
|
|
1060
|
+
spec: rootSpec,
|
|
1061
|
+
description: rootSpec?.description
|
|
1062
|
+
};
|
|
1063
|
+
return [root, ...stack];
|
|
1064
|
+
}, [rootSpec, stack]);
|
|
1065
|
+
const handleNodeClick = useCallback3(
|
|
1066
|
+
(nodeId) => {
|
|
1067
|
+
const subflowNode = subflowMap.get(nodeId);
|
|
1068
|
+
if (!subflowNode?.subflowStructure) return false;
|
|
1069
|
+
setStack((prev) => [
|
|
1070
|
+
...prev,
|
|
1071
|
+
{
|
|
1072
|
+
label: subflowNode.subflowName || subflowNode.name,
|
|
1073
|
+
spec: subflowNode.subflowStructure,
|
|
1074
|
+
description: subflowNode.description
|
|
1075
|
+
}
|
|
1076
|
+
]);
|
|
1077
|
+
return true;
|
|
1078
|
+
},
|
|
1079
|
+
[subflowMap]
|
|
1080
|
+
);
|
|
1081
|
+
const navigateTo = useCallback3(
|
|
1082
|
+
(level) => {
|
|
1083
|
+
if (level === 0) {
|
|
1084
|
+
setStack([]);
|
|
1085
|
+
} else {
|
|
1086
|
+
setStack((prev) => prev.slice(0, level));
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
[]
|
|
1090
|
+
);
|
|
1091
|
+
return {
|
|
1092
|
+
breadcrumbs,
|
|
1093
|
+
nodes,
|
|
1094
|
+
edges,
|
|
1095
|
+
handleNodeClick,
|
|
1096
|
+
navigateTo,
|
|
1097
|
+
isInSubflow: stack.length > 0,
|
|
1098
|
+
currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
737
1102
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
738
|
-
import { memo as memo3, useState as useState3, useCallback as
|
|
739
|
-
import { Fragment as Fragment2, jsx as
|
|
1103
|
+
import { memo as memo3, useState as useState3, useCallback as useCallback4, useMemo as useMemo4 } from "react";
|
|
1104
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
740
1105
|
function specToTree(node) {
|
|
1106
|
+
if (!node) return [];
|
|
741
1107
|
const entries = [];
|
|
742
1108
|
const seen = /* @__PURE__ */ new Set();
|
|
743
|
-
function
|
|
1109
|
+
function walk(n) {
|
|
1110
|
+
if (!n) return;
|
|
744
1111
|
const id = n.name || n.id || "";
|
|
745
1112
|
if (seen.has(id)) return;
|
|
746
1113
|
seen.add(id);
|
|
@@ -756,14 +1123,14 @@ function specToTree(node) {
|
|
|
756
1123
|
entries.push(entry);
|
|
757
1124
|
if (n.children) {
|
|
758
1125
|
for (const child of n.children) {
|
|
759
|
-
|
|
1126
|
+
if (child) walk(child);
|
|
760
1127
|
}
|
|
761
1128
|
}
|
|
762
1129
|
if (n.next) {
|
|
763
|
-
|
|
1130
|
+
walk(n.next);
|
|
764
1131
|
}
|
|
765
1132
|
}
|
|
766
|
-
|
|
1133
|
+
walk(node);
|
|
767
1134
|
return entries;
|
|
768
1135
|
}
|
|
769
1136
|
var TreeNode = memo3(function TreeNode2({
|
|
@@ -777,14 +1144,14 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
777
1144
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
778
1145
|
const isActive = activeStage === entry.name;
|
|
779
1146
|
const isDone = doneStages?.has(entry.name);
|
|
780
|
-
const handleClick =
|
|
1147
|
+
const handleClick = useCallback4(() => {
|
|
781
1148
|
if (hasChildren) {
|
|
782
1149
|
setExpanded((prev) => !prev);
|
|
783
1150
|
}
|
|
784
1151
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
785
1152
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
786
|
-
return /* @__PURE__ */
|
|
787
|
-
/* @__PURE__ */
|
|
1153
|
+
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1154
|
+
/* @__PURE__ */ jsxs4(
|
|
788
1155
|
"button",
|
|
789
1156
|
{
|
|
790
1157
|
onClick: handleClick,
|
|
@@ -815,7 +1182,7 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
815
1182
|
}
|
|
816
1183
|
},
|
|
817
1184
|
children: [
|
|
818
|
-
hasChildren ? /* @__PURE__ */
|
|
1185
|
+
hasChildren ? /* @__PURE__ */ jsx6(
|
|
819
1186
|
"span",
|
|
820
1187
|
{
|
|
821
1188
|
style: {
|
|
@@ -830,8 +1197,8 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
830
1197
|
},
|
|
831
1198
|
children: "\u25B6"
|
|
832
1199
|
}
|
|
833
|
-
) : /* @__PURE__ */
|
|
834
|
-
/* @__PURE__ */
|
|
1200
|
+
) : /* @__PURE__ */ jsx6("span", { style: { width: 12, flexShrink: 0 } }),
|
|
1201
|
+
/* @__PURE__ */ jsx6(
|
|
835
1202
|
"span",
|
|
836
1203
|
{
|
|
837
1204
|
style: {
|
|
@@ -843,8 +1210,8 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
843
1210
|
}
|
|
844
1211
|
}
|
|
845
1212
|
),
|
|
846
|
-
/* @__PURE__ */
|
|
847
|
-
/* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ jsxs4("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
1214
|
+
/* @__PURE__ */ jsxs4(
|
|
848
1215
|
"span",
|
|
849
1216
|
{
|
|
850
1217
|
style: {
|
|
@@ -856,11 +1223,11 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
856
1223
|
},
|
|
857
1224
|
children: [
|
|
858
1225
|
entry.name,
|
|
859
|
-
entry.isSubflow && /* @__PURE__ */
|
|
1226
|
+
entry.isSubflow && /* @__PURE__ */ jsx6("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
860
1227
|
]
|
|
861
1228
|
}
|
|
862
1229
|
),
|
|
863
|
-
entry.description && /* @__PURE__ */
|
|
1230
|
+
entry.description && /* @__PURE__ */ jsx6(
|
|
864
1231
|
"span",
|
|
865
1232
|
{
|
|
866
1233
|
style: {
|
|
@@ -877,7 +1244,7 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
877
1244
|
]
|
|
878
1245
|
}
|
|
879
1246
|
),
|
|
880
|
-
hasChildren && expanded && /* @__PURE__ */
|
|
1247
|
+
hasChildren && expanded && /* @__PURE__ */ jsx6("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx6(
|
|
881
1248
|
TreeNode2,
|
|
882
1249
|
{
|
|
883
1250
|
entry: child,
|
|
@@ -886,12 +1253,12 @@ var TreeNode = memo3(function TreeNode2({
|
|
|
886
1253
|
doneStages,
|
|
887
1254
|
onNodeSelect
|
|
888
1255
|
},
|
|
889
|
-
`${child.name}-${i}`
|
|
1256
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
890
1257
|
)) })
|
|
891
1258
|
] });
|
|
892
1259
|
});
|
|
893
1260
|
var SectionLabel = memo3(function SectionLabel2({ children }) {
|
|
894
|
-
return /* @__PURE__ */
|
|
1261
|
+
return /* @__PURE__ */ jsx6(
|
|
895
1262
|
"div",
|
|
896
1263
|
{
|
|
897
1264
|
style: {
|
|
@@ -915,10 +1282,10 @@ var SubflowTree = memo3(function SubflowTree2({
|
|
|
915
1282
|
className,
|
|
916
1283
|
style
|
|
917
1284
|
}) {
|
|
918
|
-
const tree =
|
|
919
|
-
const subflowStages =
|
|
1285
|
+
const tree = useMemo4(() => specToTree(spec), [spec]);
|
|
1286
|
+
const subflowStages = useMemo4(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
920
1287
|
if (subflowStages.length === 0) return null;
|
|
921
|
-
return /* @__PURE__ */
|
|
1288
|
+
return /* @__PURE__ */ jsxs4(
|
|
922
1289
|
"div",
|
|
923
1290
|
{
|
|
924
1291
|
className,
|
|
@@ -936,8 +1303,8 @@ var SubflowTree = memo3(function SubflowTree2({
|
|
|
936
1303
|
...style
|
|
937
1304
|
},
|
|
938
1305
|
children: [
|
|
939
|
-
!unstyled && /* @__PURE__ */
|
|
940
|
-
subflowStages.map((entry, i) => /* @__PURE__ */
|
|
1306
|
+
!unstyled && /* @__PURE__ */ jsx6(SectionLabel, { children: "Subflows" }),
|
|
1307
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ jsx6(
|
|
941
1308
|
TreeNode,
|
|
942
1309
|
{
|
|
943
1310
|
entry,
|
|
@@ -946,180 +1313,18 @@ var SubflowTree = memo3(function SubflowTree2({
|
|
|
946
1313
|
doneStages,
|
|
947
1314
|
onNodeSelect
|
|
948
1315
|
},
|
|
949
|
-
`${entry.name}-${i}`
|
|
1316
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
950
1317
|
))
|
|
951
1318
|
]
|
|
952
1319
|
}
|
|
953
1320
|
);
|
|
954
1321
|
});
|
|
955
1322
|
|
|
956
|
-
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
957
|
-
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
958
|
-
var nodeTypes2 = { stage: StageNode };
|
|
959
|
-
function TracedFlowchartView({
|
|
960
|
-
spec,
|
|
961
|
-
snapshots,
|
|
962
|
-
snapshotIndex = 0,
|
|
963
|
-
onNodeClick,
|
|
964
|
-
onSubflowChange,
|
|
965
|
-
showTree = true,
|
|
966
|
-
treeWidth = 200,
|
|
967
|
-
unstyled = false,
|
|
968
|
-
className,
|
|
969
|
-
style
|
|
970
|
-
}) {
|
|
971
|
-
const [treeVisible, setTreeVisible] = useState4(showTree);
|
|
972
|
-
const subflowNav = useSubflowNavigation(spec);
|
|
973
|
-
const currentSpec = subflowNav.breadcrumbs.length > 0 ? subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec : null;
|
|
974
|
-
const overlay = useMemo4(() => {
|
|
975
|
-
if (!snapshots || snapshots.length === 0) return void 0;
|
|
976
|
-
const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
|
|
977
|
-
const doneStages = new Set(
|
|
978
|
-
snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
|
|
979
|
-
);
|
|
980
|
-
const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
|
|
981
|
-
const executedStages = /* @__PURE__ */ new Set([...doneStages]);
|
|
982
|
-
if (activeStage) executedStages.add(activeStage);
|
|
983
|
-
return { doneStages, activeStage, executedStages, executionOrder };
|
|
984
|
-
}, [snapshots, snapshotIndex]);
|
|
985
|
-
const { nodes, edges } = useMemo4(() => {
|
|
986
|
-
if (!currentSpec) return { nodes: [], edges: [] };
|
|
987
|
-
return specToReactFlow(currentSpec, overlay);
|
|
988
|
-
}, [currentSpec, overlay]);
|
|
989
|
-
const handleNodeClick = useCallback4(
|
|
990
|
-
(_, node) => {
|
|
991
|
-
if (subflowNav.handleNodeClick(node.id)) {
|
|
992
|
-
onSubflowChange?.(true, node.id);
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
if (onNodeClick && snapshots) {
|
|
996
|
-
const idx = snapshots.findIndex((s) => s.stageLabel === node.id);
|
|
997
|
-
if (idx >= 0) onNodeClick(idx);
|
|
998
|
-
} else if (onNodeClick) {
|
|
999
|
-
onNodeClick(node.id);
|
|
1000
|
-
}
|
|
1001
|
-
},
|
|
1002
|
-
[subflowNav, onNodeClick, onSubflowChange, snapshots]
|
|
1003
|
-
);
|
|
1004
|
-
const handleBreadcrumbNavigate = useCallback4(
|
|
1005
|
-
(level) => {
|
|
1006
|
-
subflowNav.navigateTo(level);
|
|
1007
|
-
onSubflowChange?.(level > 0, null);
|
|
1008
|
-
},
|
|
1009
|
-
[subflowNav, onSubflowChange]
|
|
1010
|
-
);
|
|
1011
|
-
const handleTreeNodeSelect = useCallback4(
|
|
1012
|
-
(name, isSubflow) => {
|
|
1013
|
-
if (isSubflow) {
|
|
1014
|
-
if (subflowNav.handleNodeClick(name)) {
|
|
1015
|
-
onSubflowChange?.(true, name);
|
|
1016
|
-
}
|
|
1017
|
-
} else if (onNodeClick && snapshots) {
|
|
1018
|
-
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
1019
|
-
if (idx >= 0) onNodeClick(idx);
|
|
1020
|
-
}
|
|
1021
|
-
},
|
|
1022
|
-
[subflowNav, onNodeClick, onSubflowChange, snapshots]
|
|
1023
|
-
);
|
|
1024
|
-
return /* @__PURE__ */ jsxs4(
|
|
1025
|
-
"div",
|
|
1026
|
-
{
|
|
1027
|
-
className,
|
|
1028
|
-
style: { width: "100%", height: "100%", display: "flex", flexDirection: "row", ...style },
|
|
1029
|
-
"data-fp": "traced-flowchart",
|
|
1030
|
-
children: [
|
|
1031
|
-
showTree && treeVisible && /* @__PURE__ */ jsx6(
|
|
1032
|
-
SubflowTree,
|
|
1033
|
-
{
|
|
1034
|
-
spec,
|
|
1035
|
-
activeStage: overlay?.activeStage,
|
|
1036
|
-
doneStages: overlay?.doneStages,
|
|
1037
|
-
onNodeSelect: handleTreeNodeSelect,
|
|
1038
|
-
unstyled,
|
|
1039
|
-
style: { width: treeWidth, flexShrink: 0, height: "100%" }
|
|
1040
|
-
}
|
|
1041
|
-
),
|
|
1042
|
-
/* @__PURE__ */ jsxs4("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0, height: "100%" }, children: [
|
|
1043
|
-
(subflowNav.isInSubflow || showTree && !treeVisible) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
|
|
1044
|
-
showTree && !treeVisible && /* @__PURE__ */ jsx6(
|
|
1045
|
-
"button",
|
|
1046
|
-
{
|
|
1047
|
-
onClick: () => setTreeVisible(true),
|
|
1048
|
-
"data-fp": "tree-toggle",
|
|
1049
|
-
style: unstyled ? {} : {
|
|
1050
|
-
background: "transparent",
|
|
1051
|
-
border: "none",
|
|
1052
|
-
cursor: "pointer",
|
|
1053
|
-
padding: "6px 8px",
|
|
1054
|
-
fontSize: 10,
|
|
1055
|
-
flexShrink: 0
|
|
1056
|
-
},
|
|
1057
|
-
children: "\u25B6"
|
|
1058
|
-
}
|
|
1059
|
-
),
|
|
1060
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(
|
|
1061
|
-
SubflowBreadcrumb,
|
|
1062
|
-
{
|
|
1063
|
-
breadcrumbs: subflowNav.breadcrumbs,
|
|
1064
|
-
onNavigate: handleBreadcrumbNavigate
|
|
1065
|
-
}
|
|
1066
|
-
) })
|
|
1067
|
-
] }),
|
|
1068
|
-
showTree && treeVisible && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
|
|
1069
|
-
/* @__PURE__ */ jsx6(
|
|
1070
|
-
"button",
|
|
1071
|
-
{
|
|
1072
|
-
onClick: () => setTreeVisible(false),
|
|
1073
|
-
"data-fp": "tree-toggle",
|
|
1074
|
-
style: unstyled ? {} : {
|
|
1075
|
-
background: "transparent",
|
|
1076
|
-
border: "none",
|
|
1077
|
-
cursor: "pointer",
|
|
1078
|
-
padding: "6px 8px",
|
|
1079
|
-
fontSize: 10,
|
|
1080
|
-
flexShrink: 0
|
|
1081
|
-
},
|
|
1082
|
-
children: "\u25C0"
|
|
1083
|
-
}
|
|
1084
|
-
),
|
|
1085
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: subflowNav.isInSubflow && /* @__PURE__ */ jsx6(
|
|
1086
|
-
SubflowBreadcrumb,
|
|
1087
|
-
{
|
|
1088
|
-
breadcrumbs: subflowNav.breadcrumbs,
|
|
1089
|
-
onNavigate: handleBreadcrumbNavigate
|
|
1090
|
-
}
|
|
1091
|
-
) })
|
|
1092
|
-
] }),
|
|
1093
|
-
/* @__PURE__ */ jsx6("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx6(
|
|
1094
|
-
ReactFlow2,
|
|
1095
|
-
{
|
|
1096
|
-
nodes,
|
|
1097
|
-
edges,
|
|
1098
|
-
onNodeClick: handleNodeClick,
|
|
1099
|
-
nodeTypes: nodeTypes2,
|
|
1100
|
-
fitView: true,
|
|
1101
|
-
panOnDrag: false,
|
|
1102
|
-
zoomOnScroll: false,
|
|
1103
|
-
zoomOnPinch: false,
|
|
1104
|
-
zoomOnDoubleClick: false,
|
|
1105
|
-
preventScrolling: false,
|
|
1106
|
-
nodesDraggable: false,
|
|
1107
|
-
nodesConnectable: false,
|
|
1108
|
-
elementsSelectable: !!onNodeClick,
|
|
1109
|
-
children: !unstyled && /* @__PURE__ */ jsx6(Background2, { variant: BackgroundVariant2.Dots, gap: 16, size: 1 })
|
|
1110
|
-
}
|
|
1111
|
-
) })
|
|
1112
|
-
] })
|
|
1113
|
-
]
|
|
1114
|
-
}
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1323
|
// src/components/TimeTravelDebugger/TimeTravelDebugger.tsx
|
|
1119
|
-
import { useState as
|
|
1324
|
+
import { useState as useState5 } from "react";
|
|
1120
1325
|
|
|
1121
1326
|
// src/components/MemoryInspector/MemoryInspector.tsx
|
|
1122
|
-
import { useMemo as useMemo5 } from "react";
|
|
1327
|
+
import { useMemo as useMemo5, useRef as useRef2 } from "react";
|
|
1123
1328
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1124
1329
|
function MemoryInspector({
|
|
1125
1330
|
data,
|
|
@@ -1132,6 +1337,7 @@ function MemoryInspector({
|
|
|
1132
1337
|
className,
|
|
1133
1338
|
style
|
|
1134
1339
|
}) {
|
|
1340
|
+
const cacheRef = useRef2(null);
|
|
1135
1341
|
const { memory, newKeys } = useMemo5(() => {
|
|
1136
1342
|
if (data) {
|
|
1137
1343
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
@@ -1139,21 +1345,37 @@ function MemoryInspector({
|
|
|
1139
1345
|
if (!snapshots || snapshots.length === 0) {
|
|
1140
1346
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
1141
1347
|
}
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1348
|
+
const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
|
|
1349
|
+
let merged;
|
|
1350
|
+
const cache = cacheRef.current;
|
|
1351
|
+
if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
|
|
1352
|
+
merged = { ...cache.accumulated };
|
|
1353
|
+
for (let i = cache.index + 1; i <= safeIdx; i++) {
|
|
1354
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
1355
|
+
}
|
|
1356
|
+
} else {
|
|
1357
|
+
merged = {};
|
|
1358
|
+
for (let i = 0; i <= safeIdx; i++) {
|
|
1359
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
1360
|
+
}
|
|
1145
1361
|
}
|
|
1362
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
1146
1363
|
const nk = /* @__PURE__ */ new Set();
|
|
1147
|
-
if (highlightNew &&
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1364
|
+
if (highlightNew && safeIdx > 0) {
|
|
1365
|
+
let prev;
|
|
1366
|
+
if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
|
|
1367
|
+
prev = cache.accumulated;
|
|
1368
|
+
} else {
|
|
1369
|
+
prev = {};
|
|
1370
|
+
for (let i = 0; i < safeIdx; i++) {
|
|
1371
|
+
Object.assign(prev, snapshots[i]?.memory);
|
|
1372
|
+
}
|
|
1151
1373
|
}
|
|
1152
|
-
const current = snapshots[
|
|
1374
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
1153
1375
|
for (const k of Object.keys(current)) {
|
|
1154
1376
|
if (!(k in prev)) nk.add(k);
|
|
1155
1377
|
}
|
|
1156
|
-
} else if (highlightNew &&
|
|
1378
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
1157
1379
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
1158
1380
|
}
|
|
1159
1381
|
return { memory: merged, newKeys: nk };
|
|
@@ -1162,9 +1384,9 @@ function MemoryInspector({
|
|
|
1162
1384
|
const fs = fontSize[size];
|
|
1163
1385
|
const pad = padding[size];
|
|
1164
1386
|
if (unstyled) {
|
|
1165
|
-
return /* @__PURE__ */ jsxs5("div", { className, style, "data-fp": "memory-inspector", children: [
|
|
1387
|
+
return /* @__PURE__ */ jsxs5("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
|
|
1166
1388
|
/* @__PURE__ */ jsx7("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
1167
|
-
/* @__PURE__ */ jsx7("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
|
|
1389
|
+
/* @__PURE__ */ jsx7("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ jsx7("code", { children: JSON.stringify(memory, null, 2) }) })
|
|
1168
1390
|
] });
|
|
1169
1391
|
}
|
|
1170
1392
|
return /* @__PURE__ */ jsxs5(
|
|
@@ -1177,6 +1399,8 @@ function MemoryInspector({
|
|
|
1177
1399
|
...style
|
|
1178
1400
|
},
|
|
1179
1401
|
"data-fp": "memory-inspector",
|
|
1402
|
+
role: "region",
|
|
1403
|
+
"aria-label": "Memory state",
|
|
1180
1404
|
children: [
|
|
1181
1405
|
/* @__PURE__ */ jsx7(
|
|
1182
1406
|
"span",
|
|
@@ -1409,7 +1633,7 @@ function NarrativeLog({
|
|
|
1409
1633
|
}
|
|
1410
1634
|
|
|
1411
1635
|
// src/components/GanttTimeline/GanttTimeline.tsx
|
|
1412
|
-
import { useState as
|
|
1636
|
+
import { useState as useState4, useMemo as useMemo7, useRef as useRef3, useEffect as useEffect4 } from "react";
|
|
1413
1637
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1414
1638
|
function GanttTimeline({
|
|
1415
1639
|
snapshots,
|
|
@@ -1421,9 +1645,9 @@ function GanttTimeline({
|
|
|
1421
1645
|
style,
|
|
1422
1646
|
maxVisibleRows = 5
|
|
1423
1647
|
}) {
|
|
1424
|
-
const [expanded, setExpanded] =
|
|
1425
|
-
const activeRowRef =
|
|
1426
|
-
const scrollContainerRef =
|
|
1648
|
+
const [expanded, setExpanded] = useState4(false);
|
|
1649
|
+
const activeRowRef = useRef3(null);
|
|
1650
|
+
const scrollContainerRef = useRef3(null);
|
|
1427
1651
|
const totalWallTime = useMemo7(
|
|
1428
1652
|
() => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
|
|
1429
1653
|
[snapshots]
|
|
@@ -1435,7 +1659,7 @@ function GanttTimeline({
|
|
|
1435
1659
|
const rowHeight = size === "compact" ? 18 : 22;
|
|
1436
1660
|
const collapsible = maxVisibleRows > 0 && snapshots.length > maxVisibleRows;
|
|
1437
1661
|
const showAll = expanded || !collapsible;
|
|
1438
|
-
|
|
1662
|
+
useEffect4(() => {
|
|
1439
1663
|
if (!showAll && activeRowRef.current && scrollContainerRef.current) {
|
|
1440
1664
|
activeRowRef.current.scrollIntoView({
|
|
1441
1665
|
block: "nearest",
|
|
@@ -1444,12 +1668,15 @@ function GanttTimeline({
|
|
|
1444
1668
|
}
|
|
1445
1669
|
}, [selectedIndex, showAll]);
|
|
1446
1670
|
if (unstyled) {
|
|
1447
|
-
return /* @__PURE__ */ jsx9("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs7(
|
|
1671
|
+
return /* @__PURE__ */ jsx9("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs7(
|
|
1448
1672
|
"div",
|
|
1449
1673
|
{
|
|
1450
1674
|
"data-fp": "gantt-bar",
|
|
1451
1675
|
"data-selected": idx === selectedIndex,
|
|
1452
1676
|
"data-visible": idx <= selectedIndex,
|
|
1677
|
+
role: "option",
|
|
1678
|
+
"aria-selected": idx === selectedIndex,
|
|
1679
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
1453
1680
|
onClick: () => onSelect?.(idx),
|
|
1454
1681
|
children: [
|
|
1455
1682
|
/* @__PURE__ */ jsx9("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -1459,7 +1686,7 @@ function GanttTimeline({
|
|
|
1459
1686
|
] })
|
|
1460
1687
|
]
|
|
1461
1688
|
},
|
|
1462
|
-
snap.stageName
|
|
1689
|
+
`${snap.stageName}-${idx}`
|
|
1463
1690
|
)) });
|
|
1464
1691
|
}
|
|
1465
1692
|
return /* @__PURE__ */ jsxs7(
|
|
@@ -1515,6 +1742,8 @@ function GanttTimeline({
|
|
|
1515
1742
|
"div",
|
|
1516
1743
|
{
|
|
1517
1744
|
ref: scrollContainerRef,
|
|
1745
|
+
role: "listbox",
|
|
1746
|
+
"aria-label": "Execution timeline",
|
|
1518
1747
|
style: {
|
|
1519
1748
|
marginTop: 8,
|
|
1520
1749
|
display: "flex",
|
|
@@ -1535,6 +1764,9 @@ function GanttTimeline({
|
|
|
1535
1764
|
"div",
|
|
1536
1765
|
{
|
|
1537
1766
|
ref: isSelected ? activeRowRef : void 0,
|
|
1767
|
+
role: "option",
|
|
1768
|
+
"aria-selected": isSelected,
|
|
1769
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
1538
1770
|
onClick: () => onSelect?.(idx),
|
|
1539
1771
|
style: {
|
|
1540
1772
|
display: "flex",
|
|
@@ -1550,6 +1782,7 @@ function GanttTimeline({
|
|
|
1550
1782
|
/* @__PURE__ */ jsx9(
|
|
1551
1783
|
"span",
|
|
1552
1784
|
{
|
|
1785
|
+
title: snap.stageLabel,
|
|
1553
1786
|
style: {
|
|
1554
1787
|
width: labelWidth,
|
|
1555
1788
|
fontSize: fs.small,
|
|
@@ -1609,7 +1842,7 @@ function GanttTimeline({
|
|
|
1609
1842
|
)
|
|
1610
1843
|
]
|
|
1611
1844
|
},
|
|
1612
|
-
snap.stageName
|
|
1845
|
+
`${snap.stageName}-${idx}`
|
|
1613
1846
|
);
|
|
1614
1847
|
})
|
|
1615
1848
|
}
|
|
@@ -1659,7 +1892,7 @@ function TimeTravelDebugger({
|
|
|
1659
1892
|
className,
|
|
1660
1893
|
style
|
|
1661
1894
|
}) {
|
|
1662
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1895
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
1663
1896
|
const fs = fontSize[size];
|
|
1664
1897
|
const pad = padding[size];
|
|
1665
1898
|
if (snapshots.length === 0) {
|
|
@@ -1963,6 +2196,8 @@ export {
|
|
|
1963
2196
|
SubflowTree,
|
|
1964
2197
|
TimeTravelDebugger,
|
|
1965
2198
|
TracedFlowchartView,
|
|
2199
|
+
applyOverlay,
|
|
2200
|
+
specToLayout,
|
|
1966
2201
|
specToReactFlow,
|
|
1967
2202
|
useSubflowNavigation
|
|
1968
2203
|
};
|