footprint-explainable-ui 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/index.cjs +224 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +224 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -326,6 +326,12 @@ declare function StageDetailPanel({ snapshots, selectedIndex, mode: controlledMo
|
|
|
326
326
|
* is set, slice members stay landable ("stops"), everything else fades to
|
|
327
327
|
* unlandable ticks, and prev/next walk stop-to-stop ("◀ earlier cause").
|
|
328
328
|
* The cursor stays the ONE `selectedIndex` — no second position exists.
|
|
329
|
+
*
|
|
330
|
+
* THEMING: tracing mode recolors its chrome with ONE token, `--fp-tracing`
|
|
331
|
+
* (default #0d9488 teal) — the TRACING badge, the rail's bottom border, the
|
|
332
|
+
* stop ticks, the walk buttons, and Done — so the tracing rail is
|
|
333
|
+
* unmistakably not normal time-travel. Consumers retheme by setting
|
|
334
|
+
* `--fp-tracing` on any ancestor. Normal mode never reads the token.
|
|
329
335
|
*/
|
|
330
336
|
interface TracingRail {
|
|
331
337
|
/** The traced variable — rendered in the mode header. */
|
|
@@ -343,6 +349,14 @@ interface TracingRail {
|
|
|
343
349
|
onExit: () => void;
|
|
344
350
|
/** Clear the via filter back to the full walk (breadcrumb's "show all"). */
|
|
345
351
|
onShowAll?: () => void;
|
|
352
|
+
/** Ingredient count at the CURRENT stop. Together with `onForkPrompt`,
|
|
353
|
+
* >= 2 turns the walk-back control into "choose cause…": it PROMPTS
|
|
354
|
+
* instead of moving, so a silent default never hides one parent of a
|
|
355
|
+
* fork. 0/undefined = not a fork — classic earlier-cause behavior. */
|
|
356
|
+
forkCount?: number;
|
|
357
|
+
/** Open the fork chooser — fired INSTEAD of a cursor move when
|
|
358
|
+
* `forkCount >= 2` (click or ArrowLeft on the walk-back control). */
|
|
359
|
+
onForkPrompt?: () => void;
|
|
346
360
|
}
|
|
347
361
|
interface TimeTravelControlsProps extends BaseComponentProps {
|
|
348
362
|
/** Stage snapshots */
|
|
@@ -1265,6 +1279,17 @@ interface TraceWalkCardProps {
|
|
|
1265
1279
|
onJumpToStop?: (runtimeStageId: string) => void;
|
|
1266
1280
|
onShowAll?: () => void;
|
|
1267
1281
|
onExit?: () => void;
|
|
1282
|
+
/** F2 fork chooser: when true AND the current stop has 2+ ingredients,
|
|
1283
|
+
* a loud chooser block asks WHICH ingredient the walk should follow —
|
|
1284
|
+
* the rail's walk-back control opens it instead of moving. */
|
|
1285
|
+
forkChooserOpen?: boolean;
|
|
1286
|
+
/** The chooser's neutral option — today's behavior: step to the nearest
|
|
1287
|
+
* earlier stop in time. The SHELL computes the move; the card only
|
|
1288
|
+
* fires this. */
|
|
1289
|
+
onContinueTimeOrder?: () => void;
|
|
1290
|
+
/** False at the walk's earliest stop — there IS no earlier stop, so the
|
|
1291
|
+
* chooser's time-order button must not pretend to move (review fix). */
|
|
1292
|
+
canContinueTimeOrder?: boolean;
|
|
1268
1293
|
}
|
|
1269
1294
|
declare const TraceWalkCard: react.NamedExoticComponent<TraceWalkCardProps>;
|
|
1270
1295
|
|
package/dist/index.d.ts
CHANGED
|
@@ -326,6 +326,12 @@ declare function StageDetailPanel({ snapshots, selectedIndex, mode: controlledMo
|
|
|
326
326
|
* is set, slice members stay landable ("stops"), everything else fades to
|
|
327
327
|
* unlandable ticks, and prev/next walk stop-to-stop ("◀ earlier cause").
|
|
328
328
|
* The cursor stays the ONE `selectedIndex` — no second position exists.
|
|
329
|
+
*
|
|
330
|
+
* THEMING: tracing mode recolors its chrome with ONE token, `--fp-tracing`
|
|
331
|
+
* (default #0d9488 teal) — the TRACING badge, the rail's bottom border, the
|
|
332
|
+
* stop ticks, the walk buttons, and Done — so the tracing rail is
|
|
333
|
+
* unmistakably not normal time-travel. Consumers retheme by setting
|
|
334
|
+
* `--fp-tracing` on any ancestor. Normal mode never reads the token.
|
|
329
335
|
*/
|
|
330
336
|
interface TracingRail {
|
|
331
337
|
/** The traced variable — rendered in the mode header. */
|
|
@@ -343,6 +349,14 @@ interface TracingRail {
|
|
|
343
349
|
onExit: () => void;
|
|
344
350
|
/** Clear the via filter back to the full walk (breadcrumb's "show all"). */
|
|
345
351
|
onShowAll?: () => void;
|
|
352
|
+
/** Ingredient count at the CURRENT stop. Together with `onForkPrompt`,
|
|
353
|
+
* >= 2 turns the walk-back control into "choose cause…": it PROMPTS
|
|
354
|
+
* instead of moving, so a silent default never hides one parent of a
|
|
355
|
+
* fork. 0/undefined = not a fork — classic earlier-cause behavior. */
|
|
356
|
+
forkCount?: number;
|
|
357
|
+
/** Open the fork chooser — fired INSTEAD of a cursor move when
|
|
358
|
+
* `forkCount >= 2` (click or ArrowLeft on the walk-back control). */
|
|
359
|
+
onForkPrompt?: () => void;
|
|
346
360
|
}
|
|
347
361
|
interface TimeTravelControlsProps extends BaseComponentProps {
|
|
348
362
|
/** Stage snapshots */
|
|
@@ -1265,6 +1279,17 @@ interface TraceWalkCardProps {
|
|
|
1265
1279
|
onJumpToStop?: (runtimeStageId: string) => void;
|
|
1266
1280
|
onShowAll?: () => void;
|
|
1267
1281
|
onExit?: () => void;
|
|
1282
|
+
/** F2 fork chooser: when true AND the current stop has 2+ ingredients,
|
|
1283
|
+
* a loud chooser block asks WHICH ingredient the walk should follow —
|
|
1284
|
+
* the rail's walk-back control opens it instead of moving. */
|
|
1285
|
+
forkChooserOpen?: boolean;
|
|
1286
|
+
/** The chooser's neutral option — today's behavior: step to the nearest
|
|
1287
|
+
* earlier stop in time. The SHELL computes the move; the card only
|
|
1288
|
+
* fires this. */
|
|
1289
|
+
onContinueTimeOrder?: () => void;
|
|
1290
|
+
/** False at the walk's earliest stop — there IS no earlier stop, so the
|
|
1291
|
+
* chooser's time-order button must not pretend to move (review fix). */
|
|
1292
|
+
canContinueTimeOrder?: boolean;
|
|
1268
1293
|
}
|
|
1269
1294
|
declare const TraceWalkCard: react.NamedExoticComponent<TraceWalkCardProps>;
|
|
1270
1295
|
|
package/dist/index.js
CHANGED
|
@@ -2014,14 +2014,19 @@ function TimeTravelControls({
|
|
|
2014
2014
|
if (!tracing) return null;
|
|
2015
2015
|
return tracing.stopIndices.find((i) => i > selectedIndex) ?? null;
|
|
2016
2016
|
}, [tracing, selectedIndex]);
|
|
2017
|
-
const
|
|
2017
|
+
const forkPrompt = isTracing && (tracing.forkCount ?? 0) >= 2 && !!tracing.onForkPrompt;
|
|
2018
|
+
const canPrev = isTracing ? forkPrompt || earlierStop !== null : selectedIndex > 0;
|
|
2018
2019
|
const canNext = isTracing ? laterStop !== null : selectedIndex < total - 1;
|
|
2019
2020
|
const goPrev = useCallback3(() => {
|
|
2020
2021
|
setPlaying(false);
|
|
2021
2022
|
if (isTracing) {
|
|
2023
|
+
if (forkPrompt) {
|
|
2024
|
+
tracing.onForkPrompt();
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2022
2027
|
if (earlierStop !== null) onIndexChange(earlierStop);
|
|
2023
2028
|
} else if (selectedIndex > 0) onIndexChange(selectedIndex - 1);
|
|
2024
|
-
}, [isTracing, earlierStop, selectedIndex, onIndexChange]);
|
|
2029
|
+
}, [isTracing, forkPrompt, tracing, earlierStop, selectedIndex, onIndexChange]);
|
|
2025
2030
|
const goNext = useCallback3(() => {
|
|
2026
2031
|
setPlaying(false);
|
|
2027
2032
|
if (isTracing) {
|
|
@@ -2073,7 +2078,7 @@ function TimeTravelControls({
|
|
|
2073
2078
|
[canPrev, canNext, playing, goPrev, goNext, autoPlayable, togglePlay, isTracing, tracing]
|
|
2074
2079
|
);
|
|
2075
2080
|
const fs = fontSize[size];
|
|
2076
|
-
const
|
|
2081
|
+
const tracingColor = "var(--fp-tracing, #0d9488)";
|
|
2077
2082
|
if (unstyled) {
|
|
2078
2083
|
return /* @__PURE__ */ jsxs9(
|
|
2079
2084
|
"div",
|
|
@@ -2110,8 +2115,8 @@ function TimeTravelControls({
|
|
|
2110
2115
|
"data-fp": "tt-prev",
|
|
2111
2116
|
disabled: !canPrev || playing,
|
|
2112
2117
|
onClick: goPrev,
|
|
2113
|
-
"aria-label": isTracing ? "Earlier cause" : "Previous stage",
|
|
2114
|
-
children: isTracing ? "Earlier cause" : "Prev"
|
|
2118
|
+
"aria-label": isTracing ? forkPrompt ? "Choose cause" : "Earlier cause" : "Previous stage",
|
|
2119
|
+
children: isTracing ? forkPrompt ? "Choose cause\u2026" : "Earlier cause" : "Prev"
|
|
2115
2120
|
}
|
|
2116
2121
|
),
|
|
2117
2122
|
autoPlayable && !isTracing && /* @__PURE__ */ jsx10("button", { "data-fp": "tt-play", onClick: togglePlay, "aria-label": playing ? "Pause" : "Play", children: playing ? "Pause" : "Play" }),
|
|
@@ -2150,8 +2155,8 @@ function TimeTravelControls({
|
|
|
2150
2155
|
}
|
|
2151
2156
|
const btnStyle = (disabled) => ({
|
|
2152
2157
|
background: theme.bgTertiary,
|
|
2153
|
-
border: `1px solid ${theme.border}`,
|
|
2154
|
-
color: disabled ? theme.textMuted : theme.textPrimary,
|
|
2158
|
+
border: `1px solid ${isTracing ? tracingColor : theme.border}`,
|
|
2159
|
+
color: disabled ? theme.textMuted : isTracing ? tracingColor : theme.textPrimary,
|
|
2155
2160
|
borderRadius: "6px",
|
|
2156
2161
|
padding: "4px 12px",
|
|
2157
2162
|
fontSize: fs.body,
|
|
@@ -2167,7 +2172,7 @@ function TimeTravelControls({
|
|
|
2167
2172
|
style: {
|
|
2168
2173
|
padding: "6px 12px",
|
|
2169
2174
|
background: theme.bgSecondary,
|
|
2170
|
-
borderBottom: isTracing ? `2px solid ${
|
|
2175
|
+
borderBottom: isTracing ? `2px solid ${tracingColor}` : `1px solid ${theme.border}`,
|
|
2171
2176
|
display: "flex",
|
|
2172
2177
|
alignItems: "center",
|
|
2173
2178
|
gap: 6,
|
|
@@ -2202,14 +2207,14 @@ function TimeTravelControls({
|
|
|
2202
2207
|
letterSpacing: "0.06em",
|
|
2203
2208
|
textTransform: "uppercase",
|
|
2204
2209
|
color: "#fff",
|
|
2205
|
-
background:
|
|
2210
|
+
background: tracingColor,
|
|
2206
2211
|
borderRadius: 4,
|
|
2207
2212
|
padding: "2px 7px"
|
|
2208
2213
|
},
|
|
2209
2214
|
children: "Tracing"
|
|
2210
2215
|
}
|
|
2211
2216
|
),
|
|
2212
|
-
/* @__PURE__ */ jsx10("span", { style: { fontFamily: "monospace", fontWeight: 600, color:
|
|
2217
|
+
/* @__PURE__ */ jsx10("span", { style: { fontFamily: "monospace", fontWeight: 600, color: tracingColor }, children: tracing.tracedKey }),
|
|
2213
2218
|
tracing.viaKey && /* @__PURE__ */ jsxs9("span", { style: { color: theme.textMuted }, children: [
|
|
2214
2219
|
"\u25B8 via",
|
|
2215
2220
|
" ",
|
|
@@ -2223,7 +2228,7 @@ function TimeTravelControls({
|
|
|
2223
2228
|
style: {
|
|
2224
2229
|
border: "none",
|
|
2225
2230
|
background: "transparent",
|
|
2226
|
-
color:
|
|
2231
|
+
color: tracingColor,
|
|
2227
2232
|
cursor: "pointer",
|
|
2228
2233
|
fontSize: fs.body,
|
|
2229
2234
|
textDecoration: "underline",
|
|
@@ -2248,10 +2253,10 @@ function TimeTravelControls({
|
|
|
2248
2253
|
style: btnStyle(!canPrev || playing),
|
|
2249
2254
|
disabled: !canPrev || playing,
|
|
2250
2255
|
onClick: goPrev,
|
|
2251
|
-
"aria-label": isTracing ? "Earlier cause" : "Previous stage",
|
|
2252
|
-
title: isTracing ? "Earlier cause" : "Previous stage",
|
|
2256
|
+
"aria-label": isTracing ? forkPrompt ? "Choose cause" : "Earlier cause" : "Previous stage",
|
|
2257
|
+
title: isTracing ? forkPrompt ? "This stop is a fork \u2014 choose which cause to follow" : "Earlier cause" : "Previous stage",
|
|
2253
2258
|
"data-fp": "tt-prev",
|
|
2254
|
-
children: isTracing ? "\u25C0 earlier cause" : "\u25C0"
|
|
2259
|
+
children: isTracing ? forkPrompt ? "\u2442 choose cause\u2026" : "\u25C0 earlier cause" : "\u25C0"
|
|
2255
2260
|
}
|
|
2256
2261
|
),
|
|
2257
2262
|
autoPlayable && !isTracing && /* @__PURE__ */ jsx10(
|
|
@@ -2322,7 +2327,7 @@ function TimeTravelControls({
|
|
|
2322
2327
|
borderRadius: 3,
|
|
2323
2328
|
border: "none",
|
|
2324
2329
|
cursor: unlandable ? "default" : "pointer",
|
|
2325
|
-
background: isTracing ? isActive ?
|
|
2330
|
+
background: isTracing ? isActive ? tracingColor : isStop ? "color-mix(in srgb, " + tracingColor + " 55%, transparent)" : theme.bgTertiary : isActive ? theme.primary : isDone ? theme.success : theme.bgTertiary,
|
|
2326
2331
|
opacity: unlandable ? 0.3 : isTracing || isDone || isActive ? 1 : 0.4,
|
|
2327
2332
|
transition: "all 0.15s ease"
|
|
2328
2333
|
}
|
|
@@ -2573,16 +2578,22 @@ var TraceWalkCard = memo(function TraceWalkCard2({
|
|
|
2573
2578
|
onFollowIngredient,
|
|
2574
2579
|
onJumpToStop,
|
|
2575
2580
|
onShowAll,
|
|
2576
|
-
onExit
|
|
2581
|
+
onExit,
|
|
2582
|
+
forkChooserOpen,
|
|
2583
|
+
onContinueTimeOrder,
|
|
2584
|
+
canContinueTimeOrder = true
|
|
2577
2585
|
}) {
|
|
2578
2586
|
const [copied, setCopied] = useState7(false);
|
|
2579
2587
|
const accent = "var(--fp-accent, #6366f1)";
|
|
2588
|
+
const tracingColor = "var(--fp-tracing, #0d9488)";
|
|
2580
2589
|
const currentIdx = useMemo8(() => {
|
|
2581
2590
|
if (!cursorRuntimeStageId) return 0;
|
|
2582
2591
|
const i = walk.stops.findIndex((s) => s.runtimeStageId === cursorRuntimeStageId);
|
|
2583
2592
|
return i >= 0 ? i : 0;
|
|
2584
2593
|
}, [walk, cursorRuntimeStageId]);
|
|
2585
2594
|
const current = walk.stops[currentIdx];
|
|
2595
|
+
const followableCount = current ? current.ingredients.filter((ing) => ing.writerRuntimeStageId !== null).length : 0;
|
|
2596
|
+
const chooserVisible = !!forkChooserOpen && followableCount >= 2;
|
|
2586
2597
|
const copyStory = () => {
|
|
2587
2598
|
const text = formatTraceWalk(walk, stepNumberOf);
|
|
2588
2599
|
void navigator.clipboard?.writeText(text).then(() => {
|
|
@@ -2668,7 +2679,60 @@ var TraceWalkCard = memo(function TraceWalkCard2({
|
|
|
2668
2679
|
preview
|
|
2669
2680
|
] })
|
|
2670
2681
|
] }),
|
|
2671
|
-
|
|
2682
|
+
chooserVisible && /* @__PURE__ */ jsxs10(
|
|
2683
|
+
"div",
|
|
2684
|
+
{
|
|
2685
|
+
"data-fp": "twc-fork-chooser",
|
|
2686
|
+
style: {
|
|
2687
|
+
marginTop: 10,
|
|
2688
|
+
padding: "10px 12px",
|
|
2689
|
+
border: `1.5px solid ${tracingColor}`,
|
|
2690
|
+
borderRadius: 8,
|
|
2691
|
+
background: `color-mix(in srgb, ${tracingColor} 8%, transparent)`
|
|
2692
|
+
},
|
|
2693
|
+
children: [
|
|
2694
|
+
/* @__PURE__ */ jsxs10("div", { style: { fontWeight: 600, fontSize: 12, color: theme.textPrimary }, children: [
|
|
2695
|
+
"This value was made from ",
|
|
2696
|
+
current.ingredients.length,
|
|
2697
|
+
" ingredients \u2014 which one should the walk follow?"
|
|
2698
|
+
] }),
|
|
2699
|
+
/* @__PURE__ */ jsx11("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }, children: current.ingredients.map((ing, i) => /* @__PURE__ */ jsx11(
|
|
2700
|
+
IngredientChip,
|
|
2701
|
+
{
|
|
2702
|
+
ing,
|
|
2703
|
+
color: i < CHIP_COLORS.length ? CHIP_COLORS[i] : theme.textMuted,
|
|
2704
|
+
step: ing.writerRuntimeStageId ? stepNumberOf(ing.writerRuntimeStageId) : null,
|
|
2705
|
+
onFollow: onFollowIngredient
|
|
2706
|
+
},
|
|
2707
|
+
ing.key
|
|
2708
|
+
)) }),
|
|
2709
|
+
/* @__PURE__ */ jsx11(
|
|
2710
|
+
"button",
|
|
2711
|
+
{
|
|
2712
|
+
"data-fp": "twc-continue-time",
|
|
2713
|
+
onClick: canContinueTimeOrder ? onContinueTimeOrder : void 0,
|
|
2714
|
+
disabled: !canContinueTimeOrder,
|
|
2715
|
+
title: canContinueTimeOrder ? void 0 : "This is the walk's earliest stop \u2014 there is nothing earlier to visit",
|
|
2716
|
+
style: {
|
|
2717
|
+
display: "block",
|
|
2718
|
+
width: "100%",
|
|
2719
|
+
marginTop: 8,
|
|
2720
|
+
border: `1px solid ${theme.border}`,
|
|
2721
|
+
background: theme.bgTertiary,
|
|
2722
|
+
color: theme.textPrimary,
|
|
2723
|
+
borderRadius: 6,
|
|
2724
|
+
padding: "5px 10px",
|
|
2725
|
+
fontSize: 11,
|
|
2726
|
+
fontWeight: 600,
|
|
2727
|
+
cursor: "pointer"
|
|
2728
|
+
},
|
|
2729
|
+
children: "visit all, oldest cause last (time order)"
|
|
2730
|
+
}
|
|
2731
|
+
)
|
|
2732
|
+
]
|
|
2733
|
+
}
|
|
2734
|
+
),
|
|
2735
|
+
/* @__PURE__ */ jsx11("div", { style: { marginTop: 10 }, children: chooserVisible ? null : current.ingredients.length > 0 ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
2672
2736
|
/* @__PURE__ */ jsxs10("div", { style: { fontSize: 11, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.5px", fontWeight: 600 }, children: [
|
|
2673
2737
|
"Made from ",
|
|
2674
2738
|
current.ingredients.length,
|
|
@@ -6805,6 +6869,11 @@ function ExplainableShell({
|
|
|
6805
6869
|
const [rightPanelMode, setRightPanelMode] = useState15("insights");
|
|
6806
6870
|
const [inspectorTab, setInspectorTab] = useState15("state");
|
|
6807
6871
|
const [tracing, setTracing] = useState15(null);
|
|
6872
|
+
const [forkChooserOpen, setForkChooserOpen] = useState15(false);
|
|
6873
|
+
const [traceSearch, setTraceSearch] = useState15("");
|
|
6874
|
+
useEffect11(() => {
|
|
6875
|
+
setForkChooserOpen(false);
|
|
6876
|
+
}, [snapshotIdx]);
|
|
6808
6877
|
const [leftExpanded, setLeftExpanded] = useState15(defaultExpanded?.topology ?? false);
|
|
6809
6878
|
const [timelineExpanded, setTimelineExpanded] = useState15(defaultExpanded?.timeline ?? false);
|
|
6810
6879
|
useEffect11(() => {
|
|
@@ -6844,6 +6913,21 @@ function ExplainableShell({
|
|
|
6844
6913
|
() => runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, activeSnapshots[safeIdx]?.runtimeStageId ?? "") : { frames: [], readsAvailable: true },
|
|
6845
6914
|
[runtimeSnapshot, activeSnapshots, safeIdx]
|
|
6846
6915
|
);
|
|
6916
|
+
const allTracedKeys = useMemo14(() => {
|
|
6917
|
+
const log = runtimeSnapshot?.commitLog;
|
|
6918
|
+
if (!log?.length) return [];
|
|
6919
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6920
|
+
const keys = [];
|
|
6921
|
+
for (const c of log) {
|
|
6922
|
+
for (const t of c.trace ?? []) {
|
|
6923
|
+
if (!seen.has(t.path)) {
|
|
6924
|
+
seen.add(t.path);
|
|
6925
|
+
keys.push(t.path);
|
|
6926
|
+
}
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
return keys;
|
|
6930
|
+
}, [runtimeSnapshot]);
|
|
6847
6931
|
const traceWalk = useMemo14(() => {
|
|
6848
6932
|
if (!tracing || !runtimeSnapshot?.commitLog) return null;
|
|
6849
6933
|
const scope = tracing.via.length > 0 ? tracing.via[tracing.via.length - 1] : tracing;
|
|
@@ -6917,6 +7001,8 @@ function ExplainableShell({
|
|
|
6917
7001
|
const cursorCommitIdx = log.findIndex((c) => c.runtimeStageId === cursorRsid);
|
|
6918
7002
|
const beforeCommitIdx = cursorCommitIdx >= 0 ? cursorCommitIdx + 1 : void 0;
|
|
6919
7003
|
setTracing({ key, beforeCommitIdx, via: [] });
|
|
7004
|
+
setForkChooserOpen(false);
|
|
7005
|
+
setTraceSearch("");
|
|
6920
7006
|
setRightPanelMode("what");
|
|
6921
7007
|
setInspectorTab("trace");
|
|
6922
7008
|
jumpToAnchor(
|
|
@@ -6930,6 +7016,7 @@ function ExplainableShell({
|
|
|
6930
7016
|
if (!tracing || !runtimeSnapshot?.commitLog || ing.writerCommitIdx === null) return;
|
|
6931
7017
|
const scope = { key: ing.key, beforeCommitIdx: ing.writerCommitIdx + 1 };
|
|
6932
7018
|
setTracing({ ...tracing, via: [...tracing.via, scope] });
|
|
7019
|
+
setForkChooserOpen(false);
|
|
6933
7020
|
jumpToAnchor(
|
|
6934
7021
|
buildTraceWalk(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, scope.key, {
|
|
6935
7022
|
beforeCommitIdx: scope.beforeCommitIdx
|
|
@@ -6941,18 +7028,29 @@ function ExplainableShell({
|
|
|
6941
7028
|
const handleShowAllIngredients = useCallback8(() => {
|
|
6942
7029
|
if (!tracing || !runtimeSnapshot?.commitLog) return;
|
|
6943
7030
|
setTracing({ ...tracing, via: [] });
|
|
7031
|
+
setForkChooserOpen(false);
|
|
6944
7032
|
jumpToAnchor(
|
|
6945
7033
|
buildTraceWalk(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, tracing.key, {
|
|
6946
7034
|
beforeCommitIdx: tracing.beforeCommitIdx
|
|
6947
7035
|
})
|
|
6948
7036
|
);
|
|
6949
7037
|
}, [tracing, runtimeSnapshot, jumpToAnchor]);
|
|
6950
|
-
const handleExitTracing = useCallback8(() =>
|
|
7038
|
+
const handleExitTracing = useCallback8(() => {
|
|
7039
|
+
setTracing(null);
|
|
7040
|
+
setForkChooserOpen(false);
|
|
7041
|
+
}, []);
|
|
7042
|
+
const handleForkPrompt = useCallback8(() => setForkChooserOpen(true), []);
|
|
7043
|
+
const handleContinueTimeOrder = useCallback8(() => {
|
|
7044
|
+
const earlier = traceStopIndices.filter((i) => i < safeIdx);
|
|
7045
|
+
if (earlier.length > 0) setSnapshotIdx(earlier[earlier.length - 1]);
|
|
7046
|
+
setForkChooserOpen(false);
|
|
7047
|
+
}, [traceStopIndices, safeIdx]);
|
|
6951
7048
|
const handleDrillDown = useCallback8(
|
|
6952
7049
|
(nodeName) => {
|
|
6953
7050
|
const entry = resolveSubflowFromRuntime(activeSnapshots, nodeName, narrativeEntries);
|
|
6954
7051
|
if (entry) {
|
|
6955
7052
|
setTracing(null);
|
|
7053
|
+
setForkChooserOpen(false);
|
|
6956
7054
|
setDrillDownStack((prev) => [...prev, { ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
6957
7055
|
setSnapshotIdx(0);
|
|
6958
7056
|
}
|
|
@@ -7018,6 +7116,7 @@ function ExplainableShell({
|
|
|
7018
7116
|
if (!tracing || !traceWalk || traceWalk.missing || traceStopIndices.length === 0) return null;
|
|
7019
7117
|
const cursorRsid = activeSnapshots[safeIdx]?.runtimeStageId;
|
|
7020
7118
|
const walkIdx = traceWalk.stops.findIndex((st) => st.runtimeStageId === cursorRsid);
|
|
7119
|
+
const currentStop = traceWalk.stops[walkIdx >= 0 ? walkIdx : 0];
|
|
7021
7120
|
return {
|
|
7022
7121
|
tracedKey: tracing.key,
|
|
7023
7122
|
viaKey: activeViaKey,
|
|
@@ -7025,59 +7124,116 @@ function ExplainableShell({
|
|
|
7025
7124
|
stopOrdinal: walkIdx >= 0 ? walkIdx + 1 : 1,
|
|
7026
7125
|
totalStops: traceWalk.stops.length,
|
|
7027
7126
|
onExit: handleExitTracing,
|
|
7028
|
-
onShowAll: activeViaKey ? handleShowAllIngredients : void 0
|
|
7029
|
-
};
|
|
7030
|
-
}, [tracing, traceWalk, traceStopIndices, activeSnapshots, safeIdx, activeViaKey, handleExitTracing, handleShowAllIngredients]);
|
|
7031
|
-
const traceTabContent = useMemo14(() => tracing && traceWalk ? /* @__PURE__ */ jsx27(
|
|
7032
|
-
TraceWalkCard,
|
|
7033
|
-
{
|
|
7034
|
-
walk: traceWalk,
|
|
7035
|
-
cursorRuntimeStageId: activeSnapshots[safeIdx]?.runtimeStageId ?? null,
|
|
7036
|
-
viaKey: activeViaKey,
|
|
7037
|
-
stepNumberOf,
|
|
7038
|
-
previewValueOf: (k) => activeSnapshots[safeIdx]?.memory?.[k],
|
|
7039
|
-
onFollowIngredient: handleFollowIngredient,
|
|
7040
|
-
onJumpToStop: navigateToStage,
|
|
7041
7127
|
onShowAll: activeViaKey ? handleShowAllIngredients : void 0,
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7128
|
+
// Followable ingredients only — termini can't be chosen, so a stop of
|
|
7129
|
+
// run-inputs must not prompt (matches the card's chooser gate).
|
|
7130
|
+
forkCount: currentStop?.ingredients.filter((ing) => ing.writerRuntimeStageId !== null).length ?? 0,
|
|
7131
|
+
onForkPrompt: handleForkPrompt
|
|
7132
|
+
};
|
|
7133
|
+
}, [tracing, traceWalk, traceStopIndices, activeSnapshots, safeIdx, activeViaKey, handleExitTracing, handleShowAllIngredients, handleForkPrompt]);
|
|
7134
|
+
const traceTabContent = useMemo14(() => {
|
|
7135
|
+
if (tracing && traceWalk) {
|
|
7136
|
+
return /* @__PURE__ */ jsx27(
|
|
7137
|
+
TraceWalkCard,
|
|
7049
7138
|
{
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7139
|
+
walk: traceWalk,
|
|
7140
|
+
cursorRuntimeStageId: activeSnapshots[safeIdx]?.runtimeStageId ?? null,
|
|
7141
|
+
viaKey: activeViaKey,
|
|
7142
|
+
stepNumberOf,
|
|
7143
|
+
previewValueOf: (k) => activeSnapshots[safeIdx]?.memory?.[k],
|
|
7144
|
+
onFollowIngredient: handleFollowIngredient,
|
|
7145
|
+
onJumpToStop: navigateToStage,
|
|
7146
|
+
onShowAll: activeViaKey ? handleShowAllIngredients : void 0,
|
|
7147
|
+
onExit: handleExitTracing,
|
|
7148
|
+
forkChooserOpen,
|
|
7149
|
+
onContinueTimeOrder: handleContinueTimeOrder,
|
|
7150
|
+
canContinueTimeOrder: traceStopIndices.some((i) => i < safeIdx)
|
|
7151
|
+
}
|
|
7152
|
+
);
|
|
7153
|
+
}
|
|
7154
|
+
const chipStyle = {
|
|
7155
|
+
border: "1px solid var(--fp-accent, #6366f1)",
|
|
7156
|
+
background: "transparent",
|
|
7157
|
+
color: "var(--fp-accent, #6366f1)",
|
|
7158
|
+
borderRadius: 12,
|
|
7159
|
+
padding: "2px 10px",
|
|
7160
|
+
margin: "0 6px 6px 0",
|
|
7161
|
+
fontSize: 11,
|
|
7162
|
+
fontWeight: 600,
|
|
7163
|
+
fontFamily: "monospace",
|
|
7164
|
+
cursor: "pointer"
|
|
7165
|
+
};
|
|
7166
|
+
const query = traceSearch.trim().toLowerCase();
|
|
7167
|
+
const matchedKeys = query ? allTracedKeys.filter((k) => k.toLowerCase().includes(query)) : allTracedKeys;
|
|
7168
|
+
const shownKeys = matchedKeys.slice(0, 12);
|
|
7169
|
+
return /* @__PURE__ */ jsxs24(Fragment8, { children: [
|
|
7170
|
+
!isInSubflow && (shellDataTrace.frames[0]?.keysWritten?.length ?? 0) > 0 && /* @__PURE__ */ jsxs24("div", { "data-fp": "trace-entry", style: { padding: "10px 14px 0", fontSize: 12 }, children: [
|
|
7171
|
+
/* @__PURE__ */ jsx27("span", { style: { color: theme.textMuted, marginRight: 6 }, children: "This step wrote:" }),
|
|
7172
|
+
shellDataTrace.frames[0].keysWritten.map((k) => /* @__PURE__ */ jsx27(
|
|
7173
|
+
"button",
|
|
7174
|
+
{
|
|
7175
|
+
"data-fp": "trace-entry-chip",
|
|
7176
|
+
onClick: () => handleStartTracing(k),
|
|
7177
|
+
title: "Where did " + k + " come from? Walk its causes on the timeline.",
|
|
7178
|
+
style: chipStyle,
|
|
7179
|
+
children: k
|
|
7064
7180
|
},
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7181
|
+
k
|
|
7182
|
+
))
|
|
7183
|
+
] }),
|
|
7184
|
+
!isInSubflow && allTracedKeys.length > 0 && /* @__PURE__ */ jsxs24("div", { "data-fp": "trace-any", style: { padding: "6px 14px 0", fontSize: 12 }, children: [
|
|
7185
|
+
/* @__PURE__ */ jsx27("div", { style: { color: theme.textMuted, marginBottom: 4 }, children: "Trace any variable:" }),
|
|
7186
|
+
/* @__PURE__ */ jsx27(
|
|
7187
|
+
"input",
|
|
7188
|
+
{
|
|
7189
|
+
"data-fp": "trace-search",
|
|
7190
|
+
value: traceSearch,
|
|
7191
|
+
onChange: (e) => setTraceSearch(e.target.value),
|
|
7192
|
+
placeholder: "search any variable...",
|
|
7193
|
+
style: {
|
|
7194
|
+
display: "block",
|
|
7195
|
+
width: "100%",
|
|
7196
|
+
boxSizing: "border-box",
|
|
7197
|
+
background: theme.bgTertiary,
|
|
7198
|
+
border: `1px solid ${theme.border}`,
|
|
7199
|
+
borderRadius: 6,
|
|
7200
|
+
color: theme.textPrimary,
|
|
7201
|
+
fontSize: 11,
|
|
7202
|
+
fontFamily: "monospace",
|
|
7203
|
+
padding: "4px 8px",
|
|
7204
|
+
marginBottom: 6
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7207
|
+
),
|
|
7208
|
+
shownKeys.map((k) => /* @__PURE__ */ jsx27(
|
|
7209
|
+
"button",
|
|
7210
|
+
{
|
|
7211
|
+
"data-fp": "trace-any-chip",
|
|
7212
|
+
onClick: () => handleStartTracing(k),
|
|
7213
|
+
title: "Where did " + k + " come from? Walk its causes on the timeline.",
|
|
7214
|
+
style: chipStyle,
|
|
7215
|
+
children: k
|
|
7216
|
+
},
|
|
7217
|
+
k
|
|
7218
|
+
)),
|
|
7219
|
+
query === "" && matchedKeys.length > shownKeys.length && /* @__PURE__ */ jsxs24("span", { style: { color: theme.textMuted, fontSize: 11 }, children: [
|
|
7220
|
+
"+",
|
|
7221
|
+
matchedKeys.length - shownKeys.length,
|
|
7222
|
+
" more \u2014 type to search"
|
|
7223
|
+
] })
|
|
7224
|
+
] }),
|
|
7225
|
+
/* @__PURE__ */ jsx27(
|
|
7226
|
+
DataTracePanel,
|
|
7227
|
+
{
|
|
7228
|
+
frames: shellDataTrace.frames,
|
|
7229
|
+
note: shellDataTrace.readsAvailable ? void 0 : "\u26A0 reads were not recorded (readTracking off) \u2014 dependencies are unknowable, not absent.",
|
|
7230
|
+
selectedStageId: activeSnapshots[safeIdx]?.runtimeStageId,
|
|
7231
|
+
onFrameClick: navigateToStage,
|
|
7232
|
+
fromStageName: activeSnapshots[safeIdx]?.stageName
|
|
7233
|
+
}
|
|
7234
|
+
)
|
|
7235
|
+
] });
|
|
7236
|
+
}, [tracing, traceWalk, activeSnapshots, safeIdx, activeViaKey, stepNumberOf, handleFollowIngredient, navigateToStage, handleShowAllIngredients, handleExitTracing, handleStartTracing, isInSubflow, shellDataTrace, forkChooserOpen, handleContinueTimeOrder, traceStopIndices, traceSearch, allTracedKeys]);
|
|
7081
7237
|
const tabLabels = new Map(allTabs.map((t) => [t.id, t.name]));
|
|
7082
7238
|
if (unstyled) {
|
|
7083
7239
|
return /* @__PURE__ */ jsxs24("div", { className, style, "data-fp": "explainable-shell", children: [
|