@chuvenger/devdock 0.4.1 → 0.6.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.
Files changed (43) hide show
  1. package/README.md +206 -16
  2. package/dist/chunk-3MVFATRN.js +60 -0
  3. package/dist/chunk-3MVFATRN.js.map +1 -0
  4. package/dist/{chunk-WVRXVSEL.js → chunk-NXZNFF34.js} +229 -42
  5. package/dist/chunk-NXZNFF34.js.map +1 -0
  6. package/dist/{dock-DOWeFZJ5.d.ts → dock-P6l6pKjS.d.ts} +1 -1
  7. package/dist/{dock-dq6OS0Jm.d.cts → dock-d3HqT25k.d.cts} +1 -1
  8. package/dist/index.cjs +226 -39
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +1 -1
  13. package/dist/{match-BZoH6rW7.d.cts → match-DDSJIRYW.d.cts} +1 -1
  14. package/dist/{match-D1am9aTf.d.ts → match-DwGIF1Xt.d.ts} +1 -1
  15. package/dist/react-router.cjs +365 -52
  16. package/dist/react-router.cjs.map +1 -1
  17. package/dist/react-router.d.cts +58 -11
  18. package/dist/react-router.d.ts +58 -11
  19. package/dist/react-router.js +101 -8
  20. package/dist/react-router.js.map +1 -1
  21. package/dist/react.cjs +268 -47
  22. package/dist/react.cjs.map +1 -1
  23. package/dist/react.d.cts +28 -8
  24. package/dist/react.d.ts +28 -8
  25. package/dist/react.js +2 -2
  26. package/dist/svelte.cjs +226 -39
  27. package/dist/svelte.cjs.map +1 -1
  28. package/dist/svelte.d.cts +3 -3
  29. package/dist/svelte.d.ts +3 -3
  30. package/dist/svelte.js +2 -2
  31. package/dist/svelte.js.map +1 -1
  32. package/dist/sveltekit.cjs +226 -39
  33. package/dist/sveltekit.cjs.map +1 -1
  34. package/dist/sveltekit.d.cts +4 -4
  35. package/dist/sveltekit.d.ts +4 -4
  36. package/dist/sveltekit.js +2 -2
  37. package/dist/sveltekit.js.map +1 -1
  38. package/dist/{types-B-V99upW.d.cts → types-B_SpF32A.d.cts} +73 -5
  39. package/dist/{types-B-V99upW.d.ts → types-B_SpF32A.d.ts} +73 -5
  40. package/package.json +1 -1
  41. package/dist/chunk-P4ZUS4GB.js +0 -24
  42. package/dist/chunk-P4ZUS4GB.js.map +0 -1
  43. package/dist/chunk-WVRXVSEL.js.map +0 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  var react = require('react');
4
4
  var reactRouter = require('react-router');
5
+ var reactDom = require('react-dom');
5
6
  var jsxRuntime = require('react/jsx-runtime');
6
7
 
7
8
  // src/react/react-router.tsx
@@ -60,6 +61,27 @@ function el(tag, styles, props) {
60
61
  if (props) Object.assign(node, props);
61
62
  return node;
62
63
  }
64
+ function sectionHeading(text) {
65
+ return el(
66
+ "div",
67
+ {
68
+ padding: "6px 8px 2px",
69
+ fontSize: "10px",
70
+ fontWeight: "700",
71
+ letterSpacing: "0.6px",
72
+ textTransform: "uppercase",
73
+ color: "#6b7280"
74
+ },
75
+ { textContent: text }
76
+ );
77
+ }
78
+ function viewLabel(text) {
79
+ return el(
80
+ "div",
81
+ { padding: "4px 8px 0", fontSize: "11px", color: "#8b909c" },
82
+ { textContent: text }
83
+ );
84
+ }
63
85
  function cornerStyle(position) {
64
86
  const gap = "16px";
65
87
  switch (position) {
@@ -83,6 +105,15 @@ function panelAnchor(position) {
83
105
  };
84
106
  }
85
107
  var FONT = "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif";
108
+ function keyViews(views) {
109
+ const used = /* @__PURE__ */ new Set();
110
+ return views.map((view, i) => {
111
+ let key = view.id ?? `#${i}`;
112
+ while (used.has(key)) key = `${key}#${i}`;
113
+ used.add(key);
114
+ return { view, key };
115
+ });
116
+ }
86
117
  function createDevDock(initial = {}) {
87
118
  if (typeof document === "undefined") {
88
119
  return { update() {
@@ -96,9 +127,12 @@ function createDevDock(initial = {}) {
96
127
  let mounted = false;
97
128
  const host = el("div");
98
129
  host.setAttribute("data-devdock", "");
99
- const useShadow = (initial.shadow ?? true) && typeof host.attachShadow === "function";
130
+ const shadowMode = initial.shadow ?? true;
131
+ const useShadow = shadowMode !== false && typeof host.attachShadow === "function";
100
132
  const shadowRoot = useShadow ? host.attachShadow({ mode: "open" }) : null;
101
133
  const mountPoint = shadowRoot ?? host;
134
+ const styleSlot = shadowRoot && shadowMode === "inherit" ? el("div", { display: "contents" }) : null;
135
+ if (styleSlot) mountPoint.appendChild(styleSlot);
102
136
  const root = el("div", { fontFamily: FONT });
103
137
  mountPoint.appendChild(root);
104
138
  const panel = el("div", {
@@ -137,8 +171,19 @@ function createDevDock(initial = {}) {
137
171
  });
138
172
  input.placeholder = "Filter\u2026";
139
173
  inputWrap.appendChild(input);
140
- const list = el("div", { overflowY: "auto", padding: "0 6px 8px" });
141
- panel.append(heading, inputWrap, list);
174
+ const list = el("div", { padding: "0 6px 8px" });
175
+ list.setAttribute("data-devdock-list", "");
176
+ const viewsBefore = el("div");
177
+ viewsBefore.setAttribute("data-devdock-views", "before");
178
+ const viewsAfter = el("div");
179
+ viewsAfter.setAttribute("data-devdock-views", "after");
180
+ const body = el("div", {
181
+ overflowY: "auto",
182
+ display: "flex",
183
+ flexDirection: "column"
184
+ });
185
+ body.append(viewsBefore, list, viewsAfter);
186
+ panel.append(heading, inputWrap, body);
142
187
  const button = el("button", {
143
188
  display: "inline-flex",
144
189
  alignItems: "center",
@@ -179,20 +224,35 @@ function createDevDock(initial = {}) {
179
224
  const buildItems = () => {
180
225
  const out = [];
181
226
  (opts.routes ?? []).forEach((r, i) => {
227
+ const key = `route:${r.path}:${i}`;
228
+ const label = r.label ?? r.path;
182
229
  out.push({
183
230
  kind: "route",
184
- key: `route:${r.path}:${i}`,
185
- label: r.label ?? r.path,
231
+ key,
232
+ label,
186
233
  group: r.group ?? "Routes",
234
+ sig: [key, r.group ?? "Routes", label, r.path].join("\0"),
187
235
  route: r
188
236
  });
189
237
  });
190
238
  (opts.commands ?? []).forEach((c, i) => {
239
+ const live = typeof c.label === "function";
240
+ const label = typeof c.label === "function" ? c.label() : c.label;
241
+ const checked = c.checked ? c.checked() === true : null;
242
+ const key = `cmd:${c.id ?? (live ? "" : label)}:${i}`;
243
+ const group = c.group ?? "Commands";
191
244
  out.push({
192
245
  kind: "command",
193
- key: `cmd:${c.id ?? c.label}:${i}`,
194
- label: c.label,
195
- group: c.group ?? "Commands",
246
+ key,
247
+ label,
248
+ group,
249
+ sig: [
250
+ key,
251
+ group,
252
+ live ? "\0live" : label,
253
+ checked === null ? "" : "\0checkable"
254
+ ].join("\0"),
255
+ checked,
196
256
  command: c
197
257
  });
198
258
  });
@@ -220,9 +280,17 @@ function createDevDock(initial = {}) {
220
280
  };
221
281
  let rows = [];
222
282
  let rowsSig = null;
223
- const itemSig = (it) => [it.key, it.group, it.label, it.kind === "route" ? it.route.path : ""].join(
224
- "\0"
225
- );
283
+ const itemSig = (it) => it.sig;
284
+ const paintRow = (row) => {
285
+ const it = row.item;
286
+ if (row.labelSpan.textContent !== it.label)
287
+ row.labelSpan.textContent = it.label;
288
+ if (row.checkSpan && it.kind === "command") {
289
+ const on = it.checked === true;
290
+ row.checkSpan.textContent = on ? "\u2713" : "";
291
+ row.node.setAttribute("aria-checked", String(on));
292
+ }
293
+ };
226
294
  const paintActive = () => {
227
295
  rows.forEach((row, idx) => {
228
296
  const isActive = idx === active;
@@ -244,7 +312,9 @@ function createDevDock(initial = {}) {
244
312
  if (sig === rowsSig) {
245
313
  filtered.forEach((it, idx) => {
246
314
  const row = rows[idx];
247
- if (row) row.item = it;
315
+ if (!row) return;
316
+ row.item = it;
317
+ paintRow(row);
248
318
  });
249
319
  paintActive();
250
320
  return;
@@ -270,20 +340,7 @@ function createDevDock(initial = {}) {
270
340
  }
271
341
  for (const [groupName, groupItems] of groups) {
272
342
  const section = el("div", { marginTop: "6px" });
273
- section.appendChild(
274
- el(
275
- "div",
276
- {
277
- padding: "6px 8px 2px",
278
- fontSize: "10px",
279
- fontWeight: "700",
280
- letterSpacing: "0.6px",
281
- textTransform: "uppercase",
282
- color: "#6b7280"
283
- },
284
- { textContent: groupName }
285
- )
286
- );
343
+ section.appendChild(sectionHeading(groupName));
287
344
  for (const it of groupItems) {
288
345
  const idx = filtered.indexOf(it);
289
346
  const row = el("button", {
@@ -302,15 +359,11 @@ function createDevDock(initial = {}) {
302
359
  cursor: "pointer"
303
360
  });
304
361
  row.type = "button";
305
- const labelSpan = el(
306
- "span",
307
- {
308
- overflow: "hidden",
309
- textOverflow: "ellipsis",
310
- whiteSpace: "nowrap"
311
- },
312
- { textContent: it.label }
313
- );
362
+ const labelSpan = el("span", {
363
+ overflow: "hidden",
364
+ textOverflow: "ellipsis",
365
+ whiteSpace: "nowrap"
366
+ });
314
367
  row.appendChild(labelSpan);
315
368
  let pathSpan = null;
316
369
  if (it.kind === "route" && it.route.path !== it.label) {
@@ -326,8 +379,20 @@ function createDevDock(initial = {}) {
326
379
  );
327
380
  row.appendChild(pathSpan);
328
381
  }
329
- const rec = { item: it, node: row, pathSpan };
382
+ let checkSpan = null;
383
+ if (it.kind === "command" && it.checked !== null) {
384
+ row.setAttribute("role", "checkbox");
385
+ checkSpan = el("span", {
386
+ flexShrink: "0",
387
+ width: "12px",
388
+ textAlign: "center",
389
+ color: "#22c55e"
390
+ });
391
+ row.appendChild(checkSpan);
392
+ }
393
+ const rec = { item: it, node: row, labelSpan, pathSpan, checkSpan };
330
394
  rows[idx] = rec;
395
+ paintRow(rec);
331
396
  row.addEventListener("mouseenter", () => setActive(idx));
332
397
  row.addEventListener("click", () => void runItem(rec.item));
333
398
  section.appendChild(row);
@@ -336,6 +401,120 @@ function createDevDock(initial = {}) {
336
401
  }
337
402
  paintActive();
338
403
  };
404
+ const mountedViews = /* @__PURE__ */ new Map();
405
+ let viewsSig = null;
406
+ const viewOrder = (v) => v.order === "after" ? "after" : "before";
407
+ const unmountViews = () => {
408
+ for (const mv of mountedViews.values()) {
409
+ mv.teardown?.();
410
+ mv.host.remove();
411
+ }
412
+ mountedViews.clear();
413
+ viewsBefore.replaceChildren();
414
+ viewsAfter.replaceChildren();
415
+ viewsSig = null;
416
+ };
417
+ const renderViews = () => {
418
+ const keyed = keyViews(opts.views ?? []);
419
+ const sig = keyed.map(
420
+ ({ view, key }) => [key, viewOrder(view), view.group ?? "", view.label ?? ""].join(
421
+ "\0"
422
+ )
423
+ ).join("\n");
424
+ if (sig === viewsSig) return;
425
+ viewsSig = sig;
426
+ const live = new Set(keyed.map(({ key }) => key));
427
+ for (const [key, mv] of mountedViews) {
428
+ if (live.has(key)) continue;
429
+ mv.teardown?.();
430
+ mv.host.remove();
431
+ mountedViews.delete(key);
432
+ }
433
+ viewsBefore.replaceChildren();
434
+ viewsAfter.replaceChildren();
435
+ const pending = [];
436
+ for (const placement of ["before", "after"]) {
437
+ const container = placement === "before" ? viewsBefore : viewsAfter;
438
+ const here = keyed.filter(({ view }) => viewOrder(view) === placement);
439
+ const sections = /* @__PURE__ */ new Map();
440
+ for (const entry of here) {
441
+ const groupKey = entry.view.group ?? `\0${entry.key}`;
442
+ const section = sections.get(groupKey);
443
+ if (section) section.items.push(entry);
444
+ else
445
+ sections.set(groupKey, {
446
+ heading: entry.view.group ?? entry.view.label,
447
+ items: [entry]
448
+ });
449
+ }
450
+ for (const section of sections.values()) {
451
+ const wrap = el("div", { marginTop: "6px" });
452
+ if (section.heading) wrap.appendChild(sectionHeading(section.heading));
453
+ for (const { view, key } of section.items) {
454
+ if (view.group != null && view.label)
455
+ wrap.appendChild(viewLabel(view.label));
456
+ const existing = mountedViews.get(key);
457
+ if (existing) {
458
+ wrap.appendChild(existing.host);
459
+ continue;
460
+ }
461
+ const viewHost = el("div", { padding: "2px 8px 6px" });
462
+ viewHost.setAttribute("data-devdock-view", view.id ?? "");
463
+ wrap.appendChild(viewHost);
464
+ pending.push({ view, key, host: viewHost });
465
+ }
466
+ container.appendChild(wrap);
467
+ }
468
+ }
469
+ for (const { view, key, host: viewHost } of pending) {
470
+ const mv = { host: viewHost };
471
+ mountedViews.set(key, mv);
472
+ const teardown = view.mount(viewHost);
473
+ if (typeof teardown === "function") mv.teardown = teardown;
474
+ }
475
+ };
476
+ let clonesSig = null;
477
+ const syncStyles = () => {
478
+ if (!styleSlot || !shadowRoot) return;
479
+ const adopted = document.adoptedStyleSheets;
480
+ if (Array.isArray(adopted)) {
481
+ try {
482
+ const sr = shadowRoot;
483
+ sr.adoptedStyleSheets = [...adopted];
484
+ } catch {
485
+ }
486
+ }
487
+ const sheets = [
488
+ ...document.querySelectorAll('style, link[rel~="stylesheet" i]')
489
+ ];
490
+ const sig = sheets.map((n) => `${n.tagName}\0${n.getAttribute("href") ?? n.textContent}`).join("\n");
491
+ if (sig === clonesSig) return;
492
+ clonesSig = sig;
493
+ styleSlot.replaceChildren(...sheets.map((n) => n.cloneNode(true)));
494
+ };
495
+ let styleObserver = null;
496
+ let syncQueued = false;
497
+ const watchStyles = () => {
498
+ if (!styleSlot || styleObserver || !document.head || typeof MutationObserver === "undefined")
499
+ return;
500
+ styleObserver = new MutationObserver(() => {
501
+ if (syncQueued) return;
502
+ syncQueued = true;
503
+ queueMicrotask(() => {
504
+ syncQueued = false;
505
+ syncStyles();
506
+ });
507
+ });
508
+ styleObserver.observe(document.head, {
509
+ childList: true,
510
+ subtree: true,
511
+ characterData: true
512
+ });
513
+ };
514
+ const unwatchStyles = () => {
515
+ styleObserver?.disconnect();
516
+ styleObserver = null;
517
+ };
339
518
  const renderShell = () => {
340
519
  const position = opts.position ?? "bottom-right";
341
520
  const zIndex = opts.zIndex ?? 2147483e3;
@@ -365,9 +544,12 @@ function createDevDock(initial = {}) {
365
544
  panel.isConnected || root.insertBefore(panel, button);
366
545
  renderShell();
367
546
  renderList();
547
+ syncStyles();
548
+ renderViews();
368
549
  requestAnimationFrame(() => input.focus());
369
- } else if (panel.isConnected) {
370
- panel.remove();
550
+ } else {
551
+ unmountViews();
552
+ if (panel.isConnected) panel.remove();
371
553
  }
372
554
  renderShell();
373
555
  }
@@ -414,11 +596,14 @@ function createDevDock(initial = {}) {
414
596
  if (mounted) return;
415
597
  (opts.container ?? document.body).appendChild(host);
416
598
  addListeners();
599
+ syncStyles();
600
+ watchStyles();
417
601
  mounted = true;
418
602
  };
419
603
  const unmount = () => {
420
604
  if (!mounted) return;
421
605
  removeListeners();
606
+ unwatchStyles();
422
607
  host.remove();
423
608
  mounted = false;
424
609
  };
@@ -430,7 +615,10 @@ function createDevDock(initial = {}) {
430
615
  if (enabled()) {
431
616
  mount();
432
617
  renderShell();
433
- if (open) renderList();
618
+ if (open) {
619
+ renderList();
620
+ renderViews();
621
+ }
434
622
  } else {
435
623
  setOpen(false);
436
624
  unmount();
@@ -442,24 +630,56 @@ function createDevDock(initial = {}) {
442
630
  }
443
631
  };
444
632
  }
445
-
446
- // src/react/DevDock.tsx
447
- function DevDock(props) {
633
+ var NO_VIEWS = [];
634
+ function DevDock({ views = NO_VIEWS, ...options }) {
448
635
  const instance = react.useRef(null);
449
- const latest = react.useRef(props);
450
- latest.current = props;
636
+ const [hosts, setHosts] = react.useState(
637
+ () => /* @__PURE__ */ new Map()
638
+ );
639
+ const alive = react.useRef(true);
640
+ const keyed = react.useMemo(() => keyViews(views), [views]);
641
+ const coreViews = react.useMemo(
642
+ () => keyed.map(({ key, view }) => ({
643
+ id: key,
644
+ label: view.label,
645
+ group: view.group,
646
+ order: view.order,
647
+ mount: (host) => {
648
+ setHosts((prev) => new Map(prev).set(key, host));
649
+ return () => {
650
+ if (!alive.current) return;
651
+ setHosts((prev) => {
652
+ const next = new Map(prev);
653
+ next.delete(key);
654
+ return next;
655
+ });
656
+ };
657
+ }
658
+ })),
659
+ [keyed]
660
+ );
661
+ const latest = react.useRef({ options, coreViews });
662
+ latest.current = { options, coreViews };
451
663
  react.useEffect(() => {
452
- const inst = createDevDock(latest.current);
664
+ alive.current = true;
665
+ const inst = createDevDock({
666
+ ...latest.current.options,
667
+ views: latest.current.coreViews
668
+ });
453
669
  instance.current = inst;
454
670
  return () => {
671
+ alive.current = false;
455
672
  inst.destroy();
456
673
  instance.current = null;
457
674
  };
458
675
  }, []);
459
676
  react.useEffect(() => {
460
- instance.current?.update(props);
677
+ instance.current?.update({ ...options, views: coreViews });
461
678
  });
462
- return null;
679
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: keyed.map(({ key, view }) => {
680
+ const host = hosts.get(key);
681
+ return host ? reactDom.createPortal(view.render(), host, key) : null;
682
+ }) });
463
683
  }
464
684
 
465
685
  // src/core/match.ts
@@ -515,23 +735,116 @@ function walk(routes, parent, staticRoutes, inherited) {
515
735
  }
516
736
  return out;
517
737
  }
738
+ var NO_ROUTES = [];
739
+ var NO_HANDLES = /* @__PURE__ */ new Map();
740
+ function lazyHandleOf(route) {
741
+ const lazy = route.lazy;
742
+ if (typeof lazy === "function") return async () => (await lazy())?.handle;
743
+ return typeof lazy?.handle === "function" ? lazy.handle : void 0;
744
+ }
745
+ async function readLazyHandle(route) {
746
+ try {
747
+ return await lazyHandleOf(route)?.();
748
+ } catch {
749
+ return void 0;
750
+ }
751
+ }
752
+ function collectLazy(routes, out = []) {
753
+ for (const r of routes) {
754
+ if (r.handle == null && lazyHandleOf(r) != null) out.push(r);
755
+ if (r.children?.length) collectLazy(r.children, out);
756
+ }
757
+ return out;
758
+ }
759
+ function signature(routes, out = []) {
760
+ for (const r of routes) {
761
+ const h = r.handle;
762
+ out.push(
763
+ `${r.path ?? ""}|${r.index ? 1 : 0}|${h?.dev}|${h?.devLabel}|${h?.devGroup}|${h?.hidden}|${lazyHandleOf(r) ? 1 : 0}`
764
+ );
765
+ if (r.children?.length) signature(r.children, out);
766
+ }
767
+ return out;
768
+ }
769
+ function useRouteTree(router) {
770
+ const routes = router?.routes ?? NO_ROUTES;
771
+ const [sig, setSig] = react.useState(() => signature(routes).join("\n"));
772
+ react.useEffect(() => {
773
+ if (!router?.subscribe) return;
774
+ const check = () => {
775
+ const next = signature(router.routes ?? NO_ROUTES).join("\n");
776
+ setSig((prev) => prev === next ? prev : next);
777
+ };
778
+ check();
779
+ return router.subscribe(check);
780
+ }, [router]);
781
+ return [routes, sig];
782
+ }
783
+ function useLazyHandles(routes, sig, enabled) {
784
+ const [handles, setHandles] = react.useState(NO_HANDLES);
785
+ const started = react.useRef(
786
+ /* @__PURE__ */ new WeakMap()
787
+ );
788
+ react.useEffect(() => {
789
+ if (!enabled) return;
790
+ const targets = collectLazy(routes);
791
+ if (targets.length === 0) return;
792
+ let cancelled = false;
793
+ void Promise.all(
794
+ targets.map((route) => {
795
+ let pending = started.current.get(route);
796
+ if (!pending) {
797
+ pending = readLazyHandle(route);
798
+ started.current.set(route, pending);
799
+ }
800
+ return pending.then((handle) => [route, handle]);
801
+ })
802
+ ).then((resolved) => {
803
+ if (cancelled) return;
804
+ const found = resolved.filter(
805
+ (entry) => entry[1] != null
806
+ );
807
+ setHandles(
808
+ (prev) => found.some(([route, handle]) => prev.get(route) !== handle) ? new Map([...prev, ...found]) : prev
809
+ );
810
+ });
811
+ return () => {
812
+ cancelled = true;
813
+ };
814
+ }, [routes, sig, enabled]);
815
+ return handles;
816
+ }
817
+ function mergeHandles(routes, handles) {
818
+ let changed = false;
819
+ const merged = routes.map((r) => {
820
+ const handle = r.handle ?? handles.get(r);
821
+ const children = r.children ? mergeHandles(r.children, handles) : r.children;
822
+ if (handle === r.handle && children === r.children) return r;
823
+ changed = true;
824
+ return { ...r, handle, children };
825
+ });
826
+ return changed ? merged : routes;
827
+ }
518
828
  function useDetectedRoutes(options = {}) {
519
829
  const opts = isMatcher(options) ? { match: options } : options;
520
- const { match, staticRoutes } = opts;
830
+ const { match, staticRoutes, loadHandles } = opts;
521
831
  const ctx = react.useContext(reactRouter.UNSAFE_DataRouterContext);
522
- const routes = ctx?.router?.routes ?? [];
832
+ const [routes, sig] = useRouteTree(ctx?.router);
833
+ const handles = useLazyHandles(routes, sig, loadHandles === true);
523
834
  return react.useMemo(() => {
524
- const deduped = dedupeByPath(flattenRoutes(routes, "", { staticRoutes }));
835
+ const tree = handles.size > 0 ? mergeHandles(routes, handles) : routes;
836
+ const deduped = dedupeByPath(flattenRoutes(tree, "", { staticRoutes }));
525
837
  return match ? deduped.filter(toPredicate(match)) : deduped;
526
- }, [routes, match, staticRoutes]);
838
+ }, [routes, sig, handles, match, staticRoutes]);
527
839
  }
528
840
  function ReactRouterDevDock({
529
841
  match,
530
842
  staticRoutes,
843
+ loadHandles,
531
844
  ...props
532
845
  }) {
533
846
  const navigate = reactRouter.useNavigate();
534
- const detected = useDetectedRoutes({ match, staticRoutes });
847
+ const detected = useDetectedRoutes({ match, staticRoutes, loadHandles });
535
848
  const routes = react.useMemo(
536
849
  () => [...detected, ...props.routes ?? []],
537
850
  [detected, props.routes]