@pipelex/mthds-ui 0.32.0 → 0.33.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.
@@ -71,7 +71,7 @@ import {
71
71
  stripDomain,
72
72
  textWidthPx,
73
73
  validateGraphSpec
74
- } from "../chunk-T5Y7KNCZ.js";
74
+ } from "../chunk-ABUDTHWF.js";
75
75
  import {
76
76
  makePipeRef,
77
77
  parsePipeRef
@@ -105,6 +105,19 @@
105
105
  inset: 0;
106
106
  background: var(--color-bg);
107
107
  }
108
+ /* The still presentation (`GraphViewer`'s `interactive={false}`): the drawing
109
+ is laid in the page like a picture. The container flows in its parent at the
110
+ height `StillFit` sets inline rather than filling a sized parent, shows the
111
+ host's surface through it, and takes no pointer event, so a click or the
112
+ wheel reaches the page beneath. `GraphViewer` also makes it `inert`, which no
113
+ descendant's own `pointer-events: auto` (a fold button's) can override. */
114
+ .react-flow-container--still {
115
+ position: relative;
116
+ inset: auto;
117
+ width: 100%;
118
+ background: transparent;
119
+ pointer-events: none;
120
+ }
108
121
  .react-flow__node {
109
122
  font-family: var(--font-sans);
110
123
  cursor: pointer;
@@ -134,6 +134,20 @@ interface GraphViewerProps {
134
134
  initialFoldMode?: FoldMode;
135
135
  /** Hide the built-in floating toolbar (direction + controllers toggle). */
136
136
  hideToolbar?: boolean;
137
+ /**
138
+ * `false` draws the graph as a still picture laid in the page, for a host
139
+ * that shows a method's flowchart before anyone asks to explore it: nothing
140
+ * pans, zooms, drags or selects, the drawing is `inert` so no button in it
141
+ * takes a click or the keyboard, a click opens no detail panel and fires no
142
+ * callback, the wheel scrolls the page, and there is no background, no toolbar
143
+ * and no detail panel. The drawing is fitted to the container's width, never
144
+ * past its natural size, and the container takes the height the drawing
145
+ * needs, so the host gives it a width and no height. Defaults to `true`, the
146
+ * interactive canvas. Reactive: a host switching to `true` gets the canvas,
147
+ * fitted to view, and must then size its parent as for any canvas. See
148
+ * `docs/still-presentation.md`.
149
+ */
150
+ interactive?: boolean;
137
151
  /**
138
152
  * Theme *mode*: `dark | light | system`. Reactive: passing it as a prop drives
139
153
  * the active mode, and clearing it back to `undefined` hands control back to
@@ -21,7 +21,7 @@ import {
21
21
  roundedPolylinePath,
22
22
  routeLabelCenter,
23
23
  validateGraphSpec
24
- } from "../../chunk-T5Y7KNCZ.js";
24
+ } from "../../chunk-ABUDTHWF.js";
25
25
  import {
26
26
  parsePipeRef
27
27
  } from "../../chunk-2N65P32H.js";
@@ -68,7 +68,7 @@ import "./viewer/GraphToolbar.css";
68
68
  import "./styles/simple/SimpleStyle.css";
69
69
 
70
70
  // src/graph/react/viewer/GraphViewer.tsx
71
- import React12 from "react";
71
+ import React13 from "react";
72
72
  import {
73
73
  ReactFlow,
74
74
  useNodesState,
@@ -2556,6 +2556,71 @@ function GraphToolbar({
2556
2556
  ] }) });
2557
2557
  }
2558
2558
 
2559
+ // src/graph/react/viewer/StillFit.tsx
2560
+ import React11 from "react";
2561
+ import { useEdges, useNodes, useNodesInitialized, useReactFlow, useStore } from "@xyflow/react";
2562
+
2563
+ // src/graph/react/viewer/stillViewport.ts
2564
+ var STILL_PADDING_PX = 16;
2565
+ var STILL_PLACEHOLDER_HEIGHT_PX = 120;
2566
+ function stillContentBounds(nodeBounds, routes) {
2567
+ let minX = nodeBounds.x;
2568
+ let minY = nodeBounds.y;
2569
+ let maxX = nodeBounds.x + nodeBounds.width;
2570
+ let maxY = nodeBounds.y + nodeBounds.height;
2571
+ const take = (x, y) => {
2572
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return;
2573
+ minX = Math.min(minX, x);
2574
+ minY = Math.min(minY, y);
2575
+ maxX = Math.max(maxX, x);
2576
+ maxY = Math.max(maxY, y);
2577
+ };
2578
+ for (const route of routes) {
2579
+ if (!route) continue;
2580
+ for (const point of route.points) take(point.x, point.y);
2581
+ if (route.label) {
2582
+ take(route.label.x, route.label.y);
2583
+ take(route.label.x + route.label.width, route.label.y + route.label.height);
2584
+ }
2585
+ }
2586
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
2587
+ }
2588
+ function stillViewport(bounds, containerWidth, padding = STILL_PADDING_PX) {
2589
+ if (!(containerWidth > 0) || !(bounds.width > 0) || !(bounds.height > 0)) return null;
2590
+ const room = Math.max(containerWidth - 2 * padding, 1);
2591
+ const zoom = Math.min(1, room / bounds.width);
2592
+ return {
2593
+ x: (containerWidth - bounds.width * zoom) / 2 - bounds.x * zoom,
2594
+ y: padding - bounds.y * zoom,
2595
+ zoom,
2596
+ height: Math.ceil(bounds.height * zoom + 2 * padding)
2597
+ };
2598
+ }
2599
+
2600
+ // src/graph/react/viewer/StillFit.tsx
2601
+ function StillFit({ onHeight }) {
2602
+ const width = useStore((s) => s.width);
2603
+ const nodesInitialized = useNodesInitialized();
2604
+ const nodes = useNodes();
2605
+ const edges = useEdges();
2606
+ const { getNodesBounds, setViewport } = useReactFlow();
2607
+ React11.useLayoutEffect(() => {
2608
+ if (!nodesInitialized || nodes.length === 0) return;
2609
+ const bounds = stillContentBounds(
2610
+ getNodesBounds(nodes),
2611
+ edges.map((e) => {
2612
+ var _a;
2613
+ return (_a = e.data) == null ? void 0 : _a.route;
2614
+ })
2615
+ );
2616
+ const view = stillViewport(bounds, width);
2617
+ if (!view) return;
2618
+ void setViewport({ x: view.x, y: view.y, zoom: view.zoom });
2619
+ onHeight(view.height);
2620
+ }, [nodesInitialized, nodes, edges, width, getNodesBounds, setViewport, onHeight]);
2621
+ return null;
2622
+ }
2623
+
2559
2624
  // src/graph/react/nodes/NodeValidationBadge.tsx
2560
2625
  import { jsx as jsx23 } from "react/jsx-runtime";
2561
2626
  function NodeValidationBadge({
@@ -3213,6 +3278,19 @@ var nodeTypes = __spreadValues(__spreadProps(__spreadValues({}, controllerNodeTy
3213
3278
  pipeCard: PipeCardRFNode
3214
3279
  }), simpleNodeTypes);
3215
3280
  var edgeTypes = { [EDGE_TYPE_ROUTED]: RoutedEdge };
3281
+ var STILL_FLOW_PROPS = {
3282
+ panOnDrag: false,
3283
+ zoomOnScroll: false,
3284
+ zoomOnPinch: false,
3285
+ zoomOnDoubleClick: false,
3286
+ preventScrolling: false,
3287
+ nodesDraggable: false,
3288
+ nodesConnectable: false,
3289
+ nodesFocusable: false,
3290
+ edgesFocusable: false,
3291
+ elementsSelectable: false,
3292
+ disableKeyboardA11y: true
3293
+ };
3216
3294
  function StuffNodeDetail({
3217
3295
  nodeId,
3218
3296
  stuffData,
@@ -3316,6 +3394,7 @@ function GraphViewer(props) {
3316
3394
  initialShowControllers,
3317
3395
  initialFoldMode,
3318
3396
  hideToolbar = false,
3397
+ interactive = true,
3319
3398
  theme: themeProp,
3320
3399
  graphStyle: graphStyleProp,
3321
3400
  onGraphStyleChange,
@@ -3340,20 +3419,20 @@ function GraphViewer(props) {
3340
3419
  onValidationIssueClick
3341
3420
  } = props;
3342
3421
  const graphspecProp = (_a = graph == null ? void 0 : graph.graphSpec) != null ? _a : null;
3343
- const graphspec = React12.useMemo(
3422
+ const graphspec = React13.useMemo(
3344
3423
  () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
3345
3424
  [graphspecProp]
3346
3425
  );
3347
- const [direction, setDirection] = React12.useState(
3426
+ const [direction, setDirection] = React13.useState(
3348
3427
  () => {
3349
3428
  var _a2, _b2;
3350
3429
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
3351
3430
  }
3352
3431
  );
3353
3432
  const externalMode = resolveExternalThemeMode(themeProp, config.theme);
3354
- const [mode, setMode] = React12.useState(externalMode);
3355
- const prevExternalModeRef = React12.useRef(externalMode);
3356
- React12.useEffect(() => {
3433
+ const [mode, setMode] = React13.useState(externalMode);
3434
+ const prevExternalModeRef = React13.useRef(externalMode);
3435
+ React13.useEffect(() => {
3357
3436
  if (externalMode !== prevExternalModeRef.current) {
3358
3437
  prevExternalModeRef.current = externalMode;
3359
3438
  setMode(externalMode);
@@ -3361,13 +3440,13 @@ function GraphViewer(props) {
3361
3440
  }, [externalMode]);
3362
3441
  const systemTheme = useSystemTheme(systemThemeProp);
3363
3442
  const resolvedTheme = resolveActiveTheme(mode, systemTheme);
3364
- const onThemeChangeRef = React12.useRef(onThemeChange);
3443
+ const onThemeChangeRef = React13.useRef(onThemeChange);
3365
3444
  onThemeChangeRef.current = onThemeChange;
3366
- const prevReportedRef = React12.useRef({
3445
+ const prevReportedRef = React13.useRef({
3367
3446
  mode,
3368
3447
  resolvedTheme
3369
3448
  });
3370
- React12.useEffect(() => {
3449
+ React13.useEffect(() => {
3371
3450
  var _a2;
3372
3451
  const prev = prevReportedRef.current;
3373
3452
  if (prev.mode !== mode || prev.resolvedTheme !== resolvedTheme) {
@@ -3376,48 +3455,48 @@ function GraphViewer(props) {
3376
3455
  }
3377
3456
  }, [mode, resolvedTheme]);
3378
3457
  const externalStyle = resolveGraphStyle(graphStyleProp, config.graphStyle);
3379
- const [graphStyle, setGraphStyle] = React12.useState(externalStyle);
3380
- const prevExternalStyleRef = React12.useRef(externalStyle);
3381
- React12.useEffect(() => {
3458
+ const [graphStyle, setGraphStyle] = React13.useState(externalStyle);
3459
+ const prevExternalStyleRef = React13.useRef(externalStyle);
3460
+ React13.useEffect(() => {
3382
3461
  if (externalStyle !== prevExternalStyleRef.current) {
3383
3462
  prevExternalStyleRef.current = externalStyle;
3384
3463
  setGraphStyle(externalStyle);
3385
3464
  }
3386
3465
  }, [externalStyle]);
3387
- const onGraphStyleChangeRef = React12.useRef(onGraphStyleChange);
3466
+ const onGraphStyleChangeRef = React13.useRef(onGraphStyleChange);
3388
3467
  onGraphStyleChangeRef.current = onGraphStyleChange;
3389
- const prevReportedStyleRef = React12.useRef(graphStyle);
3390
- React12.useEffect(() => {
3468
+ const prevReportedStyleRef = React13.useRef(graphStyle);
3469
+ React13.useEffect(() => {
3391
3470
  var _a2;
3392
3471
  if (prevReportedStyleRef.current !== graphStyle) {
3393
3472
  prevReportedStyleRef.current = graphStyle;
3394
3473
  (_a2 = onGraphStyleChangeRef.current) == null ? void 0 : _a2.call(onGraphStyleChangeRef, graphStyle);
3395
3474
  }
3396
3475
  }, [graphStyle]);
3397
- const graphStyleRef = React12.useRef(graphStyle);
3476
+ const graphStyleRef = React13.useRef(graphStyle);
3398
3477
  graphStyleRef.current = graphStyle;
3399
3478
  const styleCapabilities = GRAPH_STYLES[graphStyle].capabilities;
3400
3479
  const effectiveFoldMode = (_c = (_b = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _b : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _c : FOLD_MODE.EXPANDED;
3401
- const [showControllers, setShowControllers] = React12.useState(() => {
3480
+ const [showControllers, setShowControllers] = React13.useState(() => {
3402
3481
  var _a2, _b2;
3403
3482
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
3404
3483
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
3405
3484
  });
3406
- const foldModeRef = React12.useRef(effectiveFoldMode);
3485
+ const foldModeRef = React13.useRef(effectiveFoldMode);
3407
3486
  foldModeRef.current = effectiveFoldMode;
3408
- const containerRef = React12.useRef(null);
3409
- const [detailSelection, setDetailSelection] = React12.useState(null);
3410
- const [conceptOverride, setConceptOverride] = React12.useState(null);
3487
+ const containerRef = React13.useRef(null);
3488
+ const [detailSelection, setDetailSelection] = React13.useState(null);
3489
+ const [conceptOverride, setConceptOverride] = React13.useState(null);
3411
3490
  const {
3412
3491
  width: panelWidth,
3413
3492
  isDragging: isPanelDragging,
3414
3493
  handleMouseDown: onResizeMouseDown
3415
3494
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
3416
- React12.useEffect(() => {
3495
+ React13.useEffect(() => {
3417
3496
  setDetailSelection(null);
3418
3497
  setConceptOverride(null);
3419
3498
  }, [graphspec]);
3420
- React12.useEffect(() => {
3499
+ React13.useEffect(() => {
3421
3500
  const el = containerRef.current;
3422
3501
  if (!el) return;
3423
3502
  const themePalette = getPaletteForTheme(resolvedTheme);
@@ -3432,14 +3511,15 @@ function GraphViewer(props) {
3432
3511
  }
3433
3512
  };
3434
3513
  }, [config.paletteColors, resolvedTheme]);
3514
+ const [stillHeight, setStillHeight] = React13.useState(STILL_PLACEHOLDER_HEIGHT_PX);
3435
3515
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
3436
3516
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
3437
- const reactFlowRef = React12.useRef(null);
3438
- const initialDataRef = React12.useRef(null);
3439
- const rawGraphDataRef = React12.useRef(null);
3440
- const layoutCacheRef = React12.useRef(null);
3441
- const [expandedControllers, setExpandedControllers] = React12.useState(/* @__PURE__ */ new Set());
3442
- const toggleCollapse = React12.useCallback((controllerId) => {
3517
+ const reactFlowRef = React13.useRef(null);
3518
+ const initialDataRef = React13.useRef(null);
3519
+ const rawGraphDataRef = React13.useRef(null);
3520
+ const layoutCacheRef = React13.useRef(null);
3521
+ const [expandedControllers, setExpandedControllers] = React13.useState(/* @__PURE__ */ new Set());
3522
+ const toggleCollapse = React13.useCallback((controllerId) => {
3443
3523
  setExpandedControllers((prev) => {
3444
3524
  const next = new Set(prev);
3445
3525
  if (next.has(controllerId)) next.delete(controllerId);
@@ -3447,17 +3527,17 @@ function GraphViewer(props) {
3447
3527
  return next;
3448
3528
  });
3449
3529
  }, []);
3450
- const [foldedControllers, setFoldedControllers] = React12.useState(/* @__PURE__ */ new Set());
3451
- const styleDefaultFoldsRef = React12.useRef(/* @__PURE__ */ new Set());
3452
- const readerOpenedRef = React12.useRef(/* @__PURE__ */ new Set());
3453
- const markReaderFolds = React12.useCallback((ids, folded) => {
3530
+ const [foldedControllers, setFoldedControllers] = React13.useState(/* @__PURE__ */ new Set());
3531
+ const styleDefaultFoldsRef = React13.useRef(/* @__PURE__ */ new Set());
3532
+ const readerOpenedRef = React13.useRef(/* @__PURE__ */ new Set());
3533
+ const markReaderFolds = React13.useCallback((ids, folded) => {
3454
3534
  for (const id of ids) {
3455
3535
  styleDefaultFoldsRef.current.delete(id);
3456
3536
  if (folded) readerOpenedRef.current.delete(id);
3457
3537
  else readerOpenedRef.current.add(id);
3458
3538
  }
3459
3539
  }, []);
3460
- const toggleFold = React12.useCallback(
3540
+ const toggleFold = React13.useCallback(
3461
3541
  (controllerId, options) => {
3462
3542
  setFoldedControllers((prev) => {
3463
3543
  const next = new Set(prev);
@@ -3475,43 +3555,45 @@ function GraphViewer(props) {
3475
3555
  [markReaderFolds]
3476
3556
  );
3477
3557
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
3478
- const layoutConfig = React12.useMemo(
3558
+ const layoutConfig = React13.useMemo(
3479
3559
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
3480
3560
  [config.nodesep, config.ranksep]
3481
3561
  );
3482
- const showControllersRef = React12.useRef(showControllers);
3562
+ const showControllersRef = React13.useRef(showControllers);
3483
3563
  showControllersRef.current = showControllers;
3484
- const directionRef = React12.useRef(direction);
3564
+ const directionRef = React13.useRef(direction);
3485
3565
  directionRef.current = direction;
3486
- const layoutConfigRef = React12.useRef(layoutConfig);
3566
+ const layoutConfigRef = React13.useRef(layoutConfig);
3487
3567
  layoutConfigRef.current = layoutConfig;
3488
- const initialZoomRef = React12.useRef(config.initialZoom);
3568
+ const initialZoomRef = React13.useRef(config.initialZoom);
3489
3569
  initialZoomRef.current = config.initialZoom;
3490
- const panToTopRef = React12.useRef(config.panToTop);
3570
+ const panToTopRef = React13.useRef(config.panToTop);
3491
3571
  panToTopRef.current = config.panToTop;
3492
- const expandedRef = React12.useRef(expandedControllers);
3572
+ const interactiveRef = React13.useRef(interactive);
3573
+ interactiveRef.current = interactive;
3574
+ const expandedRef = React13.useRef(expandedControllers);
3493
3575
  expandedRef.current = expandedControllers;
3494
- const toggleCollapseRef = React12.useRef(toggleCollapse);
3576
+ const toggleCollapseRef = React13.useRef(toggleCollapse);
3495
3577
  toggleCollapseRef.current = toggleCollapse;
3496
- const foldedRef = React12.useRef(foldedControllers);
3578
+ const foldedRef = React13.useRef(foldedControllers);
3497
3579
  foldedRef.current = foldedControllers;
3498
- const toggleFoldRef = React12.useRef(toggleFold);
3580
+ const toggleFoldRef = React13.useRef(toggleFold);
3499
3581
  toggleFoldRef.current = toggleFold;
3500
- const isFirstFoldEffect = React12.useRef(true);
3501
- const prevFoldSizeRef = React12.useRef(0);
3502
- const skipNextFoldEffectRef = React12.useRef(false);
3503
- const statusMapRef = React12.useRef(statusMap);
3582
+ const isFirstFoldEffect = React13.useRef(true);
3583
+ const prevFoldSizeRef = React13.useRef(0);
3584
+ const skipNextFoldEffectRef = React13.useRef(false);
3585
+ const statusMapRef = React13.useRef(statusMap);
3504
3586
  statusMapRef.current = statusMap;
3505
- const validationIssuesRef = React12.useRef(validationIssues);
3587
+ const validationIssuesRef = React13.useRef(validationIssues);
3506
3588
  validationIssuesRef.current = validationIssues;
3507
- const selectedNodeIdRef = React12.useRef(null);
3589
+ const selectedNodeIdRef = React13.useRef(null);
3508
3590
  selectedNodeIdRef.current = (_d = detailSelection == null ? void 0 : detailSelection.nodeId) != null ? _d : null;
3509
- const [validationOpen, setValidationOpen] = React12.useState(false);
3510
- const openValidationPanel = React12.useCallback(() => setValidationOpen(true), []);
3511
- const validationWidgetAvailable = validationState !== void 0 && !hideToolbar;
3512
- const validationWidgetAvailableRef = React12.useRef(validationWidgetAvailable);
3591
+ const [validationOpen, setValidationOpen] = React13.useState(false);
3592
+ const openValidationPanel = React13.useCallback(() => setValidationOpen(true), []);
3593
+ const validationWidgetAvailable = validationState !== void 0 && !hideToolbar && interactive;
3594
+ const validationWidgetAvailableRef = React13.useRef(validationWidgetAvailable);
3513
3595
  validationWidgetAvailableRef.current = validationWidgetAvailable;
3514
- const decorateNodes = React12.useCallback(
3596
+ const decorateNodes = React13.useCallback(
3515
3597
  (nodes2) => {
3516
3598
  var _a2, _b2, _c2;
3517
3599
  const raw = rawGraphDataRef.current;
@@ -3532,7 +3614,7 @@ function GraphViewer(props) {
3532
3614
  },
3533
3615
  [openValidationPanel]
3534
3616
  );
3535
- const frameLayout = React12.useCallback(
3617
+ const frameLayout = React13.useCallback(
3536
3618
  (style, layout, graphspec2, analysis) => GRAPH_STYLE_PIPELINES[style].frame({
3537
3619
  nodes: cloneCachedNodes(layout.nodes),
3538
3620
  edges: layout.edges,
@@ -3551,18 +3633,18 @@ function GraphViewer(props) {
3551
3633
  const data = initialDataRef.current;
3552
3634
  return cache !== null && data !== null && cache.graphspec === data._graphspec && cache.graphStyle === data._graphStyle;
3553
3635
  };
3554
- const nodesRef = React12.useRef([]);
3636
+ const nodesRef = React13.useRef([]);
3555
3637
  nodesRef.current = nodes;
3556
- const onValidationIssueClickRef = React12.useRef(onValidationIssueClick);
3638
+ const onValidationIssueClickRef = React13.useRef(onValidationIssueClick);
3557
3639
  onValidationIssueClickRef.current = onValidationIssueClick;
3558
- const flashTimeoutRef = React12.useRef(null);
3559
- React12.useEffect(
3640
+ const flashTimeoutRef = React13.useRef(null);
3641
+ React13.useEffect(
3560
3642
  () => () => {
3561
3643
  if (flashTimeoutRef.current) clearTimeout(flashTimeoutRef.current);
3562
3644
  },
3563
3645
  []
3564
3646
  );
3565
- const handleValidationIssueClick = React12.useCallback(
3647
+ const handleValidationIssueClick = React13.useCallback(
3566
3648
  (index, issue) => {
3567
3649
  var _a2, _b2, _c2;
3568
3650
  (_a2 = onValidationIssueClickRef.current) == null ? void 0 : _a2.call(onValidationIssueClickRef, index, issue);
@@ -3605,7 +3687,7 @@ function GraphViewer(props) {
3605
3687
  },
3606
3688
  [setNodes]
3607
3689
  );
3608
- React12.useEffect(() => {
3690
+ React13.useEffect(() => {
3609
3691
  if (!initialDataRef.current) return;
3610
3692
  let cancelled = false;
3611
3693
  void (async () => {
@@ -3638,7 +3720,7 @@ function GraphViewer(props) {
3638
3720
  setNodes(decorateNodes(withControllers.nodes));
3639
3721
  setEdges(toAppEdges(withControllers.edges));
3640
3722
  setTimeout(() => {
3641
- if (!cancelled && reactFlowRef.current) {
3723
+ if (!cancelled && reactFlowRef.current && interactiveRef.current) {
3642
3724
  void reactFlowRef.current.fitView({ padding: 0.1 });
3643
3725
  }
3644
3726
  }, 50);
@@ -3650,7 +3732,7 @@ function GraphViewer(props) {
3650
3732
  cancelled = true;
3651
3733
  };
3652
3734
  }, [direction, layoutConfig]);
3653
- React12.useEffect(() => {
3735
+ React13.useEffect(() => {
3654
3736
  const cache = layoutCacheRef.current;
3655
3737
  const data = initialDataRef.current;
3656
3738
  if (!cache || !data || !cacheMatchesData()) return;
@@ -3658,7 +3740,7 @@ function GraphViewer(props) {
3658
3740
  setNodes(decorateNodes(withControllers.nodes));
3659
3741
  setEdges(toAppEdges(withControllers.edges));
3660
3742
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold, decorateNodes]);
3661
- React12.useEffect(() => {
3743
+ React13.useEffect(() => {
3662
3744
  var _a2;
3663
3745
  if (!graphspec) {
3664
3746
  initialDataRef.current = null;
@@ -3758,7 +3840,7 @@ function GraphViewer(props) {
3758
3840
  setNodes(decorateNodes(withControllers.nodes));
3759
3841
  setEdges(toAppEdges(withControllers.edges));
3760
3842
  setTimeout(() => {
3761
- if (!cancelled && reactFlowRef.current) {
3843
+ if (!cancelled && reactFlowRef.current && interactiveRef.current) {
3762
3844
  void reactFlowRef.current.fitView({ padding: 0.1 });
3763
3845
  if (initialZoomRef.current !== void 0 && initialZoomRef.current !== null) {
3764
3846
  void reactFlowRef.current.zoomTo(initialZoomRef.current);
@@ -3777,7 +3859,7 @@ function GraphViewer(props) {
3777
3859
  cancelled = true;
3778
3860
  };
3779
3861
  }, [graphspec, edgeType, graphStyle]);
3780
- React12.useEffect(() => {
3862
+ React13.useEffect(() => {
3781
3863
  if (isFirstFoldEffect.current) {
3782
3864
  isFirstFoldEffect.current = false;
3783
3865
  prevFoldSizeRef.current = foldedControllers.size;
@@ -3842,7 +3924,7 @@ function GraphViewer(props) {
3842
3924
  setNodes(decorateNodes(withControllers.nodes));
3843
3925
  setEdges(toAppEdges(withControllers.edges));
3844
3926
  setTimeout(() => {
3845
- if (!cancelled && reactFlowRef.current) {
3927
+ if (!cancelled && reactFlowRef.current && interactiveRef.current) {
3846
3928
  void reactFlowRef.current.fitView({ padding: 0.1 });
3847
3929
  }
3848
3930
  }, 50);
@@ -3854,7 +3936,7 @@ function GraphViewer(props) {
3854
3936
  cancelled = true;
3855
3937
  };
3856
3938
  }, [foldedControllers, toggleFold]);
3857
- React12.useEffect(() => {
3939
+ React13.useEffect(() => {
3858
3940
  const cache = layoutCacheRef.current;
3859
3941
  const data = initialDataRef.current;
3860
3942
  if (!cache || !data || !cacheMatchesData()) return;
@@ -3862,14 +3944,14 @@ function GraphViewer(props) {
3862
3944
  setNodes(decorateNodes(withControllers.nodes));
3863
3945
  setEdges(toAppEdges(withControllers.edges));
3864
3946
  }, [statusMap, decorateNodes]);
3865
- React12.useEffect(() => {
3947
+ React13.useEffect(() => {
3866
3948
  const cache = layoutCacheRef.current;
3867
3949
  const data = initialDataRef.current;
3868
3950
  if (!cache || !data || !cacheMatchesData()) return;
3869
3951
  const withControllers = frameLayout(cache.graphStyle, cache, data._graphspec, data._analysis);
3870
3952
  setNodes(decorateNodes(withControllers.nodes));
3871
3953
  }, [validationIssues, validationWidgetAvailable, decorateNodes]);
3872
- const onNodeClick = React12.useCallback(
3954
+ const onNodeClick = React13.useCallback(
3873
3955
  (event, node) => {
3874
3956
  const nodeData = node.data;
3875
3957
  onNodeSelect == null ? void 0 : onNodeSelect(node.id, nodeData, event);
@@ -3910,7 +3992,7 @@ function GraphViewer(props) {
3910
3992
  conceptOverride
3911
3993
  ]
3912
3994
  );
3913
- const onInit = React12.useCallback(
3995
+ const onInit = React13.useCallback(
3914
3996
  (reactFlowInstance) => {
3915
3997
  reactFlowRef.current = reactFlowInstance;
3916
3998
  if (onReactFlowInit) {
@@ -3919,12 +4001,28 @@ function GraphViewer(props) {
3919
4001
  },
3920
4002
  [onReactFlowInit]
3921
4003
  );
3922
- const handlePaneClick = React12.useCallback(() => {
4004
+ const prevInteractiveRef = React13.useRef(interactive);
4005
+ React13.useEffect(() => {
4006
+ if (prevInteractiveRef.current === interactive) return;
4007
+ prevInteractiveRef.current = interactive;
4008
+ if (!interactive) {
4009
+ setDetailSelection(null);
4010
+ setConceptOverride(null);
4011
+ setValidationOpen(false);
4012
+ return;
4013
+ }
4014
+ const timer = setTimeout(() => {
4015
+ var _a2;
4016
+ void ((_a2 = reactFlowRef.current) == null ? void 0 : _a2.fitView({ padding: 0.1 }));
4017
+ }, 50);
4018
+ return () => clearTimeout(timer);
4019
+ }, [interactive]);
4020
+ const handlePaneClick = React13.useCallback(() => {
3923
4021
  setDetailSelection(null);
3924
4022
  setConceptOverride(null);
3925
4023
  onPaneClick == null ? void 0 : onPaneClick();
3926
4024
  }, [onPaneClick]);
3927
- const handleConceptClick = React12.useCallback(
4025
+ const handleConceptClick = React13.useCallback(
3928
4026
  (conceptCode) => {
3929
4027
  if (!graphspec) return;
3930
4028
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -3941,7 +4039,7 @@ function GraphViewer(props) {
3941
4039
  const rawAnalysis = (_e = rawGraphDataRef.current) == null ? void 0 : _e.analysis;
3942
4040
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
3943
4041
  const foldControlsAvailable = styleCapabilities.foldAll && (!styleCapabilities.controllerFrameToggle || showControllers);
3944
- const foldAllProps = React12.useMemo(() => {
4042
+ const foldAllProps = React13.useMemo(() => {
3945
4043
  if (!foldControlsAvailable || !allControllerIds || allControllerIds.size === 0) {
3946
4044
  return {
3947
4045
  onFoldAll: void 0,
@@ -3963,34 +4061,37 @@ function GraphViewer(props) {
3963
4061
  expandAllDisabled: foldedControllers.size === 0
3964
4062
  };
3965
4063
  }, [foldControlsAvailable, allControllerIds, foldedControllers, markReaderFolds]);
3966
- const styleMenuOptions = React12.useMemo(() => resolveStyleMenu(styleMenu), [styleMenu]);
4064
+ const styleMenuOptions = React13.useMemo(() => resolveStyleMenu(styleMenu), [styleMenu]);
3967
4065
  return /* @__PURE__ */ jsxs27(
3968
4066
  "div",
3969
4067
  {
3970
4068
  ref: containerRef,
3971
- className: `react-flow-container react-flow-container--theme-${resolvedTheme} react-flow-container--mode-${mode}`,
4069
+ className: `react-flow-container react-flow-container--theme-${resolvedTheme} react-flow-container--mode-${mode}${interactive ? "" : " react-flow-container--still"}`,
4070
+ style: interactive ? void 0 : { height: stillHeight },
4071
+ inert: !interactive,
3972
4072
  children: [
3973
4073
  /* @__PURE__ */ jsxs27(
3974
4074
  ReactFlow,
3975
- {
4075
+ __spreadProps(__spreadValues({
3976
4076
  nodes,
3977
4077
  edges,
3978
4078
  nodeTypes,
3979
4079
  edgeTypes,
3980
4080
  onNodesChange,
3981
4081
  onEdgesChange,
3982
- onNodeClick,
3983
- onPaneClick: handlePaneClick,
4082
+ onNodeClick: interactive ? onNodeClick : void 0,
4083
+ onPaneClick: interactive ? handlePaneClick : void 0,
3984
4084
  onInit,
3985
- fitView: true,
4085
+ fitView: interactive,
3986
4086
  fitViewOptions: { padding: 0.1 },
3987
4087
  defaultEdgeOptions: { type: edgeType },
3988
- panOnScroll: true,
3989
- minZoom: 0.1,
4088
+ panOnScroll: interactive,
4089
+ minZoom: interactive ? 0.1 : 0.01,
3990
4090
  proOptions: { hideAttribution: true },
3991
- panActivationKeyCode: null,
4091
+ panActivationKeyCode: null
4092
+ }, interactive ? {} : STILL_FLOW_PROPS), {
3992
4093
  children: [
3993
- /* @__PURE__ */ jsx29(
4094
+ interactive ? /* @__PURE__ */ jsx29(
3994
4095
  Background,
3995
4096
  {
3996
4097
  variant: BackgroundVariant.Dots,
@@ -3998,8 +4099,8 @@ function GraphViewer(props) {
3998
4099
  size: 1,
3999
4100
  color: "var(--color-bg-dots)"
4000
4101
  }
4001
- ),
4002
- !hideToolbar && /* @__PURE__ */ jsx29(
4102
+ ) : /* @__PURE__ */ jsx29(StillFit, { onHeight: setStillHeight }),
4103
+ interactive && !hideToolbar && /* @__PURE__ */ jsx29(
4003
4104
  GraphToolbar,
4004
4105
  {
4005
4106
  direction,
@@ -4037,9 +4138,9 @@ function GraphViewer(props) {
4037
4138
  }
4038
4139
  )
4039
4140
  ]
4040
- }
4141
+ })
4041
4142
  ),
4042
- /* @__PURE__ */ jsxs27(
4143
+ interactive && /* @__PURE__ */ jsxs27(
4043
4144
  DetailPanel,
4044
4145
  {
4045
4146
  isOpen: detailOpen,