boltdocs 2.8.3 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/bin/boltdocs.js +7 -6
  2. package/dist/{banner-MynZD_Ox.d.cts → banner-C_xkGxzI.d.cts} +1 -1
  3. package/dist/{banner-3N4Jd_L9.d.ts → banner-D2NddBfF.d.ts} +1 -1
  4. package/dist/cache-C2PiGyg8.cjs +6 -0
  5. package/dist/cache-DFW4R3S0.mjs +6 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +693 -488
  8. package/dist/client/index.d.cts +74 -8
  9. package/dist/client/index.d.ts +74 -8
  10. package/dist/client/index.js +692 -489
  11. package/dist/client/mdx.cjs +1 -1
  12. package/dist/client/mdx.js +1 -1
  13. package/dist/client/primitives.cjs +4 -4
  14. package/dist/client/primitives.d.cts +1 -1
  15. package/dist/client/primitives.d.ts +1 -1
  16. package/dist/client/primitives.js +4 -4
  17. package/dist/client/theme/neutral.css +5 -3
  18. package/dist/client/theme/reset.css +19 -0
  19. package/dist/{docs-layout-CwCq42Zt.cjs → docs-layout-3TpAt6MB.cjs} +302 -248
  20. package/dist/{docs-layout-Dn6S5g59.js → docs-layout-6FQ8__ok.js} +303 -249
  21. package/dist/doctor-BY1tSmQd.mjs +34 -0
  22. package/dist/{doctor-DyNUVe96.cjs → doctor-Bknon8f5.cjs} +1 -1
  23. package/dist/doctor-DcFLi4t1.cjs +34 -0
  24. package/dist/{doctor-aN_leTbh.mjs → doctor-wsPTzt8Y.mjs} +1 -1
  25. package/dist/{generator-CC2yHzhZ.cjs → generator-BEIF6MOx.cjs} +2 -2
  26. package/dist/{generator-BHCrLU6h.mjs → generator-DILs0EFN.mjs} +1 -1
  27. package/dist/handler-B31cxNw0.mjs +6 -0
  28. package/dist/handler-DfhH9D1b.mjs +28 -0
  29. package/dist/handler-Djy0HfsI.cjs +28 -0
  30. package/dist/{icons-dev-DvJ-hh9x.cjs → icons-dev-BryEFkJu.cjs} +96 -7
  31. package/dist/{icons-dev-Oju24Wjp.js → icons-dev-kJyGWNeg.js} +79 -8
  32. package/dist/{image-Ch4-GxdO.cjs → image-B9YA_oQ_.cjs} +24 -10
  33. package/dist/{image-Do8V9PCW.js → image-CjYu9Eml.js} +24 -10
  34. package/dist/{mdx-5oeCOFhH.js → mdx-Cy_c2zio.js} +116 -33
  35. package/dist/{mdx-BGM7LjW5.cjs → mdx-WXjAMS8Y.cjs} +121 -32
  36. package/dist/{meta-loader-Cv9O0Pzl.cjs → meta-loader-D0V6oOQt.cjs} +1 -1
  37. package/dist/node/cli-entry.cjs +3 -3
  38. package/dist/node/cli-entry.mjs +3 -3
  39. package/dist/node/index.cjs +1 -1
  40. package/dist/node/index.d.cts +368 -24
  41. package/dist/node/index.d.mts +369 -24
  42. package/dist/node/index.mjs +1 -1
  43. package/dist/node/routes/worker.cjs +1 -1
  44. package/dist/node/routes/worker.mjs +1 -1
  45. package/dist/node-DV-EPtMU.mjs +160 -0
  46. package/dist/node-vOVNRABe.cjs +160 -0
  47. package/dist/{package-CHxJV4Jy.mjs → package-BK_82QLX.mjs} +1 -1
  48. package/dist/{package-B_SoaGeu.cjs → package-OxKE0p6j.cjs} +1 -1
  49. package/dist/{parser-B7-6PyQz.cjs → parser-C2Pw18LY.cjs} +1 -1
  50. package/dist/{parser-BzB-zCkF.cjs → parser-DJoEF96Y.cjs} +1 -1
  51. package/dist/{parser-D0PVghVt.mjs → parser-DScG_PTs.mjs} +1 -1
  52. package/dist/routes--LYHSHT7.cjs +6 -0
  53. package/dist/routes-BAsfsn_e.mjs +6 -0
  54. package/dist/routes-C6Kr2HFN.cjs +6 -0
  55. package/dist/routes-CKgGAy8P.mjs +6 -0
  56. package/dist/{search-dialog-BXVoecTx.cjs → search-dialog-BhXg05Ri.cjs} +1 -1
  57. package/dist/{search-dialog-BYhOov4S.cjs → search-dialog-BsAiQgnx.cjs} +2 -2
  58. package/dist/{search-dialog-C09riYmx.js → search-dialog-CaOO1Q4i.js} +2 -2
  59. package/dist/{search-dialog-CUeAfy-8.cjs → search-dialog-CzwvKoLW.cjs} +1 -1
  60. package/dist/{search-dialog-DHc_8FFX.js → search-dialog-DRNJsxOv.js} +1 -1
  61. package/dist/{search-dialog-D8gLkhUV.js → search-dialog-DaR66wl0.js} +1 -1
  62. package/dist/shiki-adapter-CUtj-xKv.cjs +6 -0
  63. package/dist/shiki-adapter-EZptJAmW.mjs +6 -0
  64. package/dist/shiki-adapter-F-P-lDon.mjs +6 -0
  65. package/dist/shiki-adapter-PQBl32AN.cjs +6 -0
  66. package/dist/{sidebar-Dlkgbxs6.d.cts → sidebar-BtJyWHy4.d.cts} +43 -4
  67. package/dist/{sidebar-DNq4_ZAa.d.ts → sidebar-C4fuRYYS.d.ts} +43 -4
  68. package/dist/{utils-BYITg7T5.mjs → utils-DC4Wq-S2.mjs} +1 -1
  69. package/dist/{utils-Cjmx1hhk.cjs → utils-wHud9ny5.cjs} +1 -1
  70. package/dist/worker-pool-BS1LX67P.cjs +6 -0
  71. package/dist/worker-pool-DRv8ef4z.mjs +6 -0
  72. package/package.json +7 -3
  73. package/src/shared/types.ts +36 -3
  74. package/dist/cache-BMUyNiiA.mjs +0 -6
  75. package/dist/cache-CKm45d2w.cjs +0 -6
  76. package/dist/chunk-CU-zTemE.cjs +0 -6
  77. package/dist/doctor-BArviV8X.cjs +0 -28
  78. package/dist/doctor-CgLA7_Uv.mjs +0 -28
  79. package/dist/node-MsTvOgJN.mjs +0 -159
  80. package/dist/node-h9HmhAFB.cjs +0 -159
  81. package/dist/routes-BW6Dif_K.mjs +0 -6
  82. package/dist/routes-DJNJ-rTt.cjs +0 -6
  83. package/dist/routes-DiYC4nD2.cjs +0 -6
  84. package/dist/routes-ERSGuFQK.mjs +0 -6
  85. package/dist/worker-pool-CtqklOXq.cjs +0 -6
  86. package/dist/worker-pool-k0DY6k8T.mjs +0 -6
  87. /package/dist/{meta-loader-DzwDFtdT.mjs → meta-loader-By22EnYa.mjs} +0 -0
@@ -3,12 +3,12 @@
3
3
  * Copyright (c) 2026 Jesus Alcala
4
4
  * Licensed under the MIT License.
5
5
  */
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";
6
+ import { $ as TextAlignStart, C as RoutesProvider, D as Check, E as ArrowLeft, F as FaceBad, I as FaceGood, J as Moon, K as Menu, L as FaceRegular, M as Copy, O as ChevronDown, P as ExternalLink, Q as Sun, S as normalizePath, U as Languages, V as Home, X as Pencil, _ as Link$1, at as useBoltdocsContext, b as useLocalizedTo, et as X, it as BoltdocsProvider, j as CircleHelp, nt as ConfigContext, q as Monitor, rt as useConfig, s as Github, tt as icons_exports, x as useRoutes, y as cn } from "../icons-dev-kJyGWNeg.js";
7
+ import { c as ThemeProvider, l as useTheme } from "../image-CjYu9Eml.js";
8
+ import { n as useCodeBlock, r as copyToClipboard, t as mdx_components_default } from "../mdx-Cy_c2zio.js";
9
+ import { A as useUI, C as useSidebar, D as ButtonGroup, O as Breadcrumbs$2, S as useLocation, T as Menu$1, f as PageNav$1, k as UIProvider, m as OnThisPage$1, n as Sidebar$1, t as DocsLayout$1, w as Tabs$1, x as useSearchHighlight } from "../docs-layout-6FQ8__ok.js";
10
+ import { a as Button$1, n as Navbar$1, r as ErrorBoundary$1 } from "../search-dialog-DaR66wl0.js";
11
+ import { n as useSearch, r as InternalErrorBoundary, t as SearchDialog } from "../search-dialog-CaOO1Q4i.js";
12
12
  import { ViteReactSSG } from "@bdocs/ssg";
13
13
  import { Suspense, createContext, isValidElement, lazy, use, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
14
  import { Link, Outlet, useLoaderData, useLocation as useLocation$1, useMatches, useNavigate } from "react-router-dom";
@@ -154,6 +154,13 @@ const mdxComponentsDefault = {
154
154
  "404": NotFound
155
155
  };
156
156
 
157
+ //#endregion
158
+ //#region src/client/collections/collections-context.tsx
159
+ const CollectionsContext = createContext({});
160
+ function useCollectionsData() {
161
+ return use(CollectionsContext);
162
+ }
163
+
157
164
  //#endregion
158
165
  //#region src/client/ssg/boltdocs-shell.tsx
159
166
  /**
@@ -189,7 +196,7 @@ function StoreSync({ config, routeMap }) {
189
196
  ]);
190
197
  return null;
191
198
  }
192
- function BoltdocsShell({ config, routes, components = {} }) {
199
+ function BoltdocsShell({ config, routes, components = {}, collectionsData }) {
193
200
  const allComponents = useMemo(() => ({
194
201
  ...mdxComponentsDefault,
195
202
  ...virtualCustomComponents,
@@ -226,20 +233,23 @@ function BoltdocsShell({ config, routes, components = {} }) {
226
233
  routes,
227
234
  children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(UIProvider, { children: /* @__PURE__ */ jsx(MdxComponentsProvider, {
228
235
  components: allComponents,
229
- children: /* @__PURE__ */ jsxs(ConfigContext.Provider, {
236
+ children: /* @__PURE__ */ jsx(ConfigContext.Provider, {
230
237
  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
- })]
238
+ children: /* @__PURE__ */ jsxs(CollectionsContext.Provider, {
239
+ value: collectionsData || {},
240
+ children: [/* @__PURE__ */ jsx(ScrollHandler, {}), /* @__PURE__ */ jsxs(BoltdocsProvider, {
241
+ initialLocale: initialData.initLocale,
242
+ initialVersion: initialData.initVersion,
243
+ children: [
244
+ /* @__PURE__ */ jsx(StoreSync, {
245
+ config,
246
+ routeMap
247
+ }),
248
+ /* @__PURE__ */ jsx(I18nUpdater, { config }),
249
+ /* @__PURE__ */ jsx(InternalErrorBoundary, { children: /* @__PURE__ */ jsx(Outlet, {}) })
250
+ ]
251
+ })]
252
+ })
243
253
  })
244
254
  }) }) })
245
255
  }) });
@@ -845,6 +855,30 @@ function useVersion() {
845
855
  };
846
856
  }
847
857
 
858
+ //#endregion
859
+ //#region src/client/components/ui-base/version-selector.tsx
860
+ function VersionSelector({ className }) {
861
+ const { currentVersionLabel, availableVersions, handleVersionChange } = useVersion();
862
+ if (availableVersions.length === 0) return null;
863
+ return /* @__PURE__ */ jsxs(Menu$1.Trigger, { children: [/* @__PURE__ */ jsxs(Button$1, {
864
+ className: cn("flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer", className),
865
+ children: [/* @__PURE__ */ jsx("span", {
866
+ className: "font-semibold text-[0.8125rem]",
867
+ children: currentVersionLabel
868
+ }), /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
869
+ }), /* @__PURE__ */ jsx(Menu$1.Root, {
870
+ className: "w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100",
871
+ children: /* @__PURE__ */ jsx(Menu$1.Section, {
872
+ items: availableVersions,
873
+ children: (version) => /* @__PURE__ */ jsx(Menu$1.Item, {
874
+ onPress: () => handleVersionChange(version.value),
875
+ className: "flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body hover:bg-primary-50/50 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5",
876
+ children: version.label
877
+ }, version.value)
878
+ })
879
+ })] });
880
+ }
881
+
848
882
  //#endregion
849
883
  //#region src/client/hooks/use-i18n.ts
850
884
  /**
@@ -924,209 +958,500 @@ function useI18n() {
924
958
  }
925
959
 
926
960
  //#endregion
927
- //#region src/client/components/ui-base/version-i18n.tsx
928
- function VersionSelector({ className }) {
929
- const { currentVersionLabel, availableVersions, handleVersionChange } = useVersion();
930
- if (availableVersions.length === 0) return null;
931
- return /* @__PURE__ */ jsxs(Menu$1.Trigger, { children: [/* @__PURE__ */ jsxs(Button$1, {
932
- className: cn("flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer", className),
933
- children: [/* @__PURE__ */ jsx("span", {
934
- className: "font-semibold text-[0.8125rem]",
935
- children: currentVersionLabel
936
- }), /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
937
- }), /* @__PURE__ */ jsx(Menu$1.Root, {
938
- className: "w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100",
939
- children: /* @__PURE__ */ jsx(Menu$1.Section, {
940
- items: availableVersions,
941
- children: (version) => /* @__PURE__ */ jsx(Menu$1.Item, {
942
- onPress: () => handleVersionChange(version.value),
943
- className: "flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body hover:bg-primary-50/50 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5",
944
- children: version.label
945
- }, `${version.value ?? ""}`)
946
- })
947
- })] });
948
- }
949
- function I18nSelector({ className }) {
950
- const { currentLocale, availableLocales, handleLocaleChange } = useI18n();
951
- if (availableLocales.length === 0) return null;
952
- return /* @__PURE__ */ jsxs(Menu$1.Trigger, { children: [/* @__PURE__ */ jsxs(Button$1, {
953
- className: cn("flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer", className),
954
- children: [/* @__PURE__ */ jsxs("div", {
955
- className: "flex items-center gap-1.5",
956
- children: [/* @__PURE__ */ jsx(Languages, { className: "w-3.5 h-3.5 text-primary-500" }), /* @__PURE__ */ jsx("span", {
957
- className: "font-bold text-[0.75rem] uppercase opacity-90",
958
- children: currentLocale || "en"
959
- })]
960
- }), /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
961
- }), /* @__PURE__ */ jsx(Menu$1.Root, {
962
- className: "w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100",
963
- children: /* @__PURE__ */ jsx(Menu$1.Section, {
964
- items: availableLocales,
965
- children: (locale) => /* @__PURE__ */ jsx(Menu$1.Item, {
966
- onPress: () => handleLocaleChange(locale.value),
967
- className: "flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5",
968
- children: /* @__PURE__ */ jsx("span", { children: locale.label })
969
- }, `${locale.value ?? ""}`)
970
- })
971
- })] });
961
+ //#region src/client/hooks/use-page-nav.ts
962
+ function usePageNav() {
963
+ const { routes, currentRoute } = useRoutes();
964
+ const location = useLocation$1();
965
+ const { groups, ungrouped } = useSidebar(routes);
966
+ return useMemo(() => {
967
+ if (!currentRoute) return {
968
+ prevPage: null,
969
+ nextPage: null,
970
+ currentRoute: null
971
+ };
972
+ const mergedItems = [...ungrouped.map((route) => ({
973
+ type: "link",
974
+ position: route.sidebarPosition ?? 999,
975
+ title: route.title,
976
+ route
977
+ })), ...groups.map((group) => ({
978
+ type: "group",
979
+ position: group.sidebarPosition ?? 999,
980
+ title: group.title,
981
+ group
982
+ }))].sort((a, b) => {
983
+ if (a.position !== b.position) return a.position - b.position;
984
+ if (a.type !== b.type) return a.type === "link" ? -1 : 1;
985
+ return a.title.localeCompare(b.title);
986
+ });
987
+ const orderedRoutes = [];
988
+ const flattenNode = (node) => {
989
+ if (node.path && node.path !== "#" && !node.sidebarHidden) orderedRoutes.push(node);
990
+ const children = node.routes || node.subRoutes;
991
+ if (children && children.length > 0) for (const child of children) flattenNode(child);
992
+ };
993
+ for (const item of mergedItems) if (item.type === "link") flattenNode(item.route);
994
+ else if (item.group.routes) for (const route of item.group.routes) flattenNode(route);
995
+ const currentNormalizedPath = normalizePath(location.pathname);
996
+ const currentIndex = orderedRoutes.findIndex((r) => normalizePath(r.path) === currentNormalizedPath);
997
+ return {
998
+ prevPage: currentIndex > 0 ? orderedRoutes[currentIndex - 1] : null,
999
+ nextPage: currentIndex !== -1 && currentIndex < orderedRoutes.length - 1 ? orderedRoutes[currentIndex + 1] : null,
1000
+ currentRoute
1001
+ };
1002
+ }, [
1003
+ groups,
1004
+ ungrouped,
1005
+ currentRoute,
1006
+ location.pathname
1007
+ ]);
972
1008
  }
973
1009
 
974
1010
  //#endregion
975
- //#region src/client/components/ui-base/navbar.tsx
976
- const SearchDialog$1 = lazy(() => import("../search-dialog-DHc_8FFX.js").then((m) => ({ default: m.SearchDialog })));
977
- function Navbar() {
978
- const { links, title, logo, logoProps, github, social, config } = useNavbar();
979
- const { routes, currentRoute, isCollectionPage, currentVersion, currentLocale } = useRoutes();
980
- const { isSidebarOpen, toggleSidebar } = useUI();
981
- const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
982
- const themeConfig = config.theme || {};
983
- const isDocs = !!currentRoute?.filePath && !isCollectionPage;
984
- const hasTabs = themeConfig?.tabs && themeConfig.tabs.length > 0;
985
- return /* @__PURE__ */ jsxs(Navbar$1.Root, {
986
- className: cn("border-b border-subtle bg-main/80 backdrop-blur-md", hasTabs && "border-b-0"),
987
- children: [
988
- /* @__PURE__ */ jsxs(Navbar$1.Content, { children: [
989
- /* @__PURE__ */ jsxs(Navbar$1.Left, { children: [
990
- isDocs && /* @__PURE__ */ jsx(Button$1, {
991
- onPress: toggleSidebar,
992
- className: "mr-2 lg:hidden p-1.5 h-8 w-8 flex items-center justify-center bg-transparent border-none outline-none select-none cursor-pointer rounded-xl hover:bg-primary-50/50 transition-colors",
993
- "aria-label": isSidebarOpen ? "Close sidebar" : "Open sidebar",
994
- children: isSidebarOpen ? /* @__PURE__ */ jsx(X, { className: "w-5 h-5 text-body" }) : /* @__PURE__ */ jsx(Menu, { className: "w-5 h-5 text-body" })
995
- }),
996
- logo && /* @__PURE__ */ jsx(Navbar$1.Logo, {
997
- src: logo,
998
- alt: logoProps?.alt || title,
999
- width: logoProps?.width ?? 24,
1000
- height: logoProps?.height ?? 24,
1001
- href: "site:/"
1002
- }),
1003
- /* @__PURE__ */ jsx(Navbar$1.Title, {
1004
- href: "site:/",
1005
- children: title
1006
- }),
1007
- /* @__PURE__ */ jsx("div", {
1008
- className: "hidden sm:block",
1009
- children: config.versions && currentVersion && /* @__PURE__ */ jsx(VersionSelector, {})
1010
- })
1011
- ] }),
1012
- /* @__PURE__ */ jsx(Navbar$1.Center, { children: /* @__PURE__ */ jsx(Suspense, {
1013
- fallback: /* @__PURE__ */ jsx("div", { className: "h-9 w-32 animate-pulse rounded-md bg-surface" }),
1014
- children: /* @__PURE__ */ jsx(SearchDialog$1, { routes: routes || [] })
1015
- }) }),
1016
- /* @__PURE__ */ jsxs(Navbar$1.Right, { children: [
1017
- /* @__PURE__ */ jsx(Suspense, {
1018
- fallback: null,
1019
- children: /* @__PURE__ */ jsx("div", {
1020
- className: "lg:hidden",
1021
- children: /* @__PURE__ */ jsx(SearchDialog$1, { routes: routes || [] })
1022
- })
1023
- }),
1024
- /* @__PURE__ */ jsx(Navbar$1.Links, { children: links.map((link) => /* @__PURE__ */ jsx(NavbarLinkItem, { link }, link.href)) }),
1025
- /* @__PURE__ */ jsxs("div", {
1026
- className: "hidden sm:flex items-center gap-2",
1027
- children: [config.i18n && currentLocale && /* @__PURE__ */ jsx(I18nSelector, {}), /* @__PURE__ */ jsx(Navbar$1.Split, { className: "bg-subtle" })]
1028
- }),
1029
- /* @__PURE__ */ jsx("div", {
1030
- className: "hidden md:block",
1031
- children: /* @__PURE__ */ jsx(ThemeToggle, {})
1032
- }),
1033
- github && /* @__PURE__ */ jsx("div", {
1034
- className: "hidden md:block",
1035
- children: /* @__PURE__ */ jsx(GithubStars, { repo: themeConfig?.githubRepo ?? "" })
1036
- }),
1037
- social.length > 0 && /* @__PURE__ */ jsx("div", {
1038
- className: "hidden md:block",
1039
- children: /* @__PURE__ */ jsx(Navbar$1.Split, { className: "bg-subtle" })
1040
- }),
1041
- /* @__PURE__ */ jsx("div", {
1042
- className: "hidden md:flex items-center gap-1",
1043
- children: social.map(({ icon, link }) => /* @__PURE__ */ jsx(Navbar$1.Socials, {
1044
- icon,
1045
- link,
1046
- className: "p-1.5 text-muted hover:text-body hover:bg-surface rounded-md transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
1047
- }, link))
1048
- }),
1049
- /* @__PURE__ */ jsx(Navbar$1.More, {
1050
- onPress: () => setIsMobileMenuOpen(true),
1051
- className: "text-muted hover:text-body active:scale-90 transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
1052
- })
1053
- ] })
1054
- ] }),
1055
- /* @__PURE__ */ jsxs(Navbar$1.MobileMenu, {
1056
- isOpen: isMobileMenuOpen,
1057
- onClose: () => setIsMobileMenuOpen(false),
1058
- className: "bg-main/98 backdrop-blur-2xl",
1059
- children: [/* @__PURE__ */ jsx("div", {
1060
- className: "flex flex-col gap-1",
1061
- children: links.map((link) => /* @__PURE__ */ jsx(NavbarMobileLinkItem, {
1062
- link,
1063
- onClose: () => setIsMobileMenuOpen(false)
1064
- }, link.href))
1065
- }), social.length > 0 && /* @__PURE__ */ jsxs("div", {
1066
- className: "mt-6",
1067
- children: [/* @__PURE__ */ jsx("div", {
1068
- className: "px-4 mb-4 text-xs font-bold uppercase tracking-widest text-muted/50",
1069
- children: "Connect"
1070
- }), /* @__PURE__ */ jsx("div", {
1071
- className: "flex flex-wrap gap-2 px-2",
1072
- children: social.map(({ icon, link }) => /* @__PURE__ */ jsx(Navbar$1.Socials, {
1073
- icon,
1074
- link,
1075
- className: "p-3 bg-surface border border-subtle rounded-xl flex-1 justify-center"
1076
- }, link))
1077
- })]
1078
- })]
1079
- }),
1080
- isDocs && hasTabs && themeConfig?.tabs && /* @__PURE__ */ jsx("div", {
1081
- className: "w-full border-b border-subtle bg-main",
1082
- children: /* @__PURE__ */ jsx(Tabs, {
1083
- tabs: themeConfig.tabs,
1084
- routes: routes || []
1085
- })
1086
- })
1087
- ]
1088
- });
1089
- }
1090
- function NavbarLinkItem({ link }) {
1091
- const localizedHref = useLocalizedTo(link.href || "");
1092
- const { pathname } = useLocation$1();
1093
- const active = pathname === localizedHref || pathname.startsWith(localizedHref + "/");
1094
- if (link.items && link.items.length > 0) return /* @__PURE__ */ jsx(Navbar$1.Dropdown, {
1095
- label: /* @__PURE__ */ jsx("span", {
1096
- className: cn("transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm px-2 py-1", active ? "text-primary-500" : "text-muted hover:text-body"),
1097
- children: link.label
1098
- }),
1099
- children: link.items?.map((item) => /* @__PURE__ */ jsx(Navbar$1.DropdownItem, {
1100
- href: useLocalizedTo(item.href || ""),
1101
- label: item.label
1102
- }, item.href))
1103
- });
1104
- return /* @__PURE__ */ jsx(Navbar$1.Link, {
1105
- ...link,
1106
- href: localizedHref,
1107
- active,
1108
- className: cn("transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm", active ? "text-primary-500" : "text-muted hover:text-body")
1109
- });
1011
+ //#region src/client/hooks/use-analytics.ts
1012
+ function createAnalyticsInstance(config) {
1013
+ if (typeof window === "undefined") return createDisabledAnalytics();
1014
+ if (typeof window.gtag === "function") return createGtagAnalytics(config);
1015
+ if (window.dataLayer) return createDataLayerAnalytics(config);
1016
+ return createDisabledAnalytics();
1110
1017
  }
1111
- function NavbarMobileLinkItem({ link, onClose }) {
1112
- const localizedHref = useLocalizedTo(link.href || "");
1113
- const { pathname } = useLocation$1();
1114
- const active = pathname === localizedHref;
1115
- if (link.items && link.items.length > 0) return /* @__PURE__ */ jsxs("div", {
1116
- className: "flex flex-col gap-1",
1117
- children: [/* @__PURE__ */ jsx("div", {
1118
- className: cn("px-3 py-2 text-sm transition-all", active ? "text-body" : "text-muted/80 hover:text-body"),
1119
- children: link.label
1120
- }), /* @__PURE__ */ jsx("div", {
1121
- className: "flex flex-col gap-1 pl-4",
1122
- children: link.items?.map((item) => /* @__PURE__ */ jsx(NavbarMobileLinkItem, {
1123
- link: item,
1124
- onClose
1125
- }, item.href))
1126
- })]
1127
- });
1128
- return /* @__PURE__ */ jsx(Navbar$1.MobileLink, {
1129
- ...link,
1018
+ function createGtagAnalytics(config) {
1019
+ return {
1020
+ trackPageView: (path, title) => {
1021
+ window.gtag?.("event", "page_view", {
1022
+ page_path: path,
1023
+ page_title: title || document.title,
1024
+ send_to: config?.ga4?.measurementId
1025
+ });
1026
+ },
1027
+ trackEvent: ({ action, category, label, value, params }) => {
1028
+ window.gtag?.("event", action, {
1029
+ event_category: category,
1030
+ event_label: label,
1031
+ value,
1032
+ send_to: config?.ga4?.measurementId,
1033
+ ...params
1034
+ });
1035
+ },
1036
+ trackSearch: (query, resultsCount) => {
1037
+ window.gtag?.("event", "search", {
1038
+ search_term: query,
1039
+ results_count: resultsCount,
1040
+ send_to: config?.ga4?.measurementId
1041
+ });
1042
+ },
1043
+ trackDownload: (file, type) => {
1044
+ window.gtag?.("event", "file_download", {
1045
+ file_name: file,
1046
+ file_type: type || file.split(".").pop(),
1047
+ send_to: config?.ga4?.measurementId
1048
+ });
1049
+ },
1050
+ trackExternalLink: (url) => {
1051
+ window.gtag?.("event", "external_link", {
1052
+ link_url: url,
1053
+ send_to: config?.ga4?.measurementId
1054
+ });
1055
+ },
1056
+ isEnabled: true
1057
+ };
1058
+ }
1059
+ function createDataLayerAnalytics(config) {
1060
+ return {
1061
+ trackPageView: (path, title) => {
1062
+ window.dataLayer?.push({
1063
+ event: "page_view",
1064
+ page_path: path,
1065
+ page_title: title || document.title,
1066
+ send_to: config?.gtm?.tagId
1067
+ });
1068
+ },
1069
+ trackEvent: ({ action, category, label, value, params }) => {
1070
+ window.dataLayer?.push({
1071
+ event: action,
1072
+ event_category: category,
1073
+ event_label: label,
1074
+ value,
1075
+ send_to: config?.gtm?.tagId,
1076
+ ...params
1077
+ });
1078
+ },
1079
+ trackSearch: (query, resultsCount) => {
1080
+ window.dataLayer?.push({
1081
+ event: "search",
1082
+ search_term: query,
1083
+ results_count: resultsCount,
1084
+ send_to: config?.gtm?.tagId
1085
+ });
1086
+ },
1087
+ trackDownload: (file, type) => {
1088
+ window.dataLayer?.push({
1089
+ event: "file_download",
1090
+ file_name: file,
1091
+ file_type: type || file.split(".").pop(),
1092
+ send_to: config?.gtm?.tagId
1093
+ });
1094
+ },
1095
+ trackExternalLink: (url) => {
1096
+ window.dataLayer?.push({
1097
+ event: "external_link",
1098
+ link_url: url,
1099
+ send_to: config?.gtm?.tagId
1100
+ });
1101
+ },
1102
+ isEnabled: true
1103
+ };
1104
+ }
1105
+ function createDisabledAnalytics() {
1106
+ return {
1107
+ trackPageView: () => {},
1108
+ trackEvent: () => {},
1109
+ trackSearch: () => {},
1110
+ trackDownload: () => {},
1111
+ trackExternalLink: () => {},
1112
+ isEnabled: false
1113
+ };
1114
+ }
1115
+ const CONFIG_INSTANCE_SYMBOL = Symbol.for("__BDOCS_CONFIG_INSTANCE__");
1116
+ function useAnalytics(options = {}) {
1117
+ const { config: optionsConfig, autoTrackPageViews = true, autoTrackDownloads = true, autoTrackExternalLinks = true, excludePatterns = [] } = options;
1118
+ const globalConfig = typeof globalThis !== "undefined" ? globalThis[CONFIG_INSTANCE_SYMBOL] : void 0;
1119
+ const config = optionsConfig ?? globalConfig?.integrations;
1120
+ const analytics = useMemo(() => createAnalyticsInstance(config), [config]);
1121
+ const previousPath = useRef("");
1122
+ const location = useLocation();
1123
+ useEffect(() => {
1124
+ if (!autoTrackPageViews || !analytics.isEnabled) return;
1125
+ const path = location.pathname + location.search;
1126
+ if (path !== previousPath.current) {
1127
+ previousPath.current = path;
1128
+ analytics.trackPageView(path, document.title);
1129
+ }
1130
+ }, [
1131
+ location.pathname,
1132
+ autoTrackPageViews,
1133
+ analytics
1134
+ ]);
1135
+ useEffect(() => {
1136
+ if (!autoTrackDownloads || !analytics.isEnabled) return;
1137
+ const handleClick = (event) => {
1138
+ const target = event.target?.closest("a");
1139
+ if (!target) return;
1140
+ const href = target.getAttribute("href");
1141
+ if (!href) return;
1142
+ if (excludePatterns.some((pattern) => pattern.test(href))) return;
1143
+ if (target.hasAttribute("download") || /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar|7z|tar|gz|mp3|mp4|avi|mov|png|jpg|jpeg|gif|svg|webp)$/i.test(href)) {
1144
+ const fileName = href.split("/").pop() || href;
1145
+ analytics.trackDownload(fileName, fileName.split(".").pop());
1146
+ }
1147
+ };
1148
+ document.addEventListener("click", handleClick);
1149
+ return () => document.removeEventListener("click", handleClick);
1150
+ }, [
1151
+ autoTrackDownloads,
1152
+ autoTrackExternalLinks,
1153
+ analytics,
1154
+ excludePatterns
1155
+ ]);
1156
+ useEffect(() => {
1157
+ if (!autoTrackExternalLinks || !analytics.isEnabled) return;
1158
+ const handleClick = (event) => {
1159
+ const target = event.target?.closest("a");
1160
+ if (!target) return;
1161
+ const href = target.getAttribute("href");
1162
+ if (!href) return;
1163
+ if (excludePatterns.some((pattern) => pattern.test(href))) return;
1164
+ if ((href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) && !href.includes(window.location.hostname)) analytics.trackExternalLink(href);
1165
+ };
1166
+ document.addEventListener("click", handleClick);
1167
+ return () => document.removeEventListener("click", handleClick);
1168
+ }, [
1169
+ autoTrackExternalLinks,
1170
+ analytics,
1171
+ excludePatterns
1172
+ ]);
1173
+ return analytics;
1174
+ }
1175
+ function useTrackPageView() {
1176
+ const analytics = useMemo(() => createAnalyticsInstance(), []);
1177
+ return useCallback((path, title) => {
1178
+ analytics.trackPageView(path, title);
1179
+ }, [analytics]);
1180
+ }
1181
+ function useTrackEvent() {
1182
+ const analytics = useMemo(() => createAnalyticsInstance(), []);
1183
+ return useCallback((event) => {
1184
+ analytics.trackEvent(event);
1185
+ }, [analytics]);
1186
+ }
1187
+
1188
+ //#endregion
1189
+ //#region src/client/hooks/use-headings.ts
1190
+ /**
1191
+ * Returns the headings of the current page, extracted from the route loader data.
1192
+ * Useful for building custom Tables of Contents or jumping to sections.
1193
+ *
1194
+ * @returns An array of heading objects with level, text, and id.
1195
+ */
1196
+ function useHeadings() {
1197
+ const matches = useMatches();
1198
+ return useMemo(() => {
1199
+ return (matches[matches.length - 1]?.data)?.headings || [];
1200
+ }, [matches]);
1201
+ }
1202
+
1203
+ //#endregion
1204
+ //#region src/client/hooks/use-feedback.ts
1205
+ /**
1206
+ * Hook for managing the feedback rating form state and submission.
1207
+ * Resets automatically when navigating to a new page.
1208
+ */
1209
+ function useFeedback(options = {}) {
1210
+ const config = useConfig();
1211
+ const { currentRoute } = useRoutes();
1212
+ const [rating, setRating] = useState(null);
1213
+ const [comment, setComment] = useState("");
1214
+ const [loading, setLoading] = useState(false);
1215
+ const [submitted, setSubmitted] = useState(false);
1216
+ const [error, setError] = useState(null);
1217
+ useEffect(() => {
1218
+ setRating(null);
1219
+ setComment("");
1220
+ setSubmitted(false);
1221
+ setError(null);
1222
+ }, [currentRoute?.path]);
1223
+ const submit = async (commentOverride, blockId) => {
1224
+ if (!rating) return;
1225
+ setLoading(true);
1226
+ setError(null);
1227
+ const payload = {
1228
+ rating,
1229
+ comment: (commentOverride !== void 0 ? commentOverride : comment) || void 0,
1230
+ path: currentRoute?.path || window.location.pathname,
1231
+ title: currentRoute?.title || document.title,
1232
+ blockId
1233
+ };
1234
+ try {
1235
+ if (options.onSubmit) await options.onSubmit(payload);
1236
+ else {
1237
+ const endpoint = options.endpoint || config.integrations?.feedback?.custom?.endpoint || "/api/feedback";
1238
+ const res = await fetch(endpoint, {
1239
+ method: "POST",
1240
+ headers: { "Content-Type": "application/json" },
1241
+ body: JSON.stringify(payload)
1242
+ });
1243
+ if (!res.ok) {
1244
+ const text = await res.text();
1245
+ let errorText = text;
1246
+ try {
1247
+ const data = JSON.parse(text);
1248
+ errorText = data.error || data.message || text;
1249
+ } catch {}
1250
+ throw new Error(errorText || `Failed to submit feedback: ${res.statusText}`);
1251
+ }
1252
+ }
1253
+ setSubmitted(true);
1254
+ } catch (e) {
1255
+ setError(e.message || "An error occurred while submitting feedback.");
1256
+ } finally {
1257
+ setLoading(false);
1258
+ }
1259
+ };
1260
+ return {
1261
+ rating,
1262
+ setRating,
1263
+ comment,
1264
+ setComment,
1265
+ loading,
1266
+ submitted,
1267
+ error,
1268
+ submit
1269
+ };
1270
+ }
1271
+
1272
+ //#endregion
1273
+ //#region src/client/components/ui-base/i18n-selector.tsx
1274
+ function I18nSelector({ className }) {
1275
+ const { currentLocale, availableLocales, handleLocaleChange } = useI18n();
1276
+ if (availableLocales.length === 0) return null;
1277
+ return /* @__PURE__ */ jsxs(Menu$1.Trigger, { children: [/* @__PURE__ */ jsxs(Button$1, {
1278
+ className: cn("flex h-9 items-center justify-between gap-2 border border-subtle bg-surface px-4 py-1.5 rounded-xl text-xs font-semibold text-body hover:bg-primary-50/20 hover:border-primary-500/50 transition-all duration-300 outline-none select-none cursor-pointer", className),
1279
+ children: [/* @__PURE__ */ jsxs("div", {
1280
+ className: "flex items-center gap-1.5",
1281
+ children: [/* @__PURE__ */ jsx(Languages, { className: "w-3.5 h-3.5 text-primary-500" }), /* @__PURE__ */ jsx("span", {
1282
+ className: "font-bold text-[0.75rem] uppercase opacity-90",
1283
+ children: currentLocale || "en"
1284
+ })]
1285
+ }), /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
1286
+ }), /* @__PURE__ */ jsx(Menu$1.Root, {
1287
+ className: "w-40 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100",
1288
+ children: /* @__PURE__ */ jsx(Menu$1.Section, {
1289
+ items: availableLocales,
1290
+ children: (locale) => /* @__PURE__ */ jsx(Menu$1.Item, {
1291
+ onPress: () => handleLocaleChange(locale.value),
1292
+ className: "flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group data-selected:text-primary-500 data-selected:bg-primary-500/5",
1293
+ children: /* @__PURE__ */ jsx("span", { children: locale.label })
1294
+ }, locale.value)
1295
+ })
1296
+ })] });
1297
+ }
1298
+
1299
+ //#endregion
1300
+ //#region src/client/components/ui-base/navbar.tsx
1301
+ const SearchDialog$1 = lazy(() => import("../search-dialog-DRNJsxOv.js").then((m) => ({ default: m.SearchDialog })));
1302
+ function Navbar() {
1303
+ const { links, title, logo, logoProps, github, social, config } = useNavbar();
1304
+ const { routes, currentRoute, isCollectionPage, currentVersion, currentLocale } = useRoutes();
1305
+ const { isSidebarOpen, toggleSidebar } = useUI();
1306
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
1307
+ const themeConfig = config.theme || {};
1308
+ const isDocs = !!currentRoute?.filePath && !isCollectionPage;
1309
+ const hasTabs = themeConfig?.tabs && themeConfig.tabs.length > 0;
1310
+ return /* @__PURE__ */ jsxs(Navbar$1.Root, {
1311
+ className: cn("border-b border-subtle bg-main/80 backdrop-blur-md", hasTabs && "border-b-0"),
1312
+ children: [
1313
+ /* @__PURE__ */ jsxs(Navbar$1.Content, { children: [
1314
+ /* @__PURE__ */ jsxs(Navbar$1.Left, { children: [
1315
+ isDocs && /* @__PURE__ */ jsx(Button$1, {
1316
+ onPress: toggleSidebar,
1317
+ className: "mr-2 lg:hidden p-1.5 h-8 w-8 flex items-center justify-center bg-transparent border-none outline-none select-none cursor-pointer rounded-xl hover:bg-primary-50/50 transition-colors",
1318
+ "aria-label": isSidebarOpen ? "Close sidebar" : "Open sidebar",
1319
+ children: isSidebarOpen ? /* @__PURE__ */ jsx(X, { className: "w-5 h-5 text-body" }) : /* @__PURE__ */ jsx(Menu, { className: "w-5 h-5 text-body" })
1320
+ }),
1321
+ logo && /* @__PURE__ */ jsx(Navbar$1.Logo, {
1322
+ src: logo,
1323
+ alt: logoProps?.alt || title,
1324
+ width: logoProps?.width ?? 24,
1325
+ height: logoProps?.height ?? 24,
1326
+ href: "site:/"
1327
+ }),
1328
+ /* @__PURE__ */ jsx(Navbar$1.Title, {
1329
+ href: "site:/",
1330
+ children: title
1331
+ }),
1332
+ /* @__PURE__ */ jsx("div", {
1333
+ className: "hidden sm:block",
1334
+ children: config.versions && currentVersion && /* @__PURE__ */ jsx(VersionSelector, {})
1335
+ })
1336
+ ] }),
1337
+ /* @__PURE__ */ jsx(Navbar$1.Center, { children: /* @__PURE__ */ jsx(Suspense, {
1338
+ fallback: /* @__PURE__ */ jsx("div", { className: "h-9 w-32 animate-pulse rounded-md bg-surface" }),
1339
+ children: /* @__PURE__ */ jsx(SearchDialog$1, { routes: routes || [] })
1340
+ }) }),
1341
+ /* @__PURE__ */ jsxs(Navbar$1.Right, { children: [
1342
+ /* @__PURE__ */ jsx(Suspense, {
1343
+ fallback: null,
1344
+ children: /* @__PURE__ */ jsx("div", {
1345
+ className: "lg:hidden",
1346
+ children: /* @__PURE__ */ jsx(SearchDialog$1, { routes: routes || [] })
1347
+ })
1348
+ }),
1349
+ /* @__PURE__ */ jsx(Navbar$1.Links, { children: links.map((link) => /* @__PURE__ */ jsx(NavbarLinkItem, { link }, link.href)) }),
1350
+ /* @__PURE__ */ jsxs("div", {
1351
+ className: "hidden sm:flex items-center gap-2",
1352
+ children: [config.i18n && currentLocale && /* @__PURE__ */ jsx(I18nSelector, {}), /* @__PURE__ */ jsx(Navbar$1.Split, { className: "bg-subtle" })]
1353
+ }),
1354
+ /* @__PURE__ */ jsx("div", {
1355
+ className: "hidden md:block",
1356
+ children: /* @__PURE__ */ jsx(ThemeToggle, {})
1357
+ }),
1358
+ github && /* @__PURE__ */ jsx("div", {
1359
+ className: "hidden md:block",
1360
+ children: /* @__PURE__ */ jsx(GithubStars, { repo: themeConfig?.githubRepo ?? "" })
1361
+ }),
1362
+ social.length > 0 && /* @__PURE__ */ jsx("div", {
1363
+ className: "hidden md:block",
1364
+ children: /* @__PURE__ */ jsx(Navbar$1.Split, { className: "bg-subtle" })
1365
+ }),
1366
+ /* @__PURE__ */ jsx("div", {
1367
+ className: "hidden md:flex items-center gap-1",
1368
+ children: social.map(({ icon, link }) => /* @__PURE__ */ jsx(Navbar$1.Socials, {
1369
+ icon,
1370
+ link,
1371
+ className: "p-1.5 text-muted hover:text-body hover:bg-surface rounded-md transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
1372
+ }, link))
1373
+ }),
1374
+ /* @__PURE__ */ jsx(Navbar$1.More, {
1375
+ onPress: () => setIsMobileMenuOpen(true),
1376
+ className: "text-muted hover:text-body active:scale-90 transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
1377
+ })
1378
+ ] })
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxs(Navbar$1.MobileMenu, {
1381
+ isOpen: isMobileMenuOpen,
1382
+ onClose: () => setIsMobileMenuOpen(false),
1383
+ className: "bg-main/98 backdrop-blur-2xl",
1384
+ children: [/* @__PURE__ */ jsx("div", {
1385
+ className: "flex flex-col gap-1",
1386
+ children: links.map((link) => /* @__PURE__ */ jsx(NavbarMobileLinkItem, {
1387
+ link,
1388
+ onClose: () => setIsMobileMenuOpen(false)
1389
+ }, link.href))
1390
+ }), social.length > 0 && /* @__PURE__ */ jsxs("div", {
1391
+ className: "mt-6",
1392
+ children: [/* @__PURE__ */ jsx("div", {
1393
+ className: "px-4 mb-4 text-xs font-bold uppercase tracking-widest text-muted/50",
1394
+ children: "Connect"
1395
+ }), /* @__PURE__ */ jsx("div", {
1396
+ className: "flex flex-wrap gap-2 px-2",
1397
+ children: social.map(({ icon, link }) => /* @__PURE__ */ jsx(Navbar$1.Socials, {
1398
+ icon,
1399
+ link,
1400
+ className: "p-3 bg-surface border border-subtle rounded-xl flex-1 justify-center"
1401
+ }, link))
1402
+ })]
1403
+ })]
1404
+ }),
1405
+ isDocs && hasTabs && themeConfig?.tabs && /* @__PURE__ */ jsx("div", {
1406
+ className: "w-full border-b border-subtle bg-main",
1407
+ children: /* @__PURE__ */ jsx(Tabs, {
1408
+ tabs: themeConfig.tabs,
1409
+ routes: routes || []
1410
+ })
1411
+ })
1412
+ ]
1413
+ });
1414
+ }
1415
+ function NavbarLinkItem({ link }) {
1416
+ const localizedHref = useLocalizedTo(link.href || "");
1417
+ const { pathname } = useLocation$1();
1418
+ const active = pathname === localizedHref || pathname.startsWith(`${localizedHref}/`);
1419
+ if (link.items && link.items.length > 0) return /* @__PURE__ */ jsx(Navbar$1.Dropdown, {
1420
+ label: /* @__PURE__ */ jsx("span", {
1421
+ className: cn("transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm px-2 py-1", active ? "text-primary-500" : "text-muted hover:text-body"),
1422
+ children: link.label
1423
+ }),
1424
+ children: link.items?.map((item) => /* @__PURE__ */ jsx(Navbar$1.DropdownItem, {
1425
+ href: useLocalizedTo(item.href || ""),
1426
+ label: item.label
1427
+ }, item.href))
1428
+ });
1429
+ return /* @__PURE__ */ jsx(Navbar$1.Link, {
1430
+ ...link,
1431
+ href: localizedHref,
1432
+ active,
1433
+ className: cn("transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm", active ? "text-primary-500" : "text-muted hover:text-body")
1434
+ });
1435
+ }
1436
+ function NavbarMobileLinkItem({ link, onClose }) {
1437
+ const localizedHref = useLocalizedTo(link.href || "");
1438
+ const { pathname } = useLocation$1();
1439
+ const active = pathname === localizedHref;
1440
+ if (link.items && link.items.length > 0) return /* @__PURE__ */ jsxs("div", {
1441
+ className: "flex flex-col gap-1",
1442
+ children: [/* @__PURE__ */ jsx("div", {
1443
+ className: cn("px-3 py-2 text-sm transition-all", active ? "text-body" : "text-muted/80 hover:text-body"),
1444
+ children: link.label
1445
+ }), /* @__PURE__ */ jsx("div", {
1446
+ className: "flex flex-col gap-1 pl-4",
1447
+ children: link.items?.map((item) => /* @__PURE__ */ jsx(NavbarMobileLinkItem, {
1448
+ link: item,
1449
+ onClose
1450
+ }, item.href))
1451
+ })]
1452
+ });
1453
+ return /* @__PURE__ */ jsx(Navbar$1.MobileLink, {
1454
+ ...link,
1130
1455
  href: localizedHref,
1131
1456
  active,
1132
1457
  onPress: onClose,
@@ -1145,58 +1470,28 @@ function OnThisPage({ headings = [], editLink, communityHelp, filePath }) {
1145
1470
  }),
1146
1471
  /* @__PURE__ */ jsx(OnThisPage$1.Tree, { headings }),
1147
1472
  (editLink || communityHelp) && /* @__PURE__ */ jsxs("div", {
1148
- className: "mt-8 pt-8 border-t border-subtle space-y-4",
1149
- children: [/* @__PURE__ */ jsx("p", {
1150
- className: "text-xs font-bold text-body",
1151
- children: "Need help?"
1152
- }), /* @__PURE__ */ jsxs("ul", {
1153
- className: "space-y-3",
1154
- children: [editLink && filePath && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
1155
- href: editLink.replace(":path", filePath),
1156
- target: "_blank",
1157
- rel: "noopener noreferrer",
1158
- className: "flex items-center gap-2 text-sm text-muted hover:text-body transition-colors",
1159
- children: [/* @__PURE__ */ jsx(Pencil, { size: 16 }), "Edit this page"]
1160
- }) }), communityHelp && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
1161
- href: communityHelp,
1162
- target: "_blank",
1163
- rel: "noopener noreferrer",
1164
- className: "flex items-center gap-2 text-sm text-muted hover:text-body transition-colors",
1165
- children: [/* @__PURE__ */ jsx(CircleHelp, { size: 16 }), "Community help"]
1166
- }) })]
1167
- })]
1168
- })
1169
- ] });
1170
- }
1171
-
1172
- //#endregion
1173
- //#region src/client/hooks/use-page-nav.ts
1174
- /**
1175
- * Hook to manage the previous and next button functionality for documentation pages.
1176
- * Intelligent: respects current locale, version, and tab to keep navigation logical.
1177
- */
1178
- function usePageNav() {
1179
- const { routes, currentRoute } = useRoutes();
1180
- const location = useLocation$1();
1181
- return useMemo(() => {
1182
- if (!currentRoute) return {
1183
- prevPage: null,
1184
- nextPage: null,
1185
- currentRoute: null
1186
- };
1187
- const activeTabId = currentRoute.tab?.toLowerCase();
1188
- const contextRoutes = activeTabId ? routes.filter((r) => r.tab?.toLowerCase() === activeTabId) : routes.filter((r) => !r.tab);
1189
- const currentIndex = contextRoutes.findIndex((r) => r.path === location.pathname);
1190
- return {
1191
- prevPage: currentIndex > 0 ? contextRoutes[currentIndex - 1] : null,
1192
- nextPage: currentIndex !== -1 && currentIndex < contextRoutes.length - 1 ? contextRoutes[currentIndex + 1] : null,
1193
- currentRoute
1194
- };
1195
- }, [
1196
- routes,
1197
- currentRoute,
1198
- location.pathname
1199
- ]);
1473
+ className: "mt-8 pt-8 border-t border-subtle space-y-4",
1474
+ children: [/* @__PURE__ */ jsx("p", {
1475
+ className: "text-xs font-bold text-body",
1476
+ children: "Need help?"
1477
+ }), /* @__PURE__ */ jsxs("ul", {
1478
+ className: "space-y-3",
1479
+ children: [editLink && filePath && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
1480
+ href: editLink.replace(":path", filePath),
1481
+ target: "_blank",
1482
+ rel: "noopener noreferrer",
1483
+ className: "flex items-center gap-2 text-sm text-muted hover:text-body transition-colors",
1484
+ children: [/* @__PURE__ */ jsx(Pencil, { size: 16 }), "Edit this page"]
1485
+ }) }), communityHelp && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
1486
+ href: communityHelp,
1487
+ target: "_blank",
1488
+ rel: "noopener noreferrer",
1489
+ className: "flex items-center gap-2 text-sm text-muted hover:text-body transition-colors",
1490
+ children: [/* @__PURE__ */ jsx(CircleHelp, { size: 16 }), "Community help"]
1491
+ }) })]
1492
+ })]
1493
+ })
1494
+ ] });
1200
1495
  }
1201
1496
 
1202
1497
  //#endregion
@@ -1288,13 +1583,15 @@ const Sidebar = Object.assign(SidebarMain, {
1288
1583
  function Banner({ children, className = "", dismissible = false, id = "boltdocs-banner", ...props }) {
1289
1584
  const [isVisible, setIsVisible] = useState(true);
1290
1585
  useEffect(() => {
1291
- if (dismissible && id) {
1292
- if (localStorage.getItem(`boltdocs-banner-dismissed-${id}`) === "true") setIsVisible(false);
1293
- }
1586
+ if (dismissible && id && typeof window !== "undefined") try {
1587
+ if (window.localStorage.getItem(`boltdocs-banner-dismissed-${id}`) === "true") setIsVisible(false);
1588
+ } catch {}
1294
1589
  }, [dismissible, id]);
1295
1590
  const handleDismiss = () => {
1296
1591
  setIsVisible(false);
1297
- if (dismissible && id) localStorage.setItem(`boltdocs-banner-dismissed-${id}`, "true");
1592
+ if (dismissible && id && typeof window !== "undefined") try {
1593
+ window.localStorage.setItem(`boltdocs-banner-dismissed-${id}`, "true");
1594
+ } catch {}
1298
1595
  };
1299
1596
  if (!isVisible) return null;
1300
1597
  return /* @__PURE__ */ jsxs("div", {
@@ -1312,6 +1609,90 @@ function Banner({ children, className = "", dismissible = false, id = "boltdocs-
1312
1609
  });
1313
1610
  }
1314
1611
 
1612
+ //#endregion
1613
+ //#region src/client/components/ui-base/feedback.tsx
1614
+ function Feedback({ className }) {
1615
+ if (!(useConfig().integrations?.feedback?.custom)?.enabled) return null;
1616
+ const { rating, setRating, comment, setComment, loading, submitted, error, submit } = useFeedback();
1617
+ return /* @__PURE__ */ jsx("div", {
1618
+ className: cn("w-full max-w-2xl mt-12 mb-6 p-6 rounded-2xl border border-subtle bg-surface/50 backdrop-blur-xs select-none", className),
1619
+ children: submitted ? /* @__PURE__ */ jsxs("div", {
1620
+ className: "flex flex-col items-center justify-center py-4 text-center animate-in fade-in zoom-in duration-300",
1621
+ children: [
1622
+ /* @__PURE__ */ jsx("div", {
1623
+ className: "flex h-12 w-12 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500 mb-3 border border-emerald-500/20",
1624
+ children: /* @__PURE__ */ jsx(Check, { size: 24 })
1625
+ }),
1626
+ /* @__PURE__ */ jsx("h3", {
1627
+ className: "text-lg font-semibold text-default",
1628
+ children: "Thank you for your feedback!"
1629
+ }),
1630
+ /* @__PURE__ */ jsx("p", {
1631
+ className: "text-sm text-muted mt-1",
1632
+ children: "Your comments help us improve the documentation."
1633
+ })
1634
+ ]
1635
+ }) : /* @__PURE__ */ jsxs("div", {
1636
+ className: "flex flex-col gap-4",
1637
+ children: [/* @__PURE__ */ jsxs("div", {
1638
+ className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
1639
+ children: [/* @__PURE__ */ jsx("h4", {
1640
+ className: "text-base font-semibold text-default",
1641
+ children: "Was this page helpful?"
1642
+ }), /* @__PURE__ */ jsxs("div", {
1643
+ className: "flex items-center gap-2",
1644
+ children: [
1645
+ /* @__PURE__ */ jsxs("button", {
1646
+ type: "button",
1647
+ onClick: () => setRating("good"),
1648
+ className: cn("flex items-center gap-1.5 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all duration-200 outline-none cursor-pointer", rating === "good" ? "border-primary-500 bg-primary-500/10 text-primary-500 dark:text-primary-400" : "border-subtle hover:border-body hover:bg-surface text-muted hover:text-body"),
1649
+ "aria-label": "Helpful",
1650
+ children: [/* @__PURE__ */ jsx(FaceGood, {}), /* @__PURE__ */ jsx("span", { children: "Yes" })]
1651
+ }),
1652
+ /* @__PURE__ */ jsxs("button", {
1653
+ type: "button",
1654
+ onClick: () => setRating("neutral"),
1655
+ className: cn("flex items-center gap-1.5 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all duration-200 outline-none cursor-pointer", rating === "neutral" ? "border-primary-500 bg-primary-500/10 text-primary-500 dark:text-primary-400" : "border-subtle hover:border-body hover:bg-surface text-muted hover:text-body"),
1656
+ "aria-label": "Neutral",
1657
+ children: [/* @__PURE__ */ jsx(FaceRegular, {}), /* @__PURE__ */ jsx("span", { children: "Regular" })]
1658
+ }),
1659
+ /* @__PURE__ */ jsxs("button", {
1660
+ type: "button",
1661
+ onClick: () => setRating("bad"),
1662
+ className: cn("flex items-center gap-1.5 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all duration-200 outline-none cursor-pointer", rating === "bad" ? "border-primary-500 bg-primary-500/10 text-primary-500 dark:text-primary-400" : "border-subtle hover:border-body hover:bg-surface text-muted hover:text-body"),
1663
+ "aria-label": "Not helpful",
1664
+ children: [/* @__PURE__ */ jsx(FaceBad, {}), /* @__PURE__ */ jsx("span", { children: "No" })]
1665
+ })
1666
+ ]
1667
+ })]
1668
+ }), rating && /* @__PURE__ */ jsxs("div", {
1669
+ className: "flex flex-col gap-3 mt-1 animate-in slide-in-from-top-3 duration-300",
1670
+ children: [/* @__PURE__ */ jsx("textarea", {
1671
+ value: comment,
1672
+ onChange: (e) => setComment(e.target.value),
1673
+ placeholder: "¿Tienes alguna sugerencia para mejorar esta página? (Opcional)",
1674
+ className: "w-full h-24 p-3 text-sm rounded-xl border border-subtle bg-main text-body placeholder:text-muted focus:outline-none focus:ring-1 focus:ring-primary-500 focus:border-primary-500 resize-none transition-shadow"
1675
+ }), /* @__PURE__ */ jsxs("div", {
1676
+ className: "flex items-center justify-between gap-3",
1677
+ children: [error && /* @__PURE__ */ jsx("p", {
1678
+ className: "text-xs text-rose-500 font-medium flex-1",
1679
+ children: error
1680
+ }), /* @__PURE__ */ jsx("div", {
1681
+ className: "flex items-center gap-2 ml-auto",
1682
+ children: /* @__PURE__ */ jsx("button", {
1683
+ type: "button",
1684
+ onClick: () => submit(),
1685
+ disabled: loading,
1686
+ className: "px-4 py-2 rounded-xl text-xs font-semibold bg-primary-500 hover:bg-primary-600 active:bg-primary-700 text-white transition-colors outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed",
1687
+ children: loading ? "Submitting..." : "Submit"
1688
+ })
1689
+ })]
1690
+ })]
1691
+ })]
1692
+ })
1693
+ });
1694
+ }
1695
+
1315
1696
  //#endregion
1316
1697
  //#region src/client/ssg/mdx-elements.tsx
1317
1698
  const Loading = () => /* @__PURE__ */ jsx("div", {
@@ -1448,13 +1829,6 @@ function Head({ siteTitle, siteDescription, routes }) {
1448
1829
  ] });
1449
1830
  }
1450
1831
 
1451
- //#endregion
1452
- //#region src/client/collections/collections-context.tsx
1453
- const CollectionsContext = createContext({});
1454
- function useCollectionsData() {
1455
- return use(CollectionsContext);
1456
- }
1457
-
1458
1832
  //#endregion
1459
1833
  //#region src/client/app/docs-layout.tsx
1460
1834
  function DocsLayout({ collectionsData }) {
@@ -1577,21 +1951,37 @@ function buildDocRoutes(options) {
1577
1951
  const targetBasePaths = [];
1578
1952
  targetBasePaths.push({
1579
1953
  path: baseDocsPath,
1580
- filter: () => true
1954
+ filter: () => true,
1955
+ locale: config.i18n?.defaultLocale,
1956
+ version: config.versions?.defaultVersion
1581
1957
  });
1582
- const subPaths = [];
1583
- if (allVersions.length > 0) allVersions.forEach((v) => subPaths.push(`/${v}`));
1584
- if (locales.length > 0) locales.forEach((l) => subPaths.push(`/${l}`));
1585
- if (allVersions.length > 0 && locales.length > 0) allVersions.forEach((v) => {
1586
- locales.forEach((l) => {
1587
- subPaths.push(`/${v}/${l}`);
1958
+ if (allVersions.length > 0) allVersions.forEach((v) => {
1959
+ const fullP = baseDocsPath === "/" ? `/${v}` : `${baseDocsPath}/${v}`;
1960
+ targetBasePaths.push({
1961
+ path: fullP,
1962
+ filter: (rp) => !!rp && rp.startsWith(fullP.replace(/\/$/, "") + "/"),
1963
+ locale: config.i18n?.defaultLocale,
1964
+ version: v
1588
1965
  });
1589
1966
  });
1590
- subPaths.forEach((sp) => {
1591
- const fullP = baseDocsPath === "/" ? sp : `${baseDocsPath}${sp}`;
1967
+ if (locales.length > 0) locales.forEach((l) => {
1968
+ const fullP = baseDocsPath === "/" ? `/${l}` : `${baseDocsPath}/${l}`;
1592
1969
  targetBasePaths.push({
1593
1970
  path: fullP,
1594
- filter: (rp) => !!rp && rp.startsWith(fullP.replace(/\/$/, "") + "/")
1971
+ filter: (rp) => !!rp && rp.startsWith(fullP.replace(/\/$/, "") + "/"),
1972
+ locale: l,
1973
+ version: config.versions?.defaultVersion
1974
+ });
1975
+ });
1976
+ if (allVersions.length > 0 && locales.length > 0) allVersions.forEach((v) => {
1977
+ locales.forEach((l) => {
1978
+ const fullP = baseDocsPath === "/" ? `/${v}/${l}` : `${baseDocsPath}/${v}/${l}`;
1979
+ targetBasePaths.push({
1980
+ path: fullP,
1981
+ filter: (rp) => !!rp && rp.startsWith(fullP.replace(/\/$/, "") + "/"),
1982
+ locale: l,
1983
+ version: v
1984
+ });
1595
1985
  });
1596
1986
  });
1597
1987
  const docPathRegistry = new Set(docRoutes.map((r) => (r.path || "").replace(/\/$/, "")));
@@ -1603,7 +1993,7 @@ function buildDocRoutes(options) {
1603
1993
  externalPaths.add(`/${locale}${p === "/" ? "" : p}`.replace(/\/$/, ""));
1604
1994
  });
1605
1995
  });
1606
- targetBasePaths.forEach(({ path: bPath, filter }) => {
1996
+ targetBasePaths.forEach(({ path: bPath, filter, locale: bLocale, version: bVersion }) => {
1607
1997
  if (bPath === "/") return;
1608
1998
  const normalizedPath = bPath.replace(/\/$/, "");
1609
1999
  if (!(docPathRegistry.has(normalizedPath) || externalPaths.has(normalizedPath))) {
@@ -1617,6 +2007,7 @@ function buildDocRoutes(options) {
1617
2007
  docRoutes.push({
1618
2008
  ...isBasePathFallback ? { index: true } : { path: redirectPath },
1619
2009
  element: matchedRouteObj.element,
2010
+ lazy: matchedRouteObj.lazy,
1620
2011
  loader: matchedRouteObj.loader,
1621
2012
  getStaticPaths: () => []
1622
2013
  });
@@ -1630,8 +2021,11 @@ function buildDocRoutes(options) {
1630
2021
  docMetadata.push({
1631
2022
  ...matchedMetaObj,
1632
2023
  path: bPath,
1633
- filePath: "",
2024
+ filePath: matchedMetaObj.filePath,
2025
+ locale: bLocale,
2026
+ version: bVersion,
1634
2027
  slugParts: [],
2028
+ fallback: true,
1635
2029
  seo: {
1636
2030
  ...matchedMetaObj.seo,
1637
2031
  canonical: canonicalUrl
@@ -1912,205 +2306,13 @@ function createRoutes(options) {
1912
2306
  ...externalRoutes.metadata,
1913
2307
  ...collectionRoutes.metadata
1914
2308
  ],
1915
- components
2309
+ components,
2310
+ collectionsData: options.collectionsData
1916
2311
  }),
1917
2312
  children
1918
2313
  }];
1919
2314
  }
1920
2315
 
1921
- //#endregion
1922
- //#region src/client/hooks/use-analytics.ts
1923
- function createAnalyticsInstance(config) {
1924
- if (typeof window === "undefined") return createDisabledAnalytics();
1925
- if (typeof window.gtag === "function") return createGtagAnalytics(config);
1926
- if (window.dataLayer) return createDataLayerAnalytics(config);
1927
- return createDisabledAnalytics();
1928
- }
1929
- function createGtagAnalytics(config) {
1930
- return {
1931
- trackPageView: (path, title) => {
1932
- window.gtag?.("event", "page_view", {
1933
- page_path: path,
1934
- page_title: title || document.title,
1935
- send_to: config?.ga4?.measurementId
1936
- });
1937
- },
1938
- trackEvent: ({ action, category, label, value, params }) => {
1939
- window.gtag?.("event", action, {
1940
- event_category: category,
1941
- event_label: label,
1942
- value,
1943
- send_to: config?.ga4?.measurementId,
1944
- ...params
1945
- });
1946
- },
1947
- trackSearch: (query, resultsCount) => {
1948
- window.gtag?.("event", "search", {
1949
- search_term: query,
1950
- results_count: resultsCount,
1951
- send_to: config?.ga4?.measurementId
1952
- });
1953
- },
1954
- trackDownload: (file, type) => {
1955
- window.gtag?.("event", "file_download", {
1956
- file_name: file,
1957
- file_type: type || file.split(".").pop(),
1958
- send_to: config?.ga4?.measurementId
1959
- });
1960
- },
1961
- trackExternalLink: (url) => {
1962
- window.gtag?.("event", "external_link", {
1963
- link_url: url,
1964
- send_to: config?.ga4?.measurementId
1965
- });
1966
- },
1967
- isEnabled: true
1968
- };
1969
- }
1970
- function createDataLayerAnalytics(config) {
1971
- return {
1972
- trackPageView: (path, title) => {
1973
- window.dataLayer?.push({
1974
- event: "page_view",
1975
- page_path: path,
1976
- page_title: title || document.title,
1977
- send_to: config?.gtm?.tagId
1978
- });
1979
- },
1980
- trackEvent: ({ action, category, label, value, params }) => {
1981
- window.dataLayer?.push({
1982
- event: action,
1983
- event_category: category,
1984
- event_label: label,
1985
- value,
1986
- send_to: config?.gtm?.tagId,
1987
- ...params
1988
- });
1989
- },
1990
- trackSearch: (query, resultsCount) => {
1991
- window.dataLayer?.push({
1992
- event: "search",
1993
- search_term: query,
1994
- results_count: resultsCount,
1995
- send_to: config?.gtm?.tagId
1996
- });
1997
- },
1998
- trackDownload: (file, type) => {
1999
- window.dataLayer?.push({
2000
- event: "file_download",
2001
- file_name: file,
2002
- file_type: type || file.split(".").pop(),
2003
- send_to: config?.gtm?.tagId
2004
- });
2005
- },
2006
- trackExternalLink: (url) => {
2007
- window.dataLayer?.push({
2008
- event: "external_link",
2009
- link_url: url,
2010
- send_to: config?.gtm?.tagId
2011
- });
2012
- },
2013
- isEnabled: true
2014
- };
2015
- }
2016
- function createDisabledAnalytics() {
2017
- return {
2018
- trackPageView: () => {},
2019
- trackEvent: () => {},
2020
- trackSearch: () => {},
2021
- trackDownload: () => {},
2022
- trackExternalLink: () => {},
2023
- isEnabled: false
2024
- };
2025
- }
2026
- const CONFIG_INSTANCE_SYMBOL = Symbol.for("__BDOCS_CONFIG_INSTANCE__");
2027
- function useAnalytics(options = {}) {
2028
- const { config: optionsConfig, autoTrackPageViews = true, autoTrackDownloads = true, autoTrackExternalLinks = true, excludePatterns = [] } = options;
2029
- const globalConfig = typeof globalThis !== "undefined" ? globalThis[CONFIG_INSTANCE_SYMBOL] : void 0;
2030
- const config = optionsConfig ?? globalConfig?.integrations;
2031
- const analytics = useMemo(() => createAnalyticsInstance(config), [config]);
2032
- const previousPath = useRef("");
2033
- const location = useLocation();
2034
- useEffect(() => {
2035
- if (!autoTrackPageViews || !analytics.isEnabled) return;
2036
- const path = location.pathname + location.search;
2037
- if (path !== previousPath.current) {
2038
- previousPath.current = path;
2039
- analytics.trackPageView(path, document.title);
2040
- }
2041
- }, [
2042
- location.pathname,
2043
- autoTrackPageViews,
2044
- analytics
2045
- ]);
2046
- useEffect(() => {
2047
- if (!autoTrackDownloads || !analytics.isEnabled) return;
2048
- const handleClick = (event) => {
2049
- const target = event.target?.closest("a");
2050
- if (!target) return;
2051
- const href = target.getAttribute("href");
2052
- if (!href) return;
2053
- if (excludePatterns.some((pattern) => pattern.test(href))) return;
2054
- if (target.hasAttribute("download") || /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip|rar|7z|tar|gz|mp3|mp4|avi|mov|png|jpg|jpeg|gif|svg|webp)$/i.test(href)) {
2055
- const fileName = href.split("/").pop() || href;
2056
- analytics.trackDownload(fileName, fileName.split(".").pop());
2057
- }
2058
- };
2059
- document.addEventListener("click", handleClick);
2060
- return () => document.removeEventListener("click", handleClick);
2061
- }, [
2062
- autoTrackDownloads,
2063
- autoTrackExternalLinks,
2064
- analytics,
2065
- excludePatterns
2066
- ]);
2067
- useEffect(() => {
2068
- if (!autoTrackExternalLinks || !analytics.isEnabled) return;
2069
- const handleClick = (event) => {
2070
- const target = event.target?.closest("a");
2071
- if (!target) return;
2072
- const href = target.getAttribute("href");
2073
- if (!href) return;
2074
- if (excludePatterns.some((pattern) => pattern.test(href))) return;
2075
- if ((href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) && !href.includes(window.location.hostname)) analytics.trackExternalLink(href);
2076
- };
2077
- document.addEventListener("click", handleClick);
2078
- return () => document.removeEventListener("click", handleClick);
2079
- }, [
2080
- autoTrackExternalLinks,
2081
- analytics,
2082
- excludePatterns
2083
- ]);
2084
- return analytics;
2085
- }
2086
- function useTrackPageView() {
2087
- const analytics = useMemo(() => createAnalyticsInstance(), []);
2088
- return useCallback((path, title) => {
2089
- analytics.trackPageView(path, title);
2090
- }, [analytics]);
2091
- }
2092
- function useTrackEvent() {
2093
- const analytics = useMemo(() => createAnalyticsInstance(), []);
2094
- return useCallback((event) => {
2095
- analytics.trackEvent(event);
2096
- }, [analytics]);
2097
- }
2098
-
2099
- //#endregion
2100
- //#region src/client/hooks/use-headings.ts
2101
- /**
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.
2106
- */
2107
- function useHeadings() {
2108
- const matches = useMatches();
2109
- return useMemo(() => {
2110
- return (matches[matches.length - 1]?.data)?.headings || [];
2111
- }, [matches]);
2112
- }
2113
-
2114
2316
  //#endregion
2115
2317
  //#region src/client/components/docs-layout-default.tsx
2116
2318
  function DocsLayoutComponent({ children }) {
@@ -2151,6 +2353,7 @@ function DocsLayoutComponent({ children }) {
2151
2353
  className: "prose prose-neutral dark:prose-invert max-w-none",
2152
2354
  children
2153
2355
  }) }),
2356
+ !isCollectionPage && /* @__PURE__ */ jsx(Feedback, {}),
2154
2357
  /* @__PURE__ */ jsx(DocsLayout$1.Footer, { children: !isCollectionPage && /* @__PURE__ */ jsx(PageNav, {}) })
2155
2358
  ]
2156
2359
  })
@@ -2216,4 +2419,4 @@ const reactToText = (node, resolvers) => {
2216
2419
  };
2217
2420
 
2218
2421
  //#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 };
2422
+ 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, useCodeBlock, useConfig, useFeedback, useHeadings, useI18n, useLocalizedTo, useLocation, useMdxComponents, useMergedComponents, useNavbar, usePageNav, usePost, usePosts, useRecentPosts, useRoutes, useSearch, useSearchHighlight, useSidebar, useTabs, useTheme, useTrackEvent, useTrackPageView, useUI, useVersion };