boltdocs 2.8.3 → 2.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.
Files changed (87) hide show
  1. package/bin/boltdocs.js +7 -6
  2. package/dist/{banner-MynZD_Ox.d.cts → banner-C_xkGxzI.d.cts} +1 -1
  3. package/dist/{banner-3N4Jd_L9.d.ts → banner-D2NddBfF.d.ts} +1 -1
  4. package/dist/cache-C2PiGyg8.cjs +6 -0
  5. package/dist/cache-DFW4R3S0.mjs +6 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +693 -488
  8. package/dist/client/index.d.cts +74 -8
  9. package/dist/client/index.d.ts +74 -8
  10. package/dist/client/index.js +692 -489
  11. package/dist/client/mdx.cjs +1 -1
  12. package/dist/client/mdx.js +1 -1
  13. package/dist/client/primitives.cjs +4 -4
  14. package/dist/client/primitives.d.cts +1 -1
  15. package/dist/client/primitives.d.ts +1 -1
  16. package/dist/client/primitives.js +4 -4
  17. package/dist/client/theme/neutral.css +5 -3
  18. package/dist/client/theme/reset.css +19 -0
  19. package/dist/{docs-layout-CwCq42Zt.cjs → docs-layout-3TpAt6MB.cjs} +302 -248
  20. package/dist/{docs-layout-Dn6S5g59.js → docs-layout-6FQ8__ok.js} +303 -249
  21. package/dist/doctor-BY1tSmQd.mjs +34 -0
  22. package/dist/{doctor-DyNUVe96.cjs → doctor-Bknon8f5.cjs} +1 -1
  23. package/dist/doctor-DcFLi4t1.cjs +34 -0
  24. package/dist/{doctor-aN_leTbh.mjs → doctor-wsPTzt8Y.mjs} +1 -1
  25. package/dist/{generator-CC2yHzhZ.cjs → generator-BEIF6MOx.cjs} +2 -2
  26. package/dist/{generator-BHCrLU6h.mjs → generator-DILs0EFN.mjs} +1 -1
  27. package/dist/handler-B31cxNw0.mjs +6 -0
  28. package/dist/handler-DfhH9D1b.mjs +28 -0
  29. package/dist/handler-Djy0HfsI.cjs +28 -0
  30. package/dist/{icons-dev-DvJ-hh9x.cjs → icons-dev-BryEFkJu.cjs} +96 -7
  31. package/dist/{icons-dev-Oju24Wjp.js → icons-dev-kJyGWNeg.js} +79 -8
  32. package/dist/{image-Ch4-GxdO.cjs → image-B9YA_oQ_.cjs} +24 -10
  33. package/dist/{image-Do8V9PCW.js → image-CjYu9Eml.js} +24 -10
  34. package/dist/{mdx-5oeCOFhH.js → mdx-Cy_c2zio.js} +116 -33
  35. package/dist/{mdx-BGM7LjW5.cjs → mdx-WXjAMS8Y.cjs} +121 -32
  36. package/dist/{meta-loader-Cv9O0Pzl.cjs → meta-loader-D0V6oOQt.cjs} +1 -1
  37. package/dist/node/cli-entry.cjs +3 -3
  38. package/dist/node/cli-entry.mjs +3 -3
  39. package/dist/node/index.cjs +1 -1
  40. package/dist/node/index.d.cts +368 -24
  41. package/dist/node/index.d.mts +369 -24
  42. package/dist/node/index.mjs +1 -1
  43. package/dist/node/routes/worker.cjs +1 -1
  44. package/dist/node/routes/worker.mjs +1 -1
  45. package/dist/node-DV-EPtMU.mjs +160 -0
  46. package/dist/node-vOVNRABe.cjs +160 -0
  47. package/dist/{package-CHxJV4Jy.mjs → package-BK_82QLX.mjs} +1 -1
  48. package/dist/{package-B_SoaGeu.cjs → package-OxKE0p6j.cjs} +1 -1
  49. package/dist/{parser-B7-6PyQz.cjs → parser-C2Pw18LY.cjs} +1 -1
  50. package/dist/{parser-BzB-zCkF.cjs → parser-DJoEF96Y.cjs} +1 -1
  51. package/dist/{parser-D0PVghVt.mjs → parser-DScG_PTs.mjs} +1 -1
  52. package/dist/routes--LYHSHT7.cjs +6 -0
  53. package/dist/routes-BAsfsn_e.mjs +6 -0
  54. package/dist/routes-C6Kr2HFN.cjs +6 -0
  55. package/dist/routes-CKgGAy8P.mjs +6 -0
  56. package/dist/{search-dialog-BXVoecTx.cjs → search-dialog-BhXg05Ri.cjs} +1 -1
  57. package/dist/{search-dialog-BYhOov4S.cjs → search-dialog-BsAiQgnx.cjs} +2 -2
  58. package/dist/{search-dialog-C09riYmx.js → search-dialog-CaOO1Q4i.js} +2 -2
  59. package/dist/{search-dialog-CUeAfy-8.cjs → search-dialog-CzwvKoLW.cjs} +1 -1
  60. package/dist/{search-dialog-DHc_8FFX.js → search-dialog-DRNJsxOv.js} +1 -1
  61. package/dist/{search-dialog-D8gLkhUV.js → search-dialog-DaR66wl0.js} +1 -1
  62. package/dist/shiki-adapter-CUtj-xKv.cjs +6 -0
  63. package/dist/shiki-adapter-EZptJAmW.mjs +6 -0
  64. package/dist/shiki-adapter-F-P-lDon.mjs +6 -0
  65. package/dist/shiki-adapter-PQBl32AN.cjs +6 -0
  66. package/dist/{sidebar-Dlkgbxs6.d.cts → sidebar-BtJyWHy4.d.cts} +43 -4
  67. package/dist/{sidebar-DNq4_ZAa.d.ts → sidebar-C4fuRYYS.d.ts} +43 -4
  68. package/dist/{utils-BYITg7T5.mjs → utils-DC4Wq-S2.mjs} +1 -1
  69. package/dist/{utils-Cjmx1hhk.cjs → utils-wHud9ny5.cjs} +1 -1
  70. package/dist/worker-pool-BS1LX67P.cjs +6 -0
  71. package/dist/worker-pool-DRv8ef4z.mjs +6 -0
  72. package/package.json +7 -3
  73. package/src/shared/types.ts +36 -3
  74. package/dist/cache-BMUyNiiA.mjs +0 -6
  75. package/dist/cache-CKm45d2w.cjs +0 -6
  76. package/dist/chunk-CU-zTemE.cjs +0 -6
  77. package/dist/doctor-BArviV8X.cjs +0 -28
  78. package/dist/doctor-CgLA7_Uv.mjs +0 -28
  79. package/dist/node-MsTvOgJN.mjs +0 -159
  80. package/dist/node-h9HmhAFB.cjs +0 -159
  81. package/dist/routes-BW6Dif_K.mjs +0 -6
  82. package/dist/routes-DJNJ-rTt.cjs +0 -6
  83. package/dist/routes-DiYC4nD2.cjs +0 -6
  84. package/dist/routes-ERSGuFQK.mjs +0 -6
  85. package/dist/worker-pool-CtqklOXq.cjs +0 -6
  86. package/dist/worker-pool-k0DY6k8T.mjs +0 -6
  87. /package/dist/{meta-loader-DzwDFtdT.mjs → meta-loader-By22EnYa.mjs} +0 -0
@@ -3,7 +3,7 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
- import { A as ChevronRight, D as Check, Q as icons_exports, S as normalizePath, _ as Link$1, b as useLocalizedTo, et as useConfig, k as ChevronLeft, y as cn } from "./icons-dev-Oju24Wjp.js";
6
+ import { A as ChevronRight, D as Check, S as normalizePath, _ as Link$1, b as useLocalizedTo, k as ChevronLeft, rt as useConfig, tt as icons_exports, y as cn } from "./icons-dev-kJyGWNeg.js";
7
7
  import { Children, createContext, use, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
8
8
  import { useLocation } from "react-router-dom";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -242,6 +242,235 @@ Tabs.Item = TabsItem;
242
242
  Tabs.Content = TabsContent;
243
243
  Tabs.Indicator = TabsIndicator;
244
244
 
245
+ //#endregion
246
+ //#region src/client/hooks/use-sidebar.ts
247
+ const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
248
+ const getCleanDirectoryMeta = (directoryMeta) => {
249
+ const meta = {};
250
+ if (!directoryMeta) return meta;
251
+ for (const [key, value] of Object.entries(directoryMeta)) {
252
+ const cleanKey = key.split("/").filter((part) => !part.startsWith("(") || !part.endsWith(")")).map((part) => part.replace(/^\d+-/, "")).join("/");
253
+ meta[cleanKey === "" ? "." : cleanKey] = value;
254
+ }
255
+ return meta;
256
+ };
257
+ const getOrCreateNode = (parts, rootMap, directoryMeta) => {
258
+ let currentMap = rootMap;
259
+ let parentPath = "";
260
+ let lastNode;
261
+ for (const segment of parts) {
262
+ const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment;
263
+ if (!currentMap.has(segment)) {
264
+ const meta = directoryMeta[currentRelPath] || {};
265
+ const newNode = {
266
+ path: "#",
267
+ title: meta.title || capitalize(segment),
268
+ componentPath: "",
269
+ filePath: "",
270
+ icon: meta.icon,
271
+ groupPosition: typeof meta.order === "number" ? meta.order : 999,
272
+ collapsible: meta.collapsible,
273
+ collapsed: meta.collapsed,
274
+ hasCustomTitle: meta.title !== void 0,
275
+ subRoutes: [],
276
+ childrenMap: /* @__PURE__ */ new Map()
277
+ };
278
+ currentMap.set(segment, newNode);
279
+ }
280
+ lastNode = currentMap.get(segment);
281
+ currentMap = lastNode.childrenMap;
282
+ parentPath = currentRelPath;
283
+ }
284
+ return lastNode;
285
+ };
286
+ const getRoutePosition = (r) => r.sidebarPosition ?? r.order ?? 999;
287
+ const getNodePosition = (n) => n.sidebarPosition ?? n.groupPosition ?? 999;
288
+ const finalizeTree = (nodes) => {
289
+ return nodes.map((node) => {
290
+ if (node.childrenMap && node.childrenMap.size > 0) {
291
+ const childDirs = Array.from(node.childrenMap.values());
292
+ node.subRoutes = [...node.subRoutes || [], ...childDirs];
293
+ }
294
+ const { childrenMap, ...restNode } = node;
295
+ if (restNode.subRoutes && restNode.subRoutes.length > 0) restNode.subRoutes = finalizeTree(restNode.subRoutes);
296
+ return restNode;
297
+ }).sort((a, b) => {
298
+ const posA = getNodePosition(a);
299
+ const posB = getNodePosition(b);
300
+ return posA !== posB ? posA - posB : a.title.localeCompare(b.title);
301
+ });
302
+ };
303
+ function useSidebar(routes) {
304
+ const config = useConfig();
305
+ const { pathname } = useLocation();
306
+ return useMemo(() => {
307
+ const currentPath = normalizePath(pathname);
308
+ const activeRoute = routes.find((r) => normalizePath(r.path) === currentPath);
309
+ const activeTabId = activeRoute?.tab?.toLowerCase();
310
+ const filteredRoutes = routes.filter((r) => !r.collection && !r.sidebarHidden && !r.fallback).filter((r) => !activeTabId || r.tab?.toLowerCase() === activeTabId).sort((a, b) => getRoutePosition(a) - getRoutePosition(b));
311
+ const directoryMeta = getCleanDirectoryMeta(config.directoryMeta);
312
+ const rootNodesMap = /* @__PURE__ */ new Map();
313
+ const ungrouped = [];
314
+ for (const route of filteredRoutes) {
315
+ const parts = route.slugParts || [];
316
+ const isIndex = /^index\.mdx?$/.test(route.filePath.split("/").pop() || "");
317
+ if (parts.length === 0) {
318
+ if (route.filePath) ungrouped.push(route);
319
+ continue;
320
+ }
321
+ const containerNode = getOrCreateNode(parts, rootNodesMap, directoryMeta);
322
+ if (isIndex) Object.assign(containerNode, {
323
+ path: route.path,
324
+ title: containerNode.hasCustomTitle ? containerNode.title : route.title || containerNode.title,
325
+ icon: route.icon || containerNode.icon,
326
+ badge: route.badge,
327
+ sidebarPosition: route.sidebarPosition,
328
+ frontmatter: route.frontmatter,
329
+ filePath: route.filePath
330
+ });
331
+ else containerNode.subRoutes.push(route);
332
+ }
333
+ const finalizedTopNodes = finalizeTree(Array.from(rootNodesMap.values()));
334
+ const groups = [];
335
+ for (const node of finalizedTopNodes) if (node.subRoutes && node.subRoutes.length > 0) groups.push({
336
+ slug: node.title.toLowerCase().replace(/\s+/g, "-"),
337
+ title: node.title,
338
+ icon: node.icon,
339
+ path: node.path,
340
+ filePath: node.filePath,
341
+ routes: node.subRoutes,
342
+ sidebarPosition: node.sidebarPosition ?? node.groupPosition ?? 999,
343
+ collapsible: node.collapsible,
344
+ collapsed: node.collapsed
345
+ });
346
+ else ungrouped.push(node);
347
+ return {
348
+ groups,
349
+ ungrouped: finalizeTree(ungrouped),
350
+ activeRoute,
351
+ activePath: currentPath,
352
+ config
353
+ };
354
+ }, [
355
+ routes,
356
+ config,
357
+ pathname
358
+ ]);
359
+ }
360
+
361
+ //#endregion
362
+ //#region src/client/hooks/use-location.ts
363
+ const useLocation$1 = () => {
364
+ return useLocation();
365
+ };
366
+
367
+ //#endregion
368
+ //#region src/client/hooks/use-search-highlight.ts
369
+ /**
370
+ * Hook to highlight search terms based on the 'hl' query parameter.
371
+ */
372
+ function useSearchHighlight(containerSelector = ".boltdocs-page") {
373
+ const { search } = useLocation$1();
374
+ const query = new URLSearchParams(search).get("hl");
375
+ useEffect(() => {
376
+ if (!query) {
377
+ clearHighlights(containerSelector);
378
+ return;
379
+ }
380
+ const container = document.querySelector(containerSelector);
381
+ if (!container) return;
382
+ let rafId;
383
+ const observer = new MutationObserver((mutations) => {
384
+ if (mutations.some((m) => {
385
+ const addedNodes = Array.from(m.addedNodes);
386
+ const removedNodes = Array.from(m.removedNodes);
387
+ return addedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight"))) || removedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight")));
388
+ })) run();
389
+ });
390
+ function run() {
391
+ cancelAnimationFrame(rafId);
392
+ rafId = requestAnimationFrame(() => {
393
+ observer.disconnect();
394
+ clearHighlights(containerSelector);
395
+ const terms = query.split(/\s+/).map((t) => t.trim()).filter((t) => t.length >= 2);
396
+ if (terms.length > 0) highlightTerms(container, terms);
397
+ observer.observe(container, {
398
+ childList: true,
399
+ subtree: true
400
+ });
401
+ });
402
+ }
403
+ run();
404
+ return () => {
405
+ cancelAnimationFrame(rafId);
406
+ observer.disconnect();
407
+ clearHighlights(containerSelector);
408
+ };
409
+ }, [
410
+ query,
411
+ search,
412
+ containerSelector
413
+ ]);
414
+ }
415
+ function clearHighlights(selector) {
416
+ document.querySelectorAll(`${selector} mark[data-search-highlight]`).forEach((mark) => {
417
+ try {
418
+ const parent = mark.parentNode;
419
+ if (parent && parent.contains(mark)) {
420
+ const text = mark.textContent || "";
421
+ parent.replaceChild(document.createTextNode(text), mark);
422
+ }
423
+ } catch (e) {}
424
+ });
425
+ }
426
+ function highlightTerms(container, terms) {
427
+ const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
428
+ const parent = node.parentElement;
429
+ if (parent && (parent.tagName === "SCRIPT" || parent.tagName === "STYLE" || parent.tagName === "MARK" || parent.closest("pre") || parent.closest("code"))) return NodeFilter.FILTER_REJECT;
430
+ return NodeFilter.FILTER_ACCEPT;
431
+ } });
432
+ const nodes = [];
433
+ let node;
434
+ while (node = walker.nextNode()) nodes.push(node);
435
+ const accentMap = {
436
+ a: "[aáàäâã]",
437
+ e: "[eéèëê]",
438
+ i: "[iíìïî]",
439
+ o: "[oóòöôõ]",
440
+ u: "[uúùüû]",
441
+ n: "[nñ]",
442
+ c: "[cç]"
443
+ };
444
+ const prepareRegex = (term) => {
445
+ let pattern = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
446
+ pattern = pattern.split("").map((char) => {
447
+ return accentMap[char.toLowerCase()] || char;
448
+ }).join("");
449
+ return pattern;
450
+ };
451
+ const combinedPattern = terms.map(prepareRegex).join("|");
452
+ const regex = new RegExp(`(${combinedPattern})`, "gi");
453
+ const matchRegexes = terms.map((term) => {
454
+ const p = prepareRegex(term);
455
+ return new RegExp(`^${p}$`, "i");
456
+ });
457
+ nodes.forEach((textNode) => {
458
+ const text = textNode.textContent;
459
+ if (text && regex.test(text)) {
460
+ const fragment = document.createDocumentFragment();
461
+ text.split(regex).forEach((part) => {
462
+ if (matchRegexes.some((rx) => rx.test(part))) {
463
+ const mark = document.createElement("mark");
464
+ mark.textContent = part;
465
+ mark.setAttribute("data-search-highlight", "true");
466
+ fragment.appendChild(mark);
467
+ } else if (part) fragment.appendChild(document.createTextNode(part));
468
+ });
469
+ if (textNode.parentNode) textNode.parentNode.replaceChild(fragment, textNode);
470
+ }
471
+ });
472
+ }
473
+
245
474
  //#endregion
246
475
  //#region src/client/components/primitives/helpers/observer.ts
247
476
  function getItemId(url) {
@@ -617,126 +846,6 @@ PageNav.Title = PageNavTitle;
617
846
  PageNav.Description = PageNavDescription;
618
847
  PageNav.Icon = PageNavIcon;
619
848
 
620
- //#endregion
621
- //#region src/client/hooks/use-sidebar.ts
622
- function useSidebar(routes) {
623
- const config = useConfig();
624
- const currentPath = normalizePath(useLocation().pathname);
625
- return useMemo(() => {
626
- const activeRoute = routes.find((r) => normalizePath(r.path) === currentPath);
627
- const activeTabId = activeRoute?.tab?.toLowerCase();
628
- const noCollection = routes.filter((r) => !r.collection);
629
- const filteredRoutes = activeTabId ? noCollection.filter((r) => !r.tab || r.tab.toLowerCase() === activeTabId) : noCollection;
630
- const directoryMeta = {};
631
- if (config.directoryMeta) for (const [key, value] of Object.entries(config.directoryMeta)) {
632
- const cleanKey = key.split("/").filter((part) => !part.startsWith("(") || !part.endsWith(")")).map((part) => part.replace(/^\d+-/, "")).join("/");
633
- directoryMeta[cleanKey === "" ? "." : cleanKey] = value;
634
- }
635
- const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
636
- const rootNodesMap = /* @__PURE__ */ new Map();
637
- const ungrouped = [];
638
- const getOrCreateNode = (parts, rootStore) => {
639
- let currentMap = rootStore;
640
- let parentPath = "";
641
- let lastNode = null;
642
- for (let i = 0; i < parts.length; i++) {
643
- const segment = parts[i];
644
- const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment;
645
- if (!currentMap.has(segment)) {
646
- const meta = directoryMeta[currentRelPath] || {};
647
- const newNode = {
648
- path: "#",
649
- title: meta.title || capitalize(segment),
650
- componentPath: "",
651
- filePath: "",
652
- icon: meta.icon,
653
- groupPosition: typeof meta.order === "number" ? meta.order : 999,
654
- subRoutes: []
655
- };
656
- currentMap.set(segment, newNode);
657
- }
658
- lastNode = currentMap.get(segment);
659
- if (!lastNode._subMap) lastNode._subMap = /* @__PURE__ */ new Map();
660
- currentMap = lastNode._subMap;
661
- parentPath = currentRelPath;
662
- }
663
- return lastNode;
664
- };
665
- const sortedRoutes = [...filteredRoutes].sort((a, b) => {
666
- return (a.sidebarPosition ?? a.order ?? 999) - (b.sidebarPosition ?? b.order ?? 999);
667
- });
668
- for (const route of sortedRoutes) {
669
- if (route.sidebarHidden) continue;
670
- const parts = route.slugParts || [];
671
- const fileName = route.filePath.split("/").pop() || "";
672
- const isIndex = /^index\.mdx?$/.test(fileName);
673
- if (parts.length === 0) {
674
- if (route.filePath) ungrouped.push(route);
675
- continue;
676
- }
677
- if (isIndex) {
678
- const containerNode = getOrCreateNode(parts, rootNodesMap);
679
- if (containerNode) {
680
- containerNode.path = route.path;
681
- containerNode.title = route.title || containerNode.title;
682
- containerNode.icon = route.icon || containerNode.icon;
683
- containerNode.badge = route.badge;
684
- containerNode.sidebarPosition = route.sidebarPosition;
685
- containerNode.frontmatter = route.frontmatter;
686
- }
687
- } else {
688
- const parentNode = getOrCreateNode(parts, rootNodesMap);
689
- if (parentNode) parentNode.subRoutes.push(route);
690
- }
691
- }
692
- const finalizeTree = (nodes, currentPathPrefix = "") => {
693
- nodes.forEach((node) => {
694
- if (node._subMap) {
695
- const childDirs = Array.from(node._subMap.values());
696
- node.subRoutes = [...node.subRoutes || [], ...childDirs];
697
- delete node._subMap;
698
- }
699
- if (node.subRoutes && node.subRoutes.length > 0) node.subRoutes = finalizeTree(node.subRoutes);
700
- });
701
- return nodes.sort((a, b) => {
702
- const posA = a.sidebarPosition ?? a.groupPosition ?? 999;
703
- const posB = b.sidebarPosition ?? b.groupPosition ?? 999;
704
- if (posA !== posB) return posA - posB;
705
- return a.title.localeCompare(b.title);
706
- });
707
- };
708
- const finalizedTopNodes = finalizeTree(Array.from(rootNodesMap.values()));
709
- finalizedTopNodes.map((node) => {
710
- return {
711
- slug: node.title.toLowerCase().replace(/\s+/g, "-"),
712
- title: node.title,
713
- icon: node.icon,
714
- routes: [node]
715
- };
716
- });
717
- return {
718
- groups: finalizedTopNodes.map((node) => {
719
- if (node.subRoutes && node.subRoutes.length > 0) return {
720
- slug: node.title.toLowerCase().replace(/\s+/g, "-"),
721
- title: node.title,
722
- icon: node.icon,
723
- routes: node.subRoutes
724
- };
725
- ungrouped.push(node);
726
- return null;
727
- }).filter(Boolean),
728
- ungrouped: finalizeTree(ungrouped),
729
- activeRoute,
730
- activePath: currentPath,
731
- config
732
- };
733
- }, [
734
- routes,
735
- config,
736
- currentPath
737
- ]);
738
- }
739
-
740
849
  //#endregion
741
850
  //#region src/client/components/primitives/sidebar.tsx
742
851
  let sidebarScrollPos = 0;
@@ -830,13 +939,29 @@ function SidebarContent({ children, className }) {
830
939
  /**
831
940
  * Navigation Group
832
941
  */
833
- function SidebarGroup({ title, icon: Icon, children, className }) {
942
+ function SidebarGroup({ title, icon: Icon, children, className, collapsible = false, collapsed = false, active = false }) {
943
+ const [isOpen, setIsOpen] = useState(() => active || !collapsed);
944
+ const [prevActive, setPrevActive] = useState(active);
945
+ if (active !== prevActive) {
946
+ setPrevActive(active);
947
+ if (active) setIsOpen(true);
948
+ }
834
949
  return /* @__PURE__ */ jsxs("div", {
835
950
  className,
836
- children: [title && /* @__PURE__ */ jsxs("h4", {
951
+ children: [title && (collapsible ? /* @__PURE__ */ jsxs("button", {
952
+ onClick: () => setIsOpen(!isOpen),
953
+ className: "w-full text-left px-2 mb-2 flex items-center justify-between text-xs font-regular tracking-widest text-muted/50 hover:text-body transition-colors outline-none cursor-pointer group",
954
+ children: [/* @__PURE__ */ jsxs("span", {
955
+ className: "flex items-center gap-2",
956
+ children: [Icon && /* @__PURE__ */ jsx(Icon, { size: 12 }), title]
957
+ }), /* @__PURE__ */ jsx(ChevronRight, {
958
+ size: 12,
959
+ className: cn("transition-transform duration-200 text-muted/40 group-hover:text-body", isOpen && "rotate-90")
960
+ })]
961
+ }) : /* @__PURE__ */ jsxs("h4", {
837
962
  className: "px-2 mb-2 flex items-center gap-2 text-[11px] font-bold uppercase tracking-widest text-muted/50",
838
963
  children: [Icon && /* @__PURE__ */ jsx(Icon, { size: 12 }), title]
839
- }), /* @__PURE__ */ jsx("div", {
964
+ })), (!collapsible || isOpen) && /* @__PURE__ */ jsx("div", {
840
965
  className: "flex flex-col gap-0.5",
841
966
  children
842
967
  })]
@@ -927,23 +1052,65 @@ function SidebarItem({ route, activePath, activeRoute, className }) {
927
1052
  className
928
1053
  });
929
1054
  }
1055
+ const isRouteActive = (route, activePath, activeRoute) => {
1056
+ const normalizedPath = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
1057
+ if ((activePath.endsWith("/") ? activePath.slice(0, -1) : activePath) === normalizedPath) return true;
1058
+ if (activeRoute?.filePath && route.filePath && activeRoute.filePath === route.filePath) return true;
1059
+ if (route.routes && route.routes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
1060
+ if (route.subRoutes && route.subRoutes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
1061
+ return false;
1062
+ };
930
1063
  function SidebarItems({ routes, className }) {
931
1064
  const { groups, ungrouped, activePath, activeRoute } = useSidebar(routes);
932
- return /* @__PURE__ */ jsxs("div", {
933
- className: cn("flex flex-col gap-6", className),
934
- children: [ungrouped.length > 0 && /* @__PURE__ */ jsx(SidebarGroup, { children: ungrouped.map((route) => /* @__PURE__ */ jsx(SidebarItem, {
935
- route,
936
- activePath,
937
- activeRoute
938
- }, route.path)) }), groups.map((group) => /* @__PURE__ */ jsx(SidebarGroup, {
939
- title: group.title,
940
- icon: getIcon(group.icon),
941
- children: group.routes.map((route) => /* @__PURE__ */ jsx(SidebarItem, {
1065
+ const mergedItems = [...ungrouped.map((route) => ({
1066
+ type: "link",
1067
+ position: route.sidebarPosition ?? 999,
1068
+ title: route.title,
1069
+ route
1070
+ })), ...groups.map((group) => ({
1071
+ type: "group",
1072
+ position: group.sidebarPosition ?? 999,
1073
+ title: group.title,
1074
+ group
1075
+ }))].sort((a, b) => {
1076
+ if (a.position !== b.position) return a.position - b.position;
1077
+ if (a.type !== b.type) return a.type === "link" ? -1 : 1;
1078
+ return a.title.localeCompare(b.title);
1079
+ });
1080
+ const renderedElements = [];
1081
+ let currentUngrouped = [];
1082
+ const pushUngrouped = () => {
1083
+ if (currentUngrouped.length > 0) {
1084
+ const routesToRender = [...currentUngrouped];
1085
+ renderedElements.push(/* @__PURE__ */ jsx(SidebarGroup, { children: routesToRender.map((route) => /* @__PURE__ */ jsx(SidebarItem, {
1086
+ route,
1087
+ activePath,
1088
+ activeRoute
1089
+ }, route.path)) }, `ungrouped-${routesToRender[0].path}`));
1090
+ currentUngrouped = [];
1091
+ }
1092
+ };
1093
+ for (const item of mergedItems) if (item.type === "link") currentUngrouped.push(item.route);
1094
+ else {
1095
+ pushUngrouped();
1096
+ const isGroupActive = item.group.routes.some((route) => isRouteActive(route, activePath, activeRoute));
1097
+ renderedElements.push(/* @__PURE__ */ jsx(SidebarGroup, {
1098
+ title: item.group.title,
1099
+ icon: getIcon(item.group.icon),
1100
+ collapsible: item.group.collapsible,
1101
+ collapsed: item.group.collapsed,
1102
+ active: isGroupActive,
1103
+ children: item.group.routes.map((route) => /* @__PURE__ */ jsx(SidebarItem, {
942
1104
  route,
943
1105
  activePath,
944
1106
  activeRoute
945
1107
  }, route.path))
946
- }, group.title))]
1108
+ }, item.group.title));
1109
+ }
1110
+ pushUngrouped();
1111
+ return /* @__PURE__ */ jsx("div", {
1112
+ className: cn("flex flex-col gap-6", className),
1113
+ children: renderedElements
947
1114
  });
948
1115
  }
949
1116
  /**
@@ -961,119 +1128,6 @@ const Sidebar = Object.assign(SidebarRoot, {
961
1128
  Items: SidebarItems
962
1129
  });
963
1130
 
964
- //#endregion
965
- //#region src/client/hooks/use-location.ts
966
- const useLocation$1 = () => {
967
- return useLocation();
968
- };
969
-
970
- //#endregion
971
- //#region src/client/hooks/use-search-highlight.ts
972
- /**
973
- * Hook to highlight search terms based on the 'hl' query parameter.
974
- */
975
- function useSearchHighlight(containerSelector = ".boltdocs-page") {
976
- const { search } = useLocation$1();
977
- const query = new URLSearchParams(search).get("hl");
978
- useEffect(() => {
979
- if (!query) {
980
- clearHighlights(containerSelector);
981
- return;
982
- }
983
- const container = document.querySelector(containerSelector);
984
- if (!container) return;
985
- let rafId;
986
- const observer = new MutationObserver((mutations) => {
987
- if (mutations.some((m) => {
988
- const addedNodes = Array.from(m.addedNodes);
989
- const removedNodes = Array.from(m.removedNodes);
990
- return addedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight"))) || removedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight")));
991
- })) run();
992
- });
993
- function run() {
994
- cancelAnimationFrame(rafId);
995
- rafId = requestAnimationFrame(() => {
996
- observer.disconnect();
997
- clearHighlights(containerSelector);
998
- const terms = query.split(/\s+/).map((t) => t.trim()).filter((t) => t.length >= 2);
999
- if (terms.length > 0) highlightTerms(container, terms);
1000
- observer.observe(container, {
1001
- childList: true,
1002
- subtree: true
1003
- });
1004
- });
1005
- }
1006
- run();
1007
- return () => {
1008
- cancelAnimationFrame(rafId);
1009
- observer.disconnect();
1010
- clearHighlights(containerSelector);
1011
- };
1012
- }, [
1013
- query,
1014
- search,
1015
- containerSelector
1016
- ]);
1017
- }
1018
- function clearHighlights(selector) {
1019
- document.querySelectorAll(`${selector} mark[data-search-highlight]`).forEach((mark) => {
1020
- try {
1021
- const parent = mark.parentNode;
1022
- if (parent && parent.contains(mark)) {
1023
- const text = mark.textContent || "";
1024
- parent.replaceChild(document.createTextNode(text), mark);
1025
- }
1026
- } catch (e) {}
1027
- });
1028
- }
1029
- function highlightTerms(container, terms) {
1030
- const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
1031
- const parent = node.parentElement;
1032
- if (parent && (parent.tagName === "SCRIPT" || parent.tagName === "STYLE" || parent.tagName === "MARK" || parent.closest("pre") || parent.closest("code"))) return NodeFilter.FILTER_REJECT;
1033
- return NodeFilter.FILTER_ACCEPT;
1034
- } });
1035
- const nodes = [];
1036
- let node;
1037
- while (node = walker.nextNode()) nodes.push(node);
1038
- const accentMap = {
1039
- a: "[aáàäâã]",
1040
- e: "[eéèëê]",
1041
- i: "[iíìïî]",
1042
- o: "[oóòöôõ]",
1043
- u: "[uúùüû]",
1044
- n: "[nñ]",
1045
- c: "[cç]"
1046
- };
1047
- const prepareRegex = (term) => {
1048
- let pattern = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1049
- pattern = pattern.split("").map((char) => {
1050
- return accentMap[char.toLowerCase()] || char;
1051
- }).join("");
1052
- return pattern;
1053
- };
1054
- const combinedPattern = terms.map(prepareRegex).join("|");
1055
- const regex = new RegExp(`(${combinedPattern})`, "gi");
1056
- const matchRegexes = terms.map((term) => {
1057
- const p = prepareRegex(term);
1058
- return new RegExp(`^${p}$`, "i");
1059
- });
1060
- nodes.forEach((textNode) => {
1061
- const text = textNode.textContent;
1062
- if (text && regex.test(text)) {
1063
- const fragment = document.createDocumentFragment();
1064
- text.split(regex).forEach((part) => {
1065
- if (matchRegexes.some((rx) => rx.test(part))) {
1066
- const mark = document.createElement("mark");
1067
- mark.textContent = part;
1068
- mark.setAttribute("data-search-highlight", "true");
1069
- fragment.appendChild(mark);
1070
- } else if (part) fragment.appendChild(document.createTextNode(part));
1071
- });
1072
- if (textNode.parentNode) textNode.parentNode.replaceChild(fragment, textNode);
1073
- }
1074
- });
1075
- }
1076
-
1077
1131
  //#endregion
1078
1132
  //#region src/client/components/ui-base/search-highlight.tsx
1079
1133
  /**
@@ -1164,4 +1218,4 @@ const DocsLayout = Object.assign(DocsLayoutRoot, {
1164
1218
  });
1165
1219
 
1166
1220
  //#endregion
1167
- export { useUI as A, useItems as C, ButtonGroup as D, Popover$1 as E, Breadcrumbs$1 as O, useActiveAnchors as S, Menu as T, OnThisPage as _, SidebarContent as a, ScrollProvider as b, SidebarItem as c, SidebarMobile as d, SidebarRoot as f, AnchorProvider as g, PageNav as h, Sidebar as i, UIProvider as k, SidebarItems as l, useSidebar as m, useSearchHighlight as n, SidebarGroup as o, SidebarSubGroup as p, useLocation$1 as r, SidebarHeader as s, DocsLayout as t, SidebarLink as u, OnThisPageItems as v, Tabs as w, useActiveAnchor as x, OnThisPageTree as y };
1221
+ export { useUI as A, useSidebar as C, ButtonGroup as D, Popover$1 as E, Breadcrumbs$1 as O, useLocation$1 as S, Menu as T, ScrollProvider as _, SidebarHeader as a, useItems as b, SidebarLink as c, SidebarSubGroup as d, PageNav as f, OnThisPageTree as g, OnThisPageItems as h, SidebarGroup as i, UIProvider as k, SidebarMobile as l, OnThisPage as m, Sidebar as n, SidebarItem as o, AnchorProvider as p, SidebarContent as r, SidebarItems as s, DocsLayout as t, SidebarRoot as u, useActiveAnchor as v, Tabs as w, useSearchHighlight as x, useActiveAnchors as y };