boltdocs 2.7.11 → 2.8.1

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 (85) hide show
  1. package/README.md +2 -54
  2. package/dist/banner-3N4Jd_L9.d.ts +100 -0
  3. package/dist/banner-MynZD_Ox.d.cts +100 -0
  4. package/dist/cache-BMUyNiiA.mjs +6 -0
  5. package/dist/cache-CKm45d2w.cjs +6 -0
  6. package/dist/client/index.cjs +782 -443
  7. package/dist/client/index.d.cts +86 -110
  8. package/dist/client/index.d.ts +87 -111
  9. package/dist/client/index.js +773 -439
  10. package/dist/client/mdx.cjs +8 -3
  11. package/dist/client/mdx.d.cts +39 -93
  12. package/dist/client/mdx.d.ts +38 -93
  13. package/dist/client/mdx.js +3 -3
  14. package/dist/client/primitives.cjs +7 -7
  15. package/dist/client/primitives.d.cts +411 -347
  16. package/dist/client/primitives.d.ts +411 -347
  17. package/dist/client/primitives.js +4 -4
  18. package/dist/client/theme/neutral.css +1 -1
  19. package/dist/{docs-layout-BXHV0xw_.cjs → docs-layout-CwCq42Zt.cjs} +95 -178
  20. package/dist/{docs-layout-DwFndmj5.js → docs-layout-Dn6S5g59.js} +99 -163
  21. package/dist/doctor-BArviV8X.cjs +28 -0
  22. package/dist/doctor-CgLA7_Uv.mjs +28 -0
  23. package/dist/{doctor-CrytFkqW.cjs → doctor-DyNUVe96.cjs} +1 -1
  24. package/dist/{routes-DP1vmWRj.cjs → doctor-aN_leTbh.mjs} +1 -1
  25. package/dist/{generator-ClVanhvi.mjs → generator-BHCrLU6h.mjs} +2 -2
  26. package/dist/{generator-CHqxiQhF.cjs → generator-CC2yHzhZ.cjs} +2 -2
  27. package/dist/{icons-dev-3cZMyt8r.cjs → icons-dev-DvJ-hh9x.cjs} +116 -111
  28. package/dist/{icons-dev-Df8OQ481.js → icons-dev-Oju24Wjp.js} +120 -114
  29. package/dist/{image-DtrI2cw3.cjs → image-Ch4-GxdO.cjs} +13 -13
  30. package/dist/{image-jxPb-2iV.js → image-Do8V9PCW.js} +13 -13
  31. package/dist/{mdx-UTTLFWJq.js → mdx-5oeCOFhH.js} +111 -81
  32. package/dist/{mdx-BdWkJTeB.cjs → mdx-BGM7LjW5.cjs} +109 -97
  33. package/dist/node/cli-entry.cjs +3 -1
  34. package/dist/node/cli-entry.mjs +3 -1
  35. package/dist/node/index.cjs +1 -1
  36. package/dist/node/index.d.cts +258 -152
  37. package/dist/node/index.d.mts +258 -150
  38. package/dist/node/index.mjs +1 -1
  39. package/dist/node/routes/worker.cjs +1 -1
  40. package/dist/node/routes/worker.mjs +1 -1
  41. package/dist/node-CefnjllX.cjs +159 -0
  42. package/dist/node-DruKROCt.mjs +159 -0
  43. package/dist/package-CmP_9rJ8.cjs +6 -0
  44. package/dist/{package-K0zsjGIz.mjs → package-DpbnBMR1.mjs} +1 -1
  45. package/dist/parser-B0YtJPDz.mjs +6 -0
  46. package/dist/parser-B7-6PyQz.cjs +6 -0
  47. package/dist/{parser-Aq8LoH-0.cjs → parser-BzB-zCkF.cjs} +1 -1
  48. package/dist/routes-ChS-zgzh.mjs +6 -0
  49. package/dist/routes-DJNJ-rTt.cjs +6 -0
  50. package/dist/routes-DiYC4nD2.cjs +6 -0
  51. package/dist/routes-rKlxFkqq.mjs +6 -0
  52. package/dist/{search-dialog-C7xuvyNk.cjs → search-dialog-BXVoecTx.cjs} +175 -78
  53. package/dist/{search-dialog-BwkDuI9R.cjs → search-dialog-BYhOov4S.cjs} +118 -7
  54. package/dist/{search-dialog-D-DDN7zJ.js → search-dialog-C09riYmx.js} +113 -8
  55. package/dist/{search-dialog-CIQg6k8c.cjs → search-dialog-CUeAfy-8.cjs} +1 -1
  56. package/dist/{search-dialog-BNF10tDl.js → search-dialog-D8gLkhUV.js} +158 -80
  57. package/dist/{search-dialog-BHuIiUC6.js → search-dialog-DHc_8FFX.js} +1 -1
  58. package/dist/{sidebar-CyZS9YOm.d.ts → sidebar-DNq4_ZAa.d.ts} +117 -51
  59. package/dist/{sidebar-CcBkrm06.d.cts → sidebar-Dlkgbxs6.d.cts} +117 -51
  60. package/dist/utils-BYITg7T5.mjs +7 -0
  61. package/dist/utils-Cjmx1hhk.cjs +7 -0
  62. package/dist/worker-pool-CtqklOXq.cjs +6 -0
  63. package/dist/worker-pool-k0DY6k8T.mjs +6 -0
  64. package/package.json +3 -3
  65. package/src/shared/config-utils.ts +4 -0
  66. package/src/shared/types.ts +52 -6
  67. package/dist/cache-Ba-DZQNH.cjs +0 -6
  68. package/dist/cache-BuMZ58L5.mjs +0 -6
  69. package/dist/cards-BakZPTz9.d.ts +0 -30
  70. package/dist/cards-CQn9mXZS.d.cts +0 -30
  71. package/dist/doctor-Be7Ly1oM.mjs +0 -21
  72. package/dist/doctor-jMxWZyLJ.cjs +0 -21
  73. package/dist/node-BSM4qcDK.cjs +0 -111
  74. package/dist/node-BspZN3R2.mjs +0 -111
  75. package/dist/package-DIIrjuWI.cjs +0 -6
  76. package/dist/parser-CdNbqN5y.cjs +0 -6
  77. package/dist/parser-nE792MLO.mjs +0 -6
  78. package/dist/rolldown-runtime-fkIsjY3S.mjs +0 -6
  79. package/dist/routes-2k3tbUmC.cjs +0 -6
  80. package/dist/routes-CpxZIsMM.mjs +0 -6
  81. package/dist/utils-CG65J0Sc.mjs +0 -7
  82. package/dist/utils-CKunkU96.cjs +0 -7
  83. package/dist/worker-pool-CGn7DrLb.mjs +0 -6
  84. package/dist/worker-pool-Crbqgw5R.cjs +0 -6
  85. /package/dist/{meta-loader-CWg2gnbY.mjs → meta-loader-DzwDFtdT.mjs} +0 -0
@@ -3,51 +3,321 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
6
- import { $ as useBoltdocsContext, B as Menu, C as ArrowLeft, F as Home, H as Moon, J as X, K as Sun, L as Languages, O as CircleHelp, Q as BoltdocsProvider, T as ChevronDown, V as Monitor, W as Pencil, X as useRoutes, Y as icons_exports, Z as normalizePath$1, _ as Link, b as useLocalizedTo, et as RoutesProvider, j as ExternalLink, k as Copy, nt as useConfig, q as TextAlignStart, s as Github, tt as ConfigContext, w as Check, y as cn } from "../icons-dev-Df8OQ481.js";
7
- import { D as ErrorBoundary$1, E as Tabs$1, M as Button$1, N as Breadcrumbs$2, T as useUI, _ as OnThisPage$1, h as PageNav$1, i as Sidebar$1, j as ButtonGroup, k as Menu$1, m as useSidebar, n as useSearchHighlight, r as useLocation, t as DocsLayout$2, w as UIProvider } from "../docs-layout-DwFndmj5.js";
8
- import { c as ThemeProvider, l as useTheme } from "../image-jxPb-2iV.js";
9
- import { n as Navbar$1 } from "../search-dialog-BNF10tDl.js";
10
- import { n as useSearch, t as SearchDialog } from "../search-dialog-D-DDN7zJ.js";
11
- import { a as reactToText, i as copyToClipboard, n as Cards, r as Card, t as mdx_components_default } from "../mdx-UTTLFWJq.js";
6
+ import { $ as ConfigContext, B as Languages, C as RoutesProvider, D as Check, E as ArrowLeft, G as Moon, M as Copy, O as ChevronDown, P as ExternalLink, Q as icons_exports, R as Home, S as normalizePath, U as Menu, W as Monitor, X as TextAlignStart, Y as Sun, Z as X, _ as Link$1, b as useLocalizedTo, et as useConfig, j as CircleHelp, nt as useBoltdocsContext, q as Pencil, s as Github, tt as BoltdocsProvider, x as useRoutes, y as cn } from "../icons-dev-Oju24Wjp.js";
7
+ import { c as ThemeProvider, l as useTheme } from "../image-Do8V9PCW.js";
8
+ import { n as copyToClipboard, t as mdx_components_default } from "../mdx-5oeCOFhH.js";
9
+ import { A as useUI, D as ButtonGroup, O as Breadcrumbs$2, T as Menu$1, _ as OnThisPage$1, h as PageNav$1, i as Sidebar$1, k as UIProvider, m as useSidebar, n as useSearchHighlight, r as useLocation, t as DocsLayout$1, w as Tabs$1 } from "../docs-layout-Dn6S5g59.js";
10
+ import { a as Button$1, n as Navbar$1, r as ErrorBoundary$1 } from "../search-dialog-D8gLkhUV.js";
11
+ import { n as useSearch, r as InternalErrorBoundary, t as SearchDialog } from "../search-dialog-C09riYmx.js";
12
12
  import { ViteReactSSG } from "@bdocs/ssg";
13
- import { Outlet, useLoaderData, useLocation as useLocation$1, useNavigate } from "react-router-dom";
14
- import { Suspense, createContext, lazy, use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
13
+ import { Suspense, createContext, isValidElement, lazy, use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
+ import { Link, Outlet, useLoaderData, useLocation as useLocation$1, useMatches, useNavigate } from "react-router-dom";
15
15
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
16
- import { Button } from "react-aria-components";
17
- import virtualIcons from "virtual:boltdocs-icons";
18
16
  import * as ReactHelmetAsync from "react-helmet-async";
17
+ import { Button } from "react-aria-components";
19
18
  import virtualCustomComponents from "virtual:boltdocs-mdx-components";
19
+ import virtualIcons from "virtual:boltdocs-icons";
20
20
  import UserLayout from "virtual:boltdocs-layout";
21
21
 
22
22
  //#region src/client/app/mdx-components-context.tsx
23
23
  const MDX_COMPONENTS_CONTEXT_SYMBOL = Symbol.for("__BDOCS_MDX_COMPONENTS_CONTEXT__");
24
- const MDX_COMPONENTS_INSTANCE_SYMBOL = Symbol.for("__BDOCS_MDX_COMPONENTS_INSTANCE__");
25
- const MdxComponentsContext = globalThis[MDX_COMPONENTS_CONTEXT_SYMBOL] || (globalThis[MDX_COMPONENTS_CONTEXT_SYMBOL] = createContext({}));
24
+ const registry = globalThis;
25
+ if (!registry[MDX_COMPONENTS_CONTEXT_SYMBOL]) registry[MDX_COMPONENTS_CONTEXT_SYMBOL] = createContext({});
26
+ const MdxComponentsContext = registry[MDX_COMPONENTS_CONTEXT_SYMBOL];
26
27
  function useMdxComponents() {
27
- const context = use(MdxComponentsContext);
28
- if ((!context || Object.keys(context).length === 0) && globalThis[MDX_COMPONENTS_INSTANCE_SYMBOL]) return globalThis[MDX_COMPONENTS_INSTANCE_SYMBOL];
29
- return context;
28
+ return use(MdxComponentsContext);
30
29
  }
31
30
  function MdxComponentsProvider({ components, children }) {
32
31
  const processedComponents = useMemo(() => {
33
32
  const processed = {};
34
33
  const frontmatter = {};
34
+ let hasFrontmatter = false;
35
35
  Object.entries(components).forEach(([key, value]) => {
36
36
  if (key.startsWith("Frontmatter_")) {
37
37
  const cleanKey = key.slice(12);
38
38
  frontmatter[cleanKey] = value;
39
+ hasFrontmatter = true;
39
40
  } else processed[key] = value;
40
41
  });
41
- processed.Frontmatter = frontmatter;
42
+ if (hasFrontmatter) processed.Frontmatter = frontmatter;
42
43
  return processed;
43
44
  }, [components]);
44
- if (typeof globalThis !== "undefined") globalThis[MDX_COMPONENTS_INSTANCE_SYMBOL] = processedComponents;
45
45
  return /* @__PURE__ */ jsx(MdxComponentsContext.Provider, {
46
46
  value: processedComponents,
47
47
  children
48
48
  });
49
49
  }
50
50
 
51
+ //#endregion
52
+ //#region src/client/app/helmet-compat.tsx
53
+ const mod = ReactHelmetAsync;
54
+ /**
55
+ * The `<Helmet>` component, resolved across CJS/ESM module shapes.
56
+ * Falls back to a transparent fragment wrapper if the module cannot be resolved.
57
+ */
58
+ const Helmet = mod.Helmet || mod.default?.Helmet || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
59
+ /**
60
+ * The `<HelmetProvider>` component, resolved across CJS/ESM module shapes.
61
+ * Falls back to a transparent fragment wrapper if the module cannot be resolved.
62
+ */
63
+ const HelmetProvider = mod.HelmetProvider || mod.default?.HelmetProvider || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
64
+
65
+ //#endregion
66
+ //#region src/client/app/scroll-handler.tsx
67
+ /**
68
+ * Handles scroll restoration and hash scrolling on navigation.
69
+ * It ensures the page scrolls to top on pathname changes,
70
+ * or specifically to an anchor element if a hash is present.
71
+ */
72
+ function ScrollHandler() {
73
+ const { pathname, hash } = useLocation$1();
74
+ const handleScroll = (behavior = "auto") => {
75
+ const container = document.querySelector(".boltdocs-content") || window;
76
+ const getScrollTop = () => {
77
+ if (container === window) return window.scrollY;
78
+ return container.scrollTop;
79
+ };
80
+ const scrollTo = (top, scrollBehavior) => {
81
+ if (container === window) window.scrollTo({
82
+ top,
83
+ behavior: scrollBehavior
84
+ });
85
+ else container.scrollTo({
86
+ top,
87
+ behavior: scrollBehavior
88
+ });
89
+ };
90
+ if (hash) {
91
+ const id = hash.replace("#", "");
92
+ const element = document.getElementById(id);
93
+ if (element) {
94
+ const offset = 80;
95
+ const containerTop = container === window ? 0 : container.getBoundingClientRect().top;
96
+ scrollTo(element.getBoundingClientRect().top - containerTop - offset + getScrollTop(), behavior);
97
+ return true;
98
+ }
99
+ }
100
+ scrollTo(0, behavior);
101
+ return false;
102
+ };
103
+ useLayoutEffect(() => {
104
+ handleScroll("auto");
105
+ }, [pathname, hash]);
106
+ useEffect(() => {
107
+ handleScroll("auto");
108
+ const rafId = requestAnimationFrame(() => {
109
+ handleScroll("auto");
110
+ window.dispatchEvent(new Event("resize"));
111
+ });
112
+ return () => cancelAnimationFrame(rafId);
113
+ }, [pathname, hash]);
114
+ return null;
115
+ }
116
+
117
+ //#endregion
118
+ //#region src/client/components/ui-base/not-found.tsx
119
+ function NotFound() {
120
+ return /* @__PURE__ */ jsx("div", {
121
+ className: "flex items-center justify-center min-h-[65vh] text-center px-4",
122
+ children: /* @__PURE__ */ jsxs("div", {
123
+ className: "space-y-6 max-w-md mx-auto p-8 border border-subtle bg-surface rounded-2xl shadow-xs",
124
+ children: [
125
+ /* @__PURE__ */ jsx("span", {
126
+ className: "block text-7xl font-extrabold tracking-tight text-primary-500",
127
+ children: "404"
128
+ }),
129
+ /* @__PURE__ */ jsxs("div", {
130
+ className: "space-y-2",
131
+ children: [/* @__PURE__ */ jsx("h1", {
132
+ className: "text-xl font-bold text-body",
133
+ children: "Page Not Found"
134
+ }), /* @__PURE__ */ jsx("p", {
135
+ className: "text-sm text-muted leading-relaxed",
136
+ children: "The page you're looking for doesn't exist or has been moved."
137
+ })]
138
+ }),
139
+ /* @__PURE__ */ jsxs(Link$1, {
140
+ href: "/",
141
+ className: "inline-flex items-center gap-2 rounded-xl border border-subtle bg-main px-6 py-2.5 text-xs font-semibold text-body hover:bg-primary-50/50 hover:border-primary-500/50 transition-all duration-300 outline-none select-none",
142
+ children: [/* @__PURE__ */ jsx(ArrowLeft, { size: 14 }), " Go to Home"]
143
+ })
144
+ ]
145
+ })
146
+ });
147
+ }
148
+
149
+ //#endregion
150
+ //#region src/client/app/mdx-component.tsx
151
+ const mdxComponentsDefault = {
152
+ ...mdx_components_default,
153
+ NotFound,
154
+ "404": NotFound
155
+ };
156
+
157
+ //#endregion
158
+ //#region src/client/ssg/boltdocs-shell.tsx
159
+ /**
160
+ * Updates the HTML lang and dir attributes based on the current locale configuration.
161
+ */
162
+ function I18nUpdater({ config }) {
163
+ const { currentLocale } = useBoltdocsContext();
164
+ useEffect(() => {
165
+ if (!config.i18n || typeof document === "undefined") return;
166
+ const locale = currentLocale || config.i18n.defaultLocale;
167
+ const localeConfig = config.i18n.localeConfigs?.[locale];
168
+ document.documentElement.lang = localeConfig?.htmlLang || locale || "en";
169
+ document.documentElement.dir = localeConfig?.direction || "ltr";
170
+ }, [currentLocale, config.i18n]);
171
+ return null;
172
+ }
173
+ function StoreSync({ config, routeMap }) {
174
+ const location = useLocation$1();
175
+ const { setLocale, setVersion } = useBoltdocsContext();
176
+ useEffect(() => {
177
+ const currentPath = normalizePath(location.pathname);
178
+ const matchedRoute = routeMap.get(currentPath);
179
+ if (matchedRoute) {
180
+ if (config.i18n) setLocale(matchedRoute.locale || config.i18n.defaultLocale);
181
+ if (config.versions) setVersion(matchedRoute.version || config.versions.defaultVersion);
182
+ }
183
+ }, [
184
+ location.pathname,
185
+ config,
186
+ routeMap,
187
+ setLocale,
188
+ setVersion
189
+ ]);
190
+ return null;
191
+ }
192
+ function BoltdocsShell({ config, routes, components = {} }) {
193
+ const allComponents = useMemo(() => ({
194
+ ...mdxComponentsDefault,
195
+ ...virtualCustomComponents,
196
+ ...components
197
+ }), [components]);
198
+ const { pathname } = useLocation$1();
199
+ const currentPath = useMemo(() => normalizePath(pathname || "/"), [pathname]);
200
+ const routeMap = useMemo(() => {
201
+ const map = /* @__PURE__ */ new Map();
202
+ for (const r of routes) {
203
+ const key = normalizePath(r.path === "" ? "/" : r.path);
204
+ map.set(key, r);
205
+ }
206
+ return map;
207
+ }, [routes]);
208
+ const initialData = useMemo(() => {
209
+ const matched = routeMap.get(currentPath);
210
+ let initLocale;
211
+ let initVersion;
212
+ if (matched) {
213
+ if (config.i18n) initLocale = matched.locale || config.i18n.defaultLocale;
214
+ if (config.versions) initVersion = matched.version || config.versions.defaultVersion;
215
+ }
216
+ return {
217
+ initLocale,
218
+ initVersion
219
+ };
220
+ }, [
221
+ currentPath,
222
+ config,
223
+ routeMap
224
+ ]);
225
+ return /* @__PURE__ */ jsx(HelmetProvider, { children: /* @__PURE__ */ jsx(RoutesProvider, {
226
+ routes,
227
+ children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(UIProvider, { children: /* @__PURE__ */ jsx(MdxComponentsProvider, {
228
+ components: allComponents,
229
+ children: /* @__PURE__ */ jsxs(ConfigContext.Provider, {
230
+ value: config,
231
+ children: [/* @__PURE__ */ jsx(ScrollHandler, {}), /* @__PURE__ */ jsxs(BoltdocsProvider, {
232
+ initialLocale: initialData.initLocale,
233
+ initialVersion: initialData.initVersion,
234
+ children: [
235
+ /* @__PURE__ */ jsx(StoreSync, {
236
+ config,
237
+ routeMap
238
+ }),
239
+ /* @__PURE__ */ jsx(I18nUpdater, { config }),
240
+ /* @__PURE__ */ jsx(InternalErrorBoundary, { children: /* @__PURE__ */ jsx(Outlet, {}) })
241
+ ]
242
+ })]
243
+ })
244
+ }) }) })
245
+ }) });
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/client/hooks/use-merged-components.ts
250
+ function useMergedComponents(propComponents) {
251
+ const contextComponents = useMdxComponents();
252
+ return useMemo(() => {
253
+ if (!propComponents) return contextComponents;
254
+ const merged = { ...contextComponents };
255
+ const mergedFrontmatter = { ...contextComponents.Frontmatter || {} };
256
+ let hasPropFrontmatter = false;
257
+ Object.entries(propComponents).forEach(([key, value]) => {
258
+ if (key.startsWith("Frontmatter_")) {
259
+ const cleanKey = key.slice(12);
260
+ mergedFrontmatter[cleanKey] = value;
261
+ hasPropFrontmatter = true;
262
+ } else if (key === "Frontmatter" && value && typeof value === "object") {
263
+ Object.assign(mergedFrontmatter, value);
264
+ hasPropFrontmatter = true;
265
+ } else merged[key] = value;
266
+ });
267
+ if (hasPropFrontmatter || contextComponents.Frontmatter) merged.Frontmatter = mergedFrontmatter;
268
+ return merged;
269
+ }, [contextComponents, propComponents]);
270
+ }
271
+
272
+ //#endregion
273
+ //#region src/client/app/doc-page.tsx
274
+ /**
275
+ * DocPage renders the MDX content and page-specific metadata.
276
+ * It is rendered inside the Outlet of DocsLayout.
277
+ */
278
+ function DocPage({ route, content: Content, mdxComponents: propComponents }) {
279
+ const allComponents = useMergedComponents(propComponents);
280
+ const { LastUpdated } = allComponents;
281
+ if (!Content) return null;
282
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Content, { components: allComponents }), route?.lastUpdated && /* @__PURE__ */ jsx(LastUpdated, { date: route.lastUpdated })] });
283
+ }
284
+
285
+ //#endregion
286
+ //#region src/client/ssg/mdx-page.tsx
287
+ /**
288
+ * Renders an MDX page by consuming pre-loaded route data.
289
+ *
290
+ * - If the route belongs to a collection (`data.collection` is set), renders
291
+ * the custom post component if provided, else falls back to the standard DocPage.
292
+ * - Otherwise, renders the standard `DocPage` layout.
293
+ */
294
+ function MdxPage({ MDXComponent, mdxComponents: propComponents, collectionPostComponent: CollectionPost }) {
295
+ const data = useLoaderData();
296
+ if (!MDXComponent) return null;
297
+ if (!!data?.collection && CollectionPost) return /* @__PURE__ */ jsx(CollectionPost, {
298
+ MDXComponent,
299
+ mdxComponents: propComponents
300
+ });
301
+ const docData = data;
302
+ return /* @__PURE__ */ jsx(DocPage, {
303
+ route: {
304
+ path: docData.path,
305
+ filePath: docData.filePath,
306
+ title: docData.frontmatter?.title,
307
+ description: docData.frontmatter?.description,
308
+ headings: docData.headings,
309
+ locale: docData.locale,
310
+ version: docData.version,
311
+ group: docData.group,
312
+ groupTitle: docData.groupTitle,
313
+ lastUpdated: docData.lastUpdated,
314
+ frontmatter: docData.frontmatter
315
+ },
316
+ content: MDXComponent,
317
+ mdxComponents: propComponents
318
+ });
319
+ }
320
+
51
321
  //#endregion
52
322
  //#region src/client/hooks/use-breadcrumbs.ts
53
323
  /**
@@ -98,10 +368,20 @@ function Breadcrumbs() {
98
368
  //#region src/client/components/ui-base/copy-markdown.tsx
99
369
  const useCopyMarkdown = (content) => {
100
370
  const [copied, setCopied] = useState(false);
371
+ const timerRef = useRef(null);
372
+ useEffect(() => {
373
+ return () => {
374
+ if (timerRef.current) clearTimeout(timerRef.current);
375
+ };
376
+ }, []);
101
377
  const handleCopy = () => {
102
378
  navigator.clipboard.writeText(content);
103
379
  setCopied(true);
104
- setTimeout(() => setCopied(false), 2e3);
380
+ if (timerRef.current) clearTimeout(timerRef.current);
381
+ timerRef.current = setTimeout(() => {
382
+ setCopied(false);
383
+ timerRef.current = null;
384
+ }, 2e3);
105
385
  };
106
386
  const handleOpenRaw = () => {
107
387
  const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
@@ -426,8 +706,8 @@ function useTabs(tabs = [], routes = []) {
426
706
  transform: "translateX(0) scaleX(0)",
427
707
  width: 0
428
708
  });
429
- const currentPath = normalizePath$1(location.pathname);
430
- const activeTabId = routes.find((r) => normalizePath$1(r.path) === currentPath)?.tab?.toLowerCase();
709
+ const currentPath = normalizePath(location.pathname);
710
+ const activeTabId = routes.find((r) => normalizePath(r.path) === currentPath)?.tab?.toLowerCase();
431
711
  const activeIndex = tabs.findIndex((tab) => tab.id.toLowerCase() === activeTabId);
432
712
  const finalActiveIndex = activeIndex === -1 ? 0 : activeIndex;
433
713
  useEffect(() => {
@@ -485,11 +765,11 @@ function Tabs({ tabs, routes }) {
485
765
  return /* @__PURE__ */ jsx("div", {
486
766
  className: "mx-auto max-w-(--breakpoint-3xl) px-4 md:px-6 select-none",
487
767
  children: /* @__PURE__ */ jsxs(Tabs$1.List, {
488
- className: "border-none py-0 scrollbar-hide relative flex flex-row items-center",
768
+ className: "border-none py-0 scrollbar-hide relative flex flex-row items-center overflow-x-auto",
489
769
  children: [tabs.map((tab, index) => {
490
770
  const isActive = index === activeIndex;
491
771
  const firstRoute = routes.find((r) => r.tab && r.tab.toLowerCase() === tab.id.toLowerCase());
492
- return /* @__PURE__ */ jsxs(Link, {
772
+ return /* @__PURE__ */ jsxs(Link$1, {
493
773
  href: firstRoute ? firstRoute.path : "#",
494
774
  ref: (el) => {
495
775
  tabRefs.current[index] = el;
@@ -554,7 +834,7 @@ function useVersion() {
554
834
  currentVersion,
555
835
  currentVersionLabel: (versions?.versions?.find?.((v) => v.path === currentVersion))?.label || currentVersion,
556
836
  availableVersions: useMemo(() => {
557
- return versions ? versions.versions.map((v) => ({
837
+ return versions?.versions ? versions.versions.map((v) => ({
558
838
  key: v.path,
559
839
  label: v.label,
560
840
  value: v.path,
@@ -693,15 +973,14 @@ function I18nSelector({ className }) {
693
973
 
694
974
  //#endregion
695
975
  //#region src/client/components/ui-base/navbar.tsx
696
- const SearchDialog$1 = lazy(() => import("../search-dialog-BHuIiUC6.js").then((m) => ({ default: m.SearchDialog })));
976
+ const SearchDialog$1 = lazy(() => import("../search-dialog-DHc_8FFX.js").then((m) => ({ default: m.SearchDialog })));
697
977
  function Navbar() {
698
978
  const { links, title, logo, logoProps, github, social, config } = useNavbar();
699
- const { routes, allRoutes, currentRoute, currentVersion, currentLocale } = useRoutes();
700
- const { pathname } = useLocation$1();
979
+ const { routes, currentRoute, isCollectionPage, currentVersion, currentLocale } = useRoutes();
701
980
  const { isSidebarOpen, toggleSidebar } = useUI();
702
981
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
703
982
  const themeConfig = config.theme || {};
704
- const isDocs = !!currentRoute?.filePath;
983
+ const isDocs = !!currentRoute?.filePath && !isCollectionPage;
705
984
  const hasTabs = themeConfig?.tabs && themeConfig.tabs.length > 0;
706
985
  return /* @__PURE__ */ jsxs(Navbar$1.Root, {
707
986
  className: cn("border-b border-subtle bg-main/80 backdrop-blur-md", hasTabs && "border-b-0"),
@@ -802,7 +1081,7 @@ function Navbar() {
802
1081
  className: "w-full border-b border-subtle bg-main",
803
1082
  children: /* @__PURE__ */ jsx(Tabs, {
804
1083
  tabs: themeConfig.tabs,
805
- routes: allRoutes || routes || []
1084
+ routes: routes || []
806
1085
  })
807
1086
  })
808
1087
  ]
@@ -855,38 +1134,6 @@ function NavbarMobileLinkItem({ link, onClose }) {
855
1134
  });
856
1135
  }
857
1136
 
858
- //#endregion
859
- //#region src/client/components/ui-base/not-found.tsx
860
- function NotFound() {
861
- return /* @__PURE__ */ jsx("div", {
862
- className: "flex items-center justify-center min-h-[65vh] text-center px-4",
863
- children: /* @__PURE__ */ jsxs("div", {
864
- className: "space-y-6 max-w-md mx-auto p-8 border border-subtle bg-surface rounded-2xl shadow-xs",
865
- children: [
866
- /* @__PURE__ */ jsx("span", {
867
- className: "block text-7xl font-extrabold tracking-tight text-primary-500",
868
- children: "404"
869
- }),
870
- /* @__PURE__ */ jsxs("div", {
871
- className: "space-y-2",
872
- children: [/* @__PURE__ */ jsx("h1", {
873
- className: "text-xl font-bold text-body",
874
- children: "Page Not Found"
875
- }), /* @__PURE__ */ jsx("p", {
876
- className: "text-sm text-muted leading-relaxed",
877
- children: "The page you're looking for doesn't exist or has been moved."
878
- })]
879
- }),
880
- /* @__PURE__ */ jsxs(Link, {
881
- href: "/",
882
- className: "inline-flex items-center gap-2 rounded-xl border border-subtle bg-main px-6 py-2.5 text-xs font-semibold text-body hover:bg-primary-50/50 hover:border-primary-500/50 transition-all duration-300 outline-none select-none",
883
- children: [/* @__PURE__ */ jsx(ArrowLeft, { size: 14 }), " Go to Home"]
884
- })
885
- ]
886
- })
887
- });
888
- }
889
-
890
1137
  //#endregion
891
1138
  //#region src/client/components/ui-base/on-this-page.tsx
892
1139
  function OnThisPage({ headings = [], editLink, communityHelp, filePath }) {
@@ -1037,249 +1284,71 @@ const Sidebar = Object.assign(SidebarMain, {
1037
1284
  });
1038
1285
 
1039
1286
  //#endregion
1040
- //#region src/client/components/ui-base/last-updated.tsx
1041
- /**
1042
- * A subtle display for when the page was last updated.
1043
- * Small, opaque, and positioned at the bottom of the content with a thin top border divider.
1044
- */
1045
- function LastUpdated({ date }) {
1046
- if (!date) return null;
1047
- const d = new Date(date);
1048
- if (isNaN(d.getTime())) return null;
1049
- const formattedDate = d.toLocaleDateString(void 0, {
1050
- year: "numeric",
1051
- month: "long",
1052
- day: "numeric"
1053
- });
1287
+ //#region src/client/components/ui-base/banner.tsx
1288
+ function Banner({ children, className = "", dismissible = false, id = "boltdocs-banner", ...props }) {
1289
+ const [isVisible, setIsVisible] = useState(true);
1290
+ useEffect(() => {
1291
+ if (dismissible && id) {
1292
+ if (localStorage.getItem(`boltdocs-banner-dismissed-${id}`) === "true") setIsVisible(false);
1293
+ }
1294
+ }, [dismissible, id]);
1295
+ const handleDismiss = () => {
1296
+ setIsVisible(false);
1297
+ if (dismissible && id) localStorage.setItem(`boltdocs-banner-dismissed-${id}`, "true");
1298
+ };
1299
+ if (!isVisible) return null;
1054
1300
  return /* @__PURE__ */ jsxs("div", {
1055
- className: "mt-16 pt-6 border-t border-subtle flex items-center justify-between text-xs text-muted select-none",
1056
- children: [/* @__PURE__ */ jsx("span", {}), /* @__PURE__ */ jsxs("span", {
1057
- className: "italic",
1058
- children: ["Last updated on ", formattedDate]
1301
+ className: cn("relative flex items-center justify-center px-4 py-2.5 text-xs font-semibold tracking-wide bg-primary-500/10 dark:bg-primary-500/15 text-primary-700 dark:text-primary-300 border-b border-primary-500/20 select-none animate-in fade-in duration-300", className),
1302
+ ...props,
1303
+ children: [/* @__PURE__ */ jsx("div", {
1304
+ className: "flex-1 text-center flex items-center justify-center gap-2",
1305
+ children
1306
+ }), dismissible && /* @__PURE__ */ jsx("button", {
1307
+ onClick: handleDismiss,
1308
+ className: "absolute right-3 top-1/2 -translate-y-1/2 p-1.5 opacity-70 hover:opacity-100 transition-all duration-300 rounded-xl hover:bg-primary-500/10 cursor-pointer border-none bg-transparent flex items-center justify-center outline-none",
1309
+ "aria-label": "Dismiss banner",
1310
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
1059
1311
  })]
1060
1312
  });
1061
1313
  }
1062
1314
 
1063
1315
  //#endregion
1064
- //#region src/client/app/doc-page.tsx
1065
- /**
1066
- * DocPage renders the MDX content and page-specific metadata.
1067
- * It is rendered inside the Outlet of DocsLayout.
1068
- */
1069
- function DocPage({ route, content: Content, mdxComponents: propComponents }) {
1070
- const contextComponents = useMdxComponents();
1071
- const allComponents = useMemo(() => ({
1072
- LastUpdated,
1073
- ...contextComponents,
1074
- ...propComponents
1075
- }), [contextComponents, propComponents]);
1076
- const LastUpdated$1 = allComponents.LastUpdated || LastUpdated;
1077
- if (!Content) return null;
1078
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Content, { components: allComponents }), route?.lastUpdated && /* @__PURE__ */ jsx(LastUpdated$1, { date: route.lastUpdated })] });
1079
- }
1080
-
1081
- //#endregion
1082
- //#region src/client/ssg/mdx-page.tsx
1083
- /**
1084
- * Renders an MDX page by consuming pre-loaded module data.
1085
- * Uses DocPage to ensure consistent layout and metadata application.
1086
- */
1087
- function MdxPage({ MDXComponent: propMDX, mdxComponents: propComponents }) {
1088
- const data = useLoaderData();
1089
- const MDXComponent = propMDX || data?.MDXComponent;
1090
- const components = propComponents || data?.mdxComponents;
1091
- if (!MDXComponent) return null;
1092
- return /* @__PURE__ */ jsx(DocPage, {
1093
- route: {
1094
- path: data.path,
1095
- filePath: data.filePath,
1096
- title: data.frontmatter.title,
1097
- description: data.frontmatter.description,
1098
- headings: data.headings,
1099
- locale: data.locale,
1100
- version: data.version,
1101
- group: data.group,
1102
- groupTitle: data.groupTitle,
1103
- lastUpdated: data.lastUpdated,
1104
- frontmatter: data.frontmatter
1105
- },
1106
- content: MDXComponent,
1107
- mdxComponents: components
1108
- });
1109
- }
1110
-
1111
- //#endregion
1112
- //#region src/client/app/helmet-compat.tsx
1113
- const mod = ReactHelmetAsync;
1114
- /**
1115
- * The `<Helmet>` component, resolved across CJS/ESM module shapes.
1116
- * Falls back to a transparent fragment wrapper if the module cannot be resolved.
1117
- */
1118
- const Helmet = mod.Helmet || mod.default?.Helmet || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
1119
- /**
1120
- * The `<HelmetProvider>` component, resolved across CJS/ESM module shapes.
1121
- * Falls back to a transparent fragment wrapper if the module cannot be resolved.
1122
- */
1123
- const HelmetProvider = mod.HelmetProvider || mod.default?.HelmetProvider || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
1124
-
1125
- //#endregion
1126
- //#region src/client/app/scroll-handler.tsx
1127
- /**
1128
- * Handles scroll restoration and hash scrolling on navigation.
1129
- * It ensures the page scrolls to top on pathname changes,
1130
- * or specifically to an anchor element if a hash is present.
1131
- */
1132
- function ScrollHandler() {
1133
- const { pathname, hash } = useLocation$1();
1134
- const handleScroll = (behavior = "auto") => {
1135
- const container = document.querySelector(".boltdocs-content") || window;
1136
- const getScrollTop = () => {
1137
- if (container === window) return window.scrollY;
1138
- return container.scrollTop;
1139
- };
1140
- const scrollTo = (top, scrollBehavior) => {
1141
- if (container === window) window.scrollTo({
1142
- top,
1143
- behavior: scrollBehavior
1144
- });
1145
- else container.scrollTo({
1146
- top,
1147
- behavior: scrollBehavior
1148
- });
1149
- };
1150
- if (hash) {
1151
- const id = hash.replace("#", "");
1152
- const element = document.getElementById(id);
1153
- if (element) {
1154
- const offset = 80;
1155
- const containerTop = container === window ? 0 : container.getBoundingClientRect().top;
1156
- scrollTo(element.getBoundingClientRect().top - containerTop - offset + getScrollTop(), behavior);
1157
- return true;
1158
- }
1159
- }
1160
- scrollTo(0, behavior);
1161
- return false;
1162
- };
1163
- useLayoutEffect(() => {
1164
- handleScroll("auto");
1165
- }, [pathname, hash]);
1166
- useEffect(() => {
1167
- handleScroll("auto");
1168
- const rafId = requestAnimationFrame(() => {
1169
- handleScroll("auto");
1170
- window.dispatchEvent(new Event("resize"));
1171
- });
1172
- return () => cancelAnimationFrame(rafId);
1173
- }, [pathname, hash]);
1174
- return null;
1175
- }
1176
-
1177
- //#endregion
1178
- //#region src/client/app/mdx-component.tsx
1179
- const mdxComponentsDefault = {
1180
- ...mdx_components_default,
1181
- NotFound,
1182
- "404": NotFound
1183
- };
1184
-
1185
- //#endregion
1186
- //#region src/client/ssg/boltdocs-shell.tsx
1187
- /** Normalize a path: strip trailing slash unless it is exactly '/'. */
1188
- function normalizePath(p) {
1189
- return p.endsWith("/") && p.length > 1 ? p.slice(0, -1) : p;
1316
+ //#region src/client/ssg/mdx-elements.tsx
1317
+ const Loading = () => /* @__PURE__ */ jsx("div", {
1318
+ className: "text-muted text-sm py-4",
1319
+ children: "Loading..."
1320
+ });
1321
+ function resolveModuleLoader(loader) {
1322
+ return typeof loader === "function" ? loader() : Promise.resolve(loader);
1190
1323
  }
1191
- /**
1192
- * Updates the HTML lang and dir attributes based on the current locale configuration.
1193
- */
1194
- function I18nUpdater({ config }) {
1195
- const { currentLocale } = useBoltdocsContext();
1324
+ const EagerMdxElement = ({ moduleLoader, moduleKey, route, components, collectionPostComponent }) => {
1325
+ const [mod, setMod] = useState(moduleLoader);
1196
1326
  useEffect(() => {
1197
- if (!config.i18n || typeof document === "undefined") return;
1198
- const locale = currentLocale || config.i18n.defaultLocale;
1199
- const localeConfig = config.i18n.localeConfigs?.[locale];
1200
- document.documentElement.lang = localeConfig?.htmlLang || locale || "en";
1201
- document.documentElement.dir = localeConfig?.direction || "ltr";
1202
- }, [currentLocale, config.i18n]);
1203
- return null;
1204
- }
1205
- /**
1206
- * Synchronizes the Zustand store with the current URL pathname.
1207
- * Receives a pre-built Map for O(1) route lookups instead of O(n) .find().
1208
- */
1209
- function StoreSync({ config, routeMap }) {
1210
- const location = useLocation$1();
1211
- const { setLocale, setVersion } = useBoltdocsContext();
1327
+ setMod(moduleLoader);
1328
+ }, [moduleLoader]);
1212
1329
  useEffect(() => {
1213
- const currentPath = normalizePath(location.pathname);
1214
- const matchedRoute = routeMap.get(currentPath);
1215
- if (matchedRoute) {
1216
- if (config.i18n) setLocale(matchedRoute.locale || config.i18n.defaultLocale);
1217
- if (config.versions) setVersion(matchedRoute.version || config.versions.defaultVersion);
1218
- }
1219
- }, [
1220
- location.pathname,
1221
- config,
1222
- routeMap,
1223
- setLocale,
1224
- setVersion
1225
- ]);
1226
- return null;
1227
- }
1228
- function BoltdocsShell({ config, routes, components = {} }) {
1229
- const allComponents = useMemo(() => ({
1230
- ...mdxComponentsDefault,
1231
- ...virtualCustomComponents,
1232
- ...components
1233
- }), [components]);
1234
- const { pathname } = useLocation$1();
1235
- const currentPath = useMemo(() => normalizePath(pathname || "/"), [pathname]);
1236
- const routeMap = useMemo(() => {
1237
- const map = /* @__PURE__ */ new Map();
1238
- for (const r of routes) {
1239
- const key = normalizePath(r.path === "" ? "/" : r.path);
1240
- map.set(key, r);
1241
- }
1242
- return map;
1243
- }, [routes]);
1244
- const initialData = useMemo(() => {
1245
- const matched = routeMap.get(currentPath);
1246
- let initLocale = void 0;
1247
- let initVersion = void 0;
1248
- if (matched) {
1249
- if (config.i18n) initLocale = matched.locale || config.i18n.defaultLocale;
1250
- if (config.versions) initVersion = matched.version || config.versions.defaultVersion;
1251
- }
1252
- return {
1253
- initLocale,
1254
- initVersion
1330
+ if (!import.meta.hot || !moduleKey) return;
1331
+ const handler = (data) => {
1332
+ if (data.relPath.replace(/\\/g, "/").replace(/^\//, "") !== route.filePath.replace(/\\/g, "/").replace(/^\//, "")) return;
1333
+ import(moduleKey + "?t=" + Date.now()).then((m) => {
1334
+ setMod(m);
1335
+ });
1255
1336
  };
1256
- }, [
1257
- currentPath,
1258
- config,
1259
- routeMap
1260
- ]);
1261
- return /* @__PURE__ */ jsx(HelmetProvider, { children: /* @__PURE__ */ jsx(RoutesProvider, {
1262
- routes,
1263
- children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(UIProvider, { children: /* @__PURE__ */ jsx(MdxComponentsProvider, {
1264
- components: allComponents,
1265
- children: /* @__PURE__ */ jsxs(ConfigContext.Provider, {
1266
- value: config,
1267
- children: [/* @__PURE__ */ jsx(ScrollHandler, {}), /* @__PURE__ */ jsxs(BoltdocsProvider, {
1268
- initialLocale: initialData.initLocale,
1269
- initialVersion: initialData.initVersion,
1270
- children: [
1271
- /* @__PURE__ */ jsx(StoreSync, {
1272
- config,
1273
- routeMap
1274
- }),
1275
- /* @__PURE__ */ jsx(I18nUpdater, { config }),
1276
- /* @__PURE__ */ jsx(Outlet, {})
1277
- ]
1278
- })]
1279
- })
1280
- }) }) })
1281
- }) });
1282
- }
1337
+ import.meta.hot.on("boltdocs:mdx-update", handler);
1338
+ return () => import.meta.hot?.off("boltdocs:mdx-update", handler);
1339
+ }, [moduleKey, route.filePath]);
1340
+ const MDXComponent = mod?.default ?? mod ?? null;
1341
+ if (!MDXComponent) return /* @__PURE__ */ jsx(Loading, {});
1342
+ return /* @__PURE__ */ jsx(MdxPage, {
1343
+ MDXComponent,
1344
+ mdxComponents: components,
1345
+ collectionPostComponent
1346
+ });
1347
+ };
1348
+ const NotFoundWrapper = () => {
1349
+ const components = useMdxComponents();
1350
+ return /* @__PURE__ */ jsx(components.NotFound || components["404"] || NotFound, {});
1351
+ };
1283
1352
 
1284
1353
  //#endregion
1285
1354
  //#region src/client/app/head.tsx
@@ -1294,6 +1363,8 @@ function Head({ siteTitle, siteDescription, routes }) {
1294
1363
  const translatedSiteTitle = getTranslated(siteTitle, currentLocale);
1295
1364
  const finalTitle = pageTitle ? `${pageTitle} | ${translatedSiteTitle}` : translatedSiteTitle;
1296
1365
  const seo = currentRoute?.seo || {};
1366
+ const canonicalUrl = seo.canonical || (config?.siteUrl && currentRoute?.path ? `${config.siteUrl.replace(/\/$/, "")}${currentRoute.path}` : void 0);
1367
+ const ogUrl = seo["og:url"] || canonicalUrl || (typeof window !== "undefined" ? window.location.href : void 0);
1297
1368
  const globalMetatags = config?.seo?.metatags || {};
1298
1369
  const defaultOgImage = config?.seo?.thumbnails?.background;
1299
1370
  const ogImage = seo["og:image"] || defaultOgImage;
@@ -1315,13 +1386,13 @@ function Head({ siteTitle, siteDescription, routes }) {
1315
1386
  property: "og:type",
1316
1387
  content: "article"
1317
1388
  }),
1318
- typeof window !== "undefined" && /* @__PURE__ */ jsx("meta", {
1319
- property: "og:url",
1320
- content: window.location.href
1321
- }),
1322
- typeof window !== "undefined" && /* @__PURE__ */ jsx("link", {
1389
+ canonicalUrl && /* @__PURE__ */ jsx("link", {
1323
1390
  rel: "canonical",
1324
- href: window.location.origin + location.pathname
1391
+ href: canonicalUrl
1392
+ }),
1393
+ ogUrl && /* @__PURE__ */ jsx("meta", {
1394
+ property: "og:url",
1395
+ content: ogUrl
1325
1396
  }),
1326
1397
  /* @__PURE__ */ jsx("meta", {
1327
1398
  name: "twitter:card",
@@ -1365,10 +1436,7 @@ function Head({ siteTitle, siteDescription, routes }) {
1365
1436
  name: "robots",
1366
1437
  content: value
1367
1438
  }, "robots");
1368
- if (key === "canonical") return /* @__PURE__ */ jsx("link", {
1369
- rel: "canonical",
1370
- href: value
1371
- }, "canonical");
1439
+ if (key === "canonical" || key === "og:url") return null;
1372
1440
  return key.startsWith("og:") || key.startsWith("music:") || key.startsWith("video:") || key.startsWith("article:") || key.startsWith("book:") || key.startsWith("profile:") ? /* @__PURE__ */ jsx("meta", {
1373
1441
  property: key,
1374
1442
  content: value
@@ -1380,71 +1448,68 @@ function Head({ siteTitle, siteDescription, routes }) {
1380
1448
  ] });
1381
1449
  }
1382
1450
 
1451
+ //#endregion
1452
+ //#region src/client/collections/collections-context.tsx
1453
+ const CollectionsContext = createContext({});
1454
+ function useCollectionsData() {
1455
+ return use(CollectionsContext);
1456
+ }
1457
+
1383
1458
  //#endregion
1384
1459
  //#region src/client/app/docs-layout.tsx
1385
- /**
1386
- * Wraps the docs Outlet with the user's (or default) layout component.
1387
- * The Layout receives the routed page as `children`.
1388
- * We use useRoutes to pass the current route context to the persistent layout.
1389
- */
1390
- function DocsLayout$1() {
1460
+ function DocsLayout({ collectionsData }) {
1391
1461
  const config = useConfig();
1392
1462
  const { currentRoute, allRoutes } = useRoutes();
1393
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Head, {
1463
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Head, {
1394
1464
  siteTitle: config.theme?.title,
1395
1465
  siteDescription: config.theme?.description,
1396
1466
  routes: allRoutes || []
1397
- }), /* @__PURE__ */ jsx(UserLayout, {
1467
+ }), /* @__PURE__ */ jsx(InternalErrorBoundary, { children: /* @__PURE__ */ jsx(UserLayout, {
1398
1468
  route: currentRoute,
1399
1469
  children: /* @__PURE__ */ jsx(Outlet, {})
1400
- })] });
1470
+ }) })] });
1471
+ if (collectionsData) return /* @__PURE__ */ jsx(CollectionsContext.Provider, {
1472
+ value: collectionsData,
1473
+ children: content
1474
+ });
1475
+ return content;
1401
1476
  }
1402
1477
 
1403
1478
  //#endregion
1404
- //#region src/client/ssg/create-routes.tsx
1405
- const Loading = () => /* @__PURE__ */ jsx("div", {
1406
- className: "text-muted text-sm py-4",
1407
- children: "Loading..."
1408
- });
1409
- /**
1410
- * Stable component to render MDX pages.
1411
- * By being outside createRoutes, it prevents React from unmounting the page on HMR.
1412
- */
1413
- const MdxRouteElement = ({ moduleLoader, moduleKey, route, components }) => {
1414
- const MDXComponent = moduleLoader?.default ?? moduleLoader ?? null;
1415
- useEffect(() => {
1416
- if (!import.meta.hot || !moduleKey) return;
1417
- const handler = (data) => {
1418
- if (data.relPath.replace(/\\/g, "/").replace(/^\//, "") !== route.filePath.replace(/\\/g, "/").replace(/^\//, "")) return;
1419
- import(moduleKey + "?t=" + Date.now()).then((m) => {});
1420
- };
1421
- import.meta.hot.on("boltdocs:mdx-update", handler);
1422
- return () => import.meta.hot?.off("boltdocs:mdx-update", handler);
1423
- }, [moduleKey, route.filePath]);
1424
- if (!MDXComponent) return /* @__PURE__ */ jsx(Loading, {});
1425
- return /* @__PURE__ */ jsx(MdxPage, {
1426
- MDXComponent,
1427
- mdxComponents: components
1428
- });
1429
- };
1430
- const NotFoundWrapper = () => {
1431
- const components = useMdxComponents();
1432
- return /* @__PURE__ */ jsx(components.NotFound || components["404"] || NotFound, {});
1433
- };
1434
- function createRoutes(options) {
1435
- const { routesData, config, mdxModules, externalPages, externalLayout, components } = options;
1436
- const EffectiveExternalLayout = externalLayout || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
1437
- const withBase = (path) => {
1438
- const base = config.base || "/";
1439
- if (path.startsWith(base)) return path;
1440
- return `${base === "/" ? "" : base.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}` || "/";
1441
- };
1479
+ //#region src/client/ssg/create-routes.utils.ts
1480
+ function withBase(path, config) {
1481
+ const base = config.base || "/";
1482
+ if (path.startsWith(base)) return path;
1483
+ return `${base === "/" ? "" : base.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}` || "/";
1484
+ }
1485
+ function buildModuleMap(mdxModules) {
1486
+ const moduleMap = /* @__PURE__ */ new Map();
1487
+ const mdxModuleKeys = Object.keys(mdxModules);
1488
+ if (mdxModuleKeys.length > 0) {
1489
+ const docsDirName = mdxModuleKeys[0].replace(/\\/g, "/").split("/").filter(Boolean)[0] || "docs";
1490
+ const primaryPrefix = `/${docsDirName}/`;
1491
+ const altPrefix = `./${docsDirName}/`;
1492
+ for (const rawKey of mdxModuleKeys) {
1493
+ const k = rawKey.replace(/\\/g, "/");
1494
+ let relativePath = "";
1495
+ if (k.indexOf(primaryPrefix) !== -1) relativePath = k.substring(k.indexOf(primaryPrefix) + primaryPrefix.length);
1496
+ else if (k.startsWith(altPrefix)) relativePath = k.substring(altPrefix.length);
1497
+ if (relativePath) moduleMap.set(relativePath, rawKey);
1498
+ else moduleMap.set(k, rawKey);
1499
+ }
1500
+ }
1501
+ return moduleMap;
1502
+ }
1503
+
1504
+ //#endregion
1505
+ //#region src/client/ssg/create-routes.doc.tsx
1506
+ function buildDocRoutes(options) {
1507
+ const { routesData, config, mdxModules, components, externalPages } = options;
1508
+ const baseDocsPath = (config.base || "/docs").replace(/\/$/, "") || "/";
1442
1509
  const defaultVersionMetadata = [];
1443
1510
  const defaultVersion = config.versions?.defaultVersion;
1444
1511
  const docsBase = (config.base || "/docs").replace(/\/$/, "");
1445
- let baseDocsPath = (config.base || "/docs").replace(/\/$/, "");
1446
- if (!baseDocsPath) baseDocsPath = "/";
1447
- if (defaultVersion) routesData.forEach((route) => {
1512
+ if (defaultVersion) routesData.filter((r) => !r.collection).forEach((route) => {
1448
1513
  if (route.version) return;
1449
1514
  const p = route.path || "";
1450
1515
  const subPath = p.startsWith(docsBase) ? p.substring(docsBase.length).replace(/^\//, "") : p.replace(/^\//, "");
@@ -1457,36 +1522,18 @@ function createRoutes(options) {
1457
1522
  });
1458
1523
  }
1459
1524
  });
1460
- const docMetadata = [...routesData, ...defaultVersionMetadata];
1461
- const moduleMap = /* @__PURE__ */ new Map();
1525
+ const docMetadata = [...routesData.filter((r) => !r.collection), ...defaultVersionMetadata];
1526
+ const moduleMap = buildModuleMap(mdxModules);
1462
1527
  const mdxModuleKeys = Object.keys(mdxModules);
1463
- if (mdxModuleKeys.length > 0) {
1464
- const docsDirName = mdxModuleKeys[0].replace(/\\/g, "/").split("/").filter(Boolean)[0] || "docs";
1465
- const primaryPrefix = `/${docsDirName}/`;
1466
- const altPrefix = `./${docsDirName}/`;
1467
- for (const rawKey of mdxModuleKeys) {
1468
- const k = rawKey.replace(/\\/g, "/");
1469
- let relativePath = "";
1470
- if (k.indexOf(primaryPrefix) !== -1) relativePath = k.substring(k.indexOf(primaryPrefix) + primaryPrefix.length);
1471
- else if (k.startsWith(altPrefix)) relativePath = k.substring(altPrefix.length);
1472
- if (relativePath) moduleMap.set(relativePath, rawKey);
1473
- else moduleMap.set(k, rawKey);
1474
- }
1475
- }
1528
+ const isLazy = mdxModuleKeys.length > 0 && typeof mdxModules[mdxModuleKeys[0]] === "function";
1476
1529
  const docRoutes = docMetadata.map((route) => {
1477
1530
  const normalizedFilePath = route.filePath.replace(/\\/g, "/");
1478
1531
  const moduleKey = moduleMap.get(normalizedFilePath);
1479
1532
  const moduleLoader = moduleKey ? mdxModules[moduleKey] : null;
1480
- const fullPath = withBase(route.path === "" ? "/" : route.path);
1533
+ const fullPath = withBase(route.path === "" ? "/" : route.path, config);
1481
1534
  const path = fullPath === baseDocsPath ? "." : fullPath.startsWith(baseDocsPath + "/") ? fullPath.slice(baseDocsPath.length + 1) : fullPath;
1482
- return {
1535
+ const routeRecord = {
1483
1536
  path,
1484
- element: /* @__PURE__ */ jsx(MdxRouteElement, {
1485
- moduleKey,
1486
- moduleLoader,
1487
- route,
1488
- components
1489
- }, moduleKey || path),
1490
1537
  loader: async () => ({
1491
1538
  path,
1492
1539
  frontmatter: {
@@ -1494,6 +1541,7 @@ function createRoutes(options) {
1494
1541
  description: route.description || "",
1495
1542
  ...route.frontmatter || {}
1496
1543
  },
1544
+ seo: route.seo,
1497
1545
  headings: route.headings || [],
1498
1546
  filePath: route.filePath,
1499
1547
  locale: route.locale,
@@ -1505,6 +1553,24 @@ function createRoutes(options) {
1505
1553
  }),
1506
1554
  getStaticPaths: () => [path]
1507
1555
  };
1556
+ if (isLazy && moduleLoader) routeRecord.lazy = async () => {
1557
+ const mod = await resolveModuleLoader(moduleLoader);
1558
+ return { Component: function LoadedMdxRoute() {
1559
+ return /* @__PURE__ */ jsx(EagerMdxElement, {
1560
+ moduleKey,
1561
+ moduleLoader: mod,
1562
+ route,
1563
+ components
1564
+ }, moduleKey || path);
1565
+ } };
1566
+ };
1567
+ else routeRecord.element = /* @__PURE__ */ jsx(EagerMdxElement, {
1568
+ moduleKey,
1569
+ moduleLoader,
1570
+ route,
1571
+ components
1572
+ }, moduleKey || path);
1573
+ return routeRecord;
1508
1574
  });
1509
1575
  const locales = config.i18n?.locales ? Array.isArray(config.i18n.locales) ? config.i18n.locales : Object.keys(config.i18n.locales) : [];
1510
1576
  const allVersions = config.versions?.versions?.map((v) => v.path) || [];
@@ -1555,11 +1621,11 @@ function createRoutes(options) {
1555
1621
  getStaticPaths: () => []
1556
1622
  });
1557
1623
  const matchedMetaObj = docMetadata.find((m) => {
1558
- const fullPath = withBase(m.path === "" ? "/" : m.path);
1624
+ const fullPath = withBase(m.path === "" ? "/" : m.path, config);
1559
1625
  return (fullPath === baseDocsPath ? "." : fullPath.startsWith(baseDocsPath + "/") ? fullPath.slice(baseDocsPath.length + 1) : fullPath) === matchedRouteObj.path;
1560
1626
  });
1561
1627
  if (matchedMetaObj) {
1562
- const canonicalPath = withBase(matchedMetaObj.path);
1628
+ const canonicalPath = withBase(matchedMetaObj.path, config);
1563
1629
  const canonicalUrl = config.siteUrl ? `${config.siteUrl.replace(/\/$/, "")}${canonicalPath}` : canonicalPath;
1564
1630
  docMetadata.push({
1565
1631
  ...matchedMetaObj,
@@ -1575,62 +1641,277 @@ function createRoutes(options) {
1575
1641
  }
1576
1642
  }
1577
1643
  });
1578
- const children = [{
1579
- path: baseDocsPath,
1580
- element: /* @__PURE__ */ jsx(DocsLayout$1, {}),
1581
- children: docRoutes
1582
- }];
1583
- const externalMetadata = [];
1584
- if (externalPages) Object.entries(externalPages).forEach(([rawPath, ExtComponent]) => {
1644
+ return {
1645
+ routes: docRoutes,
1646
+ metadata: docMetadata
1647
+ };
1648
+ }
1649
+
1650
+ //#endregion
1651
+ //#region src/client/ssg/create-routes.external.tsx
1652
+ function buildExternalRoutes(options) {
1653
+ const { externalPages, externalLayout, config } = options;
1654
+ const children = [];
1655
+ const metadata = [];
1656
+ if (!externalPages) return {
1657
+ children,
1658
+ metadata
1659
+ };
1660
+ const EffectiveExternalLayout = externalLayout || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
1661
+ Object.entries(externalPages).forEach(([rawPath, ExtComponent]) => {
1585
1662
  const path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
1586
- if (!children.find((r) => r.path === path)) {
1587
- externalMetadata.push({
1663
+ metadata.push({
1664
+ path,
1665
+ locale: config.i18n?.defaultLocale,
1666
+ title: rawPath === "/" ? "Home" : rawPath.replace(/^\//, "").split("/").pop() || "Page",
1667
+ filePath: "",
1668
+ headings: []
1669
+ });
1670
+ children.push({
1671
+ path,
1672
+ element: /* @__PURE__ */ jsx(EffectiveExternalLayout, { children: /* @__PURE__ */ jsx(ExtComponent, {}) }),
1673
+ loader: async () => ({
1588
1674
  path,
1589
- locale: config.i18n?.defaultLocale,
1590
- title: rawPath === "/" ? "Home" : rawPath.replace(/^\//, "").split("/").pop() || "Page",
1675
+ locale: config.i18n?.defaultLocale
1676
+ }),
1677
+ getStaticPaths: () => [path]
1678
+ });
1679
+ if (config.i18n) Object.keys(config.i18n.locales).forEach((locale) => {
1680
+ const localePath = `/${locale}${rawPath === "/" ? "" : rawPath}`;
1681
+ metadata.push({
1682
+ path: localePath,
1683
+ locale,
1684
+ title: rawPath,
1591
1685
  filePath: "",
1592
1686
  headings: []
1593
1687
  });
1594
1688
  children.push({
1595
- path,
1689
+ path: localePath,
1596
1690
  element: /* @__PURE__ */ jsx(EffectiveExternalLayout, { children: /* @__PURE__ */ jsx(ExtComponent, {}) }),
1597
1691
  loader: async () => ({
1598
- path,
1599
- locale: config.i18n?.defaultLocale
1692
+ path: localePath,
1693
+ locale
1600
1694
  }),
1601
- getStaticPaths: () => [path]
1602
- });
1603
- if (config.i18n) Object.keys(config.i18n.locales).forEach((locale) => {
1604
- const localePath = `/${locale}${rawPath === "/" ? "" : rawPath}`;
1605
- if (!children.find((r) => r.path === localePath)) {
1606
- externalMetadata.push({
1607
- path: localePath,
1608
- locale,
1609
- title: rawPath,
1610
- filePath: "",
1611
- headings: []
1612
- });
1613
- children.push({
1614
- path: localePath,
1615
- element: /* @__PURE__ */ jsx(EffectiveExternalLayout, { children: /* @__PURE__ */ jsx(ExtComponent, {}) }),
1616
- loader: async () => ({
1617
- path: localePath,
1618
- locale
1619
- }),
1620
- getStaticPaths: () => [localePath]
1621
- });
1622
- }
1695
+ getStaticPaths: () => [localePath]
1623
1696
  });
1624
- }
1697
+ });
1625
1698
  });
1626
- children.push({
1627
- path: "*",
1628
- element: /* @__PURE__ */ jsx(EffectiveExternalLayout, { children: /* @__PURE__ */ jsx(NotFoundWrapper, {}) })
1699
+ return {
1700
+ children,
1701
+ metadata
1702
+ };
1703
+ }
1704
+
1705
+ //#endregion
1706
+ //#region src/client/ssg/create-routes.collection.tsx
1707
+ function DefaultCollectionList() {
1708
+ const data = useLoaderData();
1709
+ if (!data || !data.posts) return null;
1710
+ return /* @__PURE__ */ jsxs("div", {
1711
+ className: "py-8 max-w-2xl mx-auto px-4",
1712
+ children: [
1713
+ /* @__PURE__ */ jsx("h1", {
1714
+ className: "text-3xl font-bold mb-6 capitalize",
1715
+ children: data.collection
1716
+ }),
1717
+ /* @__PURE__ */ jsx("div", {
1718
+ className: "space-y-6",
1719
+ children: data.posts.map((post) => /* @__PURE__ */ jsxs("article", {
1720
+ className: "border-b border-subtle pb-4",
1721
+ children: [
1722
+ /* @__PURE__ */ jsx("h2", {
1723
+ className: "text-xl font-semibold mb-2",
1724
+ children: /* @__PURE__ */ jsx(Link, {
1725
+ to: post.path,
1726
+ className: "text-primary-600 hover:underline",
1727
+ children: post.title
1728
+ })
1729
+ }),
1730
+ post.date && /* @__PURE__ */ jsx("time", {
1731
+ className: "text-xs text-muted block mb-2",
1732
+ children: new Date(post.date).toLocaleDateString()
1733
+ }),
1734
+ post.excerpt && /* @__PURE__ */ jsx("p", {
1735
+ className: "text-sm text-body",
1736
+ children: post.excerpt
1737
+ })
1738
+ ]
1739
+ }, post.path))
1740
+ }),
1741
+ data.totalPages > 1 && /* @__PURE__ */ jsxs("div", {
1742
+ className: "mt-8 flex gap-4 text-sm",
1743
+ children: [
1744
+ data.currentPage > 1 && /* @__PURE__ */ jsx(Link, {
1745
+ to: data.currentPage === 2 ? `/${data.collection}` : `/${data.collection}/page/${data.currentPage - 1}`,
1746
+ className: "text-primary-600 hover:underline",
1747
+ children: "Previous"
1748
+ }),
1749
+ /* @__PURE__ */ jsxs("span", { children: [
1750
+ "Page ",
1751
+ data.currentPage,
1752
+ " of ",
1753
+ data.totalPages
1754
+ ] }),
1755
+ data.currentPage < data.totalPages && /* @__PURE__ */ jsx(Link, {
1756
+ to: `/${data.collection}/page/${data.currentPage + 1}`,
1757
+ className: "text-primary-600 hover:underline",
1758
+ children: "Next"
1759
+ })
1760
+ ]
1761
+ })
1762
+ ]
1629
1763
  });
1764
+ }
1765
+ function buildCollectionRoutes(options) {
1766
+ const { routesData, collectionsData, collectionLayouts, collectionLists, collectionPosts, config, mdxModules, components } = options;
1767
+ const postsPerPage = options.postsPerPage ?? config.collections?.postsPerPage ?? 10;
1768
+ const children = [];
1769
+ const metadata = [];
1770
+ const collectionsMap = /* @__PURE__ */ new Map();
1771
+ for (const r of routesData) if (r.collection) {
1772
+ if (!collectionsMap.has(r.collection)) collectionsMap.set(r.collection, []);
1773
+ collectionsMap.get(r.collection).push(r);
1774
+ }
1775
+ const moduleMap = buildModuleMap(mdxModules);
1776
+ const mdxModuleKeys = Object.keys(mdxModules);
1777
+ const isLazy = mdxModuleKeys.length > 0 && typeof mdxModules[mdxModuleKeys[0]] === "function";
1778
+ for (const [colName, colRoutes] of collectionsMap) {
1779
+ const colBase = `/${colName}`;
1780
+ colRoutes.sort((a, b) => {
1781
+ const da = a.date ? new Date(a.date).getTime() : 0;
1782
+ return (b.date ? new Date(b.date).getTime() : 0) - da;
1783
+ });
1784
+ const colChildren = [];
1785
+ for (const route of colRoutes) {
1786
+ const normalizedFilePath = route.filePath.replace(/\\/g, "/");
1787
+ const moduleKey = moduleMap.get(normalizedFilePath);
1788
+ const moduleLoader = moduleKey ? mdxModules[moduleKey] : null;
1789
+ const subPath = route.path.startsWith(colBase + "/") ? route.path.slice(colBase.length + 1) : route.path.replace(colBase, "") || "";
1790
+ const routeWithCollection = {
1791
+ ...route,
1792
+ collection: colName
1793
+ };
1794
+ const postComponent = collectionPosts?.[colName];
1795
+ const routeRecord = {
1796
+ path: subPath,
1797
+ loader: async () => ({
1798
+ route: routeWithCollection,
1799
+ headings: route.headings || [],
1800
+ collection: colName,
1801
+ path: subPath,
1802
+ frontmatter: {
1803
+ title: route.title,
1804
+ description: route.description || "",
1805
+ ...route.frontmatter || {}
1806
+ },
1807
+ filePath: route.filePath,
1808
+ locale: route.locale,
1809
+ version: route.version,
1810
+ date: route.date,
1811
+ lastUpdated: route.lastUpdated
1812
+ }),
1813
+ getStaticPaths: () => [subPath || "."]
1814
+ };
1815
+ if (isLazy && moduleLoader) routeRecord.lazy = async () => {
1816
+ const mod = await resolveModuleLoader(moduleLoader);
1817
+ return { Component: function LoadedCollectionMdxRoute() {
1818
+ return /* @__PURE__ */ jsx(EagerMdxElement, {
1819
+ moduleKey,
1820
+ moduleLoader: mod,
1821
+ route,
1822
+ components,
1823
+ collectionPostComponent: postComponent
1824
+ }, moduleKey || subPath);
1825
+ } };
1826
+ };
1827
+ else routeRecord.element = /* @__PURE__ */ jsx(EagerMdxElement, {
1828
+ moduleKey,
1829
+ moduleLoader,
1830
+ route,
1831
+ components,
1832
+ collectionPostComponent: postComponent
1833
+ }, moduleKey || subPath);
1834
+ colChildren.push(routeRecord);
1835
+ metadata.push(route);
1836
+ }
1837
+ const totalPages = Math.ceil(colRoutes.length / postsPerPage);
1838
+ const paginatedPosts = colRoutes.map((r) => ({
1839
+ path: r.path,
1840
+ title: r.title,
1841
+ date: r.date,
1842
+ excerpt: r.excerpt,
1843
+ tags: r.tags,
1844
+ author: r.author,
1845
+ coverImage: r.coverImage,
1846
+ filePath: r.filePath,
1847
+ frontmatter: r.frontmatter
1848
+ }));
1849
+ const ListElement = collectionLists?.[colName] || DefaultCollectionList;
1850
+ colChildren.unshift({
1851
+ index: true,
1852
+ element: /* @__PURE__ */ jsx(ListElement, {}),
1853
+ loader: async () => ({
1854
+ posts: paginatedPosts.slice(0, postsPerPage),
1855
+ totalPages,
1856
+ currentPage: 1,
1857
+ collection: colName
1858
+ }),
1859
+ getStaticPaths: () => []
1860
+ });
1861
+ for (let p = 2; p <= totalPages; p++) colChildren.push({
1862
+ path: `page/${p}`,
1863
+ element: /* @__PURE__ */ jsx(ListElement, {}),
1864
+ loader: async () => ({
1865
+ posts: paginatedPosts.slice((p - 1) * postsPerPage, p * postsPerPage),
1866
+ totalPages,
1867
+ currentPage: p,
1868
+ collection: colName
1869
+ }),
1870
+ getStaticPaths: () => [`page/${p}`]
1871
+ });
1872
+ const blogLayoutRoute = {
1873
+ path: colBase,
1874
+ element: /* @__PURE__ */ jsx(collectionLayouts?.[colName] || DocsLayout, { collectionsData: collectionsData || {} }),
1875
+ children: colChildren
1876
+ };
1877
+ children.push(blogLayoutRoute);
1878
+ }
1879
+ return {
1880
+ children,
1881
+ metadata
1882
+ };
1883
+ }
1884
+
1885
+ //#endregion
1886
+ //#region src/client/ssg/create-routes.tsx
1887
+ function createRoutes(options) {
1888
+ const { config, components, externalLayout } = options;
1889
+ const EffectiveExternalLayout = externalLayout || (({ children }) => /* @__PURE__ */ jsx(Fragment, { children }));
1890
+ const baseDocsPath = (config.base || "/docs").replace(/\/$/, "") || "/";
1891
+ const { routes: docRoutes, metadata: docMetadata } = buildDocRoutes(options);
1892
+ const externalRoutes = buildExternalRoutes(options);
1893
+ const collectionRoutes = buildCollectionRoutes(options);
1894
+ const children = [
1895
+ {
1896
+ path: baseDocsPath,
1897
+ element: /* @__PURE__ */ jsx(DocsLayout, {}),
1898
+ children: docRoutes
1899
+ },
1900
+ ...externalRoutes.children,
1901
+ ...collectionRoutes.children,
1902
+ {
1903
+ path: "*",
1904
+ element: /* @__PURE__ */ jsx(EffectiveExternalLayout, { children: /* @__PURE__ */ jsx(NotFoundWrapper, {}) })
1905
+ }
1906
+ ];
1630
1907
  return [{
1631
1908
  element: /* @__PURE__ */ jsx(BoltdocsShell, {
1632
1909
  config,
1633
- routes: [...docMetadata, ...externalMetadata],
1910
+ routes: [
1911
+ ...docMetadata,
1912
+ ...externalRoutes.metadata,
1913
+ ...collectionRoutes.metadata
1914
+ ],
1634
1915
  components
1635
1916
  }),
1636
1917
  children
@@ -1816,29 +2097,40 @@ function useTrackEvent() {
1816
2097
  }
1817
2098
 
1818
2099
  //#endregion
1819
- //#region src/client/components/docs-layout-default.tsx
2100
+ //#region src/client/hooks/use-headings.ts
1820
2101
  /**
1821
- * Pre-assembled high-fidelity documentation layout component.
1822
- * Fully styled and optimized to adapt seamlessly to our custom Parchment/Slate theme.
2102
+ * Returns the headings of the current page, extracted from the route loader data.
2103
+ * Useful for building custom Tables of Contents or jumping to sections.
2104
+ *
2105
+ * @returns An array of heading objects with level, text, and id.
1823
2106
  */
2107
+ function useHeadings() {
2108
+ const matches = useMatches();
2109
+ return useMemo(() => {
2110
+ return (matches[matches.length - 1]?.data)?.headings || [];
2111
+ }, [matches]);
2112
+ }
2113
+
2114
+ //#endregion
2115
+ //#region src/client/components/docs-layout-default.tsx
1824
2116
  function DocsLayoutComponent({ children }) {
1825
- const { routes: filteredRoutes, currentRoute } = useRoutes();
2117
+ const { routes: filteredRoutes, currentRoute, isCollectionPage } = useRoutes();
1826
2118
  const config = useConfig();
1827
- return /* @__PURE__ */ jsxs(DocsLayout$2, {
2119
+ return /* @__PURE__ */ jsxs(DocsLayout$1, {
1828
2120
  className: "selection:bg-primary-500/10 selection:text-primary-500",
1829
- children: [/* @__PURE__ */ jsx(Navbar, {}), /* @__PURE__ */ jsxs(DocsLayout$2.Body, {
2121
+ children: [/* @__PURE__ */ jsx(Navbar, {}), /* @__PURE__ */ jsxs(DocsLayout$1.Body, {
1830
2122
  className: "bg-main",
1831
2123
  children: [
1832
- /* @__PURE__ */ jsx(Sidebar, {
2124
+ !isCollectionPage && /* @__PURE__ */ jsx(Sidebar, {
1833
2125
  routes: filteredRoutes || [],
1834
2126
  config
1835
2127
  }),
1836
- /* @__PURE__ */ jsx(DocsLayout$2.Content, {
2128
+ /* @__PURE__ */ jsx(DocsLayout$1.Content, {
1837
2129
  className: "animate-in fade-in duration-500 scroll-smooth",
1838
- children: /* @__PURE__ */ jsxs(DocsLayout$2.ContentMdx, {
1839
- className: "max-w-3xl sm:max-w-4xl lg:max-w-4xl px-2 pt-8 pb-24",
2130
+ children: /* @__PURE__ */ jsxs(DocsLayout$1.ContentMdx, {
2131
+ className: "max-w-3xl sm:max-w-4xl lg:max-w-5xl px-4 sm:px-6 pt-8 pb-24",
1840
2132
  children: [
1841
- /* @__PURE__ */ jsxs(DocsLayout$2.Header, { children: [
2133
+ !isCollectionPage && /* @__PURE__ */ jsxs(DocsLayout$1.Header, { children: [
1842
2134
  /* @__PURE__ */ jsxs("div", {
1843
2135
  className: "mb-4 border-b border-subtle pb-4 flex flex-wrap items-center justify-between gap-3",
1844
2136
  children: [/* @__PURE__ */ jsx(Breadcrumbs, {}), /* @__PURE__ */ jsx(CopyMarkdown, {
@@ -1859,7 +2151,7 @@ function DocsLayoutComponent({ children }) {
1859
2151
  className: "prose prose-neutral dark:prose-invert max-w-none",
1860
2152
  children
1861
2153
  }) }),
1862
- /* @__PURE__ */ jsx(DocsLayout$2.Footer, { children: /* @__PURE__ */ jsx(PageNav, {}) })
2154
+ /* @__PURE__ */ jsx(DocsLayout$1.Footer, { children: !isCollectionPage && /* @__PURE__ */ jsx(PageNav, {}) })
1863
2155
  ]
1864
2156
  })
1865
2157
  }),
@@ -1873,13 +2165,55 @@ function DocsLayoutComponent({ children }) {
1873
2165
  })]
1874
2166
  });
1875
2167
  }
1876
- const DocsLayout = Object.assign(DocsLayoutComponent, {
1877
- Body: DocsLayout$2.Body,
1878
- Content: DocsLayout$2.Content,
1879
- ContentMdx: DocsLayout$2.ContentMdx,
1880
- Header: DocsLayout$2.Header,
1881
- Footer: DocsLayout$2.Footer
1882
- });
1883
2168
 
1884
2169
  //#endregion
1885
- export { BoltdocsShell, Breadcrumbs, Card, Cards, CopyMarkdown, DocsLayout, ErrorBoundary, MdxPage, Navbar, NotFound, OnThisPage, PageNav, SearchDialog, Sidebar, ViteReactSSG, cn, copyToClipboard, createRoutes, getStarsRepo, getTranslated, reactToText, useAnalytics, useBreadcrumbs, useConfig, useI18n, useLocalizedTo, useLocation, useMdxComponents, useNavbar, usePageNav, useRoutes, useSearch, useSearchHighlight, useSidebar, useTabs, useTheme, useTrackEvent, useTrackPageView, useUI, useVersion };
2170
+ //#region src/client/collections/hooks.ts
2171
+ /**
2172
+ * Returns the posts of a collection.
2173
+ * @param collection - The name of the collection.
2174
+ * @returns The posts of the collection.
2175
+ */
2176
+ function usePosts(collection) {
2177
+ return useCollectionsData()[collection] || [];
2178
+ }
2179
+ /**
2180
+ * Returns a post by its slug.
2181
+ * @param collection - The name of the collection.
2182
+ * @param slug - The slug of the post.
2183
+ * @returns The post with the given slug.
2184
+ */
2185
+ function usePost(collection, slug) {
2186
+ const posts = usePosts(collection);
2187
+ return useMemo(() => posts.find((p) => p.path === `/${collection}/${slug}` || p.path.endsWith(`/${slug}`) || p.path === slug), [
2188
+ posts,
2189
+ collection,
2190
+ slug
2191
+ ]);
2192
+ }
2193
+ /**
2194
+ * Returns the recent posts of a collection.
2195
+ * @param collection - The name of the collection.
2196
+ * @param count - The number of recent posts to return.
2197
+ * @returns The recent posts of the collection.
2198
+ */
2199
+ function useRecentPosts(collection, count = 5) {
2200
+ const posts = usePosts(collection);
2201
+ return useMemo(() => posts.slice(0, count), [posts, count]);
2202
+ }
2203
+
2204
+ //#endregion
2205
+ //#region src/client/utils/react-to-text.ts
2206
+ const reactToText = (node, resolvers) => {
2207
+ if (node == null || typeof node === "boolean") return "";
2208
+ if (typeof node === "string" || typeof node === "number") return String(node);
2209
+ if (Array.isArray(node)) return node.map((n) => reactToText(n, resolvers)).join("");
2210
+ if (isValidElement(node)) {
2211
+ const resolver = resolvers?.get(node.type);
2212
+ if (resolver) return resolver(node.props);
2213
+ return reactToText(node.props.children, resolvers);
2214
+ }
2215
+ return "";
2216
+ };
2217
+
2218
+ //#endregion
2219
+ export { Banner, BoltdocsShell, Breadcrumbs, CollectionsContext, CopyMarkdown, DocsLayoutComponent as DocsLayout, ErrorBoundary, MdxPage, Navbar, NotFound, OnThisPage, PageNav, SearchDialog, Sidebar, ViteReactSSG, cn, copyToClipboard, createRoutes, getStarsRepo, getTranslated, reactToText, useAnalytics, useBreadcrumbs, useConfig, useHeadings, useI18n, useLocalizedTo, useLocation, useMdxComponents, useMergedComponents, useNavbar, usePageNav, usePost, usePosts, useRecentPosts, useRoutes, useSearch, useSearchHighlight, useSidebar, useTabs, useTheme, useTrackEvent, useTrackPageView, useUI, useVersion };