@pipelex/mthds-ui 0.6.4 → 0.7.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.
@@ -4,16 +4,19 @@ import {
4
4
  EDGE_TYPE,
5
5
  FOLD_MODE,
6
6
  GRAPH_DIRECTION,
7
+ GRAPH_THEME,
7
8
  MAX_VISIBLE_CONTROLLER_CHILDREN,
8
9
  applyControllers,
9
10
  applyFolds,
10
11
  buildGraph,
11
12
  findCousinControllers,
12
13
  getLayoutedElements,
14
+ getPaletteForTheme,
13
15
  getPipeBlueprint,
14
16
  resolveConceptRef,
15
- stuffDigestFromId
16
- } from "../../chunk-FHRUYFGV.js";
17
+ stuffDigestFromId,
18
+ validateGraphSpec
19
+ } from "../../chunk-ILX53OYM.js";
17
20
  import {
18
21
  __spreadProps,
19
22
  __spreadValues
@@ -26,7 +29,7 @@ import "./stuff/StuffViewer.css";
26
29
  import "./viewer/GraphToolbar.css";
27
30
 
28
31
  // src/graph/react/viewer/GraphViewer.tsx
29
- import React7 from "react";
32
+ import React8 from "react";
30
33
  import {
31
34
  ReactFlow,
32
35
  useNodesState,
@@ -186,7 +189,7 @@ function StuffViewer({
186
189
  canEmbedPdf,
187
190
  onOpenExternally
188
191
  }) {
189
- var _a;
192
+ var _a, _b;
190
193
  const [activeTab, setActiveTab] = React.useState("html");
191
194
  const [copied, setCopied] = React.useState(false);
192
195
  const contentRef = React.useRef(null);
@@ -220,12 +223,12 @@ function StuffViewer({
220
223
  const isPdf = effectiveMime === "application/pdf";
221
224
  const isImage = (_a = effectiveMime == null ? void 0 : effectiveMime.startsWith("image/")) != null ? _a : false;
222
225
  const htmlTabLabel = getHtmlTabLabel(effectiveMime != null ? effectiveMime : stuff.contentType);
223
- const jsonString = React.useMemo(() => {
224
- if (stuff.data == null) return null;
226
+ const { jsonString, jsonError } = React.useMemo(() => {
227
+ if (stuff.data == null) return { jsonString: null, jsonError: null };
225
228
  try {
226
- return JSON.stringify(stuff.data, null, 2);
227
- } catch (e) {
228
- return "[Unable to serialize data]";
229
+ return { jsonString: JSON.stringify(stuff.data, null, 2), jsonError: null };
230
+ } catch (err) {
231
+ return { jsonString: null, jsonError: err instanceof Error ? err.message : String(err) };
229
232
  }
230
233
  }, [stuff.data]);
231
234
  React.useEffect(() => {
@@ -248,7 +251,14 @@ function StuffViewer({
248
251
  ] })
249
252
  ] });
250
253
  }
254
+ function renderJsonError(detail) {
255
+ return /* @__PURE__ */ jsxs("div", { className: "stuff-viewer-error", role: "alert", children: [
256
+ /* @__PURE__ */ jsx("div", { className: "stuff-viewer-error-title", children: "Unable to serialize data" }),
257
+ /* @__PURE__ */ jsx("div", { className: "stuff-viewer-error-detail", children: detail })
258
+ ] });
259
+ }
251
260
  function renderContent() {
261
+ var _a2;
252
262
  if (activeTab === "html") {
253
263
  if (isPdf) {
254
264
  const canEmbed = canEmbedPdf !== false;
@@ -278,7 +288,7 @@ function StuffViewer({
278
288
  }
279
289
  if (isImage) {
280
290
  if (inlineUrl) {
281
- return /* @__PURE__ */ jsx("div", { className: "stuff-viewer-image", children: /* @__PURE__ */ jsx("img", { src: inlineUrl, alt: stuff.name || "Image content" }) });
291
+ return /* @__PURE__ */ jsx("div", { className: "stuff-viewer-image", children: /* @__PURE__ */ jsx("img", { src: inlineUrl, alt: (_a2 = stuff.name) != null ? _a2 : "(unnamed stuff)" }) });
282
292
  }
283
293
  return renderMediaFallback("Image");
284
294
  }
@@ -300,6 +310,7 @@ function StuffViewer({
300
310
  }
301
311
  );
302
312
  }
313
+ if (jsonError) return renderJsonError(jsonError);
303
314
  return /* @__PURE__ */ jsx("div", { className: "stuff-viewer-placeholder", children: "No content available" });
304
315
  }
305
316
  if (activeTab === "json") {
@@ -312,6 +323,7 @@ function StuffViewer({
312
323
  }
313
324
  );
314
325
  }
326
+ if (jsonError) return renderJsonError(jsonError);
315
327
  return /* @__PURE__ */ jsx("div", { className: "stuff-viewer-placeholder", children: "No JSON data available" });
316
328
  }
317
329
  if (stuff.dataText) {
@@ -332,6 +344,7 @@ function StuffViewer({
332
344
  }
333
345
  );
334
346
  }
347
+ if (jsonError) return renderJsonError(jsonError);
335
348
  return /* @__PURE__ */ jsx("div", { className: "stuff-viewer-placeholder", children: "No text data available" });
336
349
  }
337
350
  function handleCopy() {
@@ -389,7 +402,7 @@ function StuffViewer({
389
402
  const rootClass = ["stuff-viewer", className].filter(Boolean).join(" ");
390
403
  return /* @__PURE__ */ jsxs("div", { className: rootClass, children: [
391
404
  /* @__PURE__ */ jsxs("div", { className: "stuff-viewer-header", children: [
392
- /* @__PURE__ */ jsx("h3", { className: "stuff-viewer-title", children: stuff.name || "Data" }),
405
+ /* @__PURE__ */ jsx("h3", { className: "stuff-viewer-title", children: (_b = stuff.name) != null ? _b : "(unnamed stuff)" }),
393
406
  stuff.concept && /* @__PURE__ */ jsx("p", { className: "stuff-viewer-subtitle", children: stuff.concept })
394
407
  ] }),
395
408
  /* @__PURE__ */ jsxs("div", { className: "stuff-viewer-toolbar", children: [
@@ -483,8 +496,10 @@ function DetailPanel({
483
496
  "aria-label": "Resize panel"
484
497
  }
485
498
  ),
486
- /* @__PURE__ */ jsx2("button", { className: "detail-panel-close", onClick: onClose, "aria-label": "Close panel", children: "x" }),
487
- /* @__PURE__ */ jsx2("div", { className: "detail-panel-content", children })
499
+ /* @__PURE__ */ jsxs2("div", { className: "detail-panel-content", children: [
500
+ /* @__PURE__ */ jsx2("div", { className: "detail-panel-close-row", children: /* @__PURE__ */ jsx2("button", { className: "detail-panel-close", onClick: onClose, "aria-label": "Close panel", children: "x" }) }),
501
+ children
502
+ ] })
488
503
  ] });
489
504
  }
490
505
 
@@ -1134,15 +1149,16 @@ var STATUS_COLORS = {
1134
1149
  failed: "#FF5555",
1135
1150
  running: "#8BE9FD",
1136
1151
  scheduled: "#6272a4",
1137
- skipped: "#6272a4"
1152
+ skipped: "#6272a4",
1153
+ canceled: "#6272a4"
1138
1154
  };
1139
1155
  function PipeDetailPanel({ node, spec, onConceptClick }) {
1140
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1141
- const pipeType = (_a = node.pipe_type) != null ? _a : "PipeFunc";
1156
+ var _a, _b, _c, _d, _e, _f, _g;
1157
+ const pipeType = node.pipe_type;
1142
1158
  const isController = CONTROLLER_TYPES.has(pipeType);
1143
- const badge = (_b = PIPE_TYPE_BADGES[pipeType]) != null ? _b : pipeType;
1144
- const status = (_c = node.status) != null ? _c : "scheduled";
1145
- const statusColor = (_d = STATUS_COLORS[status]) != null ? _d : "#6272a4";
1159
+ const badge = PIPE_TYPE_BADGES[pipeType];
1160
+ const status = node.status;
1161
+ const statusColor = (_a = STATUS_COLORS[status]) != null ? _a : "#6272a4";
1146
1162
  const blueprint = React5.useMemo(() => {
1147
1163
  var _a2, _b2;
1148
1164
  if (!node.pipe_code || !spec.pipe_registry) return void 0;
@@ -1154,9 +1170,9 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1154
1170
  }
1155
1171
  return void 0;
1156
1172
  }, [node.pipe_code, spec]);
1157
- const inputs = (_f = (_e = node.io) == null ? void 0 : _e.inputs) != null ? _f : [];
1158
- const outputs = (_h = (_g = node.io) == null ? void 0 : _g.outputs) != null ? _h : [];
1159
- const description = (_i = blueprint == null ? void 0 : blueprint.description) != null ? _i : node.description;
1173
+ const inputs = (_c = (_b = node.io) == null ? void 0 : _b.inputs) != null ? _c : [];
1174
+ const outputs = (_e = (_d = node.io) == null ? void 0 : _d.outputs) != null ? _e : [];
1175
+ const description = (_f = blueprint == null ? void 0 : blueprint.description) != null ? _f : node.description;
1160
1176
  return /* @__PURE__ */ jsxs13(Fragment9, { children: [
1161
1177
  /* @__PURE__ */ jsxs13("div", { className: "detail-sticky-header", children: [
1162
1178
  /* @__PURE__ */ jsxs13("div", { className: "detail-header", children: [
@@ -1171,53 +1187,47 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1171
1187
  "span",
1172
1188
  {
1173
1189
  className: `detail-pipe-code ${isController ? "detail-pipe-code--controller" : ""}`,
1174
- children: (_j = node.pipe_code) != null ? _j : "unknown"
1190
+ children: node.pipe_code
1175
1191
  }
1176
1192
  )
1177
1193
  ] }),
1178
1194
  /* @__PURE__ */ jsxs13("div", { className: "detail-status", children: [
1179
1195
  /* @__PURE__ */ jsx13("span", { className: "detail-status-dot", style: { background: statusColor } }),
1180
1196
  /* @__PURE__ */ jsx13("span", { className: "detail-status-label", style: { color: statusColor }, children: status }),
1181
- ((_k = node.timing) == null ? void 0 : _k.duration) != null && /* @__PURE__ */ jsx13("span", { className: "detail-duration", children: formatDuration(node.timing.duration) })
1197
+ ((_g = node.timing) == null ? void 0 : _g.duration) != null && /* @__PURE__ */ jsx13("span", { className: "detail-duration", children: formatDuration(node.timing.duration) })
1182
1198
  ] }),
1183
1199
  description && /* @__PURE__ */ jsx13("div", { className: "detail-description", children: description }),
1184
1200
  inputs.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
1185
1201
  /* @__PURE__ */ jsx13("div", { className: "detail-section-label", children: "Inputs" }),
1186
- /* @__PURE__ */ jsx13("div", { className: "detail-io-list", children: inputs.map((input, idx) => {
1187
- var _a2;
1188
- return /* @__PURE__ */ jsxs13(
1189
- "div",
1190
- {
1191
- className: "detail-io-pill",
1192
- style: { cursor: input.concept && onConceptClick ? "pointer" : void 0 },
1193
- onClick: () => input.concept && (onConceptClick == null ? void 0 : onConceptClick(input.concept)),
1194
- children: [
1195
- /* @__PURE__ */ jsx13("span", { className: "detail-io-name", children: (_a2 = input.name) != null ? _a2 : "unnamed" }),
1196
- input.concept && /* @__PURE__ */ jsx13("span", { className: "detail-io-concept", children: input.concept })
1197
- ]
1198
- },
1199
- idx
1200
- );
1201
- }) })
1202
+ /* @__PURE__ */ jsx13("div", { className: "detail-io-list", children: inputs.map((input, idx) => /* @__PURE__ */ jsxs13(
1203
+ "div",
1204
+ {
1205
+ className: "detail-io-pill",
1206
+ style: { cursor: input.concept && onConceptClick ? "pointer" : void 0 },
1207
+ onClick: () => input.concept && (onConceptClick == null ? void 0 : onConceptClick(input.concept)),
1208
+ children: [
1209
+ /* @__PURE__ */ jsx13("span", { className: "detail-io-name", children: input.name }),
1210
+ input.concept && /* @__PURE__ */ jsx13("span", { className: "detail-io-concept", children: input.concept })
1211
+ ]
1212
+ },
1213
+ idx
1214
+ )) })
1202
1215
  ] }),
1203
1216
  outputs.length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
1204
1217
  /* @__PURE__ */ jsx13("div", { className: "detail-section-label", children: "Output" }),
1205
- /* @__PURE__ */ jsx13("div", { className: "detail-io-list", children: outputs.map((output, idx) => {
1206
- var _a2;
1207
- return /* @__PURE__ */ jsxs13(
1208
- "div",
1209
- {
1210
- className: "detail-io-pill",
1211
- style: { cursor: output.concept && onConceptClick ? "pointer" : void 0 },
1212
- onClick: () => output.concept && (onConceptClick == null ? void 0 : onConceptClick(output.concept)),
1213
- children: [
1214
- /* @__PURE__ */ jsx13("span", { className: "detail-io-name", children: (_a2 = output.name) != null ? _a2 : "unnamed" }),
1215
- output.concept && /* @__PURE__ */ jsx13("span", { className: "detail-io-concept", children: output.concept })
1216
- ]
1217
- },
1218
- idx
1219
- );
1220
- }) })
1218
+ /* @__PURE__ */ jsx13("div", { className: "detail-io-list", children: outputs.map((output, idx) => /* @__PURE__ */ jsxs13(
1219
+ "div",
1220
+ {
1221
+ className: "detail-io-pill",
1222
+ style: { cursor: output.concept && onConceptClick ? "pointer" : void 0 },
1223
+ onClick: () => output.concept && (onConceptClick == null ? void 0 : onConceptClick(output.concept)),
1224
+ children: [
1225
+ /* @__PURE__ */ jsx13("span", { className: "detail-io-name", children: output.name }),
1226
+ output.concept && /* @__PURE__ */ jsx13("span", { className: "detail-io-concept", children: output.concept })
1227
+ ]
1228
+ },
1229
+ idx
1230
+ )) })
1221
1231
  ] }),
1222
1232
  (inputs.length > 0 || outputs.length > 0) && /* @__PURE__ */ jsx13("div", { style: { borderTop: "1px solid rgba(255,255,255,0.06)", margin: "4px 0" } })
1223
1233
  ] }),
@@ -1316,6 +1326,7 @@ function GenericExecutionData({ data }) {
1316
1326
  }
1317
1327
 
1318
1328
  // src/graph/react/detail/ConceptDetailPanel.tsx
1329
+ import React6 from "react";
1319
1330
  import { Fragment as Fragment10, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1320
1331
  function ConceptDetailPanel({
1321
1332
  concept,
@@ -1323,8 +1334,10 @@ function ConceptDetailPanel({
1323
1334
  isDryRun,
1324
1335
  resolveStorageUrl,
1325
1336
  canEmbedPdf,
1326
- onOpenExternally
1337
+ onOpenExternally,
1338
+ instanceKey
1327
1339
  }) {
1340
+ var _a;
1328
1341
  return /* @__PURE__ */ jsxs14(Fragment10, { children: [
1329
1342
  /* @__PURE__ */ jsxs14("div", { className: "detail-header", children: [
1330
1343
  /* @__PURE__ */ jsx14("span", { className: "detail-concept-code", children: concept.code }),
@@ -1335,22 +1348,88 @@ function ConceptDetailPanel({
1335
1348
  "refines ",
1336
1349
  /* @__PURE__ */ jsx14("span", { className: "detail-refines-code", children: concept.refines })
1337
1350
  ] }),
1338
- concept.json_schema ? /* @__PURE__ */ jsxs14("div", { children: [
1339
- /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Structure" }),
1340
- /* @__PURE__ */ jsx14(SchemaTable, { schema: concept.json_schema, isDryRun })
1341
- ] }) : /* @__PURE__ */ jsx14("div", { className: "detail-not-available", children: "Schema not available" }),
1342
- ioData && !isDryRun && /* @__PURE__ */ jsxs14("div", { children: [
1343
- /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Data" }),
1344
- /* @__PURE__ */ jsx14(
1345
- StuffViewer,
1346
- {
1347
- stuff: toStuffViewerData(ioData),
1348
- resolveStorageUrl,
1349
- canEmbedPdf,
1350
- onOpenExternally
1351
- }
1352
- )
1353
- ] })
1351
+ /* @__PURE__ */ jsx14(
1352
+ ConceptBody,
1353
+ {
1354
+ concept,
1355
+ ioData,
1356
+ isDryRun,
1357
+ resolveStorageUrl,
1358
+ canEmbedPdf,
1359
+ onOpenExternally
1360
+ },
1361
+ instanceKey != null ? instanceKey : `${concept.code}:${ioData && "digest" in ioData ? ioData.digest : (_a = ioData == null ? void 0 : ioData.name) != null ? _a : ""}`
1362
+ )
1363
+ ] });
1364
+ }
1365
+ function ConceptBody({
1366
+ concept,
1367
+ ioData,
1368
+ isDryRun,
1369
+ resolveStorageUrl,
1370
+ canEmbedPdf,
1371
+ onOpenExternally
1372
+ }) {
1373
+ const hasData = Boolean(ioData) && !isDryRun;
1374
+ const [activeTab, setActiveTab] = React6.useState(hasData ? "data" : "structure");
1375
+ const baseId = React6.useId();
1376
+ const tabId = (tab) => `${baseId}-tab-${tab}`;
1377
+ const panelId = (tab) => `${baseId}-tabpanel-${tab}`;
1378
+ const structure = concept.json_schema ? /* @__PURE__ */ jsxs14("div", { children: [
1379
+ /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Structure" }),
1380
+ /* @__PURE__ */ jsx14(SchemaTable, { schema: concept.json_schema, isDryRun })
1381
+ ] }) : /* @__PURE__ */ jsx14("div", { className: "detail-not-available", children: "Schema not available" });
1382
+ if (!hasData) return structure;
1383
+ const onTabKeyDown = (event) => {
1384
+ var _a;
1385
+ let next;
1386
+ switch (event.key) {
1387
+ case "ArrowLeft":
1388
+ case "ArrowRight":
1389
+ next = activeTab === "data" ? "structure" : "data";
1390
+ break;
1391
+ case "Home":
1392
+ next = "data";
1393
+ break;
1394
+ case "End":
1395
+ next = "structure";
1396
+ break;
1397
+ default:
1398
+ return;
1399
+ }
1400
+ event.preventDefault();
1401
+ setActiveTab(next);
1402
+ (_a = document.getElementById(tabId(next))) == null ? void 0 : _a.focus();
1403
+ };
1404
+ const renderTab = (tab, label) => /* @__PURE__ */ jsx14(
1405
+ "button",
1406
+ {
1407
+ type: "button",
1408
+ role: "tab",
1409
+ id: tabId(tab),
1410
+ "aria-selected": activeTab === tab,
1411
+ "aria-controls": panelId(tab),
1412
+ tabIndex: activeTab === tab ? 0 : -1,
1413
+ className: `detail-tab ${activeTab === tab ? "detail-tab--active" : ""}`,
1414
+ onClick: () => setActiveTab(tab),
1415
+ onKeyDown: onTabKeyDown,
1416
+ children: label
1417
+ }
1418
+ );
1419
+ return /* @__PURE__ */ jsxs14(Fragment10, { children: [
1420
+ /* @__PURE__ */ jsxs14("div", { className: "detail-tabs", role: "tablist", "aria-label": "Concept views", children: [
1421
+ renderTab("data", "Data"),
1422
+ renderTab("structure", "Structure")
1423
+ ] }),
1424
+ /* @__PURE__ */ jsx14("div", { role: "tabpanel", id: panelId(activeTab), "aria-labelledby": tabId(activeTab), children: activeTab === "data" ? /* @__PURE__ */ jsx14(
1425
+ StuffViewer,
1426
+ {
1427
+ stuff: toStuffViewerData(ioData),
1428
+ resolveStorageUrl,
1429
+ canEmbedPdf,
1430
+ onOpenExternally
1431
+ }
1432
+ ) : structure })
1354
1433
  ] });
1355
1434
  }
1356
1435
  function SchemaTable({
@@ -1390,9 +1469,9 @@ function extractType(schema) {
1390
1469
  if (schema.allOf) return "all";
1391
1470
  if (schema.$ref) {
1392
1471
  const ref = String(schema.$ref);
1393
- return (_a = ref.split("/").pop()) != null ? _a : "ref";
1472
+ return (_a = ref.split("/").pop()) != null ? _a : "(unresolved type)";
1394
1473
  }
1395
- return "unknown";
1474
+ return "(unresolved type)";
1396
1475
  }
1397
1476
  function toStuffViewerData(ioData) {
1398
1477
  var _a;
@@ -1539,7 +1618,7 @@ function hydrateLabels(nodes) {
1539
1618
  }
1540
1619
 
1541
1620
  // src/graph/react/viewer/GraphToolbar.tsx
1542
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
1621
+ import { Fragment as Fragment11, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
1543
1622
  var ARROW_RIGHT_ICON = /* @__PURE__ */ jsxs16(
1544
1623
  "svg",
1545
1624
  {
@@ -1691,6 +1770,44 @@ var EXPAND_ALL_ICON = /* @__PURE__ */ jsxs16(
1691
1770
  ]
1692
1771
  }
1693
1772
  );
1773
+ var SUN_ICON = /* @__PURE__ */ jsxs16(
1774
+ "svg",
1775
+ {
1776
+ viewBox: "0 0 24 24",
1777
+ width: "14",
1778
+ height: "14",
1779
+ fill: "none",
1780
+ stroke: "currentColor",
1781
+ strokeWidth: "2",
1782
+ strokeLinecap: "round",
1783
+ strokeLinejoin: "round",
1784
+ children: [
1785
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "4" }),
1786
+ /* @__PURE__ */ jsx16("line", { x1: "12", y1: "2", x2: "12", y2: "4" }),
1787
+ /* @__PURE__ */ jsx16("line", { x1: "12", y1: "20", x2: "12", y2: "22" }),
1788
+ /* @__PURE__ */ jsx16("line", { x1: "4.93", y1: "4.93", x2: "6.34", y2: "6.34" }),
1789
+ /* @__PURE__ */ jsx16("line", { x1: "17.66", y1: "17.66", x2: "19.07", y2: "19.07" }),
1790
+ /* @__PURE__ */ jsx16("line", { x1: "2", y1: "12", x2: "4", y2: "12" }),
1791
+ /* @__PURE__ */ jsx16("line", { x1: "20", y1: "12", x2: "22", y2: "12" }),
1792
+ /* @__PURE__ */ jsx16("line", { x1: "4.93", y1: "19.07", x2: "6.34", y2: "17.66" }),
1793
+ /* @__PURE__ */ jsx16("line", { x1: "17.66", y1: "6.34", x2: "19.07", y2: "4.93" })
1794
+ ]
1795
+ }
1796
+ );
1797
+ var MOON_ICON = /* @__PURE__ */ jsx16(
1798
+ "svg",
1799
+ {
1800
+ viewBox: "0 0 24 24",
1801
+ width: "14",
1802
+ height: "14",
1803
+ fill: "none",
1804
+ stroke: "currentColor",
1805
+ strokeWidth: "2",
1806
+ strokeLinecap: "round",
1807
+ strokeLinejoin: "round",
1808
+ children: /* @__PURE__ */ jsx16("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
1809
+ }
1810
+ );
1694
1811
  function GraphToolbar({
1695
1812
  direction,
1696
1813
  onDirectionChange,
@@ -1703,8 +1820,12 @@ function GraphToolbar({
1703
1820
  onExpandAll,
1704
1821
  foldAllDisabled = false,
1705
1822
  expandAllDisabled = false,
1823
+ theme,
1824
+ onThemeChange,
1706
1825
  rightOffset = 0
1707
1826
  }) {
1827
+ const themeToggleEnabled = theme !== void 0 && onThemeChange !== void 0;
1828
+ const themeLabel = theme === GRAPH_THEME.LIGHT ? "Switch to dark theme" : "Switch to light theme";
1708
1829
  const isVertical = direction === GRAPH_DIRECTION.TB || direction === GRAPH_DIRECTION.BT;
1709
1830
  const directionLabel = isVertical ? "Switch to horizontal layout" : "Switch to vertical layout";
1710
1831
  const controllersLabel = showControllers ? "Hide pipe controllers" : "Show pipe controllers \u2014 groups pipes by their controlling pipe";
@@ -1792,7 +1913,21 @@ function GraphToolbar({
1792
1913
  "aria-label": "Fit view",
1793
1914
  children: FIT_VIEW_ICON
1794
1915
  }
1795
- )
1916
+ ),
1917
+ themeToggleEnabled && /* @__PURE__ */ jsxs16(Fragment11, { children: [
1918
+ /* @__PURE__ */ jsx16("div", { className: "graph-toolbar-separator" }),
1919
+ /* @__PURE__ */ jsx16(
1920
+ "button",
1921
+ {
1922
+ type: "button",
1923
+ className: "graph-toolbar-btn",
1924
+ onClick: () => onThemeChange(theme === GRAPH_THEME.LIGHT ? GRAPH_THEME.DARK : GRAPH_THEME.LIGHT),
1925
+ title: themeLabel,
1926
+ "aria-label": themeLabel,
1927
+ children: theme === GRAPH_THEME.LIGHT ? MOON_ICON : SUN_ICON
1928
+ }
1929
+ )
1930
+ ] })
1796
1931
  ] });
1797
1932
  }
1798
1933
 
@@ -1888,16 +2023,16 @@ var STATUS_CONFIG = {
1888
2023
  failed: { color: "#FF5555", label: "Failed" },
1889
2024
  running: { color: "#8BE9FD", label: "Running" },
1890
2025
  scheduled: { color: "#6272a4", label: "Scheduled" },
1891
- skipped: { color: "#6272a4", label: "Skipped" }
2026
+ skipped: { color: "#6272a4", label: "Skipped" },
2027
+ canceled: { color: "#6272a4", label: "Canceled" }
1892
2028
  };
1893
2029
  var MAX_VISIBLE_INPUTS = 4;
1894
2030
  function getBadge(pipeType) {
1895
2031
  return PIPE_TYPE_BADGES2[pipeType];
1896
2032
  }
1897
2033
  function PipeCardBase({ data, children }) {
1898
- var _a;
1899
2034
  const badge = getBadge(data.pipeType);
1900
- const statusConfig = (_a = STATUS_CONFIG[data.status]) != null ? _a : STATUS_CONFIG.scheduled;
2035
+ const statusConfig = STATUS_CONFIG[data.status];
1901
2036
  const isRunning = data.status === "running";
1902
2037
  const isController = isControllerType(data.pipeType);
1903
2038
  const [inputsExpanded, setInputsExpanded] = useState2(false);
@@ -1934,9 +2069,9 @@ function PipeCardBase({ data, children }) {
1934
2069
  title: "Expand controller (alt/option: only this one)",
1935
2070
  "aria-label": "Expand controller",
1936
2071
  onClick: (e) => {
1937
- var _a2;
2072
+ var _a;
1938
2073
  e.stopPropagation();
1939
- (_a2 = data.onExpand) == null ? void 0 : _a2.call(data, { soloMode: e.altKey });
2074
+ (_a = data.onExpand) == null ? void 0 : _a.call(data, { soloMode: e.altKey });
1940
2075
  },
1941
2076
  children: "\u2922"
1942
2077
  }
@@ -2008,7 +2143,7 @@ function getPipeCardComponent(pipeType) {
2008
2143
  }
2009
2144
 
2010
2145
  // src/graph/react/nodes/pipe/PipeCardNode.tsx
2011
- import { Fragment as Fragment11, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2146
+ import { Fragment as Fragment12, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
2012
2147
  function PipeCardNode({ data }) {
2013
2148
  var _a;
2014
2149
  const Card = (_a = getPipeCardComponent(data.pipeType)) != null ? _a : PipeCardBase;
@@ -2021,7 +2156,7 @@ function PipeCardRFNode({
2021
2156
  }) {
2022
2157
  const payload = data.pipeCardData;
2023
2158
  if (!payload) return null;
2024
- return /* @__PURE__ */ jsxs19(Fragment11, { children: [
2159
+ return /* @__PURE__ */ jsxs19(Fragment12, { children: [
2025
2160
  /* @__PURE__ */ jsx19(Handle, { type: "target", position: targetPosition }),
2026
2161
  /* @__PURE__ */ jsx19(PipeCardNode, { data: payload }),
2027
2162
  /* @__PURE__ */ jsx19(Handle, { type: "source", position: sourcePosition })
@@ -2029,11 +2164,12 @@ function PipeCardRFNode({
2029
2164
  }
2030
2165
 
2031
2166
  // src/graph/react/viewer/GraphViewer.tsx
2032
- import { Fragment as Fragment12, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2167
+ import { Fragment as Fragment13, jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
2033
2168
  var nodeTypes = __spreadProps(__spreadValues({}, controllerNodeTypes), {
2034
2169
  pipeCard: PipeCardRFNode
2035
2170
  });
2036
2171
  function StuffNodeDetail({
2172
+ nodeId,
2037
2173
  stuffData,
2038
2174
  graphspec,
2039
2175
  resolveStorageUrl,
@@ -2041,11 +2177,12 @@ function StuffNodeDetail({
2041
2177
  onOpenExternally
2042
2178
  }) {
2043
2179
  const conceptInfo = stuffData.concept && graphspec ? resolveConceptRef(graphspec, stuffData.concept) : void 0;
2044
- return /* @__PURE__ */ jsx20(Fragment12, { children: conceptInfo ? /* @__PURE__ */ jsx20(
2180
+ return /* @__PURE__ */ jsx20(Fragment13, { children: conceptInfo ? /* @__PURE__ */ jsx20(
2045
2181
  ConceptDetailPanel,
2046
2182
  {
2047
2183
  concept: conceptInfo,
2048
2184
  ioData: stuffData,
2185
+ instanceKey: nodeId,
2049
2186
  resolveStorageUrl,
2050
2187
  canEmbedPdf,
2051
2188
  onOpenExternally
@@ -2067,6 +2204,10 @@ function seedFoldedControllers(mode, controllerIds) {
2067
2204
  if (mode === FOLD_MODE.FOLDED) return new Set(controllerIds);
2068
2205
  return /* @__PURE__ */ new Set();
2069
2206
  }
2207
+ function resolveExternalTheme(themeProp, configTheme) {
2208
+ var _a, _b;
2209
+ return (_b = (_a = themeProp != null ? themeProp : configTheme) != null ? _a : DEFAULT_GRAPH_CONFIG.theme) != null ? _b : GRAPH_THEME.DARK;
2210
+ }
2070
2211
  function cloneCachedNodes(nodes) {
2071
2212
  return nodes.map((n) => __spreadProps(__spreadValues({}, n), {
2072
2213
  position: __spreadValues({}, n.position),
@@ -2093,12 +2234,15 @@ function applyStatusOverrides(nodes, statusMap) {
2093
2234
  function GraphViewer(props) {
2094
2235
  var _a, _b, _c;
2095
2236
  const {
2096
- graphspec,
2237
+ graphspec: graphspecProp,
2097
2238
  config = DEFAULT_GRAPH_CONFIG,
2098
2239
  initialDirection,
2099
2240
  initialShowControllers,
2100
2241
  initialFoldMode,
2101
2242
  hideToolbar = false,
2243
+ theme: themeProp,
2244
+ showThemeToggle = true,
2245
+ onThemeChange,
2102
2246
  onNavigateToPipe,
2103
2247
  onStuffNodeClick,
2104
2248
  onReactFlowInit,
@@ -2110,38 +2254,61 @@ function GraphViewer(props) {
2110
2254
  canEmbedPdf,
2111
2255
  onOpenExternally
2112
2256
  } = props;
2113
- const [direction, setDirection] = React7.useState(
2257
+ const graphspec = React8.useMemo(
2258
+ () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
2259
+ [graphspecProp]
2260
+ );
2261
+ const [direction, setDirection] = React8.useState(
2114
2262
  () => {
2115
2263
  var _a2, _b2;
2116
2264
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
2117
2265
  }
2118
2266
  );
2267
+ const externalTheme = resolveExternalTheme(themeProp, config.theme);
2268
+ const [theme, setTheme] = React8.useState(externalTheme);
2269
+ const prevExternalThemeRef = React8.useRef(externalTheme);
2270
+ React8.useEffect(() => {
2271
+ if (externalTheme !== prevExternalThemeRef.current) {
2272
+ prevExternalThemeRef.current = externalTheme;
2273
+ setTheme(externalTheme);
2274
+ }
2275
+ }, [externalTheme]);
2276
+ const onThemeChangeRef = React8.useRef(onThemeChange);
2277
+ onThemeChangeRef.current = onThemeChange;
2278
+ const prevReportedThemeRef = React8.useRef(theme);
2279
+ React8.useEffect(() => {
2280
+ var _a2;
2281
+ if (theme !== prevReportedThemeRef.current) {
2282
+ prevReportedThemeRef.current = theme;
2283
+ (_a2 = onThemeChangeRef.current) == null ? void 0 : _a2.call(onThemeChangeRef, theme);
2284
+ }
2285
+ }, [theme]);
2119
2286
  const effectiveFoldMode = (_b = (_a = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _a : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _b : FOLD_MODE.EXPANDED;
2120
- const [showControllers, setShowControllers] = React7.useState(() => {
2287
+ const [showControllers, setShowControllers] = React8.useState(() => {
2121
2288
  var _a2, _b2;
2122
2289
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
2123
2290
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
2124
2291
  });
2125
- const foldModeRef = React7.useRef(effectiveFoldMode);
2292
+ const foldModeRef = React8.useRef(effectiveFoldMode);
2126
2293
  foldModeRef.current = effectiveFoldMode;
2127
- const containerRef = React7.useRef(null);
2128
- const [detailSelection, setDetailSelection] = React7.useState(null);
2129
- const [conceptOverride, setConceptOverride] = React7.useState(null);
2294
+ const containerRef = React8.useRef(null);
2295
+ const [detailSelection, setDetailSelection] = React8.useState(null);
2296
+ const [conceptOverride, setConceptOverride] = React8.useState(null);
2130
2297
  const {
2131
2298
  width: panelWidth,
2132
2299
  isDragging: isPanelDragging,
2133
2300
  handleMouseDown: onResizeMouseDown
2134
2301
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
2135
- React7.useEffect(() => {
2302
+ React8.useEffect(() => {
2136
2303
  setDetailSelection(null);
2137
2304
  setConceptOverride(null);
2138
2305
  }, [graphspec]);
2139
- React7.useEffect(() => {
2140
- var _a2;
2306
+ React8.useEffect(() => {
2141
2307
  const el = containerRef.current;
2142
2308
  if (!el) return;
2143
- const palette = (_a2 = config.paletteColors) != null ? _a2 : DEFAULT_GRAPH_CONFIG.paletteColors;
2144
- if (!palette) return;
2309
+ const themePalette = getPaletteForTheme(theme);
2310
+ const overrides = config.paletteColors;
2311
+ const palette = overrides ? __spreadValues(__spreadValues({}, themePalette), overrides) : themePalette;
2145
2312
  for (const [cssVar, value] of Object.entries(palette)) {
2146
2313
  el.style.setProperty(cssVar, value);
2147
2314
  }
@@ -2150,15 +2317,15 @@ function GraphViewer(props) {
2150
2317
  el.style.removeProperty(cssVar);
2151
2318
  }
2152
2319
  };
2153
- }, [config.paletteColors]);
2320
+ }, [config.paletteColors, theme]);
2154
2321
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
2155
2322
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
2156
- const reactFlowRef = React7.useRef(null);
2157
- const initialDataRef = React7.useRef(null);
2158
- const rawGraphDataRef = React7.useRef(null);
2159
- const layoutCacheRef = React7.useRef(null);
2160
- const [expandedControllers, setExpandedControllers] = React7.useState(/* @__PURE__ */ new Set());
2161
- const toggleCollapse = React7.useCallback((controllerId) => {
2323
+ const reactFlowRef = React8.useRef(null);
2324
+ const initialDataRef = React8.useRef(null);
2325
+ const rawGraphDataRef = React8.useRef(null);
2326
+ const layoutCacheRef = React8.useRef(null);
2327
+ const [expandedControllers, setExpandedControllers] = React8.useState(/* @__PURE__ */ new Set());
2328
+ const toggleCollapse = React8.useCallback((controllerId) => {
2162
2329
  setExpandedControllers((prev) => {
2163
2330
  const next = new Set(prev);
2164
2331
  if (next.has(controllerId)) next.delete(controllerId);
@@ -2166,8 +2333,8 @@ function GraphViewer(props) {
2166
2333
  return next;
2167
2334
  });
2168
2335
  }, []);
2169
- const [foldedControllers, setFoldedControllers] = React7.useState(/* @__PURE__ */ new Set());
2170
- const toggleFold = React7.useCallback((controllerId, options) => {
2336
+ const [foldedControllers, setFoldedControllers] = React8.useState(/* @__PURE__ */ new Set());
2337
+ const toggleFold = React8.useCallback((controllerId, options) => {
2171
2338
  setFoldedControllers((prev) => {
2172
2339
  const next = new Set(prev);
2173
2340
  const shouldFold = !next.has(controllerId);
@@ -2181,34 +2348,34 @@ function GraphViewer(props) {
2181
2348
  });
2182
2349
  }, []);
2183
2350
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
2184
- const layoutConfig = React7.useMemo(
2351
+ const layoutConfig = React8.useMemo(
2185
2352
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
2186
2353
  [config.nodesep, config.ranksep]
2187
2354
  );
2188
- const showControllersRef = React7.useRef(showControllers);
2355
+ const showControllersRef = React8.useRef(showControllers);
2189
2356
  showControllersRef.current = showControllers;
2190
- const directionRef = React7.useRef(direction);
2357
+ const directionRef = React8.useRef(direction);
2191
2358
  directionRef.current = direction;
2192
- const layoutConfigRef = React7.useRef(layoutConfig);
2359
+ const layoutConfigRef = React8.useRef(layoutConfig);
2193
2360
  layoutConfigRef.current = layoutConfig;
2194
- const initialZoomRef = React7.useRef(config.initialZoom);
2361
+ const initialZoomRef = React8.useRef(config.initialZoom);
2195
2362
  initialZoomRef.current = config.initialZoom;
2196
- const panToTopRef = React7.useRef(config.panToTop);
2363
+ const panToTopRef = React8.useRef(config.panToTop);
2197
2364
  panToTopRef.current = config.panToTop;
2198
- const expandedRef = React7.useRef(expandedControllers);
2365
+ const expandedRef = React8.useRef(expandedControllers);
2199
2366
  expandedRef.current = expandedControllers;
2200
- const toggleCollapseRef = React7.useRef(toggleCollapse);
2367
+ const toggleCollapseRef = React8.useRef(toggleCollapse);
2201
2368
  toggleCollapseRef.current = toggleCollapse;
2202
- const foldedRef = React7.useRef(foldedControllers);
2369
+ const foldedRef = React8.useRef(foldedControllers);
2203
2370
  foldedRef.current = foldedControllers;
2204
- const toggleFoldRef = React7.useRef(toggleFold);
2371
+ const toggleFoldRef = React8.useRef(toggleFold);
2205
2372
  toggleFoldRef.current = toggleFold;
2206
- const isFirstFoldEffect = React7.useRef(true);
2207
- const prevFoldSizeRef = React7.useRef(0);
2208
- const skipNextFoldEffectRef = React7.useRef(false);
2209
- const statusMapRef = React7.useRef(statusMap);
2373
+ const isFirstFoldEffect = React8.useRef(true);
2374
+ const prevFoldSizeRef = React8.useRef(0);
2375
+ const skipNextFoldEffectRef = React8.useRef(false);
2376
+ const statusMapRef = React8.useRef(statusMap);
2210
2377
  statusMapRef.current = statusMap;
2211
- React7.useEffect(() => {
2378
+ React8.useEffect(() => {
2212
2379
  if (!initialDataRef.current) return;
2213
2380
  let cancelled = false;
2214
2381
  (async () => {
@@ -2260,7 +2427,7 @@ function GraphViewer(props) {
2260
2427
  cancelled = true;
2261
2428
  };
2262
2429
  }, [direction, layoutConfig]);
2263
- React7.useEffect(() => {
2430
+ React8.useEffect(() => {
2264
2431
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2265
2432
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2266
2433
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2280,7 +2447,7 @@ function GraphViewer(props) {
2280
2447
  );
2281
2448
  setEdges(toAppEdges(withControllers.edges));
2282
2449
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold]);
2283
- React7.useEffect(() => {
2450
+ React8.useEffect(() => {
2284
2451
  if (!graphspec) {
2285
2452
  initialDataRef.current = null;
2286
2453
  rawGraphDataRef.current = null;
@@ -2384,7 +2551,7 @@ function GraphViewer(props) {
2384
2551
  cancelled = true;
2385
2552
  };
2386
2553
  }, [graphspec, edgeType]);
2387
- React7.useEffect(() => {
2554
+ React8.useEffect(() => {
2388
2555
  if (isFirstFoldEffect.current) {
2389
2556
  isFirstFoldEffect.current = false;
2390
2557
  prevFoldSizeRef.current = foldedControllers.size;
@@ -2464,7 +2631,7 @@ function GraphViewer(props) {
2464
2631
  cancelled = true;
2465
2632
  };
2466
2633
  }, [foldedControllers, toggleFold]);
2467
- React7.useEffect(() => {
2634
+ React8.useEffect(() => {
2468
2635
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2469
2636
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2470
2637
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2482,7 +2649,7 @@ function GraphViewer(props) {
2482
2649
  setNodes(applyStatusOverrides(toAppNodes(hydrateLabels(withControllers.nodes)), statusMap));
2483
2650
  setEdges(toAppEdges(withControllers.edges));
2484
2651
  }, [statusMap]);
2485
- const onNodeClick = React7.useCallback(
2652
+ const onNodeClick = React8.useCallback(
2486
2653
  (event, node) => {
2487
2654
  var _a2;
2488
2655
  const nodeData = node.data;
@@ -2524,7 +2691,7 @@ function GraphViewer(props) {
2524
2691
  conceptOverride
2525
2692
  ]
2526
2693
  );
2527
- const onInit = React7.useCallback(
2694
+ const onInit = React8.useCallback(
2528
2695
  (reactFlowInstance) => {
2529
2696
  reactFlowRef.current = reactFlowInstance;
2530
2697
  if (onReactFlowInit) {
@@ -2533,12 +2700,12 @@ function GraphViewer(props) {
2533
2700
  },
2534
2701
  [onReactFlowInit]
2535
2702
  );
2536
- const handlePaneClick = React7.useCallback(() => {
2703
+ const handlePaneClick = React8.useCallback(() => {
2537
2704
  setDetailSelection(null);
2538
2705
  setConceptOverride(null);
2539
2706
  onPaneClick == null ? void 0 : onPaneClick();
2540
2707
  }, [onPaneClick]);
2541
- const handleConceptClick = React7.useCallback(
2708
+ const handleConceptClick = React8.useCallback(
2542
2709
  (conceptCode) => {
2543
2710
  if (!graphspec) return;
2544
2711
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -2550,7 +2717,7 @@ function GraphViewer(props) {
2550
2717
  const detailOpen = detailSelection !== null || conceptOverride !== null;
2551
2718
  const rawAnalysis = (_c = rawGraphDataRef.current) == null ? void 0 : _c.analysis;
2552
2719
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
2553
- const foldAllProps = React7.useMemo(() => {
2720
+ const foldAllProps = React8.useMemo(() => {
2554
2721
  if (!showControllers || !allControllerIds || allControllerIds.size === 0) {
2555
2722
  return {
2556
2723
  onFoldAll: void 0,
@@ -2566,93 +2733,103 @@ function GraphViewer(props) {
2566
2733
  expandAllDisabled: foldedControllers.size === 0
2567
2734
  };
2568
2735
  }, [showControllers, allControllerIds, foldedControllers]);
2569
- return /* @__PURE__ */ jsxs20("div", { ref: containerRef, className: "react-flow-container", children: [
2570
- /* @__PURE__ */ jsx20(
2571
- ReactFlow,
2572
- {
2573
- nodes,
2574
- edges,
2575
- nodeTypes,
2576
- onNodesChange,
2577
- onEdgesChange,
2578
- onNodeClick,
2579
- onPaneClick: handlePaneClick,
2580
- onInit,
2581
- fitView: true,
2582
- fitViewOptions: { padding: 0.1 },
2583
- defaultEdgeOptions: { type: edgeType },
2584
- panOnScroll: true,
2585
- minZoom: 0.1,
2586
- proOptions: { hideAttribution: true },
2587
- panActivationKeyCode: null,
2588
- children: /* @__PURE__ */ jsx20(
2589
- Background,
2736
+ return /* @__PURE__ */ jsxs20(
2737
+ "div",
2738
+ {
2739
+ ref: containerRef,
2740
+ className: `react-flow-container react-flow-container--theme-${theme}`,
2741
+ children: [
2742
+ /* @__PURE__ */ jsx20(
2743
+ ReactFlow,
2744
+ {
2745
+ nodes,
2746
+ edges,
2747
+ nodeTypes,
2748
+ onNodesChange,
2749
+ onEdgesChange,
2750
+ onNodeClick,
2751
+ onPaneClick: handlePaneClick,
2752
+ onInit,
2753
+ fitView: true,
2754
+ fitViewOptions: { padding: 0.1 },
2755
+ defaultEdgeOptions: { type: edgeType },
2756
+ panOnScroll: true,
2757
+ minZoom: 0.1,
2758
+ proOptions: { hideAttribution: true },
2759
+ panActivationKeyCode: null,
2760
+ children: /* @__PURE__ */ jsx20(
2761
+ Background,
2762
+ {
2763
+ variant: BackgroundVariant.Dots,
2764
+ gap: 20,
2765
+ size: 1,
2766
+ color: "var(--color-bg-dots)"
2767
+ }
2768
+ )
2769
+ }
2770
+ ),
2771
+ /* @__PURE__ */ jsxs20(
2772
+ DetailPanel,
2590
2773
  {
2591
- variant: BackgroundVariant.Dots,
2592
- gap: 20,
2593
- size: 1,
2594
- color: "var(--color-bg-dots)"
2774
+ isOpen: detailOpen,
2775
+ onClose: handlePaneClick,
2776
+ width: panelWidth,
2777
+ isDragging: isPanelDragging,
2778
+ onResizeHandleMouseDown: onResizeMouseDown,
2779
+ children: [
2780
+ conceptOverride ? /* @__PURE__ */ jsx20(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx20(
2781
+ PipeDetailPanel,
2782
+ {
2783
+ node: selectedSpecNode,
2784
+ spec: graphspec,
2785
+ onConceptClick: handleConceptClick
2786
+ }
2787
+ ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx20(
2788
+ StuffNodeDetail,
2789
+ {
2790
+ nodeId: detailSelection.nodeId,
2791
+ stuffData: detailSelection.stuffData,
2792
+ graphspec,
2793
+ resolveStorageUrl,
2794
+ canEmbedPdf,
2795
+ onOpenExternally
2796
+ }
2797
+ ) : null,
2798
+ renderDetailExtra && detailSelection && !conceptOverride && renderDetailExtra(detailSelection.nodeId, detailSelection.nodeData)
2799
+ ]
2800
+ }
2801
+ ),
2802
+ !hideToolbar && /* @__PURE__ */ jsx20(
2803
+ GraphToolbar,
2804
+ {
2805
+ direction,
2806
+ onDirectionChange: setDirection,
2807
+ showControllers,
2808
+ onShowControllersChange: setShowControllers,
2809
+ onZoomIn: () => {
2810
+ var _a2;
2811
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomIn();
2812
+ },
2813
+ onZoomOut: () => {
2814
+ var _a2;
2815
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomOut();
2816
+ },
2817
+ onFitView: () => {
2818
+ var _a2;
2819
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.fitView({ padding: 0.1 });
2820
+ },
2821
+ onFoldAll: foldAllProps.onFoldAll,
2822
+ onExpandAll: foldAllProps.onExpandAll,
2823
+ foldAllDisabled: foldAllProps.foldAllDisabled,
2824
+ expandAllDisabled: foldAllProps.expandAllDisabled,
2825
+ theme: showThemeToggle ? theme : void 0,
2826
+ onThemeChange: showThemeToggle ? setTheme : void 0,
2827
+ rightOffset: detailOpen ? panelWidth : 0
2595
2828
  }
2596
2829
  )
2597
- }
2598
- ),
2599
- /* @__PURE__ */ jsxs20(
2600
- DetailPanel,
2601
- {
2602
- isOpen: detailOpen,
2603
- onClose: handlePaneClick,
2604
- width: panelWidth,
2605
- isDragging: isPanelDragging,
2606
- onResizeHandleMouseDown: onResizeMouseDown,
2607
- children: [
2608
- conceptOverride ? /* @__PURE__ */ jsx20(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx20(
2609
- PipeDetailPanel,
2610
- {
2611
- node: selectedSpecNode,
2612
- spec: graphspec,
2613
- onConceptClick: handleConceptClick
2614
- }
2615
- ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx20(
2616
- StuffNodeDetail,
2617
- {
2618
- stuffData: detailSelection.stuffData,
2619
- graphspec,
2620
- resolveStorageUrl,
2621
- canEmbedPdf,
2622
- onOpenExternally
2623
- }
2624
- ) : null,
2625
- renderDetailExtra && detailSelection && !conceptOverride && renderDetailExtra(detailSelection.nodeId, detailSelection.nodeData)
2626
- ]
2627
- }
2628
- ),
2629
- !hideToolbar && /* @__PURE__ */ jsx20(
2630
- GraphToolbar,
2631
- {
2632
- direction,
2633
- onDirectionChange: setDirection,
2634
- showControllers,
2635
- onShowControllersChange: setShowControllers,
2636
- onZoomIn: () => {
2637
- var _a2;
2638
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomIn();
2639
- },
2640
- onZoomOut: () => {
2641
- var _a2;
2642
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomOut();
2643
- },
2644
- onFitView: () => {
2645
- var _a2;
2646
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.fitView({ padding: 0.1 });
2647
- },
2648
- onFoldAll: foldAllProps.onFoldAll,
2649
- onExpandAll: foldAllProps.onExpandAll,
2650
- foldAllDisabled: foldAllProps.foldAllDisabled,
2651
- expandAllDisabled: foldAllProps.expandAllDisabled,
2652
- rightOffset: detailOpen ? panelWidth : 0
2653
- }
2654
- )
2655
- ] });
2830
+ ]
2831
+ }
2832
+ );
2656
2833
  }
2657
2834
  export {
2658
2835
  ConceptDetailPanel,