@pipelex/mthds-ui 0.11.0 → 0.13.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.
@@ -1,11 +1,6 @@
1
1
  "use client";
2
2
  import {
3
3
  DEFAULT_GRAPH_CONFIG,
4
- EDGE_TYPE,
5
- FOLD_MODE,
6
- GRAPH_DIRECTION,
7
- GRAPH_THEME,
8
- GRAPH_THEME_MODE,
9
4
  MAX_VISIBLE_CONTROLLER_CHILDREN,
10
5
  applyControllers,
11
6
  applyFolds,
@@ -15,11 +10,24 @@ import {
15
10
  getPaletteForTheme,
16
11
  getPipeBlueprint,
17
12
  resolveConceptRef,
13
+ validateGraphSpec
14
+ } from "../../chunk-XCZLRCEJ.js";
15
+ import {
16
+ EDGE_TYPE,
17
+ FOLD_MODE,
18
+ GRAPH_DIRECTION,
19
+ GRAPH_SPEC_MODE,
20
+ GRAPH_THEME,
21
+ GRAPH_THEME_MODE,
22
+ TOOLBAR_POSITION,
23
+ VALIDATION_STATE,
24
+ graphSpecMode,
25
+ isDryGraphSpec,
26
+ isStaticGraphSpec,
18
27
  stuffDigestFromId,
19
28
  toolbarOrientation,
20
- toolbarSide,
21
- validateGraphSpec
22
- } from "../../chunk-R34J4GFM.js";
29
+ toolbarSide
30
+ } from "../../chunk-IMQ7MVYL.js";
23
31
  import {
24
32
  __spreadProps,
25
33
  __spreadValues
@@ -32,7 +40,7 @@ import "./stuff/StuffViewer.css";
32
40
  import "./viewer/GraphToolbar.css";
33
41
 
34
42
  // src/graph/react/viewer/GraphViewer.tsx
35
- import React9 from "react";
43
+ import React10 from "react";
36
44
  import {
37
45
  ReactFlow,
38
46
  useNodesState,
@@ -136,6 +144,24 @@ function extractStorageUri(data) {
136
144
  }
137
145
  return null;
138
146
  }
147
+ function extractInnerHtml(data) {
148
+ const fromObject = (obj2) => {
149
+ const value = obj2.inner_html;
150
+ if (typeof value === "string" && value.trim() !== "") return value;
151
+ return null;
152
+ };
153
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
154
+ const obj = data;
155
+ const direct = fromObject(obj);
156
+ if (direct != null) return direct;
157
+ for (const value of Object.values(obj)) {
158
+ if (value && typeof value === "object" && !Array.isArray(value)) {
159
+ const nested = fromObject(value);
160
+ if (nested != null) return nested;
161
+ }
162
+ }
163
+ return null;
164
+ }
139
165
  function extractFilename(data) {
140
166
  if (!data || typeof data !== "object") return null;
141
167
  const obj = data;
@@ -245,6 +271,7 @@ function StuffViewer({
245
271
  const httpExternalUrl = React2.useMemo(() => extractUrl(stuff.data), [stuff.data]);
246
272
  const storageUri = React2.useMemo(() => extractStorageUri(stuff.data), [stuff.data]);
247
273
  const filename = React2.useMemo(() => extractFilename(stuff.data), [stuff.data]);
274
+ const innerHtml = React2.useMemo(() => extractInnerHtml(stuff.data), [stuff.data]);
248
275
  const [resolvedStorageUrl, setResolvedStorageUrl] = React2.useState(null);
249
276
  React2.useEffect(() => {
250
277
  setResolvedStorageUrl(null);
@@ -340,6 +367,17 @@ function StuffViewer({
340
367
  }
341
368
  return renderMediaFallback("Image");
342
369
  }
370
+ if (innerHtml != null) {
371
+ return /* @__PURE__ */ jsx(
372
+ "iframe",
373
+ {
374
+ className: "stuff-viewer-html-frame",
375
+ title: stuff.name ? `${stuff.name} preview` : "HTML preview",
376
+ sandbox: "",
377
+ srcDoc: DOMPurify.sanitize(innerHtml, { WHOLE_DOCUMENT: true })
378
+ }
379
+ );
380
+ }
343
381
  if (stuff.dataHtml) {
344
382
  return /* @__PURE__ */ jsx(
345
383
  "div",
@@ -402,7 +440,7 @@ function StuffViewer({
402
440
  } else if (activeTab === "text") {
403
441
  textToCopy = stuff.dataText || jsonString || "";
404
442
  } else {
405
- textToCopy = stuff.dataText || stuff.dataHtml || jsonString || "";
443
+ textToCopy = innerHtml || stuff.dataText || stuff.dataHtml || jsonString || "";
406
444
  }
407
445
  if (!textToCopy) return;
408
446
  if (navigator.clipboard) {
@@ -433,7 +471,7 @@ function StuffViewer({
433
471
  );
434
472
  } else {
435
473
  downloadBlob(
436
- stuff.dataHtml || jsonString || "",
474
+ innerHtml || stuff.dataHtml || jsonString || "",
437
475
  `${stuff.name || "stuff"}.html`,
438
476
  "text/html"
439
477
  );
@@ -1234,6 +1272,8 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1234
1272
  var _a, _b, _c, _d, _e, _f, _g;
1235
1273
  const pipeType = node.pipe_type;
1236
1274
  const isController = CONTROLLER_TYPES.has(pipeType);
1275
+ const isStatic = isStaticGraphSpec(spec);
1276
+ const showsGeneratedData = !isStatic && !isDryGraphSpec(spec);
1237
1277
  const badge = PIPE_TYPE_BADGES[pipeType];
1238
1278
  const status = node.status;
1239
1279
  const statusColor = (_a = STATUS_COLORS[status]) != null ? _a : "#6272a4";
@@ -1269,7 +1309,7 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1269
1309
  }
1270
1310
  )
1271
1311
  ] }),
1272
- /* @__PURE__ */ jsxs14("div", { className: "detail-status", children: [
1312
+ !isStatic && /* @__PURE__ */ jsxs14("div", { className: "detail-status", children: [
1273
1313
  /* @__PURE__ */ jsx14("span", { className: "detail-status-dot", style: { background: statusColor } }),
1274
1314
  /* @__PURE__ */ jsx14("span", { className: "detail-status-label", style: { color: statusColor }, children: status }),
1275
1315
  ((_g = node.timing) == null ? void 0 : _g.duration) != null && /* @__PURE__ */ jsx14("span", { className: "detail-duration", children: formatDuration(node.timing.duration) })
@@ -1309,8 +1349,14 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1309
1349
  ] }),
1310
1350
  (inputs.length > 0 || outputs.length > 0) && /* @__PURE__ */ jsx14("div", { style: { borderTop: "1px solid rgba(255,255,255,0.06)", margin: "4px 0" } })
1311
1351
  ] }),
1312
- blueprint && /* @__PURE__ */ jsx14(BlueprintSection, { blueprint, executionData: node.execution_data }),
1313
- node.execution_data && Object.keys(node.execution_data).length > 0 && /* @__PURE__ */ jsx14(
1352
+ blueprint && /* @__PURE__ */ jsx14(
1353
+ BlueprintSection,
1354
+ {
1355
+ blueprint,
1356
+ executionData: showsGeneratedData ? node.execution_data : void 0
1357
+ }
1358
+ ),
1359
+ showsGeneratedData && node.execution_data && Object.keys(node.execution_data).length > 0 && /* @__PURE__ */ jsx14(
1314
1360
  ExecutionDataSection,
1315
1361
  {
1316
1362
  executionData: node.execution_data,
@@ -1323,7 +1369,7 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1323
1369
  /* @__PURE__ */ jsx14("div", { className: "detail-error-message", children: node.error.message }),
1324
1370
  node.error.stack && /* @__PURE__ */ jsx14("div", { className: "detail-error-stack", children: node.error.stack })
1325
1371
  ] }),
1326
- node.metrics && Object.keys(node.metrics).length > 0 && /* @__PURE__ */ jsxs14("div", { children: [
1372
+ showsGeneratedData && node.metrics && Object.keys(node.metrics).length > 0 && /* @__PURE__ */ jsxs14("div", { children: [
1327
1373
  /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Metrics" }),
1328
1374
  Object.entries(node.metrics).map(([key, value]) => /* @__PURE__ */ jsx14(
1329
1375
  KV,
@@ -1694,10 +1740,125 @@ function hydrateLabels(nodes) {
1694
1740
  }
1695
1741
 
1696
1742
  // src/graph/react/viewer/GraphToolbar.tsx
1743
+ import React8 from "react";
1697
1744
  import { Panel } from "@xyflow/react";
1698
- import { Fragment as Fragment12, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
1745
+
1746
+ // src/graph/react/viewer/ValidationPanel.tsx
1747
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
1748
+ function validationLabel(state, issueCount) {
1749
+ const issues = issueCount === 1 ? "1 issue" : `${issueCount} issues`;
1750
+ switch (state) {
1751
+ case VALIDATION_STATE.VALIDATING:
1752
+ return issueCount > 0 ? `Validating method\u2026 \u2014 ${issues}` : "Validating method\u2026";
1753
+ case VALIDATION_STATE.VALID:
1754
+ return issueCount > 0 ? `Method is valid \u2014 ${issues}` : "Method is valid";
1755
+ case VALIDATION_STATE.INVALID:
1756
+ return `Method is invalid \u2014 ${issues}`;
1757
+ case VALIDATION_STATE.ERROR:
1758
+ return "Validation could not run";
1759
+ default: {
1760
+ const _exhaustive = state;
1761
+ return _exhaustive;
1762
+ }
1763
+ }
1764
+ }
1765
+ function validationPanelPlacement(position) {
1766
+ switch (position) {
1767
+ case TOOLBAR_POSITION.TOP_LEFT:
1768
+ case TOOLBAR_POSITION.TOP_CENTER:
1769
+ return "down-start";
1770
+ case TOOLBAR_POSITION.TOP_RIGHT:
1771
+ return "down-end";
1772
+ case TOOLBAR_POSITION.BOTTOM_LEFT:
1773
+ case TOOLBAR_POSITION.BOTTOM_CENTER:
1774
+ return "up-start";
1775
+ case TOOLBAR_POSITION.BOTTOM_RIGHT:
1776
+ return "up-end";
1777
+ case TOOLBAR_POSITION.CENTER_LEFT:
1778
+ return "side-end";
1779
+ case TOOLBAR_POSITION.CENTER_RIGHT:
1780
+ return "side-start";
1781
+ default: {
1782
+ const _exhaustive = position;
1783
+ return _exhaustive;
1784
+ }
1785
+ }
1786
+ }
1787
+ function validationEmptyText(state) {
1788
+ switch (state) {
1789
+ case VALIDATION_STATE.VALIDATING:
1790
+ return "Validating\u2026";
1791
+ case VALIDATION_STATE.VALID:
1792
+ return "No issues found.";
1793
+ case VALIDATION_STATE.INVALID:
1794
+ return "No details available.";
1795
+ case VALIDATION_STATE.ERROR:
1796
+ return "Validation could not run.";
1797
+ default: {
1798
+ const _exhaustive = state;
1799
+ return _exhaustive;
1800
+ }
1801
+ }
1802
+ }
1803
+ function ValidationPanel({
1804
+ state,
1805
+ issues,
1806
+ onIssueClick,
1807
+ placement,
1808
+ id
1809
+ }) {
1810
+ return /* @__PURE__ */ jsxs17(
1811
+ "div",
1812
+ {
1813
+ id,
1814
+ className: `graph-validation-panel graph-validation-panel--${placement}`,
1815
+ role: "region",
1816
+ "aria-label": "Validation issues",
1817
+ children: [
1818
+ /* @__PURE__ */ jsx17("div", { className: "graph-validation-panel-header", children: validationLabel(state, issues.length) }),
1819
+ issues.length === 0 ? /* @__PURE__ */ jsx17("div", { className: "graph-validation-empty", children: validationEmptyText(state) }) : /* @__PURE__ */ jsx17("ul", { className: "graph-validation-issues", role: "list", children: issues.map((issue, index) => {
1820
+ const clickable = onIssueClick !== void 0;
1821
+ const meta = (issue.context || issue.file) && /* @__PURE__ */ jsxs17("div", { className: "graph-validation-issue-meta", children: [
1822
+ issue.context && /* @__PURE__ */ jsx17("span", { className: "graph-validation-issue-context", children: issue.context }),
1823
+ issue.file && /* @__PURE__ */ jsx17("span", { className: "graph-validation-issue-file", children: issue.file })
1824
+ ] });
1825
+ return /* @__PURE__ */ jsx17(
1826
+ "li",
1827
+ {
1828
+ className: `graph-validation-issue graph-validation-issue--${issue.severity}` + (clickable ? " graph-validation-issue--clickable" : ""),
1829
+ children: /* @__PURE__ */ jsxs17(
1830
+ "div",
1831
+ {
1832
+ className: "graph-validation-issue-body",
1833
+ role: clickable ? "button" : void 0,
1834
+ tabIndex: clickable ? 0 : void 0,
1835
+ onClick: clickable ? () => onIssueClick(index, issue) : void 0,
1836
+ onKeyDown: clickable ? (event) => {
1837
+ if (event.key === "Enter" || event.key === " ") {
1838
+ event.preventDefault();
1839
+ onIssueClick(index, issue);
1840
+ }
1841
+ } : void 0,
1842
+ children: [
1843
+ meta,
1844
+ /* @__PURE__ */ jsx17("div", { className: "graph-validation-issue-message", children: issue.message }),
1845
+ issue.suggestedFix && /* @__PURE__ */ jsx17("div", { className: "graph-validation-issue-fix", children: issue.suggestedFix })
1846
+ ]
1847
+ }
1848
+ )
1849
+ },
1850
+ index
1851
+ );
1852
+ }) })
1853
+ ]
1854
+ }
1855
+ );
1856
+ }
1857
+
1858
+ // src/graph/react/viewer/GraphToolbar.tsx
1859
+ import { Fragment as Fragment12, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
1699
1860
  var REACTFLOW_PANEL_MARGIN = 15;
1700
- var ARROW_RIGHT_ICON = /* @__PURE__ */ jsxs17(
1861
+ var ARROW_RIGHT_ICON = /* @__PURE__ */ jsxs18(
1701
1862
  "svg",
1702
1863
  {
1703
1864
  viewBox: "0 0 24 24",
@@ -1709,12 +1870,12 @@ var ARROW_RIGHT_ICON = /* @__PURE__ */ jsxs17(
1709
1870
  strokeLinecap: "round",
1710
1871
  strokeLinejoin: "round",
1711
1872
  children: [
1712
- /* @__PURE__ */ jsx17("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
1713
- /* @__PURE__ */ jsx17("polyline", { points: "12 5 19 12 12 19" })
1873
+ /* @__PURE__ */ jsx18("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
1874
+ /* @__PURE__ */ jsx18("polyline", { points: "12 5 19 12 12 19" })
1714
1875
  ]
1715
1876
  }
1716
1877
  );
1717
- var ARROW_DOWN_ICON = /* @__PURE__ */ jsxs17(
1878
+ var ARROW_DOWN_ICON = /* @__PURE__ */ jsxs18(
1718
1879
  "svg",
1719
1880
  {
1720
1881
  viewBox: "0 0 24 24",
@@ -1726,12 +1887,12 @@ var ARROW_DOWN_ICON = /* @__PURE__ */ jsxs17(
1726
1887
  strokeLinecap: "round",
1727
1888
  strokeLinejoin: "round",
1728
1889
  children: [
1729
- /* @__PURE__ */ jsx17("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1730
- /* @__PURE__ */ jsx17("polyline", { points: "19 12 12 19 5 12" })
1890
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1891
+ /* @__PURE__ */ jsx18("polyline", { points: "19 12 12 19 5 12" })
1731
1892
  ]
1732
1893
  }
1733
1894
  );
1734
- var MINUS_ICON = /* @__PURE__ */ jsx17(
1895
+ var MINUS_ICON = /* @__PURE__ */ jsx18(
1735
1896
  "svg",
1736
1897
  {
1737
1898
  viewBox: "0 0 24 24",
@@ -1742,10 +1903,27 @@ var MINUS_ICON = /* @__PURE__ */ jsx17(
1742
1903
  strokeWidth: "2",
1743
1904
  strokeLinecap: "round",
1744
1905
  strokeLinejoin: "round",
1745
- children: /* @__PURE__ */ jsx17("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1906
+ children: /* @__PURE__ */ jsx18("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1907
+ }
1908
+ );
1909
+ var PLUS_ICON = /* @__PURE__ */ jsxs18(
1910
+ "svg",
1911
+ {
1912
+ viewBox: "0 0 24 24",
1913
+ width: "14",
1914
+ height: "14",
1915
+ fill: "none",
1916
+ stroke: "currentColor",
1917
+ strokeWidth: "2",
1918
+ strokeLinecap: "round",
1919
+ strokeLinejoin: "round",
1920
+ children: [
1921
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1922
+ /* @__PURE__ */ jsx18("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1923
+ ]
1746
1924
  }
1747
1925
  );
1748
- var PLUS_ICON = /* @__PURE__ */ jsxs17(
1926
+ var FIT_VIEW_ICON = /* @__PURE__ */ jsxs18(
1749
1927
  "svg",
1750
1928
  {
1751
1929
  viewBox: "0 0 24 24",
@@ -1757,12 +1935,14 @@ var PLUS_ICON = /* @__PURE__ */ jsxs17(
1757
1935
  strokeLinecap: "round",
1758
1936
  strokeLinejoin: "round",
1759
1937
  children: [
1760
- /* @__PURE__ */ jsx17("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1761
- /* @__PURE__ */ jsx17("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1938
+ /* @__PURE__ */ jsx18("polyline", { points: "15 3 21 3 21 9" }),
1939
+ /* @__PURE__ */ jsx18("polyline", { points: "9 21 3 21 3 15" }),
1940
+ /* @__PURE__ */ jsx18("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
1941
+ /* @__PURE__ */ jsx18("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1762
1942
  ]
1763
1943
  }
1764
1944
  );
1765
- var FIT_VIEW_ICON = /* @__PURE__ */ jsxs17(
1945
+ var BOXES_ICON = /* @__PURE__ */ jsxs18(
1766
1946
  "svg",
1767
1947
  {
1768
1948
  viewBox: "0 0 24 24",
@@ -1774,14 +1954,22 @@ var FIT_VIEW_ICON = /* @__PURE__ */ jsxs17(
1774
1954
  strokeLinecap: "round",
1775
1955
  strokeLinejoin: "round",
1776
1956
  children: [
1777
- /* @__PURE__ */ jsx17("polyline", { points: "15 3 21 3 21 9" }),
1778
- /* @__PURE__ */ jsx17("polyline", { points: "9 21 3 21 3 15" }),
1779
- /* @__PURE__ */ jsx17("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
1780
- /* @__PURE__ */ jsx17("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1957
+ /* @__PURE__ */ jsx18("path", { d: "M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" }),
1958
+ /* @__PURE__ */ jsx18("path", { d: "m7 16.5-4.74-2.85" }),
1959
+ /* @__PURE__ */ jsx18("path", { d: "m7 16.5 5-3" }),
1960
+ /* @__PURE__ */ jsx18("path", { d: "M7 16.5v5.17" }),
1961
+ /* @__PURE__ */ jsx18("path", { d: "M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" }),
1962
+ /* @__PURE__ */ jsx18("path", { d: "m17 16.5-5-3" }),
1963
+ /* @__PURE__ */ jsx18("path", { d: "m17 16.5 4.74-2.85" }),
1964
+ /* @__PURE__ */ jsx18("path", { d: "M17 16.5v5.17" }),
1965
+ /* @__PURE__ */ jsx18("path", { d: "M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" }),
1966
+ /* @__PURE__ */ jsx18("path", { d: "M12 8 7.26 5.15" }),
1967
+ /* @__PURE__ */ jsx18("path", { d: "m12 8 4.74-2.85" }),
1968
+ /* @__PURE__ */ jsx18("path", { d: "M12 13.5V8" })
1781
1969
  ]
1782
1970
  }
1783
1971
  );
1784
- var BOXES_ICON = /* @__PURE__ */ jsxs17(
1972
+ var FOLD_ALL_ICON = /* @__PURE__ */ jsxs18(
1785
1973
  "svg",
1786
1974
  {
1787
1975
  viewBox: "0 0 24 24",
@@ -1793,22 +1981,15 @@ var BOXES_ICON = /* @__PURE__ */ jsxs17(
1793
1981
  strokeLinecap: "round",
1794
1982
  strokeLinejoin: "round",
1795
1983
  children: [
1796
- /* @__PURE__ */ jsx17("path", { d: "M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" }),
1797
- /* @__PURE__ */ jsx17("path", { d: "m7 16.5-4.74-2.85" }),
1798
- /* @__PURE__ */ jsx17("path", { d: "m7 16.5 5-3" }),
1799
- /* @__PURE__ */ jsx17("path", { d: "M7 16.5v5.17" }),
1800
- /* @__PURE__ */ jsx17("path", { d: "M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" }),
1801
- /* @__PURE__ */ jsx17("path", { d: "m17 16.5-5-3" }),
1802
- /* @__PURE__ */ jsx17("path", { d: "m17 16.5 4.74-2.85" }),
1803
- /* @__PURE__ */ jsx17("path", { d: "M17 16.5v5.17" }),
1804
- /* @__PURE__ */ jsx17("path", { d: "M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" }),
1805
- /* @__PURE__ */ jsx17("path", { d: "M12 8 7.26 5.15" }),
1806
- /* @__PURE__ */ jsx17("path", { d: "m12 8 4.74-2.85" }),
1807
- /* @__PURE__ */ jsx17("path", { d: "M12 13.5V8" })
1984
+ /* @__PURE__ */ jsx18("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }),
1985
+ /* @__PURE__ */ jsx18("polyline", { points: "2 2 6 6 2 6" }),
1986
+ /* @__PURE__ */ jsx18("polyline", { points: "22 2 18 6 22 6" }),
1987
+ /* @__PURE__ */ jsx18("polyline", { points: "2 22 6 18 2 18" }),
1988
+ /* @__PURE__ */ jsx18("polyline", { points: "22 22 18 18 22 18" })
1808
1989
  ]
1809
1990
  }
1810
1991
  );
1811
- var FOLD_ALL_ICON = /* @__PURE__ */ jsxs17(
1992
+ var EXPAND_ALL_ICON = /* @__PURE__ */ jsxs18(
1812
1993
  "svg",
1813
1994
  {
1814
1995
  viewBox: "0 0 24 24",
@@ -1820,15 +2001,15 @@ var FOLD_ALL_ICON = /* @__PURE__ */ jsxs17(
1820
2001
  strokeLinecap: "round",
1821
2002
  strokeLinejoin: "round",
1822
2003
  children: [
1823
- /* @__PURE__ */ jsx17("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }),
1824
- /* @__PURE__ */ jsx17("polyline", { points: "2 2 6 6 2 6" }),
1825
- /* @__PURE__ */ jsx17("polyline", { points: "22 2 18 6 22 6" }),
1826
- /* @__PURE__ */ jsx17("polyline", { points: "2 22 6 18 2 18" }),
1827
- /* @__PURE__ */ jsx17("polyline", { points: "22 22 18 18 22 18" })
2004
+ /* @__PURE__ */ jsx18("rect", { x: "9", y: "9", width: "6", height: "6", rx: "1" }),
2005
+ /* @__PURE__ */ jsx18("polyline", { points: "3 3 7 7 3 7" }),
2006
+ /* @__PURE__ */ jsx18("polyline", { points: "21 3 17 7 21 7" }),
2007
+ /* @__PURE__ */ jsx18("polyline", { points: "3 21 7 17 3 17" }),
2008
+ /* @__PURE__ */ jsx18("polyline", { points: "21 21 17 17 21 17" })
1828
2009
  ]
1829
2010
  }
1830
2011
  );
1831
- var EXPAND_ALL_ICON = /* @__PURE__ */ jsxs17(
2012
+ var SUN_ICON = /* @__PURE__ */ jsxs18(
1832
2013
  "svg",
1833
2014
  {
1834
2015
  viewBox: "0 0 24 24",
@@ -1840,15 +2021,33 @@ var EXPAND_ALL_ICON = /* @__PURE__ */ jsxs17(
1840
2021
  strokeLinecap: "round",
1841
2022
  strokeLinejoin: "round",
1842
2023
  children: [
1843
- /* @__PURE__ */ jsx17("rect", { x: "9", y: "9", width: "6", height: "6", rx: "1" }),
1844
- /* @__PURE__ */ jsx17("polyline", { points: "3 3 7 7 3 7" }),
1845
- /* @__PURE__ */ jsx17("polyline", { points: "21 3 17 7 21 7" }),
1846
- /* @__PURE__ */ jsx17("polyline", { points: "3 21 7 17 3 17" }),
1847
- /* @__PURE__ */ jsx17("polyline", { points: "21 21 17 17 21 17" })
2024
+ /* @__PURE__ */ jsx18("circle", { cx: "12", cy: "12", r: "4" }),
2025
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "2", x2: "12", y2: "4" }),
2026
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "20", x2: "12", y2: "22" }),
2027
+ /* @__PURE__ */ jsx18("line", { x1: "4.93", y1: "4.93", x2: "6.34", y2: "6.34" }),
2028
+ /* @__PURE__ */ jsx18("line", { x1: "17.66", y1: "17.66", x2: "19.07", y2: "19.07" }),
2029
+ /* @__PURE__ */ jsx18("line", { x1: "2", y1: "12", x2: "4", y2: "12" }),
2030
+ /* @__PURE__ */ jsx18("line", { x1: "20", y1: "12", x2: "22", y2: "12" }),
2031
+ /* @__PURE__ */ jsx18("line", { x1: "4.93", y1: "19.07", x2: "6.34", y2: "17.66" }),
2032
+ /* @__PURE__ */ jsx18("line", { x1: "17.66", y1: "6.34", x2: "19.07", y2: "4.93" })
1848
2033
  ]
1849
2034
  }
1850
2035
  );
1851
- var SUN_ICON = /* @__PURE__ */ jsxs17(
2036
+ var MOON_ICON = /* @__PURE__ */ jsx18(
2037
+ "svg",
2038
+ {
2039
+ viewBox: "0 0 24 24",
2040
+ width: "14",
2041
+ height: "14",
2042
+ fill: "none",
2043
+ stroke: "currentColor",
2044
+ strokeWidth: "2",
2045
+ strokeLinecap: "round",
2046
+ strokeLinejoin: "round",
2047
+ children: /* @__PURE__ */ jsx18("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
2048
+ }
2049
+ );
2050
+ var MONITOR_ICON = /* @__PURE__ */ jsxs18(
1852
2051
  "svg",
1853
2052
  {
1854
2053
  viewBox: "0 0 24 24",
@@ -1860,19 +2059,42 @@ var SUN_ICON = /* @__PURE__ */ jsxs17(
1860
2059
  strokeLinecap: "round",
1861
2060
  strokeLinejoin: "round",
1862
2061
  children: [
1863
- /* @__PURE__ */ jsx17("circle", { cx: "12", cy: "12", r: "4" }),
1864
- /* @__PURE__ */ jsx17("line", { x1: "12", y1: "2", x2: "12", y2: "4" }),
1865
- /* @__PURE__ */ jsx17("line", { x1: "12", y1: "20", x2: "12", y2: "22" }),
1866
- /* @__PURE__ */ jsx17("line", { x1: "4.93", y1: "4.93", x2: "6.34", y2: "6.34" }),
1867
- /* @__PURE__ */ jsx17("line", { x1: "17.66", y1: "17.66", x2: "19.07", y2: "19.07" }),
1868
- /* @__PURE__ */ jsx17("line", { x1: "2", y1: "12", x2: "4", y2: "12" }),
1869
- /* @__PURE__ */ jsx17("line", { x1: "20", y1: "12", x2: "22", y2: "12" }),
1870
- /* @__PURE__ */ jsx17("line", { x1: "4.93", y1: "19.07", x2: "6.34", y2: "17.66" }),
1871
- /* @__PURE__ */ jsx17("line", { x1: "17.66", y1: "6.34", x2: "19.07", y2: "4.93" })
2062
+ /* @__PURE__ */ jsx18("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
2063
+ /* @__PURE__ */ jsx18("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
2064
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
1872
2065
  ]
1873
2066
  }
1874
2067
  );
1875
- var MOON_ICON = /* @__PURE__ */ jsx17(
2068
+ var SPINNER_ICON = /* @__PURE__ */ jsx18(
2069
+ "svg",
2070
+ {
2071
+ className: "graph-toolbar-validation-spin",
2072
+ viewBox: "0 0 24 24",
2073
+ width: "14",
2074
+ height: "14",
2075
+ fill: "none",
2076
+ stroke: "currentColor",
2077
+ strokeWidth: "2",
2078
+ strokeLinecap: "round",
2079
+ strokeLinejoin: "round",
2080
+ children: /* @__PURE__ */ jsx18("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
2081
+ }
2082
+ );
2083
+ var CHECK_ICON2 = /* @__PURE__ */ jsx18(
2084
+ "svg",
2085
+ {
2086
+ viewBox: "0 0 24 24",
2087
+ width: "14",
2088
+ height: "14",
2089
+ fill: "none",
2090
+ stroke: "currentColor",
2091
+ strokeWidth: "2",
2092
+ strokeLinecap: "round",
2093
+ strokeLinejoin: "round",
2094
+ children: /* @__PURE__ */ jsx18("polyline", { points: "20 6 9 17 4 12" })
2095
+ }
2096
+ );
2097
+ var X_CIRCLE_ICON = /* @__PURE__ */ jsxs18(
1876
2098
  "svg",
1877
2099
  {
1878
2100
  viewBox: "0 0 24 24",
@@ -1883,10 +2105,14 @@ var MOON_ICON = /* @__PURE__ */ jsx17(
1883
2105
  strokeWidth: "2",
1884
2106
  strokeLinecap: "round",
1885
2107
  strokeLinejoin: "round",
1886
- children: /* @__PURE__ */ jsx17("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
2108
+ children: [
2109
+ /* @__PURE__ */ jsx18("circle", { cx: "12", cy: "12", r: "10" }),
2110
+ /* @__PURE__ */ jsx18("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
2111
+ /* @__PURE__ */ jsx18("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
2112
+ ]
1887
2113
  }
1888
2114
  );
1889
- var MONITOR_ICON = /* @__PURE__ */ jsxs17(
2115
+ var ALERT_TRIANGLE_ICON = /* @__PURE__ */ jsxs18(
1890
2116
  "svg",
1891
2117
  {
1892
2118
  viewBox: "0 0 24 24",
@@ -1898,12 +2124,28 @@ var MONITOR_ICON = /* @__PURE__ */ jsxs17(
1898
2124
  strokeLinecap: "round",
1899
2125
  strokeLinejoin: "round",
1900
2126
  children: [
1901
- /* @__PURE__ */ jsx17("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
1902
- /* @__PURE__ */ jsx17("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
1903
- /* @__PURE__ */ jsx17("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
2127
+ /* @__PURE__ */ jsx18("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" }),
2128
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
2129
+ /* @__PURE__ */ jsx18("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1904
2130
  ]
1905
2131
  }
1906
2132
  );
2133
+ function validationIcon(state) {
2134
+ switch (state) {
2135
+ case VALIDATION_STATE.VALIDATING:
2136
+ return SPINNER_ICON;
2137
+ case VALIDATION_STATE.VALID:
2138
+ return CHECK_ICON2;
2139
+ case VALIDATION_STATE.INVALID:
2140
+ return X_CIRCLE_ICON;
2141
+ case VALIDATION_STATE.ERROR:
2142
+ return ALERT_TRIANGLE_ICON;
2143
+ default: {
2144
+ const _exhaustive = state;
2145
+ return _exhaustive;
2146
+ }
2147
+ }
2148
+ }
1907
2149
  var THEME_MODE_CYCLE = [
1908
2150
  GRAPH_THEME_MODE.SYSTEM,
1909
2151
  GRAPH_THEME_MODE.LIGHT,
@@ -1942,10 +2184,37 @@ function GraphToolbar({
1942
2184
  themeMode,
1943
2185
  onThemeModeChange,
1944
2186
  rightOffset = 0,
1945
- position
2187
+ position,
2188
+ validationState,
2189
+ validationIssues = [],
2190
+ onValidationIssueClick
1946
2191
  }) {
1947
2192
  const themeToggleEnabled = themeMode !== void 0 && onThemeModeChange !== void 0;
1948
2193
  const orientation = toolbarOrientation(position);
2194
+ const [validationOpen, setValidationOpen] = React8.useState(false);
2195
+ const validationRef = React8.useRef(null);
2196
+ const validationPanelId = React8.useId();
2197
+ React8.useEffect(() => {
2198
+ if (validationState === void 0) setValidationOpen(false);
2199
+ }, [validationState]);
2200
+ React8.useEffect(() => {
2201
+ if (!validationOpen) return;
2202
+ const onPointerDown = (event) => {
2203
+ const wrapper = validationRef.current;
2204
+ if (wrapper && event.target instanceof Node && !wrapper.contains(event.target)) {
2205
+ setValidationOpen(false);
2206
+ }
2207
+ };
2208
+ const onKeyDown = (event) => {
2209
+ if (event.key === "Escape") setValidationOpen(false);
2210
+ };
2211
+ document.addEventListener("mousedown", onPointerDown);
2212
+ document.addEventListener("keydown", onKeyDown);
2213
+ return () => {
2214
+ document.removeEventListener("mousedown", onPointerDown);
2215
+ document.removeEventListener("keydown", onKeyDown);
2216
+ };
2217
+ }, [validationOpen]);
1949
2218
  const dodgesDetailPanel = toolbarSide(position) === "right" && rightOffset > 0;
1950
2219
  const panelStyle = dodgesDetailPanel ? { marginRight: REACTFLOW_PANEL_MARGIN + rightOffset } : void 0;
1951
2220
  const isVertical = direction === GRAPH_DIRECTION.TB || direction === GRAPH_DIRECTION.BT;
@@ -1954,8 +2223,40 @@ function GraphToolbar({
1954
2223
  const foldAllSection = onFoldAll || onExpandAll;
1955
2224
  const foldAllTitle = foldAllDisabled ? "Fold all controllers (nothing to fold)" : "Fold all controllers";
1956
2225
  const expandAllTitle = expandAllDisabled ? "Expand all controllers (nothing to expand)" : "Expand all controllers";
1957
- return /* @__PURE__ */ jsx17(Panel, { position, className: "graph-toolbar-panel", style: panelStyle, children: /* @__PURE__ */ jsxs17("div", { className: `graph-toolbar graph-toolbar--${orientation}`, children: [
1958
- onFoldAll && /* @__PURE__ */ jsx17(
2226
+ return /* @__PURE__ */ jsx18(Panel, { position, className: "graph-toolbar-panel", style: panelStyle, children: /* @__PURE__ */ jsxs18("div", { className: `graph-toolbar graph-toolbar--${orientation}`, children: [
2227
+ validationState !== void 0 && /* @__PURE__ */ jsxs18(Fragment12, { children: [
2228
+ /* @__PURE__ */ jsxs18("div", { className: "graph-toolbar-validation", ref: validationRef, children: [
2229
+ /* @__PURE__ */ jsxs18(
2230
+ "button",
2231
+ {
2232
+ type: "button",
2233
+ className: `graph-toolbar-btn graph-toolbar-validation-btn graph-toolbar-validation-btn--${validationState}`,
2234
+ onClick: () => setValidationOpen((open) => !open),
2235
+ title: validationLabel(validationState, validationIssues.length),
2236
+ "aria-label": validationLabel(validationState, validationIssues.length),
2237
+ "aria-expanded": validationOpen,
2238
+ "aria-haspopup": "true",
2239
+ "aria-controls": validationPanelId,
2240
+ children: [
2241
+ validationIcon(validationState),
2242
+ validationIssues.length > 0 && /* @__PURE__ */ jsx18("span", { className: "graph-toolbar-validation-badge", children: validationIssues.length > 99 ? "99+" : validationIssues.length })
2243
+ ]
2244
+ }
2245
+ ),
2246
+ validationOpen && /* @__PURE__ */ jsx18(
2247
+ ValidationPanel,
2248
+ {
2249
+ id: validationPanelId,
2250
+ state: validationState,
2251
+ issues: validationIssues,
2252
+ onIssueClick: onValidationIssueClick,
2253
+ placement: validationPanelPlacement(position)
2254
+ }
2255
+ )
2256
+ ] }),
2257
+ /* @__PURE__ */ jsx18("div", { className: "graph-toolbar-separator" })
2258
+ ] }),
2259
+ onFoldAll && /* @__PURE__ */ jsx18(
1959
2260
  "button",
1960
2261
  {
1961
2262
  type: "button",
@@ -1967,7 +2268,7 @@ function GraphToolbar({
1967
2268
  children: FOLD_ALL_ICON
1968
2269
  }
1969
2270
  ),
1970
- onExpandAll && /* @__PURE__ */ jsx17(
2271
+ onExpandAll && /* @__PURE__ */ jsx18(
1971
2272
  "button",
1972
2273
  {
1973
2274
  type: "button",
@@ -1979,8 +2280,8 @@ function GraphToolbar({
1979
2280
  children: EXPAND_ALL_ICON
1980
2281
  }
1981
2282
  ),
1982
- foldAllSection && /* @__PURE__ */ jsx17("div", { className: "graph-toolbar-separator" }),
1983
- /* @__PURE__ */ jsx17(
2283
+ foldAllSection && /* @__PURE__ */ jsx18("div", { className: "graph-toolbar-separator" }),
2284
+ /* @__PURE__ */ jsx18(
1984
2285
  "button",
1985
2286
  {
1986
2287
  type: "button",
@@ -1991,7 +2292,7 @@ function GraphToolbar({
1991
2292
  children: BOXES_ICON
1992
2293
  }
1993
2294
  ),
1994
- /* @__PURE__ */ jsx17(
2295
+ /* @__PURE__ */ jsx18(
1995
2296
  "button",
1996
2297
  {
1997
2298
  type: "button",
@@ -2002,8 +2303,8 @@ function GraphToolbar({
2002
2303
  children: isVertical ? ARROW_RIGHT_ICON : ARROW_DOWN_ICON
2003
2304
  }
2004
2305
  ),
2005
- (onZoomOut || onZoomIn || onFitView) && /* @__PURE__ */ jsx17("div", { className: "graph-toolbar-separator" }),
2006
- onZoomOut && /* @__PURE__ */ jsx17(
2306
+ (onZoomOut || onZoomIn || onFitView) && /* @__PURE__ */ jsx18("div", { className: "graph-toolbar-separator" }),
2307
+ onZoomOut && /* @__PURE__ */ jsx18(
2007
2308
  "button",
2008
2309
  {
2009
2310
  type: "button",
@@ -2014,7 +2315,7 @@ function GraphToolbar({
2014
2315
  children: MINUS_ICON
2015
2316
  }
2016
2317
  ),
2017
- onZoomIn && /* @__PURE__ */ jsx17(
2318
+ onZoomIn && /* @__PURE__ */ jsx18(
2018
2319
  "button",
2019
2320
  {
2020
2321
  type: "button",
@@ -2025,7 +2326,7 @@ function GraphToolbar({
2025
2326
  children: PLUS_ICON
2026
2327
  }
2027
2328
  ),
2028
- onFitView && /* @__PURE__ */ jsx17(
2329
+ onFitView && /* @__PURE__ */ jsx18(
2029
2330
  "button",
2030
2331
  {
2031
2332
  type: "button",
@@ -2036,9 +2337,9 @@ function GraphToolbar({
2036
2337
  children: FIT_VIEW_ICON
2037
2338
  }
2038
2339
  ),
2039
- themeToggleEnabled && /* @__PURE__ */ jsxs17(Fragment12, { children: [
2040
- /* @__PURE__ */ jsx17("div", { className: "graph-toolbar-separator" }),
2041
- /* @__PURE__ */ jsx17(
2340
+ themeToggleEnabled && /* @__PURE__ */ jsxs18(Fragment12, { children: [
2341
+ /* @__PURE__ */ jsx18("div", { className: "graph-toolbar-separator" }),
2342
+ /* @__PURE__ */ jsx18(
2042
2343
  "button",
2043
2344
  {
2044
2345
  type: "button",
@@ -2054,7 +2355,7 @@ function GraphToolbar({
2054
2355
  }
2055
2356
 
2056
2357
  // src/graph/react/nodes/controller/ControllerGroupNode.tsx
2057
- import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
2358
+ import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2058
2359
  var CONTROLLER_CONFIG = {
2059
2360
  PipeSequence: { badge: "Sequence", icon: "\u2192" },
2060
2361
  PipeParallel: { badge: "Parallel", icon: "//" },
@@ -2076,12 +2377,12 @@ function ControllerGroupNode({ data }) {
2076
2377
  const modifier = getControllerModifier(data.pipeType);
2077
2378
  const isCollapsible = (data.pipeType === "PipeParallel" || data.pipeType === "PipeBatch") && ((_a = data.childCount) != null ? _a : 0) > MAX_VISIBLE_CONTROLLER_CHILDREN;
2078
2379
  const hiddenCount = isCollapsible ? ((_b = data.childCount) != null ? _b : 0) - MAX_VISIBLE_CONTROLLER_CHILDREN : 0;
2079
- return /* @__PURE__ */ jsxs18("div", { className: `controller-group-node ${modifier}`, children: [
2080
- /* @__PURE__ */ jsxs18("div", { className: "controller-group-header", children: [
2081
- /* @__PURE__ */ jsx18("span", { className: "controller-group-icon", children: config.icon }),
2082
- /* @__PURE__ */ jsx18("span", { className: "controller-group-badge", children: config.badge }),
2083
- data.label && /* @__PURE__ */ jsx18("span", { className: "controller-group-label", children: data.label }),
2084
- data.onToggleFold && /* @__PURE__ */ jsx18(
2380
+ return /* @__PURE__ */ jsxs19("div", { className: `controller-group-node ${modifier}`, children: [
2381
+ /* @__PURE__ */ jsxs19("div", { className: "controller-group-header", children: [
2382
+ /* @__PURE__ */ jsx19("span", { className: "controller-group-icon", children: config.icon }),
2383
+ /* @__PURE__ */ jsx19("span", { className: "controller-group-badge", children: config.badge }),
2384
+ data.label && /* @__PURE__ */ jsx19("span", { className: "controller-group-label", children: data.label }),
2385
+ data.onToggleFold && /* @__PURE__ */ jsx19(
2085
2386
  "button",
2086
2387
  {
2087
2388
  type: "button",
@@ -2097,7 +2398,7 @@ function ControllerGroupNode({ data }) {
2097
2398
  }
2098
2399
  )
2099
2400
  ] }),
2100
- isCollapsible && /* @__PURE__ */ jsx18(
2401
+ isCollapsible && /* @__PURE__ */ jsx19(
2101
2402
  "button",
2102
2403
  {
2103
2404
  className: "controller-group-collapse",
@@ -2118,7 +2419,7 @@ import { Handle, Position } from "@xyflow/react";
2118
2419
 
2119
2420
  // src/graph/react/nodes/pipe/PipeCardBase.tsx
2120
2421
  import { useState as useState2 } from "react";
2121
- import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2422
+ import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2122
2423
  var PIPE_TYPE_BADGES2 = {
2123
2424
  PipeLLM: "LLM",
2124
2425
  PipeExtract: "Extract",
@@ -2155,11 +2456,15 @@ function getBadge(pipeType) {
2155
2456
  return PIPE_TYPE_BADGES2[pipeType];
2156
2457
  }
2157
2458
  function PipeCardBase({ data, children }) {
2459
+ var _a, _b;
2158
2460
  const badge = getBadge(data.pipeType);
2159
2461
  const statusConfig = STATUS_CONFIG[data.status];
2160
2462
  const isRunning = data.status === "running";
2161
2463
  const isController = isControllerType(data.pipeType);
2162
2464
  const isSignature = data.pipeType === "PipeSignature";
2465
+ const isStatic = data.graphMode === "static";
2466
+ const outcome = isStatic ? (_a = data.tags) == null ? void 0 : _a.outcome : void 0;
2467
+ const batchMultiplicity = isStatic ? (_b = data.tags) == null ? void 0 : _b.batch_multiplicity : void 0;
2163
2468
  const [inputsExpanded, setInputsExpanded] = useState2(false);
2164
2469
  const hasMany = data.inputs.length > MAX_VISIBLE_INPUTS;
2165
2470
  const visibleInputs = hasMany && !inputsExpanded ? data.inputs.slice(0, MAX_VISIBLE_INPUTS) : data.inputs;
@@ -2167,18 +2472,19 @@ function PipeCardBase({ data, children }) {
2167
2472
  const dirClass = data.direction === "TB" ? "pipe-card--tb" : "pipe-card--lr";
2168
2473
  const controllerClass = isController ? " pipe-card--controller" : "";
2169
2474
  const signatureClass = isSignature ? " pipe-card--signature" : "";
2475
+ const staticClass = isStatic ? " pipe-card--static" : "";
2170
2476
  const badgeClass = isController ? "pipe-card-badge pipe-card-badge--controller" : isSignature ? "pipe-card-badge pipe-card-badge--signature" : "pipe-card-badge";
2171
- return /* @__PURE__ */ jsxs19("div", { className: `pipe-card ${dirClass}${controllerClass}${signatureClass}`, children: [
2172
- /* @__PURE__ */ jsxs19("div", { className: "pipe-card-header", children: [
2173
- /* @__PURE__ */ jsx19("span", { className: badgeClass, children: badge }),
2174
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-code", title: data.pipeCode, children: data.pipeCode }),
2175
- /* @__PURE__ */ jsx19(
2477
+ return /* @__PURE__ */ jsxs20("div", { className: `pipe-card ${dirClass}${controllerClass}${signatureClass}${staticClass}`, children: [
2478
+ /* @__PURE__ */ jsxs20("div", { className: "pipe-card-header", children: [
2479
+ /* @__PURE__ */ jsx20("span", { className: badgeClass, children: badge }),
2480
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-code", title: data.pipeCode, children: data.pipeCode }),
2481
+ !isStatic && /* @__PURE__ */ jsx20(
2176
2482
  "span",
2177
2483
  {
2178
2484
  className: "pipe-card-status",
2179
2485
  style: { color: statusConfig.color },
2180
2486
  title: statusConfig.label,
2181
- children: /* @__PURE__ */ jsx19(
2487
+ children: /* @__PURE__ */ jsx20(
2182
2488
  "span",
2183
2489
  {
2184
2490
  className: `pipe-card-status-dot ${isRunning ? "pipe-card-status-dot--pulse" : ""}`,
@@ -2187,7 +2493,7 @@ function PipeCardBase({ data, children }) {
2187
2493
  )
2188
2494
  }
2189
2495
  ),
2190
- data.onExpand && /* @__PURE__ */ jsx19(
2496
+ data.onExpand && /* @__PURE__ */ jsx20(
2191
2497
  "button",
2192
2498
  {
2193
2499
  type: "button",
@@ -2195,31 +2501,38 @@ function PipeCardBase({ data, children }) {
2195
2501
  title: "Expand controller (alt/option: only this one)",
2196
2502
  "aria-label": "Expand controller",
2197
2503
  onClick: (e) => {
2198
- var _a;
2504
+ var _a2;
2199
2505
  e.stopPropagation();
2200
- (_a = data.onExpand) == null ? void 0 : _a.call(data, { soloMode: e.altKey });
2506
+ (_a2 = data.onExpand) == null ? void 0 : _a2.call(data, { soloMode: e.altKey });
2201
2507
  },
2202
2508
  children: "\u2922"
2203
2509
  }
2204
2510
  )
2205
2511
  ] }),
2206
- data.description && /* @__PURE__ */ jsx19("span", { className: "pipe-card-description", title: data.description, children: data.description }),
2207
- data.inputs.length > 0 && /* @__PURE__ */ jsxs19("div", { className: "pipe-card-io", children: [
2208
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-label", children: "INPUTS" }),
2209
- /* @__PURE__ */ jsxs19("div", { className: "pipe-card-io-pills", children: [
2210
- visibleInputs.map((input) => /* @__PURE__ */ jsxs19(
2512
+ (outcome || batchMultiplicity) && /* @__PURE__ */ jsxs20("div", { className: "pipe-card-annotations", children: [
2513
+ outcome && /* @__PURE__ */ jsxs20("span", { className: "pipe-card-annotation pipe-card-annotation--outcome", children: [
2514
+ "outcome: ",
2515
+ outcome
2516
+ ] }),
2517
+ batchMultiplicity && /* @__PURE__ */ jsx20("span", { className: "pipe-card-annotation pipe-card-annotation--batch", children: batchMultiplicity })
2518
+ ] }),
2519
+ data.description && /* @__PURE__ */ jsx20("span", { className: "pipe-card-description", title: data.description, children: data.description }),
2520
+ data.inputs.length > 0 && /* @__PURE__ */ jsxs20("div", { className: "pipe-card-io", children: [
2521
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-label", children: "INPUTS" }),
2522
+ /* @__PURE__ */ jsxs20("div", { className: "pipe-card-io-pills", children: [
2523
+ visibleInputs.map((input) => /* @__PURE__ */ jsxs20(
2211
2524
  "span",
2212
2525
  {
2213
2526
  className: "pipe-card-io-pill",
2214
2527
  title: `${input.name}: ${input.concept}`,
2215
2528
  children: [
2216
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-pill-name", children: input.name }),
2217
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-pill-concept", children: input.concept })
2529
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-pill-name", children: input.name }),
2530
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-pill-concept", children: input.concept })
2218
2531
  ]
2219
2532
  },
2220
2533
  input.name
2221
2534
  )),
2222
- hasMany && /* @__PURE__ */ jsx19(
2535
+ hasMany && /* @__PURE__ */ jsx20(
2223
2536
  "button",
2224
2537
  {
2225
2538
  className: "pipe-card-io-more",
@@ -2232,16 +2545,16 @@ function PipeCardBase({ data, children }) {
2232
2545
  )
2233
2546
  ] })
2234
2547
  ] }),
2235
- data.outputs.length > 0 && /* @__PURE__ */ jsxs19("div", { className: "pipe-card-io", children: [
2236
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-label", children: "OUTPUT" }),
2237
- /* @__PURE__ */ jsx19("div", { className: "pipe-card-io-pills", children: data.outputs.map((output) => /* @__PURE__ */ jsxs19(
2548
+ data.outputs.length > 0 && /* @__PURE__ */ jsxs20("div", { className: "pipe-card-io", children: [
2549
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-label", children: "OUTPUT" }),
2550
+ /* @__PURE__ */ jsx20("div", { className: "pipe-card-io-pills", children: data.outputs.map((output) => /* @__PURE__ */ jsxs20(
2238
2551
  "span",
2239
2552
  {
2240
2553
  className: "pipe-card-io-pill",
2241
2554
  title: `${output.name}: ${output.concept}`,
2242
2555
  children: [
2243
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-pill-name", children: output.name }),
2244
- /* @__PURE__ */ jsx19("span", { className: "pipe-card-io-pill-concept", children: output.concept })
2556
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-pill-name", children: output.name }),
2557
+ /* @__PURE__ */ jsx20("span", { className: "pipe-card-io-pill-concept", children: output.concept })
2245
2558
  ]
2246
2559
  },
2247
2560
  output.name
@@ -2271,11 +2584,11 @@ function getPipeCardComponent(pipeType) {
2271
2584
  }
2272
2585
 
2273
2586
  // src/graph/react/nodes/pipe/PipeCardNode.tsx
2274
- import { Fragment as Fragment13, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2587
+ import { Fragment as Fragment13, jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
2275
2588
  function PipeCardNode({ data }) {
2276
2589
  var _a;
2277
2590
  const Card = (_a = getPipeCardComponent(data.pipeType)) != null ? _a : PipeCardBase;
2278
- return /* @__PURE__ */ jsx20(Card, { data });
2591
+ return /* @__PURE__ */ jsx21(Card, { data });
2279
2592
  }
2280
2593
  function PipeCardRFNode({
2281
2594
  data,
@@ -2284,15 +2597,15 @@ function PipeCardRFNode({
2284
2597
  }) {
2285
2598
  const payload = data.pipeCardData;
2286
2599
  if (!payload) return null;
2287
- return /* @__PURE__ */ jsxs20(Fragment13, { children: [
2288
- /* @__PURE__ */ jsx20(Handle, { type: "target", position: targetPosition }),
2289
- /* @__PURE__ */ jsx20(PipeCardNode, { data: payload }),
2290
- /* @__PURE__ */ jsx20(Handle, { type: "source", position: sourcePosition })
2600
+ return /* @__PURE__ */ jsxs21(Fragment13, { children: [
2601
+ /* @__PURE__ */ jsx21(Handle, { type: "target", position: targetPosition }),
2602
+ /* @__PURE__ */ jsx21(PipeCardNode, { data: payload }),
2603
+ /* @__PURE__ */ jsx21(Handle, { type: "source", position: sourcePosition })
2291
2604
  ] });
2292
2605
  }
2293
2606
 
2294
2607
  // src/graph/react/viewer/GraphViewer.tsx
2295
- import { Fragment as Fragment14, jsx as jsx21, jsxs as jsxs21 } from "react/jsx-runtime";
2608
+ import { Fragment as Fragment14, jsx as jsx22, jsxs as jsxs22 } from "react/jsx-runtime";
2296
2609
  var nodeTypes = __spreadProps(__spreadValues({}, controllerNodeTypes), {
2297
2610
  pipeCard: PipeCardRFNode
2298
2611
  });
@@ -2305,19 +2618,21 @@ function StuffNodeDetail({
2305
2618
  onOpenExternally
2306
2619
  }) {
2307
2620
  const conceptInfo = stuffData.concept && graphspec ? resolveConceptRef(graphspec, stuffData.concept) : void 0;
2308
- return /* @__PURE__ */ jsx21(Fragment14, { children: conceptInfo ? /* @__PURE__ */ jsx21(
2621
+ const isDryRun = graphSpecMode(graphspec) === GRAPH_SPEC_MODE.DRY;
2622
+ return /* @__PURE__ */ jsx22(Fragment14, { children: conceptInfo ? /* @__PURE__ */ jsx22(
2309
2623
  ConceptDetailPanel,
2310
2624
  {
2311
2625
  concept: conceptInfo,
2312
2626
  ioData: stuffData,
2627
+ isDryRun,
2313
2628
  instanceKey: nodeId,
2314
2629
  resolveStorageUrl,
2315
2630
  canEmbedPdf,
2316
2631
  onOpenExternally
2317
2632
  }
2318
- ) : (
2633
+ ) : isDryRun ? /* @__PURE__ */ jsx22("div", { className: "detail-not-available", children: "Dry run data hidden" }) : (
2319
2634
  /* Fallback: just show the StuffViewer if no concept info */
2320
- /* @__PURE__ */ jsx21(
2635
+ /* @__PURE__ */ jsx22(
2321
2636
  StuffViewer,
2322
2637
  {
2323
2638
  stuff: stuffData,
@@ -2353,11 +2668,14 @@ function cloneCachedNodes(nodes) {
2353
2668
  function applyStatusOverrides(nodes, statusMap) {
2354
2669
  if (!statusMap || Object.keys(statusMap).length === 0) return nodes;
2355
2670
  return nodes.map((node) => {
2356
- var _a;
2671
+ var _a, _b;
2672
+ if (node.data.graphMode === "static" || ((_a = node.data.pipeCardData) == null ? void 0 : _a.graphMode) === "static") {
2673
+ return node;
2674
+ }
2357
2675
  const pipeCode = node.data.pipeCode;
2358
2676
  if (!pipeCode || !Object.hasOwn(statusMap, pipeCode)) return node;
2359
2677
  const newStatus = statusMap[pipeCode];
2360
- if (((_a = node.data.pipeCardData) == null ? void 0 : _a.status) === newStatus) return node;
2678
+ if (((_b = node.data.pipeCardData) == null ? void 0 : _b.status) === newStatus) return node;
2361
2679
  return __spreadProps(__spreadValues({}, node), {
2362
2680
  data: __spreadProps(__spreadValues({}, node.data), {
2363
2681
  nodeData: node.data.nodeData ? __spreadProps(__spreadValues({}, node.data.nodeData), { status: newStatus }) : node.data.nodeData,
@@ -2389,22 +2707,25 @@ function GraphViewer(props) {
2389
2707
  renderDetailExtra,
2390
2708
  resolveStorageUrl,
2391
2709
  canEmbedPdf,
2392
- onOpenExternally
2710
+ onOpenExternally,
2711
+ validationState,
2712
+ validationIssues,
2713
+ onValidationIssueClick
2393
2714
  } = props;
2394
- const graphspec = React9.useMemo(
2715
+ const graphspec = React10.useMemo(
2395
2716
  () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
2396
2717
  [graphspecProp]
2397
2718
  );
2398
- const [direction, setDirection] = React9.useState(
2719
+ const [direction, setDirection] = React10.useState(
2399
2720
  () => {
2400
2721
  var _a2, _b2;
2401
2722
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
2402
2723
  }
2403
2724
  );
2404
2725
  const externalMode = resolveExternalThemeMode(themeProp, config.theme);
2405
- const [mode, setMode] = React9.useState(externalMode);
2406
- const prevExternalModeRef = React9.useRef(externalMode);
2407
- React9.useEffect(() => {
2726
+ const [mode, setMode] = React10.useState(externalMode);
2727
+ const prevExternalModeRef = React10.useRef(externalMode);
2728
+ React10.useEffect(() => {
2408
2729
  if (externalMode !== prevExternalModeRef.current) {
2409
2730
  prevExternalModeRef.current = externalMode;
2410
2731
  setMode(externalMode);
@@ -2412,13 +2733,13 @@ function GraphViewer(props) {
2412
2733
  }, [externalMode]);
2413
2734
  const systemTheme = useSystemTheme(systemThemeProp);
2414
2735
  const resolvedTheme = resolveActiveTheme(mode, systemTheme);
2415
- const onThemeChangeRef = React9.useRef(onThemeChange);
2736
+ const onThemeChangeRef = React10.useRef(onThemeChange);
2416
2737
  onThemeChangeRef.current = onThemeChange;
2417
- const prevReportedRef = React9.useRef({
2738
+ const prevReportedRef = React10.useRef({
2418
2739
  mode,
2419
2740
  resolvedTheme
2420
2741
  });
2421
- React9.useEffect(() => {
2742
+ React10.useEffect(() => {
2422
2743
  var _a2;
2423
2744
  const prev = prevReportedRef.current;
2424
2745
  if (prev.mode !== mode || prev.resolvedTheme !== resolvedTheme) {
@@ -2427,26 +2748,26 @@ function GraphViewer(props) {
2427
2748
  }
2428
2749
  }, [mode, resolvedTheme]);
2429
2750
  const effectiveFoldMode = (_b = (_a = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _a : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _b : FOLD_MODE.EXPANDED;
2430
- const [showControllers, setShowControllers] = React9.useState(() => {
2751
+ const [showControllers, setShowControllers] = React10.useState(() => {
2431
2752
  var _a2, _b2;
2432
2753
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
2433
2754
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
2434
2755
  });
2435
- const foldModeRef = React9.useRef(effectiveFoldMode);
2756
+ const foldModeRef = React10.useRef(effectiveFoldMode);
2436
2757
  foldModeRef.current = effectiveFoldMode;
2437
- const containerRef = React9.useRef(null);
2438
- const [detailSelection, setDetailSelection] = React9.useState(null);
2439
- const [conceptOverride, setConceptOverride] = React9.useState(null);
2758
+ const containerRef = React10.useRef(null);
2759
+ const [detailSelection, setDetailSelection] = React10.useState(null);
2760
+ const [conceptOverride, setConceptOverride] = React10.useState(null);
2440
2761
  const {
2441
2762
  width: panelWidth,
2442
2763
  isDragging: isPanelDragging,
2443
2764
  handleMouseDown: onResizeMouseDown
2444
2765
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
2445
- React9.useEffect(() => {
2766
+ React10.useEffect(() => {
2446
2767
  setDetailSelection(null);
2447
2768
  setConceptOverride(null);
2448
2769
  }, [graphspec]);
2449
- React9.useEffect(() => {
2770
+ React10.useEffect(() => {
2450
2771
  const el = containerRef.current;
2451
2772
  if (!el) return;
2452
2773
  const themePalette = getPaletteForTheme(resolvedTheme);
@@ -2463,12 +2784,12 @@ function GraphViewer(props) {
2463
2784
  }, [config.paletteColors, resolvedTheme]);
2464
2785
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
2465
2786
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
2466
- const reactFlowRef = React9.useRef(null);
2467
- const initialDataRef = React9.useRef(null);
2468
- const rawGraphDataRef = React9.useRef(null);
2469
- const layoutCacheRef = React9.useRef(null);
2470
- const [expandedControllers, setExpandedControllers] = React9.useState(/* @__PURE__ */ new Set());
2471
- const toggleCollapse = React9.useCallback((controllerId) => {
2787
+ const reactFlowRef = React10.useRef(null);
2788
+ const initialDataRef = React10.useRef(null);
2789
+ const rawGraphDataRef = React10.useRef(null);
2790
+ const layoutCacheRef = React10.useRef(null);
2791
+ const [expandedControllers, setExpandedControllers] = React10.useState(/* @__PURE__ */ new Set());
2792
+ const toggleCollapse = React10.useCallback((controllerId) => {
2472
2793
  setExpandedControllers((prev) => {
2473
2794
  const next = new Set(prev);
2474
2795
  if (next.has(controllerId)) next.delete(controllerId);
@@ -2476,8 +2797,8 @@ function GraphViewer(props) {
2476
2797
  return next;
2477
2798
  });
2478
2799
  }, []);
2479
- const [foldedControllers, setFoldedControllers] = React9.useState(/* @__PURE__ */ new Set());
2480
- const toggleFold = React9.useCallback((controllerId, options) => {
2800
+ const [foldedControllers, setFoldedControllers] = React10.useState(/* @__PURE__ */ new Set());
2801
+ const toggleFold = React10.useCallback((controllerId, options) => {
2481
2802
  setFoldedControllers((prev) => {
2482
2803
  const next = new Set(prev);
2483
2804
  const shouldFold = !next.has(controllerId);
@@ -2491,34 +2812,34 @@ function GraphViewer(props) {
2491
2812
  });
2492
2813
  }, []);
2493
2814
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
2494
- const layoutConfig = React9.useMemo(
2815
+ const layoutConfig = React10.useMemo(
2495
2816
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
2496
2817
  [config.nodesep, config.ranksep]
2497
2818
  );
2498
- const showControllersRef = React9.useRef(showControllers);
2819
+ const showControllersRef = React10.useRef(showControllers);
2499
2820
  showControllersRef.current = showControllers;
2500
- const directionRef = React9.useRef(direction);
2821
+ const directionRef = React10.useRef(direction);
2501
2822
  directionRef.current = direction;
2502
- const layoutConfigRef = React9.useRef(layoutConfig);
2823
+ const layoutConfigRef = React10.useRef(layoutConfig);
2503
2824
  layoutConfigRef.current = layoutConfig;
2504
- const initialZoomRef = React9.useRef(config.initialZoom);
2825
+ const initialZoomRef = React10.useRef(config.initialZoom);
2505
2826
  initialZoomRef.current = config.initialZoom;
2506
- const panToTopRef = React9.useRef(config.panToTop);
2827
+ const panToTopRef = React10.useRef(config.panToTop);
2507
2828
  panToTopRef.current = config.panToTop;
2508
- const expandedRef = React9.useRef(expandedControllers);
2829
+ const expandedRef = React10.useRef(expandedControllers);
2509
2830
  expandedRef.current = expandedControllers;
2510
- const toggleCollapseRef = React9.useRef(toggleCollapse);
2831
+ const toggleCollapseRef = React10.useRef(toggleCollapse);
2511
2832
  toggleCollapseRef.current = toggleCollapse;
2512
- const foldedRef = React9.useRef(foldedControllers);
2833
+ const foldedRef = React10.useRef(foldedControllers);
2513
2834
  foldedRef.current = foldedControllers;
2514
- const toggleFoldRef = React9.useRef(toggleFold);
2835
+ const toggleFoldRef = React10.useRef(toggleFold);
2515
2836
  toggleFoldRef.current = toggleFold;
2516
- const isFirstFoldEffect = React9.useRef(true);
2517
- const prevFoldSizeRef = React9.useRef(0);
2518
- const skipNextFoldEffectRef = React9.useRef(false);
2519
- const statusMapRef = React9.useRef(statusMap);
2837
+ const isFirstFoldEffect = React10.useRef(true);
2838
+ const prevFoldSizeRef = React10.useRef(0);
2839
+ const skipNextFoldEffectRef = React10.useRef(false);
2840
+ const statusMapRef = React10.useRef(statusMap);
2520
2841
  statusMapRef.current = statusMap;
2521
- React9.useEffect(() => {
2842
+ React10.useEffect(() => {
2522
2843
  if (!initialDataRef.current) return;
2523
2844
  let cancelled = false;
2524
2845
  void (async () => {
@@ -2570,7 +2891,7 @@ function GraphViewer(props) {
2570
2891
  cancelled = true;
2571
2892
  };
2572
2893
  }, [direction, layoutConfig]);
2573
- React9.useEffect(() => {
2894
+ React10.useEffect(() => {
2574
2895
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2575
2896
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2576
2897
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2590,7 +2911,7 @@ function GraphViewer(props) {
2590
2911
  );
2591
2912
  setEdges(toAppEdges(withControllers.edges));
2592
2913
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold]);
2593
- React9.useEffect(() => {
2914
+ React10.useEffect(() => {
2594
2915
  if (!graphspec) {
2595
2916
  initialDataRef.current = null;
2596
2917
  rawGraphDataRef.current = null;
@@ -2694,7 +3015,7 @@ function GraphViewer(props) {
2694
3015
  cancelled = true;
2695
3016
  };
2696
3017
  }, [graphspec, edgeType]);
2697
- React9.useEffect(() => {
3018
+ React10.useEffect(() => {
2698
3019
  if (isFirstFoldEffect.current) {
2699
3020
  isFirstFoldEffect.current = false;
2700
3021
  prevFoldSizeRef.current = foldedControllers.size;
@@ -2774,7 +3095,7 @@ function GraphViewer(props) {
2774
3095
  cancelled = true;
2775
3096
  };
2776
3097
  }, [foldedControllers, toggleFold]);
2777
- React9.useEffect(() => {
3098
+ React10.useEffect(() => {
2778
3099
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2779
3100
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2780
3101
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2792,7 +3113,7 @@ function GraphViewer(props) {
2792
3113
  setNodes(applyStatusOverrides(toAppNodes(hydrateLabels(withControllers.nodes)), statusMap));
2793
3114
  setEdges(toAppEdges(withControllers.edges));
2794
3115
  }, [statusMap]);
2795
- const onNodeClick = React9.useCallback(
3116
+ const onNodeClick = React10.useCallback(
2796
3117
  (event, node) => {
2797
3118
  var _a2;
2798
3119
  const nodeData = node.data;
@@ -2834,7 +3155,7 @@ function GraphViewer(props) {
2834
3155
  conceptOverride
2835
3156
  ]
2836
3157
  );
2837
- const onInit = React9.useCallback(
3158
+ const onInit = React10.useCallback(
2838
3159
  (reactFlowInstance) => {
2839
3160
  reactFlowRef.current = reactFlowInstance;
2840
3161
  if (onReactFlowInit) {
@@ -2843,12 +3164,12 @@ function GraphViewer(props) {
2843
3164
  },
2844
3165
  [onReactFlowInit]
2845
3166
  );
2846
- const handlePaneClick = React9.useCallback(() => {
3167
+ const handlePaneClick = React10.useCallback(() => {
2847
3168
  setDetailSelection(null);
2848
3169
  setConceptOverride(null);
2849
3170
  onPaneClick == null ? void 0 : onPaneClick();
2850
3171
  }, [onPaneClick]);
2851
- const handleConceptClick = React9.useCallback(
3172
+ const handleConceptClick = React10.useCallback(
2852
3173
  (conceptCode) => {
2853
3174
  if (!graphspec) return;
2854
3175
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -2864,7 +3185,7 @@ function GraphViewer(props) {
2864
3185
  );
2865
3186
  const rawAnalysis = (_c = rawGraphDataRef.current) == null ? void 0 : _c.analysis;
2866
3187
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
2867
- const foldAllProps = React9.useMemo(() => {
3188
+ const foldAllProps = React10.useMemo(() => {
2868
3189
  if (!showControllers || !allControllerIds || allControllerIds.size === 0) {
2869
3190
  return {
2870
3191
  onFoldAll: void 0,
@@ -2880,13 +3201,13 @@ function GraphViewer(props) {
2880
3201
  expandAllDisabled: foldedControllers.size === 0
2881
3202
  };
2882
3203
  }, [showControllers, allControllerIds, foldedControllers]);
2883
- return /* @__PURE__ */ jsxs21(
3204
+ return /* @__PURE__ */ jsxs22(
2884
3205
  "div",
2885
3206
  {
2886
3207
  ref: containerRef,
2887
3208
  className: `react-flow-container react-flow-container--theme-${resolvedTheme} react-flow-container--mode-${mode}`,
2888
3209
  children: [
2889
- /* @__PURE__ */ jsxs21(
3210
+ /* @__PURE__ */ jsxs22(
2890
3211
  ReactFlow,
2891
3212
  {
2892
3213
  nodes,
@@ -2905,7 +3226,7 @@ function GraphViewer(props) {
2905
3226
  proOptions: { hideAttribution: true },
2906
3227
  panActivationKeyCode: null,
2907
3228
  children: [
2908
- /* @__PURE__ */ jsx21(
3229
+ /* @__PURE__ */ jsx22(
2909
3230
  Background,
2910
3231
  {
2911
3232
  variant: BackgroundVariant.Dots,
@@ -2914,7 +3235,7 @@ function GraphViewer(props) {
2914
3235
  color: "var(--color-bg-dots)"
2915
3236
  }
2916
3237
  ),
2917
- !hideToolbar && /* @__PURE__ */ jsx21(
3238
+ !hideToolbar && /* @__PURE__ */ jsx22(
2918
3239
  GraphToolbar,
2919
3240
  {
2920
3241
  direction,
@@ -2940,13 +3261,16 @@ function GraphViewer(props) {
2940
3261
  themeMode: showThemeToggle ? mode : void 0,
2941
3262
  onThemeModeChange: showThemeToggle ? setMode : void 0,
2942
3263
  rightOffset: detailOpen ? panelWidth : 0,
2943
- position: effectiveToolbarPosition
3264
+ position: effectiveToolbarPosition,
3265
+ validationState,
3266
+ validationIssues,
3267
+ onValidationIssueClick
2944
3268
  }
2945
3269
  )
2946
3270
  ]
2947
3271
  }
2948
3272
  ),
2949
- /* @__PURE__ */ jsxs21(
3273
+ /* @__PURE__ */ jsxs22(
2950
3274
  DetailPanel,
2951
3275
  {
2952
3276
  isOpen: detailOpen,
@@ -2955,14 +3279,14 @@ function GraphViewer(props) {
2955
3279
  isDragging: isPanelDragging,
2956
3280
  onResizeHandleMouseDown: onResizeMouseDown,
2957
3281
  children: [
2958
- conceptOverride ? /* @__PURE__ */ jsx21(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx21(
3282
+ conceptOverride ? /* @__PURE__ */ jsx22(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx22(
2959
3283
  PipeDetailPanel,
2960
3284
  {
2961
3285
  node: selectedSpecNode,
2962
3286
  spec: graphspec,
2963
3287
  onConceptClick: handleConceptClick
2964
3288
  }
2965
- ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx21(
3289
+ ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx22(
2966
3290
  StuffNodeDetail,
2967
3291
  {
2968
3292
  nodeId: detailSelection.nodeId,
@@ -2990,6 +3314,7 @@ export {
2990
3314
  PipeCardNode,
2991
3315
  PipeDetailPanel,
2992
3316
  StuffViewer,
3317
+ ValidationPanel,
2993
3318
  applyStatusOverrides,
2994
3319
  controllerNodeTypes,
2995
3320
  detectSystemTheme,
@@ -3007,6 +3332,8 @@ export {
3007
3332
  toAppEdges,
3008
3333
  toAppNodes,
3009
3334
  useResizable,
3010
- useSystemTheme
3335
+ useSystemTheme,
3336
+ validationLabel,
3337
+ validationPanelPlacement
3011
3338
  };
3012
3339
  //# sourceMappingURL=index.js.map