@pipelex/mthds-ui 0.7.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-ILX53OYM.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";
@@ -1133,6 +1179,7 @@ var PIPE_TYPE_BADGES = {
1133
1179
  PipeImgGen: "ImgGen",
1134
1180
  PipeSearch: "Search",
1135
1181
  PipeFunc: "Func",
1182
+ PipeSignature: "Signature",
1136
1183
  PipeSequence: "Seq",
1137
1184
  PipeParallel: "Par",
1138
1185
  PipeCondition: "Cond",
@@ -1159,7 +1206,7 @@ function PipeDetailPanel({ node, spec, onConceptClick }) {
1159
1206
  const badge = PIPE_TYPE_BADGES[pipeType];
1160
1207
  const status = node.status;
1161
1208
  const statusColor = (_a = STATUS_COLORS[status]) != null ? _a : "#6272a4";
1162
- const blueprint = React5.useMemo(() => {
1209
+ const blueprint = React6.useMemo(() => {
1163
1210
  var _a2, _b2;
1164
1211
  if (!node.pipe_code || !spec.pipe_registry) return void 0;
1165
1212
  const directKey = `${(_b2 = (_a2 = spec.pipeline_ref) == null ? void 0 : _a2.domain) != null ? _b2 : ""}.${node.pipe_code}`;
@@ -1287,6 +1334,10 @@ function BlueprintSection({
1287
1334
  return /* @__PURE__ */ jsx13(PipeBatchSection, { blueprint, executionData });
1288
1335
  case "PipeFunc":
1289
1336
  return null;
1337
+ case "PipeSignature":
1338
+ return /* @__PURE__ */ jsx13("div", { className: "detail-not-available", children: "Signature stub \u2014 declared but not yet implemented." });
1339
+ default:
1340
+ return null;
1290
1341
  }
1291
1342
  }
1292
1343
  function ExecutionDataSection({
@@ -1326,7 +1377,7 @@ function GenericExecutionData({ data }) {
1326
1377
  }
1327
1378
 
1328
1379
  // src/graph/react/detail/ConceptDetailPanel.tsx
1329
- import React6 from "react";
1380
+ import React7 from "react";
1330
1381
  import { Fragment as Fragment10, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
1331
1382
  function ConceptDetailPanel({
1332
1383
  concept,
@@ -1371,8 +1422,8 @@ function ConceptBody({
1371
1422
  onOpenExternally
1372
1423
  }) {
1373
1424
  const hasData = Boolean(ioData) && !isDryRun;
1374
- const [activeTab, setActiveTab] = React6.useState(hasData ? "data" : "structure");
1375
- const baseId = React6.useId();
1425
+ const [activeTab, setActiveTab] = React7.useState(hasData ? "data" : "structure");
1426
+ const baseId = React7.useId();
1376
1427
  const tabId = (tab) => `${baseId}-tab-${tab}`;
1377
1428
  const panelId = (tab) => `${baseId}-tabpanel-${tab}`;
1378
1429
  const structure = concept.json_schema ? /* @__PURE__ */ jsxs14("div", { children: [
@@ -1808,6 +1859,47 @@ var MOON_ICON = /* @__PURE__ */ jsx16(
1808
1859
  children: /* @__PURE__ */ jsx16("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" })
1809
1860
  }
1810
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
+ }
1811
1903
  function GraphToolbar({
1812
1904
  direction,
1813
1905
  onDirectionChange,
@@ -1820,12 +1912,11 @@ function GraphToolbar({
1820
1912
  onExpandAll,
1821
1913
  foldAllDisabled = false,
1822
1914
  expandAllDisabled = false,
1823
- theme,
1824
- onThemeChange,
1915
+ themeMode,
1916
+ onThemeModeChange,
1825
1917
  rightOffset = 0
1826
1918
  }) {
1827
- const themeToggleEnabled = theme !== void 0 && onThemeChange !== void 0;
1828
- const themeLabel = theme === GRAPH_THEME.LIGHT ? "Switch to dark theme" : "Switch to light theme";
1919
+ const themeToggleEnabled = themeMode !== void 0 && onThemeModeChange !== void 0;
1829
1920
  const isVertical = direction === GRAPH_DIRECTION.TB || direction === GRAPH_DIRECTION.BT;
1830
1921
  const directionLabel = isVertical ? "Switch to horizontal layout" : "Switch to vertical layout";
1831
1922
  const controllersLabel = showControllers ? "Hide pipe controllers" : "Show pipe controllers \u2014 groups pipes by their controlling pipe";
@@ -1921,10 +2012,10 @@ function GraphToolbar({
1921
2012
  {
1922
2013
  type: "button",
1923
2014
  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
2015
+ onClick: () => onThemeModeChange(nextThemeMode(themeMode)),
2016
+ title: themeModeLabel(themeMode),
2017
+ "aria-label": themeModeLabel(themeMode),
2018
+ children: themeModeIcon(themeMode)
1928
2019
  }
1929
2020
  )
1930
2021
  ] })
@@ -2004,6 +2095,7 @@ var PIPE_TYPE_BADGES2 = {
2004
2095
  PipeImgGen: "ImgGen",
2005
2096
  PipeSearch: "Search",
2006
2097
  PipeFunc: "Func",
2098
+ PipeSignature: "Signature",
2007
2099
  PipeSequence: "Sequence",
2008
2100
  PipeParallel: "Parallel",
2009
2101
  PipeCondition: "Condition",
@@ -2035,14 +2127,16 @@ function PipeCardBase({ data, children }) {
2035
2127
  const statusConfig = STATUS_CONFIG[data.status];
2036
2128
  const isRunning = data.status === "running";
2037
2129
  const isController = isControllerType(data.pipeType);
2130
+ const isSignature = data.pipeType === "PipeSignature";
2038
2131
  const [inputsExpanded, setInputsExpanded] = useState2(false);
2039
2132
  const hasMany = data.inputs.length > MAX_VISIBLE_INPUTS;
2040
2133
  const visibleInputs = hasMany && !inputsExpanded ? data.inputs.slice(0, MAX_VISIBLE_INPUTS) : data.inputs;
2041
2134
  const hiddenCount = data.inputs.length - MAX_VISIBLE_INPUTS;
2042
2135
  const dirClass = data.direction === "TB" ? "pipe-card--tb" : "pipe-card--lr";
2043
2136
  const controllerClass = isController ? " pipe-card--controller" : "";
2044
- const badgeClass = isController ? "pipe-card-badge pipe-card-badge--controller" : "pipe-card-badge";
2045
- return /* @__PURE__ */ jsxs18("div", { className: `pipe-card ${dirClass}${controllerClass}`, children: [
2137
+ const signatureClass = isSignature ? " pipe-card--signature" : "";
2138
+ const badgeClass = isController ? "pipe-card-badge pipe-card-badge--controller" : isSignature ? "pipe-card-badge pipe-card-badge--signature" : "pipe-card-badge";
2139
+ return /* @__PURE__ */ jsxs18("div", { className: `pipe-card ${dirClass}${controllerClass}${signatureClass}`, children: [
2046
2140
  /* @__PURE__ */ jsxs18("div", { className: "pipe-card-header", children: [
2047
2141
  /* @__PURE__ */ jsx18("span", { className: badgeClass, children: badge }),
2048
2142
  /* @__PURE__ */ jsx18("span", { className: "pipe-card-code", title: data.pipeCode, children: data.pipeCode }),
@@ -2133,6 +2227,7 @@ var PIPE_CARD_REGISTRY = {
2133
2227
  PipeImgGen: PipeCardBase,
2134
2228
  PipeSearch: PipeCardBase,
2135
2229
  PipeFunc: PipeCardBase,
2230
+ PipeSignature: PipeCardBase,
2136
2231
  PipeSequence: PipeCardBase,
2137
2232
  PipeParallel: PipeCardBase,
2138
2233
  PipeCondition: PipeCardBase,
@@ -2204,9 +2299,12 @@ function seedFoldedControllers(mode, controllerIds) {
2204
2299
  if (mode === FOLD_MODE.FOLDED) return new Set(controllerIds);
2205
2300
  return /* @__PURE__ */ new Set();
2206
2301
  }
2207
- function resolveExternalTheme(themeProp, configTheme) {
2302
+ function resolveExternalThemeMode(themeProp, configTheme) {
2208
2303
  var _a, _b;
2209
- 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;
2210
2308
  }
2211
2309
  function cloneCachedNodes(nodes) {
2212
2310
  return nodes.map((n) => __spreadProps(__spreadValues({}, n), {
@@ -2241,6 +2339,7 @@ function GraphViewer(props) {
2241
2339
  initialFoldMode,
2242
2340
  hideToolbar = false,
2243
2341
  theme: themeProp,
2342
+ systemTheme: systemThemeProp,
2244
2343
  showThemeToggle = true,
2245
2344
  onThemeChange,
2246
2345
  onNavigateToPipe,
@@ -2254,59 +2353,65 @@ function GraphViewer(props) {
2254
2353
  canEmbedPdf,
2255
2354
  onOpenExternally
2256
2355
  } = props;
2257
- const graphspec = React8.useMemo(
2356
+ const graphspec = React9.useMemo(
2258
2357
  () => graphspecProp === null ? null : validateGraphSpec(graphspecProp),
2259
2358
  [graphspecProp]
2260
2359
  );
2261
- const [direction, setDirection] = React8.useState(
2360
+ const [direction, setDirection] = React9.useState(
2262
2361
  () => {
2263
2362
  var _a2, _b2;
2264
2363
  return (_b2 = (_a2 = initialDirection != null ? initialDirection : config.direction) != null ? _a2 : DEFAULT_GRAPH_CONFIG.direction) != null ? _b2 : GRAPH_DIRECTION.TB;
2265
2364
  }
2266
2365
  );
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);
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);
2274
2373
  }
2275
- }, [externalTheme]);
2276
- const onThemeChangeRef = React8.useRef(onThemeChange);
2374
+ }, [externalMode]);
2375
+ const systemTheme = useSystemTheme(systemThemeProp);
2376
+ const resolvedTheme = resolveActiveTheme(mode, systemTheme);
2377
+ const onThemeChangeRef = React9.useRef(onThemeChange);
2277
2378
  onThemeChangeRef.current = onThemeChange;
2278
- const prevReportedThemeRef = React8.useRef(theme);
2279
- React8.useEffect(() => {
2379
+ const prevReportedRef = React9.useRef({
2380
+ mode,
2381
+ resolvedTheme
2382
+ });
2383
+ React9.useEffect(() => {
2280
2384
  var _a2;
2281
- if (theme !== prevReportedThemeRef.current) {
2282
- prevReportedThemeRef.current = theme;
2283
- (_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);
2284
2389
  }
2285
- }, [theme]);
2390
+ }, [mode, resolvedTheme]);
2286
2391
  const effectiveFoldMode = (_b = (_a = initialFoldMode != null ? initialFoldMode : config.foldMode) != null ? _a : DEFAULT_GRAPH_CONFIG.foldMode) != null ? _b : FOLD_MODE.EXPANDED;
2287
- const [showControllers, setShowControllers] = React8.useState(() => {
2392
+ const [showControllers, setShowControllers] = React9.useState(() => {
2288
2393
  var _a2, _b2;
2289
2394
  if (effectiveFoldMode === FOLD_MODE.FOLDED) return true;
2290
2395
  return (_b2 = (_a2 = initialShowControllers != null ? initialShowControllers : config.showControllers) != null ? _a2 : DEFAULT_GRAPH_CONFIG.showControllers) != null ? _b2 : false;
2291
2396
  });
2292
- const foldModeRef = React8.useRef(effectiveFoldMode);
2397
+ const foldModeRef = React9.useRef(effectiveFoldMode);
2293
2398
  foldModeRef.current = effectiveFoldMode;
2294
- const containerRef = React8.useRef(null);
2295
- const [detailSelection, setDetailSelection] = React8.useState(null);
2296
- 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);
2297
2402
  const {
2298
2403
  width: panelWidth,
2299
2404
  isDragging: isPanelDragging,
2300
2405
  handleMouseDown: onResizeMouseDown
2301
2406
  } = useResizable({ defaultWidth: 380, minWidth: 280, maxWidth: 800, containerRef });
2302
- React8.useEffect(() => {
2407
+ React9.useEffect(() => {
2303
2408
  setDetailSelection(null);
2304
2409
  setConceptOverride(null);
2305
2410
  }, [graphspec]);
2306
- React8.useEffect(() => {
2411
+ React9.useEffect(() => {
2307
2412
  const el = containerRef.current;
2308
2413
  if (!el) return;
2309
- const themePalette = getPaletteForTheme(theme);
2414
+ const themePalette = getPaletteForTheme(resolvedTheme);
2310
2415
  const overrides = config.paletteColors;
2311
2416
  const palette = overrides ? __spreadValues(__spreadValues({}, themePalette), overrides) : themePalette;
2312
2417
  for (const [cssVar, value] of Object.entries(palette)) {
@@ -2317,15 +2422,15 @@ function GraphViewer(props) {
2317
2422
  el.style.removeProperty(cssVar);
2318
2423
  }
2319
2424
  };
2320
- }, [config.paletteColors, theme]);
2425
+ }, [config.paletteColors, resolvedTheme]);
2321
2426
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
2322
2427
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
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) => {
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) => {
2329
2434
  setExpandedControllers((prev) => {
2330
2435
  const next = new Set(prev);
2331
2436
  if (next.has(controllerId)) next.delete(controllerId);
@@ -2333,8 +2438,8 @@ function GraphViewer(props) {
2333
2438
  return next;
2334
2439
  });
2335
2440
  }, []);
2336
- const [foldedControllers, setFoldedControllers] = React8.useState(/* @__PURE__ */ new Set());
2337
- const toggleFold = React8.useCallback((controllerId, options) => {
2441
+ const [foldedControllers, setFoldedControllers] = React9.useState(/* @__PURE__ */ new Set());
2442
+ const toggleFold = React9.useCallback((controllerId, options) => {
2338
2443
  setFoldedControllers((prev) => {
2339
2444
  const next = new Set(prev);
2340
2445
  const shouldFold = !next.has(controllerId);
@@ -2348,34 +2453,34 @@ function GraphViewer(props) {
2348
2453
  });
2349
2454
  }, []);
2350
2455
  const edgeType = config.edgeType || EDGE_TYPE.DEFAULT;
2351
- const layoutConfig = React8.useMemo(
2456
+ const layoutConfig = React9.useMemo(
2352
2457
  () => ({ nodesep: config.nodesep, ranksep: config.ranksep }),
2353
2458
  [config.nodesep, config.ranksep]
2354
2459
  );
2355
- const showControllersRef = React8.useRef(showControllers);
2460
+ const showControllersRef = React9.useRef(showControllers);
2356
2461
  showControllersRef.current = showControllers;
2357
- const directionRef = React8.useRef(direction);
2462
+ const directionRef = React9.useRef(direction);
2358
2463
  directionRef.current = direction;
2359
- const layoutConfigRef = React8.useRef(layoutConfig);
2464
+ const layoutConfigRef = React9.useRef(layoutConfig);
2360
2465
  layoutConfigRef.current = layoutConfig;
2361
- const initialZoomRef = React8.useRef(config.initialZoom);
2466
+ const initialZoomRef = React9.useRef(config.initialZoom);
2362
2467
  initialZoomRef.current = config.initialZoom;
2363
- const panToTopRef = React8.useRef(config.panToTop);
2468
+ const panToTopRef = React9.useRef(config.panToTop);
2364
2469
  panToTopRef.current = config.panToTop;
2365
- const expandedRef = React8.useRef(expandedControllers);
2470
+ const expandedRef = React9.useRef(expandedControllers);
2366
2471
  expandedRef.current = expandedControllers;
2367
- const toggleCollapseRef = React8.useRef(toggleCollapse);
2472
+ const toggleCollapseRef = React9.useRef(toggleCollapse);
2368
2473
  toggleCollapseRef.current = toggleCollapse;
2369
- const foldedRef = React8.useRef(foldedControllers);
2474
+ const foldedRef = React9.useRef(foldedControllers);
2370
2475
  foldedRef.current = foldedControllers;
2371
- const toggleFoldRef = React8.useRef(toggleFold);
2476
+ const toggleFoldRef = React9.useRef(toggleFold);
2372
2477
  toggleFoldRef.current = toggleFold;
2373
- const isFirstFoldEffect = React8.useRef(true);
2374
- const prevFoldSizeRef = React8.useRef(0);
2375
- const skipNextFoldEffectRef = React8.useRef(false);
2376
- 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);
2377
2482
  statusMapRef.current = statusMap;
2378
- React8.useEffect(() => {
2483
+ React9.useEffect(() => {
2379
2484
  if (!initialDataRef.current) return;
2380
2485
  let cancelled = false;
2381
2486
  (async () => {
@@ -2427,7 +2532,7 @@ function GraphViewer(props) {
2427
2532
  cancelled = true;
2428
2533
  };
2429
2534
  }, [direction, layoutConfig]);
2430
- React8.useEffect(() => {
2535
+ React9.useEffect(() => {
2431
2536
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2432
2537
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2433
2538
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2447,7 +2552,7 @@ function GraphViewer(props) {
2447
2552
  );
2448
2553
  setEdges(toAppEdges(withControllers.edges));
2449
2554
  }, [showControllers, expandedControllers, toggleCollapse, toggleFold]);
2450
- React8.useEffect(() => {
2555
+ React9.useEffect(() => {
2451
2556
  if (!graphspec) {
2452
2557
  initialDataRef.current = null;
2453
2558
  rawGraphDataRef.current = null;
@@ -2551,7 +2656,7 @@ function GraphViewer(props) {
2551
2656
  cancelled = true;
2552
2657
  };
2553
2658
  }, [graphspec, edgeType]);
2554
- React8.useEffect(() => {
2659
+ React9.useEffect(() => {
2555
2660
  if (isFirstFoldEffect.current) {
2556
2661
  isFirstFoldEffect.current = false;
2557
2662
  prevFoldSizeRef.current = foldedControllers.size;
@@ -2631,7 +2736,7 @@ function GraphViewer(props) {
2631
2736
  cancelled = true;
2632
2737
  };
2633
2738
  }, [foldedControllers, toggleFold]);
2634
- React8.useEffect(() => {
2739
+ React9.useEffect(() => {
2635
2740
  if (!layoutCacheRef.current || !initialDataRef.current) return;
2636
2741
  const cachedNodes = cloneCachedNodes(layoutCacheRef.current.nodes);
2637
2742
  const cachedEdges = layoutCacheRef.current.edges;
@@ -2649,7 +2754,7 @@ function GraphViewer(props) {
2649
2754
  setNodes(applyStatusOverrides(toAppNodes(hydrateLabels(withControllers.nodes)), statusMap));
2650
2755
  setEdges(toAppEdges(withControllers.edges));
2651
2756
  }, [statusMap]);
2652
- const onNodeClick = React8.useCallback(
2757
+ const onNodeClick = React9.useCallback(
2653
2758
  (event, node) => {
2654
2759
  var _a2;
2655
2760
  const nodeData = node.data;
@@ -2691,7 +2796,7 @@ function GraphViewer(props) {
2691
2796
  conceptOverride
2692
2797
  ]
2693
2798
  );
2694
- const onInit = React8.useCallback(
2799
+ const onInit = React9.useCallback(
2695
2800
  (reactFlowInstance) => {
2696
2801
  reactFlowRef.current = reactFlowInstance;
2697
2802
  if (onReactFlowInit) {
@@ -2700,12 +2805,12 @@ function GraphViewer(props) {
2700
2805
  },
2701
2806
  [onReactFlowInit]
2702
2807
  );
2703
- const handlePaneClick = React8.useCallback(() => {
2808
+ const handlePaneClick = React9.useCallback(() => {
2704
2809
  setDetailSelection(null);
2705
2810
  setConceptOverride(null);
2706
2811
  onPaneClick == null ? void 0 : onPaneClick();
2707
2812
  }, [onPaneClick]);
2708
- const handleConceptClick = React8.useCallback(
2813
+ const handleConceptClick = React9.useCallback(
2709
2814
  (conceptCode) => {
2710
2815
  if (!graphspec) return;
2711
2816
  const info = resolveConceptRef(graphspec, conceptCode);
@@ -2717,7 +2822,7 @@ function GraphViewer(props) {
2717
2822
  const detailOpen = detailSelection !== null || conceptOverride !== null;
2718
2823
  const rawAnalysis = (_c = rawGraphDataRef.current) == null ? void 0 : _c.analysis;
2719
2824
  const allControllerIds = rawAnalysis == null ? void 0 : rawAnalysis.controllerNodeIds;
2720
- const foldAllProps = React8.useMemo(() => {
2825
+ const foldAllProps = React9.useMemo(() => {
2721
2826
  if (!showControllers || !allControllerIds || allControllerIds.size === 0) {
2722
2827
  return {
2723
2828
  onFoldAll: void 0,
@@ -2737,7 +2842,7 @@ function GraphViewer(props) {
2737
2842
  "div",
2738
2843
  {
2739
2844
  ref: containerRef,
2740
- className: `react-flow-container react-flow-container--theme-${theme}`,
2845
+ className: `react-flow-container react-flow-container--theme-${resolvedTheme} react-flow-container--mode-${mode}`,
2741
2846
  children: [
2742
2847
  /* @__PURE__ */ jsx20(
2743
2848
  ReactFlow,
@@ -2822,8 +2927,8 @@ function GraphViewer(props) {
2822
2927
  onExpandAll: foldAllProps.onExpandAll,
2823
2928
  foldAllDisabled: foldAllProps.foldAllDisabled,
2824
2929
  expandAllDisabled: foldAllProps.expandAllDisabled,
2825
- theme: showThemeToggle ? theme : void 0,
2826
- onThemeChange: showThemeToggle ? setTheme : void 0,
2930
+ themeMode: showThemeToggle ? mode : void 0,
2931
+ onThemeModeChange: showThemeToggle ? setMode : void 0,
2827
2932
  rightOffset: detailOpen ? panelWidth : 0
2828
2933
  }
2829
2934
  )
@@ -2842,6 +2947,7 @@ export {
2842
2947
  StuffViewer,
2843
2948
  applyStatusOverrides,
2844
2949
  controllerNodeTypes,
2950
+ detectSystemTheme,
2845
2951
  extractFilename,
2846
2952
  extractInlineUrl,
2847
2953
  extractStorageUri,
@@ -2855,6 +2961,7 @@ export {
2855
2961
  resolveMimeType,
2856
2962
  toAppEdges,
2857
2963
  toAppNodes,
2858
- useResizable
2964
+ useResizable,
2965
+ useSystemTheme
2859
2966
  };
2860
2967
  //# sourceMappingURL=index.js.map