@pipelex/mthds-ui 0.6.5 → 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.
@@ -29,7 +29,7 @@ import "./stuff/StuffViewer.css";
29
29
  import "./viewer/GraphToolbar.css";
30
30
 
31
31
  // src/graph/react/viewer/GraphViewer.tsx
32
- import React7 from "react";
32
+ import React8 from "react";
33
33
  import {
34
34
  ReactFlow,
35
35
  useNodesState,
@@ -496,8 +496,10 @@ function DetailPanel({
496
496
  "aria-label": "Resize panel"
497
497
  }
498
498
  ),
499
- /* @__PURE__ */ jsx2("button", { className: "detail-panel-close", onClick: onClose, "aria-label": "Close panel", children: "x" }),
500
- /* @__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
+ ] })
501
503
  ] });
502
504
  }
503
505
 
@@ -1324,6 +1326,7 @@ function GenericExecutionData({ data }) {
1324
1326
  }
1325
1327
 
1326
1328
  // src/graph/react/detail/ConceptDetailPanel.tsx
1329
+ import React6 from "react";
1327
1330
  import { Fragment as Fragment10, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1328
1331
  function ConceptDetailPanel({
1329
1332
  concept,
@@ -1331,8 +1334,10 @@ function ConceptDetailPanel({
1331
1334
  isDryRun,
1332
1335
  resolveStorageUrl,
1333
1336
  canEmbedPdf,
1334
- onOpenExternally
1337
+ onOpenExternally,
1338
+ instanceKey
1335
1339
  }) {
1340
+ var _a;
1336
1341
  return /* @__PURE__ */ jsxs14(Fragment10, { children: [
1337
1342
  /* @__PURE__ */ jsxs14("div", { className: "detail-header", children: [
1338
1343
  /* @__PURE__ */ jsx14("span", { className: "detail-concept-code", children: concept.code }),
@@ -1343,22 +1348,88 @@ function ConceptDetailPanel({
1343
1348
  "refines ",
1344
1349
  /* @__PURE__ */ jsx14("span", { className: "detail-refines-code", children: concept.refines })
1345
1350
  ] }),
1346
- concept.json_schema ? /* @__PURE__ */ jsxs14("div", { children: [
1347
- /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Structure" }),
1348
- /* @__PURE__ */ jsx14(SchemaTable, { schema: concept.json_schema, isDryRun })
1349
- ] }) : /* @__PURE__ */ jsx14("div", { className: "detail-not-available", children: "Schema not available" }),
1350
- ioData && !isDryRun && /* @__PURE__ */ jsxs14("div", { children: [
1351
- /* @__PURE__ */ jsx14("div", { className: "detail-section-label", children: "Data" }),
1352
- /* @__PURE__ */ jsx14(
1353
- StuffViewer,
1354
- {
1355
- stuff: toStuffViewerData(ioData),
1356
- resolveStorageUrl,
1357
- canEmbedPdf,
1358
- onOpenExternally
1359
- }
1360
- )
1361
- ] })
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 })
1362
1433
  ] });
1363
1434
  }
1364
1435
  function SchemaTable({
@@ -2098,6 +2169,7 @@ var nodeTypes = __spreadProps(__spreadValues({}, controllerNodeTypes), {
2098
2169
  pipeCard: PipeCardRFNode
2099
2170
  });
2100
2171
  function StuffNodeDetail({
2172
+ nodeId,
2101
2173
  stuffData,
2102
2174
  graphspec,
2103
2175
  resolveStorageUrl,
@@ -2110,6 +2182,7 @@ function StuffNodeDetail({
2110
2182
  {
2111
2183
  concept: conceptInfo,
2112
2184
  ioData: stuffData,
2185
+ instanceKey: nodeId,
2113
2186
  resolveStorageUrl,
2114
2187
  canEmbedPdf,
2115
2188
  onOpenExternally
@@ -2181,29 +2254,29 @@ function GraphViewer(props) {
2181
2254
  canEmbedPdf,
2182
2255
  onOpenExternally
2183
2256
  } = props;
2184
- const graphspec = React7.useMemo(
2257
+ const graphspec = React8.useMemo(
2185
2258
  () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
2186
2259
  [graphspecProp]
2187
2260
  );
2188
- const [direction, setDirection] = React7.useState(
2261
+ const [direction, setDirection] = React8.useState(
2189
2262
  () => {
2190
2263
  var _a2, _b2;
2191
2264
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
2192
2265
  }
2193
2266
  );
2194
2267
  const externalTheme = resolveExternalTheme(themeProp, config.theme);
2195
- const [theme, setTheme] = React7.useState(externalTheme);
2196
- const prevExternalThemeRef = React7.useRef(externalTheme);
2197
- React7.useEffect(() => {
2268
+ const [theme, setTheme] = React8.useState(externalTheme);
2269
+ const prevExternalThemeRef = React8.useRef(externalTheme);
2270
+ React8.useEffect(() => {
2198
2271
  if (externalTheme !== prevExternalThemeRef.current) {
2199
2272
  prevExternalThemeRef.current = externalTheme;
2200
2273
  setTheme(externalTheme);
2201
2274
  }
2202
2275
  }, [externalTheme]);
2203
- const onThemeChangeRef = React7.useRef(onThemeChange);
2276
+ const onThemeChangeRef = React8.useRef(onThemeChange);
2204
2277
  onThemeChangeRef.current = onThemeChange;
2205
- const prevReportedThemeRef = React7.useRef(theme);
2206
- React7.useEffect(() => {
2278
+ const prevReportedThemeRef = React8.useRef(theme);
2279
+ React8.useEffect(() => {
2207
2280
  var _a2;
2208
2281
  if (theme !== prevReportedThemeRef.current) {
2209
2282
  prevReportedThemeRef.current = theme;
@@ -2211,26 +2284,26 @@ function GraphViewer(props) {
2211
2284
  }
2212
2285
  }, [theme]);
2213
2286
  const effectiveFoldMode = (_b = (_a = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _a : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _b : FOLD_MODE.EXPANDED;
2214
- const [showControllers, setShowControllers] = React7.useState(() => {
2287
+ const [showControllers, setShowControllers] = React8.useState(() => {
2215
2288
  var _a2, _b2;
2216
2289
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
2217
2290
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
2218
2291
  });
2219
- const foldModeRef = React7.useRef(effectiveFoldMode);
2292
+ const foldModeRef = React8.useRef(effectiveFoldMode);
2220
2293
  foldModeRef.current = effectiveFoldMode;
2221
- const containerRef = React7.useRef(null);
2222
- const [detailSelection, setDetailSelection] = React7.useState(null);
2223
- 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);
2224
2297
  const {
2225
2298
  width: panelWidth,
2226
2299
  isDragging: isPanelDragging,
2227
2300
  handleMouseDown: onResizeMouseDown
2228
2301
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
2229
- React7.useEffect(() => {
2302
+ React8.useEffect(() => {
2230
2303
  setDetailSelection(null);
2231
2304
  setConceptOverride(null);
2232
2305
  }, [graphspec]);
2233
- React7.useEffect(() => {
2306
+ React8.useEffect(() => {
2234
2307
  const el = containerRef.current;
2235
2308
  if (!el) return;
2236
2309
  const themePalette = getPaletteForTheme(theme);
@@ -2247,12 +2320,12 @@ function GraphViewer(props) {
2247
2320
  }, [config.paletteColors, theme]);
2248
2321
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
2249
2322
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
2250
- const reactFlowRef = React7.useRef(null);
2251
- const initialDataRef = React7.useRef(null);
2252
- const rawGraphDataRef = React7.useRef(null);
2253
- const layoutCacheRef = React7.useRef(null);
2254
- const [expandedControllers, setExpandedControllers] = React7.useState(/* @__PURE__ */ new Set());
2255
- 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) => {
2256
2329
  setExpandedControllers((prev) => {
2257
2330
  const next = new Set(prev);
2258
2331
  if (next.has(controllerId)) next.delete(controllerId);
@@ -2260,8 +2333,8 @@ function GraphViewer(props) {
2260
2333
  return next;
2261
2334
  });
2262
2335
  }, []);
2263
- const [foldedControllers, setFoldedControllers] = React7.useState(/* @__PURE__ */ new Set());
2264
- const toggleFold = React7.useCallback((controllerId, options) => {
2336
+ const [foldedControllers, setFoldedControllers] = React8.useState(/* @__PURE__ */ new Set());
2337
+ const toggleFold = React8.useCallback((controllerId, options) => {
2265
2338
  setFoldedControllers((prev) => {
2266
2339
  const next = new Set(prev);
2267
2340
  const shouldFold = !next.has(controllerId);
@@ -2275,34 +2348,34 @@ function GraphViewer(props) {
2275
2348
  });
2276
2349
  }, []);
2277
2350
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
2278
- const layoutConfig = React7.useMemo(
2351
+ const layoutConfig = React8.useMemo(
2279
2352
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
2280
2353
  [config.nodesep, config.ranksep]
2281
2354
  );
2282
- const showControllersRef = React7.useRef(showControllers);
2355
+ const showControllersRef = React8.useRef(showControllers);
2283
2356
  showControllersRef.current = showControllers;
2284
- const directionRef = React7.useRef(direction);
2357
+ const directionRef = React8.useRef(direction);
2285
2358
  directionRef.current = direction;
2286
- const layoutConfigRef = React7.useRef(layoutConfig);
2359
+ const layoutConfigRef = React8.useRef(layoutConfig);
2287
2360
  layoutConfigRef.current = layoutConfig;
2288
- const initialZoomRef = React7.useRef(config.initialZoom);
2361
+ const initialZoomRef = React8.useRef(config.initialZoom);
2289
2362
  initialZoomRef.current = config.initialZoom;
2290
- const panToTopRef = React7.useRef(config.panToTop);
2363
+ const panToTopRef = React8.useRef(config.panToTop);
2291
2364
  panToTopRef.current = config.panToTop;
2292
- const expandedRef = React7.useRef(expandedControllers);
2365
+ const expandedRef = React8.useRef(expandedControllers);
2293
2366
  expandedRef.current = expandedControllers;
2294
- const toggleCollapseRef = React7.useRef(toggleCollapse);
2367
+ const toggleCollapseRef = React8.useRef(toggleCollapse);
2295
2368
  toggleCollapseRef.current = toggleCollapse;
2296
- const foldedRef = React7.useRef(foldedControllers);
2369
+ const foldedRef = React8.useRef(foldedControllers);
2297
2370
  foldedRef.current = foldedControllers;
2298
- const toggleFoldRef = React7.useRef(toggleFold);
2371
+ const toggleFoldRef = React8.useRef(toggleFold);
2299
2372
  toggleFoldRef.current = toggleFold;
2300
- const isFirstFoldEffect = React7.useRef(true);
2301
- const prevFoldSizeRef = React7.useRef(0);
2302
- const skipNextFoldEffectRef = React7.useRef(false);
2303
- 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);
2304
2377
  statusMapRef.current = statusMap;
2305
- React7.useEffect(() => {
2378
+ React8.useEffect(() => {
2306
2379
  if (!initialDataRef.current) return;
2307
2380
  let cancelled = false;
2308
2381
  (async () => {
@@ -2354,7 +2427,7 @@ function GraphViewer(props) {
2354
2427
  cancelled = true;
2355
2428
  };
2356
2429
  }, [direction, layoutConfig]);
2357
- React7.useEffect(() => {
2430
+ React8.useEffect(() => {
2358
2431
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2359
2432
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2360
2433
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2374,7 +2447,7 @@ function GraphViewer(props) {
2374
2447
  );
2375
2448
  setEdges(toAppEdges(withControllers.edges));
2376
2449
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold]);
2377
- React7.useEffect(() => {
2450
+ React8.useEffect(() => {
2378
2451
  if (!graphspec) {
2379
2452
  initialDataRef.current = null;
2380
2453
  rawGraphDataRef.current = null;
@@ -2478,7 +2551,7 @@ function GraphViewer(props) {
2478
2551
  cancelled = true;
2479
2552
  };
2480
2553
  }, [graphspec, edgeType]);
2481
- React7.useEffect(() => {
2554
+ React8.useEffect(() => {
2482
2555
  if (isFirstFoldEffect.current) {
2483
2556
  isFirstFoldEffect.current = false;
2484
2557
  prevFoldSizeRef.current = foldedControllers.size;
@@ -2558,7 +2631,7 @@ function GraphViewer(props) {
2558
2631
  cancelled = true;
2559
2632
  };
2560
2633
  }, [foldedControllers, toggleFold]);
2561
- React7.useEffect(() => {
2634
+ React8.useEffect(() => {
2562
2635
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2563
2636
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2564
2637
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2576,7 +2649,7 @@ function GraphViewer(props) {
2576
2649
  setNodes(applyStatusOverrides(toAppNodes(hydrateLabels(withControllers.nodes)), statusMap));
2577
2650
  setEdges(toAppEdges(withControllers.edges));
2578
2651
  }, [statusMap]);
2579
- const onNodeClick = React7.useCallback(
2652
+ const onNodeClick = React8.useCallback(
2580
2653
  (event, node) => {
2581
2654
  var _a2;
2582
2655
  const nodeData = node.data;
@@ -2618,7 +2691,7 @@ function GraphViewer(props) {
2618
2691
  conceptOverride
2619
2692
  ]
2620
2693
  );
2621
- const onInit = React7.useCallback(
2694
+ const onInit = React8.useCallback(
2622
2695
  (reactFlowInstance) => {
2623
2696
  reactFlowRef.current = reactFlowInstance;
2624
2697
  if (onReactFlowInit) {
@@ -2627,12 +2700,12 @@ function GraphViewer(props) {
2627
2700
  },
2628
2701
  [onReactFlowInit]
2629
2702
  );
2630
- const handlePaneClick = React7.useCallback(() => {
2703
+ const handlePaneClick = React8.useCallback(() => {
2631
2704
  setDetailSelection(null);
2632
2705
  setConceptOverride(null);
2633
2706
  onPaneClick == null ? void 0 : onPaneClick();
2634
2707
  }, [onPaneClick]);
2635
- const handleConceptClick = React7.useCallback(
2708
+ const handleConceptClick = React8.useCallback(
2636
2709
  (conceptCode) => {
2637
2710
  if (!graphspec) return;
2638
2711
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -2644,7 +2717,7 @@ function GraphViewer(props) {
2644
2717
  const detailOpen = detailSelection !== null || conceptOverride !== null;
2645
2718
  const rawAnalysis = (_c = rawGraphDataRef.current) == null ? void 0 : _c.analysis;
2646
2719
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
2647
- const foldAllProps = React7.useMemo(() => {
2720
+ const foldAllProps = React8.useMemo(() => {
2648
2721
  if (!showControllers || !allControllerIds || allControllerIds.size === 0) {
2649
2722
  return {
2650
2723
  onFoldAll: void 0,
@@ -2714,6 +2787,7 @@ function GraphViewer(props) {
2714
2787
  ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx20(
2715
2788
  StuffNodeDetail,
2716
2789
  {
2790
+ nodeId: detailSelection.nodeId,
2717
2791
  stuffData: detailSelection.stuffData,
2718
2792
  graphspec,
2719
2793
  resolveStorageUrl,