@pipelex/mthds-ui 0.8.0 → 0.9.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.
@@ -5,6 +5,7 @@ import {
5
5
  FOLD_MODE,
6
6
  GRAPH_DIRECTION,
7
7
  GRAPH_THEME,
8
+ GRAPH_THEME_MODE,
8
9
  MAX_VISIBLE_CONTROLLER_CHILDREN,
9
10
  applyControllers,
10
11
  applyFolds,
@@ -16,7 +17,7 @@ import {
16
17
  resolveConceptRef,
17
18
  stuffDigestFromId,
18
19
  validateGraphSpec
19
- } from "../../chunk-7VQUZ35P.js";
20
+ } from "../../chunk-WNSV4E7G.js";
20
21
  import {
21
22
  __spreadProps,
22
23
  __spreadValues
@@ -29,7 +30,7 @@ import "./stuff/StuffViewer.css";
29
30
  import "./viewer/GraphToolbar.css";
30
31
 
31
32
  // src/graph/react/viewer/GraphViewer.tsx
32
- import React8 from "react";
33
+ import React9 from "react";
33
34
  import {
34
35
  ReactFlow,
35
36
  useNodesState,
@@ -38,6 +39,51 @@ import {
38
39
  BackgroundVariant
39
40
  } from "@xyflow/react";
40
41
 
42
+ // src/graph/react/viewer/useSystemTheme.ts
43
+ import React from "react";
44
+ var PREFERS_DARK_QUERY = "(prefers-color-scheme: dark)";
45
+ function prefersDarkToTheme(prefersDark) {
46
+ return prefersDark ? GRAPH_THEME.DARK : GRAPH_THEME.LIGHT;
47
+ }
48
+ function detectSystemTheme() {
49
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
50
+ return GRAPH_THEME.DARK;
51
+ }
52
+ return prefersDarkToTheme(window.matchMedia(PREFERS_DARK_QUERY).matches);
53
+ }
54
+ function subscribeToSystemTheme(onChange) {
55
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
56
+ return () => {
57
+ };
58
+ }
59
+ const mql = window.matchMedia(PREFERS_DARK_QUERY);
60
+ if (typeof mql.addEventListener === "function") {
61
+ mql.addEventListener("change", onChange);
62
+ return () => mql.removeEventListener("change", onChange);
63
+ }
64
+ mql.addListener(onChange);
65
+ return () => mql.removeListener(onChange);
66
+ }
67
+ function systemThemeStore(injected) {
68
+ if (injected !== void 0) {
69
+ return {
70
+ subscribe: () => () => {
71
+ },
72
+ getSnapshot: () => injected,
73
+ getServerSnapshot: () => injected
74
+ };
75
+ }
76
+ return {
77
+ subscribe: subscribeToSystemTheme,
78
+ getSnapshot: detectSystemTheme,
79
+ getServerSnapshot: () => GRAPH_THEME.DARK
80
+ };
81
+ }
82
+ function useSystemTheme(injected) {
83
+ const store = React.useMemo(() => systemThemeStore(injected), [injected]);
84
+ return React.useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot);
85
+ }
86
+
41
87
  // src/graph/react/stuff/stuffViewerUtils.ts
42
88
  function isSafeDisplayUrl(url) {
43
89
  if (!url || typeof url !== "string") return false;
@@ -155,7 +201,7 @@ function findStuffDataByDigest(graphspec, digest) {
155
201
  }
156
202
 
157
203
  // src/graph/react/stuff/StuffViewer.tsx
158
- import React from "react";
204
+ import React2 from "react";
159
205
  import DOMPurify from "dompurify";
160
206
  import { jsx, jsxs } from "react/jsx-runtime";
161
207
  var ICON_COPY = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" }) });
@@ -190,15 +236,15 @@ function StuffViewer({
190
236
  onOpenExternally
191
237
  }) {
192
238
  var _a, _b;
193
- const [activeTab, setActiveTab] = React.useState("html");
194
- const [copied, setCopied] = React.useState(false);
195
- const contentRef = React.useRef(null);
196
- const httpInlineUrl = React.useMemo(() => extractInlineUrl(stuff.data), [stuff.data]);
197
- const httpExternalUrl = React.useMemo(() => extractUrl(stuff.data), [stuff.data]);
198
- const storageUri = React.useMemo(() => extractStorageUri(stuff.data), [stuff.data]);
199
- const filename = React.useMemo(() => extractFilename(stuff.data), [stuff.data]);
200
- const [resolvedStorageUrl, setResolvedStorageUrl] = React.useState(null);
201
- React.useEffect(() => {
239
+ const [activeTab, setActiveTab] = React2.useState("html");
240
+ const [copied, setCopied] = React2.useState(false);
241
+ const contentRef = React2.useRef(null);
242
+ const httpInlineUrl = React2.useMemo(() => extractInlineUrl(stuff.data), [stuff.data]);
243
+ const httpExternalUrl = React2.useMemo(() => extractUrl(stuff.data), [stuff.data]);
244
+ const storageUri = React2.useMemo(() => extractStorageUri(stuff.data), [stuff.data]);
245
+ const filename = React2.useMemo(() => extractFilename(stuff.data), [stuff.data]);
246
+ const [resolvedStorageUrl, setResolvedStorageUrl] = React2.useState(null);
247
+ React2.useEffect(() => {
202
248
  setResolvedStorageUrl(null);
203
249
  if (!storageUri || !resolveStorageUrl || httpInlineUrl) {
204
250
  return;
@@ -216,14 +262,14 @@ function StuffViewer({
216
262
  }, [storageUri, resolveStorageUrl, httpInlineUrl]);
217
263
  const inlineUrl = httpInlineUrl != null ? httpInlineUrl : resolvedStorageUrl;
218
264
  const externalUrl = httpExternalUrl != null ? httpExternalUrl : resolvedStorageUrl;
219
- const effectiveMime = React.useMemo(
265
+ const effectiveMime = React2.useMemo(
220
266
  () => resolveMimeType(stuff.data, stuff.contentType, externalUrl != null ? externalUrl : storageUri),
221
267
  [stuff.data, stuff.contentType, externalUrl, storageUri]
222
268
  );
223
269
  const isPdf = effectiveMime === "application/pdf";
224
270
  const isImage = (_a = effectiveMime == null ? void 0 : effectiveMime.startsWith("image/")) != null ? _a : false;
225
271
  const htmlTabLabel = getHtmlTabLabel(effectiveMime != null ? effectiveMime : stuff.contentType);
226
- const { jsonString, jsonError } = React.useMemo(() => {
272
+ const { jsonString, jsonError } = React2.useMemo(() => {
227
273
  if (stuff.data == null) return { jsonString: null, jsonError: null };
228
274
  try {
229
275
  return { jsonString: JSON.stringify(stuff.data, null, 2), jsonError: null };
@@ -231,7 +277,7 @@ function StuffViewer({
231
277
  return { jsonString: null, jsonError: err instanceof Error ? err.message : String(err) };
232
278
  }
233
279
  }, [stuff.data]);
234
- React.useEffect(() => {
280
+ React2.useEffect(() => {
235
281
  if (activeTab !== "html" || !contentRef.current) return;
236
282
  contentRef.current.querySelectorAll("a").forEach((link) => {
237
283
  link.setAttribute("target", "_blank");
@@ -461,7 +507,7 @@ function StuffViewer({
461
507
  }
462
508
 
463
509
  // src/graph/react/detail/DetailPanel.tsx
464
- import React2 from "react";
510
+ import React3 from "react";
465
511
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
466
512
  function DetailPanel({
467
513
  isOpen,
@@ -472,7 +518,7 @@ function DetailPanel({
472
518
  onResizeHandleMouseDown,
473
519
  closeOnEscape = true
474
520
  }) {
475
- React2.useEffect(() => {
521
+ React3.useEffect(() => {
476
522
  if (!isOpen || !closeOnEscape) return;
477
523
  const onKeyDown = (e) => {
478
524
  if (e.key === "Escape") onClose();
@@ -504,7 +550,7 @@ function DetailPanel({
504
550
  }
505
551
 
506
552
  // src/graph/react/detail/useResizable.ts
507
- import React3 from "react";
553
+ import React4 from "react";
508
554
  var MAX_WIDTH_RATIO = 0.6;
509
555
  function useResizable({
510
556
  defaultWidth,
@@ -512,11 +558,11 @@ function useResizable({
512
558
  maxWidth,
513
559
  containerRef
514
560
  }) {
515
- const [width, setWidth] = React3.useState(defaultWidth);
516
- const [isDragging, setIsDragging] = React3.useState(false);
517
- const dragRef = React3.useRef({ startX: 0, startWidth: 0, maxAllowed: maxWidth });
518
- const rafRef = React3.useRef(null);
519
- const handleMouseDown = React3.useCallback(
561
+ const [width, setWidth] = React4.useState(defaultWidth);
562
+ const [isDragging, setIsDragging] = React4.useState(false);
563
+ const dragRef = React4.useRef({ startX: 0, startWidth: 0, maxAllowed: maxWidth });
564
+ const rafRef = React4.useRef(null);
565
+ const handleMouseDown = React4.useCallback(
520
566
  (e) => {
521
567
  var _a;
522
568
  e.preventDefault();
@@ -530,7 +576,7 @@ function useResizable({
530
576
  },
531
577
  [width, maxWidth, containerRef]
532
578
  );
533
- React3.useEffect(() => {
579
+ React4.useEffect(() => {
534
580
  if (!isDragging) return;
535
581
  const onMouseMove = (e) => {
536
582
  if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
@@ -568,7 +614,7 @@ function useResizable({
568
614
  }
569
615
 
570
616
  // src/graph/react/detail/PipeDetailPanel.tsx
571
- import React5 from "react";
617
+ import React6 from "react";
572
618
 
573
619
  // src/graph/react/detail/sections/shared.tsx
574
620
  import { useState } from "react";
@@ -1160,7 +1206,7 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1160
1206
  const badge = PIPE_TYPE_BADGES[pipeType];
1161
1207
  const status = node.status;
1162
1208
  const statusColor = (_a = STATUS_COLORS[status]) != null ? _a : "#6272a4";
1163
- const blueprint = React5.useMemo(() => {
1209
+ const blueprint = React6.useMemo(() => {
1164
1210
  var _a2, _b2;
1165
1211
  if (!node.pipe_code || !spec.pipe_registry) return void 0;
1166
1212
  const directKey = `${(_b2 = (_a2 = spec.pipeline_ref) == null ? void 0 : _a2.domain) != null ? _b2 : ""}.${node.pipe_code}`;
@@ -1331,7 +1377,7 @@ function GenericExecutionData({ data }) {
1331
1377
  }
1332
1378
 
1333
1379
  // src/graph/react/detail/ConceptDetailPanel.tsx
1334
- import React6 from "react";
1380
+ import React7 from "react";
1335
1381
  import { Fragment as Fragment10, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1336
1382
  function ConceptDetailPanel({
1337
1383
  concept,
@@ -1376,8 +1422,8 @@ function ConceptBody({
1376
1422
  onOpenExternally
1377
1423
  }) {
1378
1424
  const hasData = Boolean(ioData) && !isDryRun;
1379
- const [activeTab, setActiveTab] = React6.useState(hasData ? "data" : "structure");
1380
- const baseId = React6.useId();
1425
+ const [activeTab, setActiveTab] = React7.useState(hasData ? "data" : "structure");
1426
+ const baseId = React7.useId();
1381
1427
  const tabId = (tab) => `${baseId}-tab-${tab}`;
1382
1428
  const panelId = (tab) => `${baseId}-tabpanel-${tab}`;
1383
1429
  const structure = concept.json_schema ? /* @__PURE__ */ jsxs14("div", { children: [
@@ -1813,6 +1859,47 @@ var MOON_ICON = /* @__PURE__ */ jsx16(
1813
1859
  children: /* @__PURE__ */ jsx16("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
1814
1860
  }
1815
1861
  );
1862
+ var MONITOR_ICON = /* @__PURE__ */ jsxs16(
1863
+ "svg",
1864
+ {
1865
+ viewBox: "0 0 24 24",
1866
+ width: "14",
1867
+ height: "14",
1868
+ fill: "none",
1869
+ stroke: "currentColor",
1870
+ strokeWidth: "2",
1871
+ strokeLinecap: "round",
1872
+ strokeLinejoin: "round",
1873
+ children: [
1874
+ /* @__PURE__ */ jsx16("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
1875
+ /* @__PURE__ */ jsx16("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
1876
+ /* @__PURE__ */ jsx16("line", { x1: "12", y1: "17", x2: "12", y2: "21" })
1877
+ ]
1878
+ }
1879
+ );
1880
+ var THEME_MODE_CYCLE = [
1881
+ GRAPH_THEME_MODE.SYSTEM,
1882
+ GRAPH_THEME_MODE.LIGHT,
1883
+ GRAPH_THEME_MODE.DARK
1884
+ ];
1885
+ function nextThemeMode(current) {
1886
+ const idx = THEME_MODE_CYCLE.indexOf(current);
1887
+ return THEME_MODE_CYCLE[(idx + 1) % THEME_MODE_CYCLE.length];
1888
+ }
1889
+ function themeModeIcon(mode) {
1890
+ if (mode === GRAPH_THEME_MODE.LIGHT) return SUN_ICON;
1891
+ if (mode === GRAPH_THEME_MODE.DARK) return MOON_ICON;
1892
+ return MONITOR_ICON;
1893
+ }
1894
+ function themeModeLabel(mode) {
1895
+ const names = {
1896
+ [GRAPH_THEME_MODE.SYSTEM]: "system",
1897
+ [GRAPH_THEME_MODE.LIGHT]: "light",
1898
+ [GRAPH_THEME_MODE.DARK]: "dark"
1899
+ };
1900
+ const next = nextThemeMode(mode);
1901
+ return `Theme: ${names[mode]} \u2014 switch to ${names[next]}`;
1902
+ }
1816
1903
  function GraphToolbar({
1817
1904
  direction,
1818
1905
  onDirectionChange,
@@ -1825,12 +1912,11 @@ function GraphToolbar({
1825
1912
  onExpandAll,
1826
1913
  foldAllDisabled = false,
1827
1914
  expandAllDisabled = false,
1828
- theme,
1829
- onThemeChange,
1915
+ themeMode,
1916
+ onThemeModeChange,
1830
1917
  rightOffset = 0
1831
1918
  }) {
1832
- const themeToggleEnabled = theme !== void 0 && onThemeChange !== void 0;
1833
- const themeLabel = theme === GRAPH_THEME.LIGHT ? "Switch to dark theme" : "Switch to light theme";
1919
+ const themeToggleEnabled = themeMode !== void 0 && onThemeModeChange !== void 0;
1834
1920
  const isVertical = direction === GRAPH_DIRECTION.TB || direction === GRAPH_DIRECTION.BT;
1835
1921
  const directionLabel = isVertical ? "Switch to horizontal layout" : "Switch to vertical layout";
1836
1922
  const controllersLabel = showControllers ? "Hide pipe controllers" : "Show pipe controllers \u2014 groups pipes by their controlling pipe";
@@ -1926,10 +2012,10 @@ function GraphToolbar({
1926
2012
  {
1927
2013
  type: "button",
1928
2014
  className: "graph-toolbar-btn",
1929
- onClick: () => onThemeChange(theme === GRAPH_THEME.LIGHT ? GRAPH_THEME.DARK : GRAPH_THEME.LIGHT),
1930
- title: themeLabel,
1931
- "aria-label": themeLabel,
1932
- children: theme === GRAPH_THEME.LIGHT ? MOON_ICON : SUN_ICON
2015
+ onClick: () => onThemeModeChange(nextThemeMode(themeMode)),
2016
+ title: themeModeLabel(themeMode),
2017
+ "aria-label": themeModeLabel(themeMode),
2018
+ children: themeModeIcon(themeMode)
1933
2019
  }
1934
2020
  )
1935
2021
  ] })
@@ -2213,9 +2299,12 @@ function seedFoldedControllers(mode, controllerIds) {
2213
2299
  if (mode === FOLD_MODE.FOLDED) return new Set(controllerIds);
2214
2300
  return /* @__PURE__ */ new Set();
2215
2301
  }
2216
- function resolveExternalTheme(themeProp, configTheme) {
2302
+ function resolveExternalThemeMode(themeProp, configTheme) {
2217
2303
  var _a, _b;
2218
- return (_b = (_a = themeProp != null ? themeProp : configTheme) != null ? _a : DEFAULT_GRAPH_CONFIG.theme) != null ? _b : GRAPH_THEME.DARK;
2304
+ return (_b = (_a = themeProp != null ? themeProp : configTheme) != null ? _a : DEFAULT_GRAPH_CONFIG.theme) != null ? _b : GRAPH_THEME_MODE.SYSTEM;
2305
+ }
2306
+ function resolveActiveTheme(mode, systemTheme) {
2307
+ return mode === GRAPH_THEME_MODE.SYSTEM ? systemTheme : mode;
2219
2308
  }
2220
2309
  function cloneCachedNodes(nodes) {
2221
2310
  return nodes.map((n) => __spreadProps(__spreadValues({}, n), {
@@ -2250,6 +2339,7 @@ function GraphViewer(props) {
2250
2339
  initialFoldMode,
2251
2340
  hideToolbar = false,
2252
2341
  theme: themeProp,
2342
+ systemTheme: systemThemeProp,
2253
2343
  showThemeToggle = true,
2254
2344
  onThemeChange,
2255
2345
  onNavigateToPipe,
@@ -2263,59 +2353,65 @@ function GraphViewer(props) {
2263
2353
  canEmbedPdf,
2264
2354
  onOpenExternally
2265
2355
  } = props;
2266
- const graphspec = React8.useMemo(
2356
+ const graphspec = React9.useMemo(
2267
2357
  () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
2268
2358
  [graphspecProp]
2269
2359
  );
2270
- const [direction, setDirection] = React8.useState(
2360
+ const [direction, setDirection] = React9.useState(
2271
2361
  () => {
2272
2362
  var _a2, _b2;
2273
2363
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
2274
2364
  }
2275
2365
  );
2276
- const externalTheme = resolveExternalTheme(themeProp, config.theme);
2277
- const [theme, setTheme] = React8.useState(externalTheme);
2278
- const prevExternalThemeRef = React8.useRef(externalTheme);
2279
- React8.useEffect(() => {
2280
- if (externalTheme !== prevExternalThemeRef.current) {
2281
- prevExternalThemeRef.current = externalTheme;
2282
- setTheme(externalTheme);
2366
+ const externalMode = resolveExternalThemeMode(themeProp, config.theme);
2367
+ const [mode, setMode] = React9.useState(externalMode);
2368
+ const prevExternalModeRef = React9.useRef(externalMode);
2369
+ React9.useEffect(() => {
2370
+ if (externalMode !== prevExternalModeRef.current) {
2371
+ prevExternalModeRef.current = externalMode;
2372
+ setMode(externalMode);
2283
2373
  }
2284
- }, [externalTheme]);
2285
- const onThemeChangeRef = React8.useRef(onThemeChange);
2374
+ }, [externalMode]);
2375
+ const systemTheme = useSystemTheme(systemThemeProp);
2376
+ const resolvedTheme = resolveActiveTheme(mode, systemTheme);
2377
+ const onThemeChangeRef = React9.useRef(onThemeChange);
2286
2378
  onThemeChangeRef.current = onThemeChange;
2287
- const prevReportedThemeRef = React8.useRef(theme);
2288
- React8.useEffect(() => {
2379
+ const prevReportedRef = React9.useRef({
2380
+ mode,
2381
+ resolvedTheme
2382
+ });
2383
+ React9.useEffect(() => {
2289
2384
  var _a2;
2290
- if (theme !== prevReportedThemeRef.current) {
2291
- prevReportedThemeRef.current = theme;
2292
- (_a2 = onThemeChangeRef.current) == null ? void 0 : _a2.call(onThemeChangeRef, theme);
2385
+ const prev = prevReportedRef.current;
2386
+ if (prev.mode !== mode || prev.resolvedTheme !== resolvedTheme) {
2387
+ prevReportedRef.current = { mode, resolvedTheme };
2388
+ (_a2 = onThemeChangeRef.current) == null ? void 0 : _a2.call(onThemeChangeRef, mode, resolvedTheme);
2293
2389
  }
2294
- }, [theme]);
2390
+ }, [mode, resolvedTheme]);
2295
2391
  const effectiveFoldMode = (_b = (_a = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _a : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _b : FOLD_MODE.EXPANDED;
2296
- const [showControllers, setShowControllers] = React8.useState(() => {
2392
+ const [showControllers, setShowControllers] = React9.useState(() => {
2297
2393
  var _a2, _b2;
2298
2394
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
2299
2395
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
2300
2396
  });
2301
- const foldModeRef = React8.useRef(effectiveFoldMode);
2397
+ const foldModeRef = React9.useRef(effectiveFoldMode);
2302
2398
  foldModeRef.current = effectiveFoldMode;
2303
- const containerRef = React8.useRef(null);
2304
- const [detailSelection, setDetailSelection] = React8.useState(null);
2305
- const [conceptOverride, setConceptOverride] = React8.useState(null);
2399
+ const containerRef = React9.useRef(null);
2400
+ const [detailSelection, setDetailSelection] = React9.useState(null);
2401
+ const [conceptOverride, setConceptOverride] = React9.useState(null);
2306
2402
  const {
2307
2403
  width: panelWidth,
2308
2404
  isDragging: isPanelDragging,
2309
2405
  handleMouseDown: onResizeMouseDown
2310
2406
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
2311
- React8.useEffect(() => {
2407
+ React9.useEffect(() => {
2312
2408
  setDetailSelection(null);
2313
2409
  setConceptOverride(null);
2314
2410
  }, [graphspec]);
2315
- React8.useEffect(() => {
2411
+ React9.useEffect(() => {
2316
2412
  const el = containerRef.current;
2317
2413
  if (!el) return;
2318
- const themePalette = getPaletteForTheme(theme);
2414
+ const themePalette = getPaletteForTheme(resolvedTheme);
2319
2415
  const overrides = config.paletteColors;
2320
2416
  const palette = overrides ? __spreadValues(__spreadValues({}, themePalette), overrides) : themePalette;
2321
2417
  for (const [cssVar, value] of Object.entries(palette)) {
@@ -2326,15 +2422,15 @@ function GraphViewer(props) {
2326
2422
  el.style.removeProperty(cssVar);
2327
2423
  }
2328
2424
  };
2329
- }, [config.paletteColors, theme]);
2425
+ }, [config.paletteColors, resolvedTheme]);
2330
2426
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
2331
2427
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
2332
- const reactFlowRef = React8.useRef(null);
2333
- const initialDataRef = React8.useRef(null);
2334
- const rawGraphDataRef = React8.useRef(null);
2335
- const layoutCacheRef = React8.useRef(null);
2336
- const [expandedControllers, setExpandedControllers] = React8.useState(/* @__PURE__ */ new Set());
2337
- const toggleCollapse = React8.useCallback((controllerId) => {
2428
+ const reactFlowRef = React9.useRef(null);
2429
+ const initialDataRef = React9.useRef(null);
2430
+ const rawGraphDataRef = React9.useRef(null);
2431
+ const layoutCacheRef = React9.useRef(null);
2432
+ const [expandedControllers, setExpandedControllers] = React9.useState(/* @__PURE__ */ new Set());
2433
+ const toggleCollapse = React9.useCallback((controllerId) => {
2338
2434
  setExpandedControllers((prev) => {
2339
2435
  const next = new Set(prev);
2340
2436
  if (next.has(controllerId)) next.delete(controllerId);
@@ -2342,8 +2438,8 @@ function GraphViewer(props) {
2342
2438
  return next;
2343
2439
  });
2344
2440
  }, []);
2345
- const [foldedControllers, setFoldedControllers] = React8.useState(/* @__PURE__ */ new Set());
2346
- const toggleFold = React8.useCallback((controllerId, options) => {
2441
+ const [foldedControllers, setFoldedControllers] = React9.useState(/* @__PURE__ */ new Set());
2442
+ const toggleFold = React9.useCallback((controllerId, options) => {
2347
2443
  setFoldedControllers((prev) => {
2348
2444
  const next = new Set(prev);
2349
2445
  const shouldFold = !next.has(controllerId);
@@ -2357,34 +2453,34 @@ function GraphViewer(props) {
2357
2453
  });
2358
2454
  }, []);
2359
2455
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
2360
- const layoutConfig = React8.useMemo(
2456
+ const layoutConfig = React9.useMemo(
2361
2457
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
2362
2458
  [config.nodesep, config.ranksep]
2363
2459
  );
2364
- const showControllersRef = React8.useRef(showControllers);
2460
+ const showControllersRef = React9.useRef(showControllers);
2365
2461
  showControllersRef.current = showControllers;
2366
- const directionRef = React8.useRef(direction);
2462
+ const directionRef = React9.useRef(direction);
2367
2463
  directionRef.current = direction;
2368
- const layoutConfigRef = React8.useRef(layoutConfig);
2464
+ const layoutConfigRef = React9.useRef(layoutConfig);
2369
2465
  layoutConfigRef.current = layoutConfig;
2370
- const initialZoomRef = React8.useRef(config.initialZoom);
2466
+ const initialZoomRef = React9.useRef(config.initialZoom);
2371
2467
  initialZoomRef.current = config.initialZoom;
2372
- const panToTopRef = React8.useRef(config.panToTop);
2468
+ const panToTopRef = React9.useRef(config.panToTop);
2373
2469
  panToTopRef.current = config.panToTop;
2374
- const expandedRef = React8.useRef(expandedControllers);
2470
+ const expandedRef = React9.useRef(expandedControllers);
2375
2471
  expandedRef.current = expandedControllers;
2376
- const toggleCollapseRef = React8.useRef(toggleCollapse);
2472
+ const toggleCollapseRef = React9.useRef(toggleCollapse);
2377
2473
  toggleCollapseRef.current = toggleCollapse;
2378
- const foldedRef = React8.useRef(foldedControllers);
2474
+ const foldedRef = React9.useRef(foldedControllers);
2379
2475
  foldedRef.current = foldedControllers;
2380
- const toggleFoldRef = React8.useRef(toggleFold);
2476
+ const toggleFoldRef = React9.useRef(toggleFold);
2381
2477
  toggleFoldRef.current = toggleFold;
2382
- const isFirstFoldEffect = React8.useRef(true);
2383
- const prevFoldSizeRef = React8.useRef(0);
2384
- const skipNextFoldEffectRef = React8.useRef(false);
2385
- const statusMapRef = React8.useRef(statusMap);
2478
+ const isFirstFoldEffect = React9.useRef(true);
2479
+ const prevFoldSizeRef = React9.useRef(0);
2480
+ const skipNextFoldEffectRef = React9.useRef(false);
2481
+ const statusMapRef = React9.useRef(statusMap);
2386
2482
  statusMapRef.current = statusMap;
2387
- React8.useEffect(() => {
2483
+ React9.useEffect(() => {
2388
2484
  if (!initialDataRef.current) return;
2389
2485
  let cancelled = false;
2390
2486
  (async () => {
@@ -2436,7 +2532,7 @@ function GraphViewer(props) {
2436
2532
  cancelled = true;
2437
2533
  };
2438
2534
  }, [direction, layoutConfig]);
2439
- React8.useEffect(() => {
2535
+ React9.useEffect(() => {
2440
2536
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2441
2537
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2442
2538
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2456,7 +2552,7 @@ function GraphViewer(props) {
2456
2552
  );
2457
2553
  setEdges(toAppEdges(withControllers.edges));
2458
2554
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold]);
2459
- React8.useEffect(() => {
2555
+ React9.useEffect(() => {
2460
2556
  if (!graphspec) {
2461
2557
  initialDataRef.current = null;
2462
2558
  rawGraphDataRef.current = null;
@@ -2560,7 +2656,7 @@ function GraphViewer(props) {
2560
2656
  cancelled = true;
2561
2657
  };
2562
2658
  }, [graphspec, edgeType]);
2563
- React8.useEffect(() => {
2659
+ React9.useEffect(() => {
2564
2660
  if (isFirstFoldEffect.current) {
2565
2661
  isFirstFoldEffect.current = false;
2566
2662
  prevFoldSizeRef.current = foldedControllers.size;
@@ -2640,7 +2736,7 @@ function GraphViewer(props) {
2640
2736
  cancelled = true;
2641
2737
  };
2642
2738
  }, [foldedControllers, toggleFold]);
2643
- React8.useEffect(() => {
2739
+ React9.useEffect(() => {
2644
2740
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2645
2741
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2646
2742
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2658,7 +2754,7 @@ function GraphViewer(props) {
2658
2754
  setNodes(applyStatusOverrides(toAppNodes(hydrateLabels(withControllers.nodes)), statusMap));
2659
2755
  setEdges(toAppEdges(withControllers.edges));
2660
2756
  }, [statusMap]);
2661
- const onNodeClick = React8.useCallback(
2757
+ const onNodeClick = React9.useCallback(
2662
2758
  (event, node) => {
2663
2759
  var _a2;
2664
2760
  const nodeData = node.data;
@@ -2700,7 +2796,7 @@ function GraphViewer(props) {
2700
2796
  conceptOverride
2701
2797
  ]
2702
2798
  );
2703
- const onInit = React8.useCallback(
2799
+ const onInit = React9.useCallback(
2704
2800
  (reactFlowInstance) => {
2705
2801
  reactFlowRef.current = reactFlowInstance;
2706
2802
  if (onReactFlowInit) {
@@ -2709,12 +2805,12 @@ function GraphViewer(props) {
2709
2805
  },
2710
2806
  [onReactFlowInit]
2711
2807
  );
2712
- const handlePaneClick = React8.useCallback(() => {
2808
+ const handlePaneClick = React9.useCallback(() => {
2713
2809
  setDetailSelection(null);
2714
2810
  setConceptOverride(null);
2715
2811
  onPaneClick == null ? void 0 : onPaneClick();
2716
2812
  }, [onPaneClick]);
2717
- const handleConceptClick = React8.useCallback(
2813
+ const handleConceptClick = React9.useCallback(
2718
2814
  (conceptCode) => {
2719
2815
  if (!graphspec) return;
2720
2816
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -2726,7 +2822,7 @@ function GraphViewer(props) {
2726
2822
  const detailOpen = detailSelection !== null || conceptOverride !== null;
2727
2823
  const rawAnalysis = (_c = rawGraphDataRef.current) == null ? void 0 : _c.analysis;
2728
2824
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
2729
- const foldAllProps = React8.useMemo(() => {
2825
+ const foldAllProps = React9.useMemo(() => {
2730
2826
  if (!showControllers || !allControllerIds || allControllerIds.size === 0) {
2731
2827
  return {
2732
2828
  onFoldAll: void 0,
@@ -2742,96 +2838,103 @@ function GraphViewer(props) {
2742
2838
  expandAllDisabled: foldedControllers.size === 0
2743
2839
  };
2744
2840
  }, [showControllers, allControllerIds, foldedControllers]);
2745
- return /* @__PURE__ */ jsxs20("div", { ref: containerRef, className: `react-flow-container react-flow-container--theme-${theme}`, children: [
2746
- /* @__PURE__ */ jsx20(
2747
- ReactFlow,
2748
- {
2749
- nodes,
2750
- edges,
2751
- nodeTypes,
2752
- onNodesChange,
2753
- onEdgesChange,
2754
- onNodeClick,
2755
- onPaneClick: handlePaneClick,
2756
- onInit,
2757
- fitView: true,
2758
- fitViewOptions: { padding: 0.1 },
2759
- defaultEdgeOptions: { type: edgeType },
2760
- panOnScroll: true,
2761
- minZoom: 0.1,
2762
- proOptions: { hideAttribution: true },
2763
- panActivationKeyCode: null,
2764
- children: /* @__PURE__ */ jsx20(
2765
- Background,
2841
+ return /* @__PURE__ */ jsxs20(
2842
+ "div",
2843
+ {
2844
+ ref: containerRef,
2845
+ className: `react-flow-container react-flow-container--theme-${resolvedTheme} react-flow-container--mode-${mode}`,
2846
+ children: [
2847
+ /* @__PURE__ */ jsx20(
2848
+ ReactFlow,
2849
+ {
2850
+ nodes,
2851
+ edges,
2852
+ nodeTypes,
2853
+ onNodesChange,
2854
+ onEdgesChange,
2855
+ onNodeClick,
2856
+ onPaneClick: handlePaneClick,
2857
+ onInit,
2858
+ fitView: true,
2859
+ fitViewOptions: { padding: 0.1 },
2860
+ defaultEdgeOptions: { type: edgeType },
2861
+ panOnScroll: true,
2862
+ minZoom: 0.1,
2863
+ proOptions: { hideAttribution: true },
2864
+ panActivationKeyCode: null,
2865
+ children: /* @__PURE__ */ jsx20(
2866
+ Background,
2867
+ {
2868
+ variant: BackgroundVariant.Dots,
2869
+ gap: 20,
2870
+ size: 1,
2871
+ color: "var(--color-bg-dots)"
2872
+ }
2873
+ )
2874
+ }
2875
+ ),
2876
+ /* @__PURE__ */ jsxs20(
2877
+ DetailPanel,
2878
+ {
2879
+ isOpen: detailOpen,
2880
+ onClose: handlePaneClick,
2881
+ width: panelWidth,
2882
+ isDragging: isPanelDragging,
2883
+ onResizeHandleMouseDown: onResizeMouseDown,
2884
+ children: [
2885
+ conceptOverride ? /* @__PURE__ */ jsx20(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx20(
2886
+ PipeDetailPanel,
2887
+ {
2888
+ node: selectedSpecNode,
2889
+ spec: graphspec,
2890
+ onConceptClick: handleConceptClick
2891
+ }
2892
+ ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx20(
2893
+ StuffNodeDetail,
2894
+ {
2895
+ nodeId: detailSelection.nodeId,
2896
+ stuffData: detailSelection.stuffData,
2897
+ graphspec,
2898
+ resolveStorageUrl,
2899
+ canEmbedPdf,
2900
+ onOpenExternally
2901
+ }
2902
+ ) : null,
2903
+ renderDetailExtra && detailSelection && !conceptOverride && renderDetailExtra(detailSelection.nodeId, detailSelection.nodeData)
2904
+ ]
2905
+ }
2906
+ ),
2907
+ !hideToolbar && /* @__PURE__ */ jsx20(
2908
+ GraphToolbar,
2766
2909
  {
2767
- variant: BackgroundVariant.Dots,
2768
- gap: 20,
2769
- size: 1,
2770
- color: "var(--color-bg-dots)"
2910
+ direction,
2911
+ onDirectionChange: setDirection,
2912
+ showControllers,
2913
+ onShowControllersChange: setShowControllers,
2914
+ onZoomIn: () => {
2915
+ var _a2;
2916
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomIn();
2917
+ },
2918
+ onZoomOut: () => {
2919
+ var _a2;
2920
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomOut();
2921
+ },
2922
+ onFitView: () => {
2923
+ var _a2;
2924
+ return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.fitView({ padding: 0.1 });
2925
+ },
2926
+ onFoldAll: foldAllProps.onFoldAll,
2927
+ onExpandAll: foldAllProps.onExpandAll,
2928
+ foldAllDisabled: foldAllProps.foldAllDisabled,
2929
+ expandAllDisabled: foldAllProps.expandAllDisabled,
2930
+ themeMode: showThemeToggle ? mode : void 0,
2931
+ onThemeModeChange: showThemeToggle ? setMode : void 0,
2932
+ rightOffset: detailOpen ? panelWidth : 0
2771
2933
  }
2772
2934
  )
2773
- }
2774
- ),
2775
- /* @__PURE__ */ jsxs20(
2776
- DetailPanel,
2777
- {
2778
- isOpen: detailOpen,
2779
- onClose: handlePaneClick,
2780
- width: panelWidth,
2781
- isDragging: isPanelDragging,
2782
- onResizeHandleMouseDown: onResizeMouseDown,
2783
- children: [
2784
- conceptOverride ? /* @__PURE__ */ jsx20(ConceptDetailPanel, { concept: conceptOverride }) : selectedSpecNode && graphspec ? /* @__PURE__ */ jsx20(
2785
- PipeDetailPanel,
2786
- {
2787
- node: selectedSpecNode,
2788
- spec: graphspec,
2789
- onConceptClick: handleConceptClick
2790
- }
2791
- ) : (detailSelection == null ? void 0 : detailSelection.stuffData) ? /* @__PURE__ */ jsx20(
2792
- StuffNodeDetail,
2793
- {
2794
- nodeId: detailSelection.nodeId,
2795
- stuffData: detailSelection.stuffData,
2796
- graphspec,
2797
- resolveStorageUrl,
2798
- canEmbedPdf,
2799
- onOpenExternally
2800
- }
2801
- ) : null,
2802
- renderDetailExtra && detailSelection && !conceptOverride && renderDetailExtra(detailSelection.nodeId, detailSelection.nodeData)
2803
- ]
2804
- }
2805
- ),
2806
- !hideToolbar && /* @__PURE__ */ jsx20(
2807
- GraphToolbar,
2808
- {
2809
- direction,
2810
- onDirectionChange: setDirection,
2811
- showControllers,
2812
- onShowControllersChange: setShowControllers,
2813
- onZoomIn: () => {
2814
- var _a2;
2815
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomIn();
2816
- },
2817
- onZoomOut: () => {
2818
- var _a2;
2819
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.zoomOut();
2820
- },
2821
- onFitView: () => {
2822
- var _a2;
2823
- return (_a2 = reactFlowRef.current) == null ? void 0 : _a2.fitView({ padding: 0.1 });
2824
- },
2825
- onFoldAll: foldAllProps.onFoldAll,
2826
- onExpandAll: foldAllProps.onExpandAll,
2827
- foldAllDisabled: foldAllProps.foldAllDisabled,
2828
- expandAllDisabled: foldAllProps.expandAllDisabled,
2829
- theme: showThemeToggle ? theme : void 0,
2830
- onThemeChange: showThemeToggle ? setTheme : void 0,
2831
- rightOffset: detailOpen ? panelWidth : 0
2832
- }
2833
- )
2834
- ] });
2935
+ ]
2936
+ }
2937
+ );
2835
2938
  }
2836
2939
  export {
2837
2940
  ConceptDetailPanel,
@@ -2844,6 +2947,7 @@ export {
2844
2947
  StuffViewer,
2845
2948
  applyStatusOverrides,
2846
2949
  controllerNodeTypes,
2950
+ detectSystemTheme,
2847
2951
  extractFilename,
2848
2952
  extractInlineUrl,
2849
2953
  extractStorageUri,
@@ -2857,6 +2961,7 @@ export {
2857
2961
  resolveMimeType,
2858
2962
  toAppEdges,
2859
2963
  toAppNodes,
2860
- useResizable
2964
+ useResizable,
2965
+ useSystemTheme
2861
2966
  };
2862
2967
  //# sourceMappingURL=index.js.map