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,135 @@
1
+ import { Children, isValidElement, useMemo } from 'react'
2
+ import * as RAC from 'react-aria-components'
3
+ import { useTabs } from './hooks/useTabs'
4
+ import { cn } from '@client/utils/cn'
5
+ import { CodeBlock } from './code-block'
6
+ import { cva } from 'class-variance-authority'
7
+
8
+ const tabListVariants = cva(
9
+ 'relative flex items-center border-b border-border-subtle gap-1 overflow-x-auto no-scrollbar',
10
+ {
11
+ variants: {
12
+ size: {
13
+ default: 'px-0',
14
+ compact: 'px-2',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ size: 'default',
19
+ },
20
+ },
21
+ )
22
+
23
+ const tabItemVariants = cva(
24
+ 'flex items-center gap-2 px-4 py-2.5 text-sm font-medium outline-none transition-all duration-200 cursor-pointer bg-transparent border-none select-none whitespace-nowrap',
25
+ {
26
+ variants: {
27
+ isActive: {
28
+ true: 'text-primary-500',
29
+ false: 'text-text-muted hover:text-text-main',
30
+ },
31
+ isDisabled: {
32
+ true: 'opacity-40 pointer-events-none',
33
+ false: '',
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ isActive: false,
38
+ isDisabled: false,
39
+ },
40
+ },
41
+ )
42
+
43
+ export interface TabProps {
44
+ label: string
45
+ icon?: React.ReactNode
46
+ disabled?: boolean
47
+ children: React.ReactNode
48
+ }
49
+
50
+ export function Tab({ children }: TabProps) {
51
+ const content =
52
+ typeof children === 'string' ? (
53
+ <CodeBlock className="language-bash">
54
+ <code>{children.trim()}</code>
55
+ </CodeBlock>
56
+ ) : (
57
+ children
58
+ )
59
+
60
+ return <div className="py-4">{content}</div>
61
+ }
62
+
63
+ export interface TabsProps {
64
+ defaultIndex?: number
65
+ children: React.ReactNode
66
+ }
67
+
68
+ export function Tabs({ defaultIndex = 0, children }: TabsProps) {
69
+ const tabs = useMemo(() => {
70
+ return Children.toArray(children).filter(
71
+ (child) =>
72
+ isValidElement(child) &&
73
+ (child as React.ReactElement<TabProps>).props?.label,
74
+ ) as React.ReactElement<TabProps>[]
75
+ }, [children])
76
+
77
+ const { active, setActive, tabRefs, indicatorStyle } = useTabs({
78
+ initialIndex: defaultIndex,
79
+ tabs,
80
+ })
81
+
82
+ return (
83
+ <div className="my-8 w-full group/tabs">
84
+ <RAC.Tabs
85
+ selectedKey={active.toString()}
86
+ onSelectionChange={(key) => setActive(Number(key))}
87
+ className="w-full"
88
+ >
89
+ <RAC.TabList
90
+ aria-label="Content Tabs"
91
+ className={cn(tabListVariants())}
92
+ >
93
+ {tabs.map((child, i) => {
94
+ const { label, icon, disabled } = child.props
95
+ const key = i.toString()
96
+
97
+ return (
98
+ <RAC.Tab
99
+ key={key}
100
+ id={key}
101
+ isDisabled={disabled}
102
+ ref={(el: any) => {
103
+ tabRefs.current[i] = el
104
+ }}
105
+ className={({ isSelected, isDisabled }) =>
106
+ cn(tabItemVariants({ isActive: isSelected, isDisabled }))
107
+ }
108
+ >
109
+ {!!icon && (
110
+ <span className="shrink-0 [&>svg]:w-4 [&>svg]:h-4">
111
+ {icon}
112
+ </span>
113
+ )}
114
+ <span>{label}</span>
115
+ </RAC.Tab>
116
+ )
117
+ })}
118
+
119
+ <div
120
+ className="absolute bottom-0 h-0.5 bg-primary-500 transition-all duration-300 ease-in-out pointer-events-none"
121
+ style={indicatorStyle}
122
+ aria-hidden="true"
123
+ />
124
+ </RAC.TabList>
125
+
126
+ {tabs.map((_tab, i) => (
127
+ <RAC.TabPanel key={i} id={i.toString()}>
128
+ {/* biome-ignore lint/suspicious/noExplicitAny: bypass version-specific ReactNode mismatch */}
129
+ {tabs[i] as any}
130
+ </RAC.TabPanel>
131
+ ))}
132
+ </RAC.Tabs>
133
+ </div>
134
+ )
135
+ }
@@ -0,0 +1,68 @@
1
+ import { useRef, useState, useEffect } from 'react'
2
+
3
+ interface VideoProps {
4
+ src?: string
5
+ poster?: string
6
+ alt?: string
7
+ controls?: boolean
8
+ preload?: string
9
+ children?: React.ReactNode
10
+ [key: string]: any
11
+ }
12
+
13
+ export function Video({
14
+ src,
15
+ poster,
16
+ alt,
17
+ children,
18
+ controls,
19
+ preload = 'metadata',
20
+ ...rest
21
+ }: VideoProps) {
22
+ const containerRef = useRef<HTMLDivElement>(null)
23
+ const [isVisible, setIsVisible] = useState(false)
24
+
25
+ useEffect(() => {
26
+ const el = containerRef.current
27
+ if (!el) return
28
+ const observer = new IntersectionObserver(
29
+ ([entry]) => {
30
+ if (entry.isIntersecting) {
31
+ setIsVisible(true)
32
+ observer.disconnect()
33
+ }
34
+ },
35
+ { rootMargin: '200px' },
36
+ )
37
+ observer.observe(el)
38
+ return () => observer.disconnect()
39
+ }, [])
40
+
41
+ return (
42
+ <div
43
+ ref={containerRef}
44
+ className="my-6 overflow-hidden rounded-lg border border-border-subtle"
45
+ >
46
+ {isVisible ? (
47
+ <video
48
+ className="block w-full h-auto"
49
+ src={src}
50
+ poster={poster}
51
+ controls={true}
52
+ preload={preload}
53
+ playsInline
54
+ {...rest}
55
+ >
56
+ {children}
57
+ Your browser does not support the video tag.
58
+ </video>
59
+ ) : (
60
+ <div
61
+ className="aspect-video bg-bg-surface animate-pulse"
62
+ role="img"
63
+ aria-label={alt || 'Video'}
64
+ />
65
+ )}
66
+ </div>
67
+ )
68
+ }
@@ -0,0 +1,79 @@
1
+ import {
2
+ Breadcrumb,
3
+ Breadcrumbs as BreadcrumbsRAC,
4
+ Link,
5
+ } from 'react-aria-components'
6
+ import type { LinkProps } from 'react-aria-components'
7
+ import { ChevronRight } from 'lucide-react'
8
+ import { cn } from '../../utils/cn'
9
+ import type { ComponentBase } from './types'
10
+
11
+ export const BreadcrumbsRoot = ({
12
+ children,
13
+ className,
14
+ ...props
15
+ }: ComponentBase) => {
16
+ return (
17
+ <BreadcrumbsRAC
18
+ className={cn(
19
+ 'flex items-center gap-1.5 mb-0 text-sm text-text-muted',
20
+ className,
21
+ )}
22
+ {...props}
23
+ >
24
+ {children as any}
25
+ </BreadcrumbsRAC>
26
+ )
27
+ }
28
+
29
+ export const BreadcrumbsItem = ({
30
+ children,
31
+ className,
32
+ ...props
33
+ }: ComponentBase) => {
34
+ return (
35
+ <Breadcrumb
36
+ className={cn('flex items-center mb-0 gap-1.5', className)}
37
+ {...props}
38
+ >
39
+ {children as any}
40
+ </Breadcrumb>
41
+ )
42
+ }
43
+
44
+ export const BreadcrumbsLink = ({
45
+ children,
46
+ href,
47
+ className,
48
+ ...props
49
+ }: LinkProps & { className?: string }) => {
50
+ return (
51
+ <Link
52
+ href={href}
53
+ className={cn(
54
+ 'transition-colors outline-none hover:text-text-main focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm',
55
+ 'current:font-medium current:text-text-main current:pointer-events-none cursor-pointer',
56
+ className,
57
+ )}
58
+ {...props}
59
+ >
60
+ {children as any}
61
+ </Link>
62
+ )
63
+ }
64
+
65
+ export const BreadcrumbsSeparator = ({ className }: ComponentBase) => {
66
+ return (
67
+ <ChevronRight
68
+ size={14}
69
+ className={cn('shrink-0 text-text-dim', className)}
70
+ />
71
+ )
72
+ }
73
+
74
+ export default {
75
+ BreadcrumbsRoot,
76
+ BreadcrumbsItem,
77
+ BreadcrumbsLink,
78
+ BreadcrumbsSeparator,
79
+ }
@@ -0,0 +1,54 @@
1
+ import { cn } from '@client/utils/cn'
2
+ import type { ComponentBase } from './types'
3
+
4
+ export interface ButtonGroupProps extends ComponentBase {
5
+ vertical?: boolean
6
+ }
7
+
8
+ export const ButtonGroup = ({
9
+ children,
10
+ className,
11
+ vertical = false,
12
+ }: ButtonGroupProps) => {
13
+ return (
14
+ <div
15
+ className={cn(
16
+ 'inline-flex',
17
+ vertical ? 'flex-col' : 'flex-row',
18
+ // Handle nested button borders and radii
19
+ !vertical && [
20
+ '[&>*:not(:first-child)]:-ml-px',
21
+ '[&>*:first-child]:rounded-r-none',
22
+ '[&>*:last-child]:rounded-l-none',
23
+ '[&>*:not(:first-child):not(:last-child)]:rounded-none',
24
+ // Extra polish for outer corners
25
+ className?.includes('rounded-full') && [
26
+ '[&>*:first-child]:rounded-l-full',
27
+ '[&>*:last-child]:rounded-r-full',
28
+ ],
29
+ className?.includes('rounded-xl') && [
30
+ '[&>*:first-child]:rounded-l-xl',
31
+ '[&>*:last-child]:rounded-r-xl',
32
+ ],
33
+ className?.includes('rounded-lg') && [
34
+ '[&>*:first-child]:rounded-l-lg',
35
+ '[&>*:last-child]:rounded-r-lg',
36
+ ],
37
+ ],
38
+ vertical && [
39
+ '[&>*:not(:first-child)]:-mt-px',
40
+ '[&>*:first-child]:rounded-b-none',
41
+ '[&>*:last-child]:rounded-t-none',
42
+ '[&>*:not(:first-child):not(:last-child)]:rounded-none',
43
+ className?.includes('rounded-full') && [
44
+ '[&>*:first-child]:rounded-t-full',
45
+ '[&>*:last-child]:rounded-b-full',
46
+ ],
47
+ ],
48
+ className,
49
+ )}
50
+ >
51
+ {children}
52
+ </div>
53
+ )
54
+ }
@@ -0,0 +1,145 @@
1
+ import * as RAC from 'react-aria-components'
2
+ import { cn } from '@client/utils/cn'
3
+ import { cva } from 'class-variance-authority'
4
+ import type { VariantProps } from 'class-variance-authority'
5
+
6
+ export const buttonVariants = cva(
7
+ 'flex flex-row items-center justify-center w-auto font-semibold tracking-tight no-underline whitespace-nowrap select-none outline-none transition-all duration-200 cursor-pointer pressed:scale-[0.97] hover:-translate-y-px leading-none',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ primary:
12
+ 'bg-primary-500 text-white shadow-md hover:brightness-110 hover:shadow-lg',
13
+ secondary:
14
+ 'bg-bg-surface text-text-main border border-border-subtle hover:bg-bg-muted hover:border-border-strong',
15
+ outline:
16
+ 'bg-transparent text-text-main border border-border-strong hover:bg-bg-surface hover:border-primary-500',
17
+ ghost:
18
+ 'bg-transparent text-text-muted hover:bg-bg-surface hover:text-text-main',
19
+ danger:
20
+ 'bg-[var(--color-danger-500)]/10 text-[var(--color-danger-500)] border border-[var(--color-danger-500)]/20 hover:bg-[var(--color-danger-500)]/15',
21
+ success:
22
+ 'bg-[var(--color-success-500)]/10 text-[var(--color-success-500)] border border-[var(--color-success-500)]/20 hover:bg-[var(--color-success-500)]/15',
23
+ warning:
24
+ 'bg-[var(--color-warning-500)]/10 text-[var(--color-warning-500)] border border-[var(--color-warning-500)]/20 hover:bg-[var(--color-warning-500)]/15',
25
+ info: 'bg-[var(--color-info-500)]/10 text-[var(--color-info-500)] border border-[var(--color-info-500)]/20 hover:bg-[var(--color-info-500)]/15',
26
+ subtle: 'bg-primary-500/10 text-primary-500 hover:bg-primary-500/20',
27
+ link: 'bg-transparent text-primary-500 !p-0 !min-h-0 hover:underline',
28
+ },
29
+ size: {
30
+ sm: 'min-h-8 px-3.5 text-[0.8125rem] gap-1.5',
31
+ md: 'min-h-10 px-5 text-[0.9375rem] gap-2',
32
+ lg: 'min-h-12 px-7 text-[1.05rem] gap-2.5',
33
+ },
34
+ rounded: {
35
+ none: 'rounded-none',
36
+ sm: 'rounded-sm',
37
+ md: 'rounded-md',
38
+ lg: 'rounded-lg',
39
+ full: 'rounded-full',
40
+ },
41
+ iconSize: {
42
+ sm: 'w-8 h-8 p-0',
43
+ md: 'w-10 h-10 p-0',
44
+ lg: 'w-12 h-12 p-0',
45
+ },
46
+ disabled: {
47
+ true: 'opacity-50 cursor-not-allowed pointer-events-none',
48
+ false: null,
49
+ },
50
+ },
51
+ defaultVariants: {
52
+ variant: 'primary',
53
+ size: 'md',
54
+ rounded: 'md',
55
+ },
56
+ },
57
+ )
58
+ type ButtonVariantType = VariantProps<typeof buttonVariants>
59
+
60
+ export interface ButtonProps
61
+ extends Omit<RAC.ButtonProps, 'children' | 'className'>,
62
+ ButtonVariantType {
63
+ icon?: React.ReactNode
64
+ iconPosition?: 'left' | 'right'
65
+ href?: string
66
+ children?: React.ReactNode
67
+ className?: string
68
+ isIconOnly?: boolean
69
+ }
70
+
71
+ export const Button = ({
72
+ href,
73
+ icon,
74
+ iconPosition = 'left',
75
+ isIconOnly,
76
+ children,
77
+ className,
78
+ variant,
79
+ size,
80
+ rounded,
81
+ iconSize,
82
+ disabled,
83
+ ...props
84
+ }: ButtonProps) => {
85
+ const isOnlyIcon = isIconOnly || (!children && !!icon)
86
+
87
+ const content = isOnlyIcon ? (
88
+ <span className="inline-flex items-center justify-center [&>svg]:w-[1.2rem] [&>svg]:h-[1.2rem]">
89
+ {icon}
90
+ </span>
91
+ ) : (
92
+ <>
93
+ {icon && iconPosition === 'left' && (
94
+ <span className="inline-flex items-center shrink-0 [&>svg]:w-[1.1rem] [&>svg]:h-[1.1rem]">
95
+ {icon}
96
+ </span>
97
+ )}
98
+ <span className="flex items-center">{children}</span>
99
+ {icon && iconPosition === 'right' && (
100
+ <span className="inline-flex items-center shrink-0 [&>svg]:w-[1.1rem] [&>svg]:h-[1.1rem]">
101
+ {icon}
102
+ </span>
103
+ )}
104
+ </>
105
+ )
106
+
107
+ if (href) {
108
+ return (
109
+ <RAC.Link
110
+ href={href}
111
+ className={cn(
112
+ buttonVariants({
113
+ variant,
114
+ size,
115
+ rounded,
116
+ iconSize: isOnlyIcon ? iconSize : undefined,
117
+ disabled,
118
+ }),
119
+ className,
120
+ )}
121
+ {...(props as RAC.LinkProps)}
122
+ >
123
+ {content}
124
+ </RAC.Link>
125
+ )
126
+ }
127
+
128
+ return (
129
+ <RAC.Button
130
+ className={cn(
131
+ buttonVariants({
132
+ variant,
133
+ size,
134
+ rounded,
135
+ iconSize: isOnlyIcon ? iconSize : undefined,
136
+ disabled,
137
+ }),
138
+ className,
139
+ )}
140
+ {...(props as RAC.ButtonProps)}
141
+ >
142
+ {content}
143
+ </RAC.Button>
144
+ )
145
+ }
@@ -0,0 +1,120 @@
1
+ import type { TOCItemInfo, TOCItemType } from '../on-this-page'
2
+
3
+ export function getItemId(url: string) {
4
+ if (url.startsWith('#')) return url.slice(1)
5
+ return null
6
+ }
7
+
8
+ export class Observer {
9
+ items: TOCItemInfo[] = []
10
+ single = false
11
+ private observer: IntersectionObserver | null = null
12
+ onChange?: () => void
13
+
14
+ private callback(entries: IntersectionObserverEntry[]) {
15
+ if (entries.length === 0) return
16
+
17
+ let hasActive = false
18
+ this.items = this.items.map((item) => {
19
+ const entry = entries.find((entry) => entry.target.id === item.id)
20
+ let active = entry ? entry.isIntersecting : item.active && !item.fallback
21
+ if (this.single && hasActive) active = false
22
+
23
+ if (item.active !== active) {
24
+ item = {
25
+ ...item,
26
+ t: Date.now(),
27
+ active,
28
+ fallback: false,
29
+ }
30
+ }
31
+
32
+ if (active) hasActive = true
33
+ return item
34
+ })
35
+
36
+ if (!hasActive && entries[0].rootBounds) {
37
+ const viewTop = entries[0].rootBounds.top
38
+ let min = Number.MAX_VALUE
39
+ let fallbackIdx = -1
40
+
41
+ for (let i = 0; i < this.items.length; i++) {
42
+ const element = document.getElementById(this.items[i].id)
43
+ if (!element) continue
44
+
45
+ const d = Math.abs(viewTop - element.getBoundingClientRect().top)
46
+ if (d < min) {
47
+ fallbackIdx = i
48
+ min = d
49
+ }
50
+ }
51
+
52
+ if (fallbackIdx !== -1) {
53
+ this.items[fallbackIdx] = {
54
+ ...this.items[fallbackIdx],
55
+ active: true,
56
+ fallback: true,
57
+ t: Date.now(),
58
+ }
59
+ }
60
+ }
61
+
62
+ this.onChange?.()
63
+ }
64
+
65
+ setItems(newItems: TOCItemType[]) {
66
+ const observer = this.observer
67
+ if (observer) {
68
+ for (const item of this.items) {
69
+ const element = document.getElementById(item.id)
70
+ if (!element) continue
71
+ observer.unobserve(element)
72
+ }
73
+ }
74
+
75
+ this.items = []
76
+ for (const item of newItems) {
77
+ const id = getItemId(item.url)
78
+ if (!id) continue
79
+
80
+ this.items.push({
81
+ id,
82
+ active: false,
83
+ fallback: false,
84
+ t: 0,
85
+ original: item,
86
+ })
87
+ }
88
+ this.watchItems()
89
+
90
+ // In an SPA, the TOC might update before the MDX content is in the DOM.
91
+ // We perform a few delayed scans to ensure we catch those elements.
92
+ if (typeof window !== 'undefined') {
93
+ setTimeout(() => this.watchItems(), 100)
94
+ setTimeout(() => this.watchItems(), 500)
95
+ setTimeout(() => this.watchItems(), 1000)
96
+ }
97
+
98
+ this.onChange?.()
99
+ }
100
+
101
+ watch(options?: IntersectionObserverInit) {
102
+ if (this.observer) return
103
+ this.observer = new IntersectionObserver(this.callback.bind(this), options)
104
+ this.watchItems()
105
+ }
106
+
107
+ private watchItems() {
108
+ if (!this.observer) return
109
+ for (const item of this.items) {
110
+ const element = document.getElementById(item.id)
111
+ if (!element) continue
112
+ this.observer.observe(element)
113
+ }
114
+ }
115
+
116
+ unwatch() {
117
+ this.observer?.disconnect()
118
+ this.observer = null
119
+ }
120
+ }
@@ -0,0 +1,17 @@
1
+ export * from './navbar'
2
+ export * from './navigation-menu'
3
+ export * from './search-dialog'
4
+ export * from './on-this-page'
5
+ export * from './page-nav'
6
+ export * from './tabs'
7
+ export * from './sidebar'
8
+ export * from './breadcrumbs'
9
+ export * from './button'
10
+ export * from './button-group'
11
+ export * from './menu'
12
+ export * from './popover'
13
+ export * from './tooltip'
14
+ export * from './link'
15
+ export { Separator, ToggleButton } from 'react-aria-components'
16
+
17
+ export { cn } from '../../utils/cn'