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.cjs
CHANGED
|
@@ -26,6 +26,8 @@ __export(flowchart_exports, {
|
|
|
26
26
|
SubflowTree: () => SubflowTree,
|
|
27
27
|
TimeTravelDebugger: () => TimeTravelDebugger,
|
|
28
28
|
TracedFlowchartView: () => TracedFlowchartView,
|
|
29
|
+
applyOverlay: () => applyOverlay,
|
|
30
|
+
specToLayout: () => specToLayout,
|
|
29
31
|
specToReactFlow: () => specToReactFlow,
|
|
30
32
|
useSubflowNavigation: () => useSubflowNavigation
|
|
31
33
|
});
|
|
@@ -43,7 +45,7 @@ var import_react4 = require("@xyflow/react");
|
|
|
43
45
|
var import_react = require("react");
|
|
44
46
|
|
|
45
47
|
// src/theme/tokens.ts
|
|
46
|
-
var
|
|
48
|
+
var rawDefaults = {
|
|
47
49
|
colors: {
|
|
48
50
|
primary: "#6366f1",
|
|
49
51
|
success: "#22c55e",
|
|
@@ -63,6 +65,26 @@ var defaultTokens = {
|
|
|
63
65
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
64
66
|
}
|
|
65
67
|
};
|
|
68
|
+
var defaultTokens = {
|
|
69
|
+
colors: {
|
|
70
|
+
primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
|
|
71
|
+
success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
|
|
72
|
+
error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
|
|
73
|
+
warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
|
|
74
|
+
bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
|
|
75
|
+
bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
|
|
76
|
+
bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
|
|
77
|
+
textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
|
|
78
|
+
textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
|
|
79
|
+
textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
|
|
80
|
+
border: `var(--fp-border, ${rawDefaults.colors.border})`
|
|
81
|
+
},
|
|
82
|
+
radius: `var(--fp-radius, ${rawDefaults.radius})`,
|
|
83
|
+
fontFamily: {
|
|
84
|
+
sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
|
|
85
|
+
mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
|
|
86
|
+
}
|
|
87
|
+
};
|
|
66
88
|
|
|
67
89
|
// src/theme/ThemeProvider.tsx
|
|
68
90
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -104,10 +126,169 @@ var import_react2 = require("react");
|
|
|
104
126
|
|
|
105
127
|
// src/components/StageNode/StageNode.tsx
|
|
106
128
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
129
|
+
var KEYFRAMES_ID = "fp-stage-node-keyframes";
|
|
130
|
+
var KEYFRAMES_CSS = `
|
|
131
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
132
|
+
@keyframes fp-pulse {
|
|
133
|
+
0%, 100% { opacity: 0.4; transform: scale(1); }
|
|
134
|
+
50% { opacity: 0.15; transform: scale(1.06); }
|
|
135
|
+
}
|
|
136
|
+
@keyframes fp-blink {
|
|
137
|
+
0%, 100% { opacity: 1; }
|
|
138
|
+
50% { opacity: 0.3; }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
@media (prefers-reduced-motion: reduce) {
|
|
142
|
+
@keyframes fp-pulse { 0%, 100% { opacity: 0.3; } }
|
|
143
|
+
@keyframes fp-blink { 0%, 100% { opacity: 1; } }
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
var ICON_SIZE = 16;
|
|
147
|
+
function StageIcon({ type, color }) {
|
|
148
|
+
const s = ICON_SIZE;
|
|
149
|
+
const props = { width: s, height: s, viewBox: `0 0 ${s} ${s}`, fill: "none", style: { flexShrink: 0 } };
|
|
150
|
+
switch (type) {
|
|
151
|
+
// LLM / AI call — brain/sparkle
|
|
152
|
+
case "llm":
|
|
153
|
+
case "ai":
|
|
154
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: color, strokeWidth: "1.5" }),
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "9.5", r: "1", fill: color }),
|
|
158
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "3.5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12.5", y1: "4", x2: "11.2", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
160
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3.5", y1: "4", x2: "4.8", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
|
|
161
|
+
] });
|
|
162
|
+
// Tool / function call — gear
|
|
163
|
+
case "tool":
|
|
164
|
+
case "function":
|
|
165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
166
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "3", stroke: color, strokeWidth: "1.5" }),
|
|
167
|
+
[0, 45, 90, 135, 180, 225, 270, 315].map((angle) => {
|
|
168
|
+
const rad = angle * Math.PI / 180;
|
|
169
|
+
const x1 = 8 + Math.cos(rad) * 4.5;
|
|
170
|
+
const y1 = 8 + Math.sin(rad) * 4.5;
|
|
171
|
+
const x2 = 8 + Math.cos(rad) * 6;
|
|
172
|
+
const y2 = 8 + Math.sin(rad) * 6;
|
|
173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1, y1, x2, y2, stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }, angle);
|
|
174
|
+
})
|
|
175
|
+
] });
|
|
176
|
+
// RAG / retrieval — magnifying glass + doc
|
|
177
|
+
case "rag":
|
|
178
|
+
case "search":
|
|
179
|
+
case "retrieval":
|
|
180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
181
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "7", cy: "7", r: "4", stroke: color, strokeWidth: "1.5" }),
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "10", x2: "13.5", y2: "13.5", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5.5", y1: "6", x2: "8.5", y2: "6", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5.5", y1: "8", x2: "7.5", y2: "8", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
|
|
185
|
+
] });
|
|
186
|
+
// Parse / process — diamond with arrows
|
|
187
|
+
case "parse":
|
|
188
|
+
case "process":
|
|
189
|
+
case "transform":
|
|
190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: color, strokeWidth: "1.5", transform: "rotate(45 8 8)" }) });
|
|
191
|
+
// Start / seed — play triangle
|
|
192
|
+
case "start":
|
|
193
|
+
case "seed":
|
|
194
|
+
case "init":
|
|
195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 3.5L12.5 8L5 12.5V3.5Z", fill: color, opacity: "0.8" }) });
|
|
196
|
+
// End / finalize — stop square
|
|
197
|
+
case "end":
|
|
198
|
+
case "finalize":
|
|
199
|
+
case "output":
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: color, opacity: "0.8" }) });
|
|
201
|
+
// Agent — person silhouette
|
|
202
|
+
case "agent":
|
|
203
|
+
case "orchestrator":
|
|
204
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "5", r: "2.5", stroke: color, strokeWidth: "1.5" }),
|
|
206
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 14C3.5 11 5.5 9 8 9S12.5 11 12.5 14", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
207
|
+
] });
|
|
208
|
+
// Swarm — multi-agent
|
|
209
|
+
case "swarm":
|
|
210
|
+
case "multi-agent":
|
|
211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
214
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "11", r: "2", stroke: color, strokeWidth: "1.2" }),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" }),
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "11", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" })
|
|
217
|
+
] });
|
|
218
|
+
// Guard / guardrail — shield
|
|
219
|
+
case "guard":
|
|
220
|
+
case "guardrail":
|
|
221
|
+
case "validate":
|
|
222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
|
|
224
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 8L7.5 9.5L10 6.5", stroke: color, strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
225
|
+
] });
|
|
226
|
+
// Stream — wave
|
|
227
|
+
case "stream":
|
|
228
|
+
case "streaming":
|
|
229
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
230
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 8C4 5 6 11 8 8S12 5 14 8", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 11C4 8 6 14 8 11S12 8 14 11", stroke: color, strokeWidth: "1", strokeLinecap: "round", fill: "none", opacity: "0.5" })
|
|
232
|
+
] });
|
|
233
|
+
// Memory / state — database cylinder
|
|
234
|
+
case "memory":
|
|
235
|
+
case "state":
|
|
236
|
+
case "db":
|
|
237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "8", cy: "4.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" }),
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "4.5", x2: "3", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "13", y1: "4.5", x2: "13", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "8", cy: "11.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" })
|
|
242
|
+
] });
|
|
243
|
+
// Loop — circular arrow
|
|
244
|
+
case "loop":
|
|
245
|
+
case "retry":
|
|
246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
247
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 8A4 4 0 1 1 8 4", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 1.5L10.5 4L8 6.5", stroke: color, strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })
|
|
249
|
+
] });
|
|
250
|
+
// Lazy / service — cloud (deferred resolution, loaded on demand)
|
|
251
|
+
case "lazy":
|
|
252
|
+
case "service":
|
|
253
|
+
case "cloud":
|
|
254
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
255
|
+
"path",
|
|
256
|
+
{
|
|
257
|
+
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",
|
|
258
|
+
stroke: color,
|
|
259
|
+
strokeWidth: "1.3",
|
|
260
|
+
strokeLinecap: "round",
|
|
261
|
+
fill: "none"
|
|
262
|
+
}
|
|
263
|
+
) });
|
|
264
|
+
// Decision — diamond (already handled by isDecider shape)
|
|
265
|
+
case "decision":
|
|
266
|
+
case "router":
|
|
267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
|
|
268
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2L14 8L8 14L2 8Z", stroke: color, strokeWidth: "1.5", fill: "none" }),
|
|
269
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "1.5", fill: color })
|
|
270
|
+
] });
|
|
271
|
+
default:
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
107
275
|
var StageNode = (0, import_react3.memo)(function StageNode2({
|
|
108
276
|
data
|
|
109
277
|
}) {
|
|
110
|
-
const { label, active, done, error, linked, stepNumbers, dimmed, isSubflow, description } = data;
|
|
278
|
+
const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description } = data;
|
|
279
|
+
const effectiveIcon = icon || (isLazy ? "lazy" : void 0);
|
|
280
|
+
const isLazyUnresolved = isLazy && !done && !active;
|
|
281
|
+
const injectedRef = (0, import_react3.useRef)(false);
|
|
282
|
+
(0, import_react3.useEffect)(() => {
|
|
283
|
+
if (injectedRef.current) return;
|
|
284
|
+
if (typeof document !== "undefined" && !document.getElementById(KEYFRAMES_ID)) {
|
|
285
|
+
const styleEl = document.createElement("style");
|
|
286
|
+
styleEl.id = KEYFRAMES_ID;
|
|
287
|
+
styleEl.textContent = KEYFRAMES_CSS;
|
|
288
|
+
document.head.appendChild(styleEl);
|
|
289
|
+
}
|
|
290
|
+
injectedRef.current = true;
|
|
291
|
+
}, []);
|
|
111
292
|
const isOnPath = active || done;
|
|
112
293
|
const bg = active ? theme.primary : done ? theme.success : error ? theme.error : theme.bgSecondary;
|
|
113
294
|
const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : theme.border;
|
|
@@ -169,7 +350,8 @@ var StageNode = (0, import_react3.memo)(function StageNode2({
|
|
|
169
350
|
style: {
|
|
170
351
|
position: "absolute",
|
|
171
352
|
inset: -6,
|
|
172
|
-
borderRadius: `calc(${theme.radius} + 4px)`,
|
|
353
|
+
borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
|
|
354
|
+
transform: isDecider ? "rotate(45deg)" : void 0,
|
|
173
355
|
border: `2px solid ${theme.primary}`,
|
|
174
356
|
opacity: 0.4,
|
|
175
357
|
animation: "fp-pulse 2s ease-in-out infinite"
|
|
@@ -182,107 +364,175 @@ var StageNode = (0, import_react3.memo)(function StageNode2({
|
|
|
182
364
|
style: {
|
|
183
365
|
position: "absolute",
|
|
184
366
|
inset: -6,
|
|
185
|
-
borderRadius: `calc(${theme.radius} + 4px)`,
|
|
367
|
+
borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
|
|
368
|
+
transform: isDecider ? "rotate(45deg)" : void 0,
|
|
186
369
|
border: `2px solid ${theme.primary}`,
|
|
187
370
|
opacity: 0.3,
|
|
188
371
|
animation: "fp-pulse 1.5s ease-out infinite"
|
|
189
372
|
}
|
|
190
373
|
}
|
|
191
374
|
),
|
|
192
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
375
|
+
isDecider ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
193
376
|
"div",
|
|
194
377
|
{
|
|
195
378
|
style: {
|
|
196
379
|
background: bg,
|
|
197
|
-
border: `2px solid ${borderColor}`,
|
|
198
|
-
borderRadius:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
flexDirection: "column",
|
|
202
|
-
alignItems: "center",
|
|
203
|
-
gap: description ? 2 : 0,
|
|
380
|
+
border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
|
|
381
|
+
borderRadius: 4,
|
|
382
|
+
transform: "rotate(45deg)",
|
|
383
|
+
padding: 20,
|
|
204
384
|
boxShadow: shadow,
|
|
205
385
|
transition: "all 0.3s ease",
|
|
206
|
-
|
|
207
|
-
|
|
386
|
+
display: "flex",
|
|
387
|
+
alignItems: "center",
|
|
208
388
|
justifyContent: "center"
|
|
209
389
|
},
|
|
210
|
-
children:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
"
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
390
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
391
|
+
"div",
|
|
392
|
+
{
|
|
393
|
+
style: {
|
|
394
|
+
transform: "rotate(-45deg)",
|
|
395
|
+
display: "flex",
|
|
396
|
+
flexDirection: "column",
|
|
397
|
+
alignItems: "center",
|
|
398
|
+
gap: 2,
|
|
399
|
+
fontFamily: theme.fontSans
|
|
400
|
+
},
|
|
401
|
+
children: [
|
|
402
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [
|
|
403
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StageIcon, { type: icon, color: textColor }),
|
|
404
|
+
!icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u25C7" }),
|
|
405
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
406
|
+
"span",
|
|
407
|
+
{
|
|
408
|
+
style: {
|
|
409
|
+
fontSize: 12,
|
|
410
|
+
fontWeight: 600,
|
|
411
|
+
color: textColor,
|
|
412
|
+
whiteSpace: "nowrap"
|
|
413
|
+
},
|
|
414
|
+
children: label
|
|
415
|
+
}
|
|
416
|
+
)
|
|
417
|
+
] }),
|
|
418
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
419
|
+
"span",
|
|
420
|
+
{
|
|
421
|
+
style: {
|
|
422
|
+
fontSize: 9,
|
|
423
|
+
fontWeight: 400,
|
|
424
|
+
color: textColor,
|
|
425
|
+
opacity: 0.7,
|
|
426
|
+
whiteSpace: "nowrap",
|
|
427
|
+
overflow: "hidden",
|
|
428
|
+
textOverflow: "ellipsis",
|
|
429
|
+
maxWidth: 130
|
|
430
|
+
},
|
|
431
|
+
children: description
|
|
223
432
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
433
|
+
)
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
) : (
|
|
439
|
+
/* Standard rectangular node */
|
|
440
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
441
|
+
"div",
|
|
442
|
+
{
|
|
443
|
+
style: {
|
|
444
|
+
background: bg,
|
|
445
|
+
border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
|
|
446
|
+
borderRadius: theme.radius,
|
|
447
|
+
padding: description ? "8px 16px" : "10px 20px",
|
|
448
|
+
display: "flex",
|
|
449
|
+
flexDirection: "column",
|
|
450
|
+
alignItems: "center",
|
|
451
|
+
gap: description ? 2 : 0,
|
|
452
|
+
boxShadow: shadow,
|
|
453
|
+
transition: "all 0.3s ease",
|
|
454
|
+
fontFamily: theme.fontSans,
|
|
455
|
+
minWidth: 100,
|
|
456
|
+
justifyContent: "center"
|
|
457
|
+
},
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
460
|
+
effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StageIcon, { type: effectiveIcon, color: textColor }),
|
|
461
|
+
done && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
|
|
462
|
+
active && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
463
|
+
"span",
|
|
464
|
+
{
|
|
465
|
+
style: {
|
|
466
|
+
width: 8,
|
|
467
|
+
height: 8,
|
|
468
|
+
borderRadius: "50%",
|
|
469
|
+
background: "#fff",
|
|
470
|
+
animation: "fp-blink 1s ease-in-out infinite",
|
|
471
|
+
flexShrink: 0
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
),
|
|
475
|
+
error && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
|
|
476
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
477
|
+
"span",
|
|
478
|
+
{
|
|
479
|
+
style: {
|
|
480
|
+
fontSize: 13,
|
|
481
|
+
fontWeight: 500,
|
|
482
|
+
color: textColor,
|
|
483
|
+
whiteSpace: "nowrap"
|
|
484
|
+
},
|
|
485
|
+
children: label
|
|
486
|
+
}
|
|
487
|
+
),
|
|
488
|
+
isSubflow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
489
|
+
"span",
|
|
490
|
+
{
|
|
491
|
+
style: {
|
|
492
|
+
display: "inline-flex",
|
|
493
|
+
alignItems: "center",
|
|
494
|
+
justifyContent: "center",
|
|
495
|
+
width: 16,
|
|
496
|
+
height: 16,
|
|
497
|
+
borderRadius: 3,
|
|
498
|
+
border: `1.5px solid ${textColor}`,
|
|
499
|
+
position: "relative",
|
|
500
|
+
opacity: 0.7,
|
|
501
|
+
flexShrink: 0
|
|
502
|
+
},
|
|
503
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
504
|
+
"span",
|
|
505
|
+
{
|
|
506
|
+
style: {
|
|
507
|
+
width: 8,
|
|
508
|
+
height: 8,
|
|
509
|
+
borderRadius: 2,
|
|
510
|
+
border: `1px solid ${textColor}`
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
)
|
|
516
|
+
] }),
|
|
517
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
228
518
|
"span",
|
|
229
519
|
{
|
|
230
520
|
style: {
|
|
231
|
-
fontSize:
|
|
232
|
-
fontWeight:
|
|
521
|
+
fontSize: 10,
|
|
522
|
+
fontWeight: 400,
|
|
233
523
|
color: textColor,
|
|
234
|
-
whiteSpace: "nowrap"
|
|
235
|
-
},
|
|
236
|
-
children: label
|
|
237
|
-
}
|
|
238
|
-
),
|
|
239
|
-
isSubflow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
240
|
-
"span",
|
|
241
|
-
{
|
|
242
|
-
style: {
|
|
243
|
-
display: "inline-flex",
|
|
244
|
-
alignItems: "center",
|
|
245
|
-
justifyContent: "center",
|
|
246
|
-
width: 16,
|
|
247
|
-
height: 16,
|
|
248
|
-
borderRadius: 3,
|
|
249
|
-
border: `1.5px solid ${textColor}`,
|
|
250
|
-
position: "relative",
|
|
251
524
|
opacity: 0.7,
|
|
252
|
-
|
|
525
|
+
whiteSpace: "nowrap",
|
|
526
|
+
overflow: "hidden",
|
|
527
|
+
textOverflow: "ellipsis",
|
|
528
|
+
maxWidth: 160
|
|
253
529
|
},
|
|
254
|
-
children:
|
|
255
|
-
"span",
|
|
256
|
-
{
|
|
257
|
-
style: {
|
|
258
|
-
width: 8,
|
|
259
|
-
height: 8,
|
|
260
|
-
borderRadius: 2,
|
|
261
|
-
border: `1px solid ${textColor}`
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
)
|
|
530
|
+
children: description
|
|
265
531
|
}
|
|
266
532
|
)
|
|
267
|
-
]
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
{
|
|
271
|
-
style: {
|
|
272
|
-
fontSize: 10,
|
|
273
|
-
fontWeight: 400,
|
|
274
|
-
color: textColor,
|
|
275
|
-
opacity: 0.7,
|
|
276
|
-
whiteSpace: "nowrap",
|
|
277
|
-
overflow: "hidden",
|
|
278
|
-
textOverflow: "ellipsis",
|
|
279
|
-
maxWidth: 160
|
|
280
|
-
},
|
|
281
|
-
children: description
|
|
282
|
-
}
|
|
283
|
-
)
|
|
284
|
-
]
|
|
285
|
-
}
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
)
|
|
286
536
|
)
|
|
287
537
|
]
|
|
288
538
|
}
|
|
@@ -423,19 +673,19 @@ function FlowchartView({
|
|
|
423
673
|
}
|
|
424
674
|
|
|
425
675
|
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
426
|
-
var
|
|
427
|
-
var
|
|
676
|
+
var import_react7 = require("react");
|
|
677
|
+
var import_react8 = require("@xyflow/react");
|
|
428
678
|
|
|
429
679
|
// src/components/FlowchartView/specToReactFlow.ts
|
|
430
680
|
var DEFAULT_COLORS = {
|
|
431
|
-
edgeDefault:
|
|
432
|
-
edgeExecuted:
|
|
433
|
-
edgeActive:
|
|
434
|
-
edgeLoop:
|
|
435
|
-
labelDefault:
|
|
436
|
-
labelExecuted:
|
|
437
|
-
labelLoop:
|
|
438
|
-
pathGlow: `${
|
|
681
|
+
edgeDefault: rawDefaults.colors.textMuted,
|
|
682
|
+
edgeExecuted: rawDefaults.colors.success,
|
|
683
|
+
edgeActive: rawDefaults.colors.primary,
|
|
684
|
+
edgeLoop: rawDefaults.colors.warning,
|
|
685
|
+
labelDefault: rawDefaults.colors.textSecondary,
|
|
686
|
+
labelExecuted: rawDefaults.colors.success,
|
|
687
|
+
labelLoop: rawDefaults.colors.warning,
|
|
688
|
+
pathGlow: `${rawDefaults.colors.success}4D`
|
|
439
689
|
// ~30% opacity hex
|
|
440
690
|
};
|
|
441
691
|
var Y_STEP = 100;
|
|
@@ -443,122 +693,33 @@ var X_SPREAD = 200;
|
|
|
443
693
|
function nid(n) {
|
|
444
694
|
return n.name || n.id || `spec-${Math.random()}`;
|
|
445
695
|
}
|
|
446
|
-
function
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const c = state.colors;
|
|
450
|
-
const executed = o && o.executedStages.has(source) && o.executedStages.has(target);
|
|
451
|
-
const isLeadingEdge = o && source === o.activeStage && !o.doneStages.has(target);
|
|
452
|
-
if (isLoop) {
|
|
453
|
-
let loopExecuted = false;
|
|
454
|
-
if (o?.executionOrder) {
|
|
455
|
-
const lastSourceIdx = o.executionOrder.lastIndexOf(source);
|
|
456
|
-
if (lastSourceIdx >= 0) {
|
|
457
|
-
loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(target);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
state.edges.push({
|
|
461
|
-
id: `se${state.edgeCounter}`,
|
|
462
|
-
source,
|
|
463
|
-
target,
|
|
464
|
-
sourceHandle: "loop-source",
|
|
465
|
-
targetHandle: "loop-target",
|
|
466
|
-
label: label ?? "loop",
|
|
467
|
-
type: "smoothstep",
|
|
468
|
-
pathOptions: { offset: 40, borderRadius: 16 },
|
|
469
|
-
style: {
|
|
470
|
-
stroke: c.edgeLoop,
|
|
471
|
-
strokeWidth: loopExecuted ? 3 : 2,
|
|
472
|
-
strokeDasharray: "6 3",
|
|
473
|
-
opacity: o && !loopExecuted ? 0.35 : 1
|
|
474
|
-
},
|
|
475
|
-
labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
|
|
476
|
-
animated: loopExecuted,
|
|
477
|
-
zIndex: 2
|
|
478
|
-
});
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (executed) {
|
|
482
|
-
state.edges.push({
|
|
483
|
-
id: `se${state.edgeCounter}-glow`,
|
|
484
|
-
source,
|
|
485
|
-
target,
|
|
486
|
-
style: {
|
|
487
|
-
stroke: c.pathGlow,
|
|
488
|
-
strokeWidth: 8,
|
|
489
|
-
opacity: 0.4
|
|
490
|
-
},
|
|
491
|
-
zIndex: 0,
|
|
492
|
-
selectable: false,
|
|
493
|
-
focusable: false
|
|
494
|
-
});
|
|
495
|
-
state.edges.push({
|
|
496
|
-
id: `se${state.edgeCounter}`,
|
|
497
|
-
source,
|
|
498
|
-
target,
|
|
499
|
-
label,
|
|
500
|
-
style: {
|
|
501
|
-
stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
|
|
502
|
-
strokeWidth: 3.5
|
|
503
|
-
},
|
|
504
|
-
labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
|
|
505
|
-
animated: !!isLeadingEdge,
|
|
506
|
-
zIndex: 1
|
|
507
|
-
});
|
|
508
|
-
} else {
|
|
509
|
-
state.edges.push({
|
|
510
|
-
id: `se${state.edgeCounter}`,
|
|
511
|
-
source,
|
|
512
|
-
target,
|
|
513
|
-
label,
|
|
514
|
-
style: {
|
|
515
|
-
stroke: c.edgeDefault,
|
|
516
|
-
strokeWidth: 1.5,
|
|
517
|
-
opacity: o ? 0.3 : 1
|
|
518
|
-
},
|
|
519
|
-
labelStyle: { fontSize: 10, fill: c.labelDefault }
|
|
520
|
-
});
|
|
696
|
+
function registerNode(state, node) {
|
|
697
|
+
if (node.id && node.name) {
|
|
698
|
+
state.idToName.set(node.id, node.name);
|
|
521
699
|
}
|
|
522
700
|
}
|
|
523
|
-
function
|
|
701
|
+
function walkLayout(node, state, x, y) {
|
|
702
|
+
if (!node) return { lastIds: [], bottomY: y };
|
|
703
|
+
registerNode(state, node);
|
|
524
704
|
const id = nid(node);
|
|
525
705
|
if (state.seen.has(id)) {
|
|
526
706
|
return { lastIds: [id], bottomY: y };
|
|
527
707
|
}
|
|
528
708
|
state.seen.add(id);
|
|
529
|
-
const isDecider = node.type === "decider" || node.hasDecider;
|
|
709
|
+
const isDecider = node.type === "decider" || !!node.hasDecider;
|
|
530
710
|
const isFork = node.type === "fork";
|
|
531
|
-
const o = state.overlay;
|
|
532
|
-
const isDone = o ? o.doneStages.has(id) : false;
|
|
533
|
-
const isActive = o ? o.activeStage === id : false;
|
|
534
|
-
const wasExecuted = o ? o.executedStages.has(id) : false;
|
|
535
|
-
const dimmed = o && !wasExecuted;
|
|
536
|
-
let stepNumbers;
|
|
537
|
-
if (o?.executionOrder) {
|
|
538
|
-
const nums = [];
|
|
539
|
-
for (let i = 0; i < o.executionOrder.length; i++) {
|
|
540
|
-
if (o.executionOrder[i] === id) nums.push(i + 1);
|
|
541
|
-
}
|
|
542
|
-
if (nums.length > 0) stepNumbers = nums;
|
|
543
|
-
}
|
|
544
711
|
state.nodes.push({
|
|
545
712
|
id,
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
dimmed,
|
|
557
|
-
stepNumbers,
|
|
558
|
-
isSubflow: !!node.isSubflowRoot
|
|
559
|
-
},
|
|
560
|
-
type: "stage",
|
|
561
|
-
style: dimmed ? { opacity: 0.35 } : void 0
|
|
713
|
+
x,
|
|
714
|
+
y,
|
|
715
|
+
label: node.name,
|
|
716
|
+
isDecider,
|
|
717
|
+
isFork,
|
|
718
|
+
description: node.description,
|
|
719
|
+
icon: node.icon,
|
|
720
|
+
subflowId: node.subflowId,
|
|
721
|
+
isSubflow: !!node.isSubflowRoot,
|
|
722
|
+
isLazy: node.isLazy
|
|
562
723
|
});
|
|
563
724
|
let lastIds = [id];
|
|
564
725
|
let bottomY = y;
|
|
@@ -569,120 +730,257 @@ function walk(node, state, x, y) {
|
|
|
569
730
|
const childResults = [];
|
|
570
731
|
for (let i = 0; i < node.children.length; i++) {
|
|
571
732
|
const child = node.children[i];
|
|
733
|
+
if (!child) continue;
|
|
572
734
|
const childX = startX + i * X_SPREAD;
|
|
573
735
|
const edgeLabel = node.branchIds?.[i];
|
|
574
|
-
|
|
575
|
-
|
|
736
|
+
state.edgeCounter++;
|
|
737
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: nid(child), label: edgeLabel, isLoop: false });
|
|
738
|
+
const result = walkLayout(child, state, childX, childY);
|
|
576
739
|
childResults.push(result);
|
|
577
740
|
}
|
|
578
741
|
lastIds = childResults.flatMap((r) => r.lastIds);
|
|
579
742
|
bottomY = Math.max(...childResults.map((r) => r.bottomY));
|
|
580
743
|
}
|
|
581
744
|
if (node.loopTarget) {
|
|
582
|
-
|
|
745
|
+
const resolvedTarget = state.idToName.get(node.loopTarget) ?? node.loopTarget;
|
|
746
|
+
state.edgeCounter++;
|
|
747
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: resolvedTarget, label: "loop", isLoop: true });
|
|
583
748
|
}
|
|
584
749
|
if (node.next) {
|
|
750
|
+
const rawNextId = nid(node.next);
|
|
751
|
+
const resolvedNextId = state.idToName.get(rawNextId) ?? rawNextId;
|
|
752
|
+
const isLoopRef = node.loopTarget && state.seen.has(resolvedNextId);
|
|
753
|
+
if (isLoopRef) {
|
|
754
|
+
return { lastIds, bottomY };
|
|
755
|
+
}
|
|
585
756
|
const nextY = bottomY + Y_STEP;
|
|
586
|
-
const nextId = nid(node.next);
|
|
587
757
|
for (const lid of lastIds) {
|
|
588
|
-
|
|
589
|
-
|
|
758
|
+
state.edgeCounter++;
|
|
759
|
+
state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, isLoop: false });
|
|
590
760
|
}
|
|
591
|
-
|
|
592
|
-
return result;
|
|
761
|
+
return walkLayout(node.next, state, x, nextY);
|
|
593
762
|
}
|
|
594
763
|
return { lastIds, bottomY };
|
|
595
764
|
}
|
|
596
|
-
function
|
|
765
|
+
function specToLayout(spec) {
|
|
597
766
|
const state = {
|
|
598
767
|
nodes: [],
|
|
599
768
|
edges: [],
|
|
600
769
|
edgeCounter: 0,
|
|
601
770
|
seen: /* @__PURE__ */ new Set(),
|
|
602
|
-
|
|
603
|
-
colors: { ...DEFAULT_COLORS, ...colors }
|
|
771
|
+
idToName: /* @__PURE__ */ new Map()
|
|
604
772
|
};
|
|
605
|
-
|
|
606
|
-
return { nodes: state.nodes, edges: state.edges };
|
|
773
|
+
walkLayout(spec, state, 300, 0);
|
|
774
|
+
return { nodes: state.nodes, edges: state.edges, idToName: state.idToName };
|
|
607
775
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
function collectSubflows(node) {
|
|
622
|
-
if (node.isSubflowRoot && node.subflowStructure) {
|
|
623
|
-
const id = node.name || node.id || "";
|
|
624
|
-
map.set(id, node);
|
|
776
|
+
function applyOverlay(layout, overlay, colors) {
|
|
777
|
+
const c = { ...DEFAULT_COLORS, ...colors };
|
|
778
|
+
const o = overlay ?? null;
|
|
779
|
+
const nodes = layout.nodes.map((ln) => {
|
|
780
|
+
const isDone = o ? o.doneStages.has(ln.id) : false;
|
|
781
|
+
const isActive = o ? o.activeStage === ln.id : false;
|
|
782
|
+
const wasExecuted = o ? o.executedStages.has(ln.id) : false;
|
|
783
|
+
const dimmed = o && !wasExecuted;
|
|
784
|
+
let stepNumbers;
|
|
785
|
+
if (o?.executionOrder) {
|
|
786
|
+
const nums = [];
|
|
787
|
+
for (let i = 0; i < o.executionOrder.length; i++) {
|
|
788
|
+
if (o.executionOrder[i] === ln.id) nums.push(i + 1);
|
|
625
789
|
}
|
|
626
|
-
if (
|
|
627
|
-
if (node.next) collectSubflows(node.next);
|
|
790
|
+
if (nums.length > 0) stepNumbers = nums;
|
|
628
791
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
792
|
+
return {
|
|
793
|
+
id: ln.id,
|
|
794
|
+
position: { x: ln.x, y: ln.y },
|
|
795
|
+
data: {
|
|
796
|
+
label: ln.label,
|
|
797
|
+
active: isActive,
|
|
798
|
+
done: isDone,
|
|
799
|
+
error: false,
|
|
800
|
+
isDecider: ln.isDecider,
|
|
801
|
+
isFork: ln.isFork,
|
|
802
|
+
description: ln.description,
|
|
803
|
+
icon: ln.icon,
|
|
804
|
+
subflowId: ln.subflowId,
|
|
805
|
+
dimmed,
|
|
806
|
+
stepNumbers,
|
|
807
|
+
isSubflow: ln.isSubflow,
|
|
808
|
+
isLazy: ln.isLazy
|
|
809
|
+
},
|
|
810
|
+
type: "stage",
|
|
811
|
+
style: dimmed ? { opacity: 0.35 } : void 0
|
|
637
812
|
};
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const
|
|
641
|
-
(
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
description: subflowNode.description
|
|
813
|
+
});
|
|
814
|
+
const edges = [];
|
|
815
|
+
for (const le of layout.edges) {
|
|
816
|
+
const executed = o && o.executedStages.has(le.source) && o.executedStages.has(le.target);
|
|
817
|
+
const isLeadingEdge = o && le.source === o.activeStage && !o.doneStages.has(le.target);
|
|
818
|
+
if (le.isLoop) {
|
|
819
|
+
let loopExecuted = false;
|
|
820
|
+
if (o?.executionOrder) {
|
|
821
|
+
const lastSourceIdx = o.executionOrder.lastIndexOf(le.source);
|
|
822
|
+
if (lastSourceIdx >= 0) {
|
|
823
|
+
loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(le.target);
|
|
650
824
|
}
|
|
651
|
-
]);
|
|
652
|
-
return true;
|
|
653
|
-
},
|
|
654
|
-
[subflowMap]
|
|
655
|
-
);
|
|
656
|
-
const navigateTo = (0, import_react7.useCallback)(
|
|
657
|
-
(level) => {
|
|
658
|
-
if (level === 0) {
|
|
659
|
-
setStack([]);
|
|
660
|
-
} else {
|
|
661
|
-
setStack((prev) => prev.slice(0, level));
|
|
662
825
|
}
|
|
826
|
+
edges.push({
|
|
827
|
+
id: le.id,
|
|
828
|
+
source: le.source,
|
|
829
|
+
target: le.target,
|
|
830
|
+
sourceHandle: "loop-source",
|
|
831
|
+
targetHandle: "loop-target",
|
|
832
|
+
label: le.label ?? "loop",
|
|
833
|
+
type: "smoothstep",
|
|
834
|
+
pathOptions: { offset: 40, borderRadius: 16 },
|
|
835
|
+
style: {
|
|
836
|
+
stroke: c.edgeLoop,
|
|
837
|
+
strokeWidth: loopExecuted ? 3 : 2,
|
|
838
|
+
strokeDasharray: "6 3",
|
|
839
|
+
opacity: o && !loopExecuted ? 0.35 : 1
|
|
840
|
+
},
|
|
841
|
+
labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
|
|
842
|
+
animated: loopExecuted,
|
|
843
|
+
zIndex: 2
|
|
844
|
+
});
|
|
845
|
+
} else if (executed) {
|
|
846
|
+
edges.push({
|
|
847
|
+
id: `${le.id}-glow`,
|
|
848
|
+
source: le.source,
|
|
849
|
+
target: le.target,
|
|
850
|
+
style: { stroke: c.pathGlow, strokeWidth: 8, opacity: 0.4 },
|
|
851
|
+
zIndex: 0,
|
|
852
|
+
selectable: false,
|
|
853
|
+
focusable: false
|
|
854
|
+
});
|
|
855
|
+
edges.push({
|
|
856
|
+
id: le.id,
|
|
857
|
+
source: le.source,
|
|
858
|
+
target: le.target,
|
|
859
|
+
label: le.label,
|
|
860
|
+
style: {
|
|
861
|
+
stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
|
|
862
|
+
strokeWidth: 3.5
|
|
863
|
+
},
|
|
864
|
+
labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
|
|
865
|
+
animated: !!isLeadingEdge,
|
|
866
|
+
zIndex: 1
|
|
867
|
+
});
|
|
868
|
+
} else {
|
|
869
|
+
edges.push({
|
|
870
|
+
id: le.id,
|
|
871
|
+
source: le.source,
|
|
872
|
+
target: le.target,
|
|
873
|
+
label: le.label,
|
|
874
|
+
style: {
|
|
875
|
+
stroke: c.edgeDefault,
|
|
876
|
+
strokeWidth: 1.5,
|
|
877
|
+
opacity: o ? 0.3 : 1
|
|
878
|
+
},
|
|
879
|
+
labelStyle: { fontSize: 10, fill: c.labelDefault }
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return { nodes, edges };
|
|
884
|
+
}
|
|
885
|
+
function specToReactFlow(spec, overlay, colors) {
|
|
886
|
+
const layout = specToLayout(spec);
|
|
887
|
+
return applyOverlay(layout, overlay, colors);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
891
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
892
|
+
var defaultNodeTypes = { stage: StageNode };
|
|
893
|
+
function FitViewOnResize() {
|
|
894
|
+
const { fitView } = (0, import_react8.useReactFlow)();
|
|
895
|
+
(0, import_react7.useEffect)(() => {
|
|
896
|
+
const handler = () => {
|
|
897
|
+
requestAnimationFrame(() => fitView({ padding: 0.3 }));
|
|
898
|
+
};
|
|
899
|
+
window.addEventListener("resize", handler);
|
|
900
|
+
return () => window.removeEventListener("resize", handler);
|
|
901
|
+
}, [fitView]);
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
function TracedFlowchartView({
|
|
905
|
+
spec,
|
|
906
|
+
snapshots,
|
|
907
|
+
snapshotIndex = 0,
|
|
908
|
+
onNodeClick,
|
|
909
|
+
nodeTypes: customNodeTypes,
|
|
910
|
+
unstyled = false,
|
|
911
|
+
className,
|
|
912
|
+
style
|
|
913
|
+
}) {
|
|
914
|
+
const nodeTypes2 = customNodeTypes ?? defaultNodeTypes;
|
|
915
|
+
const overlay = (0, import_react7.useMemo)(() => {
|
|
916
|
+
if (!snapshots || snapshots.length === 0) return void 0;
|
|
917
|
+
const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
|
|
918
|
+
const doneStages = new Set(
|
|
919
|
+
snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
|
|
920
|
+
);
|
|
921
|
+
const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
|
|
922
|
+
const executedStages = /* @__PURE__ */ new Set([...doneStages]);
|
|
923
|
+
if (activeStage) executedStages.add(activeStage);
|
|
924
|
+
return { doneStages, activeStage, executedStages, executionOrder };
|
|
925
|
+
}, [snapshots, snapshotIndex]);
|
|
926
|
+
const layout = (0, import_react7.useMemo)(() => {
|
|
927
|
+
if (!spec) return null;
|
|
928
|
+
return specToLayout(spec);
|
|
929
|
+
}, [spec]);
|
|
930
|
+
const { nodes, edges } = (0, import_react7.useMemo)(() => {
|
|
931
|
+
if (!layout) return { nodes: [], edges: [] };
|
|
932
|
+
return applyOverlay(layout, overlay);
|
|
933
|
+
}, [layout, overlay]);
|
|
934
|
+
const handleNodeClick = (0, import_react7.useCallback)(
|
|
935
|
+
(_, node) => {
|
|
936
|
+
if (!onNodeClick) return;
|
|
937
|
+
onNodeClick(node.id);
|
|
663
938
|
},
|
|
664
|
-
[]
|
|
939
|
+
[onNodeClick]
|
|
940
|
+
);
|
|
941
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
942
|
+
"div",
|
|
943
|
+
{
|
|
944
|
+
className,
|
|
945
|
+
style: { width: "100%", height: "100%", ...style },
|
|
946
|
+
"data-fp": "traced-flowchart",
|
|
947
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
948
|
+
import_react8.ReactFlow,
|
|
949
|
+
{
|
|
950
|
+
nodes,
|
|
951
|
+
edges,
|
|
952
|
+
onNodeClick: handleNodeClick,
|
|
953
|
+
nodeTypes: nodeTypes2,
|
|
954
|
+
fitView: true,
|
|
955
|
+
fitViewOptions: { padding: 0.3 },
|
|
956
|
+
proOptions: { hideAttribution: true },
|
|
957
|
+
panOnDrag: false,
|
|
958
|
+
zoomOnScroll: false,
|
|
959
|
+
zoomOnPinch: false,
|
|
960
|
+
zoomOnDoubleClick: false,
|
|
961
|
+
preventScrolling: false,
|
|
962
|
+
nodesDraggable: false,
|
|
963
|
+
nodesConnectable: false,
|
|
964
|
+
elementsSelectable: !!onNodeClick,
|
|
965
|
+
children: [
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FitViewOnResize, {}),
|
|
967
|
+
!unstyled && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react8.Background, { variant: import_react8.BackgroundVariant.Dots, gap: 16, size: 1 })
|
|
968
|
+
]
|
|
969
|
+
}
|
|
970
|
+
)
|
|
971
|
+
}
|
|
665
972
|
);
|
|
666
|
-
return {
|
|
667
|
-
breadcrumbs,
|
|
668
|
-
nodes,
|
|
669
|
-
edges,
|
|
670
|
-
handleNodeClick,
|
|
671
|
-
navigateTo,
|
|
672
|
-
isInSubflow: stack.length > 0,
|
|
673
|
-
currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
|
|
674
|
-
};
|
|
675
973
|
}
|
|
676
974
|
|
|
677
975
|
// src/components/FlowchartView/SubflowBreadcrumb.tsx
|
|
678
|
-
var
|
|
679
|
-
var
|
|
680
|
-
var SubflowBreadcrumb = (0,
|
|
976
|
+
var import_react9 = require("react");
|
|
977
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
978
|
+
var SubflowBreadcrumb = (0, import_react9.memo)(function SubflowBreadcrumb2({
|
|
681
979
|
breadcrumbs,
|
|
682
980
|
onNavigate
|
|
683
981
|
}) {
|
|
684
982
|
if (breadcrumbs.length <= 1) return null;
|
|
685
|
-
return /* @__PURE__ */ (0,
|
|
983
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
686
984
|
"div",
|
|
687
985
|
{
|
|
688
986
|
style: {
|
|
@@ -699,10 +997,10 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
|
|
|
699
997
|
},
|
|
700
998
|
children: breadcrumbs.map((crumb, i) => {
|
|
701
999
|
const isLast = i === breadcrumbs.length - 1;
|
|
702
|
-
return /* @__PURE__ */ (0,
|
|
703
|
-
i > 0 && /* @__PURE__ */ (0,
|
|
704
|
-
isLast ? /* @__PURE__ */ (0,
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
1000
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
1001
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
|
|
1002
|
+
isLast ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
706
1004
|
"span",
|
|
707
1005
|
{
|
|
708
1006
|
style: {
|
|
@@ -712,7 +1010,7 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
|
|
|
712
1010
|
children: crumb.label
|
|
713
1011
|
}
|
|
714
1012
|
),
|
|
715
|
-
crumb.description && /* @__PURE__ */ (0,
|
|
1013
|
+
crumb.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
716
1014
|
"span",
|
|
717
1015
|
{
|
|
718
1016
|
style: {
|
|
@@ -726,7 +1024,7 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
|
|
|
726
1024
|
]
|
|
727
1025
|
}
|
|
728
1026
|
)
|
|
729
|
-
] }) : /* @__PURE__ */ (0,
|
|
1027
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
730
1028
|
"button",
|
|
731
1029
|
{
|
|
732
1030
|
onClick: () => onNavigate(i),
|
|
@@ -757,13 +1055,83 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
|
|
|
757
1055
|
);
|
|
758
1056
|
});
|
|
759
1057
|
|
|
1058
|
+
// src/components/FlowchartView/useSubflowNavigation.ts
|
|
1059
|
+
var import_react10 = require("react");
|
|
1060
|
+
function useSubflowNavigation(rootSpec, overlay, colors) {
|
|
1061
|
+
const [stack, setStack] = (0, import_react10.useState)([]);
|
|
1062
|
+
const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
|
|
1063
|
+
const { nodes, edges } = (0, import_react10.useMemo)(() => {
|
|
1064
|
+
if (!currentSpec) return { nodes: [], edges: [] };
|
|
1065
|
+
return specToReactFlow(currentSpec, overlay, colors);
|
|
1066
|
+
}, [currentSpec, overlay, colors]);
|
|
1067
|
+
const subflowMap = (0, import_react10.useMemo)(() => {
|
|
1068
|
+
const map = /* @__PURE__ */ new Map();
|
|
1069
|
+
if (!currentSpec) return map;
|
|
1070
|
+
function collectSubflows(node) {
|
|
1071
|
+
if (node.isSubflowRoot && node.subflowStructure) {
|
|
1072
|
+
const id = node.name || node.id || "";
|
|
1073
|
+
map.set(id, node);
|
|
1074
|
+
}
|
|
1075
|
+
if (node.children) node.children.forEach(collectSubflows);
|
|
1076
|
+
if (node.next) collectSubflows(node.next);
|
|
1077
|
+
}
|
|
1078
|
+
collectSubflows(currentSpec);
|
|
1079
|
+
return map;
|
|
1080
|
+
}, [currentSpec]);
|
|
1081
|
+
const breadcrumbs = (0, import_react10.useMemo)(() => {
|
|
1082
|
+
const root = {
|
|
1083
|
+
label: rootSpec?.name || "Flowchart",
|
|
1084
|
+
spec: rootSpec,
|
|
1085
|
+
description: rootSpec?.description
|
|
1086
|
+
};
|
|
1087
|
+
return [root, ...stack];
|
|
1088
|
+
}, [rootSpec, stack]);
|
|
1089
|
+
const handleNodeClick = (0, import_react10.useCallback)(
|
|
1090
|
+
(nodeId) => {
|
|
1091
|
+
const subflowNode = subflowMap.get(nodeId);
|
|
1092
|
+
if (!subflowNode?.subflowStructure) return false;
|
|
1093
|
+
setStack((prev) => [
|
|
1094
|
+
...prev,
|
|
1095
|
+
{
|
|
1096
|
+
label: subflowNode.subflowName || subflowNode.name,
|
|
1097
|
+
spec: subflowNode.subflowStructure,
|
|
1098
|
+
description: subflowNode.description
|
|
1099
|
+
}
|
|
1100
|
+
]);
|
|
1101
|
+
return true;
|
|
1102
|
+
},
|
|
1103
|
+
[subflowMap]
|
|
1104
|
+
);
|
|
1105
|
+
const navigateTo = (0, import_react10.useCallback)(
|
|
1106
|
+
(level) => {
|
|
1107
|
+
if (level === 0) {
|
|
1108
|
+
setStack([]);
|
|
1109
|
+
} else {
|
|
1110
|
+
setStack((prev) => prev.slice(0, level));
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
[]
|
|
1114
|
+
);
|
|
1115
|
+
return {
|
|
1116
|
+
breadcrumbs,
|
|
1117
|
+
nodes,
|
|
1118
|
+
edges,
|
|
1119
|
+
handleNodeClick,
|
|
1120
|
+
navigateTo,
|
|
1121
|
+
isInSubflow: stack.length > 0,
|
|
1122
|
+
currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
|
|
760
1126
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
761
|
-
var
|
|
762
|
-
var
|
|
1127
|
+
var import_react11 = require("react");
|
|
1128
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
763
1129
|
function specToTree(node) {
|
|
1130
|
+
if (!node) return [];
|
|
764
1131
|
const entries = [];
|
|
765
1132
|
const seen = /* @__PURE__ */ new Set();
|
|
766
|
-
function
|
|
1133
|
+
function walk(n) {
|
|
1134
|
+
if (!n) return;
|
|
767
1135
|
const id = n.name || n.id || "";
|
|
768
1136
|
if (seen.has(id)) return;
|
|
769
1137
|
seen.add(id);
|
|
@@ -779,35 +1147,35 @@ function specToTree(node) {
|
|
|
779
1147
|
entries.push(entry);
|
|
780
1148
|
if (n.children) {
|
|
781
1149
|
for (const child of n.children) {
|
|
782
|
-
|
|
1150
|
+
if (child) walk(child);
|
|
783
1151
|
}
|
|
784
1152
|
}
|
|
785
1153
|
if (n.next) {
|
|
786
|
-
|
|
1154
|
+
walk(n.next);
|
|
787
1155
|
}
|
|
788
1156
|
}
|
|
789
|
-
|
|
1157
|
+
walk(node);
|
|
790
1158
|
return entries;
|
|
791
1159
|
}
|
|
792
|
-
var TreeNode = (0,
|
|
1160
|
+
var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
793
1161
|
entry,
|
|
794
1162
|
depth,
|
|
795
1163
|
activeStage,
|
|
796
1164
|
doneStages,
|
|
797
1165
|
onNodeSelect
|
|
798
1166
|
}) {
|
|
799
|
-
const [expanded, setExpanded] = (0,
|
|
1167
|
+
const [expanded, setExpanded] = (0, import_react11.useState)(true);
|
|
800
1168
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
801
1169
|
const isActive = activeStage === entry.name;
|
|
802
1170
|
const isDone = doneStages?.has(entry.name);
|
|
803
|
-
const handleClick = (0,
|
|
1171
|
+
const handleClick = (0, import_react11.useCallback)(() => {
|
|
804
1172
|
if (hasChildren) {
|
|
805
1173
|
setExpanded((prev) => !prev);
|
|
806
1174
|
}
|
|
807
1175
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
808
1176
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
809
|
-
return /* @__PURE__ */ (0,
|
|
810
|
-
/* @__PURE__ */ (0,
|
|
1177
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1178
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
811
1179
|
"button",
|
|
812
1180
|
{
|
|
813
1181
|
onClick: handleClick,
|
|
@@ -838,7 +1206,7 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
838
1206
|
}
|
|
839
1207
|
},
|
|
840
1208
|
children: [
|
|
841
|
-
hasChildren ? /* @__PURE__ */ (0,
|
|
1209
|
+
hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
842
1210
|
"span",
|
|
843
1211
|
{
|
|
844
1212
|
style: {
|
|
@@ -853,8 +1221,8 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
853
1221
|
},
|
|
854
1222
|
children: "\u25B6"
|
|
855
1223
|
}
|
|
856
|
-
) : /* @__PURE__ */ (0,
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
1224
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { width: 12, flexShrink: 0 } }),
|
|
1225
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
858
1226
|
"span",
|
|
859
1227
|
{
|
|
860
1228
|
style: {
|
|
@@ -866,8 +1234,8 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
866
1234
|
}
|
|
867
1235
|
}
|
|
868
1236
|
),
|
|
869
|
-
/* @__PURE__ */ (0,
|
|
870
|
-
/* @__PURE__ */ (0,
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
871
1239
|
"span",
|
|
872
1240
|
{
|
|
873
1241
|
style: {
|
|
@@ -879,11 +1247,11 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
879
1247
|
},
|
|
880
1248
|
children: [
|
|
881
1249
|
entry.name,
|
|
882
|
-
entry.isSubflow && /* @__PURE__ */ (0,
|
|
1250
|
+
entry.isSubflow && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
883
1251
|
]
|
|
884
1252
|
}
|
|
885
1253
|
),
|
|
886
|
-
entry.description && /* @__PURE__ */ (0,
|
|
1254
|
+
entry.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
887
1255
|
"span",
|
|
888
1256
|
{
|
|
889
1257
|
style: {
|
|
@@ -900,7 +1268,7 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
900
1268
|
]
|
|
901
1269
|
}
|
|
902
1270
|
),
|
|
903
|
-
hasChildren && expanded && /* @__PURE__ */ (0,
|
|
1271
|
+
hasChildren && expanded && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: entry.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
904
1272
|
TreeNode2,
|
|
905
1273
|
{
|
|
906
1274
|
entry: child,
|
|
@@ -909,12 +1277,12 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
|
|
|
909
1277
|
doneStages,
|
|
910
1278
|
onNodeSelect
|
|
911
1279
|
},
|
|
912
|
-
`${child.name}-${i}`
|
|
1280
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
913
1281
|
)) })
|
|
914
1282
|
] });
|
|
915
1283
|
});
|
|
916
|
-
var SectionLabel = (0,
|
|
917
|
-
return /* @__PURE__ */ (0,
|
|
1284
|
+
var SectionLabel = (0, import_react11.memo)(function SectionLabel2({ children }) {
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
918
1286
|
"div",
|
|
919
1287
|
{
|
|
920
1288
|
style: {
|
|
@@ -929,7 +1297,7 @@ var SectionLabel = (0, import_react9.memo)(function SectionLabel2({ children })
|
|
|
929
1297
|
}
|
|
930
1298
|
);
|
|
931
1299
|
});
|
|
932
|
-
var SubflowTree = (0,
|
|
1300
|
+
var SubflowTree = (0, import_react11.memo)(function SubflowTree2({
|
|
933
1301
|
spec,
|
|
934
1302
|
activeStage,
|
|
935
1303
|
doneStages,
|
|
@@ -938,10 +1306,10 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
|
|
|
938
1306
|
className,
|
|
939
1307
|
style
|
|
940
1308
|
}) {
|
|
941
|
-
const tree = (0,
|
|
942
|
-
const subflowStages = (0,
|
|
1309
|
+
const tree = (0, import_react11.useMemo)(() => specToTree(spec), [spec]);
|
|
1310
|
+
const subflowStages = (0, import_react11.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
943
1311
|
if (subflowStages.length === 0) return null;
|
|
944
|
-
return /* @__PURE__ */ (0,
|
|
1312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
945
1313
|
"div",
|
|
946
1314
|
{
|
|
947
1315
|
className,
|
|
@@ -959,8 +1327,8 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
|
|
|
959
1327
|
...style
|
|
960
1328
|
},
|
|
961
1329
|
children: [
|
|
962
|
-
!unstyled && /* @__PURE__ */ (0,
|
|
963
|
-
subflowStages.map((entry, i) => /* @__PURE__ */ (0,
|
|
1330
|
+
!unstyled && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Subflows" }),
|
|
1331
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
964
1332
|
TreeNode,
|
|
965
1333
|
{
|
|
966
1334
|
entry,
|
|
@@ -969,175 +1337,13 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
|
|
|
969
1337
|
doneStages,
|
|
970
1338
|
onNodeSelect
|
|
971
1339
|
},
|
|
972
|
-
`${entry.name}-${i}`
|
|
1340
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
973
1341
|
))
|
|
974
1342
|
]
|
|
975
1343
|
}
|
|
976
1344
|
);
|
|
977
1345
|
});
|
|
978
1346
|
|
|
979
|
-
// src/components/FlowchartView/TracedFlowchartView.tsx
|
|
980
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
981
|
-
var nodeTypes2 = { stage: StageNode };
|
|
982
|
-
function TracedFlowchartView({
|
|
983
|
-
spec,
|
|
984
|
-
snapshots,
|
|
985
|
-
snapshotIndex = 0,
|
|
986
|
-
onNodeClick,
|
|
987
|
-
onSubflowChange,
|
|
988
|
-
showTree = true,
|
|
989
|
-
treeWidth = 200,
|
|
990
|
-
unstyled = false,
|
|
991
|
-
className,
|
|
992
|
-
style
|
|
993
|
-
}) {
|
|
994
|
-
const [treeVisible, setTreeVisible] = (0, import_react10.useState)(showTree);
|
|
995
|
-
const subflowNav = useSubflowNavigation(spec);
|
|
996
|
-
const currentSpec = subflowNav.breadcrumbs.length > 0 ? subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec : null;
|
|
997
|
-
const overlay = (0, import_react10.useMemo)(() => {
|
|
998
|
-
if (!snapshots || snapshots.length === 0) return void 0;
|
|
999
|
-
const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
|
|
1000
|
-
const doneStages = new Set(
|
|
1001
|
-
snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
|
|
1002
|
-
);
|
|
1003
|
-
const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
|
|
1004
|
-
const executedStages = /* @__PURE__ */ new Set([...doneStages]);
|
|
1005
|
-
if (activeStage) executedStages.add(activeStage);
|
|
1006
|
-
return { doneStages, activeStage, executedStages, executionOrder };
|
|
1007
|
-
}, [snapshots, snapshotIndex]);
|
|
1008
|
-
const { nodes, edges } = (0, import_react10.useMemo)(() => {
|
|
1009
|
-
if (!currentSpec) return { nodes: [], edges: [] };
|
|
1010
|
-
return specToReactFlow(currentSpec, overlay);
|
|
1011
|
-
}, [currentSpec, overlay]);
|
|
1012
|
-
const handleNodeClick = (0, import_react10.useCallback)(
|
|
1013
|
-
(_, node) => {
|
|
1014
|
-
if (subflowNav.handleNodeClick(node.id)) {
|
|
1015
|
-
onSubflowChange?.(true, node.id);
|
|
1016
|
-
return;
|
|
1017
|
-
}
|
|
1018
|
-
if (onNodeClick && snapshots) {
|
|
1019
|
-
const idx = snapshots.findIndex((s) => s.stageLabel === node.id);
|
|
1020
|
-
if (idx >= 0) onNodeClick(idx);
|
|
1021
|
-
} else if (onNodeClick) {
|
|
1022
|
-
onNodeClick(node.id);
|
|
1023
|
-
}
|
|
1024
|
-
},
|
|
1025
|
-
[subflowNav, onNodeClick, onSubflowChange, snapshots]
|
|
1026
|
-
);
|
|
1027
|
-
const handleBreadcrumbNavigate = (0, import_react10.useCallback)(
|
|
1028
|
-
(level) => {
|
|
1029
|
-
subflowNav.navigateTo(level);
|
|
1030
|
-
onSubflowChange?.(level > 0, null);
|
|
1031
|
-
},
|
|
1032
|
-
[subflowNav, onSubflowChange]
|
|
1033
|
-
);
|
|
1034
|
-
const handleTreeNodeSelect = (0, import_react10.useCallback)(
|
|
1035
|
-
(name, isSubflow) => {
|
|
1036
|
-
if (isSubflow) {
|
|
1037
|
-
if (subflowNav.handleNodeClick(name)) {
|
|
1038
|
-
onSubflowChange?.(true, name);
|
|
1039
|
-
}
|
|
1040
|
-
} else if (onNodeClick && snapshots) {
|
|
1041
|
-
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
1042
|
-
if (idx >= 0) onNodeClick(idx);
|
|
1043
|
-
}
|
|
1044
|
-
},
|
|
1045
|
-
[subflowNav, onNodeClick, onSubflowChange, snapshots]
|
|
1046
|
-
);
|
|
1047
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1048
|
-
"div",
|
|
1049
|
-
{
|
|
1050
|
-
className,
|
|
1051
|
-
style: { width: "100%", height: "100%", display: "flex", flexDirection: "row", ...style },
|
|
1052
|
-
"data-fp": "traced-flowchart",
|
|
1053
|
-
children: [
|
|
1054
|
-
showTree && treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1055
|
-
SubflowTree,
|
|
1056
|
-
{
|
|
1057
|
-
spec,
|
|
1058
|
-
activeStage: overlay?.activeStage,
|
|
1059
|
-
doneStages: overlay?.doneStages,
|
|
1060
|
-
onNodeSelect: handleTreeNodeSelect,
|
|
1061
|
-
unstyled,
|
|
1062
|
-
style: { width: treeWidth, flexShrink: 0, height: "100%" }
|
|
1063
|
-
}
|
|
1064
|
-
),
|
|
1065
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0, height: "100%" }, children: [
|
|
1066
|
-
(subflowNav.isInSubflow || showTree && !treeVisible) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
|
|
1067
|
-
showTree && !treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1068
|
-
"button",
|
|
1069
|
-
{
|
|
1070
|
-
onClick: () => setTreeVisible(true),
|
|
1071
|
-
"data-fp": "tree-toggle",
|
|
1072
|
-
style: unstyled ? {} : {
|
|
1073
|
-
background: "transparent",
|
|
1074
|
-
border: "none",
|
|
1075
|
-
cursor: "pointer",
|
|
1076
|
-
padding: "6px 8px",
|
|
1077
|
-
fontSize: 10,
|
|
1078
|
-
flexShrink: 0
|
|
1079
|
-
},
|
|
1080
|
-
children: "\u25B6"
|
|
1081
|
-
}
|
|
1082
|
-
),
|
|
1083
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1084
|
-
SubflowBreadcrumb,
|
|
1085
|
-
{
|
|
1086
|
-
breadcrumbs: subflowNav.breadcrumbs,
|
|
1087
|
-
onNavigate: handleBreadcrumbNavigate
|
|
1088
|
-
}
|
|
1089
|
-
) })
|
|
1090
|
-
] }),
|
|
1091
|
-
showTree && treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
|
|
1092
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1093
|
-
"button",
|
|
1094
|
-
{
|
|
1095
|
-
onClick: () => setTreeVisible(false),
|
|
1096
|
-
"data-fp": "tree-toggle",
|
|
1097
|
-
style: unstyled ? {} : {
|
|
1098
|
-
background: "transparent",
|
|
1099
|
-
border: "none",
|
|
1100
|
-
cursor: "pointer",
|
|
1101
|
-
padding: "6px 8px",
|
|
1102
|
-
fontSize: 10,
|
|
1103
|
-
flexShrink: 0
|
|
1104
|
-
},
|
|
1105
|
-
children: "\u25C0"
|
|
1106
|
-
}
|
|
1107
|
-
),
|
|
1108
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: subflowNav.isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1109
|
-
SubflowBreadcrumb,
|
|
1110
|
-
{
|
|
1111
|
-
breadcrumbs: subflowNav.breadcrumbs,
|
|
1112
|
-
onNavigate: handleBreadcrumbNavigate
|
|
1113
|
-
}
|
|
1114
|
-
) })
|
|
1115
|
-
] }),
|
|
1116
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1117
|
-
import_react11.ReactFlow,
|
|
1118
|
-
{
|
|
1119
|
-
nodes,
|
|
1120
|
-
edges,
|
|
1121
|
-
onNodeClick: handleNodeClick,
|
|
1122
|
-
nodeTypes: nodeTypes2,
|
|
1123
|
-
fitView: true,
|
|
1124
|
-
panOnDrag: false,
|
|
1125
|
-
zoomOnScroll: false,
|
|
1126
|
-
zoomOnPinch: false,
|
|
1127
|
-
zoomOnDoubleClick: false,
|
|
1128
|
-
preventScrolling: false,
|
|
1129
|
-
nodesDraggable: false,
|
|
1130
|
-
nodesConnectable: false,
|
|
1131
|
-
elementsSelectable: !!onNodeClick,
|
|
1132
|
-
children: !unstyled && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react11.Background, { variant: import_react11.BackgroundVariant.Dots, gap: 16, size: 1 })
|
|
1133
|
-
}
|
|
1134
|
-
) })
|
|
1135
|
-
] })
|
|
1136
|
-
]
|
|
1137
|
-
}
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
1347
|
// src/components/TimeTravelDebugger/TimeTravelDebugger.tsx
|
|
1142
1348
|
var import_react15 = require("react");
|
|
1143
1349
|
|
|
@@ -1155,6 +1361,7 @@ function MemoryInspector({
|
|
|
1155
1361
|
className,
|
|
1156
1362
|
style
|
|
1157
1363
|
}) {
|
|
1364
|
+
const cacheRef = (0, import_react12.useRef)(null);
|
|
1158
1365
|
const { memory, newKeys } = (0, import_react12.useMemo)(() => {
|
|
1159
1366
|
if (data) {
|
|
1160
1367
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
@@ -1162,21 +1369,37 @@ function MemoryInspector({
|
|
|
1162
1369
|
if (!snapshots || snapshots.length === 0) {
|
|
1163
1370
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
1164
1371
|
}
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1372
|
+
const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
|
|
1373
|
+
let merged;
|
|
1374
|
+
const cache = cacheRef.current;
|
|
1375
|
+
if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
|
|
1376
|
+
merged = { ...cache.accumulated };
|
|
1377
|
+
for (let i = cache.index + 1; i <= safeIdx; i++) {
|
|
1378
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
1379
|
+
}
|
|
1380
|
+
} else {
|
|
1381
|
+
merged = {};
|
|
1382
|
+
for (let i = 0; i <= safeIdx; i++) {
|
|
1383
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
1384
|
+
}
|
|
1168
1385
|
}
|
|
1386
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
1169
1387
|
const nk = /* @__PURE__ */ new Set();
|
|
1170
|
-
if (highlightNew &&
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1388
|
+
if (highlightNew && safeIdx > 0) {
|
|
1389
|
+
let prev;
|
|
1390
|
+
if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
|
|
1391
|
+
prev = cache.accumulated;
|
|
1392
|
+
} else {
|
|
1393
|
+
prev = {};
|
|
1394
|
+
for (let i = 0; i < safeIdx; i++) {
|
|
1395
|
+
Object.assign(prev, snapshots[i]?.memory);
|
|
1396
|
+
}
|
|
1174
1397
|
}
|
|
1175
|
-
const current = snapshots[
|
|
1398
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
1176
1399
|
for (const k of Object.keys(current)) {
|
|
1177
1400
|
if (!(k in prev)) nk.add(k);
|
|
1178
1401
|
}
|
|
1179
|
-
} else if (highlightNew &&
|
|
1402
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
1180
1403
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
1181
1404
|
}
|
|
1182
1405
|
return { memory: merged, newKeys: nk };
|
|
@@ -1185,9 +1408,9 @@ function MemoryInspector({
|
|
|
1185
1408
|
const fs = fontSize[size];
|
|
1186
1409
|
const pad = padding[size];
|
|
1187
1410
|
if (unstyled) {
|
|
1188
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className, style, "data-fp": "memory-inspector", children: [
|
|
1411
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
|
|
1189
1412
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
1190
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
|
|
1413
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: JSON.stringify(memory, null, 2) }) })
|
|
1191
1414
|
] });
|
|
1192
1415
|
}
|
|
1193
1416
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
@@ -1200,6 +1423,8 @@ function MemoryInspector({
|
|
|
1200
1423
|
...style
|
|
1201
1424
|
},
|
|
1202
1425
|
"data-fp": "memory-inspector",
|
|
1426
|
+
role: "region",
|
|
1427
|
+
"aria-label": "Memory state",
|
|
1203
1428
|
children: [
|
|
1204
1429
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1205
1430
|
"span",
|
|
@@ -1467,12 +1692,15 @@ function GanttTimeline({
|
|
|
1467
1692
|
}
|
|
1468
1693
|
}, [selectedIndex, showAll]);
|
|
1469
1694
|
if (unstyled) {
|
|
1470
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1695
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1471
1696
|
"div",
|
|
1472
1697
|
{
|
|
1473
1698
|
"data-fp": "gantt-bar",
|
|
1474
1699
|
"data-selected": idx === selectedIndex,
|
|
1475
1700
|
"data-visible": idx <= selectedIndex,
|
|
1701
|
+
role: "option",
|
|
1702
|
+
"aria-selected": idx === selectedIndex,
|
|
1703
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
1476
1704
|
onClick: () => onSelect?.(idx),
|
|
1477
1705
|
children: [
|
|
1478
1706
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -1482,7 +1710,7 @@ function GanttTimeline({
|
|
|
1482
1710
|
] })
|
|
1483
1711
|
]
|
|
1484
1712
|
},
|
|
1485
|
-
snap.stageName
|
|
1713
|
+
`${snap.stageName}-${idx}`
|
|
1486
1714
|
)) });
|
|
1487
1715
|
}
|
|
1488
1716
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
@@ -1538,6 +1766,8 @@ function GanttTimeline({
|
|
|
1538
1766
|
"div",
|
|
1539
1767
|
{
|
|
1540
1768
|
ref: scrollContainerRef,
|
|
1769
|
+
role: "listbox",
|
|
1770
|
+
"aria-label": "Execution timeline",
|
|
1541
1771
|
style: {
|
|
1542
1772
|
marginTop: 8,
|
|
1543
1773
|
display: "flex",
|
|
@@ -1558,6 +1788,9 @@ function GanttTimeline({
|
|
|
1558
1788
|
"div",
|
|
1559
1789
|
{
|
|
1560
1790
|
ref: isSelected ? activeRowRef : void 0,
|
|
1791
|
+
role: "option",
|
|
1792
|
+
"aria-selected": isSelected,
|
|
1793
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
1561
1794
|
onClick: () => onSelect?.(idx),
|
|
1562
1795
|
style: {
|
|
1563
1796
|
display: "flex",
|
|
@@ -1573,6 +1806,7 @@ function GanttTimeline({
|
|
|
1573
1806
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1574
1807
|
"span",
|
|
1575
1808
|
{
|
|
1809
|
+
title: snap.stageLabel,
|
|
1576
1810
|
style: {
|
|
1577
1811
|
width: labelWidth,
|
|
1578
1812
|
fontSize: fs.small,
|
|
@@ -1632,7 +1866,7 @@ function GanttTimeline({
|
|
|
1632
1866
|
)
|
|
1633
1867
|
]
|
|
1634
1868
|
},
|
|
1635
|
-
snap.stageName
|
|
1869
|
+
`${snap.stageName}-${idx}`
|
|
1636
1870
|
);
|
|
1637
1871
|
})
|
|
1638
1872
|
}
|
|
@@ -1987,6 +2221,8 @@ function ScrubButton({
|
|
|
1987
2221
|
SubflowTree,
|
|
1988
2222
|
TimeTravelDebugger,
|
|
1989
2223
|
TracedFlowchartView,
|
|
2224
|
+
applyOverlay,
|
|
2225
|
+
specToLayout,
|
|
1990
2226
|
specToReactFlow,
|
|
1991
2227
|
useSubflowNavigation
|
|
1992
2228
|
});
|