boltdocs 2.2.0 → 2.4.1
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.
- package/CHANGELOG.md +24 -0
- package/bin/boltdocs.js +2 -2
- package/dist/base-ui/index.d.mts +4 -4
- package/dist/base-ui/index.d.ts +4 -4
- package/dist/base-ui/index.js +1 -1
- package/dist/base-ui/index.mjs +1 -1
- package/dist/{cache-CRAZ55X7.mjs → cache-P6WK424C.mjs} +1 -1
- package/dist/chunk-2DI3OGHV.mjs +1 -0
- package/dist/chunk-2Z5T6EAU.mjs +1 -0
- package/dist/chunk-64AJ5QLT.mjs +1 -0
- package/dist/chunk-DDX52BX4.mjs +1 -0
- package/dist/chunk-HRZDSFR5.mjs +1 -0
- package/dist/chunk-PPVDMDEL.mjs +1 -0
- package/dist/chunk-UBE4CKOA.mjs +1 -0
- package/dist/chunk-UWT4AJTH.mjs +73 -0
- package/dist/chunk-WWJ7WKDI.mjs +1 -0
- package/dist/chunk-Y4RRHPXC.mjs +1 -0
- package/dist/client/index.d.mts +15 -21
- package/dist/client/index.d.ts +15 -21
- package/dist/client/index.js +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/client/ssr.js +1 -1
- package/dist/client/ssr.mjs +1 -1
- package/dist/client/types.d.mts +1 -1
- package/dist/client/types.d.ts +1 -1
- package/dist/client/types.js +1 -1
- package/dist/{copy-markdown-CbS8X-qe.d.mts → copy-markdown--9yjpbyy.d.mts} +1 -1
- package/dist/{copy-markdown-C-90ixSe.d.ts → copy-markdown-l2MYkcG7.d.ts} +1 -1
- package/dist/hooks/index.d.mts +8 -16
- package/dist/hooks/index.d.ts +8 -16
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/index.d.ts +1 -1
- package/dist/{loading-chS3pm9W.d.ts → loading-BwUos0wZ.d.mts} +5 -16
- package/dist/{loading-BqGrFWO5.d.mts → loading-nlnUD01v.d.ts} +5 -16
- package/dist/mdx/index.d.mts +4 -2
- package/dist/mdx/index.d.ts +4 -2
- package/dist/mdx/index.js +1 -1
- package/dist/mdx/index.mjs +1 -1
- package/dist/node/cli-entry.js +25 -22
- package/dist/node/cli-entry.mjs +5 -1
- package/dist/node/index.d.mts +0 -9
- package/dist/node/index.d.ts +0 -9
- package/dist/node/index.js +14 -15
- package/dist/node/index.mjs +1 -1
- package/dist/primitives/index.d.mts +13 -22
- package/dist/primitives/index.d.ts +13 -22
- package/dist/primitives/index.js +1 -1
- package/dist/primitives/index.mjs +1 -1
- package/dist/search-dialog-OONKKC5H.mjs +1 -0
- package/dist/{types-j7jvWsJj.d.ts → types-opDA2E9-.d.mts} +4 -11
- package/dist/{types-j7jvWsJj.d.mts → types-opDA2E9-.d.ts} +4 -11
- package/dist/{use-routes-Cd806kGw.d.ts → use-routes-DNwgTRpU.d.ts} +1 -1
- package/dist/{use-routes-DDL0_jkQ.d.mts → use-routes-DrT80Eom.d.mts} +1 -1
- package/package.json +2 -1
- package/src/client/app/index.tsx +20 -9
- package/src/client/app/mdx-components-context.tsx +2 -2
- package/src/client/app/mdx-page.tsx +0 -1
- package/src/client/app/scroll-handler.tsx +21 -10
- package/src/client/app/theme-context.tsx +14 -7
- package/src/client/components/default-layout.tsx +6 -4
- package/src/client/components/docs-layout.tsx +34 -4
- package/src/client/components/icons-dev.tsx +154 -0
- package/src/client/components/mdx/code-block.tsx +57 -5
- package/src/client/components/mdx/component-preview.tsx +1 -0
- package/src/client/components/mdx/file-tree.tsx +35 -0
- package/src/client/components/primitives/helpers/observer.ts +30 -39
- package/src/client/components/primitives/index.ts +1 -0
- package/src/client/components/primitives/menu.tsx +18 -12
- package/src/client/components/primitives/navbar.tsx +34 -93
- package/src/client/components/primitives/on-this-page.tsx +7 -161
- package/src/client/components/primitives/popover.tsx +1 -2
- package/src/client/components/primitives/search-dialog.tsx +4 -4
- package/src/client/components/primitives/sidebar.tsx +3 -2
- package/src/client/components/primitives/skeleton.tsx +26 -0
- package/src/client/components/ui-base/copy-markdown.tsx +4 -10
- package/src/client/components/ui-base/index.ts +0 -1
- package/src/client/components/ui-base/loading.tsx +43 -73
- package/src/client/components/ui-base/navbar.tsx +18 -15
- package/src/client/components/ui-base/page-nav.tsx +2 -1
- package/src/client/components/ui-base/powered-by.tsx +4 -1
- package/src/client/components/ui-base/search-dialog.tsx +16 -5
- package/src/client/components/ui-base/sidebar.tsx +4 -2
- package/src/client/hooks/use-i18n.ts +3 -2
- package/src/client/hooks/use-localized-to.ts +6 -5
- package/src/client/hooks/use-navbar.ts +37 -6
- package/src/client/hooks/use-page-nav.ts +27 -6
- package/src/client/hooks/use-routes.ts +2 -1
- package/src/client/hooks/use-search.ts +81 -59
- package/src/client/hooks/use-sidebar.ts +2 -1
- package/src/client/index.ts +0 -1
- package/src/client/store/use-boltdocs-store.ts +6 -5
- package/src/client/theme/neutral.css +31 -3
- package/src/client/types.ts +2 -2
- package/src/node/{cli.ts → cli/build.ts} +17 -23
- package/src/node/cli/dev.ts +22 -0
- package/src/node/cli/doctor.ts +243 -0
- package/src/node/cli/index.ts +9 -0
- package/src/node/cli/ui.ts +54 -0
- package/src/node/cli-entry.ts +16 -16
- package/src/node/config.ts +1 -15
- package/src/node/mdx/cache.ts +1 -1
- package/src/node/mdx/index.ts +2 -0
- package/src/node/mdx/rehype-shiki.ts +9 -0
- package/src/node/mdx/remark-code-meta.ts +35 -0
- package/src/node/mdx/remark-shiki.ts +1 -1
- package/src/node/plugin/entry.ts +22 -15
- package/src/node/plugin/index.ts +46 -14
- package/src/node/routes/parser.ts +12 -9
- package/src/node/search/index.ts +55 -0
- package/src/node/ssg/index.ts +83 -15
- package/src/node/ssg/robots.ts +7 -4
- package/dist/chunk-5D6XPYQ3.mjs +0 -74
- package/dist/chunk-6QXCKZAT.mjs +0 -1
- package/dist/chunk-H4M6P3DM.mjs +0 -1
- package/dist/chunk-JXHNX2WN.mjs +0 -1
- package/dist/chunk-MZBG4N4W.mjs +0 -1
- package/dist/chunk-Q3MLYTIQ.mjs +0 -1
- package/dist/chunk-RSII2UPE.mjs +0 -1
- package/dist/chunk-ZK2266IZ.mjs +0 -1
- package/dist/chunk-ZRJ55GGF.mjs +0 -1
- package/dist/search-dialog-MA5AISC7.mjs +0 -1
- package/src/client/components/ui-base/progress-bar.tsx +0 -67
|
@@ -20,7 +20,7 @@ export function MenuTrigger(props: MenuTriggerProps) {
|
|
|
20
20
|
return (
|
|
21
21
|
<RAC.MenuTrigger {...props}>
|
|
22
22
|
{trigger as any}
|
|
23
|
-
<Popover placement={props.placement} className="min-w-
|
|
23
|
+
<Popover placement={props.placement} className="min-w-35">
|
|
24
24
|
{menu as any}
|
|
25
25
|
</Popover>
|
|
26
26
|
</RAC.MenuTrigger>
|
|
@@ -52,7 +52,10 @@ export function Menu<T extends object>(props: RAC.MenuProps<T>) {
|
|
|
52
52
|
<RAC.Menu
|
|
53
53
|
{...props}
|
|
54
54
|
className={RAC.composeRenderProps(props.className, (className) =>
|
|
55
|
-
cn(
|
|
55
|
+
cn(
|
|
56
|
+
'p-1.5 outline-none max-h-[inherit] overflow-auto max-w-75',
|
|
57
|
+
className,
|
|
58
|
+
),
|
|
56
59
|
)}
|
|
57
60
|
/>
|
|
58
61
|
)
|
|
@@ -74,11 +77,14 @@ export function MenuItem(props: RAC.MenuItemProps) {
|
|
|
74
77
|
props.className,
|
|
75
78
|
(className, { isFocused, isPressed, isDisabled }) =>
|
|
76
79
|
cn(
|
|
77
|
-
'group relative flex flex-row items-center gap-2
|
|
78
|
-
'text-text-main text-[
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
'group relative flex flex-row items-center gap-2 px-2 py-1 rounded-lg outline-none cursor-default hover:cursor-pointer transition-none',
|
|
81
|
+
'text-text-main text-[12px]',
|
|
82
|
+
{
|
|
83
|
+
'bg-bg-surface-elevated text-primary-600 ring-1 ring-border-strong/5':
|
|
84
|
+
isFocused,
|
|
85
|
+
'bg-bg-surface-elevanted': isPressed,
|
|
86
|
+
'opacity-40 grayscale pointer-events-none': isDisabled,
|
|
87
|
+
},
|
|
82
88
|
className,
|
|
83
89
|
),
|
|
84
90
|
)}
|
|
@@ -88,20 +94,20 @@ export function MenuItem(props: RAC.MenuItemProps) {
|
|
|
88
94
|
(children, { selectionMode, isSelected, hasSubmenu }) => (
|
|
89
95
|
<>
|
|
90
96
|
{selectionMode !== 'none' && (
|
|
91
|
-
<span className="flex items-center
|
|
97
|
+
<span className="flex items-center size-4 shrink-0 justify-center">
|
|
92
98
|
{isSelected && selectionMode === 'multiple' && (
|
|
93
|
-
<Check className="
|
|
99
|
+
<Check className="size-3.5 stroke-[2.5px] text-primary-500 animate-in zoom-in-50 duration-200" />
|
|
94
100
|
)}
|
|
95
101
|
{isSelected && selectionMode === 'single' && (
|
|
96
|
-
<Dot className="
|
|
102
|
+
<Dot className="size-5 text-primary-500 animate-in zoom-in-50 duration-200" />
|
|
97
103
|
)}
|
|
98
104
|
</span>
|
|
99
105
|
)}
|
|
100
|
-
<div className="flex
|
|
106
|
+
<div className="flex flex-row w-full transition-colors items-center gap-2 py-1 px-1">
|
|
101
107
|
{children}
|
|
102
108
|
</div>
|
|
103
109
|
{hasSubmenu && (
|
|
104
|
-
<ChevronRight className="
|
|
110
|
+
<ChevronRight className="size-4 ml-auto text-text-muted group-focused:text-primary-500/70 transition-colors" />
|
|
105
111
|
)}
|
|
106
112
|
</>
|
|
107
113
|
),
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { type ReactNode, useState, useEffect } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
Button,
|
|
4
|
-
Separator,
|
|
5
|
-
ToggleButton,
|
|
6
|
-
Link,
|
|
7
|
-
Menu,
|
|
8
|
-
MenuItem,
|
|
9
|
-
MenuTrigger,
|
|
10
|
-
cn,
|
|
11
|
-
} from './index'
|
|
2
|
+
import { Separator, ToggleButton, Link, cn } from './index'
|
|
12
3
|
import { Button as ButtonRAC } from 'react-aria-components'
|
|
13
|
-
import { Search, Sun, Moon, ExternalLink
|
|
4
|
+
import { Search, Sun, Moon, ExternalLink } from 'lucide-react'
|
|
14
5
|
import * as IconsSocials from '@components/icons-dev'
|
|
15
6
|
import type { ComponentBase } from './types'
|
|
16
7
|
import type { BoltdocsSocialLink } from '@node/config'
|
|
@@ -39,21 +30,6 @@ export interface NavbarThemeProps {
|
|
|
39
30
|
onThemeChange: (isSelected: boolean) => void
|
|
40
31
|
}
|
|
41
32
|
|
|
42
|
-
export interface NavbarMenuProps extends ComponentBase {
|
|
43
|
-
label: ReactNode
|
|
44
|
-
icon?: ReactNode
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface NavbarVersionProps extends ComponentBase {
|
|
48
|
-
current: string
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface NavbarItemProps extends Omit<ComponentBase, 'children'> {
|
|
52
|
-
label: string
|
|
53
|
-
onPress?: () => void
|
|
54
|
-
isCurrent?: boolean
|
|
55
|
-
}
|
|
56
|
-
|
|
57
33
|
export interface NavbarSocialsProps extends ComponentBase {
|
|
58
34
|
icon: string
|
|
59
35
|
link: string
|
|
@@ -92,13 +68,25 @@ export const NavbarContent = ({ children, className }: ComponentBase) => {
|
|
|
92
68
|
|
|
93
69
|
export const NavbarLeft = ({ children, className }: ComponentBase) => {
|
|
94
70
|
return (
|
|
95
|
-
<div
|
|
71
|
+
<div
|
|
72
|
+
className={cn(
|
|
73
|
+
'flex flex-1 items-center justify-start gap-4 min-w-0',
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</div>
|
|
96
79
|
)
|
|
97
80
|
}
|
|
98
81
|
|
|
99
82
|
export const NavbarRight = ({ children, className }: ComponentBase) => {
|
|
100
83
|
return (
|
|
101
|
-
<div
|
|
84
|
+
<div
|
|
85
|
+
className={cn(
|
|
86
|
+
'flex flex-1 items-center justify-end gap-2 md:gap-4 min-w-0',
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
102
90
|
{children}
|
|
103
91
|
</div>
|
|
104
92
|
)
|
|
@@ -108,7 +96,7 @@ export const NavbarCenter = ({ children, className }: ComponentBase) => {
|
|
|
108
96
|
return (
|
|
109
97
|
<div
|
|
110
98
|
className={cn(
|
|
111
|
-
'hidden lg:flex flex-1 justify-center items-center gap-4 px-4',
|
|
99
|
+
'hidden lg:flex flex-1 justify-center items-center gap-4 px-4 min-w-0 w-full',
|
|
112
100
|
className,
|
|
113
101
|
)}
|
|
114
102
|
>
|
|
@@ -144,14 +132,16 @@ export const NavbarLogo = ({
|
|
|
144
132
|
|
|
145
133
|
export const NavbarTitle = ({ children, className }: ComponentBase) => {
|
|
146
134
|
return (
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
135
|
+
<Link href="/">
|
|
136
|
+
<span
|
|
137
|
+
className={cn(
|
|
138
|
+
'text-lg font-bold tracking-tight hidden sm:inline-block',
|
|
139
|
+
className,
|
|
140
|
+
)}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
</span>
|
|
144
|
+
</Link>
|
|
155
145
|
)
|
|
156
146
|
}
|
|
157
147
|
|
|
@@ -180,10 +170,10 @@ export const NavbarLink = ({
|
|
|
180
170
|
href={href}
|
|
181
171
|
target={to === 'external' ? '_blank' : undefined}
|
|
182
172
|
className={cn(
|
|
183
|
-
'transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm',
|
|
173
|
+
'transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm',
|
|
184
174
|
{
|
|
185
|
-
'text-primary-500
|
|
186
|
-
'text-text-muted hover:text-text-main
|
|
175
|
+
'text-primary-500': active,
|
|
176
|
+
'text-text-muted hover:text-text-main': !active,
|
|
187
177
|
},
|
|
188
178
|
className,
|
|
189
179
|
)}
|
|
@@ -214,9 +204,9 @@ export const NavbarSearchTrigger = ({
|
|
|
214
204
|
onPress={onPress}
|
|
215
205
|
className={cn(
|
|
216
206
|
'flex items-center gap-2 rounded-full border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-muted outline-none cursor-pointer',
|
|
217
|
-
'transition-
|
|
207
|
+
'transition-all duration-200 hover:border-border-strong hover:text-text-main hover:bg-bg-muted hover:shadow-sm active:scale-[0.98]',
|
|
218
208
|
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
219
|
-
'w-full max-w-[
|
|
209
|
+
'w-full max-w-[720px] justify-between',
|
|
220
210
|
className,
|
|
221
211
|
)}
|
|
222
212
|
>
|
|
@@ -246,8 +236,8 @@ export const NavbarTheme = ({
|
|
|
246
236
|
isSelected={theme === 'dark'}
|
|
247
237
|
onChange={onThemeChange}
|
|
248
238
|
className={cn(
|
|
249
|
-
'rounded-md p-2 text-text-muted outline-none cursor-pointer
|
|
250
|
-
'hover:bg-bg-surface hover:text-text-main',
|
|
239
|
+
'rounded-md p-2 text-text-muted outline-none cursor-pointer',
|
|
240
|
+
'transition-all duration-300 hover:bg-bg-surface hover:text-text-main hover:rotate-12 active:scale-90',
|
|
251
241
|
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
252
242
|
className,
|
|
253
243
|
)}
|
|
@@ -258,54 +248,6 @@ export const NavbarTheme = ({
|
|
|
258
248
|
)
|
|
259
249
|
}
|
|
260
250
|
|
|
261
|
-
export const NavbarMenu = ({
|
|
262
|
-
label,
|
|
263
|
-
children,
|
|
264
|
-
className,
|
|
265
|
-
icon,
|
|
266
|
-
}: NavbarMenuProps) => {
|
|
267
|
-
return (
|
|
268
|
-
<MenuTrigger placement="bottom end">
|
|
269
|
-
<Button
|
|
270
|
-
variant="ghost"
|
|
271
|
-
className={cn(
|
|
272
|
-
'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-text-muted outline-none cursor-pointer transition-colors',
|
|
273
|
-
'hover:bg-bg-surface hover:text-text-main',
|
|
274
|
-
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
275
|
-
className,
|
|
276
|
-
)}
|
|
277
|
-
>
|
|
278
|
-
{icon && <span className="flex items-center shrink-0">{icon}</span>}
|
|
279
|
-
<span className="text-[13px] font-bold uppercase tracking-wide">
|
|
280
|
-
{label}
|
|
281
|
-
</span>
|
|
282
|
-
<ChevronDown size={14} className="ml-0.5 opacity-50" />
|
|
283
|
-
</Button>
|
|
284
|
-
<Menu className="min-w-[180px]">{children as any}</Menu>
|
|
285
|
-
</MenuTrigger>
|
|
286
|
-
)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export const NavbarItem = ({
|
|
290
|
-
label,
|
|
291
|
-
className,
|
|
292
|
-
onPress,
|
|
293
|
-
isCurrent,
|
|
294
|
-
}: NavbarItemProps) => {
|
|
295
|
-
return (
|
|
296
|
-
<MenuItem
|
|
297
|
-
onAction={onPress}
|
|
298
|
-
className={cn(
|
|
299
|
-
isCurrent &&
|
|
300
|
-
'bg-primary-500 text-white font-medium hover:bg-primary-600 focus:bg-primary-600 focus:text-white',
|
|
301
|
-
className,
|
|
302
|
-
)}
|
|
303
|
-
>
|
|
304
|
-
{label}
|
|
305
|
-
</MenuItem>
|
|
306
|
-
)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
251
|
export const Icon = ({ name }: { name: BoltdocsSocialLink['icon'] }) => {
|
|
310
252
|
if (name === 'github') return <IconsSocials.Github />
|
|
311
253
|
if (name === 'discord') return <IconsSocials.Discord />
|
|
@@ -355,7 +297,6 @@ export default {
|
|
|
355
297
|
Link: NavbarLink,
|
|
356
298
|
SearchTrigger: NavbarSearchTrigger,
|
|
357
299
|
Theme: NavbarTheme,
|
|
358
|
-
Item: NavbarItem,
|
|
359
300
|
Socials: NavbarSocials,
|
|
360
301
|
Split: NavbarSplit,
|
|
361
302
|
Content: NavbarContent,
|
|
@@ -12,8 +12,8 @@ import React, {
|
|
|
12
12
|
import scrollIntoView from 'scroll-into-view-if-needed'
|
|
13
13
|
import { cn } from '../../utils/cn'
|
|
14
14
|
import { useOnChange } from '../../utils/use-on-change'
|
|
15
|
-
import type { ComponentBase
|
|
16
|
-
import { getItemId } from './helpers/observer'
|
|
15
|
+
import type { ComponentBase } from './types'
|
|
16
|
+
import { getItemId, Observer } from './helpers/observer'
|
|
17
17
|
|
|
18
18
|
export interface TOCItemType {
|
|
19
19
|
title: ReactNode
|
|
@@ -74,120 +74,6 @@ export interface OnThisPageIndicatorProps extends ComponentBase {
|
|
|
74
74
|
const ItemsContext = createContext<TOCItemInfo[] | null>(null)
|
|
75
75
|
const ScrollContext = createContext<RefObject<HTMLElement | null> | null>(null)
|
|
76
76
|
|
|
77
|
-
class Observer {
|
|
78
|
-
items: TOCItemInfo[] = []
|
|
79
|
-
single = false
|
|
80
|
-
private observer: IntersectionObserver | null = null
|
|
81
|
-
onChange?: () => void
|
|
82
|
-
|
|
83
|
-
private callback(entries: IntersectionObserverEntry[]) {
|
|
84
|
-
if (entries.length === 0) return
|
|
85
|
-
|
|
86
|
-
let hasActive = false
|
|
87
|
-
this.items = this.items.map((item) => {
|
|
88
|
-
const entry = entries.find((entry) => entry.target.id === item.id)
|
|
89
|
-
let active = entry ? entry.isIntersecting : item.active && !item.fallback
|
|
90
|
-
if (this.single && hasActive) active = false
|
|
91
|
-
|
|
92
|
-
if (item.active !== active) {
|
|
93
|
-
item = {
|
|
94
|
-
...item,
|
|
95
|
-
t: Date.now(),
|
|
96
|
-
active,
|
|
97
|
-
fallback: false,
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (active) hasActive = true
|
|
102
|
-
return item
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
if (!hasActive && entries[0].rootBounds) {
|
|
106
|
-
const viewTop = entries[0].rootBounds.top
|
|
107
|
-
let min = Number.MAX_VALUE
|
|
108
|
-
let fallbackIdx = -1
|
|
109
|
-
|
|
110
|
-
for (let i = 0; i < this.items.length; i++) {
|
|
111
|
-
const element = document.getElementById(this.items[i].id)
|
|
112
|
-
if (!element) continue
|
|
113
|
-
|
|
114
|
-
const d = Math.abs(viewTop - element.getBoundingClientRect().top)
|
|
115
|
-
if (d < min) {
|
|
116
|
-
fallbackIdx = i
|
|
117
|
-
min = d
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (fallbackIdx !== -1) {
|
|
122
|
-
this.items[fallbackIdx] = {
|
|
123
|
-
...this.items[fallbackIdx],
|
|
124
|
-
active: true,
|
|
125
|
-
fallback: true,
|
|
126
|
-
t: Date.now(),
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this.onChange?.()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
setItems(newItems: TOCItemType[]) {
|
|
135
|
-
const observer = this.observer
|
|
136
|
-
if (observer) {
|
|
137
|
-
for (const item of this.items) {
|
|
138
|
-
const element = document.getElementById(item.id)
|
|
139
|
-
if (!element) continue
|
|
140
|
-
observer.unobserve(element)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
this.items = []
|
|
145
|
-
for (const item of newItems) {
|
|
146
|
-
const id = getItemId(item.url)
|
|
147
|
-
if (!id) continue
|
|
148
|
-
|
|
149
|
-
this.items.push({
|
|
150
|
-
id,
|
|
151
|
-
active: false,
|
|
152
|
-
fallback: false,
|
|
153
|
-
t: 0,
|
|
154
|
-
original: item,
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
this.watchItems()
|
|
158
|
-
|
|
159
|
-
// In an SPA, the TOC might update before the MDX content is in the DOM.
|
|
160
|
-
// We perform a few delayed scans to ensure we catch those elements.
|
|
161
|
-
if (typeof window !== 'undefined') {
|
|
162
|
-
setTimeout(() => this.watchItems(), 100)
|
|
163
|
-
setTimeout(() => this.watchItems(), 500)
|
|
164
|
-
setTimeout(() => this.watchItems(), 1000)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
this.onChange?.()
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
watch(options?: IntersectionObserverInit) {
|
|
171
|
-
if (this.observer) return
|
|
172
|
-
this.observer = new IntersectionObserver(this.callback.bind(this), options)
|
|
173
|
-
this.watchItems()
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private watchItems() {
|
|
177
|
-
if (!this.observer) return
|
|
178
|
-
for (const item of this.items) {
|
|
179
|
-
const element = document.getElementById(item.id)
|
|
180
|
-
if (!element) continue
|
|
181
|
-
this.observer.observe(element)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
unwatch() {
|
|
186
|
-
this.observer?.disconnect()
|
|
187
|
-
this.observer = null
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
77
|
export function useItems() {
|
|
192
78
|
const ctx = use(ItemsContext)
|
|
193
79
|
if (!ctx)
|
|
@@ -197,40 +83,6 @@ export function useItems() {
|
|
|
197
83
|
return ctx
|
|
198
84
|
}
|
|
199
85
|
|
|
200
|
-
export function useScrollStatus(ref: RefObject<HTMLElement | null>) {
|
|
201
|
-
const [status, setStatus] = useState({
|
|
202
|
-
isOverflowing: false,
|
|
203
|
-
isAtBottom: false,
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
const el = ref.current
|
|
208
|
-
if (!el) return
|
|
209
|
-
|
|
210
|
-
const checkStatus = () => {
|
|
211
|
-
const isOverflowing = el.scrollHeight > el.clientHeight
|
|
212
|
-
// We use a 2px threshold for floating point math issues
|
|
213
|
-
const isAtBottom = el.scrollHeight - el.scrollTop <= el.clientHeight + 2
|
|
214
|
-
setStatus({ isOverflowing, isAtBottom })
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
checkStatus()
|
|
218
|
-
el.addEventListener('scroll', checkStatus, { passive: true })
|
|
219
|
-
window.addEventListener('resize', checkStatus)
|
|
220
|
-
|
|
221
|
-
const mutationObserver = new MutationObserver(checkStatus)
|
|
222
|
-
mutationObserver.observe(el, { childList: true, subtree: true })
|
|
223
|
-
|
|
224
|
-
return () => {
|
|
225
|
-
el.removeEventListener('scroll', checkStatus)
|
|
226
|
-
window.removeEventListener('resize', checkStatus)
|
|
227
|
-
mutationObserver.disconnect()
|
|
228
|
-
}
|
|
229
|
-
}, [ref])
|
|
230
|
-
|
|
231
|
-
return status
|
|
232
|
-
}
|
|
233
|
-
|
|
234
86
|
export function useActiveAnchor(): string | undefined {
|
|
235
87
|
const items = useItems()
|
|
236
88
|
return useMemo(() => {
|
|
@@ -282,9 +134,11 @@ export function AnchorProvider({
|
|
|
282
134
|
}, [observer, toc])
|
|
283
135
|
|
|
284
136
|
useEffect(() => {
|
|
137
|
+
// We use a rootMargin that acts as an activation "line" near the top.
|
|
138
|
+
// headings are "intersecting" (active=true) when they are BELOW this line.
|
|
285
139
|
observer.watch({
|
|
286
|
-
rootMargin: '
|
|
287
|
-
threshold: 0
|
|
140
|
+
rootMargin: '-100px 0% 0% 0%',
|
|
141
|
+
threshold: 0,
|
|
288
142
|
})
|
|
289
143
|
observer.onChange = () => setItems([...observer.items])
|
|
290
144
|
|
|
@@ -339,18 +193,10 @@ export const OnThisPageContent = ({
|
|
|
339
193
|
|
|
340
194
|
useImperativeHandle(ref, () => internalRef.current!)
|
|
341
195
|
|
|
342
|
-
const { isOverflowing, isAtBottom } = useScrollStatus(internalRef)
|
|
343
|
-
|
|
344
196
|
return (
|
|
345
197
|
<div
|
|
346
198
|
ref={internalRef}
|
|
347
|
-
className={cn(
|
|
348
|
-
'relative overflow-y-auto boltdocs-otp-content',
|
|
349
|
-
isOverflowing &&
|
|
350
|
-
!isAtBottom &&
|
|
351
|
-
'mask-[linear-gradient(to_bottom,black_85%,transparent_100%)]',
|
|
352
|
-
className,
|
|
353
|
-
)}
|
|
199
|
+
className={cn('relative overflow-y-auto boltdocs-otp-content', className)}
|
|
354
200
|
{...props}
|
|
355
201
|
>
|
|
356
202
|
{children}
|
|
@@ -24,8 +24,7 @@ export const Popover = ({
|
|
|
24
24
|
{...props}
|
|
25
25
|
className={RAC.composeRenderProps(className, (className) =>
|
|
26
26
|
cn(
|
|
27
|
-
'z-50 overflow-auto rounded-xl border border-border-subtle bg-bg-surface/80 shadow-xl backdrop-blur-md outline-none',
|
|
28
|
-
'entering:animate-in entering:fade-in entering:zoom-in-95 exiting:animate-out exiting:fade-out exiting:zoom-out-95 fill-mode-forwards',
|
|
27
|
+
'z-50 overflow-auto rounded-xl border border-border-subtle bg-bg-surface/80 shadow-xl backdrop-blur-md outline-none transition-none',
|
|
29
28
|
className,
|
|
30
29
|
),
|
|
31
30
|
)}
|
|
@@ -50,12 +50,12 @@ export const SearchDialogRoot = ({
|
|
|
50
50
|
)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export const SearchDialogAutocomplete = ({
|
|
53
|
+
export const SearchDialogAutocomplete = <T extends object>({
|
|
54
54
|
children,
|
|
55
55
|
className,
|
|
56
56
|
onSelectionChange,
|
|
57
57
|
...props
|
|
58
|
-
}: RAC.AutocompleteProps<
|
|
58
|
+
}: RAC.AutocompleteProps<T> & {
|
|
59
59
|
className?: string
|
|
60
60
|
onSelectionChange?: (key: RAC.Key) => void
|
|
61
61
|
}) => {
|
|
@@ -98,11 +98,11 @@ export const SearchDialogInput = ({
|
|
|
98
98
|
)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
export const SearchDialogList = ({
|
|
101
|
+
export const SearchDialogList = <T extends object>({
|
|
102
102
|
children,
|
|
103
103
|
className,
|
|
104
104
|
...props
|
|
105
|
-
}: RAC.ListBoxProps<
|
|
105
|
+
}: RAC.ListBoxProps<T> & { className?: string }) => {
|
|
106
106
|
return (
|
|
107
107
|
<RAC.ListBox
|
|
108
108
|
{...props}
|
|
@@ -122,11 +122,12 @@ export const SidebarLink = ({
|
|
|
122
122
|
<Link
|
|
123
123
|
href={href}
|
|
124
124
|
className={cn(
|
|
125
|
-
'group flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm outline-none
|
|
125
|
+
'group flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm outline-none',
|
|
126
|
+
'transition-all duration-200 ease-in-out',
|
|
126
127
|
'focus-visible:ring-2 focus-visible:ring-primary-500/30',
|
|
127
128
|
active
|
|
128
129
|
? 'bg-primary-500/10 text-primary-500 font-medium'
|
|
129
|
-
: 'text-text-muted hover:bg-bg-surface hover:text-text-main',
|
|
130
|
+
: 'text-text-muted hover:bg-bg-surface hover:text-text-main hover:translate-x-1',
|
|
130
131
|
className,
|
|
131
132
|
)}
|
|
132
133
|
>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { cn } from '@client/utils/cn'
|
|
2
|
+
|
|
3
|
+
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
variant?: 'rect' | 'circle'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A flexible skeleton component that mimics the shape of content
|
|
9
|
+
* while it is loading. Features a smooth pulse animation.
|
|
10
|
+
*/
|
|
11
|
+
export function Skeleton({
|
|
12
|
+
className,
|
|
13
|
+
variant = 'rect',
|
|
14
|
+
...props
|
|
15
|
+
}: SkeletonProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className={cn(
|
|
19
|
+
'animate-pulse bg-bg-muted',
|
|
20
|
+
variant === 'circle' ? 'rounded-full' : 'rounded-md',
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -80,25 +80,19 @@ export function CopyMarkdown({ content, mdxRaw, config }: CopyMarkdownProps) {
|
|
|
80
80
|
)}
|
|
81
81
|
/>
|
|
82
82
|
<Menu className="w-52">
|
|
83
|
-
<MenuItem
|
|
84
|
-
onAction={handleCopy}
|
|
85
|
-
className="flex flex-row items-start gap-2.5 group"
|
|
86
|
-
>
|
|
83
|
+
<MenuItem onAction={handleCopy}>
|
|
87
84
|
<Copy
|
|
88
85
|
size={16}
|
|
89
|
-
className="
|
|
86
|
+
className="size-4 mt-0.5 text-text-muted group-hover:text-primary-500"
|
|
90
87
|
/>
|
|
91
88
|
<span className="font-medium text-[0.8125rem]">
|
|
92
89
|
Copy Markdown
|
|
93
90
|
</span>
|
|
94
91
|
</MenuItem>
|
|
95
|
-
<MenuItem
|
|
96
|
-
onAction={handleOpenRaw}
|
|
97
|
-
className="flex flex-row items-start gap-2.5 group"
|
|
98
|
-
>
|
|
92
|
+
<MenuItem onAction={handleOpenRaw}>
|
|
99
93
|
<ExternalLink
|
|
100
94
|
size={16}
|
|
101
|
-
className="
|
|
95
|
+
className="size-4 mt-0.5 text-text-muted group-hover:text-primary-500"
|
|
102
96
|
/>
|
|
103
97
|
<span className="font-medium text-[0.8125rem]">
|
|
104
98
|
View as Markdown
|
|
@@ -10,7 +10,6 @@ export { NotFound } from './not-found'
|
|
|
10
10
|
export { OnThisPage } from './on-this-page'
|
|
11
11
|
export { PageNav } from './page-nav'
|
|
12
12
|
export { PoweredBy } from './powered-by'
|
|
13
|
-
export { ProgressBar } from './progress-bar'
|
|
14
13
|
export { SearchDialog } from './search-dialog'
|
|
15
14
|
export { Sidebar } from './sidebar'
|
|
16
15
|
export { Tabs } from './tabs'
|