boltdocs 2.8.4 → 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-DFACg5Dy.d.cts → banner-C_xkGxzI.d.cts} +1 -1
  3. package/dist/{banner-BLTQUA2O.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 +464 -279
  8. package/dist/client/index.d.cts +74 -8
  9. package/dist/client/index.d.ts +74 -8
  10. package/dist/client/index.js +463 -280
  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-CVCf3xkZ.cjs → docs-layout-3TpAt6MB.cjs} +302 -238
  20. package/dist/{docs-layout-BBv6wILp.js → docs-layout-6FQ8__ok.js} +303 -239
  21. package/dist/doctor-BY1tSmQd.mjs +34 -0
  22. package/dist/{doctor-DT4jh07O.cjs → doctor-Bknon8f5.cjs} +1 -1
  23. package/dist/doctor-DcFLi4t1.cjs +34 -0
  24. package/dist/{doctor-BGPdO1Ah.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-C8sFxeBk.cjs → icons-dev-BryEFkJu.cjs} +90 -4
  31. package/dist/{icons-dev-CFcv5Nb8.js → icons-dev-kJyGWNeg.js} +73 -5
  32. package/dist/{image-DCMxxidi.cjs → image-B9YA_oQ_.cjs} +17 -9
  33. package/dist/{image-DufDOid8.js → image-CjYu9Eml.js} +17 -9
  34. package/dist/{mdx-Bto8CvGl.js → mdx-Cy_c2zio.js} +116 -33
  35. package/dist/{mdx-BH9kHiF9.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-DlgexgpQ.mjs → package-BK_82QLX.mjs} +1 -1
  48. package/dist/{package-CwNjt3Sd.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-9LqyiEnB.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-Cvux-qSk.cjs → search-dialog-BhXg05Ri.cjs} +1 -1
  57. package/dist/{search-dialog-CmHYOt18.cjs → search-dialog-BsAiQgnx.cjs} +2 -2
  58. package/dist/{search-dialog-jHZ21G4i.js → search-dialog-CaOO1Q4i.js} +2 -2
  59. package/dist/{search-dialog-Cr7SC8Xt.cjs → search-dialog-CzwvKoLW.cjs} +1 -1
  60. package/dist/{search-dialog-DBNaG5At.js → search-dialog-DRNJsxOv.js} +1 -1
  61. package/dist/{search-dialog-e86WPpck.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-kQBonN82.d.ts → sidebar-BtJyWHy4.d.cts} +42 -5
  67. package/dist/{sidebar---RgTmZD.d.cts → sidebar-C4fuRYYS.d.ts} +42 -5
  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 +6 -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-Dnv77Lfx.mjs +0 -28
  78. package/dist/doctor-VBPlQf7d.cjs +0 -28
  79. package/dist/node-JM9bx0mO.cjs +0 -160
  80. package/dist/node-q-Y2mdLX.mjs +0 -160
  81. package/dist/routes-3sAccArX.cjs +0 -6
  82. package/dist/routes-DZpXqq1p.mjs +0 -6
  83. package/dist/routes-DjoxNeef.mjs +0 -6
  84. package/dist/routes-DpJnb2VS.cjs +0 -6
  85. package/dist/worker-pool-DXLyeHJg.cjs +0 -6
  86. package/dist/worker-pool-Kn4F9gDq.mjs +0 -6
  87. /package/dist/{meta-loader-DzwDFtdT.mjs → meta-loader-By22EnYa.mjs} +0 -0
@@ -4,12 +4,12 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
7
- const require_icons_dev = require('../icons-dev-C8sFxeBk.cjs');
8
- const require_image = require('../image-DCMxxidi.cjs');
9
- const require_mdx = require('../mdx-BH9kHiF9.cjs');
10
- const require_docs_layout = require('../docs-layout-CVCf3xkZ.cjs');
11
- const require_search_dialog = require('../search-dialog-Cvux-qSk.cjs');
12
- const require_search_dialog$1 = require('../search-dialog-CmHYOt18.cjs');
7
+ const require_icons_dev = require('../icons-dev-BryEFkJu.cjs');
8
+ const require_image = require('../image-B9YA_oQ_.cjs');
9
+ const require_mdx = require('../mdx-WXjAMS8Y.cjs');
10
+ const require_docs_layout = require('../docs-layout-3TpAt6MB.cjs');
11
+ const require_search_dialog = require('../search-dialog-BhXg05Ri.cjs');
12
+ const require_search_dialog$1 = require('../search-dialog-BsAiQgnx.cjs');
13
13
  let react = require("react");
14
14
  let react_router_dom = require("react-router-dom");
15
15
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -159,6 +159,13 @@ const mdxComponentsDefault = {
159
159
  "404": NotFound
160
160
  };
161
161
 
162
+ //#endregion
163
+ //#region src/client/collections/collections-context.tsx
164
+ const CollectionsContext = (0, react.createContext)({});
165
+ function useCollectionsData() {
166
+ return (0, react.use)(CollectionsContext);
167
+ }
168
+
162
169
  //#endregion
163
170
  //#region src/client/ssg/boltdocs-shell.tsx
164
171
  /**
@@ -194,7 +201,7 @@ function StoreSync({ config, routeMap }) {
194
201
  ]);
195
202
  return null;
196
203
  }
197
- function BoltdocsShell({ config, routes, components = {} }) {
204
+ function BoltdocsShell({ config, routes, components = {}, collectionsData }) {
198
205
  const allComponents = (0, react.useMemo)(() => ({
199
206
  ...mdxComponentsDefault,
200
207
  ...virtual_boltdocs_mdx_components.default,
@@ -231,20 +238,23 @@ function BoltdocsShell({ config, routes, components = {} }) {
231
238
  routes,
232
239
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_image.ThemeProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.UIProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MdxComponentsProvider, {
233
240
  components: allComponents,
234
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_icons_dev.ConfigContext.Provider, {
241
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.ConfigContext.Provider, {
235
242
  value: config,
236
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollHandler, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_icons_dev.BoltdocsProvider, {
237
- initialLocale: initialData.initLocale,
238
- initialVersion: initialData.initVersion,
239
- children: [
240
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StoreSync, {
241
- config,
242
- routeMap
243
- }),
244
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(I18nUpdater, { config }),
245
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_search_dialog$1.InternalErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Outlet, {}) })
246
- ]
247
- })]
243
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollectionsContext.Provider, {
244
+ value: collectionsData || {},
245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScrollHandler, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_icons_dev.BoltdocsProvider, {
246
+ initialLocale: initialData.initLocale,
247
+ initialVersion: initialData.initVersion,
248
+ children: [
249
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StoreSync, {
250
+ config,
251
+ routeMap
252
+ }),
253
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(I18nUpdater, { config }),
254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_search_dialog$1.InternalErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Outlet, {}) })
255
+ ]
256
+ })]
257
+ })
248
258
  })
249
259
  }) }) })
250
260
  }) });
@@ -850,6 +860,30 @@ function useVersion() {
850
860
  };
851
861
  }
852
862
 
863
+ //#endregion
864
+ //#region src/client/components/ui-base/version-selector.tsx
865
+ function VersionSelector({ className }) {
866
+ const { currentVersionLabel, availableVersions, handleVersionChange } = useVersion();
867
+ if (availableVersions.length === 0) return null;
868
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_docs_layout.Menu.Trigger, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_search_dialog.Button, {
869
+ className: require_icons_dev.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),
870
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
871
+ className: "font-semibold text-[0.8125rem]",
872
+ children: currentVersionLabel
873
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
874
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Root, {
875
+ 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",
876
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Section, {
877
+ items: availableVersions,
878
+ children: (version) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Item, {
879
+ onPress: () => handleVersionChange(version.value),
880
+ 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",
881
+ children: version.label
882
+ }, version.value)
883
+ })
884
+ })] });
885
+ }
886
+
853
887
  //#endregion
854
888
  //#region src/client/hooks/use-i18n.ts
855
889
  /**
@@ -929,28 +963,319 @@ function useI18n() {
929
963
  }
930
964
 
931
965
  //#endregion
932
- //#region src/client/components/ui-base/version-i18n.tsx
933
- function VersionSelector({ className }) {
934
- const { currentVersionLabel, availableVersions, handleVersionChange } = useVersion();
935
- if (availableVersions.length === 0) return null;
936
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_docs_layout.Menu.Trigger, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_search_dialog.Button, {
937
- className: require_icons_dev.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),
938
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
939
- className: "font-semibold text-[0.8125rem]",
940
- children: currentVersionLabel
941
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.ChevronDown, { className: "w-3.5 h-3.5 text-muted/60" })]
942
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Root, {
943
- 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",
944
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Section, {
945
- items: availableVersions,
946
- children: (version) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.Menu.Item, {
947
- onPress: () => handleVersionChange(version.value),
948
- 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",
949
- children: version.label
950
- }, `${version.value ?? ""}`)
951
- })
952
- })] });
966
+ //#region src/client/hooks/use-page-nav.ts
967
+ function usePageNav() {
968
+ const { routes, currentRoute } = require_icons_dev.useRoutes();
969
+ const location = (0, react_router_dom.useLocation)();
970
+ const { groups, ungrouped } = require_docs_layout.useSidebar(routes);
971
+ return (0, react.useMemo)(() => {
972
+ if (!currentRoute) return {
973
+ prevPage: null,
974
+ nextPage: null,
975
+ currentRoute: null
976
+ };
977
+ const mergedItems = [...ungrouped.map((route) => ({
978
+ type: "link",
979
+ position: route.sidebarPosition ?? 999,
980
+ title: route.title,
981
+ route
982
+ })), ...groups.map((group) => ({
983
+ type: "group",
984
+ position: group.sidebarPosition ?? 999,
985
+ title: group.title,
986
+ group
987
+ }))].sort((a, b) => {
988
+ if (a.position !== b.position) return a.position - b.position;
989
+ if (a.type !== b.type) return a.type === "link" ? -1 : 1;
990
+ return a.title.localeCompare(b.title);
991
+ });
992
+ const orderedRoutes = [];
993
+ const flattenNode = (node) => {
994
+ if (node.path && node.path !== "#" && !node.sidebarHidden) orderedRoutes.push(node);
995
+ const children = node.routes || node.subRoutes;
996
+ if (children && children.length > 0) for (const child of children) flattenNode(child);
997
+ };
998
+ for (const item of mergedItems) if (item.type === "link") flattenNode(item.route);
999
+ else if (item.group.routes) for (const route of item.group.routes) flattenNode(route);
1000
+ const currentNormalizedPath = require_icons_dev.normalizePath(location.pathname);
1001
+ const currentIndex = orderedRoutes.findIndex((r) => require_icons_dev.normalizePath(r.path) === currentNormalizedPath);
1002
+ return {
1003
+ prevPage: currentIndex > 0 ? orderedRoutes[currentIndex - 1] : null,
1004
+ nextPage: currentIndex !== -1 && currentIndex < orderedRoutes.length - 1 ? orderedRoutes[currentIndex + 1] : null,
1005
+ currentRoute
1006
+ };
1007
+ }, [
1008
+ groups,
1009
+ ungrouped,
1010
+ currentRoute,
1011
+ location.pathname
1012
+ ]);
1013
+ }
1014
+
1015
+ //#endregion
1016
+ //#region src/client/hooks/use-analytics.ts
1017
+ function createAnalyticsInstance(config) {
1018
+ if (typeof window === "undefined") return createDisabledAnalytics();
1019
+ if (typeof window.gtag === "function") return createGtagAnalytics(config);
1020
+ if (window.dataLayer) return createDataLayerAnalytics(config);
1021
+ return createDisabledAnalytics();
1022
+ }
1023
+ function createGtagAnalytics(config) {
1024
+ return {
1025
+ trackPageView: (path, title) => {
1026
+ window.gtag?.("event", "page_view", {
1027
+ page_path: path,
1028
+ page_title: title || document.title,
1029
+ send_to: config?.ga4?.measurementId
1030
+ });
1031
+ },
1032
+ trackEvent: ({ action, category, label, value, params }) => {
1033
+ window.gtag?.("event", action, {
1034
+ event_category: category,
1035
+ event_label: label,
1036
+ value,
1037
+ send_to: config?.ga4?.measurementId,
1038
+ ...params
1039
+ });
1040
+ },
1041
+ trackSearch: (query, resultsCount) => {
1042
+ window.gtag?.("event", "search", {
1043
+ search_term: query,
1044
+ results_count: resultsCount,
1045
+ send_to: config?.ga4?.measurementId
1046
+ });
1047
+ },
1048
+ trackDownload: (file, type) => {
1049
+ window.gtag?.("event", "file_download", {
1050
+ file_name: file,
1051
+ file_type: type || file.split(".").pop(),
1052
+ send_to: config?.ga4?.measurementId
1053
+ });
1054
+ },
1055
+ trackExternalLink: (url) => {
1056
+ window.gtag?.("event", "external_link", {
1057
+ link_url: url,
1058
+ send_to: config?.ga4?.measurementId
1059
+ });
1060
+ },
1061
+ isEnabled: true
1062
+ };
1063
+ }
1064
+ function createDataLayerAnalytics(config) {
1065
+ return {
1066
+ trackPageView: (path, title) => {
1067
+ window.dataLayer?.push({
1068
+ event: "page_view",
1069
+ page_path: path,
1070
+ page_title: title || document.title,
1071
+ send_to: config?.gtm?.tagId
1072
+ });
1073
+ },
1074
+ trackEvent: ({ action, category, label, value, params }) => {
1075
+ window.dataLayer?.push({
1076
+ event: action,
1077
+ event_category: category,
1078
+ event_label: label,
1079
+ value,
1080
+ send_to: config?.gtm?.tagId,
1081
+ ...params
1082
+ });
1083
+ },
1084
+ trackSearch: (query, resultsCount) => {
1085
+ window.dataLayer?.push({
1086
+ event: "search",
1087
+ search_term: query,
1088
+ results_count: resultsCount,
1089
+ send_to: config?.gtm?.tagId
1090
+ });
1091
+ },
1092
+ trackDownload: (file, type) => {
1093
+ window.dataLayer?.push({
1094
+ event: "file_download",
1095
+ file_name: file,
1096
+ file_type: type || file.split(".").pop(),
1097
+ send_to: config?.gtm?.tagId
1098
+ });
1099
+ },
1100
+ trackExternalLink: (url) => {
1101
+ window.dataLayer?.push({
1102
+ event: "external_link",
1103
+ link_url: url,
1104
+ send_to: config?.gtm?.tagId
1105
+ });
1106
+ },
1107
+ isEnabled: true
1108
+ };
1109
+ }
1110
+ function createDisabledAnalytics() {
1111
+ return {
1112
+ trackPageView: () => {},
1113
+ trackEvent: () => {},
1114
+ trackSearch: () => {},
1115
+ trackDownload: () => {},
1116
+ trackExternalLink: () => {},
1117
+ isEnabled: false
1118
+ };
1119
+ }
1120
+ const CONFIG_INSTANCE_SYMBOL = Symbol.for("__BDOCS_CONFIG_INSTANCE__");
1121
+ function useAnalytics(options = {}) {
1122
+ const { config: optionsConfig, autoTrackPageViews = true, autoTrackDownloads = true, autoTrackExternalLinks = true, excludePatterns = [] } = options;
1123
+ const globalConfig = typeof globalThis !== "undefined" ? globalThis[CONFIG_INSTANCE_SYMBOL] : void 0;
1124
+ const config = optionsConfig ?? globalConfig?.integrations;
1125
+ const analytics = (0, react.useMemo)(() => createAnalyticsInstance(config), [config]);
1126
+ const previousPath = (0, react.useRef)("");
1127
+ const location = require_docs_layout.useLocation();
1128
+ (0, react.useEffect)(() => {
1129
+ if (!autoTrackPageViews || !analytics.isEnabled) return;
1130
+ const path = location.pathname + location.search;
1131
+ if (path !== previousPath.current) {
1132
+ previousPath.current = path;
1133
+ analytics.trackPageView(path, document.title);
1134
+ }
1135
+ }, [
1136
+ location.pathname,
1137
+ autoTrackPageViews,
1138
+ analytics
1139
+ ]);
1140
+ (0, react.useEffect)(() => {
1141
+ if (!autoTrackDownloads || !analytics.isEnabled) return;
1142
+ const handleClick = (event) => {
1143
+ const target = event.target?.closest("a");
1144
+ if (!target) return;
1145
+ const href = target.getAttribute("href");
1146
+ if (!href) return;
1147
+ if (excludePatterns.some((pattern) => pattern.test(href))) return;
1148
+ 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)) {
1149
+ const fileName = href.split("/").pop() || href;
1150
+ analytics.trackDownload(fileName, fileName.split(".").pop());
1151
+ }
1152
+ };
1153
+ document.addEventListener("click", handleClick);
1154
+ return () => document.removeEventListener("click", handleClick);
1155
+ }, [
1156
+ autoTrackDownloads,
1157
+ autoTrackExternalLinks,
1158
+ analytics,
1159
+ excludePatterns
1160
+ ]);
1161
+ (0, react.useEffect)(() => {
1162
+ if (!autoTrackExternalLinks || !analytics.isEnabled) return;
1163
+ const handleClick = (event) => {
1164
+ const target = event.target?.closest("a");
1165
+ if (!target) return;
1166
+ const href = target.getAttribute("href");
1167
+ if (!href) return;
1168
+ if (excludePatterns.some((pattern) => pattern.test(href))) return;
1169
+ if ((href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) && !href.includes(window.location.hostname)) analytics.trackExternalLink(href);
1170
+ };
1171
+ document.addEventListener("click", handleClick);
1172
+ return () => document.removeEventListener("click", handleClick);
1173
+ }, [
1174
+ autoTrackExternalLinks,
1175
+ analytics,
1176
+ excludePatterns
1177
+ ]);
1178
+ return analytics;
1179
+ }
1180
+ function useTrackPageView() {
1181
+ const analytics = (0, react.useMemo)(() => createAnalyticsInstance(), []);
1182
+ return (0, react.useCallback)((path, title) => {
1183
+ analytics.trackPageView(path, title);
1184
+ }, [analytics]);
1185
+ }
1186
+ function useTrackEvent() {
1187
+ const analytics = (0, react.useMemo)(() => createAnalyticsInstance(), []);
1188
+ return (0, react.useCallback)((event) => {
1189
+ analytics.trackEvent(event);
1190
+ }, [analytics]);
1191
+ }
1192
+
1193
+ //#endregion
1194
+ //#region src/client/hooks/use-headings.ts
1195
+ /**
1196
+ * Returns the headings of the current page, extracted from the route loader data.
1197
+ * Useful for building custom Tables of Contents or jumping to sections.
1198
+ *
1199
+ * @returns An array of heading objects with level, text, and id.
1200
+ */
1201
+ function useHeadings() {
1202
+ const matches = (0, react_router_dom.useMatches)();
1203
+ return (0, react.useMemo)(() => {
1204
+ return (matches[matches.length - 1]?.data)?.headings || [];
1205
+ }, [matches]);
1206
+ }
1207
+
1208
+ //#endregion
1209
+ //#region src/client/hooks/use-feedback.ts
1210
+ /**
1211
+ * Hook for managing the feedback rating form state and submission.
1212
+ * Resets automatically when navigating to a new page.
1213
+ */
1214
+ function useFeedback(options = {}) {
1215
+ const config = require_icons_dev.useConfig();
1216
+ const { currentRoute } = require_icons_dev.useRoutes();
1217
+ const [rating, setRating] = (0, react.useState)(null);
1218
+ const [comment, setComment] = (0, react.useState)("");
1219
+ const [loading, setLoading] = (0, react.useState)(false);
1220
+ const [submitted, setSubmitted] = (0, react.useState)(false);
1221
+ const [error, setError] = (0, react.useState)(null);
1222
+ (0, react.useEffect)(() => {
1223
+ setRating(null);
1224
+ setComment("");
1225
+ setSubmitted(false);
1226
+ setError(null);
1227
+ }, [currentRoute?.path]);
1228
+ const submit = async (commentOverride, blockId) => {
1229
+ if (!rating) return;
1230
+ setLoading(true);
1231
+ setError(null);
1232
+ const payload = {
1233
+ rating,
1234
+ comment: (commentOverride !== void 0 ? commentOverride : comment) || void 0,
1235
+ path: currentRoute?.path || window.location.pathname,
1236
+ title: currentRoute?.title || document.title,
1237
+ blockId
1238
+ };
1239
+ try {
1240
+ if (options.onSubmit) await options.onSubmit(payload);
1241
+ else {
1242
+ const endpoint = options.endpoint || config.integrations?.feedback?.custom?.endpoint || "/api/feedback";
1243
+ const res = await fetch(endpoint, {
1244
+ method: "POST",
1245
+ headers: { "Content-Type": "application/json" },
1246
+ body: JSON.stringify(payload)
1247
+ });
1248
+ if (!res.ok) {
1249
+ const text = await res.text();
1250
+ let errorText = text;
1251
+ try {
1252
+ const data = JSON.parse(text);
1253
+ errorText = data.error || data.message || text;
1254
+ } catch {}
1255
+ throw new Error(errorText || `Failed to submit feedback: ${res.statusText}`);
1256
+ }
1257
+ }
1258
+ setSubmitted(true);
1259
+ } catch (e) {
1260
+ setError(e.message || "An error occurred while submitting feedback.");
1261
+ } finally {
1262
+ setLoading(false);
1263
+ }
1264
+ };
1265
+ return {
1266
+ rating,
1267
+ setRating,
1268
+ comment,
1269
+ setComment,
1270
+ loading,
1271
+ submitted,
1272
+ error,
1273
+ submit
1274
+ };
953
1275
  }
1276
+
1277
+ //#endregion
1278
+ //#region src/client/components/ui-base/i18n-selector.tsx
954
1279
  function I18nSelector({ className }) {
955
1280
  const { currentLocale, availableLocales, handleLocaleChange } = useI18n();
956
1281
  if (availableLocales.length === 0) return null;
@@ -971,14 +1296,14 @@ function I18nSelector({ className }) {
971
1296
  onPress: () => handleLocaleChange(locale.value),
972
1297
  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",
973
1298
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: locale.label })
974
- }, `${locale.value ?? ""}`)
1299
+ }, locale.value)
975
1300
  })
976
1301
  })] });
977
1302
  }
978
1303
 
979
1304
  //#endregion
980
1305
  //#region src/client/components/ui-base/navbar.tsx
981
- const SearchDialog$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("../search-dialog-Cr7SC8Xt.cjs")).then((m) => ({ default: m.SearchDialog })));
1306
+ const SearchDialog$1 = (0, react.lazy)(() => Promise.resolve().then(() => require("../search-dialog-CzwvKoLW.cjs")).then((m) => ({ default: m.SearchDialog })));
982
1307
  function Navbar() {
983
1308
  const { links, title, logo, logoProps, github, social, config } = useNavbar();
984
1309
  const { routes, currentRoute, isCollectionPage, currentVersion, currentLocale } = require_icons_dev.useRoutes();
@@ -1095,7 +1420,7 @@ function Navbar() {
1095
1420
  function NavbarLinkItem({ link }) {
1096
1421
  const localizedHref = require_icons_dev.useLocalizedTo(link.href || "");
1097
1422
  const { pathname } = (0, react_router_dom.useLocation)();
1098
- const active = pathname === localizedHref || pathname.startsWith(localizedHref + "/");
1423
+ const active = pathname === localizedHref || pathname.startsWith(`${localizedHref}/`);
1099
1424
  if (link.items && link.items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_search_dialog.Navbar.Dropdown, {
1100
1425
  label: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1101
1426
  className: require_icons_dev.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"),
@@ -1174,36 +1499,6 @@ function OnThisPage({ headings = [], editLink, communityHelp, filePath }) {
1174
1499
  ] });
1175
1500
  }
1176
1501
 
1177
- //#endregion
1178
- //#region src/client/hooks/use-page-nav.ts
1179
- /**
1180
- * Hook to manage the previous and next button functionality for documentation pages.
1181
- * Intelligent: respects current locale, version, and tab to keep navigation logical.
1182
- */
1183
- function usePageNav() {
1184
- const { routes, currentRoute } = require_icons_dev.useRoutes();
1185
- const location = (0, react_router_dom.useLocation)();
1186
- return (0, react.useMemo)(() => {
1187
- if (!currentRoute) return {
1188
- prevPage: null,
1189
- nextPage: null,
1190
- currentRoute: null
1191
- };
1192
- const activeTabId = currentRoute.tab?.toLowerCase();
1193
- const contextRoutes = activeTabId ? routes.filter((r) => r.tab?.toLowerCase() === activeTabId) : routes.filter((r) => !r.tab);
1194
- const currentIndex = contextRoutes.findIndex((r) => r.path === location.pathname);
1195
- return {
1196
- prevPage: currentIndex > 0 ? contextRoutes[currentIndex - 1] : null,
1197
- nextPage: currentIndex !== -1 && currentIndex < contextRoutes.length - 1 ? contextRoutes[currentIndex + 1] : null,
1198
- currentRoute
1199
- };
1200
- }, [
1201
- routes,
1202
- currentRoute,
1203
- location.pathname
1204
- ]);
1205
- }
1206
-
1207
1502
  //#endregion
1208
1503
  //#region src/client/components/ui-base/page-nav.tsx
1209
1504
  /**
@@ -1293,13 +1588,15 @@ const Sidebar = Object.assign(SidebarMain, {
1293
1588
  function Banner({ children, className = "", dismissible = false, id = "boltdocs-banner", ...props }) {
1294
1589
  const [isVisible, setIsVisible] = (0, react.useState)(true);
1295
1590
  (0, react.useEffect)(() => {
1296
- if (dismissible && id) {
1297
- if (localStorage.getItem(`boltdocs-banner-dismissed-${id}`) === "true") setIsVisible(false);
1298
- }
1591
+ if (dismissible && id && typeof window !== "undefined") try {
1592
+ if (window.localStorage.getItem(`boltdocs-banner-dismissed-${id}`) === "true") setIsVisible(false);
1593
+ } catch {}
1299
1594
  }, [dismissible, id]);
1300
1595
  const handleDismiss = () => {
1301
1596
  setIsVisible(false);
1302
- if (dismissible && id) localStorage.setItem(`boltdocs-banner-dismissed-${id}`, "true");
1597
+ if (dismissible && id && typeof window !== "undefined") try {
1598
+ window.localStorage.setItem(`boltdocs-banner-dismissed-${id}`, "true");
1599
+ } catch {}
1303
1600
  };
1304
1601
  if (!isVisible) return null;
1305
1602
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1317,6 +1614,90 @@ function Banner({ children, className = "", dismissible = false, id = "boltdocs-
1317
1614
  });
1318
1615
  }
1319
1616
 
1617
+ //#endregion
1618
+ //#region src/client/components/ui-base/feedback.tsx
1619
+ function Feedback({ className }) {
1620
+ if (!(require_icons_dev.useConfig().integrations?.feedback?.custom)?.enabled) return null;
1621
+ const { rating, setRating, comment, setComment, loading, submitted, error, submit } = useFeedback();
1622
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1623
+ className: require_icons_dev.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),
1624
+ children: submitted ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1625
+ className: "flex flex-col items-center justify-center py-4 text-center animate-in fade-in zoom-in duration-300",
1626
+ children: [
1627
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1628
+ 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",
1629
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.Check, { size: 24 })
1630
+ }),
1631
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1632
+ className: "text-lg font-semibold text-default",
1633
+ children: "Thank you for your feedback!"
1634
+ }),
1635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1636
+ className: "text-sm text-muted mt-1",
1637
+ children: "Your comments help us improve the documentation."
1638
+ })
1639
+ ]
1640
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1641
+ className: "flex flex-col gap-4",
1642
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1643
+ className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
1644
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1645
+ className: "text-base font-semibold text-default",
1646
+ children: "Was this page helpful?"
1647
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1648
+ className: "flex items-center gap-2",
1649
+ children: [
1650
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1651
+ type: "button",
1652
+ onClick: () => setRating("good"),
1653
+ className: require_icons_dev.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"),
1654
+ "aria-label": "Helpful",
1655
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.FaceGood, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Yes" })]
1656
+ }),
1657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1658
+ type: "button",
1659
+ onClick: () => setRating("neutral"),
1660
+ className: require_icons_dev.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"),
1661
+ "aria-label": "Neutral",
1662
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.FaceRegular, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Regular" })]
1663
+ }),
1664
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1665
+ type: "button",
1666
+ onClick: () => setRating("bad"),
1667
+ className: require_icons_dev.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"),
1668
+ "aria-label": "Not helpful",
1669
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.FaceBad, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "No" })]
1670
+ })
1671
+ ]
1672
+ })]
1673
+ }), rating && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1674
+ className: "flex flex-col gap-3 mt-1 animate-in slide-in-from-top-3 duration-300",
1675
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1676
+ value: comment,
1677
+ onChange: (e) => setComment(e.target.value),
1678
+ placeholder: "¿Tienes alguna sugerencia para mejorar esta página? (Opcional)",
1679
+ 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"
1680
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1681
+ className: "flex items-center justify-between gap-3",
1682
+ children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1683
+ className: "text-xs text-rose-500 font-medium flex-1",
1684
+ children: error
1685
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1686
+ className: "flex items-center gap-2 ml-auto",
1687
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1688
+ type: "button",
1689
+ onClick: () => submit(),
1690
+ disabled: loading,
1691
+ 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",
1692
+ children: loading ? "Submitting..." : "Submit"
1693
+ })
1694
+ })]
1695
+ })]
1696
+ })]
1697
+ })
1698
+ });
1699
+ }
1700
+
1320
1701
  //#endregion
1321
1702
  //#region src/client/ssg/mdx-elements.tsx
1322
1703
  const Loading = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1453,13 +1834,6 @@ function Head({ siteTitle, siteDescription, routes }) {
1453
1834
  ] });
1454
1835
  }
1455
1836
 
1456
- //#endregion
1457
- //#region src/client/collections/collections-context.tsx
1458
- const CollectionsContext = (0, react.createContext)({});
1459
- function useCollectionsData() {
1460
- return (0, react.use)(CollectionsContext);
1461
- }
1462
-
1463
1837
  //#endregion
1464
1838
  //#region src/client/app/docs-layout.tsx
1465
1839
  function DocsLayout({ collectionsData }) {
@@ -1937,205 +2311,13 @@ function createRoutes(options) {
1937
2311
  ...externalRoutes.metadata,
1938
2312
  ...collectionRoutes.metadata
1939
2313
  ],
1940
- components
2314
+ components,
2315
+ collectionsData: options.collectionsData
1941
2316
  }),
1942
2317
  children
1943
2318
  }];
1944
2319
  }
1945
2320
 
1946
- //#endregion
1947
- //#region src/client/hooks/use-analytics.ts
1948
- function createAnalyticsInstance(config) {
1949
- if (typeof window === "undefined") return createDisabledAnalytics();
1950
- if (typeof window.gtag === "function") return createGtagAnalytics(config);
1951
- if (window.dataLayer) return createDataLayerAnalytics(config);
1952
- return createDisabledAnalytics();
1953
- }
1954
- function createGtagAnalytics(config) {
1955
- return {
1956
- trackPageView: (path, title) => {
1957
- window.gtag?.("event", "page_view", {
1958
- page_path: path,
1959
- page_title: title || document.title,
1960
- send_to: config?.ga4?.measurementId
1961
- });
1962
- },
1963
- trackEvent: ({ action, category, label, value, params }) => {
1964
- window.gtag?.("event", action, {
1965
- event_category: category,
1966
- event_label: label,
1967
- value,
1968
- send_to: config?.ga4?.measurementId,
1969
- ...params
1970
- });
1971
- },
1972
- trackSearch: (query, resultsCount) => {
1973
- window.gtag?.("event", "search", {
1974
- search_term: query,
1975
- results_count: resultsCount,
1976
- send_to: config?.ga4?.measurementId
1977
- });
1978
- },
1979
- trackDownload: (file, type) => {
1980
- window.gtag?.("event", "file_download", {
1981
- file_name: file,
1982
- file_type: type || file.split(".").pop(),
1983
- send_to: config?.ga4?.measurementId
1984
- });
1985
- },
1986
- trackExternalLink: (url) => {
1987
- window.gtag?.("event", "external_link", {
1988
- link_url: url,
1989
- send_to: config?.ga4?.measurementId
1990
- });
1991
- },
1992
- isEnabled: true
1993
- };
1994
- }
1995
- function createDataLayerAnalytics(config) {
1996
- return {
1997
- trackPageView: (path, title) => {
1998
- window.dataLayer?.push({
1999
- event: "page_view",
2000
- page_path: path,
2001
- page_title: title || document.title,
2002
- send_to: config?.gtm?.tagId
2003
- });
2004
- },
2005
- trackEvent: ({ action, category, label, value, params }) => {
2006
- window.dataLayer?.push({
2007
- event: action,
2008
- event_category: category,
2009
- event_label: label,
2010
- value,
2011
- send_to: config?.gtm?.tagId,
2012
- ...params
2013
- });
2014
- },
2015
- trackSearch: (query, resultsCount) => {
2016
- window.dataLayer?.push({
2017
- event: "search",
2018
- search_term: query,
2019
- results_count: resultsCount,
2020
- send_to: config?.gtm?.tagId
2021
- });
2022
- },
2023
- trackDownload: (file, type) => {
2024
- window.dataLayer?.push({
2025
- event: "file_download",
2026
- file_name: file,
2027
- file_type: type || file.split(".").pop(),
2028
- send_to: config?.gtm?.tagId
2029
- });
2030
- },
2031
- trackExternalLink: (url) => {
2032
- window.dataLayer?.push({
2033
- event: "external_link",
2034
- link_url: url,
2035
- send_to: config?.gtm?.tagId
2036
- });
2037
- },
2038
- isEnabled: true
2039
- };
2040
- }
2041
- function createDisabledAnalytics() {
2042
- return {
2043
- trackPageView: () => {},
2044
- trackEvent: () => {},
2045
- trackSearch: () => {},
2046
- trackDownload: () => {},
2047
- trackExternalLink: () => {},
2048
- isEnabled: false
2049
- };
2050
- }
2051
- const CONFIG_INSTANCE_SYMBOL = Symbol.for("__BDOCS_CONFIG_INSTANCE__");
2052
- function useAnalytics(options = {}) {
2053
- const { config: optionsConfig, autoTrackPageViews = true, autoTrackDownloads = true, autoTrackExternalLinks = true, excludePatterns = [] } = options;
2054
- const globalConfig = typeof globalThis !== "undefined" ? globalThis[CONFIG_INSTANCE_SYMBOL] : void 0;
2055
- const config = optionsConfig ?? globalConfig?.integrations;
2056
- const analytics = (0, react.useMemo)(() => createAnalyticsInstance(config), [config]);
2057
- const previousPath = (0, react.useRef)("");
2058
- const location = require_docs_layout.useLocation();
2059
- (0, react.useEffect)(() => {
2060
- if (!autoTrackPageViews || !analytics.isEnabled) return;
2061
- const path = location.pathname + location.search;
2062
- if (path !== previousPath.current) {
2063
- previousPath.current = path;
2064
- analytics.trackPageView(path, document.title);
2065
- }
2066
- }, [
2067
- location.pathname,
2068
- autoTrackPageViews,
2069
- analytics
2070
- ]);
2071
- (0, react.useEffect)(() => {
2072
- if (!autoTrackDownloads || !analytics.isEnabled) return;
2073
- const handleClick = (event) => {
2074
- const target = event.target?.closest("a");
2075
- if (!target) return;
2076
- const href = target.getAttribute("href");
2077
- if (!href) return;
2078
- if (excludePatterns.some((pattern) => pattern.test(href))) return;
2079
- 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)) {
2080
- const fileName = href.split("/").pop() || href;
2081
- analytics.trackDownload(fileName, fileName.split(".").pop());
2082
- }
2083
- };
2084
- document.addEventListener("click", handleClick);
2085
- return () => document.removeEventListener("click", handleClick);
2086
- }, [
2087
- autoTrackDownloads,
2088
- autoTrackExternalLinks,
2089
- analytics,
2090
- excludePatterns
2091
- ]);
2092
- (0, react.useEffect)(() => {
2093
- if (!autoTrackExternalLinks || !analytics.isEnabled) return;
2094
- const handleClick = (event) => {
2095
- const target = event.target?.closest("a");
2096
- if (!target) return;
2097
- const href = target.getAttribute("href");
2098
- if (!href) return;
2099
- if (excludePatterns.some((pattern) => pattern.test(href))) return;
2100
- if ((href.startsWith("http://") || href.startsWith("https://") || href.startsWith("//")) && !href.includes(window.location.hostname)) analytics.trackExternalLink(href);
2101
- };
2102
- document.addEventListener("click", handleClick);
2103
- return () => document.removeEventListener("click", handleClick);
2104
- }, [
2105
- autoTrackExternalLinks,
2106
- analytics,
2107
- excludePatterns
2108
- ]);
2109
- return analytics;
2110
- }
2111
- function useTrackPageView() {
2112
- const analytics = (0, react.useMemo)(() => createAnalyticsInstance(), []);
2113
- return (0, react.useCallback)((path, title) => {
2114
- analytics.trackPageView(path, title);
2115
- }, [analytics]);
2116
- }
2117
- function useTrackEvent() {
2118
- const analytics = (0, react.useMemo)(() => createAnalyticsInstance(), []);
2119
- return (0, react.useCallback)((event) => {
2120
- analytics.trackEvent(event);
2121
- }, [analytics]);
2122
- }
2123
-
2124
- //#endregion
2125
- //#region src/client/hooks/use-headings.ts
2126
- /**
2127
- * Returns the headings of the current page, extracted from the route loader data.
2128
- * Useful for building custom Tables of Contents or jumping to sections.
2129
- *
2130
- * @returns An array of heading objects with level, text, and id.
2131
- */
2132
- function useHeadings() {
2133
- const matches = (0, react_router_dom.useMatches)();
2134
- return (0, react.useMemo)(() => {
2135
- return (matches[matches.length - 1]?.data)?.headings || [];
2136
- }, [matches]);
2137
- }
2138
-
2139
2321
  //#endregion
2140
2322
  //#region src/client/components/docs-layout-default.tsx
2141
2323
  function DocsLayoutComponent({ children }) {
@@ -2176,6 +2358,7 @@ function DocsLayoutComponent({ children }) {
2176
2358
  className: "prose prose-neutral dark:prose-invert max-w-none",
2177
2359
  children
2178
2360
  }) }),
2361
+ !isCollectionPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Feedback, {}),
2179
2362
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_docs_layout.DocsLayout.Footer, { children: !isCollectionPage && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageNav, {}) })
2180
2363
  ]
2181
2364
  })
@@ -2269,7 +2452,9 @@ exports.getTranslated = getTranslated;
2269
2452
  exports.reactToText = reactToText;
2270
2453
  exports.useAnalytics = useAnalytics;
2271
2454
  exports.useBreadcrumbs = useBreadcrumbs;
2455
+ exports.useCodeBlock = require_mdx.useCodeBlock;
2272
2456
  exports.useConfig = require_icons_dev.useConfig;
2457
+ exports.useFeedback = useFeedback;
2273
2458
  exports.useHeadings = useHeadings;
2274
2459
  exports.useI18n = useI18n;
2275
2460
  exports.useLocalizedTo = require_icons_dev.useLocalizedTo;