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 CHANGED
@@ -2300,6 +2300,67 @@ function TimeTravelControls({
2300
2300
  // src/components/ExplainableShell/ExplainableShell.tsx
2301
2301
  var import_react31 = require("react");
2302
2302
 
2303
+ // src/components/ExplainableShell/_internal/dataTrace.ts
2304
+ function readsByStep(tree) {
2305
+ const byStep = /* @__PURE__ */ new Map();
2306
+ if (!tree) return byStep;
2307
+ const visited = /* @__PURE__ */ new Set();
2308
+ const stack = [tree];
2309
+ while (stack.length > 0) {
2310
+ const node = stack.pop();
2311
+ if (visited.has(node)) continue;
2312
+ visited.add(node);
2313
+ if (node.runtimeStageId && node.stageReads) {
2314
+ const keys = Object.keys(node.stageReads);
2315
+ if (keys.length > 0) byStep.set(node.runtimeStageId, keys);
2316
+ }
2317
+ if (node.next) stack.push(node.next);
2318
+ if (node.children) for (const c of node.children) stack.push(c);
2319
+ }
2320
+ return byStep;
2321
+ }
2322
+ function buildDataTrace(commitLog, executionTree, targetRuntimeStageId, maxDepth = 10, maxFrames = 50) {
2323
+ const log = commitLog ?? [];
2324
+ const reads = readsByStep(executionTree);
2325
+ const readsAvailable = reads.size > 0;
2326
+ if (!log.length) return { frames: [], readsAvailable };
2327
+ const idxOf = /* @__PURE__ */ new Map();
2328
+ for (let i = 0; i < log.length; i++) idxOf.set(log[i].runtimeStageId, i);
2329
+ const startIdx = idxOf.get(targetRuntimeStageId);
2330
+ if (startIdx === void 0) return { frames: [], readsAvailable };
2331
+ const findLastWriter = (key, beforeIdx) => {
2332
+ for (let i = beforeIdx - 1; i >= 0; i--) {
2333
+ if (log[i].trace.some((t) => t.path === key)) return i;
2334
+ }
2335
+ return -1;
2336
+ };
2337
+ const frames = [];
2338
+ const visited = /* @__PURE__ */ new Set();
2339
+ const queue = [[startIdx, 0, ""]];
2340
+ while (queue.length > 0 && frames.length < maxFrames) {
2341
+ const [idx, depth, linkedBy] = queue.shift();
2342
+ const commit = log[idx];
2343
+ if (visited.has(commit.runtimeStageId)) continue;
2344
+ visited.add(commit.runtimeStageId);
2345
+ frames.push({
2346
+ runtimeStageId: commit.runtimeStageId,
2347
+ stageId: commit.stageId,
2348
+ stageName: commit.stage,
2349
+ keysWritten: commit.trace.map((t) => t.path),
2350
+ linkedBy,
2351
+ depth
2352
+ });
2353
+ if (depth >= maxDepth) continue;
2354
+ for (const key of reads.get(commit.runtimeStageId) ?? []) {
2355
+ const writerIdx = findLastWriter(key, idx);
2356
+ if (writerIdx >= 0 && !visited.has(log[writerIdx].runtimeStageId)) {
2357
+ queue.push([writerIdx, depth + 1, key]);
2358
+ }
2359
+ }
2360
+ }
2361
+ return { frames, readsAvailable };
2362
+ }
2363
+
2303
2364
  // src/utils/narrativeSync.ts
2304
2365
  function buildEntryRangeIndex(entries) {
2305
2366
  const ranges = /* @__PURE__ */ new Map();
@@ -5205,8 +5266,10 @@ var DataTracePanel = (0, import_react27.memo)(function DataTracePanel2({
5205
5266
  frames,
5206
5267
  selectedStageId,
5207
5268
  onFrameClick,
5208
- fromStageName
5269
+ fromStageName,
5270
+ note
5209
5271
  }) {
5272
+ const noteLine = note ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", marginBottom: 8 }, children: note }) : null;
5210
5273
  if (frames.length === 0) {
5211
5274
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
5212
5275
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
@@ -5224,10 +5287,12 @@ var DataTracePanel = (0, import_react27.memo)(function DataTracePanel2({
5224
5287
  }
5225
5288
  ),
5226
5289
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("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." }),
5290
+ noteLine,
5227
5291
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
5228
5292
  ] });
5229
5293
  }
5230
5294
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
5295
+ note && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: "4px 12px 0", fontSize: 11, color: theme.textMuted, fontStyle: "italic" }, children: note }),
5231
5296
  fromStageName && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "4px 12px 8px" }, children: [
5232
5297
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
5233
5298
  "div",
@@ -5361,6 +5426,7 @@ var InspectorPanel = (0, import_react28.memo)(function InspectorPanel2({
5361
5426
  snapshots,
5362
5427
  selectedIndex,
5363
5428
  dataTraceFrames,
5429
+ dataTraceNote,
5364
5430
  selectedStageId,
5365
5431
  onNavigateToStage
5366
5432
  }) {
@@ -5417,6 +5483,7 @@ var InspectorPanel = (0, import_react28.memo)(function InspectorPanel2({
5417
5483
  DataTracePanel,
5418
5484
  {
5419
5485
  frames: dataTraceFrames,
5486
+ note: dataTraceNote,
5420
5487
  selectedStageId,
5421
5488
  onFrameClick: onNavigateToStage,
5422
5489
  fromStageName: currentSnapshot?.stageName
@@ -6008,40 +6075,6 @@ function resolveSubflowFromRuntime(parentSnapshots, subflowId, narrativeEntries)
6008
6075
  if (sfSnapshots.length === 0) return null;
6009
6076
  return { subflowId, label, spec: null, snapshots: sfSnapshots, narrative: sfNarrative };
6010
6077
  }
6011
- function buildDataTrace(commitLog, targetRuntimeStageId, maxDepth = 10) {
6012
- const log = commitLog;
6013
- if (!log?.length) return [];
6014
- const idxMap = /* @__PURE__ */ new Map();
6015
- for (let i = 0; i < log.length; i++) idxMap.set(log[i].runtimeStageId, i);
6016
- const startIdx = idxMap.get(targetRuntimeStageId);
6017
- if (startIdx === void 0) return [];
6018
- const startCommit = log[startIdx];
6019
- const frames = [];
6020
- const visited = /* @__PURE__ */ new Set();
6021
- let current = startCommit;
6022
- let currentIdx = startIdx;
6023
- let depth = 0;
6024
- while (current && depth <= maxDepth) {
6025
- if (visited.has(current.runtimeStageId)) break;
6026
- visited.add(current.runtimeStageId);
6027
- frames.push({
6028
- runtimeStageId: current.runtimeStageId,
6029
- stageId: current.stageId,
6030
- stageName: current.stage,
6031
- keysWritten: current.trace.map((t) => t.path),
6032
- linkedBy: depth === 0 ? "" : current.trace[0]?.path ?? "",
6033
- depth
6034
- });
6035
- if (currentIdx > 0) {
6036
- currentIdx--;
6037
- current = log[currentIdx];
6038
- depth++;
6039
- } else {
6040
- break;
6041
- }
6042
- }
6043
- return frames;
6044
- }
6045
6078
  var RightPanel = (0, import_react31.memo)(function RightPanel2({
6046
6079
  mode,
6047
6080
  onModeChange,
@@ -6056,6 +6089,10 @@ var RightPanel = (0, import_react31.memo)(function RightPanel2({
6056
6089
  size,
6057
6090
  onNavigateToStage
6058
6091
  }) {
6092
+ const dataTrace = (0, import_react31.useMemo)(
6093
+ () => runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, snapshots[selectedIndex]?.runtimeStageId ?? "") : { frames: [], readsAvailable: true },
6094
+ [runtimeSnapshot, snapshots, selectedIndex]
6095
+ );
6059
6096
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
6060
6097
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: {
6061
6098
  display: "flex",
@@ -6107,7 +6144,8 @@ var RightPanel = (0, import_react31.memo)(function RightPanel2({
6107
6144
  {
6108
6145
  snapshots,
6109
6146
  selectedIndex,
6110
- dataTraceFrames: runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, snapshots[selectedIndex]?.runtimeStageId ?? "") : [],
6147
+ dataTraceFrames: dataTrace.frames,
6148
+ dataTraceNote: dataTrace.readsAvailable ? void 0 : "\u26A0 reads were not recorded (readTracking off) \u2014 dependencies are unknowable, not absent.",
6111
6149
  selectedStageId: snapshots[selectedIndex]?.runtimeStageId,
6112
6150
  onNavigateToStage
6113
6151
  }
@@ -6436,12 +6474,22 @@ function ExplainableShell({
6436
6474
  }) }),
6437
6475
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
6438
6476
  ] });
6477
+ const shellThemeVars = (0, import_react31.useMemo)(() => {
6478
+ if (!traceTheme) return {};
6479
+ const base = traceTheme.mode ? tokensToCSSVars(traceTheme.mode === "light" ? coolLight : coolDark) : {};
6480
+ return {
6481
+ ...base,
6482
+ ...traceTheme.visited !== void 0 && { ["--fp-node-visited"]: traceTheme.visited },
6483
+ ...traceTheme.current !== void 0 && { ["--fp-node-cursor"]: traceTheme.current }
6484
+ };
6485
+ }, [traceTheme]);
6439
6486
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
6440
6487
  "div",
6441
6488
  {
6442
6489
  ref: shellRef,
6443
6490
  className,
6444
6491
  style: {
6492
+ ...shellThemeVars,
6445
6493
  height: "100%",
6446
6494
  display: "flex",
6447
6495
  flexDirection: "column",