footprint-explainable-ui 0.27.0 → 0.29.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 CHANGED
@@ -138,6 +138,32 @@ The all-in-one orchestrator. Handles time-travel, subflow drill-down, memory/nar
138
138
  | `size` | `"compact" \| "default" \| "detailed"` | `"default"` | Size variant |
139
139
  | `unstyled` | `boolean` | `false` | Strip styles, render `data-fp` attributes |
140
140
 
141
+ ### Tracing a value — walk the timeline backward through its causes
142
+
143
+ ![Tracing mode: the rail becomes the walk](demo/tracing-verified.png)
144
+
145
+ Open **Inspector → Data Trace** and click one of the "Trace a value" chips.
146
+ The time slider stays the same rail — the stages that made that value light
147
+ up as **stops**, everything else fades to unlandable ticks, and the buttons
148
+ become **◀ earlier cause / toward result ▶**. This works because every
149
+ ingredient of a value was always written *earlier in the run* than the value
150
+ it fed, so the dependency chain is a sub-sequence of the timeline you already
151
+ have. One cursor, no new axis.
152
+
153
+ - A value made from **two ingredients** shows both as colored chips —
154
+ pressing "earlier cause" visits both (most recent first); nothing is ever
155
+ silently skipped. Click a chip to **follow** just that ingredient (the
156
+ breadcrumb shows `key ▸ via ingredient · show all`).
157
+ - Every stop shows the world **as it was at that moment** — the state panel
158
+ time-travels with the walk for free.
159
+ - **Honest absence**: a value nobody wrote gets a truthful card ("never
160
+ written in this run — it arrived with the run's inputs"), and a value not
161
+ written *yet* at the cursor's moment says exactly that, naming where its
162
+ first write happens. Reads-off runs say "unknowable, not absent".
163
+ - **[Copy story]** emits the same text an LLM backtrack tool returns — the
164
+ human's board and the agent's answer are one artifact.
165
+ - Tracing lives on the root rail: drilling into a subflow exits it honestly.
166
+
141
167
  ### Panel Labels
142
168
 
143
169
  Customize the text on collapsible pill buttons. Semantic keys — not tied to position:
@@ -2697,6 +2697,7 @@ function TracedFlow({
2697
2697
  nodeTypes: userNodeTypes,
2698
2698
  edgeTypes: userEdgeTypes,
2699
2699
  coActiveStageIds,
2700
+ sliceCone,
2700
2701
  children,
2701
2702
  className,
2702
2703
  style
@@ -2798,6 +2799,38 @@ function TracedFlow({
2798
2799
  ),
2799
2800
  [positioned.edges, slice, colors]
2800
2801
  );
2802
+ const [coneRevealed, setConeRevealed] = (0, import_react18.useState)(false);
2803
+ (0, import_react18.useEffect)(() => {
2804
+ if (!sliceCone) return;
2805
+ setConeRevealed(false);
2806
+ const raf = requestAnimationFrame(() => setConeRevealed(true));
2807
+ return () => cancelAnimationFrame(raf);
2808
+ }, [sliceCone]);
2809
+ const conedNodes = (0, import_react18.useMemo)(() => {
2810
+ if (!sliceCone || sliceCone.size === 0) return reactFlowNodes;
2811
+ return reactFlowNodes.map((n) => {
2812
+ const depth = sliceCone.get(n.id);
2813
+ if (depth === void 0) {
2814
+ return { ...n, style: { ...n.style, opacity: 0.22, transition: "opacity 260ms ease" } };
2815
+ }
2816
+ return {
2817
+ ...n,
2818
+ style: {
2819
+ ...n.style,
2820
+ opacity: coneRevealed ? 1 : 0.22,
2821
+ transition: "opacity 320ms ease",
2822
+ transitionDelay: `${depth * 90}ms`
2823
+ }
2824
+ };
2825
+ });
2826
+ }, [reactFlowNodes, sliceCone, coneRevealed]);
2827
+ const conedEdges = (0, import_react18.useMemo)(() => {
2828
+ if (!sliceCone || sliceCone.size === 0) return reactFlowEdges;
2829
+ return reactFlowEdges.map((e) => {
2830
+ const inCone = sliceCone.has(e.source) && sliceCone.has(e.target);
2831
+ return inCone ? e : { ...e, style: { ...e.style, opacity: 0.12, transition: "opacity 260ms ease" } };
2832
+ });
2833
+ }, [reactFlowEdges, sliceCone]);
2801
2834
  const handleNodeClick = (0, import_react18.useCallback)(
2802
2835
  (_, node) => {
2803
2836
  const data = node.data ?? {};
@@ -2839,8 +2872,8 @@ function TracedFlow({
2839
2872
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2840
2873
  import_react19.ReactFlow,
2841
2874
  {
2842
- nodes: reactFlowNodes,
2843
- edges: reactFlowEdges,
2875
+ nodes: conedNodes,
2876
+ edges: conedEdges,
2844
2877
  nodeTypes: mergedNodeTypes,
2845
2878
  edgeTypes: mergedEdgeTypes,
2846
2879
  onNodeClick: handleNodeClick,