footprint-explainable-ui 0.26.2 → 0.28.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.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). `mode` selects
629
- * the neutral base (unvisited / edges) for dark or light; `visited` and
630
- * `current` are the two semantic colours. All optional sensible per-mode
631
- * defaults are used for anything omitted.
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` picks the
680
- * neutral base (unvisited nodes / edges follow dark/light; the background is
681
- * transparent, so it inherits your container). Colours are optional — omit to
682
- * use the per-mode defaults. The agent-semantic three-colour theme belongs to
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;
@@ -724,6 +731,9 @@ interface ExplainableShellProps extends BaseComponentProps {
724
731
  selectedIndex: number;
725
732
  onNodeClick?: (indexOrId: number | string) => void;
726
733
  showStageId?: boolean;
734
+ /** Dependency-cone overlay (chart node id → BFS depth) — painted while
735
+ * the Inspector's Data Trace tab is open. Custom renderers may ignore it. */
736
+ sliceCone?: ReadonlyMap<string, number>;
727
737
  }) => React.ReactNode;
728
738
  /**
729
739
  * When true, render each node's stable `stageId` as a small monospace
@@ -1075,6 +1085,8 @@ interface DataTracePanelProps {
1075
1085
  onFrameClick?: (runtimeStageId: string) => void;
1076
1086
  /** Optional: stage name for the "tracing from" header. */
1077
1087
  fromStageName?: string;
1088
+ /** Optional honesty line rendered under the header (⚠-style). */
1089
+ note?: string;
1078
1090
  }
1079
1091
  /**
1080
1092
  * Render the backward causal chain as a stack trace.
@@ -1088,10 +1100,15 @@ interface InspectorPanelProps {
1088
1100
  selectedIndex: number;
1089
1101
  /** Causal chain frames for the selected node (empty = no trace available). */
1090
1102
  dataTraceFrames: CausalFrame[];
1103
+ /** Optional honesty line for the Data Trace tab (e.g. reads not recorded). */
1104
+ dataTraceNote?: string;
1091
1105
  /** Currently selected runtimeStageId. */
1092
1106
  selectedStageId?: string;
1093
1107
  /** Navigate to a stage when clicking a Data Trace frame. */
1094
1108
  onNavigateToStage?: (runtimeStageId: string) => void;
1109
+ /** Fires when the user switches tabs — lets the shell paint the chart's
1110
+ * dependency cone while the Data Trace tab is open. */
1111
+ onTabChange?: (tab: "state" | "trace") => void;
1095
1112
  }
1096
1113
  declare const InspectorPanel: react.NamedExoticComponent<InspectorPanelProps>;
1097
1114
 
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). `mode` selects
629
- * the neutral base (unvisited / edges) for dark or light; `visited` and
630
- * `current` are the two semantic colours. All optional sensible per-mode
631
- * defaults are used for anything omitted.
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` picks the
680
- * neutral base (unvisited nodes / edges follow dark/light; the background is
681
- * transparent, so it inherits your container). Colours are optional — omit to
682
- * use the per-mode defaults. The agent-semantic three-colour theme belongs to
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;
@@ -724,6 +731,9 @@ interface ExplainableShellProps extends BaseComponentProps {
724
731
  selectedIndex: number;
725
732
  onNodeClick?: (indexOrId: number | string) => void;
726
733
  showStageId?: boolean;
734
+ /** Dependency-cone overlay (chart node id → BFS depth) — painted while
735
+ * the Inspector's Data Trace tab is open. Custom renderers may ignore it. */
736
+ sliceCone?: ReadonlyMap<string, number>;
727
737
  }) => React.ReactNode;
728
738
  /**
729
739
  * When true, render each node's stable `stageId` as a small monospace
@@ -1075,6 +1085,8 @@ interface DataTracePanelProps {
1075
1085
  onFrameClick?: (runtimeStageId: string) => void;
1076
1086
  /** Optional: stage name for the "tracing from" header. */
1077
1087
  fromStageName?: string;
1088
+ /** Optional honesty line rendered under the header (⚠-style). */
1089
+ note?: string;
1078
1090
  }
1079
1091
  /**
1080
1092
  * Render the backward causal chain as a stack trace.
@@ -1088,10 +1100,15 @@ interface InspectorPanelProps {
1088
1100
  selectedIndex: number;
1089
1101
  /** Causal chain frames for the selected node (empty = no trace available). */
1090
1102
  dataTraceFrames: CausalFrame[];
1103
+ /** Optional honesty line for the Data Trace tab (e.g. reads not recorded). */
1104
+ dataTraceNote?: string;
1091
1105
  /** Currently selected runtimeStageId. */
1092
1106
  selectedStageId?: string;
1093
1107
  /** Navigate to a stage when clicking a Data Trace frame. */
1094
1108
  onNavigateToStage?: (runtimeStageId: string) => void;
1109
+ /** Fires when the user switches tabs — lets the shell paint the chart's
1110
+ * dependency cone while the Data Trace tab is open. */
1111
+ onTabChange?: (tab: "state" | "trace") => void;
1095
1112
  }
1096
1113
  declare const InspectorPanel: react.NamedExoticComponent<InspectorPanelProps>;
1097
1114
 
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();
@@ -4964,6 +5025,7 @@ function TracedFlow({
4964
5025
  nodeTypes: userNodeTypes,
4965
5026
  edgeTypes: userEdgeTypes,
4966
5027
  coActiveStageIds,
5028
+ sliceCone,
4967
5029
  children,
4968
5030
  className,
4969
5031
  style
@@ -5065,6 +5127,38 @@ function TracedFlow({
5065
5127
  ),
5066
5128
  [positioned.edges, slice, colors]
5067
5129
  );
5130
+ const [coneRevealed, setConeRevealed] = useState10(false);
5131
+ useEffect10(() => {
5132
+ if (!sliceCone) return;
5133
+ setConeRevealed(false);
5134
+ const raf = requestAnimationFrame(() => setConeRevealed(true));
5135
+ return () => cancelAnimationFrame(raf);
5136
+ }, [sliceCone]);
5137
+ const conedNodes = useMemo10(() => {
5138
+ if (!sliceCone || sliceCone.size === 0) return reactFlowNodes;
5139
+ return reactFlowNodes.map((n) => {
5140
+ const depth = sliceCone.get(n.id);
5141
+ if (depth === void 0) {
5142
+ return { ...n, style: { ...n.style, opacity: 0.22, transition: "opacity 260ms ease" } };
5143
+ }
5144
+ return {
5145
+ ...n,
5146
+ style: {
5147
+ ...n.style,
5148
+ opacity: coneRevealed ? 1 : 0.22,
5149
+ transition: "opacity 320ms ease",
5150
+ transitionDelay: `${depth * 90}ms`
5151
+ }
5152
+ };
5153
+ });
5154
+ }, [reactFlowNodes, sliceCone, coneRevealed]);
5155
+ const conedEdges = useMemo10(() => {
5156
+ if (!sliceCone || sliceCone.size === 0) return reactFlowEdges;
5157
+ return reactFlowEdges.map((e) => {
5158
+ const inCone = sliceCone.has(e.source) && sliceCone.has(e.target);
5159
+ return inCone ? e : { ...e, style: { ...e.style, opacity: 0.12, transition: "opacity 260ms ease" } };
5160
+ });
5161
+ }, [reactFlowEdges, sliceCone]);
5068
5162
  const handleNodeClick = useCallback7(
5069
5163
  (_, node) => {
5070
5164
  const data = node.data ?? {};
@@ -5106,8 +5200,8 @@ function TracedFlow({
5106
5200
  /* @__PURE__ */ jsx21("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsxs18(
5107
5201
  ReactFlow,
5108
5202
  {
5109
- nodes: reactFlowNodes,
5110
- edges: reactFlowEdges,
5203
+ nodes: conedNodes,
5204
+ edges: conedEdges,
5111
5205
  nodeTypes: mergedNodeTypes,
5112
5206
  edgeTypes: mergedEdgeTypes,
5113
5207
  onNodeClick: handleNodeClick,
@@ -5138,8 +5232,10 @@ var DataTracePanel = memo4(function DataTracePanel2({
5138
5232
  frames,
5139
5233
  selectedStageId,
5140
5234
  onFrameClick,
5141
- fromStageName
5235
+ fromStageName,
5236
+ note
5142
5237
  }) {
5238
+ const noteLine = note ? /* @__PURE__ */ jsx22("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", marginBottom: 8 }, children: note }) : null;
5143
5239
  if (frames.length === 0) {
5144
5240
  return /* @__PURE__ */ jsxs19("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
5145
5241
  /* @__PURE__ */ jsx22(
@@ -5157,10 +5253,12 @@ var DataTracePanel = memo4(function DataTracePanel2({
5157
5253
  }
5158
5254
  ),
5159
5255
  /* @__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." }),
5256
+ noteLine,
5160
5257
  /* @__PURE__ */ jsx22("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
5161
5258
  ] });
5162
5259
  }
5163
5260
  return /* @__PURE__ */ jsxs19("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
5261
+ note && /* @__PURE__ */ jsx22("div", { style: { padding: "4px 12px 0", fontSize: 11, color: theme.textMuted, fontStyle: "italic" }, children: note }),
5164
5262
  fromStageName && /* @__PURE__ */ jsxs19("div", { style: { padding: "4px 12px 8px" }, children: [
5165
5263
  /* @__PURE__ */ jsxs19(
5166
5264
  "div",
@@ -5294,10 +5392,16 @@ var InspectorPanel = memo5(function InspectorPanel2({
5294
5392
  snapshots,
5295
5393
  selectedIndex,
5296
5394
  dataTraceFrames,
5395
+ dataTraceNote,
5297
5396
  selectedStageId,
5298
- onNavigateToStage
5397
+ onNavigateToStage,
5398
+ onTabChange
5299
5399
  }) {
5300
- const [tab, setTab] = useState11("state");
5400
+ const [tab, setTabState] = useState11("state");
5401
+ const setTab = (t) => {
5402
+ setTabState(t);
5403
+ onTabChange?.(t);
5404
+ };
5301
5405
  const currentSnapshot = snapshots[selectedIndex];
5302
5406
  return /* @__PURE__ */ jsxs20(
5303
5407
  "div",
@@ -5350,6 +5454,7 @@ var InspectorPanel = memo5(function InspectorPanel2({
5350
5454
  DataTracePanel,
5351
5455
  {
5352
5456
  frames: dataTraceFrames,
5457
+ note: dataTraceNote,
5353
5458
  selectedStageId,
5354
5459
  onFrameClick: onNavigateToStage,
5355
5460
  fromStageName: currentSnapshot?.stageName
@@ -5941,40 +6046,6 @@ function resolveSubflowFromRuntime(parentSnapshots, subflowId, narrativeEntries)
5941
6046
  if (sfSnapshots.length === 0) return null;
5942
6047
  return { subflowId, label, spec: null, snapshots: sfSnapshots, narrative: sfNarrative };
5943
6048
  }
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
6049
  var RightPanel = memo8(function RightPanel2({
5979
6050
  mode,
5980
6051
  onModeChange,
@@ -5987,7 +6058,9 @@ var RightPanel = memo8(function RightPanel2({
5987
6058
  recorderViews,
5988
6059
  autoRecorderViews,
5989
6060
  size,
5990
- onNavigateToStage
6061
+ onNavigateToStage,
6062
+ dataTrace,
6063
+ onInspectorTabChange
5991
6064
  }) {
5992
6065
  return /* @__PURE__ */ jsxs23(Fragment6, { children: [
5993
6066
  /* @__PURE__ */ jsx26("div", { style: {
@@ -6040,7 +6113,9 @@ var RightPanel = memo8(function RightPanel2({
6040
6113
  {
6041
6114
  snapshots,
6042
6115
  selectedIndex,
6043
- dataTraceFrames: runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, snapshots[selectedIndex]?.runtimeStageId ?? "") : [],
6116
+ dataTraceFrames: dataTrace.frames,
6117
+ dataTraceNote: dataTrace.readsAvailable ? void 0 : "\u26A0 reads were not recorded (readTracking off) \u2014 dependencies are unknowable, not absent.",
6118
+ onTabChange: onInspectorTabChange,
6044
6119
  selectedStageId: snapshots[selectedIndex]?.runtimeStageId,
6045
6120
  onNavigateToStage
6046
6121
  }
@@ -6094,7 +6169,7 @@ function ExplainableShell({
6094
6169
  const resultData = resultDataProp ?? derivedFromRuntime?.resultData ?? null;
6095
6170
  const tracedFlowRenderer = useMemo12(() => {
6096
6171
  if (!traceGraph) return void 0;
6097
- return ({ selectedIndex, snapshots: snapshots2, onNodeClick }) => {
6172
+ return ({ selectedIndex, snapshots: snapshots2, onNodeClick, sliceCone: sliceCone2 }) => {
6098
6173
  const activeRsid = snapshots2[selectedIndex]?.runtimeStageId;
6099
6174
  let overlayIdx = selectedIndex;
6100
6175
  if (activeRsid && runtimeOverlay) {
@@ -6118,6 +6193,7 @@ function ExplainableShell({
6118
6193
  {
6119
6194
  graph: traceGraph,
6120
6195
  overlay: runtimeOverlay ?? void 0,
6196
+ sliceCone: sliceCone2 ?? void 0,
6121
6197
  colors: traceColors || void 0,
6122
6198
  scrubIndex: overlayIdx,
6123
6199
  onNodeClick: (stageId) => onNodeClick?.(stageId),
@@ -6178,6 +6254,7 @@ function ExplainableShell({
6178
6254
  const [drillDownStack, setDrillDownStack] = useState14([]);
6179
6255
  const [rightExpanded, setRightExpanded] = useState14(defaultExpanded?.details ?? true);
6180
6256
  const [rightPanelMode, setRightPanelMode] = useState14("insights");
6257
+ const [inspectorTab, setInspectorTab] = useState14("state");
6181
6258
  const [leftExpanded, setLeftExpanded] = useState14(defaultExpanded?.topology ?? false);
6182
6259
  const [timelineExpanded, setTimelineExpanded] = useState14(defaultExpanded?.timeline ?? false);
6183
6260
  useEffect11(() => {
@@ -6213,6 +6290,21 @@ function ExplainableShell({
6213
6290
  }, [drillDownStack, snapshots]);
6214
6291
  const activeSnapshots = currentLevel.snapshots;
6215
6292
  const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
6293
+ const shellDataTrace = useMemo12(
6294
+ () => runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, runtimeSnapshot.executionTree, activeSnapshots[safeIdx]?.runtimeStageId ?? "") : { frames: [], readsAvailable: true },
6295
+ [runtimeSnapshot, activeSnapshots, safeIdx]
6296
+ );
6297
+ const sliceCone = useMemo12(() => {
6298
+ if (rightPanelMode !== "what" || inspectorTab !== "trace") return void 0;
6299
+ if (shellDataTrace.frames.length < 2) return void 0;
6300
+ const cone = /* @__PURE__ */ new Map();
6301
+ for (const f of shellDataTrace.frames) {
6302
+ const stagePart = f.runtimeStageId.split("#")[0];
6303
+ const prev = cone.get(stagePart);
6304
+ if (prev === void 0 || f.depth < prev) cone.set(stagePart, f.depth);
6305
+ }
6306
+ return cone;
6307
+ }, [rightPanelMode, inspectorTab, shellDataTrace]);
6216
6308
  const activeNarrativeEntries = isInSubflow ? currentLevel.narrative : narrativeEntries;
6217
6309
  const breadcrumbs = useMemo12(() => {
6218
6310
  const root = { label: title || "Flowchart", spec: null, description: void 0 };
@@ -6296,7 +6388,7 @@ function ExplainableShell({
6296
6388
  (activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ jsxs23(Fragment6, { children: [
6297
6389
  /* @__PURE__ */ jsx26(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
6298
6390
  isInSubflow && /* @__PURE__ */ jsx26(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
6299
- traceGraph && effectiveRenderFlowchart?.({ spec: null, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick, showStageId }),
6391
+ traceGraph && effectiveRenderFlowchart?.({ spec: null, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick, showStageId, ...sliceCone && { sliceCone } }),
6300
6392
  /* @__PURE__ */ jsx26(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
6301
6393
  /* @__PURE__ */ jsx26(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, unstyled: true }),
6302
6394
  /* @__PURE__ */ jsx26(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
@@ -6369,12 +6461,22 @@ function ExplainableShell({
6369
6461
  }) }),
6370
6462
  /* @__PURE__ */ jsx26("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
6371
6463
  ] });
6464
+ const shellThemeVars = useMemo12(() => {
6465
+ if (!traceTheme) return {};
6466
+ const base = traceTheme.mode ? tokensToCSSVars(traceTheme.mode === "light" ? coolLight : coolDark) : {};
6467
+ return {
6468
+ ...base,
6469
+ ...traceTheme.visited !== void 0 && { ["--fp-node-visited"]: traceTheme.visited },
6470
+ ...traceTheme.current !== void 0 && { ["--fp-node-cursor"]: traceTheme.current }
6471
+ };
6472
+ }, [traceTheme]);
6372
6473
  return /* @__PURE__ */ jsxs23(
6373
6474
  "div",
6374
6475
  {
6375
6476
  ref: shellRef,
6376
6477
  className,
6377
6478
  style: {
6479
+ ...shellThemeVars,
6378
6480
  height: "100%",
6379
6481
  display: "flex",
6380
6482
  flexDirection: "column",
@@ -6405,7 +6507,8 @@ function ExplainableShell({
6405
6507
  snapshots: activeSnapshots,
6406
6508
  selectedIndex: safeIdx,
6407
6509
  onNodeClick: handleNodeClick,
6408
- showStageId
6510
+ showStageId,
6511
+ ...sliceCone && { sliceCone }
6409
6512
  }) }),
6410
6513
  showTreeSidebar && /* @__PURE__ */ jsxs23(Fragment6, { children: [
6411
6514
  /* @__PURE__ */ jsx26(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
@@ -6445,7 +6548,8 @@ function ExplainableShell({
6445
6548
  snapshots: activeSnapshots,
6446
6549
  selectedIndex: safeIdx,
6447
6550
  onNodeClick: handleNodeClick,
6448
- showStageId
6551
+ showStageId,
6552
+ ...sliceCone && { sliceCone }
6449
6553
  }) }) : /* @__PURE__ */ jsx26("div", { style: { flex: 1 } }),
6450
6554
  /* @__PURE__ */ jsx26(VLinePill, { label: "Details", expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
6451
6555
  rightExpanded && /* @__PURE__ */ jsx26("div", { style: { width: "42%", minWidth: 320, maxWidth: 550, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ jsx26(
@@ -6453,6 +6557,8 @@ function ExplainableShell({
6453
6557
  {
6454
6558
  mode: rightPanelMode,
6455
6559
  onModeChange: setRightPanelMode,
6560
+ dataTrace: shellDataTrace,
6561
+ onInspectorTabChange: setInspectorTab,
6456
6562
  snapshots: activeSnapshots,
6457
6563
  selectedIndex: safeIdx,
6458
6564
  runtimeSnapshot,