boltdocs 1.10.2 → 2.0.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 (250) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/dist/cache-7G6D532T.mjs +1 -0
  4. package/dist/chunk-A4HQPEPU.mjs +1 -0
  5. package/dist/chunk-BA5NH5HU.mjs +1 -0
  6. package/dist/chunk-BQCD3DWG.mjs +1 -0
  7. package/dist/chunk-H63UMKYF.mjs +1 -0
  8. package/dist/chunk-IWHRQHS7.mjs +1 -0
  9. package/dist/chunk-JZXLCA2E.mjs +1 -0
  10. package/dist/chunk-MFU7Q6WF.mjs +1 -0
  11. package/dist/chunk-QYPNX5UN.mjs +1 -0
  12. package/dist/chunk-XEAPSFMB.mjs +1 -0
  13. package/dist/client/components/mdx/index.d.mts +209 -0
  14. package/dist/client/components/mdx/index.d.ts +209 -0
  15. package/dist/client/components/mdx/index.js +1 -0
  16. package/dist/client/components/mdx/index.mjs +1 -0
  17. package/dist/client/hooks/index.d.mts +133 -0
  18. package/dist/client/hooks/index.d.ts +133 -0
  19. package/dist/client/hooks/index.js +1 -0
  20. package/dist/client/hooks/index.mjs +1 -0
  21. package/dist/client/index.d.mts +138 -298
  22. package/dist/client/index.d.ts +138 -298
  23. package/dist/client/index.js +1 -3630
  24. package/dist/client/index.mjs +1 -697
  25. package/dist/client/ssr.d.mts +7 -3
  26. package/dist/client/ssr.d.ts +7 -3
  27. package/dist/client/ssr.js +1 -2928
  28. package/dist/client/ssr.mjs +1 -33
  29. package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
  30. package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
  31. package/dist/node/index.d.mts +2 -4
  32. package/dist/node/index.d.ts +2 -4
  33. package/dist/node/index.js +31 -1161
  34. package/dist/node/index.mjs +31 -736
  35. package/dist/search-dialog-EB3N4TYM.mjs +1 -0
  36. package/dist/types-BuZWFT7r.d.ts +159 -0
  37. package/dist/types-CvT-SGbK.d.mts +159 -0
  38. package/dist/use-routes-5bAtAAYX.d.mts +30 -0
  39. package/dist/use-routes-BefRXY3v.d.ts +30 -0
  40. package/package.json +34 -12
  41. package/src/client/app/config-context.tsx +18 -0
  42. package/src/client/app/docs-layout.tsx +14 -0
  43. package/src/client/app/index.tsx +137 -262
  44. package/src/client/app/mdx-component.tsx +52 -0
  45. package/src/client/app/mdx-components-context.tsx +23 -0
  46. package/src/client/app/mdx-page.tsx +20 -0
  47. package/src/client/app/preload.tsx +38 -30
  48. package/src/client/app/router.tsx +30 -0
  49. package/src/client/app/scroll-handler.tsx +40 -0
  50. package/src/client/app/theme-context.tsx +75 -0
  51. package/src/client/components/default-layout.tsx +80 -0
  52. package/src/client/components/docs-layout.tsx +105 -0
  53. package/src/client/components/icons-dev.tsx +74 -0
  54. package/src/client/components/mdx/admonition.tsx +107 -0
  55. package/src/client/components/mdx/badge.tsx +41 -0
  56. package/src/client/components/mdx/button.tsx +35 -0
  57. package/src/client/components/mdx/card.tsx +124 -0
  58. package/src/client/components/mdx/code-block.tsx +119 -0
  59. package/src/client/components/mdx/component-preview.tsx +47 -0
  60. package/src/client/components/mdx/component-props.tsx +83 -0
  61. package/src/client/components/mdx/field.tsx +66 -0
  62. package/src/client/components/mdx/file-tree.tsx +287 -0
  63. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  64. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  65. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  66. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  67. package/src/client/components/mdx/image.tsx +23 -0
  68. package/src/client/components/mdx/index.ts +53 -0
  69. package/src/client/components/mdx/link.tsx +38 -0
  70. package/src/client/components/mdx/list.tsx +192 -0
  71. package/src/client/components/mdx/table.tsx +156 -0
  72. package/src/client/components/mdx/tabs.tsx +135 -0
  73. package/src/client/components/mdx/video.tsx +68 -0
  74. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  75. package/src/client/components/primitives/button-group.tsx +54 -0
  76. package/src/client/components/primitives/button.tsx +145 -0
  77. package/src/client/components/primitives/helpers/observer.ts +120 -0
  78. package/src/client/components/primitives/index.ts +17 -0
  79. package/src/client/components/primitives/link.tsx +122 -0
  80. package/src/client/components/primitives/menu.tsx +159 -0
  81. package/src/client/components/primitives/navbar.tsx +359 -0
  82. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  83. package/src/client/components/primitives/on-this-page.tsx +461 -0
  84. package/src/client/components/primitives/page-nav.tsx +87 -0
  85. package/src/client/components/primitives/popover.tsx +47 -0
  86. package/src/client/components/primitives/search-dialog.tsx +183 -0
  87. package/src/client/components/primitives/sidebar.tsx +154 -0
  88. package/src/client/components/primitives/tabs.tsx +90 -0
  89. package/src/client/components/primitives/tooltip.tsx +83 -0
  90. package/src/client/components/primitives/types.ts +11 -0
  91. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  92. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  93. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  94. package/src/client/components/ui-base/github-stars.tsx +27 -0
  95. package/src/client/components/ui-base/head.tsx +69 -0
  96. package/src/client/components/ui-base/loading.tsx +87 -0
  97. package/src/client/components/ui-base/navbar.tsx +138 -0
  98. package/src/client/components/ui-base/not-found.tsx +24 -0
  99. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  100. package/src/client/components/ui-base/page-nav.tsx +39 -0
  101. package/src/client/components/ui-base/powered-by.tsx +19 -0
  102. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  103. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  104. package/src/client/components/ui-base/sidebar.tsx +104 -0
  105. package/src/client/components/ui-base/tabs.tsx +65 -0
  106. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  107. package/src/client/hooks/index.ts +12 -0
  108. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  109. package/src/client/hooks/use-i18n.ts +84 -0
  110. package/src/client/hooks/use-localized-to.ts +95 -0
  111. package/src/client/hooks/use-location.ts +5 -0
  112. package/src/client/hooks/use-navbar.ts +60 -0
  113. package/src/client/hooks/use-onthispage.ts +23 -0
  114. package/src/client/hooks/use-page-nav.ts +22 -0
  115. package/src/client/hooks/use-routes.ts +72 -0
  116. package/src/client/hooks/use-search.ts +71 -0
  117. package/src/client/hooks/use-sidebar.ts +49 -0
  118. package/src/client/hooks/use-tabs.ts +43 -0
  119. package/src/client/hooks/use-version.ts +78 -0
  120. package/src/client/index.ts +55 -17
  121. package/src/client/integrations/codesandbox.ts +179 -0
  122. package/src/client/ssr.tsx +27 -16
  123. package/src/client/theme/neutral.css +360 -0
  124. package/src/client/types.ts +131 -27
  125. package/src/client/utils/cn.ts +6 -0
  126. package/src/client/utils/copy-clipboard.ts +22 -0
  127. package/src/client/utils/get-base-file-path.ts +21 -0
  128. package/src/client/utils/github.ts +121 -0
  129. package/src/client/utils/use-on-change.ts +15 -0
  130. package/src/client/virtual.d.ts +24 -0
  131. package/src/node/cache.ts +156 -156
  132. package/src/node/config.ts +159 -103
  133. package/src/node/index.ts +13 -13
  134. package/src/node/mdx.ts +213 -61
  135. package/src/node/plugin/entry.ts +29 -18
  136. package/src/node/plugin/html.ts +11 -11
  137. package/src/node/plugin/index.ts +161 -84
  138. package/src/node/plugin/types.ts +2 -4
  139. package/src/node/routes/cache.ts +6 -6
  140. package/src/node/routes/index.ts +206 -113
  141. package/src/node/routes/parser.ts +102 -82
  142. package/src/node/routes/sorter.ts +15 -15
  143. package/src/node/routes/types.ts +24 -24
  144. package/src/node/ssg/index.ts +73 -47
  145. package/src/node/ssg/meta.ts +4 -4
  146. package/src/node/ssg/options.ts +5 -5
  147. package/src/node/ssg/sitemap.ts +14 -14
  148. package/src/node/utils.ts +54 -31
  149. package/tsconfig.json +25 -20
  150. package/tsup.config.ts +23 -14
  151. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  152. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  153. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  154. package/dist/Video-KNTY5BNO.mjs +0 -6
  155. package/dist/cache-KNL5B4EE.mjs +0 -12
  156. package/dist/chunk-7SFUJWTB.mjs +0 -211
  157. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  158. package/dist/chunk-FMTOYQLO.mjs +0 -37
  159. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  160. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  161. package/dist/client/index.css +0 -2847
  162. package/dist/client/ssr.css +0 -2847
  163. package/dist/types-Dj-bfnC3.d.mts +0 -74
  164. package/dist/types-Dj-bfnC3.d.ts +0 -74
  165. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  166. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  167. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  168. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  169. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  170. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  171. package/src/client/theme/components/Playground/index.ts +0 -1
  172. package/src/client/theme/components/Playground/playground.css +0 -238
  173. package/src/client/theme/components/Video/Video.tsx +0 -84
  174. package/src/client/theme/components/Video/index.ts +0 -1
  175. package/src/client/theme/components/Video/video.css +0 -41
  176. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  177. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  178. package/src/client/theme/components/mdx/Button.tsx +0 -50
  179. package/src/client/theme/components/mdx/Card.tsx +0 -80
  180. package/src/client/theme/components/mdx/Field.tsx +0 -60
  181. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  182. package/src/client/theme/components/mdx/List.tsx +0 -57
  183. package/src/client/theme/components/mdx/Table.tsx +0 -151
  184. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  185. package/src/client/theme/components/mdx/index.ts +0 -27
  186. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  187. package/src/client/theme/icons/bun.tsx +0 -62
  188. package/src/client/theme/icons/deno.tsx +0 -20
  189. package/src/client/theme/icons/discord.tsx +0 -12
  190. package/src/client/theme/icons/github.tsx +0 -15
  191. package/src/client/theme/icons/npm.tsx +0 -13
  192. package/src/client/theme/icons/pnpm.tsx +0 -72
  193. package/src/client/theme/icons/twitter.tsx +0 -12
  194. package/src/client/theme/styles/markdown.css +0 -394
  195. package/src/client/theme/styles/variables.css +0 -175
  196. package/src/client/theme/styles.css +0 -39
  197. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  198. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  199. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  200. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  201. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  202. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  203. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  204. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  205. package/src/client/theme/ui/Footer/footer.css +0 -32
  206. package/src/client/theme/ui/Head/Head.tsx +0 -69
  207. package/src/client/theme/ui/Head/index.ts +0 -1
  208. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  209. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  210. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  211. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  212. package/src/client/theme/ui/Layout/base.css +0 -106
  213. package/src/client/theme/ui/Layout/index.ts +0 -2
  214. package/src/client/theme/ui/Layout/pagination.css +0 -72
  215. package/src/client/theme/ui/Layout/responsive.css +0 -47
  216. package/src/client/theme/ui/Link/Link.tsx +0 -392
  217. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  218. package/src/client/theme/ui/Link/index.ts +0 -2
  219. package/src/client/theme/ui/Link/link-preview.css +0 -48
  220. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  221. package/src/client/theme/ui/Loading/index.ts +0 -1
  222. package/src/client/theme/ui/Loading/loading.css +0 -30
  223. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  224. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  225. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  226. package/src/client/theme/ui/Navbar/index.ts +0 -2
  227. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  228. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  229. package/src/client/theme/ui/NotFound/index.ts +0 -1
  230. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  231. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  232. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  233. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  234. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  235. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  236. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  237. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  238. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  239. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  240. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  241. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  242. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  243. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  244. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  245. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  246. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  247. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  248. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  249. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  250. package/src/client/utils.ts +0 -49
@@ -0,0 +1,71 @@
1
+ import { useState, useMemo } from 'react'
2
+ import type { ComponentRoute } from '@client/types'
3
+
4
+ export function useSearch(routes: ComponentRoute[]) {
5
+ const [isOpen, setIsOpen] = useState(false)
6
+ const [query, setQuery] = useState('')
7
+
8
+ const list = useMemo(() => {
9
+ if (!query) {
10
+ return routes.slice(0, 10).map((r) => ({
11
+ id: r.path,
12
+ title: r.title,
13
+ path: r.path,
14
+ bio: r.description || '',
15
+ groupTitle: r.groupTitle,
16
+ }))
17
+ }
18
+
19
+ const results: any[] = []
20
+ const lowerQuery = query.toLowerCase()
21
+
22
+ for (const route of routes) {
23
+ if (route.title?.toLowerCase().includes(lowerQuery)) {
24
+ results.push({
25
+ id: route.path,
26
+ title: route.title,
27
+ path: route.path,
28
+ bio: route.description || '',
29
+ groupTitle: route.groupTitle,
30
+ })
31
+ }
32
+
33
+ if (route.headings) {
34
+ for (const heading of route.headings) {
35
+ if (heading.text.toLowerCase().includes(lowerQuery)) {
36
+ results.push({
37
+ id: `${route.path}#${heading.id}`,
38
+ title: heading.text,
39
+ path: `${route.path}#${heading.id}`,
40
+ bio: `Heading in ${route.title}`,
41
+ groupTitle: route.title,
42
+ isHeading: true,
43
+ })
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ // Deduplicate by path
50
+ const seen = new Set()
51
+ return results
52
+ .filter((r) => {
53
+ if (seen.has(r.path)) return false
54
+ seen.add(r.path)
55
+ return true
56
+ })
57
+ .slice(0, 10)
58
+ }, [routes, query])
59
+
60
+ return {
61
+ isOpen,
62
+ setIsOpen,
63
+ query,
64
+ setQuery,
65
+ list,
66
+ input: {
67
+ value: query,
68
+ onChange: (e: any) => setQuery(e.target.value),
69
+ },
70
+ }
71
+ }
@@ -0,0 +1,49 @@
1
+ import { useLocation } from 'react-router-dom'
2
+ import { useConfig } from '@client/app/config-context'
3
+ import type { ComponentRoute } from '../types'
4
+
5
+ export function useSidebar(routes: ComponentRoute[]) {
6
+ const config = useConfig()
7
+ const location = useLocation()
8
+
9
+ // Find active route and tab
10
+ const activeRoute = routes.find((r) => r.path === location.pathname)
11
+ const activeTabId = activeRoute?.tab?.toLowerCase()
12
+
13
+ // Filter routes by active tab if any
14
+ const filteredRoutes = activeTabId
15
+ ? routes.filter((r) => !r.tab || r.tab.toLowerCase() === activeTabId)
16
+ : routes
17
+
18
+ const ungrouped: ComponentRoute[] = []
19
+ const groupsMap = new Map<
20
+ string,
21
+ { slug: string; title: string; routes: ComponentRoute[]; icon?: string }
22
+ >()
23
+
24
+ for (const route of filteredRoutes) {
25
+ if (!route.group) {
26
+ ungrouped.push(route)
27
+ } else {
28
+ if (!groupsMap.has(route.group)) {
29
+ groupsMap.set(route.group, {
30
+ slug: route.group,
31
+ title: route.groupTitle || route.group,
32
+ routes: [],
33
+ icon: route.groupIcon,
34
+ })
35
+ }
36
+ groupsMap.get(route.group)!.routes.push(route)
37
+ }
38
+ }
39
+
40
+ const groups = Array.from(groupsMap.values())
41
+
42
+ return {
43
+ groups,
44
+ ungrouped,
45
+ activeRoute,
46
+ activePath: location.pathname,
47
+ config,
48
+ }
49
+ }
@@ -0,0 +1,43 @@
1
+ import { useLocation } from 'react-router-dom'
2
+ import { useEffect, useState, useRef } from 'react'
3
+ import type { ComponentRoute, BoltdocsTab } from '@client/types'
4
+
5
+ export function useTabs(
6
+ tabs: BoltdocsTab[] = [],
7
+ routes: ComponentRoute[] = [],
8
+ ) {
9
+ const location = useLocation()
10
+ const tabRefs = useRef<(HTMLAnchorElement | null)[]>([])
11
+ const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
12
+ opacity: 0,
13
+ transform: 'translateX(0) scaleX(0)',
14
+ width: 0,
15
+ })
16
+
17
+ const activeRoute = routes.find((r) => r.path === location.pathname)
18
+ const activeTabId = activeRoute?.tab?.toLowerCase()
19
+ const activeIndex = tabs.findIndex(
20
+ (tab) => tab.id.toLowerCase() === activeTabId,
21
+ )
22
+ const finalActiveIndex = activeIndex === -1 ? 0 : activeIndex
23
+
24
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Updated pointer to the tab
25
+ useEffect(() => {
26
+ const activeTab = tabRefs.current[finalActiveIndex]
27
+ if (activeTab) {
28
+ setIndicatorStyle({
29
+ opacity: 1,
30
+ width: activeTab.offsetWidth,
31
+ transform: `translateX(${activeTab.offsetLeft}px)`,
32
+ })
33
+ }
34
+ }, [finalActiveIndex, tabs.length, location.pathname])
35
+
36
+ return {
37
+ tabs,
38
+ activeIndex: finalActiveIndex,
39
+ indicatorStyle,
40
+ tabRefs,
41
+ activeTabId,
42
+ }
43
+ }
@@ -0,0 +1,78 @@
1
+ import { useNavigate } from 'react-router-dom'
2
+ import { getBaseFilePath } from '@client/utils/get-base-file-path'
3
+ import { useRoutes } from './use-routes'
4
+
5
+ export interface VersionOption {
6
+ key: string
7
+ label: string
8
+ value: string
9
+ isCurrent: boolean
10
+ }
11
+
12
+ export interface UseVersionReturn {
13
+ currentVersion: string | undefined
14
+ currentVersionLabel: string | undefined
15
+ availableVersions: VersionOption[]
16
+ handleVersionChange: (version: string) => void
17
+ }
18
+
19
+ /**
20
+ * Hook to manage and switch between different versions of the documentation.
21
+ */
22
+ export function useVersion(): UseVersionReturn {
23
+ const navigate = useNavigate()
24
+ const routeContext = useRoutes()
25
+ const { allRoutes, currentRoute, currentVersion, currentLocale, config } =
26
+ routeContext
27
+ const versions = config.versions
28
+
29
+ const handleVersionChange = (version: string) => {
30
+ if (!versions || version === currentVersion) return
31
+
32
+ let targetPath = `/docs/${version}`
33
+
34
+ if (currentRoute) {
35
+ const baseFile = getBaseFilePath(
36
+ currentRoute.filePath,
37
+ currentRoute.version,
38
+ currentRoute.locale,
39
+ )
40
+
41
+ const targetRoute = allRoutes.find(
42
+ (r) =>
43
+ getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
44
+ (r.version || versions.defaultVersion) === version &&
45
+ (currentLocale ? r.locale === currentLocale : !r.locale),
46
+ )
47
+
48
+ if (targetRoute) {
49
+ targetPath = targetRoute.path
50
+ } else {
51
+ const versionIndexRoute = allRoutes.find(
52
+ (r) =>
53
+ getBaseFilePath(r.filePath, r.version, r.locale) === 'index.md' &&
54
+ (r.version || versions.defaultVersion) === version &&
55
+ (currentLocale ? r.locale === currentLocale : !r.locale),
56
+ )
57
+ targetPath = versionIndexRoute
58
+ ? versionIndexRoute.path
59
+ : `/docs/${version}${currentLocale ? `/${currentLocale}` : ''}`
60
+ }
61
+ }
62
+
63
+ navigate(targetPath)
64
+ }
65
+
66
+ const availableVersions = routeContext.availableVersions.map((v) => ({
67
+ ...v,
68
+ label: v.label as string,
69
+ value: v.key,
70
+ }))
71
+
72
+ return {
73
+ currentVersion,
74
+ currentVersionLabel: routeContext.currentVersionLabel,
75
+ availableVersions,
76
+ handleVersionChange,
77
+ }
78
+ }
@@ -1,18 +1,46 @@
1
- export type { BoltdocsConfig, BoltdocsThemeConfig } from "../node/config";
2
- export type { ComponentRoute, CreateBoltdocsAppOptions } from "./types";
3
- import { PackageManagerTabs } from "./theme/components/PackageManagerTabs";
4
- export { createBoltdocsApp } from "./app";
5
- export { ThemeLayout } from "./theme/ui/Layout";
6
- export { Navbar } from "./theme/ui/Navbar";
7
- export { Sidebar } from "./theme/ui/Sidebar";
8
- export { OnThisPage } from "./theme/ui/OnThisPage";
9
- export { Head } from "./theme/ui/Head";
10
- export { Breadcrumbs } from "./theme/ui/Breadcrumbs";
11
- export { Playground } from "./theme/components/Playground";
12
- export { NotFound } from "./theme/ui/NotFound";
13
- export { Loading } from "./theme/ui/Loading";
14
- export { CodeBlock } from "./theme/components/CodeBlock";
15
- export { Video } from "./theme/components/Video";
1
+ export type { BoltdocsConfig, BoltdocsThemeConfig } from '../node/config'
2
+ export type {
3
+ ComponentRoute,
4
+ CreateBoltdocsAppOptions,
5
+ LayoutProps,
6
+ } from './types'
7
+ export { createBoltdocsApp } from './app'
8
+ export { useConfig } from '@client/app/config-context'
9
+ export { useTheme } from '@client/app/theme-context'
10
+ export { useRoutes } from '@client/hooks/use-routes'
11
+ export { useMdxComponents } from '@client/app/mdx-components-context'
12
+
13
+ // Composable layout building blocks
14
+ export { DocsLayout } from '@components/docs-layout'
15
+ export { DefaultLayout } from '@components/default-layout'
16
+
17
+ // Default UI components (for use in custom layout.tsx)
18
+ export { Navbar } from '@components/ui-base/navbar'
19
+ export { Sidebar } from '@components/ui-base/sidebar'
20
+ export { OnThisPage } from '@components/ui-base/on-this-page'
21
+ export { Head } from '@components/ui-base/head'
22
+ export { Breadcrumbs } from '@components/ui-base/breadcrumbs'
23
+ export { PageNav } from '@components/ui-base/page-nav'
24
+ export { ProgressBar } from '@components/ui-base/progress-bar'
25
+ export { ErrorBoundary } from '@components/ui-base/error-boundary'
26
+ export { CopyMarkdown } from '@components/ui-base/copy-markdown'
27
+
28
+ export { NotFound } from '@components/ui-base/not-found'
29
+ export { Loading } from '@components/ui-base/loading'
30
+ export { CodeBlock } from '@components/mdx/code-block'
31
+ export { Video } from '@components/mdx/video'
32
+ export {
33
+ defineSandbox,
34
+ openSandbox,
35
+ embedSandbox,
36
+ } from '@integrations/codesandbox'
37
+
38
+ export type {
39
+ SandboxOptions,
40
+ SandboxFile,
41
+ SandboxFiles,
42
+ SandboxEmbedOptions,
43
+ } from './types'
16
44
  export {
17
45
  Button,
18
46
  Badge,
@@ -26,11 +54,17 @@ export {
26
54
  Warning,
27
55
  Danger,
28
56
  InfoBox,
57
+ ComponentProps,
58
+ ComponentPreview,
59
+ Important,
60
+ Caution,
29
61
  List,
30
62
  FileTree,
31
63
  Table,
32
64
  Field,
33
- } from "./theme/components/mdx";
65
+ Link,
66
+ Image,
67
+ } from './components/mdx'
34
68
  export type {
35
69
  ButtonProps,
36
70
  BadgeProps,
@@ -39,8 +73,12 @@ export type {
39
73
  TabsProps,
40
74
  TabProps,
41
75
  AdmonitionProps,
76
+ ComponentPropsProps,
77
+ ComponentPreviewProps,
42
78
  ListProps,
43
79
  FileTreeProps,
44
80
  TableProps,
45
81
  FieldProps,
46
- } from "./theme/components/mdx";
82
+ LinkProps,
83
+ ImageProps,
84
+ } from './components/mdx'
@@ -0,0 +1,179 @@
1
+ import { getParameters } from 'codesandbox/lib/api/define.js'
2
+ import { SandboxOptions } from '../types'
3
+
4
+ /**
5
+ * Build the files payload for the CodeSandbox Define API.
6
+ * Ensures every file conforms to the `{ content, isBinary }` shape
7
+ * required by the SDK, and auto-generates a `package.json` when one
8
+ * isn't explicitly provided.
9
+ */
10
+ function buildSandboxFiles(options: SandboxOptions) {
11
+ const files = options.files || {}
12
+ const dependencies = options.dependencies || {}
13
+ const devDependencies = options.devDependencies || {}
14
+ const title = options.title || 'codesandbox-project'
15
+ const description = options.description || 'Generic Sandbox'
16
+
17
+ const finalFiles: Record<string, { content: string; isBinary: boolean }> = {}
18
+
19
+ for (const [path, file] of Object.entries(files)) {
20
+ const content =
21
+ typeof file.content === 'object'
22
+ ? JSON.stringify(file.content, null, 2)
23
+ : file.content
24
+ finalFiles[path] = { content, isBinary: file.isBinary ?? false }
25
+ }
26
+
27
+ if (!finalFiles['package.json']) {
28
+ const isVite =
29
+ options.template === 'vite' ||
30
+ !!devDependencies.vite ||
31
+ !!devDependencies['@vitejs/plugin-react']
32
+
33
+ const defaultScripts = isVite
34
+ ? {
35
+ dev: 'vite',
36
+ build: 'vite build',
37
+ preview: 'vite preview',
38
+ }
39
+ : {
40
+ start: 'node index.js',
41
+ }
42
+
43
+ finalFiles['package.json'] = {
44
+ content: JSON.stringify(
45
+ {
46
+ private: true,
47
+ name: title,
48
+ description,
49
+ type: 'module',
50
+ version: '1.0.0',
51
+ scripts: options.scripts || defaultScripts,
52
+ dependencies,
53
+ devDependencies,
54
+ },
55
+ null,
56
+ 2,
57
+ ),
58
+ isBinary: false,
59
+ }
60
+ }
61
+
62
+ return finalFiles
63
+ }
64
+
65
+ /**
66
+ * Helper to define a sandbox and get a URL for the CodeSandbox Define API.
67
+ * Uses the official SDK `getParameters` for proper LZ-string compression.
68
+ */
69
+ export function defineSandbox(options: SandboxOptions) {
70
+ const finalFiles = buildSandboxFiles(options)
71
+ const parameters = getParameters({ files: finalFiles })
72
+
73
+ // FIX: Agregar query params que forzan comportamiento correcto
74
+ const query = new URLSearchParams({
75
+ parameters,
76
+ // FIX: Forzar instalación de dependencias
77
+ installDependencies: 'true',
78
+ })
79
+
80
+ // FIX: Agregar file query para que abra el entry correcto
81
+ if (options.entry) {
82
+ query.set('file', `/${options.entry}`)
83
+ }
84
+
85
+ return {
86
+ parameters,
87
+ url: `https://codesandbox.io/api/v1/sandboxes/define?${query.toString()}`,
88
+ options,
89
+ }
90
+ }
91
+
92
+ /**
93
+ * CORE API: Open a CodeSandbox using a form POST to the Define API.
94
+ *
95
+ * Uses a hidden form + POST which avoids URL length limits and is the
96
+ * recommended approach from CodeSandbox documentation. The SDK's
97
+ * `getParameters` handles LZ-string compression internally.
98
+ */
99
+ export function openSandbox(options: SandboxOptions) {
100
+ if (typeof window === 'undefined') return defineSandbox(options)
101
+
102
+ const finalFiles = buildSandboxFiles(options)
103
+ const parameters = getParameters({ files: finalFiles })
104
+ const entry = options.entry || 'src/App.tsx'
105
+
106
+ // Use form POST – the most reliable method for the Define API
107
+ const form = document.createElement('form')
108
+ form.method = 'POST'
109
+ form.target = '_blank'
110
+ form.action = 'https://codesandbox.io/api/v1/sandboxes/define'
111
+ form.style.display = 'none'
112
+
113
+ const addField = (name: string, value: string) => {
114
+ const input = document.createElement('input')
115
+ input.type = 'hidden'
116
+ input.name = name
117
+ input.value = value
118
+ form.appendChild(input)
119
+ }
120
+
121
+ const queryParams = new URLSearchParams({
122
+ file: `/${entry}`,
123
+ // FIX: Forzar vista de preview (no solo editor)
124
+ // FIX: Deshabilitar eslint que a veces bloquea
125
+ eslint: '0',
126
+ // FIX: Habilitar codemirror
127
+ codemirror: '1',
128
+ // FIX: Forzar instalación de deps
129
+ installDependencies: 'true',
130
+ })
131
+
132
+ addField('query', queryParams.toString())
133
+ addField('parameters', parameters)
134
+
135
+ document.body.appendChild(form)
136
+ form.submit()
137
+ document.body.removeChild(form)
138
+
139
+ return {
140
+ parameters,
141
+ url: `https://codesandbox.io/api/v1/sandboxes/define?parameters=${parameters}`,
142
+ options,
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Generate an embeddable iframe URL for a CodeSandbox.
148
+ *
149
+ * This gives you more control than `openSandbox` — you can embed the sandbox
150
+ * inline on the page rather than opening a new tab. The returned URL can be
151
+ * used as the `src` of an `<iframe>`.
152
+ *
153
+ * @example
154
+ * ```tsx
155
+ * const url = embedSandbox({
156
+ * files: { "index.js": { content: "console.log('hello')" } },
157
+ * embed: { view: "editor", theme: "dark" },
158
+ * }).url;
159
+ * // url → "https://codesandbox.io/api/v1/sandboxes/define?parameters=…&embed=1&view=editor&theme=dark"
160
+ * ```
161
+ */
162
+ export function embedSandbox(options: SandboxOptions) {
163
+ const finalFiles = buildSandboxFiles(options)
164
+ const parameters = getParameters({ files: finalFiles })
165
+ const embedOptions = options.embed || {}
166
+
167
+ const query = new URLSearchParams({ parameters, embed: '1' })
168
+
169
+ if (embedOptions.view) query.set('view', embedOptions.view)
170
+ if (embedOptions.theme) query.set('theme', embedOptions.theme)
171
+ if (embedOptions.hideNavigation) query.set('hidenavigation', '1')
172
+ if (options.entry) query.set('file', `/${options.entry}`)
173
+
174
+ return {
175
+ parameters,
176
+ url: `https://codesandbox.io/api/v1/sandboxes/define?${query.toString()}`,
177
+ options,
178
+ }
179
+ }
@@ -1,25 +1,27 @@
1
- import React from "react";
2
- import ReactDOMServer from "react-dom/server";
3
- import { StaticRouter } from "react-router-dom/server";
4
- import { AppShell } from "./app";
5
- import { ComponentRoute } from "./types";
1
+ import React from 'react'
2
+ import ReactDOMServer from 'react-dom/server'
3
+ import { StaticRouter } from 'react-router-dom/server'
4
+ import { AppShell } from './app'
5
+ import type { ComponentRoute } from './types'
6
6
 
7
7
  /**
8
8
  * Options for rendering the Boltdocs application on the server (SSG).
9
9
  */
10
10
  export interface RenderOptions {
11
11
  /** The URL path currently being rendered */
12
- path: string;
12
+ path: string
13
13
  /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
14
- routes: ComponentRoute[];
14
+ routes: ComponentRoute[]
15
15
  /** Site configuration (`virtual:boltdocs-config`) */
16
- config: any;
16
+ config: any
17
17
  /** The name of the documentation directory (e.g. 'docs') */
18
- docsDirName: string;
18
+ docsDirName: string
19
19
  /** Optional custom React component to render when visiting the root path ('/') */
20
- homePage?: React.ComponentType;
20
+ homePage?: React.ComponentType
21
+ /** Custom external pages mapped by their route path */
22
+ externalPages?: Record<string, React.ComponentType<any>>
21
23
  /** Preloaded modules (since SSR cannot use dynamic imports easily) */
22
- modules: Record<string, any>;
24
+ modules: Record<string, any>
23
25
  }
24
26
 
25
27
  /**
@@ -27,13 +29,21 @@ export interface RenderOptions {
27
29
  * This is called by the Node SSG script during the Vite build process.
28
30
  */
29
31
  export async function render(options: RenderOptions): Promise<string> {
30
- const { path, routes, config, modules, homePage, docsDirName } = options;
32
+ const {
33
+ path,
34
+ routes,
35
+ config,
36
+ modules,
37
+ homePage,
38
+ externalPages,
39
+ docsDirName,
40
+ } = options
31
41
 
32
42
  // For SSR, we must resolve modules synchronously. We create a mock 'loader'
33
43
  // that instantly returns the module since the SSG script already loaded it.
34
- const resolvedModules: Record<string, () => Promise<any>> = {};
44
+ const resolvedModules: Record<string, () => Promise<any>> = {}
35
45
  for (const [key, mod] of Object.entries(modules)) {
36
- resolvedModules[key] = () => Promise.resolve(mod);
46
+ resolvedModules[key] = () => Promise.resolve(mod)
37
47
  }
38
48
 
39
49
  const html = ReactDOMServer.renderToString(
@@ -45,10 +55,11 @@ export async function render(options: RenderOptions): Promise<string> {
45
55
  docsDirName={docsDirName}
46
56
  modules={resolvedModules}
47
57
  homePage={homePage}
58
+ externalPages={externalPages}
48
59
  />
49
60
  </StaticRouter>
50
61
  </React.StrictMode>,
51
- );
62
+ )
52
63
 
53
- return html;
64
+ return html
54
65
  }