boltdocs 3.2.2 → 3.3.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 (155) hide show
  1. package/bin/boltdocs.js +20 -1
  2. package/bin/preloader.mjs +28 -0
  3. package/dist/{adapters-ByYY44Zy.mjs → adapters-BmB4TKCc.mjs} +1 -1
  4. package/dist/{adapters-DvmtpdSF.cjs → adapters-Cp1Maurn.cjs} +1 -1
  5. package/dist/audit-CLXjMzwQ.mjs +11 -0
  6. package/dist/audit-COgjnQ5n.cjs +11 -0
  7. package/dist/build-BS2hefiL.cjs +6 -0
  8. package/dist/build-C5mNh9D-.mjs +6 -0
  9. package/dist/cache-CJE9nWLE.cjs +6 -0
  10. package/dist/cache-OY4HzIXl.mjs +6 -0
  11. package/dist/callout-oI_LCbxS.d.ts +71 -0
  12. package/dist/callout-plqx9Dw-.d.cts +71 -0
  13. package/dist/client/index.cjs +1265 -569
  14. package/dist/client/index.d.cts +3 -542
  15. package/dist/client/index.d.ts +3 -542
  16. package/dist/client/index.js +1167 -521
  17. package/dist/client/mdx.cjs +1 -1
  18. package/dist/client/mdx.d.cts +24 -2
  19. package/dist/client/mdx.d.ts +24 -2
  20. package/dist/client/mdx.js +1 -1
  21. package/dist/client/primitives.cjs +14 -4
  22. package/dist/client/primitives.d.cts +281 -38
  23. package/dist/client/primitives.d.ts +281 -38
  24. package/dist/client/primitives.js +5 -5
  25. package/dist/client/router.cjs +49 -0
  26. package/dist/client/router.d.cts +3 -0
  27. package/dist/client/router.d.ts +3 -0
  28. package/dist/client/router.js +8 -0
  29. package/dist/client/theme/reset.css +40 -4
  30. package/dist/{config-BpzaPsJj.d.cts → config-BoRlz0Ca.d.mts} +157 -14
  31. package/dist/{config-xtS0ZkMm.d.mts → config-CTsX_tGM.d.cts} +157 -14
  32. package/dist/config-Ceib-AOH.cjs +7 -0
  33. package/dist/config-DGLQsy7y.mjs +7 -0
  34. package/dist/{package-CH2ChQqw.cjs → config-files-BYHDOrKn.mjs} +1 -1
  35. package/dist/config-files-D9cdBMl2.cjs +6 -0
  36. package/dist/config-jJh8WYJI.cjs +6 -0
  37. package/dist/csp-C4LmN1ko.cjs +6 -0
  38. package/dist/csp-D6JEOqKh.mjs +6 -0
  39. package/dist/dev-Ch3hEe5Y.cjs +6 -0
  40. package/dist/dev-QPNxysrd.mjs +6 -0
  41. package/dist/{docs-layout-BdtZ9z6s.js → docs-layout-CbZ7LDRe.js} +504 -249
  42. package/dist/{docs-layout-CFWI15Id.cjs → docs-layout-vzfXed1Q.cjs} +516 -254
  43. package/dist/{doctor-BBtmNJE0.cjs → doctor-B1_YL0Wt.cjs} +1 -1
  44. package/dist/doctor-C7whl4ED.cjs +6 -0
  45. package/dist/doctor-DmQnml2k.mjs +6 -0
  46. package/dist/{generator-COepVWGi.cjs → generator-BlW45ST7.cjs} +5 -3
  47. package/dist/{generator-CcOBN5vI.mjs → generator-BwknNejd.mjs} +5 -3
  48. package/dist/{handler-XX7cLTqe.cjs → handler-CDEoKARD.cjs} +2 -2
  49. package/dist/{handler-DzQae4eA.mjs → handler-D7UyZlPm.mjs} +2 -2
  50. package/dist/{image-Bz87fCla.js → image-B-NDSDuY.js} +129 -11
  51. package/dist/{image-C4TlpMHn.cjs → image-Hnaa2bJd.cjs} +175 -9
  52. package/dist/index-C56bL-A-.d.cts +1344 -0
  53. package/dist/{sidebar-COTaMHR-.d.ts → index-CPUUKzxD.d.cts} +344 -327
  54. package/dist/{sidebar-CrEdJuHX.d.cts → index-Ci4kfOHF.d.ts} +344 -327
  55. package/dist/index-Cu9H6CRa.d.ts +1344 -0
  56. package/dist/link-CgdI6O2o.js +872 -0
  57. package/dist/{link-D1Gjkb-2.cjs → link-vZjmD-es.cjs} +406 -223
  58. package/dist/{mdx-BuO6a1f7.js → mdx-BRT71V5Q.js} +211 -168
  59. package/dist/{mdx-CD-tSjRG.cjs → mdx-D0Absdq5.cjs} +225 -169
  60. package/dist/native-BNCY7PRF.cjs +6 -0
  61. package/dist/native-Di9Fnfrv.mjs +6 -0
  62. package/dist/native-Kre2T5eS.cjs +6 -0
  63. package/dist/node/cache.cjs +1 -1
  64. package/dist/node/cache.d.cts +19 -3
  65. package/dist/node/cache.d.mts +19 -3
  66. package/dist/node/cache.mjs +1 -1
  67. package/dist/node/cli-entry.cjs +1 -9
  68. package/dist/node/cli-entry.mjs +1 -9
  69. package/dist/node/index.cjs +1 -1
  70. package/dist/node/index.d.cts +245 -17
  71. package/dist/node/index.d.mts +246 -18
  72. package/dist/node/index.mjs +1 -1
  73. package/dist/node/mdx/shiki-adapter.cjs +1 -1
  74. package/dist/node/mdx/shiki-adapter.d.cts +24 -3
  75. package/dist/node/mdx/shiki-adapter.d.mts +24 -2
  76. package/dist/node/mdx/shiki-adapter.mjs +1 -1
  77. package/dist/node-Du5UL5wo.mjs +23 -0
  78. package/dist/node-DunuGqyp.cjs +23 -0
  79. package/dist/{package-DSSB8B5L.mjs → package-DA848keM.mjs} +1 -1
  80. package/dist/package-wplzySjz.cjs +6 -0
  81. package/dist/pages-external-BBw8ObVK.mjs +6 -0
  82. package/dist/pages-external-DPiPFxxP.cjs +6 -0
  83. package/dist/parser-BqTOsEUa.cjs +6 -0
  84. package/dist/parser-CGMY2lJ7.mjs +6 -0
  85. package/dist/plugin-BcTzvHAJ.mjs +69 -0
  86. package/dist/plugin-TulVMt1z.cjs +69 -0
  87. package/dist/plugin-context-C-l90hFt.cjs +15 -0
  88. package/dist/plugin-context-wqHNSZum.mjs +15 -0
  89. package/dist/plugin-lifecycle-B1BzSHdn.cjs +6 -0
  90. package/dist/resolve-B7vWOd8R.cjs +6 -0
  91. package/dist/resolve-C2kKnk_5.mjs +6 -0
  92. package/dist/resolve-It1Iug83.cjs +6 -0
  93. package/dist/resolve-YdTrt_FG.mjs +6 -0
  94. package/dist/router-C3kygdRK.cjs +1285 -0
  95. package/dist/router-CZBxMRAH.js +987 -0
  96. package/dist/routes-BL4y79tb.mjs +6 -0
  97. package/dist/routes-C2nPecwu.cjs +6 -0
  98. package/dist/routes-U9L8psZ9.cjs +6 -0
  99. package/dist/{search-dialog-DHqwjJuG.cjs → search-dialog-BCOHQGSt.cjs} +1 -1
  100. package/dist/search-dialog-Bp_P1MPP.js +481 -0
  101. package/dist/{search-dialog-DFnlx8LO.js → search-dialog-BqkzDTq2.js} +107 -57
  102. package/dist/{search-dialog-BRFno7HD.cjs → search-dialog-CIeIUhAn.cjs} +218 -43
  103. package/dist/{search-dialog-D6TvjcuV.js → search-dialog-DtmxmfEu.js} +1 -1
  104. package/dist/{search-dialog-B1NERUNx.cjs → search-dialog-x_qHU8ty.cjs} +107 -58
  105. package/dist/server/index.cjs +1 -1
  106. package/dist/server/index.d.mts +1 -1
  107. package/dist/server/index.mjs +1 -1
  108. package/dist/update-check-BKqmYfwT.cjs +11 -0
  109. package/dist/update-check-QnrVR3q1.mjs +11 -0
  110. package/dist/utils-8lzafE49.mjs +7 -0
  111. package/dist/utils-ej6_SjIZ.cjs +7 -0
  112. package/dist/virtual-modules-Ca5tHk1X.cjs +30 -0
  113. package/dist/virtual-modules-Dlgt1fP0.mjs +30 -0
  114. package/package.json +18 -16
  115. package/src/shared/mdx-path.ts +12 -0
  116. package/src/shared/structured-data.ts +145 -0
  117. package/src/shared/types.ts +220 -16
  118. package/dist/banner-BMop1KLF.d.ts +0 -100
  119. package/dist/banner-CVD0GugR.d.cts +0 -100
  120. package/dist/cache-CO4Dy2uQ.cjs +0 -6
  121. package/dist/cache-Dk53iOo6.mjs +0 -6
  122. package/dist/config-CCXp3aXO.mjs +0 -7
  123. package/dist/config-CMd4XoFq.cjs +0 -6
  124. package/dist/config-DlGoEM9D.cjs +0 -7
  125. package/dist/csp-CRp_3U7a.cjs +0 -6
  126. package/dist/csp-oTC2ygvF.mjs +0 -6
  127. package/dist/doctor-Bv16QSrI.cjs +0 -7
  128. package/dist/doctor-DOAbSaiL.mjs +0 -7
  129. package/dist/headers-Ddwek8K7.mjs +0 -6
  130. package/dist/headers-dg4SDrjY.cjs +0 -6
  131. package/dist/link-2PzgTnaP.js +0 -673
  132. package/dist/meta-loader-C4Hu9RN9.mjs +0 -6
  133. package/dist/meta-loader-Dq1zcFnh.cjs +0 -6
  134. package/dist/node-B5hu_6qj.cjs +0 -104
  135. package/dist/node-BXtqM1uH.mjs +0 -6
  136. package/dist/node-C8725aw7.mjs +0 -104
  137. package/dist/node-aD1eisrR.cjs +0 -6
  138. package/dist/pages-external-BYa4JSda.mjs +0 -6
  139. package/dist/pages-external-XkYIs-d5.cjs +0 -6
  140. package/dist/parser-DCiEWkSk.cjs +0 -8
  141. package/dist/parser-qk9FIHJ7.mjs +0 -8
  142. package/dist/plugin-CX1OZ6jB.cjs +0 -73
  143. package/dist/plugin-CgfiWaFZ.mjs +0 -73
  144. package/dist/routes-BW6zsk7Z.mjs +0 -6
  145. package/dist/routes-BgoIB5wb.cjs +0 -6
  146. package/dist/routes-CORVxc02.cjs +0 -6
  147. package/dist/search-dialog-BmxHiKwt.js +0 -312
  148. package/dist/shiki-adapter-CoQ6jyJ4.cjs +0 -6
  149. package/dist/shiki-adapter-D-aKw7PP.mjs +0 -6
  150. package/dist/utils-Crm9oPcV.mjs +0 -7
  151. package/dist/utils-D6Xtj0FZ.cjs +0 -7
  152. /package/dist/{errors-0zGHRLue.mjs → errors-N-b6Sn2G.mjs} +0 -0
  153. /package/dist/{index-1KsTDq4o.d.mts → index-lFoyzCXy.d.mts} +0 -0
  154. /package/dist/{types-generator-BXhv7Ill.cjs → types-generator-DDSA93P3.cjs} +0 -0
  155. /package/dist/{types-generator-bDPvUztF.mjs → types-generator-DG1GX1EZ.mjs} +0 -0
@@ -3,27 +3,30 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
- const require_link = require('./link-D1Gjkb-2.cjs');
6
+ const require_router = require('./router-C3kygdRK.cjs');
7
+ const require_link = require('./link-vZjmD-es.cjs');
7
8
  let react = require("react");
8
- let react_router_dom = require("react-router-dom");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  let react_aria_components = require("react-aria-components");
11
- react_aria_components = require_link.__toESM(react_aria_components);
11
+ react_aria_components = require_router.__toESM(react_aria_components);
12
+ let isomorphic_dompurify = require("isomorphic-dompurify");
13
+ isomorphic_dompurify = require_router.__toESM(isomorphic_dompurify);
12
14
  let virtual_boltdocs_icons = require("virtual:boltdocs-icons");
13
- virtual_boltdocs_icons = require_link.__toESM(virtual_boltdocs_icons);
15
+ virtual_boltdocs_icons = require_router.__toESM(virtual_boltdocs_icons);
14
16
  let scroll_into_view_if_needed = require("scroll-into-view-if-needed");
15
- scroll_into_view_if_needed = require_link.__toESM(scroll_into_view_if_needed);
17
+ scroll_into_view_if_needed = require_router.__toESM(scroll_into_view_if_needed);
16
18
 
17
19
  //#region src/client/app/ui-context.tsx
18
20
  const UIContext = (0, react.createContext)(void 0);
19
21
  function UIProvider({ children }) {
20
22
  const [isSidebarOpen, setIsSidebarOpen] = (0, react.useState)(false);
21
- const location = (0, react_router_dom.useLocation)();
23
+ const location = require_router.useLocation();
22
24
  const toggleSidebar = () => setIsSidebarOpen((prev) => !prev);
23
25
  const closeSidebar = () => setIsSidebarOpen(false);
26
+ const { pathname } = location;
24
27
  (0, react.useEffect)(() => {
25
- setIsSidebarOpen(false);
26
- }, [location.pathname]);
28
+ if (pathname) setIsSidebarOpen(false);
29
+ }, [pathname]);
27
30
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UIContext.Provider, {
28
31
  value: {
29
32
  isSidebarOpen,
@@ -35,11 +38,14 @@ function UIProvider({ children }) {
35
38
  }
36
39
  function useUI() {
37
40
  const context = (0, react.useContext)(UIContext);
38
- if (context === void 0) return {
39
- isSidebarOpen: false,
40
- toggleSidebar: () => {},
41
- closeSidebar: () => {}
42
- };
41
+ if (context === void 0) {
42
+ if (typeof window !== "undefined") console.warn("[boltdocs] useUI() was called outside of a <UIProvider>. Make sure Navbar/Sidebar are rendered within BoltdocsShell. The mobile sidebar will not open until the provider is shared.");
43
+ return {
44
+ isSidebarOpen: false,
45
+ toggleSidebar: () => {},
46
+ closeSidebar: () => {}
47
+ };
48
+ }
43
49
  return context;
44
50
  }
45
51
 
@@ -67,10 +73,13 @@ function BreadcrumbsLink({ children, href, className, ...props }) {
67
73
  children
68
74
  });
69
75
  }
70
- function BreadcrumbsSeparator({ className }) {
71
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, {
72
- size: 14,
73
- className: require_link.cn("shrink-0", className)
76
+ function BreadcrumbsSeparator({ className, separator }) {
77
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
78
+ className,
79
+ children: separator ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, {
80
+ size: 14,
81
+ className: "shrink-0"
82
+ })
74
83
  });
75
84
  }
76
85
  Breadcrumbs.Root = Breadcrumbs;
@@ -80,22 +89,54 @@ Breadcrumbs.Separator = BreadcrumbsSeparator;
80
89
 
81
90
  //#endregion
82
91
  //#region src/client/components/primitives/button-group.tsx
83
- function ButtonGroup({ children, className, vertical = false }) {
92
+ const RADIUS_BY_RADIUS = {
93
+ full: [
94
+ "[&>*:first-child]:rounded-l-full",
95
+ "[&>*:last-child]:rounded-r-full",
96
+ "[&>*:first-child]:rounded-t-full",
97
+ "[&>*:last-child]:rounded-b-full"
98
+ ],
99
+ xl: [
100
+ "[&>*:first-child]:rounded-l-xl",
101
+ "[&>*:last-child]:rounded-r-xl",
102
+ "[&>*:first-child]:rounded-t-xl",
103
+ "[&>*:last-child]:rounded-b-xl"
104
+ ],
105
+ lg: [
106
+ "[&>*:first-child]:rounded-l-lg",
107
+ "[&>*:last-child]:rounded-r-lg",
108
+ "[&>*:first-child]:rounded-t-lg",
109
+ "[&>*:last-child]:rounded-b-lg"
110
+ ],
111
+ md: [
112
+ "[&>*:first-child]:rounded-l-md",
113
+ "[&>*:last-child]:rounded-r-md",
114
+ "[&>*:first-child]:rounded-t-md",
115
+ "[&>*:last-child]:rounded-b-md"
116
+ ],
117
+ none: []
118
+ };
119
+ function inferRadius(className) {
120
+ if (className?.includes("rounded-full")) return "full";
121
+ if (className?.includes("rounded-xl")) return "xl";
122
+ if (className?.includes("rounded-lg")) return "lg";
123
+ return "md";
124
+ }
125
+ function ButtonGroup({ children, className, vertical = false, radius }) {
126
+ const outer = RADIUS_BY_RADIUS[radius ?? inferRadius(className)] ?? [];
84
127
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
85
128
  className: require_link.cn("inline-flex", vertical ? "flex-col" : "flex-row", !vertical && [
86
129
  "[&>*:not(:first-child)]:-ml-px",
87
130
  "[&>*:first-child]:rounded-r-none",
88
131
  "[&>*:last-child]:rounded-l-none",
89
132
  "[&>*:not(:first-child):not(:last-child)]:rounded-none",
90
- className?.includes("rounded-full") && ["[&>*:first-child]:rounded-l-full", "[&>*:last-child]:rounded-r-full"],
91
- className?.includes("rounded-xl") && ["[&>*:first-child]:rounded-l-xl", "[&>*:last-child]:rounded-r-xl"],
92
- className?.includes("rounded-lg") && ["[&>*:first-child]:rounded-l-lg", "[&>*:last-child]:rounded-r-lg"]
133
+ outer.filter((c) => c.includes("rounded-l-") || c.includes("rounded-r-"))
93
134
  ], vertical && [
94
135
  "[&>*:not(:first-child)]:-mt-px",
95
136
  "[&>*:first-child]:rounded-b-none",
96
137
  "[&>*:last-child]:rounded-t-none",
97
138
  "[&>*:not(:first-child):not(:last-child)]:rounded-none",
98
- className?.includes("rounded-full") && ["[&>*:first-child]:rounded-t-full", "[&>*:last-child]:rounded-b-full"]
139
+ outer.filter((c) => c.includes("rounded-t-") || c.includes("rounded-b-"))
99
140
  ], className),
100
141
  children
101
142
  });
@@ -154,29 +195,33 @@ function Menu(props) {
154
195
  */
155
196
  function MenuItem(props) {
156
197
  const textValue = props.textValue || (typeof props.children === "string" ? props.children : void 0);
198
+ const { contentClassName, checkClassName, chevronClassName, check, chevron, ...racProps } = props;
157
199
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.MenuItem, {
158
- ...props,
200
+ ...racProps,
159
201
  textValue,
160
202
  className: react_aria_components.composeRenderProps(props.className, (className) => require_link.cn("group relative flex flex-row items-center cursor-default outline-none", className)),
161
203
  children: react_aria_components.composeRenderProps(props.children, (children, { selectionMode, isSelected, hasSubmenu }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
162
204
  selectionMode === "multiple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
163
- className: "flex items-center shrink-0 justify-center",
164
- children: isSelected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.Check, { className: "size-3.5" })
205
+ className: require_link.cn("flex items-center shrink-0 justify-center", checkClassName),
206
+ children: check ?? (isSelected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.Check, { className: "size-3.5" }) : null)
165
207
  }),
166
208
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
167
- className: "flex flex-row w-full items-center",
209
+ className: require_link.cn("flex flex-row w-full items-center", contentClassName),
168
210
  children
169
211
  }),
170
- hasSubmenu && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, { className: "size-4 ml-auto" })
212
+ hasSubmenu && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
213
+ className: "ml-auto",
214
+ children: chevron ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, { className: require_link.cn("size-4", chevronClassName) })
215
+ })
171
216
  ] }))
172
217
  });
173
218
  }
174
- function MenuSection({ title, ...props }) {
219
+ function MenuSection({ title, headerClassName, ...props }) {
175
220
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_aria_components.MenuSection, {
176
221
  ...props,
177
222
  className: require_link.cn("flex flex-col", props.className),
178
223
  children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.Header, {
179
- className: "select-none",
224
+ className: require_link.cn("select-none", headerClassName),
180
225
  children: title
181
226
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.Collection, {
182
227
  items: props.items,
@@ -244,33 +289,126 @@ Tabs.Item = TabsItem;
244
289
  Tabs.Content = TabsContent;
245
290
  Tabs.Indicator = TabsIndicator;
246
291
 
292
+ //#endregion
293
+ //#region src/client/components/ui-base/icon-renderer.tsx
294
+ function isRecord(value) {
295
+ return typeof value === "object" && value !== null;
296
+ }
297
+ function normalizeIconExports(value) {
298
+ if (!isRecord(value)) return {};
299
+ const directExports = Object.fromEntries(Object.entries(value).filter(([key]) => key !== "default"));
300
+ const defaultExport = value.default;
301
+ if (!isRecord(defaultExport)) return directExports;
302
+ return {
303
+ ...normalizeIconExports(defaultExport),
304
+ ...directExports
305
+ };
306
+ }
307
+ function getIconRegistry() {
308
+ return {
309
+ ...require_link.icons_exports,
310
+ ...normalizeIconExports(virtual_boltdocs_icons.default)
311
+ };
312
+ }
313
+ function resolveIcon(icon, registry = getIconRegistry()) {
314
+ if (!icon || typeof icon !== "string") return icon;
315
+ if (icon.trim().startsWith("<svg")) return icon;
316
+ if (/^(?:https?:|data:image\/|\/|\.\.?(?:\/|$))/.test(icon) || /\.(?:svg|png|jpe?g|gif|webp|avif)(?:\?.*)?$/i.test(icon)) return icon;
317
+ return registry[icon] || registry[`${icon}Icon`];
318
+ }
319
+ function isImageSource(value) {
320
+ return /^(?:https?:|data:image\/|\/|\.\.?(?:\/|$))/.test(value) || /\.(?:svg|png|jpe?g|gif|webp|avif)(?:\?.*)?$/i.test(value);
321
+ }
322
+ function IconRenderer({ icon, size = 16, className, label }) {
323
+ const config = require_link.useOptionalConfig();
324
+ const resolved = resolveIcon(icon);
325
+ if (!resolved) return null;
326
+ if (typeof resolved === "function") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(resolved, {
327
+ size,
328
+ width: size,
329
+ height: size,
330
+ className,
331
+ "aria-hidden": label ? void 0 : true,
332
+ "aria-label": label
333
+ });
334
+ if (resolved.trim().startsWith("<svg")) {
335
+ const clean = isomorphic_dompurify.default.sanitize(resolved, { USE_PROFILES: { svg: true } });
336
+ if (label) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
337
+ className,
338
+ role: "img",
339
+ "aria-label": label,
340
+ dangerouslySetInnerHTML: { __html: clean }
341
+ });
342
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
343
+ className,
344
+ "aria-hidden": "true",
345
+ dangerouslySetInnerHTML: { __html: clean }
346
+ });
347
+ }
348
+ if (isImageSource(resolved)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
349
+ src: require_link.resolvePublicAssetUrl(resolved, config?.base),
350
+ alt: label || "",
351
+ "aria-hidden": label ? void 0 : true,
352
+ className,
353
+ width: size,
354
+ height: size
355
+ });
356
+ return null;
357
+ }
358
+
247
359
  //#endregion
248
360
  //#region src/client/hooks/use-sidebar.ts
249
361
  const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
250
- const getCleanDirectoryMeta = (directoryMeta) => {
251
- const meta = {};
252
- if (!directoryMeta) return meta;
362
+ /**
363
+ * Builds a lookup map from meta.json relative-directory keys.
364
+ * Keys keep the locale (`es/...`) and tab (`(guides)/...`) segments so meta
365
+ * resolution can be scoped per tab and per locale; numeric prefixes are
366
+ * stripped and keys are lowercased for stable matching.
367
+ */
368
+ const getDirectoryMetaLookup = (directoryMeta) => {
369
+ const lookup = {};
370
+ if (!directoryMeta) return lookup;
253
371
  for (const [key, value] of Object.entries(directoryMeta)) {
254
- const cleanKey = key.split("/").filter((part) => !part.startsWith("(") || !part.endsWith(")")).map((part) => part.replace(/^\d+-/, "")).join("/");
255
- meta[cleanKey === "" ? "." : cleanKey] = value;
372
+ const normalizedKey = key.split("/").map((part) => part.replace(/^\d+-/, "")).join("/").toLowerCase();
373
+ lookup[normalizedKey] = value;
374
+ }
375
+ return lookup;
376
+ };
377
+ /**
378
+ * Resolves the meta.json entry for a sidebar group by trying increasingly
379
+ * specific directory keys: locale + tab, tab, locale, then the bare path.
380
+ * This keeps groups that share a directory name across tabs or locales
381
+ * (e.g. `(guides)/content` vs `(plugins)/content`) from clobbering each
382
+ * other and lets localized sites resolve their own translated meta.
383
+ */
384
+ const findDirectoryMeta = (lookup, route, relPath) => {
385
+ const locale = route?.locale;
386
+ const tab = route?.tab;
387
+ const candidates = [];
388
+ if (locale && tab) candidates.push(`${locale}/(${tab})/${relPath}`);
389
+ if (tab) candidates.push(`(${tab})/${relPath}`);
390
+ if (locale) candidates.push(`${locale}/${relPath}`);
391
+ candidates.push(relPath);
392
+ for (const candidate of candidates) {
393
+ const meta = lookup[candidate.toLowerCase()];
394
+ if (meta) return meta;
256
395
  }
257
- return meta;
258
396
  };
259
- const getOrCreateNode = (parts, rootMap, directoryMeta) => {
397
+ const getOrCreateNode = (parts, rootMap, directoryMetaLookup, route) => {
260
398
  let currentMap = rootMap;
261
399
  let parentPath = "";
262
400
  let lastNode;
263
401
  for (const segment of parts) {
264
402
  const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment;
265
403
  if (!currentMap.has(segment)) {
266
- const meta = directoryMeta[currentRelPath] || {};
404
+ const meta = findDirectoryMeta(directoryMetaLookup, route, currentRelPath) || {};
267
405
  const newNode = {
268
406
  path: "#",
269
407
  title: meta.title || capitalize(segment),
270
408
  componentPath: "",
271
409
  filePath: "",
272
410
  icon: meta.icon,
273
- groupPosition: typeof meta.order === "number" ? meta.order : 999,
411
+ groupPosition: typeof meta.order === "number" ? meta.order : void 0,
274
412
  collapsible: meta.collapsible,
275
413
  collapsed: meta.collapsed,
276
414
  hasCustomTitle: meta.title !== void 0,
@@ -285,6 +423,20 @@ const getOrCreateNode = (parts, rootMap, directoryMeta) => {
285
423
  }
286
424
  return lastNode;
287
425
  };
426
+ /**
427
+ * Whether a route (or any of its nested children) matches the active path or
428
+ * the active route's file. Used for group-level active detection and for the
429
+ * `isActive` helper exposed by `useSidebar`.
430
+ */
431
+ function isRouteActive(route, activePath, activeRoute) {
432
+ if (!route?.path) return false;
433
+ const normalizedPath = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
434
+ if ((activePath.endsWith("/") ? activePath.slice(0, -1) : activePath) === normalizedPath) return true;
435
+ if (activeRoute?.filePath && route.filePath && activeRoute.filePath === route.filePath) return true;
436
+ if (route.routes?.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
437
+ if (route.subRoutes?.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
438
+ return false;
439
+ }
288
440
  const getRoutePosition = (r) => r.sidebarPosition ?? r.order ?? 999;
289
441
  const getNodePosition = (n) => n.sidebarPosition ?? n.groupPosition ?? 999;
290
442
  const finalizeTree = (nodes) => {
@@ -304,13 +456,19 @@ const finalizeTree = (nodes) => {
304
456
  };
305
457
  function useSidebar(routes) {
306
458
  const config = require_link.useConfig();
307
- const { pathname } = (0, react_router_dom.useLocation)();
308
- return (0, react.useMemo)(() => {
309
- const currentPath = require_link.normalizePath(pathname);
310
- const activeRoute = routes.find((r) => require_link.normalizePath(r.path) === currentPath);
311
- const activeTabId = activeRoute?.tab?.toLowerCase();
312
- 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));
313
- const directoryMeta = getCleanDirectoryMeta(config.directoryMeta);
459
+ const { pathname } = require_router.useLocation();
460
+ const { index } = require_link.useRoutesContext();
461
+ const currentPath = require_link.normalizePath(pathname);
462
+ const activeRoute = index.byPath.size > 0 ? index.byPath.get(currentPath) : routes.find((route) => require_link.normalizePath(route.path) === currentPath);
463
+ const configuredTabs = config.theme?.tabs || [];
464
+ const configuredTabIds = new Set(configuredTabs.map((tab) => tab.id.toLowerCase()));
465
+ const normalizedBase = require_link.normalizePath(config.base || "/docs");
466
+ const routeTabId = activeRoute?.tab?.toLowerCase();
467
+ const parsedCurrentRoute = require_router.parseUrlReference(currentPath, config, { kind: "doc" });
468
+ const activeTabId = (currentPath === normalizedBase || Boolean(activeRoute?.fallback) && parsedCurrentRoute.routePath === "/" || currentPath.startsWith(`${normalizedBase}/`) && parsedCurrentRoute.routePath === "/" ? configuredTabs[0]?.id.toLowerCase() : routeTabId && configuredTabIds.has(routeTabId) ? routeTabId : void 0) || void 0;
469
+ const sidebar = (0, react.useMemo)(() => {
470
+ const filteredRoutes = routes.filter((r) => !r.collection && !r.sidebarHidden && !r.fallback).filter((r) => !configuredTabs.length || r.tab?.toLowerCase() === activeTabId).sort((a, b) => getRoutePosition(a) - getRoutePosition(b));
471
+ const directoryMeta = getDirectoryMetaLookup(config.directoryMeta);
314
472
  const rootNodesMap = /* @__PURE__ */ new Map();
315
473
  const ungrouped = [];
316
474
  for (const route of filteredRoutes) {
@@ -320,13 +478,13 @@ function useSidebar(routes) {
320
478
  if (route.filePath) ungrouped.push(route);
321
479
  continue;
322
480
  }
323
- const containerNode = getOrCreateNode(parts, rootNodesMap, directoryMeta);
481
+ const containerNode = getOrCreateNode(parts, rootNodesMap, directoryMeta, route);
324
482
  if (isIndex) Object.assign(containerNode, {
325
483
  path: route.path,
326
484
  title: containerNode.hasCustomTitle ? containerNode.title : route.title || containerNode.title,
327
485
  icon: route.icon || containerNode.icon,
328
486
  badge: route.badge,
329
- sidebarPosition: route.sidebarPosition,
487
+ sidebarPosition: containerNode.groupPosition === void 0 ? route.sidebarPosition : void 0,
330
488
  frontmatter: route.frontmatter,
331
489
  filePath: route.filePath
332
490
  });
@@ -334,45 +492,93 @@ function useSidebar(routes) {
334
492
  }
335
493
  const finalizedTopNodes = finalizeTree(Array.from(rootNodesMap.values()));
336
494
  const groups = [];
337
- for (const node of finalizedTopNodes) if (node.subRoutes && node.subRoutes.length > 0) groups.push({
338
- slug: node.title.toLowerCase().replace(/\s+/g, "-"),
339
- title: node.title,
340
- icon: node.icon,
341
- path: node.path,
342
- filePath: node.filePath,
343
- routes: node.subRoutes,
344
- sidebarPosition: node.sidebarPosition ?? node.groupPosition ?? 999,
345
- collapsible: node.collapsible,
346
- collapsed: node.collapsed
347
- });
348
- else ungrouped.push(node);
495
+ for (const node of finalizedTopNodes) if (node.subRoutes && node.subRoutes.length > 0) {
496
+ const nodeWithMeta = node;
497
+ groups.push({
498
+ slug: node.title.toLowerCase().replace(/\s+/g, "-"),
499
+ title: node.title,
500
+ icon: node.icon,
501
+ path: node.path,
502
+ filePath: node.filePath,
503
+ routes: node.subRoutes,
504
+ sidebarPosition: node.sidebarPosition ?? node.groupPosition ?? 999,
505
+ collapsible: nodeWithMeta.collapsible,
506
+ collapsed: nodeWithMeta.collapsed
507
+ });
508
+ } else ungrouped.push(node);
509
+ const finalizedUngrouped = finalizeTree(ungrouped);
349
510
  return {
350
511
  groups,
351
- ungrouped: finalizeTree(ungrouped),
352
- activeRoute,
353
- activePath: currentPath,
354
- config
512
+ ungrouped: finalizedUngrouped,
513
+ merged: [...finalizedUngrouped.map((route) => ({
514
+ type: "link",
515
+ position: route.sidebarPosition ?? 999,
516
+ title: route.title,
517
+ route
518
+ })), ...groups.map((group) => ({
519
+ type: "group",
520
+ position: group.sidebarPosition ?? 999,
521
+ title: group.title,
522
+ group
523
+ }))].sort((a, b) => {
524
+ if (a.position !== b.position) return a.position - b.position;
525
+ if (a.type !== b.type) return a.type === "link" ? -1 : 1;
526
+ return a.title.localeCompare(b.title);
527
+ })
355
528
  };
356
529
  }, [
357
530
  routes,
358
531
  config,
359
- pathname
532
+ activeTabId,
533
+ configuredTabs.length
534
+ ]);
535
+ const isActive = (0, react.useCallback)((route) => isRouteActive(route, currentPath, activeRoute), [currentPath, activeRoute]);
536
+ const isGroupActive = (0, react.useCallback)((group) => group.routes.some((route) => isRouteActive(route, currentPath, activeRoute)), [currentPath, activeRoute]);
537
+ const tree = (0, react.useMemo)(() => {
538
+ const buildNode = (route, depth) => {
539
+ const children = route.routes || route.subRoutes || [];
540
+ return {
541
+ route,
542
+ depth,
543
+ isGroup: false,
544
+ isActive: isRouteActive(route, currentPath, activeRoute),
545
+ hasChildren: children.length > 0,
546
+ children: children.map((child) => buildNode(child, depth + 1))
547
+ };
548
+ };
549
+ const buildGroupNode = (group) => ({
550
+ route: group,
551
+ depth: 0,
552
+ isGroup: true,
553
+ isActive: isGroupActive(group),
554
+ hasChildren: true,
555
+ children: group.routes.map((route) => buildNode(route, 1))
556
+ });
557
+ return sidebar.merged.map((item) => item.type === "group" ? buildGroupNode(item.group) : buildNode(item.route, 0));
558
+ }, [
559
+ sidebar,
560
+ activeRoute,
561
+ currentPath,
562
+ isGroupActive
360
563
  ]);
564
+ return {
565
+ ...sidebar,
566
+ tree,
567
+ isActive,
568
+ isGroupActive,
569
+ activeRoute,
570
+ activePath: currentPath,
571
+ config
572
+ };
361
573
  }
362
574
 
363
- //#endregion
364
- //#region src/client/hooks/use-location.ts
365
- const useLocation = () => {
366
- return (0, react_router_dom.useLocation)();
367
- };
368
-
369
575
  //#endregion
370
576
  //#region src/client/hooks/use-search-highlight.ts
371
577
  /**
372
578
  * Hook to highlight search terms based on the 'hl' query parameter.
373
579
  */
374
580
  function useSearchHighlight(containerSelector = ".boltdocs-page") {
375
- const { search } = useLocation();
581
+ const { search } = require_router.useLocation();
376
582
  const query = new URLSearchParams(search).get("hl");
377
583
  (0, react.useEffect)(() => {
378
584
  if (!query) {
@@ -408,11 +614,7 @@ function useSearchHighlight(containerSelector = ".boltdocs-page") {
408
614
  observer.disconnect();
409
615
  clearHighlights(containerSelector);
410
616
  };
411
- }, [
412
- query,
413
- search,
414
- containerSelector
415
- ]);
617
+ }, [query, containerSelector]);
416
618
  }
417
619
  function clearHighlights(selector) {
418
620
  document.querySelectorAll(`${selector} mark[data-search-highlight]`).forEach((mark) => {
@@ -432,8 +634,11 @@ function highlightTerms(container, terms) {
432
634
  return NodeFilter.FILTER_ACCEPT;
433
635
  } });
434
636
  const nodes = [];
435
- let node;
436
- while (node = walker.nextNode()) nodes.push(node);
637
+ let node = walker.nextNode();
638
+ while (node) {
639
+ nodes.push(node);
640
+ node = walker.nextNode();
641
+ }
437
642
  const accentMap = {
438
643
  a: "[aáàäâã]",
439
644
  e: "[eéèëê]",
@@ -630,7 +835,7 @@ function AnchorProvider({ toc, single = false, observerOptions, children }) {
630
835
  return () => {
631
836
  observer.unwatch();
632
837
  };
633
- }, [observer]);
838
+ }, [observer, observerOptions]);
634
839
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ItemsContext.Provider, {
635
840
  value: items,
636
841
  children
@@ -638,53 +843,64 @@ function AnchorProvider({ toc, single = false, observerOptions, children }) {
638
843
  }
639
844
  function OnThisPage({ children, className }) {
640
845
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("nav", {
641
- className: require_link.cn("sticky top-navbar hidden xl:flex flex-col shrink-0", "w-toc", "py-4 pl-6 pr-4", className),
846
+ "data-otp-root": true,
847
+ className,
642
848
  children
643
849
  });
644
850
  }
645
851
  function OnThisPageHeader({ children, className, ...props }) {
646
852
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
647
- className: require_link.cn("mb-4 text-xs font-bold text-body", className),
853
+ className,
648
854
  ...props,
649
855
  children
650
856
  });
651
857
  }
652
- function OnThisPageContent({ children, className, ref, ...props }) {
858
+ function OnThisPageContent({ children, className, ref, fadeClassName, innerClassName, ...props }) {
653
859
  const internalRef = (0, react.useRef)(null);
654
- (0, react.useImperativeHandle)(ref, () => internalRef.current);
655
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
656
- ref: internalRef,
657
- className: require_link.cn("relative overflow-y-auto boltdocs-otp-content pb-12", "max-h-[70%]", className),
658
- style: {
659
- maskImage: "linear-gradient(to bottom, black 90%, transparent 100%)",
660
- WebkitMaskImage: "linear-gradient(to bottom, black 90%, transparent 100%)"
661
- },
860
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
861
+ ref: (0, react.useCallback)((node) => {
862
+ internalRef.current = node;
863
+ if (typeof ref === "function") ref(node);
864
+ else if (ref) ref.current = node;
865
+ }, [ref]),
866
+ "data-otp-content": true,
867
+ className: require_link.cn("relative isolate overflow-y-auto", className),
662
868
  ...props,
663
- children
869
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
870
+ className: require_link.cn("relative z-10", innerClassName),
871
+ children
872
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
873
+ "aria-hidden": "true",
874
+ "data-otp-fade": true,
875
+ className: require_link.cn("pointer-events-none sticky bottom-0 z-0 -mt-10 h-10 w-full", fadeClassName)
876
+ })]
664
877
  });
665
878
  }
666
879
  OnThisPageContent.displayName = "OnThisPageContent";
667
880
  function OnThisPageList({ children, className }) {
668
881
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
669
- className: require_link.cn("relative space-y-0.5 text-sm border-l border-subtle", className),
882
+ "data-otp-list": true,
883
+ className: require_link.cn("relative", className),
670
884
  children
671
885
  });
672
886
  }
673
887
  function OnThisPageItem({ level, children, className }) {
674
888
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
675
- className: require_link.cn(level === 3 && "pl-3", className),
889
+ "data-level": level || void 0,
890
+ className,
676
891
  children
677
892
  });
678
893
  }
679
894
  function OnThisPageLink({ children, href, active, onClick, className }) {
680
895
  const items = (0, react.use)(ItemsContext);
681
896
  const containerRef = (0, react.use)(ScrollContext);
897
+ const navigate = require_router.useNavigate();
682
898
  const id = href ? getItemId(href) : null;
683
899
  const anchorRef = (0, react.useRef)(null);
684
900
  const computedActive = active !== void 0 ? active : id && items ? !!items.find((i) => i.id === id)?.active : false;
685
901
  (0, react.useEffect)(() => {
686
902
  if (computedActive && anchorRef.current && containerRef?.current) (0, scroll_into_view_if_needed.default)(anchorRef.current, {
687
- behavior: "smooth",
903
+ behavior: "auto",
688
904
  block: "center",
689
905
  inline: "center",
690
906
  scrollMode: "if-needed",
@@ -692,14 +908,14 @@ function OnThisPageLink({ children, href, active, onClick, className }) {
692
908
  });
693
909
  }, [computedActive, containerRef]);
694
910
  const handleClick = (e) => {
695
- if (onClick) onClick(e);
696
- else if (href && href.startsWith("#")) {
697
- e.preventDefault();
698
- const elementId = href.slice(1);
699
- const el = document.getElementById(elementId);
700
- if (el) {
701
- el.scrollIntoView({ behavior: "smooth" });
702
- window.history.pushState(null, "", href);
911
+ if (onClick) {
912
+ onClick(e);
913
+ return;
914
+ }
915
+ if (href) {
916
+ if ((href.includes("#") ? href.split("#")[1] : null) && navigate !== require_router.defaultNavigate) {
917
+ e.preventDefault();
918
+ navigate(href);
703
919
  }
704
920
  }
705
921
  };
@@ -707,8 +923,9 @@ function OnThisPageLink({ children, href, active, onClick, className }) {
707
923
  ref: anchorRef,
708
924
  href,
709
925
  onClick: handleClick,
710
- "data-active": computedActive,
711
- className: require_link.cn("block py-0.5 pl-4 text-[13px] outline-none transition-colors", computedActive ? "text-primary-500" : "text-muted hover:text-body", className),
926
+ "data-active": computedActive || void 0,
927
+ "aria-current": computedActive ? "true" : void 0,
928
+ className,
712
929
  children
713
930
  });
714
931
  }
@@ -718,7 +935,9 @@ function OnThisPageIndicator({ style, className }) {
718
935
  opacity: 0,
719
936
  ...style
720
937
  });
938
+ const items = useItems();
721
939
  (0, react.useEffect)(() => {
940
+ if (items.length === 0) return;
722
941
  const parent = containerRef.current?.parentElement;
723
942
  if (!parent) return;
724
943
  const activeLinks = parent.querySelectorAll("a[data-active=\"true\"]");
@@ -740,10 +959,11 @@ function OnThisPageIndicator({ style, className }) {
740
959
  opacity: 0,
741
960
  ...style
742
961
  });
743
- }, [useItems(), style]);
962
+ }, [items, style]);
744
963
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
745
964
  ref: containerRef,
746
- className: require_link.cn("absolute -left-px w-0.5 rounded-full bg-primary-500", className),
965
+ "data-otp-indicator": true,
966
+ className: require_link.cn("absolute", className),
747
967
  style: {
748
968
  transition: "transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1), height 180ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 150ms",
749
969
  ...internalStyle
@@ -753,16 +973,18 @@ function OnThisPageIndicator({ style, className }) {
753
973
  /**
754
974
  * High-level automated list of toc items
755
975
  */
756
- function OnThisPageItems({ headings = [], className }) {
976
+ function OnThisPageItems({ headings = [], className, itemClassName, linkClassName, indicatorClassName }) {
757
977
  const activeIds = useActiveAnchors();
758
978
  if (headings.length === 0) return null;
759
979
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(OnThisPageList, {
760
980
  className,
761
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageIndicator, {}), headings.map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageItem, {
981
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageIndicator, { className: indicatorClassName }), headings.map((h) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageItem, {
762
982
  level: h.level,
983
+ className: itemClassName,
763
984
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageLink, {
764
985
  href: `#${h.id}`,
765
986
  active: activeIds.includes(h.id),
987
+ className: linkClassName,
766
988
  children: h.text
767
989
  })
768
990
  }, h.id))]
@@ -771,7 +993,7 @@ function OnThisPageItems({ headings = [], className }) {
771
993
  /**
772
994
  * High-level automated Table of Contents tree
773
995
  */
774
- function OnThisPageTree({ headings = [], className }) {
996
+ function OnThisPageTree({ headings = [], className, itemClassName, linkClassName, indicatorClassName, fadeClassName, contentClassName }) {
775
997
  const toc = (0, react.useMemo)(() => headings.map((h) => ({
776
998
  title: h.text,
777
999
  url: `#${h.id}`,
@@ -781,14 +1003,19 @@ function OnThisPageTree({ headings = [], className }) {
781
1003
  if (headings.length === 0) return null;
782
1004
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnchorProvider, {
783
1005
  toc,
784
- single: false,
1006
+ single: true,
785
1007
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollProvider, {
786
1008
  containerRef: scrollContainerRef,
787
1009
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageContent, {
788
1010
  ref: scrollContainerRef,
1011
+ fadeClassName,
1012
+ className: contentClassName,
789
1013
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OnThisPageItems, {
790
1014
  headings,
791
- className
1015
+ className,
1016
+ itemClassName,
1017
+ linkClassName,
1018
+ indicatorClassName
792
1019
  })
793
1020
  })
794
1021
  })
@@ -813,18 +1040,10 @@ function PageNav({ children, className }) {
813
1040
  });
814
1041
  }
815
1042
  function PageNavLink({ children, to, direction, className }) {
816
- const isNext = direction === "next";
817
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_link.Link, {
1043
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.Link, {
818
1044
  href: to,
819
- className: require_link.cn("flex items-center outline-none no-underline", isNext ? "justify-end" : "justify-start", className),
820
- children: [
821
- !isNext && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronLeft, { className: "shrink-0" }),
822
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
823
- className: "flex flex-col flex-1",
824
- children
825
- }),
826
- isNext && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, { className: "shrink-0" })
827
- ]
1045
+ className: require_link.cn("flex items-center outline-none no-underline", direction === "next" ? "justify-end" : "justify-start", className),
1046
+ children
828
1047
  });
829
1048
  }
830
1049
  function PageNavTitle({ children, className }) {
@@ -839,8 +1058,11 @@ function PageNavDescription({ children, className }) {
839
1058
  children
840
1059
  });
841
1060
  }
842
- function PageNavIcon({ children }) {
843
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
1061
+ function PageNavIcon({ children, className }) {
1062
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1063
+ className: require_link.cn("shrink-0", className),
1064
+ children
1065
+ });
844
1066
  }
845
1067
  PageNav.Root = PageNav;
846
1068
  PageNav.Link = PageNavLink;
@@ -852,27 +1074,20 @@ PageNav.Icon = PageNavIcon;
852
1074
  //#region src/client/components/primitives/sidebar.tsx
853
1075
  let sidebarScrollPos = 0;
854
1076
  function getIcon(iconName) {
855
- if (!iconName) return void 0;
856
- const icons = {
857
- ...require_link.icons_exports,
858
- ...virtual_boltdocs_icons.default
859
- };
860
- return icons[iconName] || icons[iconName + "Icon"] || void 0;
1077
+ return resolveIcon(iconName);
861
1078
  }
862
1079
  /**
863
- * Internal Badge component for links
1080
+ * Internal Badge component for links.
1081
+ *
1082
+ * Carries no colors — the theme styles it via `data-badge`.
864
1083
  */
865
- function Badge({ badge }) {
866
- const colors = {
867
- new: "bg-primary-500/10 text-primary-500 border border-primary-500/20",
868
- updated: "bg-emerald-500/10 text-emerald-500 border border-emerald-500/20",
869
- deprecated: "bg-danger-500/10 text-danger-500 border border-danger-500/20"
870
- };
871
- const text = typeof badge === "string" ? badge : badge?.text;
872
- if (!text) return null;
1084
+ function Badge({ badge, badgeClassName }) {
1085
+ const type = typeof badge === "string" ? badge : badge?.text;
1086
+ if (!type) return null;
873
1087
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
874
- className: require_link.cn("ml-auto flex h-5 items-center rounded-md text-[10px] font-bold px-1.5 py-0.5 uppercase tracking-wider", colors[text] || colors.new),
875
- children: text
1088
+ "data-badge": type,
1089
+ className: require_link.cn("ml-auto shrink-0", badgeClassName),
1090
+ children: type
876
1091
  });
877
1092
  }
878
1093
  /**
@@ -880,24 +1095,25 @@ function Badge({ badge }) {
880
1095
  */
881
1096
  function SidebarRoot({ children, className }) {
882
1097
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
883
- className: require_link.cn("hidden lg:flex flex-col w-sidebar sticky top-navbar h-[calc(100vh-var(--spacing-navbar))] border-r border-subtle bg-main", className),
1098
+ "data-sidebar-root": true,
1099
+ className,
884
1100
  children
885
1101
  });
886
1102
  }
887
1103
  /**
888
1104
  * Mobile Sidebar Modal
889
1105
  */
890
- function SidebarMobile({ children, className }) {
1106
+ function SidebarMobile({ children, className, overlayClassName, dialogClassName }) {
891
1107
  const { isSidebarOpen, closeSidebar } = useUI();
892
1108
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.ModalOverlay, {
893
1109
  isOpen: isSidebarOpen,
894
1110
  onOpenChange: (open) => !open && closeSidebar(),
895
1111
  isDismissable: true,
896
- className: require_link.cn("fixed inset-0 z-50 bg-black/20 backdrop-blur-sm lg:hidden", "entering:animate-in entering:fade-in exiting:animate-out exiting:fade-out duration-300"),
1112
+ className: require_link.cn("lg:hidden", overlayClassName),
897
1113
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.Modal, {
898
- className: require_link.cn("fixed top-0 left-0 bottom-0 w-80 bg-main border-r border-subtle shadow-2xl outline-none", "entering:animate-in entering:slide-in-from-left exiting:animate-out exiting:slide-out-to-left duration-300", className),
1114
+ className: require_link.cn("lg:hidden", className),
899
1115
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_aria_components.Dialog, {
900
- className: "h-full focus:outline-none outline-none flex flex-col",
1116
+ className: require_link.cn("h-full flex flex-col outline-none focus:outline-none", dialogClassName),
901
1117
  children
902
1118
  })
903
1119
  })
@@ -908,12 +1124,15 @@ function SidebarMobile({ children, className }) {
908
1124
  */
909
1125
  function SidebarHeader({ children, className }) {
910
1126
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
911
- className: require_link.cn("flex items-center justify-between p-4 border-b border-subtle", className),
1127
+ className,
912
1128
  children
913
1129
  });
914
1130
  }
915
1131
  /**
916
1132
  * Scrollable Content Wrapper
1133
+ *
1134
+ * The scroll container doubles as the semantic `<nav>` landmark. State and
1135
+ * scroll-position handling live here; all visuals belong to the theme.
917
1136
  */
918
1137
  function SidebarContent({ children, className }) {
919
1138
  const scrollRef = (0, react.useRef)(null);
@@ -929,107 +1148,143 @@ function SidebarContent({ children, className }) {
929
1148
  el.addEventListener("scroll", handleScroll, { passive: true });
930
1149
  return () => el.removeEventListener("scroll", handleScroll);
931
1150
  }, []);
932
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1151
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("nav", {
933
1152
  ref: scrollRef,
934
- className: require_link.cn("flex-1 overflow-y-auto p-4 pb-16 custom-scrollbar", className),
935
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("nav", {
936
- className: "flex flex-col gap-6",
937
- children
938
- })
1153
+ "data-sidebar-content": true,
1154
+ className,
1155
+ children
939
1156
  });
940
1157
  }
941
1158
  /**
942
1159
  * Navigation Group
943
1160
  */
944
- function SidebarGroup({ title, icon: Icon, children, className, collapsible = false, collapsed = false, active = false }) {
1161
+ function SidebarGroup({ title, icon: Icon, children, className, collapsible = false, collapsed = false, active = false, headerClassName, iconClassName, titleClassName, contentClassName, trailing, trailingClassName, renderTitle }) {
945
1162
  const [isOpen, setIsOpen] = (0, react.useState)(() => active || !collapsed);
946
- const [prevActive, setPrevActive] = (0, react.useState)(active);
947
- if (active !== prevActive) {
948
- setPrevActive(active);
1163
+ (0, react.useEffect)(() => {
949
1164
  if (active) setIsOpen(true);
950
- }
1165
+ }, [active]);
1166
+ const titleContent = title && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1167
+ className: require_link.cn("flex items-center gap-2", titleClassName),
1168
+ children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRenderer, {
1169
+ icon: Icon,
1170
+ size: 12,
1171
+ className: iconClassName
1172
+ }), title]
1173
+ });
951
1174
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1175
+ "data-group": true,
1176
+ "data-active": active || void 0,
1177
+ "data-collapsible": collapsible || void 0,
952
1178
  className,
953
- children: [title && (collapsible ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1179
+ children: [title && (renderTitle ? renderTitle({
1180
+ title,
1181
+ isOpen
1182
+ }) : collapsible ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
954
1183
  onClick: () => setIsOpen(!isOpen),
955
- 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",
956
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
957
- className: "flex items-center gap-2",
958
- children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 12 }), title]
959
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, {
1184
+ "aria-expanded": isOpen,
1185
+ "data-open": isOpen || void 0,
1186
+ className: require_link.cn("group flex w-full cursor-pointer items-center justify-between gap-2 text-left outline-none", headerClassName),
1187
+ children: [titleContent, trailing ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, {
960
1188
  size: 12,
961
- className: require_link.cn("transition-transform duration-200 text-muted/40 group-hover:text-body", isOpen && "rotate-90")
1189
+ "data-open": isOpen || void 0,
1190
+ className: require_link.cn("shrink-0 transition-transform duration-200", isOpen && "rotate-90")
962
1191
  })]
963
1192
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", {
964
- className: "px-2 mb-2 flex items-center gap-2 text-[11px] font-bold uppercase tracking-widest text-muted/50",
965
- children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 12 }), title]
1193
+ className: require_link.cn("flex items-center gap-2 outline-none", headerClassName),
1194
+ children: [titleContent, trailing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1195
+ className: require_link.cn("ml-auto", trailingClassName),
1196
+ children: trailing
1197
+ })]
966
1198
  })), (!collapsible || isOpen) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
967
- className: "flex flex-col gap-0.5",
1199
+ className: require_link.cn("flex flex-col", contentClassName),
968
1200
  children
969
1201
  })]
970
1202
  });
971
1203
  }
972
- function SidebarLink({ label, href, active, icon: Icon, badge, className }) {
1204
+ function SidebarLink({ label, href, active, icon: Icon, badge, className, iconClassName, labelClassName, badgeClassName, children, trailing, depth }) {
1205
+ const linkRef = (0, react.useRef)(null);
1206
+ (0, react.useEffect)(() => {
1207
+ if (active && linkRef.current?.scrollIntoView) linkRef.current.scrollIntoView({ block: "nearest" });
1208
+ }, [active]);
973
1209
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_link.Link, {
1210
+ ref: linkRef,
974
1211
  href,
975
- className: require_link.cn("group flex items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-sm transition-all outline-none", active ? "bg-primary-500/10 text-primary-500 font-medium shadow-sm" : "text-muted hover:bg-surface hover:text-body", className),
1212
+ "data-active": active || void 0,
1213
+ "data-depth": depth !== void 0 ? depth : void 0,
1214
+ "aria-current": active ? "page" : void 0,
1215
+ className: require_link.cn("group flex items-center gap-2.5 outline-none", className),
976
1216
  children: [
977
- Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
1217
+ Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRenderer, {
1218
+ icon: Icon,
978
1219
  size: 16,
979
- className: require_link.cn(active ? "text-primary-500" : "text-muted group-hover:text-body")
1220
+ className: require_link.cn("shrink-0", iconClassName)
980
1221
  }),
981
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
982
- className: "truncate",
1222
+ children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1223
+ className: require_link.cn("truncate", labelClassName),
983
1224
  children: label
984
1225
  }),
985
- badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, { badge })
1226
+ badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Badge, {
1227
+ badge,
1228
+ badgeClassName
1229
+ }),
1230
+ trailing
986
1231
  ]
987
1232
  });
988
1233
  }
989
1234
  /**
990
1235
  * Nested SubGroup
991
1236
  */
992
- function SidebarSubGroup({ label, href, active, icon: Icon, badge, isOpen, onToggle, children, className }) {
1237
+ function SidebarSubGroup({ label, href, active, icon: Icon, badge, isOpen, onToggle, children, className, rootClassName, linkClassName, contentClassName, toggleClassName, wrapperClassName, renderToggle, depth }) {
993
1238
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
994
- className: "flex flex-col gap-0.5",
1239
+ className: require_link.cn("flex flex-col", rootClassName),
995
1240
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
996
- className: "group relative flex items-center",
1241
+ className: require_link.cn("group relative flex items-center", wrapperClassName),
997
1242
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarLink, {
998
1243
  label,
999
1244
  href,
1000
1245
  active,
1001
1246
  icon: Icon,
1002
1247
  badge,
1003
- className: require_link.cn("flex-1 pr-8", className)
1004
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1248
+ depth,
1249
+ className: require_link.cn("flex-1 pr-8", linkClassName, className)
1250
+ }), renderToggle ? renderToggle({ isOpen }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1005
1251
  onClick: (e) => {
1006
1252
  e.preventDefault();
1007
1253
  e.stopPropagation();
1008
1254
  onToggle();
1009
1255
  },
1010
- className: "absolute right-1 p-1.5 text-muted hover:text-body transition-colors outline-none cursor-pointer",
1256
+ "aria-expanded": isOpen,
1257
+ "data-open": isOpen || void 0,
1258
+ className: require_link.cn("absolute right-1 cursor-pointer outline-none", toggleClassName),
1011
1259
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_link.ChevronRight, {
1012
1260
  size: 14,
1261
+ "data-open": isOpen || void 0,
1013
1262
  className: require_link.cn("transition-transform duration-200", isOpen && "rotate-90")
1014
1263
  })
1015
1264
  })]
1016
1265
  }), isOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1017
- className: "ml-4 pl-3 border-l border-subtle/50 mt-0.5 flex flex-col gap-0.5",
1266
+ className: require_link.cn("flex flex-col", contentClassName),
1018
1267
  children
1019
1268
  })]
1020
1269
  });
1021
1270
  }
1022
- function SidebarItem({ route, activePath, activeRoute, className }) {
1271
+ function SidebarItem({ route, activePath, activeRoute, className, depth = 0, renderItem, classNames }) {
1023
1272
  const localizedHref = require_link.useLocalizedTo(route.path);
1024
1273
  const isCurrent = activePath === (localizedHref.endsWith("/") ? localizedHref.slice(0, -1) : localizedHref) || !!activeRoute?.filePath && !!route.filePath && activeRoute.filePath === route.filePath;
1025
1274
  const hasChildren = !!route.routes?.length || !!route.subRoutes?.length;
1026
1275
  const children = route.routes || route.subRoutes;
1027
- const [isOpen, setIsOpen] = (0, react.useState)(() => activePath.startsWith(localizedHref) || !!activeRoute?.filePath && !!route.filePath && activeRoute.filePath === route.filePath);
1028
- const [prevActivePath, setPrevActivePath] = (0, react.useState)(activePath);
1029
- if (activePath !== prevActivePath) {
1030
- setPrevActivePath(activePath);
1031
- if (activePath.startsWith(localizedHref) || !!activeRoute?.filePath && !!route.filePath && activeRoute.filePath === route.filePath) setIsOpen(true);
1032
- }
1276
+ const shouldBeOpen = activePath.startsWith(localizedHref) || !!activeRoute?.filePath && !!route.filePath && activeRoute.filePath === route.filePath;
1277
+ const [isOpen, setIsOpen] = (0, react.useState)(() => shouldBeOpen);
1278
+ (0, react.useEffect)(() => {
1279
+ if (shouldBeOpen) setIsOpen(true);
1280
+ }, [shouldBeOpen]);
1281
+ const renderChild = (subRoute) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderItem ? renderItem(subRoute, depth + 1) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1282
+ route: subRoute,
1283
+ activePath,
1284
+ activeRoute,
1285
+ depth: depth + 1,
1286
+ classNames
1287
+ }) }, subRoute.path);
1033
1288
  if (hasChildren) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSubGroup, {
1034
1289
  label: route.title,
1035
1290
  href: route.path,
@@ -1038,12 +1293,13 @@ function SidebarItem({ route, activePath, activeRoute, className }) {
1038
1293
  badge: route.badge,
1039
1294
  isOpen,
1040
1295
  onToggle: () => setIsOpen(!isOpen),
1041
- className,
1042
- children: children?.map((subRoute) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1043
- route: subRoute,
1044
- activePath,
1045
- activeRoute
1046
- }, subRoute.path))
1296
+ depth,
1297
+ rootClassName: classNames?.subgroup,
1298
+ linkClassName: require_link.cn(classNames?.subgroupLink, className),
1299
+ contentClassName: classNames?.subgroupContent,
1300
+ toggleClassName: classNames?.toggle,
1301
+ wrapperClassName: classNames?.subgroupWrapper,
1302
+ children: children?.map(renderChild)
1047
1303
  });
1048
1304
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarLink, {
1049
1305
  label: route.title,
@@ -1051,67 +1307,74 @@ function SidebarItem({ route, activePath, activeRoute, className }) {
1051
1307
  active: isCurrent,
1052
1308
  icon: getIcon(route.icon),
1053
1309
  badge: route.badge,
1054
- className
1310
+ depth,
1311
+ className: require_link.cn(classNames?.item, className),
1312
+ iconClassName: classNames?.itemIcon,
1313
+ labelClassName: classNames?.itemLabel,
1314
+ badgeClassName: classNames?.itemBadge
1055
1315
  });
1056
1316
  }
1057
- const isRouteActive = (route, activePath, activeRoute) => {
1058
- const normalizedPath = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
1059
- if ((activePath.endsWith("/") ? activePath.slice(0, -1) : activePath) === normalizedPath) return true;
1060
- if (activeRoute?.filePath && route.filePath && activeRoute.filePath === route.filePath) return true;
1061
- if (route.routes && route.routes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
1062
- if (route.subRoutes && route.subRoutes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
1063
- return false;
1064
- };
1065
- function SidebarItems({ routes, className }) {
1066
- const { groups, ungrouped, activePath, activeRoute } = useSidebar(routes);
1067
- const mergedItems = [...ungrouped.map((route) => ({
1068
- type: "link",
1069
- position: route.sidebarPosition ?? 999,
1070
- title: route.title,
1071
- route
1072
- })), ...groups.map((group) => ({
1073
- type: "group",
1074
- position: group.sidebarPosition ?? 999,
1075
- title: group.title,
1076
- group
1077
- }))].sort((a, b) => {
1078
- if (a.position !== b.position) return a.position - b.position;
1079
- if (a.type !== b.type) return a.type === "link" ? -1 : 1;
1080
- return a.title.localeCompare(b.title);
1081
- });
1317
+ function SidebarItems({ routes, className, classNames, componentItem, componentGroup }) {
1318
+ const { merged, activePath, activeRoute, isGroupActive } = useSidebar(routes);
1319
+ const renderItem = (route, depth) => {
1320
+ if (componentItem) return componentItem({
1321
+ route,
1322
+ activePath,
1323
+ activeRoute,
1324
+ isActive: isRouteActive(route, activePath, activeRoute),
1325
+ depth
1326
+ });
1327
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1328
+ route,
1329
+ activePath,
1330
+ activeRoute,
1331
+ depth,
1332
+ renderItem,
1333
+ classNames
1334
+ });
1335
+ };
1082
1336
  const renderedElements = [];
1083
1337
  let currentUngrouped = [];
1084
1338
  const pushUngrouped = () => {
1085
1339
  if (currentUngrouped.length > 0) {
1086
1340
  const routesToRender = [...currentUngrouped];
1087
- renderedElements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, { children: routesToRender.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1088
- route,
1089
- activePath,
1090
- activeRoute
1091
- }, route.path)) }, `ungrouped-${routesToRender[0].path}`));
1341
+ renderedElements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, {
1342
+ className: classNames?.group,
1343
+ headerClassName: classNames?.groupHeader,
1344
+ iconClassName: classNames?.groupIcon,
1345
+ titleClassName: classNames?.groupTitle,
1346
+ contentClassName: classNames?.groupContent,
1347
+ children: routesToRender.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderItem(route, 0) }, route.path))
1348
+ }, `ungrouped-${routesToRender[0]?.path || "root"}`));
1092
1349
  currentUngrouped = [];
1093
1350
  }
1094
1351
  };
1095
- for (const item of mergedItems) if (item.type === "link") currentUngrouped.push(item.route);
1352
+ for (const item of merged) if (item.type === "link") currentUngrouped.push(item.route);
1096
1353
  else {
1097
1354
  pushUngrouped();
1098
- const isGroupActive = item.group.routes.some((route) => isRouteActive(route, activePath, activeRoute));
1099
- renderedElements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, {
1355
+ const groupActive = isGroupActive(item.group);
1356
+ const groupChildren = item.group.routes.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderItem(route, 1) }, route.path));
1357
+ renderedElements.push(componentGroup ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: componentGroup({
1358
+ group: item.group,
1359
+ isGroupActive: groupActive,
1360
+ children: groupChildren
1361
+ }) }, item.group.title) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, {
1100
1362
  title: item.group.title,
1101
1363
  icon: getIcon(item.group.icon),
1102
1364
  collapsible: item.group.collapsible,
1103
1365
  collapsed: item.group.collapsed,
1104
- active: isGroupActive,
1105
- children: item.group.routes.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
1106
- route,
1107
- activePath,
1108
- activeRoute
1109
- }, route.path))
1366
+ active: groupActive,
1367
+ className: classNames?.group,
1368
+ headerClassName: classNames?.groupHeader,
1369
+ iconClassName: classNames?.groupIcon,
1370
+ titleClassName: classNames?.groupTitle,
1371
+ contentClassName: classNames?.groupContent,
1372
+ children: groupChildren
1110
1373
  }, item.group.title));
1111
1374
  }
1112
1375
  pushUngrouped();
1113
1376
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1114
- className: require_link.cn("flex flex-col gap-6", className),
1377
+ className,
1115
1378
  children: renderedElements
1116
1379
  });
1117
1380
  }
@@ -1168,12 +1431,13 @@ function Content({ children, className, style }) {
1168
1431
  children
1169
1432
  });
1170
1433
  }
1171
- function ContentMdx({ children, className, style }) {
1434
+ function ContentMdx({ children, className, style, contentClassName, contentStyle }) {
1172
1435
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1173
- className: require_link.cn("boltdocs-page w-full pt-4 pb-20 px-4 sm:px-8", className),
1436
+ className: require_link.cn("boltdocs-page w-full", className),
1174
1437
  style,
1175
1438
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchHighlight, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1176
- className: "mx-auto w-full max-w-3xl sm:max-w-4xl lg:max-w-5xl",
1439
+ className: require_link.cn("mx-auto w-full max-w-3xl sm:max-w-4xl lg:max-w-5xl", contentClassName),
1440
+ style: contentStyle,
1177
1441
  children
1178
1442
  })]
1179
1443
  });
@@ -1185,19 +1449,11 @@ function Header({ children, className, style }) {
1185
1449
  children
1186
1450
  });
1187
1451
  }
1188
- function Footer({ children, className, style }) {
1189
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1190
- className: require_link.cn("mt-20", className),
1191
- style,
1192
- children
1193
- });
1194
- }
1195
1452
  const DocsLayout = Object.assign(DocsLayoutRoot, {
1196
1453
  Body,
1197
1454
  Content,
1198
1455
  ContentMdx,
1199
- Header,
1200
- Footer
1456
+ Header
1201
1457
  });
1202
1458
 
1203
1459
  //#endregion
@@ -1225,6 +1481,12 @@ Object.defineProperty(exports, 'DocsLayout', {
1225
1481
  return DocsLayout;
1226
1482
  }
1227
1483
  });
1484
+ Object.defineProperty(exports, 'IconRenderer', {
1485
+ enumerable: true,
1486
+ get: function () {
1487
+ return IconRenderer;
1488
+ }
1489
+ });
1228
1490
  Object.defineProperty(exports, 'Menu', {
1229
1491
  enumerable: true,
1230
1492
  get: function () {
@@ -1339,6 +1601,12 @@ Object.defineProperty(exports, 'UIProvider', {
1339
1601
  return UIProvider;
1340
1602
  }
1341
1603
  });
1604
+ Object.defineProperty(exports, 'resolveIcon', {
1605
+ enumerable: true,
1606
+ get: function () {
1607
+ return resolveIcon;
1608
+ }
1609
+ });
1342
1610
  Object.defineProperty(exports, 'useActiveAnchor', {
1343
1611
  enumerable: true,
1344
1612
  get: function () {
@@ -1357,12 +1625,6 @@ Object.defineProperty(exports, 'useItems', {
1357
1625
  return useItems;
1358
1626
  }
1359
1627
  });
1360
- Object.defineProperty(exports, 'useLocation', {
1361
- enumerable: true,
1362
- get: function () {
1363
- return useLocation;
1364
- }
1365
- });
1366
1628
  Object.defineProperty(exports, 'useSearchHighlight', {
1367
1629
  enumerable: true,
1368
1630
  get: function () {