footprint-explainable-ui 0.15.0 → 0.17.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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -39,6 +49,7 @@ __export(src_exports, {
39
49
  StoryNarrative: () => StoryNarrative,
40
50
  SubflowTree: () => SubflowTree,
41
51
  TimeTravelControls: () => TimeTravelControls,
52
+ TraceViewer: () => TraceViewer,
42
53
  buildEntryRangeIndex: () => buildEntryRangeIndex,
43
54
  computeRevealedEntryCount: () => computeRevealedEntryCount,
44
55
  coolDark: () => coolDark,
@@ -4014,26 +4025,56 @@ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4014
4025
  fromStageName
4015
4026
  }) {
4016
4027
  if (frames.length === 0) {
4017
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { padding: 12, color: theme.textMuted, fontSize: 13 }, children: "Select a stage to see its data dependency chain." });
4028
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
4029
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4030
+ "div",
4031
+ {
4032
+ style: {
4033
+ fontSize: 11,
4034
+ color: theme.textMuted,
4035
+ textTransform: "uppercase",
4036
+ letterSpacing: "0.5px",
4037
+ fontWeight: 600,
4038
+ marginBottom: 6
4039
+ },
4040
+ children: "Backward causal chain"
4041
+ }
4042
+ ),
4043
+ /* @__PURE__ */ (0, import_jsx_runtime18.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." }),
4044
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
4045
+ ] });
4018
4046
  }
4019
4047
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
4020
- fromStageName && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4021
- "div",
4022
- {
4023
- style: {
4024
- padding: "4px 12px 8px",
4025
- fontSize: 11,
4026
- color: theme.textMuted,
4027
- textTransform: "uppercase",
4028
- letterSpacing: "0.5px",
4029
- fontWeight: 600
4030
- },
4031
- children: [
4032
- "Data trace from ",
4033
- fromStageName
4034
- ]
4035
- }
4036
- ),
4048
+ fromStageName && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "4px 12px 8px" }, children: [
4049
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4050
+ "div",
4051
+ {
4052
+ style: {
4053
+ fontSize: 11,
4054
+ color: theme.textMuted,
4055
+ textTransform: "uppercase",
4056
+ letterSpacing: "0.5px",
4057
+ fontWeight: 600
4058
+ },
4059
+ children: [
4060
+ "Data trace from ",
4061
+ fromStageName
4062
+ ]
4063
+ }
4064
+ ),
4065
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4066
+ "div",
4067
+ {
4068
+ style: {
4069
+ fontSize: 11,
4070
+ color: theme.textMuted,
4071
+ fontStyle: "italic",
4072
+ marginTop: 3
4073
+ },
4074
+ children: "Every value here was derived from the stages below."
4075
+ }
4076
+ )
4077
+ ] }),
4037
4078
  frames.map((frame, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4038
4079
  DataTraceFrame,
4039
4080
  {
@@ -4842,6 +4883,79 @@ function buildDataTrace(commitLog, targetRuntimeStageId, maxDepth = 10) {
4842
4883
  }
4843
4884
  return frames;
4844
4885
  }
4886
+ var RightPanel = (0, import_react24.memo)(function RightPanel2({
4887
+ mode,
4888
+ onModeChange,
4889
+ snapshots,
4890
+ selectedIndex,
4891
+ runtimeSnapshot,
4892
+ activeTab,
4893
+ allTabs,
4894
+ activeNarrativeEntries,
4895
+ activeNarrative,
4896
+ recorderViews,
4897
+ autoRecorderViews,
4898
+ size,
4899
+ onNavigateToStage
4900
+ }) {
4901
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4902
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
4903
+ display: "flex",
4904
+ borderBottom: `1px solid ${theme.border}`,
4905
+ flexShrink: 0,
4906
+ background: theme.bgSecondary
4907
+ }, children: ["insights", "what"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4908
+ "button",
4909
+ {
4910
+ onClick: () => onModeChange(m),
4911
+ style: {
4912
+ flex: 1,
4913
+ padding: "7px 12px",
4914
+ fontSize: 11,
4915
+ fontWeight: mode === m ? 700 : 500,
4916
+ textTransform: "uppercase",
4917
+ letterSpacing: "0.06em",
4918
+ color: mode === m ? theme.primary : theme.textMuted,
4919
+ background: "transparent",
4920
+ border: "none",
4921
+ borderBottom: mode === m ? `2px solid ${theme.primary}` : "2px solid transparent",
4922
+ cursor: "pointer",
4923
+ fontFamily: "inherit"
4924
+ },
4925
+ children: m === "insights" ? "Insights" : "Inspector"
4926
+ },
4927
+ m
4928
+ )) }),
4929
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: mode === "insights" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4930
+ InsightPanel,
4931
+ {
4932
+ mode: "tabs",
4933
+ expandedId: activeTab,
4934
+ insights: allTabs.filter((t) => t.id !== "result" && t.id !== "memory").map((tab) => ({
4935
+ id: tab.id,
4936
+ name: insightName(tab.name),
4937
+ render: () => {
4938
+ if (tab.id === "narrative") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots, selectedIndex, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
4939
+ const customView = recorderViews?.find((v2) => v2.id === tab.id);
4940
+ if (customView?.render) return customView.render({ snapshots, selectedIndex });
4941
+ const autoView = autoRecorderViews.find((v2) => v2.id === tab.id);
4942
+ if (autoView) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(KeyedRecorderView, { data: autoView.data, description: autoView.description, preferredOperation: autoView.preferredOperation, snapshots, selectedIndex });
4943
+ return null;
4944
+ }
4945
+ }))
4946
+ }
4947
+ ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4948
+ InspectorPanel,
4949
+ {
4950
+ snapshots,
4951
+ selectedIndex,
4952
+ dataTraceFrames: runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, snapshots[selectedIndex]?.runtimeStageId ?? "") : [],
4953
+ selectedStageId: snapshots[selectedIndex]?.runtimeStageId,
4954
+ onNavigateToStage
4955
+ }
4956
+ ) })
4957
+ ] });
4958
+ });
4845
4959
  function insightName(name) {
4846
4960
  const map = {
4847
4961
  "Narrative": "Story",
@@ -4951,6 +5065,7 @@ function ExplainableShell({
4951
5065
  const [snapshotIdx, setSnapshotIdx] = (0, import_react24.useState)(0);
4952
5066
  const [drillDownStack, setDrillDownStack] = (0, import_react24.useState)([]);
4953
5067
  const [rightExpanded, setRightExpanded] = (0, import_react24.useState)(defaultExpanded?.details ?? true);
5068
+ const [rightPanelMode, setRightPanelMode] = (0, import_react24.useState)("insights");
4954
5069
  const [leftExpanded, setLeftExpanded] = (0, import_react24.useState)(defaultExpanded?.topology ?? false);
4955
5070
  const [timelineExpanded, setTimelineExpanded] = (0, import_react24.useState)(defaultExpanded?.timeline ?? false);
4956
5071
  (0, import_react24.useEffect)(() => {
@@ -5205,56 +5320,48 @@ function ExplainableShell({
5205
5320
  timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
5206
5321
  ] })
5207
5322
  ) : (
5208
- /* ── Desktop: responsive layout ── */
5323
+ /* ── Desktop: two-column Flowchart | Right Panel ── */
5209
5324
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5210
5325
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
5211
- showTopology && !isMedium && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { width: 280, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
5212
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: effectiveRenderFlowchart({
5213
- spec: activeSpec,
5214
- snapshots: activeSnapshots,
5215
- selectedIndex: safeIdx,
5216
- onNodeClick: handleNodeClick
5217
- }) }),
5218
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
5219
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
5220
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
5221
- width: isMedium ? "50%" : 300,
5222
- flexShrink: isMedium ? 1 : 0,
5223
- flex: isMedium ? 1 : void 0,
5224
- overflow: "hidden",
5225
- borderRight: `1px solid ${theme.border}`
5226
- }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5227
- InspectorPanel,
5326
+ showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { width: 180, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
5327
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5328
+ SubflowTree,
5329
+ {
5330
+ spec,
5331
+ activeStage: rootOverlay.activeStage,
5332
+ doneStages: rootOverlay.doneStages,
5333
+ onNodeSelect: handleTreeNodeSelect
5334
+ }
5335
+ ) }),
5336
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Topology", expanded: true, side: "left", onClick: () => toggleLeft(false) })
5337
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Topology", expanded: false, side: "left", onClick: () => toggleLeft(true) })),
5338
+ showTopology ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: effectiveRenderFlowchart({
5339
+ spec: activeSpec,
5340
+ snapshots: activeSnapshots,
5341
+ selectedIndex: safeIdx,
5342
+ onNodeClick: handleNodeClick
5343
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1 } }),
5344
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Details", expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5345
+ rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { width: "42%", minWidth: 320, maxWidth: 550, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5346
+ RightPanel,
5228
5347
  {
5348
+ mode: rightPanelMode,
5349
+ onModeChange: setRightPanelMode,
5229
5350
  snapshots: activeSnapshots,
5230
5351
  selectedIndex: safeIdx,
5231
- dataTraceFrames: runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, activeSnapshots[safeIdx]?.runtimeStageId ?? "") : [],
5232
- selectedStageId: activeSnapshots[safeIdx]?.runtimeStageId,
5352
+ runtimeSnapshot,
5353
+ activeTab,
5354
+ allTabs,
5355
+ activeNarrativeEntries,
5356
+ activeNarrative,
5357
+ recorderViews,
5358
+ autoRecorderViews,
5359
+ size,
5233
5360
  onNavigateToStage: (id) => {
5234
5361
  const idx = activeSnapshots.findIndex((s) => s.runtimeStageId === id);
5235
5362
  if (idx >= 0) setSnapshotIdx(idx);
5236
5363
  }
5237
5364
  }
5238
- ) }),
5239
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5240
- InsightPanel,
5241
- {
5242
- mode: !leftExpanded && !isMedium ? "grid" : "tabs",
5243
- expandedId: activeTab,
5244
- insights: allTabs.filter((t) => t.id !== "result").map((tab) => ({
5245
- id: tab.id,
5246
- name: insightName(tab.name),
5247
- render: () => {
5248
- if (tab.id === "memory") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
5249
- if (tab.id === "narrative") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
5250
- const customView = recorderViews?.find((v2) => v2.id === tab.id);
5251
- if (customView?.render) return customView.render({ snapshots: activeSnapshots, selectedIndex: safeIdx });
5252
- const autoView = autoRecorderViews.find((v2) => v2.id === tab.id);
5253
- if (autoView) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(KeyedRecorderView, { data: autoView.data, description: autoView.description, preferredOperation: autoView.preferredOperation, snapshots: activeSnapshots, selectedIndex: safeIdx });
5254
- return null;
5255
- }
5256
- }))
5257
- }
5258
5365
  ) })
5259
5366
  ] }),
5260
5367
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
@@ -5271,6 +5378,108 @@ function ExplainableShell({
5271
5378
  }
5272
5379
  );
5273
5380
  }
5381
+
5382
+ // src/components/TraceViewer/TraceViewer.tsx
5383
+ var React = __toESM(require("react"), 1);
5384
+ var import_react25 = require("react");
5385
+ var import_jsx_runtime23 = require("react/jsx-runtime");
5386
+ function parseTrace(input) {
5387
+ if (input == null) {
5388
+ return {
5389
+ ok: false,
5390
+ error: { kind: "invalid-json", message: "No trace provided." }
5391
+ };
5392
+ }
5393
+ let candidate = input;
5394
+ if (typeof input === "string") {
5395
+ if (!input.trim()) {
5396
+ return { ok: false, error: { kind: "invalid-json", message: "Empty input." } };
5397
+ }
5398
+ try {
5399
+ candidate = JSON.parse(input);
5400
+ } catch (err) {
5401
+ return {
5402
+ ok: false,
5403
+ error: { kind: "invalid-json", message: err.message }
5404
+ };
5405
+ }
5406
+ }
5407
+ if (!candidate || typeof candidate !== "object") {
5408
+ return {
5409
+ ok: false,
5410
+ error: { kind: "not-object", message: "Trace must be a JSON object." }
5411
+ };
5412
+ }
5413
+ const t = candidate;
5414
+ if (typeof t.schemaVersion !== "number") {
5415
+ return {
5416
+ ok: false,
5417
+ error: {
5418
+ kind: "missing-version",
5419
+ message: "Trace is missing required field `schemaVersion`. Did you pass an exportTrace() output?"
5420
+ }
5421
+ };
5422
+ }
5423
+ if (t.schemaVersion !== 1) {
5424
+ return {
5425
+ ok: false,
5426
+ error: {
5427
+ kind: "unsupported-version",
5428
+ message: `Unsupported schemaVersion ${t.schemaVersion}. This TraceViewer renders schemaVersion 1.`,
5429
+ version: t.schemaVersion
5430
+ }
5431
+ };
5432
+ }
5433
+ return { ok: true, trace: t };
5434
+ }
5435
+ var DEFAULT_TABS = ["explainable"];
5436
+ function TraceViewer({
5437
+ trace,
5438
+ onError,
5439
+ fallback,
5440
+ tabs = DEFAULT_TABS,
5441
+ defaultTab = "narrative",
5442
+ hideTabs,
5443
+ size,
5444
+ panelLabels,
5445
+ recorderViews,
5446
+ renderFlowchart
5447
+ }) {
5448
+ const parsed = (0, import_react25.useMemo)(() => parseTrace(trace), [trace]);
5449
+ React.useEffect(() => {
5450
+ if (!parsed.ok && onError) onError(parsed.error);
5451
+ }, [parsed, onError]);
5452
+ const snapshots = (0, import_react25.useMemo)(() => {
5453
+ if (!parsed.ok || !parsed.trace.snapshot) return [];
5454
+ try {
5455
+ return toVisualizationSnapshots(
5456
+ parsed.trace.snapshot,
5457
+ parsed.trace.narrativeEntries ?? void 0
5458
+ );
5459
+ } catch {
5460
+ return [];
5461
+ }
5462
+ }, [parsed]);
5463
+ if (!parsed.ok || snapshots.length === 0) {
5464
+ return fallback ?? null;
5465
+ }
5466
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5467
+ ExplainableShell,
5468
+ {
5469
+ snapshots,
5470
+ spec: parsed.trace.spec,
5471
+ narrative: parsed.trace.narrative,
5472
+ narrativeEntries: parsed.trace.narrativeEntries,
5473
+ tabs,
5474
+ defaultTab,
5475
+ hideTabs,
5476
+ size,
5477
+ panelLabels,
5478
+ recorderViews,
5479
+ renderFlowchart
5480
+ }
5481
+ );
5482
+ }
5274
5483
  // Annotate the CommonJS export names for ESM import in node:
5275
5484
  0 && (module.exports = {
5276
5485
  CompactTimeline,
@@ -5292,6 +5501,7 @@ function ExplainableShell({
5292
5501
  StoryNarrative,
5293
5502
  SubflowTree,
5294
5503
  TimeTravelControls,
5504
+ TraceViewer,
5295
5505
  buildEntryRangeIndex,
5296
5506
  computeRevealedEntryCount,
5297
5507
  coolDark,