boltdocs 2.7.10 → 2.8.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 (193) hide show
  1. package/README.md +2 -2
  2. package/dist/banner-3N4Jd_L9.d.ts +100 -0
  3. package/dist/banner-MynZD_Ox.d.cts +100 -0
  4. package/dist/cache-BMUyNiiA.mjs +6 -0
  5. package/dist/cache-CKm45d2w.cjs +6 -0
  6. package/dist/client/index.cjs +2268 -1
  7. package/dist/client/index.d.cts +86 -110
  8. package/dist/client/index.d.ts +87 -111
  9. package/dist/client/index.js +2214 -1
  10. package/dist/client/mdx.cjs +12 -1
  11. package/dist/client/mdx.d.cts +39 -93
  12. package/dist/client/mdx.d.ts +38 -93
  13. package/dist/client/mdx.js +7 -1
  14. package/dist/client/primitives.cjs +60 -1
  15. package/dist/client/primitives.d.cts +411 -347
  16. package/dist/client/primitives.d.ts +411 -347
  17. package/dist/client/primitives.js +20 -1
  18. package/dist/docs-layout-CwCq42Zt.cjs +1348 -0
  19. package/dist/docs-layout-Dn6S5g59.js +1167 -0
  20. package/dist/doctor-BArviV8X.cjs +28 -0
  21. package/dist/doctor-CgLA7_Uv.mjs +28 -0
  22. package/dist/{doctor-CrytFkqW.cjs → doctor-DyNUVe96.cjs} +1 -1
  23. package/dist/{routes-DP1vmWRj.cjs → doctor-aN_leTbh.mjs} +1 -1
  24. package/dist/{generator-ClVanhvi.mjs → generator-BHCrLU6h.mjs} +2 -2
  25. package/dist/{generator-CHqxiQhF.cjs → generator-CC2yHzhZ.cjs} +2 -2
  26. package/dist/icons-dev-DvJ-hh9x.cjs +1209 -0
  27. package/dist/icons-dev-Oju24Wjp.js +845 -0
  28. package/dist/image-Ch4-GxdO.cjs +268 -0
  29. package/dist/image-Do8V9PCW.js +214 -0
  30. package/dist/mdx-D3A2_l7P.js +520 -0
  31. package/dist/mdx-PLhhPJRS.cjs +531 -0
  32. package/dist/node/cli-entry.cjs +3 -1
  33. package/dist/node/cli-entry.mjs +3 -1
  34. package/dist/node/index.cjs +1 -1
  35. package/dist/node/index.d.cts +258 -152
  36. package/dist/node/index.d.mts +258 -150
  37. package/dist/node/index.mjs +1 -1
  38. package/dist/node/routes/worker.cjs +1 -1
  39. package/dist/node/routes/worker.mjs +1 -1
  40. package/dist/node-BmlP0eBP.cjs +159 -0
  41. package/dist/node-Y8_4ayje.mjs +159 -0
  42. package/dist/package-2nFy_NsW.cjs +6 -0
  43. package/dist/{package--0Yf0t1N.mjs → package-DAbtltXX.mjs} +1 -1
  44. package/dist/parser-B7-6PyQz.cjs +6 -0
  45. package/dist/{parser-Aq8LoH-0.cjs → parser-BzB-zCkF.cjs} +1 -1
  46. package/dist/parser-WGZdWs0X.mjs +6 -0
  47. package/dist/routes-BDDSxAl0.mjs +6 -0
  48. package/dist/routes-DJNJ-rTt.cjs +6 -0
  49. package/dist/routes-DiYC4nD2.cjs +6 -0
  50. package/dist/routes-_Bb2f4eI.mjs +6 -0
  51. package/dist/search-dialog-BXVoecTx.cjs +483 -0
  52. package/dist/search-dialog-BYhOov4S.cjs +331 -0
  53. package/dist/search-dialog-C09riYmx.js +313 -0
  54. package/dist/search-dialog-CUeAfy-8.cjs +8 -0
  55. package/dist/search-dialog-D8gLkhUV.js +453 -0
  56. package/dist/search-dialog-DHc_8FFX.js +8 -0
  57. package/dist/{sidebar-CcBkrm06.d.cts → sidebar-DNq4_ZAa.d.ts} +118 -52
  58. package/dist/{sidebar-CyZS9YOm.d.ts → sidebar-Dlkgbxs6.d.cts} +118 -52
  59. package/dist/utils-BYITg7T5.mjs +7 -0
  60. package/dist/utils-Cjmx1hhk.cjs +7 -0
  61. package/dist/worker-pool-CtqklOXq.cjs +6 -0
  62. package/dist/worker-pool-k0DY6k8T.mjs +6 -0
  63. package/package.json +5 -6
  64. package/src/shared/config-utils.ts +4 -0
  65. package/src/shared/types.ts +52 -6
  66. package/dist/cache-Ba-DZQNH.cjs +0 -6
  67. package/dist/cache-BuMZ58L5.mjs +0 -6
  68. package/dist/cards-BakZPTz9.d.ts +0 -30
  69. package/dist/cards-CQn9mXZS.d.cts +0 -30
  70. package/dist/docs-layout-KoWNZc8_.js +0 -6
  71. package/dist/docs-layout-x2yKt2cL.cjs +0 -6
  72. package/dist/doctor-Be7Ly1oM.mjs +0 -21
  73. package/dist/doctor-jMxWZyLJ.cjs +0 -21
  74. package/dist/icons-dev-B_RZIyxu.js +0 -6
  75. package/dist/icons-dev-BlV3wWFT.cjs +0 -6
  76. package/dist/image-BHhTvQzr.cjs +0 -6
  77. package/dist/image-CqKzYD8f.js +0 -6
  78. package/dist/mdx-DudBEac0.js +0 -7
  79. package/dist/mdx-r4cDQxWu.cjs +0 -7
  80. package/dist/node-DtEDyN1u.cjs +0 -111
  81. package/dist/node-_1jhMGYx.mjs +0 -111
  82. package/dist/package-DrwtlXfk.cjs +0 -6
  83. package/dist/parser-CdNbqN5y.cjs +0 -6
  84. package/dist/parser-nE792MLO.mjs +0 -6
  85. package/dist/rolldown-runtime-fkIsjY3S.mjs +0 -6
  86. package/dist/routes-2k3tbUmC.cjs +0 -6
  87. package/dist/routes-CpxZIsMM.mjs +0 -6
  88. package/dist/search-dialog-B584t9ZF.js +0 -6
  89. package/dist/search-dialog-BvBopRsZ.cjs +0 -6
  90. package/dist/search-dialog-ByvGScjt.js +0 -6
  91. package/dist/search-dialog-Cyko6TJm.cjs +0 -6
  92. package/dist/search-dialog-D6BNohIJ.js +0 -6
  93. package/dist/search-dialog-DuYTIefy.cjs +0 -6
  94. package/dist/utils-CG65J0Sc.mjs +0 -7
  95. package/dist/utils-CKunkU96.cjs +0 -7
  96. package/dist/worker-pool-CGn7DrLb.mjs +0 -6
  97. package/dist/worker-pool-Crbqgw5R.cjs +0 -6
  98. package/src/client/app/config-context.tsx +0 -51
  99. package/src/client/app/doc-page.tsx +0 -38
  100. package/src/client/app/docs-layout.tsx +0 -28
  101. package/src/client/app/head.tsx +0 -122
  102. package/src/client/app/helmet-compat.tsx +0 -36
  103. package/src/client/app/mdx-component.tsx +0 -8
  104. package/src/client/app/mdx-components-context.tsx +0 -72
  105. package/src/client/app/routes-context.tsx +0 -34
  106. package/src/client/app/scroll-handler.tsx +0 -74
  107. package/src/client/app/theme-context.tsx +0 -103
  108. package/src/client/app/ui-context.tsx +0 -42
  109. package/src/client/components/docs-layout-default.tsx +0 -85
  110. package/src/client/components/icons-dev.tsx +0 -282
  111. package/src/client/components/mdx/callout.tsx +0 -97
  112. package/src/client/components/mdx/card.tsx +0 -99
  113. package/src/client/components/mdx/cards.tsx +0 -27
  114. package/src/client/components/mdx/code-block.tsx +0 -184
  115. package/src/client/components/mdx/field.tsx +0 -33
  116. package/src/client/components/mdx/image.tsx +0 -44
  117. package/src/client/components/mdx/index.ts +0 -19
  118. package/src/client/components/mdx/table.tsx +0 -54
  119. package/src/client/components/mdx/typographics.tsx +0 -120
  120. package/src/client/components/mdx/use-code-block.ts +0 -34
  121. package/src/client/components/primitives/breadcrumbs.tsx +0 -54
  122. package/src/client/components/primitives/button-group.tsx +0 -54
  123. package/src/client/components/primitives/button.tsx +0 -6
  124. package/src/client/components/primitives/code-block.tsx +0 -120
  125. package/src/client/components/primitives/docs-layout.tsx +0 -125
  126. package/src/client/components/primitives/error-boundary.tsx +0 -107
  127. package/src/client/components/primitives/heading.tsx +0 -128
  128. package/src/client/components/primitives/helpers/observer.ts +0 -141
  129. package/src/client/components/primitives/image.tsx +0 -26
  130. package/src/client/components/primitives/link.tsx +0 -102
  131. package/src/client/components/primitives/menu.tsx +0 -137
  132. package/src/client/components/primitives/navbar.tsx +0 -466
  133. package/src/client/components/primitives/on-this-page.tsx +0 -430
  134. package/src/client/components/primitives/page-nav.tsx +0 -51
  135. package/src/client/components/primitives/popover.tsx +0 -28
  136. package/src/client/components/primitives/search-dialog.tsx +0 -193
  137. package/src/client/components/primitives/sidebar.tsx +0 -423
  138. package/src/client/components/primitives/skeleton.tsx +0 -26
  139. package/src/client/components/primitives/tabs.tsx +0 -70
  140. package/src/client/components/primitives/tooltip.tsx +0 -81
  141. package/src/client/components/primitives/types.ts +0 -11
  142. package/src/client/components/ui-base/banner.tsx +0 -66
  143. package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
  144. package/src/client/components/ui-base/copy-markdown.tsx +0 -107
  145. package/src/client/components/ui-base/error-boundary.tsx +0 -15
  146. package/src/client/components/ui-base/github-stars.tsx +0 -29
  147. package/src/client/components/ui-base/icons.tsx +0 -240
  148. package/src/client/components/ui-base/index.ts +0 -16
  149. package/src/client/components/ui-base/last-updated.tsx +0 -27
  150. package/src/client/components/ui-base/navbar.tsx +0 -266
  151. package/src/client/components/ui-base/not-found.tsx +0 -26
  152. package/src/client/components/ui-base/on-this-page.tsx +0 -57
  153. package/src/client/components/ui-base/page-nav.tsx +0 -50
  154. package/src/client/components/ui-base/search-dialog.tsx +0 -163
  155. package/src/client/components/ui-base/search-highlight.tsx +0 -10
  156. package/src/client/components/ui-base/sidebar.tsx +0 -92
  157. package/src/client/components/ui-base/tabs.tsx +0 -83
  158. package/src/client/components/ui-base/theme-toggle.tsx +0 -130
  159. package/src/client/components/ui-base/version-i18n.tsx +0 -80
  160. package/src/client/hooks/index.ts +0 -13
  161. package/src/client/hooks/use-analytics.ts +0 -272
  162. package/src/client/hooks/use-breadcrumbs.ts +0 -22
  163. package/src/client/hooks/use-i18n.ts +0 -182
  164. package/src/client/hooks/use-localized-to.ts +0 -113
  165. package/src/client/hooks/use-location.ts +0 -5
  166. package/src/client/hooks/use-navbar.ts +0 -130
  167. package/src/client/hooks/use-page-nav.ts +0 -46
  168. package/src/client/hooks/use-routes.ts +0 -108
  169. package/src/client/hooks/use-search-highlight.ts +0 -185
  170. package/src/client/hooks/use-search.ts +0 -118
  171. package/src/client/hooks/use-sidebar.ts +0 -205
  172. package/src/client/hooks/use-tabs.ts +0 -46
  173. package/src/client/hooks/use-version.ts +0 -111
  174. package/src/client/index.ts +0 -31
  175. package/src/client/mdx.ts +0 -2
  176. package/src/client/primitives.ts +0 -19
  177. package/src/client/ssg/boltdocs-shell.tsx +0 -148
  178. package/src/client/ssg/create-routes.tsx +0 -473
  179. package/src/client/ssg/index.ts +0 -4
  180. package/src/client/ssg/mdx-page.tsx +0 -38
  181. package/src/client/store/boltdocs-context.tsx +0 -137
  182. package/src/client/theme/neutral.css +0 -141
  183. package/src/client/theme/reset.css +0 -189
  184. package/src/client/types.ts +0 -116
  185. package/src/client/utils/cn.ts +0 -6
  186. package/src/client/utils/copy-clipboard.ts +0 -22
  187. package/src/client/utils/get-base-file-path.ts +0 -21
  188. package/src/client/utils/github.ts +0 -121
  189. package/src/client/utils/i18n.ts +0 -23
  190. package/src/client/utils/path.ts +0 -9
  191. package/src/client/utils/react-to-text.ts +0 -34
  192. package/src/client/virtual.d.ts +0 -24
  193. /package/dist/{meta-loader-CWg2gnbY.mjs → meta-loader-DzwDFtdT.mjs} +0 -0
@@ -1,182 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { useNavigate } from 'react-router-dom'
3
- import { getBaseFilePath } from '../utils/get-base-file-path'
4
- import { useRoutes } from './use-routes'
5
- import { useConfig } from '../app/config-context'
6
- import { useBoltdocsContext } from '../store/boltdocs-context'
7
- import type { BoltdocsLocale } from '../../shared/types'
8
-
9
- export interface LocaleOption {
10
- key: BoltdocsLocale
11
- label: string
12
- value: string
13
- isCurrent: boolean
14
- }
15
-
16
- export interface UseI18nReturn {
17
- currentLocale: BoltdocsLocale | undefined
18
- currentLocaleLabel: string | undefined
19
- availableLocales: LocaleOption[]
20
- handleLocaleChange: (locale: BoltdocsLocale) => void
21
- }
22
-
23
- /**
24
- * Hook to manage and switch between different locales (languages) of the documentation.
25
- */
26
- export function useI18n(): UseI18nReturn {
27
- const navigate = useNavigate()
28
- const config = useConfig()
29
- const { allRoutes, currentRoute, currentLocale, currentVersion } = useRoutes()
30
- const i18n = config.i18n
31
- const { setLocale } = useBoltdocsContext()
32
-
33
- const handleLocaleChange = (locale: string) => {
34
- if (!i18n || locale === currentLocale) return
35
-
36
- // Update store
37
- setLocale(locale)
38
-
39
- const base = config.base || '/'
40
- const safeBase = base === '/' ? '' : base.replace(/\/$/, '')
41
- const isDocRoute = !!currentRoute?.filePath
42
-
43
- let targetPath = ''
44
-
45
- if (currentRoute) {
46
- // Case A: We are on a known route. Determine if it is doc or external.
47
- if (isDocRoute) {
48
- // Documentation Context logic
49
- const baseFile = getBaseFilePath(
50
- currentRoute.filePath,
51
- currentRoute.version,
52
- currentRoute.locale,
53
- )
54
-
55
- const targetRoute = allRoutes.find(
56
- (r) =>
57
- getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
58
- (r.locale || i18n.defaultLocale) === locale &&
59
- r.version === currentRoute.version,
60
- )
61
-
62
- if (targetRoute) {
63
- targetPath = targetRoute.path
64
- } else {
65
- // Recovery: Find target index, or hardcode reconstruct using version space
66
- const defaultIndexRoute = allRoutes.find(
67
- (r) =>
68
- getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
69
- (r.locale || i18n.defaultLocale) === locale &&
70
- r.version === currentRoute.version,
71
- )
72
-
73
- if (defaultIndexRoute) {
74
- targetPath = defaultIndexRoute.path
75
- } else {
76
- // Hardcoded absolute construction preserving existing version structure
77
- const vPath = currentRoute.version ? `/${currentRoute.version}` : ''
78
- const lPath = locale === i18n.defaultLocale ? '' : `/${locale}`
79
- targetPath = `${safeBase}${vPath}${lPath}` || '/'
80
- }
81
- }
82
- } else {
83
- // External Context Logic: simply rewrite the current absolute path
84
- // Extract pure relative component by stripping existing locale prefix
85
- let rawExternal = currentRoute.path
86
-
87
- // Strip existing locale if any
88
- const parts = rawExternal.split('/').filter(Boolean)
89
- if (
90
- parts.length > 0 &&
91
- (Array.isArray(i18n.locales)
92
- ? i18n.locales.includes(parts[0])
93
- : !!i18n.locales[parts[0]])
94
- ) {
95
- // Already prefixed external route like /es/about -> become /about
96
- parts.shift()
97
- rawExternal = '/' + parts.join('/')
98
- }
99
-
100
- // Re-apply new locale
101
- if (locale === i18n.defaultLocale) {
102
- targetPath = rawExternal === '' ? '/' : rawExternal
103
- } else {
104
- const cleanExt = rawExternal.startsWith('/')
105
- ? rawExternal
106
- : `/${rawExternal}`
107
- targetPath = `/${locale}${cleanExt === '/' ? '' : cleanExt}`
108
- }
109
- }
110
- } else {
111
- // Case B: Fallback for Unknown / 404 page
112
- // Try to find first available page that matches the intended combo
113
- const targetRoute = allRoutes.find(
114
- (r) =>
115
- (r.locale || i18n.defaultLocale) === locale &&
116
- (r.version || config.versions?.defaultVersion) ===
117
- (currentVersion || config.versions?.defaultVersion),
118
- )
119
-
120
- if (targetRoute) {
121
- targetPath = targetRoute.path
122
- } else {
123
- const vPath =
124
- currentVersion && currentVersion !== config.versions?.defaultVersion
125
- ? `/${currentVersion}`
126
- : ''
127
- targetPath =
128
- locale === i18n.defaultLocale
129
- ? `${safeBase}${vPath}`
130
- : `${safeBase}${vPath}/${locale}`
131
- }
132
- }
133
-
134
- // Final safety check: cleanup double slashes and empty targets
135
- if (!targetPath || targetPath === '') targetPath = '/'
136
- targetPath = targetPath.replace(/\/+/g, '/')
137
-
138
- navigate(targetPath)
139
- }
140
-
141
- const locales = i18n?.locales
142
- const defaultLabel = locales
143
- ? Array.isArray(locales)
144
- ? currentLocale
145
- : (locales as Record<string, string>)[currentLocale as string]
146
- : undefined
147
-
148
- const currentLocaleConfig = i18n?.localeConfigs?.[currentLocale as string]
149
- const currentLocaleLabel =
150
- currentLocaleConfig?.label || defaultLabel || currentLocale
151
-
152
- const availableLocales = useMemo(() => {
153
- return i18n
154
- ? Array.isArray(i18n.locales)
155
- ? i18n.locales.map((key) => {
156
- const localeConfig = i18n?.localeConfigs?.[key]
157
- return {
158
- key: key as BoltdocsLocale,
159
- label: localeConfig?.label || key,
160
- value: key,
161
- isCurrent: key === currentLocale,
162
- }
163
- })
164
- : Object.entries(i18n.locales).map(([key, label]) => {
165
- const localeConfig = i18n?.localeConfigs?.[key]
166
- return {
167
- key: key as BoltdocsLocale,
168
- label: localeConfig?.label || label,
169
- value: key,
170
- isCurrent: key === currentLocale,
171
- }
172
- })
173
- : []
174
- }, [i18n, currentLocale])
175
-
176
- return {
177
- currentLocale,
178
- currentLocaleLabel,
179
- availableLocales,
180
- handleLocaleChange,
181
- }
182
- }
@@ -1,113 +0,0 @@
1
- import { useConfig } from '../app/config-context'
2
- import type { LinkProps as RouterLinkProps } from 'react-router-dom'
3
- import { useRoutes } from './use-routes'
4
-
5
- /**
6
- * Hook to automatically localize a path based on the current version and locale context.
7
- * It ensures that navigation preserves the active version and language across the entire site.
8
- */
9
- export function useLocalizedTo(to: string): string
10
- export function useLocalizedTo(to: RouterLinkProps['to']): RouterLinkProps['to']
11
- export function useLocalizedTo(
12
- to: RouterLinkProps['to'],
13
- ): RouterLinkProps['to'] {
14
- const config = useConfig()
15
- const {
16
- currentLocale: activeLocale,
17
- currentVersion: activeVersion,
18
- allRoutes,
19
- } = useRoutes()
20
-
21
- if (!config || typeof to !== 'string') return to
22
-
23
- // External, absolute, or anchor links don't need localization prefixing
24
- if (
25
- to.startsWith('http') ||
26
- to.startsWith('//') ||
27
- to.startsWith('#') ||
28
- to.startsWith('site:')
29
- ) {
30
- return to.replace('site:', '')
31
- }
32
-
33
- // 0. Identify if the incoming path is explicitly registered as a known route
34
- const [pathOnly, hashAndQuery] = to.split(/([?#].*)/s)
35
- const normalizedTo =
36
- pathOnly.endsWith('/') && pathOnly.length > 1
37
- ? pathOnly.slice(0, -1)
38
- : pathOnly
39
-
40
- const isKnownRoute = allRoutes?.some((r) => {
41
- const rp =
42
- r.path.endsWith('/') && r.path.length > 1 ? r.path.slice(0, -1) : r.path
43
- return rp === (normalizedTo || '/')
44
- })
45
-
46
- const i18n = config.i18n
47
- const versions = config.versions
48
- const base = (config.base || '/docs').replace(/\/$/, '')
49
- const baseSegment = base.startsWith('/') ? base.substring(1) : base
50
-
51
- const rawParts = pathOnly.split('/').filter(Boolean)
52
-
53
- // Classify: it's a Doc Path if it explicitly contains base segment,
54
- // OR if it's an 'unknown' path (backward compatible fallback assumes unknown = doc).
55
- const hasExplicitBase =
56
- baseSegment && rawParts.length > 0 && rawParts[0] === baseSegment
57
- const isDocsPath = hasExplicitBase || (!isKnownRoute && rawParts.length > 0)
58
-
59
- // 3. Clean the 'to' path of ANY existing prefixes to avoid stacking
60
- const parts = [...rawParts]
61
- let pIdx = 0
62
-
63
- // Strip base segment if present at start
64
- if (baseSegment && parts[pIdx] === baseSegment) pIdx++
65
-
66
- // Strip versions if present
67
- if (versions && parts.length > pIdx) {
68
- const vMatch = versions.versions.find((v) => v.path === parts[pIdx])
69
- if (vMatch) pIdx++
70
- }
71
-
72
- // Strip locales if present
73
- const isLocale =
74
- i18n &&
75
- parts.length > pIdx &&
76
- (Array.isArray(i18n.locales)
77
- ? i18n.locales.includes(parts[pIdx])
78
- : parts[pIdx] in i18n.locales)
79
- if (isLocale) pIdx++
80
-
81
- // The actual relative route remaining
82
- const routeContent = parts.slice(pIdx)
83
-
84
- // 4. Reconstruct dynamically based on context
85
- const resultParts: string[] = []
86
-
87
- if (isDocsPath) {
88
- // Reconstruct DOCS path: /base/version/locale/content
89
- if (baseSegment) resultParts.push(baseSegment)
90
- if (versions && activeVersion) resultParts.push(activeVersion)
91
- if (i18n && activeLocale) resultParts.push(activeLocale)
92
- } else {
93
- // Reconstruct EXTERNAL path: /locale/content
94
- if (i18n && activeLocale) resultParts.push(activeLocale)
95
- }
96
-
97
- resultParts.push(...routeContent)
98
-
99
- let finalPath = `/${resultParts.join('/')}`
100
-
101
- // Preserve trailing slash if present in input and output isn't just root
102
- if (
103
- pathOnly.endsWith('/') &&
104
- pathOnly.length > 1 &&
105
- !finalPath.endsWith('/')
106
- ) {
107
- finalPath += '/'
108
- }
109
-
110
- // Restore original query/hash
111
- const result = (finalPath || '/') + (hashAndQuery || '')
112
- return result
113
- }
@@ -1,5 +0,0 @@
1
- import { useLocation as useReactLocation } from 'react-router-dom'
2
-
3
- export const useLocation = () => {
4
- return useReactLocation()
5
- }
@@ -1,130 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { useLocation } from 'react-router-dom'
3
- import { useConfig } from '../app/config-context'
4
- import { useTheme } from '../app/theme-context'
5
- import type { NavbarLink } from '../types'
6
- import { getTranslated } from '../utils/i18n'
7
- import { useRoutes } from './use-routes'
8
-
9
- export function useNavbar() {
10
- const config = useConfig()
11
- const { theme, resolvedTheme } = useTheme()
12
- const location = useLocation()
13
- const { currentLocale } = useRoutes()
14
-
15
- const themeConfig = config.theme || {}
16
- const title = getTranslated(themeConfig.title, currentLocale) || 'Boltdocs'
17
- const rawLinks = themeConfig.navbar || []
18
- const socialLinks = themeConfig.socialLinks || []
19
- const githubRepo = themeConfig.githubRepo
20
-
21
- // Transform links to the new NavbarLink structure
22
- const links: NavbarLink[] = useMemo(() => {
23
- return rawLinks.map((item: any) => {
24
- const href = (item.href || item.to || item.link || '') as string
25
-
26
- // Robust active state calculation
27
- const getIsActive = (h: string) => {
28
- const activePath = location.pathname
29
- if (activePath === h) return true
30
- if (!h || h === '/') return activePath === '/'
31
-
32
- const cleanPathParts = (p: string) => {
33
- const parts = p.split('/').filter(Boolean)
34
- let i = 0
35
- // Skip locale
36
- if (
37
- config.i18n?.locales &&
38
- parts[i] &&
39
- config.i18n.locales[parts[i]]
40
- ) {
41
- i++
42
- }
43
- // Skip version
44
- if (config.versions?.versions && parts[i]) {
45
- if (config.versions.versions.some((v) => v.path === parts[i])) {
46
- i++
47
- }
48
- }
49
- return parts.slice(i)
50
- }
51
-
52
- const hParts = cleanPathParts(h)
53
- const pParts = cleanPathParts(activePath)
54
-
55
- if (hParts.length === 0) return pParts.length === 0
56
-
57
- // Must match at least as many parts as the candidate link
58
- if (pParts.length < hParts.length) return false
59
-
60
- // Every part of hParts must match pParts at the same position
61
- return hParts.every((part, i) => pParts[i] === part)
62
- }
63
-
64
- // Process nested items recursively
65
- const processItems = (items?: any[]): NavbarLink[] => {
66
- if (!items || items.length === 0) return undefined as any
67
- return items.map((subItem: any) => {
68
- const subHref = (subItem.href ||
69
- subItem.to ||
70
- subItem.link ||
71
- '') as string
72
- return {
73
- label: getTranslated(subItem.label || subItem.text, currentLocale),
74
- href: subHref,
75
- active: getIsActive(subHref),
76
- to:
77
- subHref.startsWith('http') || subHref.startsWith('//')
78
- ? 'external'
79
- : undefined,
80
- }
81
- })
82
- }
83
-
84
- const linkItems = processItems(item.items)
85
-
86
- return {
87
- label: getTranslated(item.label || item.text, currentLocale),
88
- href,
89
- active: getIsActive(href),
90
- to:
91
- href.startsWith('http') || href.startsWith('//')
92
- ? 'external'
93
- : undefined,
94
- items: linkItems,
95
- }
96
- })
97
- }, [rawLinks, location.pathname, currentLocale, config])
98
-
99
- const logo = themeConfig.logo
100
- // Use resolvedTheme so 'system' correctly maps to 'dark' or 'light'
101
- // based on the OS preference, instead of always falling back to 'light'.
102
- const logoSrc = !logo
103
- ? null
104
- : typeof logo === 'string'
105
- ? logo
106
- : resolvedTheme === 'dark'
107
- ? (logo as any).dark
108
- : (logo as any).light
109
-
110
- const logoProps = {
111
- alt:
112
- (logo && typeof logo === 'object' ? (logo as any).alt : undefined) ||
113
- title,
114
- width: logo && typeof logo === 'object' ? (logo as any).width : undefined,
115
- height: logo && typeof logo === 'object' ? (logo as any).height : undefined,
116
- }
117
-
118
- const github = githubRepo ? `https://github.com/${githubRepo}` : null
119
-
120
- return {
121
- links,
122
- title,
123
- logo: logoSrc,
124
- logoProps,
125
- github,
126
- social: socialLinks,
127
- config,
128
- theme,
129
- }
130
- }
@@ -1,46 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { useLocation } from 'react-router-dom'
3
- import { useRoutes } from './use-routes'
4
-
5
- /**
6
- * Hook to manage the previous and next button functionality for documentation pages.
7
- * Intelligent: respects current locale, version, and tab to keep navigation logical.
8
- */
9
- export function usePageNav() {
10
- const { routes, currentRoute } = useRoutes()
11
- const location = useLocation()
12
-
13
- return useMemo(() => {
14
- if (!currentRoute) {
15
- return {
16
- prevPage: null,
17
- nextPage: null,
18
- currentRoute: null,
19
- }
20
- }
21
-
22
- const activeTabId = currentRoute.tab?.toLowerCase()
23
-
24
- // Subset of routes that match the current context (locale and version are already filtered by useRoutes)
25
- // We further filter by tab to keep the user in the same logical section
26
- const contextRoutes = activeTabId
27
- ? routes.filter((r) => r.tab?.toLowerCase() === activeTabId)
28
- : routes.filter((r) => !r.tab)
29
-
30
- const currentIndex = contextRoutes.findIndex(
31
- (r) => r.path === location.pathname,
32
- )
33
-
34
- const prevPage = currentIndex > 0 ? contextRoutes[currentIndex - 1] : null
35
- const nextPage =
36
- currentIndex !== -1 && currentIndex < contextRoutes.length - 1
37
- ? contextRoutes[currentIndex + 1]
38
- : null
39
-
40
- return {
41
- prevPage,
42
- nextPage,
43
- currentRoute,
44
- }
45
- }, [routes, currentRoute, location.pathname])
46
- }
@@ -1,108 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { useLocation } from 'react-router-dom'
3
- import { useConfig } from '../app/config-context'
4
- import { useRoutesContext } from '../app/routes-context'
5
- import { useBoltdocsContext } from '../store/boltdocs-context'
6
- import { normalizePath } from '../utils/path'
7
-
8
- /**
9
- * Hook to access the framework's routing state.
10
- * Returns both the complete set of routes and a filtered list based on the current
11
- * version and locale.
12
- */
13
- export function useRoutes() {
14
- const { routes: allRoutes } = useRoutesContext()
15
- const config = useConfig()
16
- const location = useLocation()
17
-
18
- // Use Zustand store for active state
19
- const {
20
- hasHydrated,
21
- currentLocale: currentLocaleStore,
22
- currentVersion: currentVersionStore,
23
- } = useBoltdocsContext()
24
-
25
- const currentPath = normalizePath(location.pathname)
26
-
27
- // Find the current active route matching the pathname
28
- const currentRoute = allRoutes?.find?.(
29
- (r) => normalizePath(r.path) === currentPath,
30
- )
31
-
32
- // 2. STRICT SOURCE OF TRUTH:
33
- // Derive the active states exclusively from the hydrated Context Store.
34
- // This ensures that user preference (LocalStorage) takes precedence over ambiguous URL fallbacks.
35
- const currentLocale = config.i18n
36
- ? currentLocaleStore || config.i18n.defaultLocale
37
- : undefined
38
-
39
- const currentVersion = config.versions
40
- ? currentVersionStore || config.versions.defaultVersion
41
- : undefined
42
-
43
- // Filter routes to those matching the current version and locale
44
- const routes = useMemo(() => {
45
- if (!allRoutes) return []
46
-
47
- // Pre-calculate alternate presence using a Map of maps or a composite key
48
- // Key: filePath | (locale || defaultLocale) | (version || defaultVersion)
49
- const alternateCounts = new Map<string, number>()
50
- const defaultLocale = config.i18n?.defaultLocale || ''
51
- const defaultVersion = config.versions?.defaultVersion || ''
52
-
53
- for (const r of allRoutes) {
54
- const locale = r.locale || defaultLocale
55
- const version = r.version || defaultVersion
56
- const key = `${r.filePath}::${locale}::${version}`
57
- alternateCounts.set(key, (alternateCounts.get(key) || 0) + 1)
58
- }
59
-
60
- return allRoutes.filter((r) => {
61
- const localeMatch = config.i18n
62
- ? (r.locale || config.i18n.defaultLocale) === currentLocale
63
- : true
64
- const versionMatch = config.versions
65
- ? (r.version || config.versions.defaultVersion) === currentVersion
66
- : true
67
-
68
- if (!(localeMatch && versionMatch)) return false
69
-
70
- // Resolve duplicate paths (aliases) like /docs vs /docs/en
71
- // 3. Resolve duplicate route aliases (e.g., /docs/page vs /docs/latest/page or /docs/es/page)
72
- // If duplicates exist, we only show the style (prefixed or unprefixed) that matches the user's current page style.
73
- const isCurrentLocalePrefixed = !!currentRoute?.locale
74
- const isCurrentVersionPrefixed = !!currentRoute?.version
75
-
76
- const isRouteLocalePrefixed = !!r.locale
77
- const isRouteVersionPrefixed = !!r.version
78
-
79
- const locale = r.locale || defaultLocale
80
- const version = r.version || defaultVersion
81
- const key = `${r.filePath}::${locale}::${version}`
82
- const hasAlternate = (alternateCounts.get(key) || 0) > 1
83
-
84
- if (hasAlternate) {
85
- // Style mismatch checks
86
- const localeMismatch =
87
- config.i18n && isCurrentLocalePrefixed !== isRouteLocalePrefixed
88
- const versionMismatch =
89
- config.versions && isCurrentVersionPrefixed !== isRouteVersionPrefixed
90
-
91
- if (localeMismatch || versionMismatch) {
92
- return false
93
- }
94
- }
95
-
96
- return true
97
- })
98
- }, [allRoutes, config, currentLocale, currentVersion, currentRoute])
99
-
100
- return {
101
- routes,
102
- allRoutes,
103
- currentRoute,
104
- currentLocale: currentLocale as import('../../shared/types').BoltdocsLocale,
105
- currentVersion:
106
- currentVersion as import('../../shared/types').BoltdocsVersion,
107
- }
108
- }