boltdocs 2.7.10 → 2.7.11

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 (137) hide show
  1. package/dist/client/index.cjs +1929 -1
  2. package/dist/client/index.js +1880 -1
  3. package/dist/client/mdx.cjs +7 -1
  4. package/dist/client/mdx.js +7 -1
  5. package/dist/client/primitives.cjs +60 -1
  6. package/dist/client/primitives.js +20 -1
  7. package/dist/docs-layout-BXHV0xw_.cjs +1431 -0
  8. package/dist/docs-layout-DwFndmj5.js +1231 -0
  9. package/dist/icons-dev-3cZMyt8r.cjs +1204 -0
  10. package/dist/icons-dev-Df8OQ481.js +839 -0
  11. package/dist/image-DtrI2cw3.cjs +268 -0
  12. package/dist/image-jxPb-2iV.js +214 -0
  13. package/dist/mdx-BdWkJTeB.cjs +523 -0
  14. package/dist/mdx-UTTLFWJq.js +494 -0
  15. package/dist/node/cli-entry.cjs +1 -1
  16. package/dist/node/cli-entry.mjs +1 -1
  17. package/dist/node/index.cjs +1 -1
  18. package/dist/node/index.mjs +1 -1
  19. package/dist/{node-DtEDyN1u.cjs → node-BSM4qcDK.cjs} +1 -1
  20. package/dist/{node-_1jhMGYx.mjs → node-BspZN3R2.mjs} +1 -1
  21. package/dist/{package-DrwtlXfk.cjs → package-DIIrjuWI.cjs} +1 -1
  22. package/dist/{package--0Yf0t1N.mjs → package-K0zsjGIz.mjs} +1 -1
  23. package/dist/{search-dialog-ByvGScjt.js → search-dialog-BHuIiUC6.js} +3 -1
  24. package/dist/search-dialog-BNF10tDl.js +375 -0
  25. package/dist/search-dialog-BwkDuI9R.cjs +220 -0
  26. package/dist/search-dialog-C7xuvyNk.cjs +386 -0
  27. package/dist/search-dialog-CIQg6k8c.cjs +8 -0
  28. package/dist/search-dialog-D-DDN7zJ.js +208 -0
  29. package/package.json +3 -4
  30. package/dist/docs-layout-KoWNZc8_.js +0 -6
  31. package/dist/docs-layout-x2yKt2cL.cjs +0 -6
  32. package/dist/icons-dev-B_RZIyxu.js +0 -6
  33. package/dist/icons-dev-BlV3wWFT.cjs +0 -6
  34. package/dist/image-BHhTvQzr.cjs +0 -6
  35. package/dist/image-CqKzYD8f.js +0 -6
  36. package/dist/mdx-DudBEac0.js +0 -7
  37. package/dist/mdx-r4cDQxWu.cjs +0 -7
  38. package/dist/search-dialog-B584t9ZF.js +0 -6
  39. package/dist/search-dialog-BvBopRsZ.cjs +0 -6
  40. package/dist/search-dialog-Cyko6TJm.cjs +0 -6
  41. package/dist/search-dialog-D6BNohIJ.js +0 -6
  42. package/dist/search-dialog-DuYTIefy.cjs +0 -6
  43. package/src/client/app/config-context.tsx +0 -51
  44. package/src/client/app/doc-page.tsx +0 -38
  45. package/src/client/app/docs-layout.tsx +0 -28
  46. package/src/client/app/head.tsx +0 -122
  47. package/src/client/app/helmet-compat.tsx +0 -36
  48. package/src/client/app/mdx-component.tsx +0 -8
  49. package/src/client/app/mdx-components-context.tsx +0 -72
  50. package/src/client/app/routes-context.tsx +0 -34
  51. package/src/client/app/scroll-handler.tsx +0 -74
  52. package/src/client/app/theme-context.tsx +0 -103
  53. package/src/client/app/ui-context.tsx +0 -42
  54. package/src/client/components/docs-layout-default.tsx +0 -85
  55. package/src/client/components/icons-dev.tsx +0 -282
  56. package/src/client/components/mdx/callout.tsx +0 -97
  57. package/src/client/components/mdx/card.tsx +0 -99
  58. package/src/client/components/mdx/cards.tsx +0 -27
  59. package/src/client/components/mdx/code-block.tsx +0 -184
  60. package/src/client/components/mdx/field.tsx +0 -33
  61. package/src/client/components/mdx/image.tsx +0 -44
  62. package/src/client/components/mdx/index.ts +0 -19
  63. package/src/client/components/mdx/table.tsx +0 -54
  64. package/src/client/components/mdx/typographics.tsx +0 -120
  65. package/src/client/components/mdx/use-code-block.ts +0 -34
  66. package/src/client/components/primitives/breadcrumbs.tsx +0 -54
  67. package/src/client/components/primitives/button-group.tsx +0 -54
  68. package/src/client/components/primitives/button.tsx +0 -6
  69. package/src/client/components/primitives/code-block.tsx +0 -120
  70. package/src/client/components/primitives/docs-layout.tsx +0 -125
  71. package/src/client/components/primitives/error-boundary.tsx +0 -107
  72. package/src/client/components/primitives/heading.tsx +0 -128
  73. package/src/client/components/primitives/helpers/observer.ts +0 -141
  74. package/src/client/components/primitives/image.tsx +0 -26
  75. package/src/client/components/primitives/link.tsx +0 -102
  76. package/src/client/components/primitives/menu.tsx +0 -137
  77. package/src/client/components/primitives/navbar.tsx +0 -466
  78. package/src/client/components/primitives/on-this-page.tsx +0 -430
  79. package/src/client/components/primitives/page-nav.tsx +0 -51
  80. package/src/client/components/primitives/popover.tsx +0 -28
  81. package/src/client/components/primitives/search-dialog.tsx +0 -193
  82. package/src/client/components/primitives/sidebar.tsx +0 -423
  83. package/src/client/components/primitives/skeleton.tsx +0 -26
  84. package/src/client/components/primitives/tabs.tsx +0 -70
  85. package/src/client/components/primitives/tooltip.tsx +0 -81
  86. package/src/client/components/primitives/types.ts +0 -11
  87. package/src/client/components/ui-base/banner.tsx +0 -66
  88. package/src/client/components/ui-base/breadcrumbs.tsx +0 -44
  89. package/src/client/components/ui-base/copy-markdown.tsx +0 -107
  90. package/src/client/components/ui-base/error-boundary.tsx +0 -15
  91. package/src/client/components/ui-base/github-stars.tsx +0 -29
  92. package/src/client/components/ui-base/icons.tsx +0 -240
  93. package/src/client/components/ui-base/index.ts +0 -16
  94. package/src/client/components/ui-base/last-updated.tsx +0 -27
  95. package/src/client/components/ui-base/navbar.tsx +0 -266
  96. package/src/client/components/ui-base/not-found.tsx +0 -26
  97. package/src/client/components/ui-base/on-this-page.tsx +0 -57
  98. package/src/client/components/ui-base/page-nav.tsx +0 -50
  99. package/src/client/components/ui-base/search-dialog.tsx +0 -163
  100. package/src/client/components/ui-base/search-highlight.tsx +0 -10
  101. package/src/client/components/ui-base/sidebar.tsx +0 -92
  102. package/src/client/components/ui-base/tabs.tsx +0 -83
  103. package/src/client/components/ui-base/theme-toggle.tsx +0 -130
  104. package/src/client/components/ui-base/version-i18n.tsx +0 -80
  105. package/src/client/hooks/index.ts +0 -13
  106. package/src/client/hooks/use-analytics.ts +0 -272
  107. package/src/client/hooks/use-breadcrumbs.ts +0 -22
  108. package/src/client/hooks/use-i18n.ts +0 -182
  109. package/src/client/hooks/use-localized-to.ts +0 -113
  110. package/src/client/hooks/use-location.ts +0 -5
  111. package/src/client/hooks/use-navbar.ts +0 -130
  112. package/src/client/hooks/use-page-nav.ts +0 -46
  113. package/src/client/hooks/use-routes.ts +0 -108
  114. package/src/client/hooks/use-search-highlight.ts +0 -185
  115. package/src/client/hooks/use-search.ts +0 -118
  116. package/src/client/hooks/use-sidebar.ts +0 -205
  117. package/src/client/hooks/use-tabs.ts +0 -46
  118. package/src/client/hooks/use-version.ts +0 -111
  119. package/src/client/index.ts +0 -31
  120. package/src/client/mdx.ts +0 -2
  121. package/src/client/primitives.ts +0 -19
  122. package/src/client/ssg/boltdocs-shell.tsx +0 -148
  123. package/src/client/ssg/create-routes.tsx +0 -473
  124. package/src/client/ssg/index.ts +0 -4
  125. package/src/client/ssg/mdx-page.tsx +0 -38
  126. package/src/client/store/boltdocs-context.tsx +0 -137
  127. package/src/client/theme/neutral.css +0 -141
  128. package/src/client/theme/reset.css +0 -189
  129. package/src/client/types.ts +0 -116
  130. package/src/client/utils/cn.ts +0 -6
  131. package/src/client/utils/copy-clipboard.ts +0 -22
  132. package/src/client/utils/get-base-file-path.ts +0 -21
  133. package/src/client/utils/github.ts +0 -121
  134. package/src/client/utils/i18n.ts +0 -23
  135. package/src/client/utils/path.ts +0 -9
  136. package/src/client/utils/react-to-text.ts +0 -34
  137. package/src/client/virtual.d.ts +0 -24
@@ -1,185 +0,0 @@
1
- import { useEffect } from 'react'
2
- import { useLocation } from './use-location'
3
-
4
- /**
5
- * Hook to highlight search terms based on the 'hl' query parameter.
6
- */
7
- export function useSearchHighlight(
8
- containerSelector: string = '.boltdocs-page',
9
- ) {
10
- const { search } = useLocation()
11
- const query = new URLSearchParams(search).get('hl')
12
-
13
- useEffect(() => {
14
- if (!query) {
15
- clearHighlights(containerSelector)
16
- return
17
- }
18
-
19
- const container = document.querySelector(containerSelector)
20
- if (!container) return
21
-
22
- let rafId: number
23
-
24
- // Observe changes to the content (e.g. navigation or lazy loading)
25
- const observer = new MutationObserver((mutations) => {
26
- const hasExternalChanges = mutations.some((m) => {
27
- const addedNodes = Array.from(m.addedNodes)
28
- const removedNodes = Array.from(m.removedNodes)
29
-
30
- return (
31
- addedNodes.some(
32
- (n) =>
33
- !(
34
- n instanceof HTMLElement &&
35
- n.hasAttribute('data-search-highlight')
36
- ),
37
- ) ||
38
- removedNodes.some(
39
- (n) =>
40
- !(
41
- n instanceof HTMLElement &&
42
- n.hasAttribute('data-search-highlight')
43
- ),
44
- )
45
- )
46
- })
47
-
48
- if (hasExternalChanges) {
49
- run()
50
- }
51
- })
52
-
53
- // Function to run highlighting
54
- function run() {
55
- cancelAnimationFrame(rafId)
56
- rafId = requestAnimationFrame(() => {
57
- // Disconnect to avoid observing our own cleanup/highlight cycle
58
- observer.disconnect()
59
- clearHighlights(containerSelector)
60
-
61
- // Split query into individual words (minimum 2 chars)
62
- const terms = query!
63
- .split(/\s+/)
64
- .map((t) => t.trim())
65
- .filter((t) => t.length >= 2)
66
-
67
- if (terms.length > 0) {
68
- highlightTerms(container!, terms)
69
- }
70
-
71
- // Re-observe
72
- observer.observe(container!, { childList: true, subtree: true })
73
- })
74
- }
75
-
76
- // Initial run
77
- run()
78
-
79
- return () => {
80
- cancelAnimationFrame(rafId)
81
- observer.disconnect()
82
- clearHighlights(containerSelector)
83
- }
84
- }, [query, search, containerSelector])
85
- }
86
-
87
- function clearHighlights(selector: string) {
88
- const marks = document.querySelectorAll(
89
- `${selector} mark[data-search-highlight]`,
90
- )
91
- marks.forEach((mark) => {
92
- try {
93
- const parent = mark.parentNode
94
- if (parent && parent.contains(mark)) {
95
- const text = mark.textContent || ''
96
- parent.replaceChild(document.createTextNode(text), mark)
97
- }
98
- } catch (e) {
99
- // Ignore DOM errors during cleanup
100
- }
101
- })
102
- }
103
-
104
- function highlightTerms(container: Element, terms: string[]) {
105
- const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
106
- acceptNode: (node) => {
107
- const parent = node.parentElement
108
- if (
109
- parent &&
110
- (parent.tagName === 'SCRIPT' ||
111
- parent.tagName === 'STYLE' ||
112
- parent.tagName === 'MARK' ||
113
- parent.closest('pre') ||
114
- parent.closest('code'))
115
- ) {
116
- return NodeFilter.FILTER_REJECT
117
- }
118
- return NodeFilter.FILTER_ACCEPT
119
- },
120
- })
121
-
122
- const nodes: Text[] = []
123
- let node: Node | null
124
- while ((node = walker.nextNode())) {
125
- nodes.push(node as Text)
126
- }
127
-
128
- // Create a combined regex for all terms
129
- // Accent-insensitive helper: replaces 'a' with '[aáàä...]'
130
- const accentMap: Record<string, string> = {
131
- a: '[aáàäâã]',
132
- e: '[eéèëê]',
133
- i: '[iíìïî]',
134
- o: '[oóòöôõ]',
135
- u: '[uúùüû]',
136
- n: '[nñ]',
137
- c: '[cç]',
138
- }
139
-
140
- const prepareRegex = (term: string) => {
141
- let pattern = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
142
- // Make it accent insensitive
143
- pattern = pattern
144
- .split('')
145
- .map((char) => {
146
- const lower = char.toLowerCase()
147
- return accentMap[lower] || char
148
- })
149
- .join('')
150
- return pattern
151
- }
152
-
153
- const combinedPattern = terms.map(prepareRegex).join('|')
154
- const regex = new RegExp(`(${combinedPattern})`, 'gi')
155
-
156
- const matchRegexes = terms.map((term) => {
157
- const p = prepareRegex(term)
158
- return new RegExp(`^${p}$`, 'i')
159
- })
160
-
161
- nodes.forEach((textNode) => {
162
- const text = textNode.textContent
163
- if (text && regex.test(text)) {
164
- const fragment = document.createDocumentFragment()
165
- const parts = text.split(regex)
166
-
167
- parts.forEach((part) => {
168
- const isMatch = matchRegexes.some((rx) => rx.test(part))
169
-
170
- if (isMatch) {
171
- const mark = document.createElement('mark')
172
- mark.textContent = part
173
- mark.setAttribute('data-search-highlight', 'true')
174
- fragment.appendChild(mark)
175
- } else if (part) {
176
- fragment.appendChild(document.createTextNode(part))
177
- }
178
- })
179
-
180
- if (textNode.parentNode) {
181
- textNode.parentNode.replaceChild(fragment, textNode)
182
- }
183
- }
184
- })
185
- }
@@ -1,118 +0,0 @@
1
- import { useState, useMemo, useEffect } from 'react'
2
- import { Index } from 'flexsearch'
3
- import { useRoutes } from './use-routes'
4
- import type { ComponentRoute } from '../types'
5
- // @ts-expect-error
6
- import searchData from 'virtual:boltdocs-search'
7
-
8
- interface SearchDataItem {
9
- id: string
10
- title: string
11
- content: string
12
- url: string
13
- display: string
14
- locale?: string
15
- version?: string
16
- }
17
-
18
- export function useSearch(routes: ComponentRoute[]) {
19
- const { currentLocale, currentVersion } = useRoutes()
20
- const [isOpen, setIsOpen] = useState(false)
21
- const [query, setQuery] = useState('')
22
- const [index, setIndex] = useState<Index | null>(null)
23
-
24
- // Initialize FlexSearch index once
25
- useEffect(() => {
26
- if (!isOpen || index) return
27
-
28
- const newIndex = new Index({
29
- preset: 'match',
30
- tokenize: 'full',
31
- resolution: 9,
32
- cache: true,
33
- })
34
-
35
- // Index all documents
36
- for (const doc of searchData as SearchDataItem[]) {
37
- newIndex.add(doc.id, `${doc.title} ${doc.content}`)
38
- }
39
-
40
- setIndex(newIndex)
41
- }, [isOpen, index])
42
-
43
- // Pre-index searchData for O(1) lookups
44
- const searchDataMap = useMemo(() => {
45
- const map = new Map<string, SearchDataItem>()
46
- for (const doc of searchData as SearchDataItem[]) {
47
- map.set(doc.id, doc)
48
- }
49
- return map
50
- }, [])
51
-
52
- const list = useMemo(() => {
53
- if (!query) {
54
- // Default results: just active routes
55
- return routes
56
- .filter((r) => {
57
- const localeMatch = !currentLocale || r.locale === currentLocale
58
- const versionMatch = !currentVersion || r.version === currentVersion
59
- return localeMatch && versionMatch
60
- })
61
- .slice(0, 10)
62
- .map((r) => ({
63
- id: r.path,
64
- title: r.title,
65
- path: r.path,
66
- bio: r.description || '',
67
- groupTitle: r.groupTitle,
68
- }))
69
- }
70
-
71
- if (!index) return []
72
-
73
- const searchResults = index.search(query, {
74
- limit: 20,
75
- suggest: true,
76
- })
77
-
78
- const results: any[] = []
79
- const seen = new Set<string>()
80
-
81
- for (const id of searchResults) {
82
- const doc = searchDataMap.get(id as string)
83
- if (!doc) continue
84
-
85
- // Filter by locale and version
86
- const localeMatch = !currentLocale || doc.locale === currentLocale
87
- const versionMatch = !currentVersion || doc.version === currentVersion
88
- if (!localeMatch || !versionMatch) continue
89
-
90
- if (seen.has(doc.url)) continue
91
- seen.add(doc.url)
92
-
93
- results.push({
94
- id: doc.url,
95
- title: doc.title,
96
- path: doc.url,
97
- bio: doc.display,
98
- groupTitle: doc.display.split(' > ')[0],
99
- isHeading: doc.url.includes('#'),
100
- })
101
- }
102
-
103
- return results.slice(0, 10)
104
- }, [query, index, currentLocale, currentVersion, routes, searchDataMap])
105
-
106
- return {
107
- isOpen,
108
- setIsOpen,
109
- query,
110
- setQuery,
111
- list,
112
- input: {
113
- value: query,
114
- onChange: (e: React.ChangeEvent<HTMLInputElement>) =>
115
- setQuery(e.target.value),
116
- },
117
- }
118
- }
@@ -1,205 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { useLocation } from 'react-router-dom'
3
- import { useConfig } from '../app/config-context'
4
- import type { ComponentRoute } from '../types'
5
- import { normalizePath } from '../utils/path'
6
-
7
- export function useSidebar(routes: ComponentRoute[]) {
8
- const config = useConfig()
9
- const location = useLocation()
10
-
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
32
- }
33
- }
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: [],
66
- }
67
- currentMap.set(segment, newNode)
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
78
- }
79
- return lastNode
80
- }
81
-
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
114
- }
115
- } else {
116
- // Normal leaf file nested under path
117
- const parentNode = getOrCreateNode(parts, rootNodesMap)
118
- if (parentNode) {
119
- parentNode.subRoutes!.push(route)
120
- }
121
- }
122
- }
123
-
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.
177
-
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])
205
- }
@@ -1,46 +0,0 @@
1
- import { useLocation } from 'react-router-dom'
2
- import { useEffect, useState, useRef } from 'react'
3
- import type { ComponentRoute, BoltdocsTab } from '../types'
4
- import { normalizePath } from '../utils/path'
5
-
6
- export function useTabs(
7
- tabs: BoltdocsTab[] = [],
8
- routes: ComponentRoute[] = [],
9
- ) {
10
- const location = useLocation()
11
- const tabRefs = useRef<(HTMLAnchorElement | null)[]>([])
12
- const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
13
- opacity: 0,
14
- transform: 'translateX(0) scaleX(0)',
15
- width: 0,
16
- })
17
-
18
- const currentPath = normalizePath(location.pathname)
19
-
20
- const activeRoute = routes.find((r) => normalizePath(r.path) === currentPath)
21
- const activeTabId = activeRoute?.tab?.toLowerCase()
22
- const activeIndex = tabs.findIndex(
23
- (tab) => tab.id.toLowerCase() === activeTabId,
24
- )
25
- const finalActiveIndex = activeIndex === -1 ? 0 : activeIndex
26
-
27
- // biome-ignore lint/correctness/useExhaustiveDependencies: Updated pointer to the tab
28
- useEffect(() => {
29
- const activeTab = tabRefs.current[finalActiveIndex]
30
- if (activeTab) {
31
- setIndicatorStyle({
32
- opacity: 1,
33
- width: activeTab.offsetWidth,
34
- transform: `translateX(${activeTab.offsetLeft}px)`,
35
- })
36
- }
37
- }, [finalActiveIndex, tabs.length, location.pathname])
38
-
39
- return {
40
- tabs,
41
- activeIndex: finalActiveIndex,
42
- indicatorStyle,
43
- tabRefs,
44
- activeTabId,
45
- }
46
- }
@@ -1,111 +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 { BoltdocsVersion } from '../../shared/types'
8
-
9
- export interface VersionOption {
10
- key: BoltdocsVersion
11
- label: string
12
- value: string
13
- isCurrent: boolean
14
- }
15
-
16
- export interface UseVersionReturn {
17
- currentVersion: BoltdocsVersion | undefined
18
- currentVersionLabel: string | undefined
19
- availableVersions: VersionOption[]
20
- handleVersionChange: (version: BoltdocsVersion) => void
21
- }
22
-
23
- /**
24
- * Hook to manage and switch between different versions of the documentation.
25
- */
26
- export function useVersion(): UseVersionReturn {
27
- const navigate = useNavigate()
28
- const config = useConfig()
29
- const { allRoutes, currentRoute, currentVersion, currentLocale } = useRoutes()
30
- const versions = config.versions
31
- const { setVersion } = useBoltdocsContext()
32
-
33
- const handleVersionChange = (version: string) => {
34
- if (!versions || version === currentVersion) return
35
-
36
- // Update store
37
- setVersion(version)
38
-
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}` : ''}`
43
-
44
- if (currentRoute) {
45
- const baseFile = getBaseFilePath(
46
- currentRoute.filePath,
47
- currentRoute.version,
48
- currentRoute.locale,
49
- )
50
-
51
- const targetRoute = allRoutes.find(
52
- (r) =>
53
- getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
54
- (r.version || versions.defaultVersion) === version &&
55
- (!config.i18n ||
56
- (r.locale || config.i18n.defaultLocale) === currentLocale),
57
- )
58
-
59
- if (targetRoute) {
60
- targetPath = targetRoute.path
61
- } else {
62
- const versionIndexRoute = allRoutes.find(
63
- (r) =>
64
- getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
65
- (r.version || versions.defaultVersion) === version &&
66
- (!config.i18n ||
67
- (r.locale || config.i18n.defaultLocale) === currentLocale),
68
- )
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
83
- }
84
- }
85
-
86
- navigate(targetPath)
87
- }
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])
104
-
105
- return {
106
- currentVersion,
107
- currentVersionLabel,
108
- availableVersions,
109
- handleVersionChange,
110
- }
111
- }
@@ -1,31 +0,0 @@
1
- export type * from './types'
2
- export type {
3
- BoltdocsLocale,
4
- BoltdocsVersion,
5
- BoltdocsTypes,
6
- } from '../shared/types'
7
- export * from './ssg'
8
- export { useConfig } from './app/config-context'
9
- export { useTheme } from './app/theme-context'
10
- export { useMdxComponents } from './app/mdx-components-context'
11
- export { useUI } from './app/ui-context'
12
- export * from './hooks/index'
13
- export { DocsLayout } from './components/docs-layout-default'
14
- export { Navbar } from './components/ui-base/navbar'
15
- export { Sidebar } from './components/ui-base/sidebar'
16
- export { OnThisPage } from './components/ui-base/on-this-page'
17
- export { Breadcrumbs } from './components/ui-base/breadcrumbs'
18
- export { PageNav } from './components/ui-base/page-nav'
19
- export { ErrorBoundary } from './components/ui-base/error-boundary'
20
- export { CopyMarkdown } from './components/ui-base/copy-markdown'
21
- export { SearchDialog } from './components/ui-base/search-dialog'
22
- export { NotFound } from './components/ui-base/not-found'
23
- export { Card } from './components/mdx/card'
24
- export { Cards } from './components/mdx/cards'
25
-
26
- // Utilities
27
- export { cn } from './utils/cn'
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'
package/src/client/mdx.ts DELETED
@@ -1,2 +0,0 @@
1
- import { mdx_components_default as Mdx } from './components/mdx'
2
- export default Mdx