boltdocs 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/bin/boltdocs.js +0 -1
  2. package/dist/cache-CQKlT4fI.mjs +6 -0
  3. package/dist/cache-DorPMFgW.cjs +6 -0
  4. package/dist/cards-BLoSiRuL.d.ts +30 -0
  5. package/dist/cards-CQn9mXZS.d.cts +30 -0
  6. package/dist/chunk-Ds5LZdWN.cjs +6 -0
  7. package/dist/client/index.cjs +1 -1
  8. package/dist/client/index.d.cts +173 -1328
  9. package/dist/client/index.d.ts +172 -1327
  10. package/dist/client/index.js +1 -1
  11. package/dist/{package-c99Cs7mD.cjs → client/mdx.cjs} +1 -1
  12. package/dist/client/mdx.d.cts +128 -0
  13. package/dist/client/mdx.d.ts +129 -0
  14. package/dist/client/mdx.js +6 -0
  15. package/dist/client/primitives.cjs +6 -0
  16. package/dist/client/primitives.d.cts +818 -0
  17. package/dist/client/primitives.d.ts +818 -0
  18. package/dist/client/primitives.js +6 -0
  19. package/dist/client/theme/neutral.css +74 -361
  20. package/dist/client/theme/reset.css +189 -0
  21. package/dist/docs-layout-BlDhcQRv.cjs +6 -0
  22. package/dist/docs-layout-BvAOWEJw.js +6 -0
  23. package/dist/doctor-BQiQhCTl.cjs +6 -0
  24. package/dist/doctor-COpf35L2.cjs +20 -0
  25. package/dist/doctor-Dh1XP7Pz.mjs +20 -0
  26. package/dist/generator-DGW6pkCC.cjs +22 -0
  27. package/dist/generator-Dv3wEmhZ.mjs +22 -0
  28. package/dist/icons-dev-CrQLjoQp.js +6 -0
  29. package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
  30. package/dist/image-BkIfa9oo.js +6 -0
  31. package/dist/image-DIGjCPe6.cjs +6 -0
  32. package/dist/mdx-K0WYBAJ3.js +7 -0
  33. package/dist/mdx-hpErbRUe.cjs +7 -0
  34. package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
  35. package/dist/meta-loader-9IpAHWDS.mjs +6 -0
  36. package/dist/node/cli-entry.cjs +1 -2
  37. package/dist/node/cli-entry.mjs +1 -2
  38. package/dist/node/index.cjs +1 -1
  39. package/dist/node/index.d.cts +66 -13
  40. package/dist/node/index.d.mts +66 -14
  41. package/dist/node/index.mjs +1 -1
  42. package/dist/node/routes/worker.cjs +6 -0
  43. package/dist/node/routes/worker.d.cts +2 -0
  44. package/dist/node/routes/worker.d.mts +2 -0
  45. package/dist/node/routes/worker.mjs +6 -0
  46. package/dist/node-C2nWXElP.mjs +112 -0
  47. package/dist/node-CinkUtxV.cjs +112 -0
  48. package/dist/package-BMYLDBBP.cjs +6 -0
  49. package/dist/{package-DukYeKmD.mjs → package-HegMOTL_.mjs} +1 -1
  50. package/dist/parser-Bh11BsdA.cjs +6 -0
  51. package/dist/parser-D8eQvE7N.mjs +6 -0
  52. package/dist/parser-DYRzXWmA.cjs +6 -0
  53. package/dist/routes-CHf76Ye4.cjs +6 -0
  54. package/dist/routes-CMUZGI6T.mjs +6 -0
  55. package/dist/routes-Co1mRM58.cjs +6 -0
  56. package/dist/search-dialog-BACuzoVX.cjs +6 -0
  57. package/dist/search-dialog-BKagVT17.js +6 -0
  58. package/dist/search-dialog-C8w12eUx.js +6 -0
  59. package/dist/search-dialog-CGyrozZE.cjs +6 -0
  60. package/dist/search-dialog-D26rUnJ_.cjs +6 -0
  61. package/dist/sidebar-DKvg6KOc.d.cts +491 -0
  62. package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
  63. package/dist/utils-BxNAXhZZ.mjs +7 -0
  64. package/dist/utils-Clzu7jvb.cjs +7 -0
  65. package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
  66. package/dist/worker-pool-BwU8ckrg.cjs +6 -0
  67. package/package.json +27 -8
  68. package/src/client/app/doc-page.tsx +9 -5
  69. package/src/client/app/docs-layout.tsx +17 -3
  70. package/src/client/app/head.tsx +122 -0
  71. package/src/client/app/helmet-compat.tsx +36 -0
  72. package/src/client/app/mdx-component.tsx +5 -52
  73. package/src/client/app/mdx-components-context.tsx +32 -8
  74. package/src/client/app/routes-context.tsx +2 -2
  75. package/src/client/app/scroll-handler.tsx +1 -1
  76. package/src/client/app/theme-context.tsx +5 -5
  77. package/src/client/app/ui-context.tsx +42 -0
  78. package/src/client/components/docs-layout-default.tsx +85 -0
  79. package/src/client/components/icons-dev.tsx +38 -15
  80. package/src/client/components/mdx/callout.tsx +97 -0
  81. package/src/client/components/mdx/card.tsx +73 -98
  82. package/src/client/components/mdx/cards.tsx +27 -0
  83. package/src/client/components/mdx/code-block.tsx +37 -17
  84. package/src/client/components/mdx/field.tsx +24 -56
  85. package/src/client/components/mdx/image.tsx +36 -15
  86. package/src/client/components/mdx/index.ts +19 -53
  87. package/src/client/components/mdx/table.tsx +46 -148
  88. package/src/client/components/mdx/typographics.tsx +120 -0
  89. package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
  90. package/src/client/components/primitives/breadcrumbs.tsx +5 -24
  91. package/src/client/components/primitives/button.tsx +3 -142
  92. package/src/client/components/primitives/code-block.tsx +104 -97
  93. package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
  94. package/src/client/components/primitives/error-boundary.tsx +107 -0
  95. package/src/client/components/primitives/heading.tsx +128 -0
  96. package/src/client/components/primitives/helpers/observer.ts +62 -32
  97. package/src/client/components/primitives/image.tsx +26 -0
  98. package/src/client/components/primitives/link.tsx +50 -52
  99. package/src/client/components/primitives/menu.tsx +25 -49
  100. package/src/client/components/primitives/navbar.tsx +234 -59
  101. package/src/client/components/primitives/on-this-page.tsx +169 -40
  102. package/src/client/components/primitives/page-nav.tsx +11 -39
  103. package/src/client/components/primitives/popover.tsx +12 -30
  104. package/src/client/components/primitives/search-dialog.tsx +77 -71
  105. package/src/client/components/primitives/sidebar.tsx +312 -119
  106. package/src/client/components/primitives/skeleton.tsx +1 -1
  107. package/src/client/components/primitives/tabs.tsx +5 -16
  108. package/src/client/components/primitives/tooltip.tsx +1 -1
  109. package/src/client/components/ui-base/banner.tsx +66 -0
  110. package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
  111. package/src/client/components/ui-base/copy-markdown.tsx +43 -35
  112. package/src/client/components/ui-base/error-boundary.tsx +9 -46
  113. package/src/client/components/ui-base/github-stars.tsx +5 -3
  114. package/src/client/components/ui-base/index.ts +3 -3
  115. package/src/client/components/ui-base/last-updated.tsx +27 -0
  116. package/src/client/components/ui-base/navbar.tsx +183 -89
  117. package/src/client/components/ui-base/not-found.tsx +11 -9
  118. package/src/client/components/ui-base/on-this-page.tsx +8 -104
  119. package/src/client/components/ui-base/page-nav.tsx +23 -9
  120. package/src/client/components/ui-base/search-dialog.tsx +111 -36
  121. package/src/client/components/ui-base/search-highlight.tsx +10 -0
  122. package/src/client/components/ui-base/sidebar.tsx +77 -154
  123. package/src/client/components/ui-base/tabs.tsx +20 -7
  124. package/src/client/components/ui-base/theme-toggle.tsx +88 -10
  125. package/src/client/components/ui-base/version-i18n.tsx +80 -0
  126. package/src/client/hooks/index.ts +2 -1
  127. package/src/client/hooks/use-analytics.ts +272 -0
  128. package/src/client/hooks/use-i18n.ts +120 -53
  129. package/src/client/hooks/use-localized-to.ts +70 -30
  130. package/src/client/hooks/use-navbar.ts +69 -39
  131. package/src/client/hooks/use-page-nav.ts +28 -25
  132. package/src/client/hooks/use-routes.ts +64 -81
  133. package/src/client/hooks/use-search-highlight.ts +185 -0
  134. package/src/client/hooks/use-search.ts +12 -3
  135. package/src/client/hooks/use-sidebar.ts +183 -77
  136. package/src/client/hooks/use-tabs.ts +3 -4
  137. package/src/client/hooks/use-version.ts +46 -18
  138. package/src/client/index.ts +13 -86
  139. package/src/client/mdx.ts +2 -0
  140. package/src/client/primitives.ts +19 -0
  141. package/src/client/ssg/boltdocs-shell.tsx +78 -57
  142. package/src/client/ssg/create-routes.tsx +290 -50
  143. package/src/client/ssg/mdx-page.tsx +2 -1
  144. package/src/client/store/boltdocs-context.tsx +83 -12
  145. package/src/client/theme/neutral.css +74 -361
  146. package/src/client/theme/reset.css +189 -0
  147. package/src/client/types.ts +10 -2
  148. package/src/client/utils/path.ts +9 -0
  149. package/src/client/utils/react-to-text.ts +24 -24
  150. package/src/client/virtual.d.ts +1 -1
  151. package/src/shared/types.ts +97 -21
  152. package/dist/node-CWN8U_p8.mjs +0 -88
  153. package/dist/node-D5iosYXv.cjs +0 -88
  154. package/dist/search-dialog-3lvKsbVG.js +0 -6
  155. package/dist/search-dialog-DMK5OpgH.cjs +0 -6
  156. package/dist/use-search-C9bxCqfF.js +0 -6
  157. package/dist/use-search-DcfZSunO.cjs +0 -6
  158. package/src/client/components/mdx/admonition.tsx +0 -91
  159. package/src/client/components/mdx/badge.tsx +0 -41
  160. package/src/client/components/mdx/button.tsx +0 -35
  161. package/src/client/components/mdx/component-preview.tsx +0 -37
  162. package/src/client/components/mdx/component-props.tsx +0 -83
  163. package/src/client/components/mdx/file-tree.tsx +0 -325
  164. package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
  165. package/src/client/components/mdx/hooks/useTable.ts +0 -74
  166. package/src/client/components/mdx/hooks/useTabs.ts +0 -68
  167. package/src/client/components/mdx/link.tsx +0 -38
  168. package/src/client/components/mdx/list.tsx +0 -192
  169. package/src/client/components/mdx/tabs.tsx +0 -135
  170. package/src/client/components/mdx/video.tsx +0 -68
  171. package/src/client/components/primitives/index.ts +0 -19
  172. package/src/client/components/primitives/navigation-menu.tsx +0 -114
  173. package/src/client/components/ui-base/head.tsx +0 -76
  174. package/src/client/components/ui-base/loading.tsx +0 -57
  175. package/src/client/components/ui-base/powered-by.tsx +0 -25
  176. package/src/client/hooks/use-onthispage.ts +0 -23
  177. package/src/client/utils/use-on-change.ts +0 -15
@@ -1,192 +0,0 @@
1
- import {
2
- Children,
3
- isValidElement,
4
- type ReactNode,
5
- type ReactElement,
6
- type ComponentPropsWithoutRef,
7
- } from 'react'
8
- import { Check, ChevronRight, Circle } from 'lucide-react'
9
- import { cn } from '../../utils/cn'
10
- import { cva, type VariantProps } from 'class-variance-authority'
11
-
12
- const listVariants = cva('my-6 transition-all duration-200', {
13
- variants: {
14
- variant: {
15
- default: 'list-disc pl-5 text-text-muted marker:text-primary-500/50',
16
- number:
17
- 'list-decimal pl-5 text-text-muted marker:text-primary-500/50 marker:font-bold',
18
- checked: 'list-none p-0',
19
- arrow: 'list-none p-0',
20
- bubble: 'list-none p-0',
21
- },
22
- cols: {
23
- 1: 'grid-cols-1',
24
- 2: 'grid-cols-1 sm:grid-cols-2',
25
- 3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
26
- 4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
27
- },
28
- isGrid: {
29
- true: 'grid gap-x-8 gap-y-3',
30
- false: 'space-y-2',
31
- },
32
- dense: {
33
- true: 'space-y-1',
34
- false: 'space-y-2',
35
- },
36
- },
37
- compoundVariants: [
38
- {
39
- variant: 'default',
40
- dense: true,
41
- className: 'space-y-0.5',
42
- },
43
- ],
44
- defaultVariants: {
45
- variant: 'default',
46
- cols: 1,
47
- isGrid: false,
48
- dense: false,
49
- },
50
- })
51
-
52
- const itemVariants = cva(
53
- 'group flex items-start gap-3 text-sm leading-relaxed transition-all duration-200',
54
- {
55
- variants: {
56
- variant: {
57
- default: '',
58
- number: '',
59
- checked: 'hover:translate-x-0.5',
60
- arrow: 'hover:translate-x-0.5',
61
- bubble: 'hover:translate-x-0.5',
62
- },
63
- dense: {
64
- true: 'py-0',
65
- false: 'py-0.5',
66
- },
67
- },
68
- defaultVariants: {
69
- variant: 'default',
70
- dense: false,
71
- },
72
- },
73
- )
74
-
75
- const iconContainerVariants = cva(
76
- 'mt-1 shrink-0 flex items-center justify-center transition-transform group-hover:scale-110',
77
- {
78
- variants: {
79
- variant: {
80
- bubble:
81
- 'h-5 w-5 rounded-full bg-primary-500/10 text-primary-500 text-[10px] font-bold',
82
- default: '',
83
- },
84
- },
85
- defaultVariants: {
86
- variant: 'default',
87
- },
88
- },
89
- )
90
-
91
- type ListVariantProps = VariantProps<typeof listVariants>
92
-
93
- export interface ListProps
94
- extends ComponentPropsWithoutRef<'ul'>,
95
- Omit<ListVariantProps, 'variant'> {
96
- variant?: 'checked' | 'arrow' | 'default' | 'bubble' | 'number'
97
- children: ReactNode
98
- }
99
-
100
- interface ListItemProps extends VariantProps<typeof itemVariants> {
101
- icon?: ReactNode
102
- children: ReactNode
103
- }
104
-
105
- function ListItem({ icon, children, variant, dense }: ListItemProps) {
106
- return (
107
- <li className={cn(itemVariants({ variant, dense }))}>
108
- {icon && (
109
- <span
110
- className={cn(
111
- iconContainerVariants({
112
- variant: variant === 'bubble' ? 'bubble' : 'default',
113
- }),
114
- )}
115
- >
116
- {icon}
117
- </span>
118
- )}
119
- <div className="flex-1 text-text-muted group-hover:text-text-main transition-colors">
120
- {children}
121
- </div>
122
- </li>
123
- )
124
- }
125
-
126
- const ICON_MAP: Record<string, (cls?: string) => ReactNode> = {
127
- checked: (cls) => (
128
- <Check size={14} className={cn('text-emerald-500 shrink-0', cls)} />
129
- ),
130
- arrow: (cls) => (
131
- <ChevronRight size={14} className={cn('text-primary-400 shrink-0', cls)} />
132
- ),
133
- bubble: (cls) => (
134
- <Circle
135
- size={6}
136
- fill="currentColor"
137
- className={cn('text-primary-500 shrink-0', cls)}
138
- />
139
- ),
140
- default: () => null,
141
- number: () => null,
142
- }
143
-
144
- export function List({
145
- variant = 'default',
146
- cols = 1,
147
- dense = false,
148
- children,
149
- className,
150
- ...props
151
- }: ListProps) {
152
- const isGrid = cols !== undefined && Number(cols) > 1
153
- const renderIcon = ICON_MAP[variant]
154
- const containerClasses = listVariants({
155
- variant,
156
- cols,
157
- dense,
158
- isGrid,
159
- className,
160
- })
161
-
162
- const Component = variant === 'number' ? 'ol' : 'ul'
163
-
164
- // Handling raw MDX siblings (nested logic)
165
- if (variant === 'default' || variant === 'number') {
166
- return (
167
- <Component className={containerClasses} {...props}>
168
- {children}
169
- </Component>
170
- )
171
- }
172
-
173
- return (
174
- <ul className={containerClasses} {...props}>
175
- {Children.map(children, (child) => {
176
- if (!isValidElement(child)) return child
177
-
178
- const element = child as ReactElement<{ children?: ReactNode }>
179
- const content =
180
- element.type === 'li'
181
- ? element.props.children
182
- : element.props.children || child
183
-
184
- return (
185
- <ListItem icon={renderIcon()} variant={variant} dense={dense}>
186
- {content}
187
- </ListItem>
188
- )
189
- })}
190
- </ul>
191
- )
192
- }
@@ -1,135 +0,0 @@
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 '../../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
- }
@@ -1,68 +0,0 @@
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
- }
@@ -1,19 +0,0 @@
1
- export * as Navbar from './navbar'
2
- export * as NavigationMenu from './navigation-menu'
3
- export * as SearchDialog from './search-dialog'
4
- export * as OnThisPage from './on-this-page'
5
- export * as PageNav from './page-nav'
6
- export * as Tabs from './tabs'
7
- export * as Sidebar from './sidebar'
8
- export * as Breadcrumbs from './breadcrumbs'
9
- export { Button } from './button'
10
- export { ButtonGroup } from './button-group'
11
- export { Menu } from './menu'
12
- export { Popover } from './popover'
13
- export { Tooltip } from './tooltip'
14
- export { Link } from './link'
15
- export { Skeleton } from './skeleton'
16
- export { Separator } from 'react-aria-components'
17
- export { ToggleButton } from 'react-aria-components'
18
-
19
- export { cn } from '../../utils/cn'
@@ -1,114 +0,0 @@
1
- import * as RAC from 'react-aria-components'
2
- import { ChevronDown } from 'lucide-react'
3
- import { cn } from '../../utils/cn'
4
- import type { ComponentBase, CompoundComponent } from './types'
5
-
6
- export interface NavigationMenuItemProps extends ComponentBase {
7
- label: string
8
- }
9
-
10
- export interface NavigationMenuLinkProps
11
- extends Omit<ComponentBase, 'children'> {
12
- href: string
13
- label: string
14
- description?: string
15
- children?:
16
- | React.ReactNode
17
- | ((opts: RAC.MenuItemRenderProps) => React.ReactNode)
18
- }
19
-
20
- export type NavigationMenuComponent = CompoundComponent<
21
- ComponentBase,
22
- {
23
- List: React.FC<ComponentBase>
24
- Item: React.FC<NavigationMenuItemProps>
25
- Link: React.FC<NavigationMenuLinkProps>
26
- }
27
- >
28
-
29
- export const NavigationMenu = ({
30
- children,
31
- className,
32
- ...props
33
- }: ComponentBase) => {
34
- return (
35
- <nav className={cn('relative flex items-center', className)} {...props}>
36
- {children}
37
- </nav>
38
- )
39
- }
40
-
41
- const NavigationMenuList = ({ children, className }: ComponentBase) => {
42
- return (
43
- <div className={cn('flex list-none items-center gap-1', className)}>
44
- {children}
45
- </div>
46
- )
47
- }
48
-
49
- const NavigationMenuItem = ({
50
- children,
51
- label,
52
- className,
53
- }: NavigationMenuItemProps) => {
54
- return (
55
- <RAC.MenuTrigger>
56
- <RAC.Button
57
- className={cn(
58
- 'flex items-center gap-1 rounded-md px-3 py-1.5 text-sm font-medium outline-none transition-colors cursor-pointer',
59
- 'text-text-muted hover:bg-bg-surface hover:text-text-main',
60
- 'focus-visible:ring-2 focus-visible:ring-primary-500/30',
61
- className,
62
- )}
63
- >
64
- {label}
65
- <ChevronDown size={14} className="transition-transform" />
66
- </RAC.Button>
67
- <RAC.Popover
68
- placement="bottom start"
69
- className="entering:animate-in entering:fade-in entering:zoom-in-95 exiting:animate-out exiting:fade-out exiting:zoom-out-95 fill-mode-forwards"
70
- >
71
- <RAC.Menu className="w-56 outline-none rounded-xl border border-border-subtle bg-bg-surface p-2 shadow-xl ring-1 ring-border-strong/5">
72
- {children as any}
73
- </RAC.Menu>
74
- </RAC.Popover>
75
- </RAC.MenuTrigger>
76
- )
77
- }
78
-
79
- const NavigationMenuLink = ({
80
- label,
81
- href,
82
- description,
83
- className,
84
- children,
85
- ...props
86
- }: NavigationMenuLinkProps) => {
87
- return (
88
- <RAC.MenuItem
89
- href={href}
90
- className={cn(
91
- 'block rounded-lg px-3 py-2 text-sm outline-none cursor-pointer transition-colors',
92
- 'hover:bg-bg-muted focus:bg-bg-muted',
93
- className,
94
- )}
95
- {...props}
96
- >
97
- {children || (
98
- <>
99
- <div className="font-semibold text-text-main">{label}</div>
100
- {description && (
101
- <div className="text-xs text-text-muted line-clamp-1 mt-0.5">
102
- {description}
103
- </div>
104
- )}
105
- </>
106
- )}
107
- </RAC.MenuItem>
108
- )
109
- }
110
-
111
- NavigationMenu.Root = NavigationMenu
112
- NavigationMenu.List = NavigationMenuList
113
- NavigationMenu.Item = NavigationMenuItem
114
- NavigationMenu.Link = NavigationMenuLink
@@ -1,76 +0,0 @@
1
- import { useLocation } from 'react-router-dom'
2
- import { Helmet } from 'react-helmet-async'
3
- import { useConfig } from '../../app/config-context'
4
-
5
- interface HeadProps {
6
- siteTitle: string
7
- siteDescription?: string
8
- routes: Array<{ path: string; title: string; description?: string; seo?: Record<string, any> }>
9
- }
10
-
11
- export function Head({ siteTitle, siteDescription, routes }: HeadProps) {
12
- const location = useLocation()
13
- const config = useConfig()
14
-
15
- // Find the current route's metadata
16
- const currentRoute = routes?.find?.((r) => r.path === location.pathname)
17
- const pageTitle = currentRoute?.title
18
- const pageDescription = currentRoute?.description || siteDescription || ''
19
-
20
- const finalTitle = pageTitle ? `${pageTitle} | ${siteTitle}` : siteTitle
21
-
22
- const seo = currentRoute?.seo || {}
23
-
24
- // Merge custom global metatags
25
- const globalMetatags = config?.seo?.metatags || {}
26
-
27
- // Calculate specific ones
28
- const defaultOgImage = config?.seo?.thumbnails?.background
29
- const ogImage = seo['og:image'] || defaultOgImage
30
-
31
- return (
32
- // @ts-ignore
33
- <Helmet>
34
- <title>{finalTitle}</title>
35
- <meta name="description" content={pageDescription} />
36
-
37
- {/* Default OG Tags */}
38
- <meta property="og:title" content={finalTitle} />
39
- <meta property="og:description" content={pageDescription} />
40
- <meta property="og:type" content="article" />
41
- {/* Canonical URL for both <link> and og:url */}
42
- {typeof window !== 'undefined' && <meta property="og:url" content={window.location.href} />}
43
- {typeof window !== 'undefined' && <link rel="canonical" href={window.location.origin + location.pathname} />}
44
-
45
- {/* Default Twitter Card */}
46
- <meta name="twitter:card" content="summary" />
47
- <meta name="twitter:title" content={finalTitle} />
48
- <meta name="twitter:description" content={pageDescription} />
49
- {ogImage && <meta name="twitter:image" content={ogImage} />}
50
- {ogImage && <meta property="og:image" content={ogImage} />}
51
-
52
- {/* Generator */}
53
- <meta name="generator" content="Boltdocs" />
54
-
55
- {/* User-defined global metatags */}
56
- {Object.entries(globalMetatags).map(([key, value]) => {
57
- const isProperty = key.startsWith('og:') || key.startsWith('music:') || key.startsWith('video:') || key.startsWith('article:') || key.startsWith('book:') || key.startsWith('profile:')
58
- return isProperty
59
- ? <meta key={key} property={key} content={value as string} />
60
- : <meta key={key} name={key} content={value as string} />
61
- })}
62
-
63
- {/* Page granular SEO tags (override global) */}
64
- {Object.entries(seo).map(([key, value]) => {
65
- if (key === 'noindex' && value === true) return <meta key="noindex" name="robots" content="noindex" />
66
- if (key === 'robots') return <meta key="robots" name="robots" content={value as string} />
67
- if (key === 'canonical') return <link key="canonical" rel="canonical" href={value as string} />
68
-
69
- const isProperty = key.startsWith('og:') || key.startsWith('music:') || key.startsWith('video:') || key.startsWith('article:') || key.startsWith('book:') || key.startsWith('profile:')
70
- return isProperty
71
- ? <meta key={key} property={key} content={value as string} />
72
- : <meta key={key} name={key} content={value as string} />
73
- })}
74
- </Helmet>
75
- )
76
- }
@@ -1,57 +0,0 @@
1
- import { cn } from '../../utils/cn'
2
- import { Skeleton } from '../primitives/skeleton'
3
-
4
- /**
5
- * A premium loading component that only skeletons the markdown content area.
6
- * Designed to be used as a Suspense fallback within a persistent layout.
7
- */
8
- export function Loading() {
9
- return (
10
- <div
11
- className={cn(
12
- 'w-full h-full relative overflow-y-auto transition-opacity duration-300 animate-fade-in',
13
- )}
14
- >
15
- <div className="mx-auto max-w-(--spacing-content-max) px-4 py-8 space-y-10">
16
- {/* Breadcrumbs */}
17
- <div className="flex gap-2">
18
- <Skeleton className="h-3 w-16" />
19
- <Skeleton className="h-3 w-24" />
20
- </div>
21
-
22
- {/* Page Title */}
23
- <Skeleton className="h-10 w-[60%] sm:h-12" />
24
-
25
- {/* Intro Paragraph */}
26
- <div className="space-y-3">
27
- <Skeleton className="h-4 w-full" />
28
- <Skeleton className="h-4 w-[95%]" />
29
- <Skeleton className="h-4 w-[40%]" />
30
- </div>
31
-
32
- {/* Section 1 */}
33
- <div className="space-y-6 pt-4">
34
- <Skeleton className="h-7 w-32" />
35
- <div className="space-y-3">
36
- <Skeleton className="h-4 w-full" />
37
- <Skeleton className="h-4 w-[98%]" />
38
- <Skeleton className="h-4 w-[92%]" />
39
- <Skeleton className="h-4 w-[60%]" />
40
- </div>
41
- </div>
42
-
43
- {/* Code Block Placeholder */}
44
- <Skeleton className="h-32 w-full rounded-lg bg-bg-muted/50" />
45
-
46
- {/* Section 2 */}
47
- <div className="space-y-6 pt-4">
48
- <Skeleton className="h-7 w-48" />
49
- <div className="space-y-3">
50
- <Skeleton className="h-4 w-full" />
51
- <Skeleton className="h-4 w-[85%]" />
52
- </div>
53
- </div>
54
- </div>
55
- </div>
56
- )
57
- }
@@ -1,25 +0,0 @@
1
- import { Zap } from 'lucide-react'
2
-
3
- export function PoweredBy() {
4
- return (
5
- <div className="flex items-center justify-center mt-10 mb-4 px-4 w-full">
6
- <a
7
- href="https://github.com/jesusalcaladev/boltdocs"
8
- target="_blank"
9
- rel="noopener noreferrer"
10
- className="group relative flex items-center gap-2 px-4 py-2 rounded-full border border-border-subtle bg-bg-surface/50 backdrop-blur-md transition-all duration-300 hover:border-primary-500/50 hover:bg-bg-surface hover:shadow-xl hover:shadow-primary-500/5 select-none"
11
- >
12
- <Zap
13
- className="w-3.5 h-3.5 text-text-muted group-hover:text-primary-500 transition-colors duration-300"
14
- fill="currentColor"
15
- />
16
- <span className="text-[11px] font-medium text-text-muted group-hover:text-text-main transition-colors duration-300 tracking-wide">
17
- Powered by{' '}
18
- <strong className="font-bold text-text-main/80 group-hover:text-text-main">
19
- Boltdocs
20
- </strong>
21
- </span>
22
- </a>
23
- </div>
24
- )
25
- }