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/flowchart.js CHANGED
@@ -12,6 +12,9 @@ var rawDefaults = {
12
12
  success: "#22c55e",
13
13
  error: "#ef4444",
14
14
  warning: "#f59e0b",
15
+ nodeCursor: "#f59e0b",
16
+ nodeVisited: "#22c55e",
17
+ nodeMain: "#6366f1",
15
18
  bgPrimary: "#0f172a",
16
19
  bgSecondary: "#1e293b",
17
20
  bgTertiary: "#334155",
@@ -32,6 +35,9 @@ var defaultTokens = {
32
35
  success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
33
36
  error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
34
37
  warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
38
+ nodeCursor: `var(--fp-node-cursor, ${rawDefaults.colors.nodeCursor})`,
39
+ nodeVisited: `var(--fp-node-visited, ${rawDefaults.colors.nodeVisited})`,
40
+ nodeMain: `var(--fp-node-main, ${rawDefaults.colors.nodeMain})`,
35
41
  bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
36
42
  bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
37
43
  bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
@@ -60,6 +66,15 @@ var theme = {
60
66
  success: v("--fp-color-success", "#22c55e"),
61
67
  error: v("--fp-color-error", "#ef4444"),
62
68
  warning: v("--fp-color-warning", "#f59e0b"),
69
+ // Semantic NODE-STATE colors — first-class, themeable roles a runtime overlay
70
+ // maps onto (scrub cursor / executed / a group's lead node). Distinct from the
71
+ // generic `primary` accent so the three read as three different things.
72
+ nodeCursor: v("--fp-node-cursor", "#f59e0b"),
73
+ // the current / scrubbed-to step
74
+ nodeVisited: v("--fp-node-visited", "#22c55e"),
75
+ // executed up to the cursor
76
+ nodeMain: v("--fp-node-main", "#6366f1"),
77
+ // the lead / "hero" node of a group
63
78
  bgPrimary: v("--fp-bg-primary", "#0f172a"),
64
79
  bgSecondary: v("--fp-bg-secondary", "#1e293b"),
65
80
  bgTertiary: v("--fp-bg-tertiary", "#334155"),
@@ -275,12 +290,12 @@ var StageNode = memo(function StageNode2({
275
290
  const isHero = data.emphasis === "hero";
276
291
  const isMuted = data.emphasis === "muted";
277
292
  const sizeScale = data.size === "lg" ? 1.3 : data.size === "sm" ? 0.85 : 1;
278
- const restingBg = isHero ? `color-mix(in srgb, ${theme.primary} 12%, ${theme.bgSecondary})` : theme.bgSecondary;
279
- const restingBorder = isHero ? theme.primary : theme.border;
280
- const restingShadow = isHero ? `0 0 10px color-mix(in srgb, ${theme.primary} 22%, transparent)` : `0 2px 8px rgba(0,0,0,0.15)`;
281
- const bg = active ? theme.primary : done ? theme.success : error ? theme.error : restingBg;
282
- const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : restingBorder;
283
- 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;
293
+ const restingBg = isHero ? `color-mix(in srgb, ${theme.nodeMain} 12%, ${theme.bgSecondary})` : theme.bgSecondary;
294
+ const restingBorder = isHero ? theme.nodeMain : theme.border;
295
+ const restingShadow = isHero ? `0 0 10px color-mix(in srgb, ${theme.nodeMain} 22%, transparent)` : `0 2px 8px rgba(0,0,0,0.15)`;
296
+ const bg = active ? theme.nodeCursor : done ? theme.nodeVisited : error ? theme.error : restingBg;
297
+ const borderColor = active ? theme.nodeCursor : done ? theme.nodeVisited : error ? theme.error : restingBorder;
298
+ 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;
284
299
  const textColor = active || done || error ? "#fff" : theme.textPrimary;
285
300
  return /* @__PURE__ */ jsxs(Fragment, { children: [
286
301
  /* @__PURE__ */ jsx2(Handle, { type: "target", position: Position.Top, style: { opacity: 0 } }),
@@ -310,8 +325,8 @@ var StageNode = memo(function StageNode2({
310
325
  },
311
326
  children: stepNumbers.map((num, i) => {
312
327
  const isLatest = i === stepNumbers.length - 1;
313
- const badgeBg = isLatest && active ? theme.primary : theme.success;
314
- const glow = isLatest && active ? `color-mix(in srgb, ${theme.primary} 50%, transparent)` : `color-mix(in srgb, ${theme.success} 40%, transparent)`;
328
+ const badgeBg = isLatest && active ? theme.nodeCursor : theme.nodeVisited;
329
+ const glow = isLatest && active ? `color-mix(in srgb, ${theme.nodeCursor} 50%, transparent)` : `color-mix(in srgb, ${theme.nodeVisited} 40%, transparent)`;
315
330
  return /* @__PURE__ */ jsx2(
316
331
  "div",
317
332
  {
@@ -357,7 +372,7 @@ var StageNode = memo(function StageNode2({
357
372
  inset: -6,
358
373
  borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
359
374
  clipPath: isDecider ? "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)" : void 0,
360
- border: `2px solid ${theme.primary}`,
375
+ border: `2px solid ${theme.nodeCursor}`,
361
376
  opacity: 0.3,
362
377
  animation: "fp-pulse 1.5s ease-out infinite"
363
378
  }
@@ -1994,6 +2009,18 @@ function centerForkParents(graph, options = {}) {
1994
2009
  }
1995
2010
  return minX <= maxX ? Math.max(minX, Math.min(maxX, desiredX)) : x0;
1996
2011
  };
2012
+ const evenFanKids = (forkCenter, kids) => {
2013
+ if (kids.length < 2) return;
2014
+ const sorted = [...kids].sort((a, b) => centerX(a) - centerX(b));
2015
+ let gap = 0;
2016
+ for (let i = 0; i < sorted.length - 1; i++) {
2017
+ gap = Math.max(gap, width.get(sorted[i]) / 2 + nodeSep + width.get(sorted[i + 1]) / 2);
2018
+ }
2019
+ const mid = (sorted.length - 1) / 2;
2020
+ for (let i = 0; i < sorted.length; i++) {
2021
+ workingX.set(sorted[i], forkCenter + (i - mid) * gap - width.get(sorted[i]) / 2);
2022
+ }
2023
+ };
1997
2024
  const order = [...graph.nodes].sort(
1998
2025
  (a, b) => b.position.y - a.position.y || a.position.x - b.position.x || a.id.localeCompare(b.id)
1999
2026
  );
@@ -2012,6 +2039,11 @@ function centerForkParents(graph, options = {}) {
2012
2039
  const wN = width.get(n.id);
2013
2040
  const span = (Math.min(...centers) + Math.max(...centers)) / 2;
2014
2041
  workingX.set(n.id, clampX(n.id, span - wN / 2));
2042
+ if (isFork) {
2043
+ const succSets = kin.map((k) => childrenOf.get(k) ?? []);
2044
+ const isDiamond = kin.length >= 2 && succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
2045
+ if (isDiamond) evenFanKids(centerX(n.id), kin);
2046
+ }
2015
2047
  const stepOf = isFork ? predsOf : childrenOf;
2016
2048
  let curId = n.id;
2017
2049
  const walked = /* @__PURE__ */ new Set([curId]);
@@ -2028,6 +2060,28 @@ function centerForkParents(graph, options = {}) {
2028
2060
  curId = m;
2029
2061
  }
2030
2062
  }
2063
+ for (const n of order) {
2064
+ const outD = outDegree.get(n.id) ?? 0;
2065
+ const inD = inDegree.get(n.id) ?? 0;
2066
+ if (!(outD >= 2 && inD <= 1)) continue;
2067
+ const kids = (childrenOf.get(n.id) ?? []).filter(
2068
+ (k) => byId.get(k)?.parentId === n.parentId
2069
+ );
2070
+ if (kids.length < 2) continue;
2071
+ const succSets = kids.map((k) => childrenOf.get(k) ?? []);
2072
+ const isDiamond = succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
2073
+ if (isDiamond) continue;
2074
+ const ps = predsOf.get(n.id);
2075
+ if (!ps || ps.length !== 1) continue;
2076
+ const pred = ps[0];
2077
+ if ((outDegree.get(pred) ?? 0) !== 1) continue;
2078
+ if (byId.get(pred)?.parentId !== byId.get(n.id)?.parentId) continue;
2079
+ const before = centerX(n.id);
2080
+ workingX.set(n.id, clampX(n.id, centerX(pred) - width.get(n.id) / 2));
2081
+ const delta = centerX(n.id) - before;
2082
+ if (delta === 0) continue;
2083
+ for (const k of kids) workingX.set(k, clampX(k, workingX.get(k) + delta));
2084
+ }
2031
2085
  const nodes = graph.nodes.map(
2032
2086
  (n) => workingX.get(n.id) === n.position.x ? n : { ...n, position: { x: workingX.get(n.id), y: n.position.y } }
2033
2087
  );