footprint-explainable-ui 0.25.2 → 0.25.4

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
@@ -68,6 +68,11 @@ interface ThemeTokens {
68
68
  success?: string;
69
69
  error?: string;
70
70
  warning?: string;
71
+ /** Semantic node-state roles (a runtime overlay maps onto these): the scrub
72
+ * cursor, the visited path, and a group's lead node. */
73
+ nodeCursor?: string;
74
+ nodeVisited?: string;
75
+ nodeMain?: string;
71
76
  bgPrimary?: string;
72
77
  bgSecondary?: string;
73
78
  bgTertiary?: string;
@@ -91,6 +96,9 @@ declare const rawDefaults: {
91
96
  readonly success: "#22c55e";
92
97
  readonly error: "#ef4444";
93
98
  readonly warning: "#f59e0b";
99
+ readonly nodeCursor: "#f59e0b";
100
+ readonly nodeVisited: "#22c55e";
101
+ readonly nodeMain: "#6366f1";
94
102
  readonly bgPrimary: "#0f172a";
95
103
  readonly bgSecondary: "#1e293b";
96
104
  readonly bgTertiary: "#334155";
package/dist/index.d.ts CHANGED
@@ -68,6 +68,11 @@ interface ThemeTokens {
68
68
  success?: string;
69
69
  error?: string;
70
70
  warning?: string;
71
+ /** Semantic node-state roles (a runtime overlay maps onto these): the scrub
72
+ * cursor, the visited path, and a group's lead node. */
73
+ nodeCursor?: string;
74
+ nodeVisited?: string;
75
+ nodeMain?: string;
71
76
  bgPrimary?: string;
72
77
  bgSecondary?: string;
73
78
  bgTertiary?: string;
@@ -91,6 +96,9 @@ declare const rawDefaults: {
91
96
  readonly success: "#22c55e";
92
97
  readonly error: "#ef4444";
93
98
  readonly warning: "#f59e0b";
99
+ readonly nodeCursor: "#f59e0b";
100
+ readonly nodeVisited: "#22c55e";
101
+ readonly nodeMain: "#6366f1";
94
102
  readonly bgPrimary: "#0f172a";
95
103
  readonly bgSecondary: "#1e293b";
96
104
  readonly bgTertiary: "#334155";
package/dist/index.js CHANGED
@@ -10,6 +10,9 @@ function tokensToCSSVars(tokens) {
10
10
  if (c.success) vars["--fp-color-success"] = c.success;
11
11
  if (c.error) vars["--fp-color-error"] = c.error;
12
12
  if (c.warning) vars["--fp-color-warning"] = c.warning;
13
+ if (c.nodeCursor) vars["--fp-node-cursor"] = c.nodeCursor;
14
+ if (c.nodeVisited) vars["--fp-node-visited"] = c.nodeVisited;
15
+ if (c.nodeMain) vars["--fp-node-main"] = c.nodeMain;
13
16
  if (c.bgPrimary) vars["--fp-bg-primary"] = c.bgPrimary;
14
17
  if (c.bgSecondary) vars["--fp-bg-secondary"] = c.bgSecondary;
15
18
  if (c.bgTertiary) vars["--fp-bg-tertiary"] = c.bgTertiary;
@@ -29,6 +32,9 @@ var rawDefaults = {
29
32
  success: "#22c55e",
30
33
  error: "#ef4444",
31
34
  warning: "#f59e0b",
35
+ nodeCursor: "#f59e0b",
36
+ nodeVisited: "#22c55e",
37
+ nodeMain: "#6366f1",
32
38
  bgPrimary: "#0f172a",
33
39
  bgSecondary: "#1e293b",
34
40
  bgTertiary: "#334155",
@@ -49,6 +55,9 @@ var defaultTokens = {
49
55
  success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
50
56
  error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
51
57
  warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
58
+ nodeCursor: `var(--fp-node-cursor, ${rawDefaults.colors.nodeCursor})`,
59
+ nodeVisited: `var(--fp-node-visited, ${rawDefaults.colors.nodeVisited})`,
60
+ nodeMain: `var(--fp-node-main, ${rawDefaults.colors.nodeMain})`,
52
61
  bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
53
62
  bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
54
63
  bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
@@ -91,6 +100,15 @@ var theme = {
91
100
  success: v("--fp-color-success", "#22c55e"),
92
101
  error: v("--fp-color-error", "#ef4444"),
93
102
  warning: v("--fp-color-warning", "#f59e0b"),
103
+ // Semantic NODE-STATE colors — first-class, themeable roles a runtime overlay
104
+ // maps onto (scrub cursor / executed / a group's lead node). Distinct from the
105
+ // generic `primary` accent so the three read as three different things.
106
+ nodeCursor: v("--fp-node-cursor", "#f59e0b"),
107
+ // the current / scrubbed-to step
108
+ nodeVisited: v("--fp-node-visited", "#22c55e"),
109
+ // executed up to the cursor
110
+ nodeMain: v("--fp-node-main", "#6366f1"),
111
+ // the lead / "hero" node of a group
94
112
  bgPrimary: v("--fp-bg-primary", "#0f172a"),
95
113
  bgSecondary: v("--fp-bg-secondary", "#1e293b"),
96
114
  bgTertiary: v("--fp-bg-tertiary", "#334155"),
@@ -3564,6 +3582,18 @@ function centerForkParents(graph, options = {}) {
3564
3582
  }
3565
3583
  return minX <= maxX ? Math.max(minX, Math.min(maxX, desiredX)) : x0;
3566
3584
  };
3585
+ const evenFanKids = (forkCenter, kids) => {
3586
+ if (kids.length < 2) return;
3587
+ const sorted = [...kids].sort((a, b) => centerX(a) - centerX(b));
3588
+ let gap = 0;
3589
+ for (let i = 0; i < sorted.length - 1; i++) {
3590
+ gap = Math.max(gap, width.get(sorted[i]) / 2 + nodeSep + width.get(sorted[i + 1]) / 2);
3591
+ }
3592
+ const mid = (sorted.length - 1) / 2;
3593
+ for (let i = 0; i < sorted.length; i++) {
3594
+ workingX.set(sorted[i], forkCenter + (i - mid) * gap - width.get(sorted[i]) / 2);
3595
+ }
3596
+ };
3567
3597
  const order = [...graph.nodes].sort(
3568
3598
  (a, b) => b.position.y - a.position.y || a.position.x - b.position.x || a.id.localeCompare(b.id)
3569
3599
  );
@@ -3582,6 +3612,11 @@ function centerForkParents(graph, options = {}) {
3582
3612
  const wN = width.get(n.id);
3583
3613
  const span = (Math.min(...centers) + Math.max(...centers)) / 2;
3584
3614
  workingX.set(n.id, clampX(n.id, span - wN / 2));
3615
+ if (isFork) {
3616
+ const succSets = kin.map((k) => childrenOf.get(k) ?? []);
3617
+ const isDiamond = kin.length >= 2 && succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
3618
+ if (isDiamond) evenFanKids(centerX(n.id), kin);
3619
+ }
3585
3620
  const stepOf = isFork ? predsOf : childrenOf;
3586
3621
  let curId = n.id;
3587
3622
  const walked = /* @__PURE__ */ new Set([curId]);
@@ -3598,6 +3633,28 @@ function centerForkParents(graph, options = {}) {
3598
3633
  curId = m;
3599
3634
  }
3600
3635
  }
3636
+ for (const n of order) {
3637
+ const outD = outDegree.get(n.id) ?? 0;
3638
+ const inD = inDegree.get(n.id) ?? 0;
3639
+ if (!(outD >= 2 && inD <= 1)) continue;
3640
+ const kids = (childrenOf.get(n.id) ?? []).filter(
3641
+ (k) => byId.get(k)?.parentId === n.parentId
3642
+ );
3643
+ if (kids.length < 2) continue;
3644
+ const succSets = kids.map((k) => childrenOf.get(k) ?? []);
3645
+ const isDiamond = succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
3646
+ if (isDiamond) continue;
3647
+ const ps = predsOf.get(n.id);
3648
+ if (!ps || ps.length !== 1) continue;
3649
+ const pred = ps[0];
3650
+ if ((outDegree.get(pred) ?? 0) !== 1) continue;
3651
+ if (byId.get(pred)?.parentId !== byId.get(n.id)?.parentId) continue;
3652
+ const before = centerX(n.id);
3653
+ workingX.set(n.id, clampX(n.id, centerX(pred) - width.get(n.id) / 2));
3654
+ const delta = centerX(n.id) - before;
3655
+ if (delta === 0) continue;
3656
+ for (const k of kids) workingX.set(k, clampX(k, workingX.get(k) + delta));
3657
+ }
3601
3658
  const nodes = graph.nodes.map(
3602
3659
  (n) => workingX.get(n.id) === n.position.x ? n : { ...n, position: { x: workingX.get(n.id), y: n.position.y } }
3603
3660
  );
@@ -3830,12 +3887,12 @@ var StageNode = memo3(function StageNode2({
3830
3887
  const isHero = data.emphasis === "hero";
3831
3888
  const isMuted = data.emphasis === "muted";
3832
3889
  const sizeScale = data.size === "lg" ? 1.3 : data.size === "sm" ? 0.85 : 1;
3833
- const restingBg = isHero ? `color-mix(in srgb, ${theme.primary} 12%, ${theme.bgSecondary})` : theme.bgSecondary;
3834
- const restingBorder = isHero ? theme.primary : theme.border;
3835
- const restingShadow = isHero ? `0 0 10px color-mix(in srgb, ${theme.primary} 22%, transparent)` : `0 2px 8px rgba(0,0,0,0.15)`;
3836
- const bg = active ? theme.primary : done ? theme.success : error ? theme.error : restingBg;
3837
- const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : restingBorder;
3838
- const shadow = active ? `0 0 22px color-mix(in srgb, ${theme.primary} 55%, transparent)` : done ? `0 0 8px color-mix(in srgb, ${theme.success} 20%, transparent)` : error ? `0 0 12px color-mix(in srgb, ${theme.error} 30%, transparent)` : restingShadow;
3890
+ const restingBg = isHero ? `color-mix(in srgb, ${theme.nodeMain} 12%, ${theme.bgSecondary})` : theme.bgSecondary;
3891
+ const restingBorder = isHero ? theme.nodeMain : theme.border;
3892
+ const restingShadow = isHero ? `0 0 10px color-mix(in srgb, ${theme.nodeMain} 22%, transparent)` : `0 2px 8px rgba(0,0,0,0.15)`;
3893
+ const bg = active ? theme.nodeCursor : done ? theme.nodeVisited : error ? theme.error : restingBg;
3894
+ const borderColor = active ? theme.nodeCursor : done ? theme.nodeVisited : error ? theme.error : restingBorder;
3895
+ const shadow = active ? `0 0 22px color-mix(in srgb, ${theme.nodeCursor} 55%, transparent)` : done ? `0 0 8px color-mix(in srgb, ${theme.nodeVisited} 20%, transparent)` : error ? `0 0 12px color-mix(in srgb, ${theme.error} 30%, transparent)` : restingShadow;
3839
3896
  const textColor = active || done || error ? "#fff" : theme.textPrimary;
3840
3897
  return /* @__PURE__ */ jsxs15(Fragment5, { children: [
3841
3898
  /* @__PURE__ */ jsx16(Handle, { type: "target", position: Position.Top, style: { opacity: 0 } }),
@@ -3865,8 +3922,8 @@ var StageNode = memo3(function StageNode2({
3865
3922
  },
3866
3923
  children: stepNumbers.map((num, i) => {
3867
3924
  const isLatest = i === stepNumbers.length - 1;
3868
- const badgeBg = isLatest && active ? theme.primary : theme.success;
3869
- const glow = isLatest && active ? `color-mix(in srgb, ${theme.primary} 50%, transparent)` : `color-mix(in srgb, ${theme.success} 40%, transparent)`;
3925
+ const badgeBg = isLatest && active ? theme.nodeCursor : theme.nodeVisited;
3926
+ const glow = isLatest && active ? `color-mix(in srgb, ${theme.nodeCursor} 50%, transparent)` : `color-mix(in srgb, ${theme.nodeVisited} 40%, transparent)`;
3870
3927
  return /* @__PURE__ */ jsx16(
3871
3928
  "div",
3872
3929
  {
@@ -3912,7 +3969,7 @@ var StageNode = memo3(function StageNode2({
3912
3969
  inset: -6,
3913
3970
  borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
3914
3971
  clipPath: isDecider ? "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)" : void 0,
3915
- border: `2px solid ${theme.primary}`,
3972
+ border: `2px solid ${theme.nodeCursor}`,
3916
3973
  opacity: 0.3,
3917
3974
  animation: "fp-pulse 1.5s ease-out infinite"
3918
3975
  }