footprint-explainable-ui 0.26.2 → 0.27.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/dist/index.cjs +84 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -9
- package/dist/index.d.ts +20 -9
- package/dist/index.js +84 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -625,10 +625,17 @@ interface RecorderView {
|
|
|
625
625
|
}) => React.ReactNode;
|
|
626
626
|
}
|
|
627
627
|
/**
|
|
628
|
-
* The Trace flowchart's two-colour theme (footprintjs level).
|
|
629
|
-
*
|
|
630
|
-
* `
|
|
631
|
-
*
|
|
628
|
+
* The Trace flowchart's two-colour theme (footprintjs level).
|
|
629
|
+
*
|
|
630
|
+
* `mode` is the COARSE switch: it applies eui's full light or dark preset
|
|
631
|
+
* (`coolLight` / `coolDark`) as `--fp-*` variables on the shell root, so the
|
|
632
|
+
* ENTIRE shell — canvas, panels, nodes, text, borders — follows dark/light from
|
|
633
|
+
* this one field. You do NOT need to hand-set `--fp-*` yourself. (`--fp-*`
|
|
634
|
+
* remains available as a fine escape hatch for individual token overrides.)
|
|
635
|
+
*
|
|
636
|
+
* `visited` and `current` are the two semantic node colours, layered on top of
|
|
637
|
+
* the mode base. All optional — sensible per-mode defaults are used for anything
|
|
638
|
+
* omitted.
|
|
632
639
|
*/
|
|
633
640
|
interface TraceTheme {
|
|
634
641
|
mode?: "dark" | "light";
|
|
@@ -676,11 +683,11 @@ interface ExplainableShellProps extends BaseComponentProps {
|
|
|
676
683
|
runtimeOverlay?: RuntimeOverlay | null;
|
|
677
684
|
/**
|
|
678
685
|
* Trace flowchart theme — the footprintjs-LEVEL **two-colour** scheme:
|
|
679
|
-
* `visited` (executed nodes) + `current` (the cursor node). `mode`
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
* use the per-mode defaults. The agent-semantic three-colour theme
|
|
683
|
-
* `<Lens>`, not here.
|
|
686
|
+
* `visited` (executed nodes) + `current` (the cursor node). `mode` is the
|
|
687
|
+
* coarse light/dark switch — it applies eui's full preset to the whole shell,
|
|
688
|
+
* so you pass one word instead of a wall of `--fp-*` vars. Colours are optional
|
|
689
|
+
* — omit to use the per-mode defaults. The agent-semantic three-colour theme
|
|
690
|
+
* belongs to `<Lens>`, not here.
|
|
684
691
|
*/
|
|
685
692
|
traceTheme?: TraceTheme;
|
|
686
693
|
title?: string;
|
|
@@ -1075,6 +1082,8 @@ interface DataTracePanelProps {
|
|
|
1075
1082
|
onFrameClick?: (runtimeStageId: string) => void;
|
|
1076
1083
|
/** Optional: stage name for the "tracing from" header. */
|
|
1077
1084
|
fromStageName?: string;
|
|
1085
|
+
/** Optional honesty line rendered under the header (⚠-style). */
|
|
1086
|
+
note?: string;
|
|
1078
1087
|
}
|
|
1079
1088
|
/**
|
|
1080
1089
|
* Render the backward causal chain as a stack trace.
|
|
@@ -1088,6 +1097,8 @@ interface InspectorPanelProps {
|
|
|
1088
1097
|
selectedIndex: number;
|
|
1089
1098
|
/** Causal chain frames for the selected node (empty = no trace available). */
|
|
1090
1099
|
dataTraceFrames: CausalFrame[];
|
|
1100
|
+
/** Optional honesty line for the Data Trace tab (e.g. reads not recorded). */
|
|
1101
|
+
dataTraceNote?: string;
|
|
1091
1102
|
/** Currently selected runtimeStageId. */
|
|
1092
1103
|
selectedStageId?: string;
|
|
1093
1104
|
/** Navigate to a stage when clicking a Data Trace frame. */
|
package/dist/index.d.ts
CHANGED
|
@@ -625,10 +625,17 @@ interface RecorderView {
|
|
|
625
625
|
}) => React.ReactNode;
|
|
626
626
|
}
|
|
627
627
|
/**
|
|
628
|
-
* The Trace flowchart's two-colour theme (footprintjs level).
|
|
629
|
-
*
|
|
630
|
-
* `
|
|
631
|
-
*
|
|
628
|
+
* The Trace flowchart's two-colour theme (footprintjs level).
|
|
629
|
+
*
|
|
630
|
+
* `mode` is the COARSE switch: it applies eui's full light or dark preset
|
|
631
|
+
* (`coolLight` / `coolDark`) as `--fp-*` variables on the shell root, so the
|
|
632
|
+
* ENTIRE shell — canvas, panels, nodes, text, borders — follows dark/light from
|
|
633
|
+
* this one field. You do NOT need to hand-set `--fp-*` yourself. (`--fp-*`
|
|
634
|
+
* remains available as a fine escape hatch for individual token overrides.)
|
|
635
|
+
*
|
|
636
|
+
* `visited` and `current` are the two semantic node colours, layered on top of
|
|
637
|
+
* the mode base. All optional — sensible per-mode defaults are used for anything
|
|
638
|
+
* omitted.
|
|
632
639
|
*/
|
|
633
640
|
interface TraceTheme {
|
|
634
641
|
mode?: "dark" | "light";
|
|
@@ -676,11 +683,11 @@ interface ExplainableShellProps extends BaseComponentProps {
|
|
|
676
683
|
runtimeOverlay?: RuntimeOverlay | null;
|
|
677
684
|
/**
|
|
678
685
|
* Trace flowchart theme — the footprintjs-LEVEL **two-colour** scheme:
|
|
679
|
-
* `visited` (executed nodes) + `current` (the cursor node). `mode`
|
|
680
|
-
*
|
|
681
|
-
*
|
|
682
|
-
* use the per-mode defaults. The agent-semantic three-colour theme
|
|
683
|
-
* `<Lens>`, not here.
|
|
686
|
+
* `visited` (executed nodes) + `current` (the cursor node). `mode` is the
|
|
687
|
+
* coarse light/dark switch — it applies eui's full preset to the whole shell,
|
|
688
|
+
* so you pass one word instead of a wall of `--fp-*` vars. Colours are optional
|
|
689
|
+
* — omit to use the per-mode defaults. The agent-semantic three-colour theme
|
|
690
|
+
* belongs to `<Lens>`, not here.
|
|
684
691
|
*/
|
|
685
692
|
traceTheme?: TraceTheme;
|
|
686
693
|
title?: string;
|
|
@@ -1075,6 +1082,8 @@ interface DataTracePanelProps {
|
|
|
1075
1082
|
onFrameClick?: (runtimeStageId: string) => void;
|
|
1076
1083
|
/** Optional: stage name for the "tracing from" header. */
|
|
1077
1084
|
fromStageName?: string;
|
|
1085
|
+
/** Optional honesty line rendered under the header (⚠-style). */
|
|
1086
|
+
note?: string;
|
|
1078
1087
|
}
|
|
1079
1088
|
/**
|
|
1080
1089
|
* Render the backward causal chain as a stack trace.
|
|
@@ -1088,6 +1097,8 @@ interface InspectorPanelProps {
|
|
|
1088
1097
|
selectedIndex: number;
|
|
1089
1098
|
/** Causal chain frames for the selected node (empty = no trace available). */
|
|
1090
1099
|
dataTraceFrames: CausalFrame[];
|
|
1100
|
+
/** Optional honesty line for the Data Trace tab (e.g. reads not recorded). */
|
|
1101
|
+
dataTraceNote?: string;
|
|
1091
1102
|
/** Currently selected runtimeStageId. */
|
|
1092
1103
|
selectedStageId?: string;
|
|
1093
1104
|
/** Navigate to a stage when clicking a Data Trace frame. */
|
package/dist/index.js
CHANGED
|
@@ -2228,6 +2228,67 @@ function TimeTravelControls({
|
|
|
2228
2228
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2229
2229
|
import { memo as memo8, useState as useState14, useCallback as useCallback8, useMemo as useMemo12, useRef as useRef9, useEffect as useEffect11 } from "react";
|
|
2230
2230
|
|
|
2231
|
+
// src/components/ExplainableShell/_internal/dataTrace.ts
|
|
2232
|
+
function readsByStep(tree) {
|
|
2233
|
+
const byStep = /* @__PURE__ */ new Map();
|
|
2234
|
+
if (!tree) return byStep;
|
|
2235
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2236
|
+
const stack = [tree];
|
|
2237
|
+
while (stack.length > 0) {
|
|
2238
|
+
const node = stack.pop();
|
|
2239
|
+
if (visited.has(node)) continue;
|
|
2240
|
+
visited.add(node);
|
|
2241
|
+
if (node.runtimeStageId && node.stageReads) {
|
|
2242
|
+
const keys = Object.keys(node.stageReads);
|
|
2243
|
+
if (keys.length > 0) byStep.set(node.runtimeStageId, keys);
|
|
2244
|
+
}
|
|
2245
|
+
if (node.next) stack.push(node.next);
|
|
2246
|
+
if (node.children) for (const c of node.children) stack.push(c);
|
|
2247
|
+
}
|
|
2248
|
+
return byStep;
|
|
2249
|
+
}
|
|
2250
|
+
function buildDataTrace(commitLog, executionTree, targetRuntimeStageId, maxDepth = 10, maxFrames = 50) {
|
|
2251
|
+
const log = commitLog ?? [];
|
|
2252
|
+
const reads = readsByStep(executionTree);
|
|
2253
|
+
const readsAvailable = reads.size > 0;
|
|
2254
|
+
if (!log.length) return { frames: [], readsAvailable };
|
|
2255
|
+
const idxOf = /* @__PURE__ */ new Map();
|
|
2256
|
+
for (let i = 0; i < log.length; i++) idxOf.set(log[i].runtimeStageId, i);
|
|
2257
|
+
const startIdx = idxOf.get(targetRuntimeStageId);
|
|
2258
|
+
if (startIdx === void 0) return { frames: [], readsAvailable };
|
|
2259
|
+
const findLastWriter = (key, beforeIdx) => {
|
|
2260
|
+
for (let i = beforeIdx - 1; i >= 0; i--) {
|
|
2261
|
+
if (log[i].trace.some((t) => t.path === key)) return i;
|
|
2262
|
+
}
|
|
2263
|
+
return -1;
|
|
2264
|
+
};
|
|
2265
|
+
const frames = [];
|
|
2266
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2267
|
+
const queue = [[startIdx, 0, ""]];
|
|
2268
|
+
while (queue.length > 0 && frames.length < maxFrames) {
|
|
2269
|
+
const [idx, depth, linkedBy] = queue.shift();
|
|
2270
|
+
const commit = log[idx];
|
|
2271
|
+
if (visited.has(commit.runtimeStageId)) continue;
|
|
2272
|
+
visited.add(commit.runtimeStageId);
|
|
2273
|
+
frames.push({
|
|
2274
|
+
runtimeStageId: commit.runtimeStageId,
|
|
2275
|
+
stageId: commit.stageId,
|
|
2276
|
+
stageName: commit.stage,
|
|
2277
|
+
keysWritten: commit.trace.map((t) => t.path),
|
|
2278
|
+
linkedBy,
|
|
2279
|
+
depth
|
|
2280
|
+
});
|
|
2281
|
+
if (depth >= maxDepth) continue;
|
|
2282
|
+
for (const key of reads.get(commit.runtimeStageId) ?? []) {
|
|
2283
|
+
const writerIdx = findLastWriter(key, idx);
|
|
2284
|
+
if (writerIdx >= 0 && !visited.has(log[writerIdx].runtimeStageId)) {
|
|
2285
|
+
queue.push([writerIdx, depth + 1, key]);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return { frames, readsAvailable };
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2231
2292
|
// src/utils/narrativeSync.ts
|
|
2232
2293
|
function buildEntryRangeIndex(entries) {
|
|
2233
2294
|
const ranges = /* @__PURE__ */ new Map();
|
|
@@ -5138,8 +5199,10 @@ var DataTracePanel = memo4(function DataTracePanel2({
|
|
|
5138
5199
|
frames,
|
|
5139
5200
|
selectedStageId,
|
|
5140
5201
|
onFrameClick,
|
|
5141
|
-
fromStageName
|
|
5202
|
+
fromStageName,
|
|
5203
|
+
note
|
|
5142
5204
|
}) {
|
|
5205
|
+
const noteLine = note ? /* @__PURE__ */ jsx22("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", marginBottom: 8 }, children: note }) : null;
|
|
5143
5206
|
if (frames.length === 0) {
|
|
5144
5207
|
return /* @__PURE__ */ jsxs19("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
|
|
5145
5208
|
/* @__PURE__ */ jsx22(
|
|
@@ -5157,10 +5220,12 @@ var DataTracePanel = memo4(function DataTracePanel2({
|
|
|
5157
5220
|
}
|
|
5158
5221
|
),
|
|
5159
5222
|
/* @__PURE__ */ jsx22("div", { style: { color: theme.textSecondary, marginBottom: 10 }, children: "Trace any value back to the stage that created it \u2014 and everything upstream that influenced it." }),
|
|
5223
|
+
noteLine,
|
|
5160
5224
|
/* @__PURE__ */ jsx22("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
|
|
5161
5225
|
] });
|
|
5162
5226
|
}
|
|
5163
5227
|
return /* @__PURE__ */ jsxs19("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
|
|
5228
|
+
note && /* @__PURE__ */ jsx22("div", { style: { padding: "4px 12px 0", fontSize: 11, color: theme.textMuted, fontStyle: "italic" }, children: note }),
|
|
5164
5229
|
fromStageName && /* @__PURE__ */ jsxs19("div", { style: { padding: "4px 12px 8px" }, children: [
|
|
5165
5230
|
/* @__PURE__ */ jsxs19(
|
|
5166
5231
|
"div",
|
|
@@ -5294,6 +5359,7 @@ var InspectorPanel = memo5(function InspectorPanel2({
|
|
|
5294
5359
|
snapshots,
|
|
5295
5360
|
selectedIndex,
|
|
5296
5361
|
dataTraceFrames,
|
|
5362
|
+
dataTraceNote,
|
|
5297
5363
|
selectedStageId,
|
|
5298
5364
|
onNavigateToStage
|
|
5299
5365
|
}) {
|
|
@@ -5350,6 +5416,7 @@ var InspectorPanel = memo5(function InspectorPanel2({
|
|
|
5350
5416
|
DataTracePanel,
|
|
5351
5417
|
{
|
|
5352
5418
|
frames: dataTraceFrames,
|
|
5419
|
+
note: dataTraceNote,
|
|
5353
5420
|
selectedStageId,
|
|
5354
5421
|
onFrameClick: onNavigateToStage,
|
|
5355
5422
|
fromStageName: currentSnapshot?.stageName
|
|
@@ -5941,40 +6008,6 @@ function resolveSubflowFromRuntime(parentSnapshots, subflowId, narrativeEntries)
|
|
|
5941
6008
|
if (sfSnapshots.length === 0) return null;
|
|
5942
6009
|
return { subflowId, label, spec: null, snapshots: sfSnapshots, narrative: sfNarrative };
|
|
5943
6010
|
}
|
|
5944
|
-
function buildDataTrace(commitLog, targetRuntimeStageId, maxDepth = 10) {
|
|
5945
|
-
const log = commitLog;
|
|
5946
|
-
if (!log?.length) return [];
|
|
5947
|
-
const idxMap = /* @__PURE__ */ new Map();
|
|
5948
|
-
for (let i = 0; i < log.length; i++) idxMap.set(log[i].runtimeStageId, i);
|
|
5949
|
-
const startIdx = idxMap.get(targetRuntimeStageId);
|
|
5950
|
-
if (startIdx === void 0) return [];
|
|
5951
|
-
const startCommit = log[startIdx];
|
|
5952
|
-
const frames = [];
|
|
5953
|
-
const visited = /* @__PURE__ */ new Set();
|
|
5954
|
-
let current = startCommit;
|
|
5955
|
-
let currentIdx = startIdx;
|
|
5956
|
-
let depth = 0;
|
|
5957
|
-
while (current && depth <= maxDepth) {
|
|
5958
|
-
if (visited.has(current.runtimeStageId)) break;
|
|
5959
|
-
visited.add(current.runtimeStageId);
|
|
5960
|
-
frames.push({
|
|
5961
|
-
runtimeStageId: current.runtimeStageId,
|
|
5962
|
-
stageId: current.stageId,
|
|
5963
|
-
stageName: current.stage,
|
|
5964
|
-
keysWritten: current.trace.map((t) => t.path),
|
|
5965
|
-
linkedBy: depth === 0 ? "" : current.trace[0]?.path ?? "",
|
|
5966
|
-
depth
|
|
5967
|
-
});
|
|
5968
|
-
if (currentIdx > 0) {
|
|
5969
|
-
currentIdx--;
|
|
5970
|
-
current = log[currentIdx];
|
|
5971
|
-
depth++;
|
|
5972
|
-
} else {
|
|
5973
|
-
break;
|
|
5974
|
-
}
|
|
5975
|
-
}
|
|
5976
|
-
return frames;
|
|
5977
|
-
}
|
|
5978
6011
|
var RightPanel = memo8(function RightPanel2({
|
|
5979
6012
|
mode,
|
|
5980
6013
|
onModeChange,
|
|
@@ -5989,6 +6022,10 @@ var RightPanel = memo8(function RightPanel2({
|
|
|
5989
6022
|
size,
|
|
5990
6023
|
onNavigateToStage
|
|
5991
6024
|
}) {
|
|
6025
|
+
const dataTrace = useMemo12(
|
|
6026
|
+
() => runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, snapshots[selectedIndex]?.runtimeStageId ?? "") : { frames: [], readsAvailable: true },
|
|
6027
|
+
[runtimeSnapshot, snapshots, selectedIndex]
|
|
6028
|
+
);
|
|
5992
6029
|
return /* @__PURE__ */ jsxs23(Fragment6, { children: [
|
|
5993
6030
|
/* @__PURE__ */ jsx26("div", { style: {
|
|
5994
6031
|
display: "flex",
|
|
@@ -6040,7 +6077,8 @@ var RightPanel = memo8(function RightPanel2({
|
|
|
6040
6077
|
{
|
|
6041
6078
|
snapshots,
|
|
6042
6079
|
selectedIndex,
|
|
6043
|
-
dataTraceFrames:
|
|
6080
|
+
dataTraceFrames: dataTrace.frames,
|
|
6081
|
+
dataTraceNote: dataTrace.readsAvailable ? void 0 : "\u26A0 reads were not recorded (readTracking off) \u2014 dependencies are unknowable, not absent.",
|
|
6044
6082
|
selectedStageId: snapshots[selectedIndex]?.runtimeStageId,
|
|
6045
6083
|
onNavigateToStage
|
|
6046
6084
|
}
|
|
@@ -6369,12 +6407,22 @@ function ExplainableShell({
|
|
|
6369
6407
|
}) }),
|
|
6370
6408
|
/* @__PURE__ */ jsx26("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
|
|
6371
6409
|
] });
|
|
6410
|
+
const shellThemeVars = useMemo12(() => {
|
|
6411
|
+
if (!traceTheme) return {};
|
|
6412
|
+
const base = traceTheme.mode ? tokensToCSSVars(traceTheme.mode === "light" ? coolLight : coolDark) : {};
|
|
6413
|
+
return {
|
|
6414
|
+
...base,
|
|
6415
|
+
...traceTheme.visited !== void 0 && { ["--fp-node-visited"]: traceTheme.visited },
|
|
6416
|
+
...traceTheme.current !== void 0 && { ["--fp-node-cursor"]: traceTheme.current }
|
|
6417
|
+
};
|
|
6418
|
+
}, [traceTheme]);
|
|
6372
6419
|
return /* @__PURE__ */ jsxs23(
|
|
6373
6420
|
"div",
|
|
6374
6421
|
{
|
|
6375
6422
|
ref: shellRef,
|
|
6376
6423
|
className,
|
|
6377
6424
|
style: {
|
|
6425
|
+
...shellThemeVars,
|
|
6378
6426
|
height: "100%",
|
|
6379
6427
|
display: "flex",
|
|
6380
6428
|
flexDirection: "column",
|