boltdocs 2.6.2 → 2.7.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 (177) hide show
  1. package/bin/boltdocs.js +0 -1
  2. package/dist/cache-CQKlT4fI.mjs +6 -0
  3. package/dist/cache-DorPMFgW.cjs +6 -0
  4. package/dist/cards-BLoSiRuL.d.ts +30 -0
  5. package/dist/cards-CQn9mXZS.d.cts +30 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +1 -1
  8. package/dist/client/index.d.cts +167 -1338
  9. package/dist/client/index.d.ts +166 -1337
  10. package/dist/client/index.js +1 -1
  11. package/dist/{package-CFP44vfn.cjs → client/mdx.cjs} +1 -1
  12. package/dist/client/mdx.d.cts +128 -0
  13. package/dist/client/mdx.d.ts +129 -0
  14. package/dist/client/mdx.js +6 -0
  15. package/dist/client/primitives.cjs +6 -0
  16. package/dist/client/primitives.d.cts +818 -0
  17. package/dist/client/primitives.d.ts +818 -0
  18. package/dist/client/primitives.js +6 -0
  19. package/dist/client/theme/neutral.css +74 -361
  20. package/dist/client/theme/reset.css +189 -0
  21. package/dist/docs-layout-BlDhcQRv.cjs +6 -0
  22. package/dist/docs-layout-BvAOWEJw.js +6 -0
  23. package/dist/doctor-BQiQhCTl.cjs +6 -0
  24. package/dist/doctor-COpf35L2.cjs +20 -0
  25. package/dist/doctor-Dh1XP7Pz.mjs +20 -0
  26. package/dist/generator-DGW6pkCC.cjs +22 -0
  27. package/dist/generator-Dv3wEmhZ.mjs +22 -0
  28. package/dist/icons-dev-CrQLjoQp.js +6 -0
  29. package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
  30. package/dist/image-BkIfa9oo.js +6 -0
  31. package/dist/image-DIGjCPe6.cjs +6 -0
  32. package/dist/mdx-K0WYBAJ3.js +7 -0
  33. package/dist/mdx-hpErbRUe.cjs +7 -0
  34. package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
  35. package/dist/meta-loader-9IpAHWDS.mjs +6 -0
  36. package/dist/node/cli-entry.cjs +1 -2
  37. package/dist/node/cli-entry.mjs +1 -2
  38. package/dist/node/index.cjs +1 -1
  39. package/dist/node/index.d.cts +55 -11
  40. package/dist/node/index.d.mts +55 -12
  41. package/dist/node/index.mjs +1 -1
  42. package/dist/node/routes/worker.cjs +6 -0
  43. package/dist/node/routes/worker.d.cts +2 -0
  44. package/dist/node/routes/worker.d.mts +2 -0
  45. package/dist/node/routes/worker.mjs +6 -0
  46. package/dist/node-C2nWXElP.mjs +112 -0
  47. package/dist/node-CinkUtxV.cjs +112 -0
  48. package/dist/package-BMYLDBBP.cjs +6 -0
  49. package/dist/{package-Bqbn1AYK.mjs → package-HegMOTL_.mjs} +1 -1
  50. package/dist/parser-Bh11BsdA.cjs +6 -0
  51. package/dist/parser-D8eQvE7N.mjs +6 -0
  52. package/dist/parser-DYRzXWmA.cjs +6 -0
  53. package/dist/routes-CHf76Ye4.cjs +6 -0
  54. package/dist/routes-CMUZGI6T.mjs +6 -0
  55. package/dist/routes-Co1mRM58.cjs +6 -0
  56. package/dist/search-dialog-BACuzoVX.cjs +6 -0
  57. package/dist/search-dialog-BKagVT17.js +6 -0
  58. package/dist/search-dialog-C8w12eUx.js +6 -0
  59. package/dist/search-dialog-CGyrozZE.cjs +6 -0
  60. package/dist/search-dialog-D26rUnJ_.cjs +6 -0
  61. package/dist/sidebar-DKvg6KOc.d.cts +491 -0
  62. package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
  63. package/dist/utils-BxNAXhZZ.mjs +7 -0
  64. package/dist/utils-Clzu7jvb.cjs +7 -0
  65. package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
  66. package/dist/worker-pool-BwU8ckrg.cjs +6 -0
  67. package/package.json +27 -8
  68. package/src/client/app/doc-page.tsx +9 -5
  69. package/src/client/app/docs-layout.tsx +17 -3
  70. package/src/client/app/head.tsx +122 -0
  71. package/src/client/app/helmet-compat.tsx +36 -0
  72. package/src/client/app/mdx-component.tsx +5 -52
  73. package/src/client/app/mdx-components-context.tsx +32 -8
  74. package/src/client/app/routes-context.tsx +2 -2
  75. package/src/client/app/scroll-handler.tsx +1 -1
  76. package/src/client/app/theme-context.tsx +5 -5
  77. package/src/client/app/ui-context.tsx +42 -0
  78. package/src/client/components/docs-layout-default.tsx +85 -0
  79. package/src/client/components/icons-dev.tsx +38 -15
  80. package/src/client/components/mdx/callout.tsx +97 -0
  81. package/src/client/components/mdx/card.tsx +73 -98
  82. package/src/client/components/mdx/cards.tsx +27 -0
  83. package/src/client/components/mdx/code-block.tsx +37 -17
  84. package/src/client/components/mdx/field.tsx +24 -56
  85. package/src/client/components/mdx/image.tsx +36 -15
  86. package/src/client/components/mdx/index.ts +19 -53
  87. package/src/client/components/mdx/table.tsx +46 -148
  88. package/src/client/components/mdx/typographics.tsx +120 -0
  89. package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
  90. package/src/client/components/primitives/breadcrumbs.tsx +5 -24
  91. package/src/client/components/primitives/button.tsx +3 -142
  92. package/src/client/components/primitives/code-block.tsx +104 -97
  93. package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
  94. package/src/client/components/primitives/error-boundary.tsx +107 -0
  95. package/src/client/components/primitives/heading.tsx +128 -0
  96. package/src/client/components/primitives/helpers/observer.ts +62 -32
  97. package/src/client/components/primitives/image.tsx +26 -0
  98. package/src/client/components/primitives/link.tsx +50 -52
  99. package/src/client/components/primitives/menu.tsx +25 -49
  100. package/src/client/components/primitives/navbar.tsx +234 -59
  101. package/src/client/components/primitives/on-this-page.tsx +169 -40
  102. package/src/client/components/primitives/page-nav.tsx +11 -39
  103. package/src/client/components/primitives/popover.tsx +12 -30
  104. package/src/client/components/primitives/search-dialog.tsx +77 -71
  105. package/src/client/components/primitives/sidebar.tsx +312 -119
  106. package/src/client/components/primitives/skeleton.tsx +1 -1
  107. package/src/client/components/primitives/tabs.tsx +5 -16
  108. package/src/client/components/primitives/tooltip.tsx +1 -1
  109. package/src/client/components/ui-base/banner.tsx +66 -0
  110. package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
  111. package/src/client/components/ui-base/copy-markdown.tsx +43 -35
  112. package/src/client/components/ui-base/error-boundary.tsx +9 -46
  113. package/src/client/components/ui-base/github-stars.tsx +5 -3
  114. package/src/client/components/ui-base/index.ts +3 -3
  115. package/src/client/components/ui-base/last-updated.tsx +27 -0
  116. package/src/client/components/ui-base/navbar.tsx +183 -89
  117. package/src/client/components/ui-base/not-found.tsx +11 -9
  118. package/src/client/components/ui-base/on-this-page.tsx +8 -104
  119. package/src/client/components/ui-base/page-nav.tsx +23 -9
  120. package/src/client/components/ui-base/search-dialog.tsx +111 -36
  121. package/src/client/components/ui-base/search-highlight.tsx +10 -0
  122. package/src/client/components/ui-base/sidebar.tsx +77 -154
  123. package/src/client/components/ui-base/tabs.tsx +20 -7
  124. package/src/client/components/ui-base/theme-toggle.tsx +88 -10
  125. package/src/client/components/ui-base/version-i18n.tsx +80 -0
  126. package/src/client/hooks/index.ts +2 -1
  127. package/src/client/hooks/use-analytics.ts +272 -0
  128. package/src/client/hooks/use-i18n.ts +116 -50
  129. package/src/client/hooks/use-localized-to.ts +70 -27
  130. package/src/client/hooks/use-navbar.ts +69 -39
  131. package/src/client/hooks/use-page-nav.ts +28 -25
  132. package/src/client/hooks/use-routes.ts +63 -80
  133. package/src/client/hooks/use-search-highlight.ts +185 -0
  134. package/src/client/hooks/use-search.ts +12 -3
  135. package/src/client/hooks/use-sidebar.ts +183 -80
  136. package/src/client/hooks/use-tabs.ts +3 -4
  137. package/src/client/hooks/use-version.ts +44 -29
  138. package/src/client/index.ts +13 -87
  139. package/src/client/mdx.ts +2 -0
  140. package/src/client/primitives.ts +19 -0
  141. package/src/client/ssg/boltdocs-shell.tsx +68 -79
  142. package/src/client/ssg/create-routes.tsx +268 -72
  143. package/src/client/ssg/mdx-page.tsx +2 -1
  144. package/src/client/store/boltdocs-context.tsx +72 -20
  145. package/src/client/theme/neutral.css +74 -361
  146. package/src/client/theme/reset.css +189 -0
  147. package/src/client/types.ts +10 -2
  148. package/src/client/utils/path.ts +9 -0
  149. package/src/client/utils/react-to-text.ts +24 -24
  150. package/src/client/virtual.d.ts +1 -1
  151. package/src/shared/types.ts +82 -22
  152. package/dist/node-Bogvkxao.mjs +0 -101
  153. package/dist/node-CXaog6St.cjs +0 -101
  154. package/dist/search-dialog-CV3eJzMm.cjs +0 -6
  155. package/dist/search-dialog-DNTomKgu.js +0 -6
  156. package/dist/use-search-CS3gH19M.js +0 -6
  157. package/dist/use-search-DBpJZQuw.cjs +0 -6
  158. package/src/client/components/mdx/admonition.tsx +0 -91
  159. package/src/client/components/mdx/badge.tsx +0 -41
  160. package/src/client/components/mdx/button.tsx +0 -35
  161. package/src/client/components/mdx/component-preview.tsx +0 -37
  162. package/src/client/components/mdx/component-props.tsx +0 -83
  163. package/src/client/components/mdx/file-tree.tsx +0 -325
  164. package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
  165. package/src/client/components/mdx/hooks/useTable.ts +0 -74
  166. package/src/client/components/mdx/hooks/useTabs.ts +0 -68
  167. package/src/client/components/mdx/link.tsx +0 -38
  168. package/src/client/components/mdx/list.tsx +0 -192
  169. package/src/client/components/mdx/tabs.tsx +0 -135
  170. package/src/client/components/mdx/video.tsx +0 -68
  171. package/src/client/components/primitives/index.ts +0 -19
  172. package/src/client/components/primitives/navigation-menu.tsx +0 -114
  173. package/src/client/components/ui-base/head.tsx +0 -83
  174. package/src/client/components/ui-base/loading.tsx +0 -57
  175. package/src/client/components/ui-base/powered-by.tsx +0 -25
  176. package/src/client/hooks/use-onthispage.ts +0 -23
  177. package/src/client/utils/use-on-change.ts +0 -15
@@ -1,102 +1,205 @@
1
+ import { useMemo } from 'react'
1
2
  import { useLocation } from 'react-router-dom'
2
3
  import { useConfig } from '../app/config-context'
3
4
  import type { ComponentRoute } from '../types'
5
+ import { normalizePath } from '../utils/path'
4
6
 
5
7
  export function useSidebar(routes: ComponentRoute[]) {
6
8
  const config = useConfig()
7
9
  const location = useLocation()
8
10
 
9
- // Find active route and tab
10
- const normalize = (p: string) =>
11
- p.endsWith('/') && p.length > 1 ? p.slice(0, -1) : p
12
- const currentPath = normalize(location.pathname)
13
-
14
- const activeRoute = routes.find((r) => normalize(r.path) === currentPath)
15
- const activeTabId = activeRoute?.tab?.toLowerCase()
16
-
17
- // Filter routes by active tab if any
18
- const filteredRoutes = activeTabId
19
- ? routes.filter((r) => !r.tab || r.tab.toLowerCase() === activeTabId)
20
- : routes
21
-
22
- const ungrouped: ComponentRoute[] = []
23
- const groupsMap = new Map<
24
- string,
25
- { slug: string; title: string; routes: ComponentRoute[]; icon?: string }
26
- >()
27
-
28
- for (const route of filteredRoutes) {
29
- // Skip home pages or external pages from the sidebar if they are not explicitly grouped
30
- if (!route.filePath && !route.group) continue
31
-
32
- if (!route.group) {
33
- ungrouped.push(route)
34
- } else {
35
- if (!groupsMap.has(route.group)) {
36
- groupsMap.set(route.group, {
37
- slug: route.group,
38
- title: route.groupTitle || route.group,
39
- routes: [],
40
- icon: route.groupIcon,
41
- })
11
+ const currentPath = normalizePath(location.pathname)
12
+
13
+ return useMemo(() => {
14
+ const activeRoute = routes.find(
15
+ (r) => normalizePath(r.path) === currentPath,
16
+ )
17
+ const activeTabId = activeRoute?.tab?.toLowerCase()
18
+
19
+ const filteredRoutes = activeTabId
20
+ ? routes.filter((r) => !r.tab || r.tab.toLowerCase() === activeTabId)
21
+ : routes
22
+
23
+ const directoryMeta: Record<string, any> = {}
24
+ if (config.directoryMeta) {
25
+ for (const [key, value] of Object.entries(config.directoryMeta)) {
26
+ const cleanKey = key
27
+ .split('/')
28
+ .filter((part) => !part.startsWith('(') || !part.endsWith(')'))
29
+ .map((part) => part.replace(/^\d+-/, ''))
30
+ .join('/')
31
+ directoryMeta[cleanKey === '' ? '.' : cleanKey] = value
42
32
  }
43
- groupsMap.get(route.group)!.routes.push(route)
44
33
  }
45
- }
46
-
47
- const groups = Array.from(groupsMap.values()).map((group) => {
48
- const subRouteParents = new Map<string, ComponentRoute>()
49
- const subRouteChildren = new Map<string, ComponentRoute[]>()
50
-
51
- // First pass: Categorize as parent or child
52
- for (const route of group.routes) {
53
- if (route.subRouteGroup) {
54
- const isParent =
55
- route.path.endsWith(`/${route.subRouteGroup}`) ||
56
- route.path.endsWith(`/${route.subRouteGroup}/`)
57
-
58
- if (isParent && !subRouteParents.has(route.subRouteGroup)) {
59
- subRouteParents.set(route.subRouteGroup, route)
60
- } else {
61
- if (!subRouteChildren.has(route.subRouteGroup)) {
62
- subRouteChildren.set(route.subRouteGroup, [])
34
+
35
+ // 1. Helper to format labels
36
+ const capitalize = (str: string) =>
37
+ str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, ' ')
38
+
39
+ // 2. Define recursive tree builder
40
+ const rootNodesMap = new Map<string, ComponentRoute>()
41
+ const ungrouped: ComponentRoute[] = []
42
+
43
+ // Helper to find or create nested folders recursively
44
+ const getOrCreateNode = (
45
+ parts: string[],
46
+ rootStore: Map<string, ComponentRoute>,
47
+ ) => {
48
+ let currentMap = rootStore
49
+ let parentPath = ''
50
+ let lastNode: ComponentRoute | null = null
51
+
52
+ for (let i = 0; i < parts.length; i++) {
53
+ const segment = parts[i]
54
+ const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment
55
+
56
+ if (!currentMap.has(segment)) {
57
+ const meta = directoryMeta[currentRelPath] || {}
58
+ const newNode: ComponentRoute = {
59
+ path: '#', // Placeholder
60
+ title: meta.title || capitalize(segment),
61
+ componentPath: '',
62
+ filePath: '',
63
+ icon: meta.icon,
64
+ groupPosition: typeof meta.order === 'number' ? meta.order : 999,
65
+ subRoutes: [],
63
66
  }
64
- subRouteChildren.get(route.subRouteGroup)!.push(route)
67
+ currentMap.set(segment, newNode)
65
68
  }
69
+
70
+ lastNode = currentMap.get(segment)!
71
+
72
+ // Create inner subRoutes mapping helper
73
+ if (!(lastNode as any)._subMap) {
74
+ ;(lastNode as any)._subMap = new Map<string, ComponentRoute>()
75
+ }
76
+ currentMap = (lastNode as any)._subMap as Map<string, ComponentRoute>
77
+ parentPath = currentRelPath
66
78
  }
79
+ return lastNode
67
80
  }
68
81
 
69
- const finalRoutes: ComponentRoute[] = []
70
- const seenSubGroups = new Set<string>()
71
-
72
- // Second pass: Assemble maintaining mostly original order
73
- for (const route of group.routes) {
74
- if (route.subRouteGroup) {
75
- if (!seenSubGroups.has(route.subRouteGroup)) {
76
- seenSubGroups.add(route.subRouteGroup)
77
- const parent = subRouteParents.get(route.subRouteGroup)
78
- const children = subRouteChildren.get(route.subRouteGroup) || []
79
-
80
- if (parent) {
81
- finalRoutes.push({ ...parent, subRoutes: children })
82
- } else {
83
- // Fallback
84
- finalRoutes.push(...children)
85
- }
82
+ // 3. Sort input routes initially by their internal position/ordering
83
+ const sortedRoutes = [...filteredRoutes].sort((a, b) => {
84
+ const posA = a.sidebarPosition ?? a.order ?? 999
85
+ const posB = b.sidebarPosition ?? b.order ?? 999
86
+ return posA - posB
87
+ })
88
+
89
+ // 4. Distribute routes into tree
90
+ for (const route of sortedRoutes) {
91
+ if (route.sidebarHidden) continue
92
+
93
+ const parts = route.slugParts || []
94
+ const fileName = route.filePath.split('/').pop() || ''
95
+ const isIndex = /^index\.mdx?$/.test(fileName)
96
+
97
+ if (parts.length === 0) {
98
+ // Top level route (not in subfolder)
99
+ if (route.filePath) ungrouped.push(route)
100
+ continue
101
+ }
102
+
103
+ if (isIndex) {
104
+ // Index files populate the CONTAINER object itself
105
+ const containerNode = getOrCreateNode(parts, rootNodesMap)
106
+ if (containerNode) {
107
+ // Merge properties onto the container so it becomes clickable
108
+ containerNode.path = route.path
109
+ containerNode.title = route.title || containerNode.title
110
+ containerNode.icon = route.icon || containerNode.icon
111
+ containerNode.badge = route.badge
112
+ containerNode.sidebarPosition = route.sidebarPosition
113
+ containerNode.frontmatter = route.frontmatter
86
114
  }
87
115
  } else {
88
- finalRoutes.push(route)
116
+ // Normal leaf file nested under path
117
+ const parentNode = getOrCreateNode(parts, rootNodesMap)
118
+ if (parentNode) {
119
+ parentNode.subRoutes!.push(route)
120
+ }
89
121
  }
90
122
  }
91
123
 
92
- return { ...group, routes: finalRoutes }
93
- })
124
+ // 5. Recursive sorting and cleanup helper
125
+ const finalizeTree = (
126
+ nodes: ComponentRoute[],
127
+ currentPathPrefix: string = '',
128
+ ): ComponentRoute[] => {
129
+ nodes.forEach((node) => {
130
+ // 1. Pull child nodes from internal Map store into subRoutes
131
+ if ((node as any)._subMap) {
132
+ const childDirs = Array.from(
133
+ ((node as any)._subMap as Map<string, ComponentRoute>).values(),
134
+ )
135
+ node.subRoutes = [...(node.subRoutes || []), ...childDirs]
136
+ delete (node as any)._subMap
137
+ }
138
+
139
+ // 2. Recursively process grandchildren
140
+ if (node.subRoutes && node.subRoutes.length > 0) {
141
+ node.subRoutes = finalizeTree(node.subRoutes)
142
+ }
143
+ })
144
+
145
+ // 3. Sort this specific depth layer
146
+ return nodes.sort((a, b) => {
147
+ // Position ordering
148
+ const posA = a.sidebarPosition ?? a.groupPosition ?? 999
149
+ const posB = b.sidebarPosition ?? b.groupPosition ?? 999
150
+
151
+ if (posA !== posB) return posA - posB
152
+
153
+ // Fallback alphabetical
154
+ return a.title.localeCompare(b.title)
155
+ })
156
+ }
157
+
158
+ // Finalize top-level groups
159
+ const rawGroups = Array.from(rootNodesMap.values())
160
+ const finalizedTopNodes = finalizeTree(rawGroups)
161
+
162
+ // Map finalized top nodes to expected format [{ title, routes }]
163
+ const groups = finalizedTopNodes.map((node) => {
164
+ // To match current primitives expectations, a 'group' is the top-level container.
165
+ // If the top node is already structured as a ComponentRoute, we wrap it.
166
+ return {
167
+ slug: node.title.toLowerCase().replace(/\s+/g, '-'),
168
+ title: node.title,
169
+ icon: node.icon,
170
+ routes: [node], // The primitives sidebar renderer iterates topGroup.routes and recurses inside.
171
+ }
172
+ })
173
+
174
+ // Wait, actually the legacy Sidebar.tsx treats "groups" as visual wrappers with headers,
175
+ // and "routes" inside as the start of items. If we want top-level direct items, they map to 'ungrouped'.
176
+ // Let's return grouped items natively.
94
177
 
95
- return {
96
- groups,
97
- ungrouped,
98
- activeRoute,
99
- activePath: currentPath,
100
- config,
101
- }
178
+ // RE-DESIGN LEGACY COMPATIBILITY:
179
+ // If user wants strict backwards layout, we unwrap the very top layer!
180
+ const legacyCompatibleGroups = finalizedTopNodes
181
+ .map((node) => {
182
+ // Check if it's a container with subitems. If it is, make IT the group header!
183
+ if (node.subRoutes && node.subRoutes.length > 0) {
184
+ return {
185
+ slug: node.title.toLowerCase().replace(/\s+/g, '-'),
186
+ title: node.title,
187
+ icon: node.icon,
188
+ routes: node.subRoutes, // Unwrap children as top-level list underneath the visual group title!
189
+ }
190
+ }
191
+ // If it's standalone, send it to ungrouped
192
+ ungrouped.push(node)
193
+ return null
194
+ })
195
+ .filter(Boolean) as any[]
196
+
197
+ return {
198
+ groups: legacyCompatibleGroups,
199
+ ungrouped: finalizeTree(ungrouped),
200
+ activeRoute,
201
+ activePath: currentPath,
202
+ config,
203
+ }
204
+ }, [routes, config, currentPath])
102
205
  }
@@ -1,6 +1,7 @@
1
1
  import { useLocation } from 'react-router-dom'
2
2
  import { useEffect, useState, useRef } from 'react'
3
3
  import type { ComponentRoute, BoltdocsTab } from '../types'
4
+ import { normalizePath } from '../utils/path'
4
5
 
5
6
  export function useTabs(
6
7
  tabs: BoltdocsTab[] = [],
@@ -14,11 +15,9 @@ export function useTabs(
14
15
  width: 0,
15
16
  })
16
17
 
17
- const normalize = (p: string) =>
18
- p.endsWith('/') && p.length > 1 ? p.slice(0, -1) : p
19
- const currentPath = normalize(location.pathname)
18
+ const currentPath = normalizePath(location.pathname)
20
19
 
21
- const activeRoute = routes.find((r) => normalize(r.path) === currentPath)
20
+ const activeRoute = routes.find((r) => normalizePath(r.path) === currentPath)
22
21
  const activeTabId = activeRoute?.tab?.toLowerCase()
23
22
  const activeIndex = tabs.findIndex(
24
23
  (tab) => tab.id.toLowerCase() === activeTabId,
@@ -1,6 +1,8 @@
1
+ import { useMemo } from 'react'
1
2
  import { useNavigate } from 'react-router-dom'
2
3
  import { getBaseFilePath } from '../utils/get-base-file-path'
3
4
  import { useRoutes } from './use-routes'
5
+ import { useConfig } from '../app/config-context'
4
6
  import { useBoltdocsContext } from '../store/boltdocs-context'
5
7
  import type { BoltdocsVersion } from '../../shared/types'
6
8
 
@@ -23,9 +25,8 @@ export interface UseVersionReturn {
23
25
  */
24
26
  export function useVersion(): UseVersionReturn {
25
27
  const navigate = useNavigate()
26
- const routeContext = useRoutes()
27
- const { allRoutes, currentRoute, currentVersion, currentLocale, config } =
28
- routeContext
28
+ const config = useConfig()
29
+ const { allRoutes, currentRoute, currentVersion, currentLocale } = useRoutes()
29
30
  const versions = config.versions
30
31
  const { setVersion } = useBoltdocsContext()
31
32
 
@@ -35,21 +36,12 @@ export function useVersion(): UseVersionReturn {
35
36
  // Update store
36
37
  setVersion(version)
37
38
 
38
- // If we are on the home page or a path that doesn't belong to the documentation,
39
- // we stay on the current page.
40
- const localePaths = config.i18n ? Object.keys(config.i18n.locales).map(l => `/${l}`) : []
41
- const isHome = !currentRoute ||
42
- currentRoute.path === '/' ||
43
- currentRoute.path === config.base ||
44
- currentRoute.path === '' ||
45
- localePaths.includes(currentRoute.path)
46
-
47
- if (isHome) {
48
- return
49
- }
39
+ // 3. Attempt derivation or deployment of navigation target
40
+ const base = config.base || '/docs'
41
+ const safeBase = base.replace(/\/$/, '')
42
+ let targetPath = `${safeBase}/${version}${currentLocale ? `/${currentLocale}` : ''}`
50
43
 
51
44
  if (currentRoute) {
52
- let targetPath = `/docs/${version}`
53
45
  const baseFile = getBaseFilePath(
54
46
  currentRoute.filePath,
55
47
  currentRoute.version,
@@ -60,7 +52,8 @@ export function useVersion(): UseVersionReturn {
60
52
  (r) =>
61
53
  getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
62
54
  (r.version || versions.defaultVersion) === version &&
63
- (currentLocale ? r.locale === currentLocale : !r.locale),
55
+ (!config.i18n ||
56
+ (r.locale || config.i18n.defaultLocale) === currentLocale),
64
57
  )
65
58
 
66
59
  if (targetRoute) {
@@ -70,26 +63,48 @@ export function useVersion(): UseVersionReturn {
70
63
  (r) =>
71
64
  getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
72
65
  (r.version || versions.defaultVersion) === version &&
73
- (currentLocale ? r.locale === currentLocale : !r.locale),
66
+ (!config.i18n ||
67
+ (r.locale || config.i18n.defaultLocale) === currentLocale),
74
68
  )
75
- targetPath = versionIndexRoute
76
- ? versionIndexRoute.path
77
- : `/docs/${version}${currentLocale ? `/${currentLocale}` : ''}`
69
+ if (versionIndexRoute) {
70
+ targetPath = versionIndexRoute.path
71
+ }
72
+ }
73
+ } else {
74
+ // Recovery mode: if currently on a 404, attempt to find ANY document in the target version
75
+ const fallbackRoute = allRoutes.find(
76
+ (r) =>
77
+ (r.version || versions.defaultVersion) === version &&
78
+ (!config.i18n ||
79
+ (r.locale || config.i18n.defaultLocale) === currentLocale),
80
+ )
81
+ if (fallbackRoute) {
82
+ targetPath = fallbackRoute.path
78
83
  }
79
-
80
- navigate(targetPath)
81
84
  }
85
+
86
+ navigate(targetPath)
82
87
  }
83
88
 
84
- const availableVersions = routeContext.availableVersions.map((v) => ({
85
- ...v,
86
- label: v.label as string,
87
- value: v.key,
88
- }))
89
+ const currentVersionConfig = versions?.versions?.find?.(
90
+ (v) => v.path === currentVersion,
91
+ )
92
+ const currentVersionLabel = currentVersionConfig?.label || currentVersion
93
+
94
+ const availableVersions = useMemo(() => {
95
+ return versions
96
+ ? versions.versions.map((v) => ({
97
+ key: v.path as BoltdocsVersion,
98
+ label: v.label,
99
+ value: v.path,
100
+ isCurrent: v.path === currentVersion,
101
+ }))
102
+ : []
103
+ }, [versions, currentVersion])
89
104
 
90
105
  return {
91
106
  currentVersion,
92
- currentVersionLabel: routeContext.currentVersionLabel,
107
+ currentVersionLabel,
93
108
  availableVersions,
94
109
  handleVersionChange,
95
110
  }
@@ -1,105 +1,31 @@
1
+ export type * from './types'
1
2
  export type {
2
- ComponentRoute,
3
- LayoutProps,
4
- } from './types'
5
- export type { BoltdocsLocale, BoltdocsVersion, BoltdocsTypes } from '../shared/types'
3
+ BoltdocsLocale,
4
+ BoltdocsVersion,
5
+ BoltdocsTypes,
6
+ } from '../shared/types'
6
7
  export * from './ssg'
7
8
  export { useConfig } from './app/config-context'
8
9
  export { useTheme } from './app/theme-context'
9
- export { useRoutes } from './hooks/use-routes'
10
10
  export { useMdxComponents } from './app/mdx-components-context'
11
-
12
- // Hooks
11
+ export { useUI } from './app/ui-context'
13
12
  export * from './hooks/index'
14
- // Composable layout building blocks
15
- export { DocsLayout } from './components/docs-layout'
16
-
17
- // Default UI components (for use in custom layout.tsx)
13
+ export { DocsLayout } from './components/docs-layout-default'
18
14
  export { Navbar } from './components/ui-base/navbar'
19
15
  export { Sidebar } from './components/ui-base/sidebar'
20
16
  export { OnThisPage } from './components/ui-base/on-this-page'
21
- export { Head } from './components/ui-base/head'
22
17
  export { Breadcrumbs } from './components/ui-base/breadcrumbs'
23
18
  export { PageNav } from './components/ui-base/page-nav'
24
19
  export { ErrorBoundary } from './components/ui-base/error-boundary'
25
20
  export { CopyMarkdown } from './components/ui-base/copy-markdown'
26
-
21
+ export { SearchDialog } from './components/ui-base/search-dialog'
27
22
  export { NotFound } from './components/ui-base/not-found'
28
- export { Loading } from './components/ui-base/loading'
29
- export { CodeBlock } from './components/mdx/code-block'
30
- export { Video } from './components/mdx/video'
31
-
32
- // MDX Components
33
- export {
34
- Button,
35
- Badge,
36
- Card,
37
- Cards,
38
- Tabs,
39
- Tab,
40
- Admonition,
41
- Note,
42
- Tip,
43
- Warning,
44
- Danger,
45
- InfoBox,
46
- ComponentProps,
47
- ComponentPreview,
48
- Important,
49
- Caution,
50
- List,
51
- FileTree,
52
- Table,
53
- Field,
54
- Link,
55
- Image,
56
- } from './components/mdx'
57
-
58
- export type {
59
- ButtonProps,
60
- BadgeProps,
61
- CardProps,
62
- CardsProps,
63
- TabsProps,
64
- TabProps,
65
- AdmonitionProps,
66
- ComponentPropsProps,
67
- ComponentPreviewProps,
68
- ListProps,
69
- FileTreeProps,
70
- TableProps,
71
- FieldProps,
72
- LinkProps,
73
- ImageProps,
74
- } from './components/mdx'
75
-
76
- // Primitives
77
- export { Navbar as PrimitiveNavbar } from './components/primitives/navbar'
78
- export { NavigationMenu as PrimitiveNavigationMenu } from './components/primitives/navigation-menu'
79
- export { SearchDialog as SearchDialogPrimitive } from './components/primitives/search-dialog'
80
- export {
81
- OnThisPage as PrimitiveOnThisPage,
82
- AnchorProvider,
83
- ScrollProvider,
84
- useActiveAnchor,
85
- useActiveAnchors,
86
- useItems,
87
- } from './components/primitives/on-this-page'
88
- export { PageNav as PrimitivePageNav } from './components/primitives/page-nav'
89
- export { Tabs as PrimitiveTabs } from './components/primitives/tabs'
90
- export { Sidebar as PrimitiveSidebar } from './components/primitives/sidebar'
91
- export { Breadcrumbs as PrimitiveBreadcrumbs } from './components/primitives/breadcrumbs'
92
- export { Button as PrimitiveButton } from './components/primitives/button'
93
- export { ButtonGroup } from './components/primitives/button-group'
94
- export { Menu as PrimitiveMenu } from './components/primitives/menu'
95
- export { Popover as PrimitivePopover } from './components/primitives/popover'
96
- export { Tooltip as PrimitiveTooltip } from './components/primitives/tooltip'
97
- export {
98
- Link as PrimitiveLink,
99
- NavLink as PrimitiveNavLink,
100
- } from './components/primitives/link'
101
- export { Skeleton as PrimitiveSkeleton } from './components/primitives/skeleton'
23
+ export { Card } from './components/mdx/card'
24
+ export { Cards } from './components/mdx/cards'
102
25
 
103
26
  // Utilities
104
27
  export { cn } from './utils/cn'
105
28
  export { getTranslated } from './utils/i18n'
29
+ export { reactToText } from './utils/react-to-text'
30
+ export { copyToClipboard } from './utils/copy-clipboard'
31
+ export { getStarsRepo } from './utils/github'
@@ -0,0 +1,2 @@
1
+ import { mdx_components_default as Mdx } from './components/mdx'
2
+ export default Mdx
@@ -0,0 +1,19 @@
1
+ export * from './components/primitives/docs-layout'
2
+ export * from './components/primitives/button-group'
3
+ export * from './components/primitives/tabs'
4
+ export * from './components/primitives/sidebar'
5
+ export * from './components/primitives/on-this-page'
6
+ export * from './components/primitives/code-block'
7
+ export * from './components/primitives/button'
8
+ export * from './components/primitives/popover'
9
+ export * from './components/primitives/tooltip'
10
+ export * from './components/primitives/link'
11
+ export * from './components/primitives/error-boundary'
12
+ export * from './components/primitives/heading'
13
+ export * from './components/primitives/image'
14
+ export * from './components/primitives/menu'
15
+ export * from './components/primitives/page-nav'
16
+ export * from './components/primitives/search-dialog'
17
+ export * from './components/primitives/skeleton'
18
+ export * from './components/primitives/breadcrumbs'
19
+ export * from './components/primitives/navbar'