footprint-explainable-ui 0.25.1 → 0.25.3

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.
@@ -1955,6 +1955,16 @@ function TraceFlow(props) {
1955
1955
  var import_react18 = require("react");
1956
1956
  var import_react19 = require("@xyflow/react");
1957
1957
 
1958
+ // src/components/FlowchartView/_internal/devWarn.ts
1959
+ function isDevModeEnv() {
1960
+ const proc = globalThis.process;
1961
+ return proc?.env?.NODE_ENV !== "production";
1962
+ }
1963
+ function devWarn(messageFn, ...extras) {
1964
+ if (!isDevModeEnv()) return;
1965
+ console.warn(messageFn(), ...extras);
1966
+ }
1967
+
1958
1968
  // src/components/FlowchartView/_internal/snapLinearSuccessors.ts
1959
1969
  function snapLinearSuccessors(graph, options = {}) {
1960
1970
  if (graph.nodes.length === 0) return graph;
@@ -2056,36 +2066,79 @@ function centerForkParents(graph, options = {}) {
2056
2066
  }
2057
2067
  return minX <= maxX ? Math.max(minX, Math.min(maxX, desiredX)) : x0;
2058
2068
  };
2069
+ const evenFanKids = (forkCenter, kids) => {
2070
+ if (kids.length < 2) return;
2071
+ const sorted = [...kids].sort((a, b) => centerX(a) - centerX(b));
2072
+ let gap = 0;
2073
+ for (let i = 0; i < sorted.length - 1; i++) {
2074
+ gap = Math.max(gap, width.get(sorted[i]) / 2 + nodeSep + width.get(sorted[i + 1]) / 2);
2075
+ }
2076
+ const mid = (sorted.length - 1) / 2;
2077
+ for (let i = 0; i < sorted.length; i++) {
2078
+ workingX.set(sorted[i], forkCenter + (i - mid) * gap - width.get(sorted[i]) / 2);
2079
+ }
2080
+ };
2059
2081
  const order = [...graph.nodes].sort(
2060
2082
  (a, b) => b.position.y - a.position.y || a.position.x - b.position.x || a.id.localeCompare(b.id)
2061
2083
  );
2062
2084
  for (const n of order) {
2063
- if ((outDegree.get(n.id) ?? 0) < 2) continue;
2064
- if ((inDegree.get(n.id) ?? 0) > 1) continue;
2065
- const kids = (childrenOf.get(n.id) ?? []).filter(
2085
+ const outD = outDegree.get(n.id) ?? 0;
2086
+ const inD = inDegree.get(n.id) ?? 0;
2087
+ const isFork = outD >= 2 && inD <= 1;
2088
+ const isMerge = inD >= 2 && outD <= 1;
2089
+ if (!isFork && !isMerge) continue;
2090
+ const kin = ((isFork ? childrenOf.get(n.id) : predsOf.get(n.id)) ?? []).filter(
2066
2091
  (k) => byId.get(k)?.parentId === n.parentId
2067
2092
  // same compound only
2068
2093
  );
2069
- if (kids.length < 2) continue;
2070
- const centers = kids.map(centerX);
2094
+ if (kin.length < 2) continue;
2095
+ const centers = kin.map(centerX);
2071
2096
  const wN = width.get(n.id);
2072
2097
  const span = (Math.min(...centers) + Math.max(...centers)) / 2;
2073
2098
  workingX.set(n.id, clampX(n.id, span - wN / 2));
2099
+ if (isFork) {
2100
+ const succSets = kin.map((k) => childrenOf.get(k) ?? []);
2101
+ const isDiamond = kin.length >= 2 && succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
2102
+ if (isDiamond) evenFanKids(centerX(n.id), kin);
2103
+ }
2104
+ const stepOf = isFork ? predsOf : childrenOf;
2074
2105
  let curId = n.id;
2075
2106
  const walked = /* @__PURE__ */ new Set([curId]);
2076
2107
  for (; ; ) {
2077
- const ps = predsOf.get(curId);
2078
- if (!ps || ps.length !== 1) break;
2079
- const p = ps[0];
2080
- if (walked.has(p)) break;
2081
- if ((outDegree.get(p) ?? 0) !== 1) break;
2082
- if ((inDegree.get(p) ?? 0) > 1) break;
2083
- if (byId.get(p)?.parentId !== byId.get(curId)?.parentId) break;
2084
- workingX.set(p, clampX(p, centerX(curId) - width.get(p) / 2));
2085
- walked.add(p);
2086
- curId = p;
2108
+ const nexts = stepOf.get(curId);
2109
+ if (!nexts || nexts.length !== 1) break;
2110
+ const m = nexts[0];
2111
+ if (walked.has(m)) break;
2112
+ if ((outDegree.get(m) ?? 0) > 1) break;
2113
+ if ((inDegree.get(m) ?? 0) > 1) break;
2114
+ if (byId.get(m)?.parentId !== byId.get(curId)?.parentId) break;
2115
+ workingX.set(m, clampX(m, centerX(curId) - width.get(m) / 2));
2116
+ walked.add(m);
2117
+ curId = m;
2087
2118
  }
2088
2119
  }
2120
+ for (const n of order) {
2121
+ const outD = outDegree.get(n.id) ?? 0;
2122
+ const inD = inDegree.get(n.id) ?? 0;
2123
+ if (!(outD >= 2 && inD <= 1)) continue;
2124
+ const kids = (childrenOf.get(n.id) ?? []).filter(
2125
+ (k) => byId.get(k)?.parentId === n.parentId
2126
+ );
2127
+ if (kids.length < 2) continue;
2128
+ const succSets = kids.map((k) => childrenOf.get(k) ?? []);
2129
+ const isDiamond = succSets[0].some((s) => succSets.every((ss) => ss.includes(s)));
2130
+ if (isDiamond) continue;
2131
+ const ps = predsOf.get(n.id);
2132
+ if (!ps || ps.length !== 1) continue;
2133
+ const pred = ps[0];
2134
+ if ((outDegree.get(pred) ?? 0) !== 1) continue;
2135
+ if (byId.get(pred)?.parentId !== byId.get(n.id)?.parentId) continue;
2136
+ const before = centerX(n.id);
2137
+ workingX.set(n.id, clampX(n.id, centerX(pred) - width.get(n.id) / 2));
2138
+ const delta = centerX(n.id) - before;
2139
+ if (delta === 0) continue;
2140
+ for (const k of kids) workingX.set(k, clampX(k, workingX.get(k) + delta));
2141
+ }
2089
2142
  const nodes = graph.nodes.map(
2090
2143
  (n) => workingX.get(n.id) === n.position.x ? n : { ...n, position: { x: workingX.get(n.id), y: n.position.y } }
2091
2144
  );
@@ -2095,16 +2148,6 @@ function withForkCentering(base, options = {}) {
2095
2148
  return (graph) => centerForkParents(base(graph), options);
2096
2149
  }
2097
2150
 
2098
- // src/components/FlowchartView/_internal/devWarn.ts
2099
- function isDevModeEnv() {
2100
- const proc = globalThis.process;
2101
- return proc?.env?.NODE_ENV !== "production";
2102
- }
2103
- function devWarn(messageFn, ...extras) {
2104
- if (!isDevModeEnv()) return;
2105
- console.warn(messageFn(), ...extras);
2106
- }
2107
-
2108
2151
  // src/components/FlowchartView/_internal/notifyChange.ts
2109
2152
  function createNotifier(label = "notifier") {
2110
2153
  const listeners = /* @__PURE__ */ new Set();
@@ -2644,6 +2687,13 @@ function TracedFlow({
2644
2687
  style
2645
2688
  }) {
2646
2689
  const layout = layoutProp ?? dagreTraceLayout;
2690
+ (0, import_react18.useEffect)(() => {
2691
+ if (layoutProp === dagreTraceLayout) {
2692
+ devWarn(
2693
+ () => "[footprint-explainable-ui] <TracedFlow layout={dagreTraceLayout}> bypasses the built-in measure-then-layout pipeline (content-exact sizing, fork/merge centering, straight spines). OMIT the `layout` prop to use it \u2014 passing the raw dagreTraceLayout silently forfeits every layout improvement eui ships."
2694
+ );
2695
+ }
2696
+ }, [layoutProp]);
2647
2697
  const colors = (0, import_react18.useMemo)(
2648
2698
  () => ({ ...DEFAULT_COLORS, ...colorOverrides ?? {} }),
2649
2699
  [colorOverrides]