boltdocs 2.6.2 → 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.
- package/bin/boltdocs.js +0 -1
- package/dist/cache-CQKlT4fI.mjs +6 -0
- package/dist/cache-DorPMFgW.cjs +6 -0
- package/dist/cards-BLoSiRuL.d.ts +30 -0
- package/dist/cards-CQn9mXZS.d.cts +30 -0
- package/dist/chunk-Ds5LZdWN.cjs +6 -0
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.cts +167 -1338
- package/dist/client/index.d.ts +166 -1337
- package/dist/client/index.js +1 -1
- package/dist/{package-CFP44vfn.cjs → client/mdx.cjs} +1 -1
- package/dist/client/mdx.d.cts +128 -0
- package/dist/client/mdx.d.ts +129 -0
- package/dist/client/mdx.js +6 -0
- package/dist/client/primitives.cjs +6 -0
- package/dist/client/primitives.d.cts +818 -0
- package/dist/client/primitives.d.ts +818 -0
- package/dist/client/primitives.js +6 -0
- package/dist/client/theme/neutral.css +74 -361
- package/dist/client/theme/reset.css +189 -0
- package/dist/docs-layout-BlDhcQRv.cjs +6 -0
- package/dist/docs-layout-BvAOWEJw.js +6 -0
- package/dist/doctor-BQiQhCTl.cjs +6 -0
- package/dist/doctor-COpf35L2.cjs +20 -0
- package/dist/doctor-Dh1XP7Pz.mjs +20 -0
- package/dist/generator-DGW6pkCC.cjs +22 -0
- package/dist/generator-Dv3wEmhZ.mjs +22 -0
- package/dist/icons-dev-CrQLjoQp.js +6 -0
- package/dist/icons-dev-rzdz6Lf3.cjs +6 -0
- package/dist/image-BkIfa9oo.js +6 -0
- package/dist/image-DIGjCPe6.cjs +6 -0
- package/dist/mdx-K0WYBAJ3.js +7 -0
- package/dist/mdx-hpErbRUe.cjs +7 -0
- package/dist/meta-loader-0gJ4PtBC.cjs +6 -0
- package/dist/meta-loader-9IpAHWDS.mjs +6 -0
- package/dist/node/cli-entry.cjs +1 -2
- package/dist/node/cli-entry.mjs +1 -2
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +55 -11
- package/dist/node/index.d.mts +55 -12
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +6 -0
- package/dist/node/routes/worker.d.cts +2 -0
- package/dist/node/routes/worker.d.mts +2 -0
- package/dist/node/routes/worker.mjs +6 -0
- package/dist/node-C2nWXElP.mjs +112 -0
- package/dist/node-CinkUtxV.cjs +112 -0
- package/dist/package-BMYLDBBP.cjs +6 -0
- package/dist/{package-Bqbn1AYK.mjs → package-HegMOTL_.mjs} +1 -1
- package/dist/parser-Bh11BsdA.cjs +6 -0
- package/dist/parser-D8eQvE7N.mjs +6 -0
- package/dist/parser-DYRzXWmA.cjs +6 -0
- package/dist/routes-CHf76Ye4.cjs +6 -0
- package/dist/routes-CMUZGI6T.mjs +6 -0
- package/dist/routes-Co1mRM58.cjs +6 -0
- package/dist/search-dialog-BACuzoVX.cjs +6 -0
- package/dist/search-dialog-BKagVT17.js +6 -0
- package/dist/search-dialog-C8w12eUx.js +6 -0
- package/dist/search-dialog-CGyrozZE.cjs +6 -0
- package/dist/search-dialog-D26rUnJ_.cjs +6 -0
- package/dist/sidebar-DKvg6KOc.d.cts +491 -0
- package/dist/sidebar-Dr1TiRIy.d.ts +491 -0
- package/dist/utils-BxNAXhZZ.mjs +7 -0
- package/dist/utils-Clzu7jvb.cjs +7 -0
- package/dist/worker-pool-Bd8Y9KDv.mjs +6 -0
- package/dist/worker-pool-BwU8ckrg.cjs +6 -0
- package/package.json +27 -8
- package/src/client/app/doc-page.tsx +9 -5
- package/src/client/app/docs-layout.tsx +17 -3
- package/src/client/app/head.tsx +122 -0
- package/src/client/app/helmet-compat.tsx +36 -0
- package/src/client/app/mdx-component.tsx +5 -52
- package/src/client/app/mdx-components-context.tsx +32 -8
- package/src/client/app/routes-context.tsx +2 -2
- package/src/client/app/scroll-handler.tsx +1 -1
- package/src/client/app/theme-context.tsx +5 -5
- package/src/client/app/ui-context.tsx +42 -0
- package/src/client/components/docs-layout-default.tsx +85 -0
- package/src/client/components/icons-dev.tsx +38 -15
- package/src/client/components/mdx/callout.tsx +97 -0
- package/src/client/components/mdx/card.tsx +73 -98
- package/src/client/components/mdx/cards.tsx +27 -0
- package/src/client/components/mdx/code-block.tsx +37 -17
- package/src/client/components/mdx/field.tsx +24 -56
- package/src/client/components/mdx/image.tsx +36 -15
- package/src/client/components/mdx/index.ts +19 -53
- package/src/client/components/mdx/table.tsx +46 -148
- package/src/client/components/mdx/typographics.tsx +120 -0
- package/src/client/components/mdx/{hooks/use-code-block.ts → use-code-block.ts} +5 -7
- package/src/client/components/primitives/breadcrumbs.tsx +5 -24
- package/src/client/components/primitives/button.tsx +3 -142
- package/src/client/components/primitives/code-block.tsx +104 -97
- package/src/client/components/{docs-layout.tsx → primitives/docs-layout.tsx} +15 -24
- package/src/client/components/primitives/error-boundary.tsx +107 -0
- package/src/client/components/primitives/heading.tsx +128 -0
- package/src/client/components/primitives/helpers/observer.ts +62 -32
- package/src/client/components/primitives/image.tsx +26 -0
- package/src/client/components/primitives/link.tsx +50 -52
- package/src/client/components/primitives/menu.tsx +25 -49
- package/src/client/components/primitives/navbar.tsx +234 -59
- package/src/client/components/primitives/on-this-page.tsx +169 -40
- package/src/client/components/primitives/page-nav.tsx +11 -39
- package/src/client/components/primitives/popover.tsx +12 -30
- package/src/client/components/primitives/search-dialog.tsx +77 -71
- package/src/client/components/primitives/sidebar.tsx +312 -119
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +5 -16
- package/src/client/components/primitives/tooltip.tsx +1 -1
- package/src/client/components/ui-base/banner.tsx +66 -0
- package/src/client/components/ui-base/breadcrumbs.tsx +26 -20
- package/src/client/components/ui-base/copy-markdown.tsx +43 -35
- package/src/client/components/ui-base/error-boundary.tsx +9 -46
- package/src/client/components/ui-base/github-stars.tsx +5 -3
- package/src/client/components/ui-base/index.ts +3 -3
- package/src/client/components/ui-base/last-updated.tsx +27 -0
- package/src/client/components/ui-base/navbar.tsx +183 -89
- package/src/client/components/ui-base/not-found.tsx +11 -9
- package/src/client/components/ui-base/on-this-page.tsx +8 -104
- package/src/client/components/ui-base/page-nav.tsx +23 -9
- package/src/client/components/ui-base/search-dialog.tsx +111 -36
- package/src/client/components/ui-base/search-highlight.tsx +10 -0
- package/src/client/components/ui-base/sidebar.tsx +77 -154
- package/src/client/components/ui-base/tabs.tsx +20 -7
- package/src/client/components/ui-base/theme-toggle.tsx +88 -10
- package/src/client/components/ui-base/version-i18n.tsx +80 -0
- package/src/client/hooks/index.ts +2 -1
- package/src/client/hooks/use-analytics.ts +272 -0
- package/src/client/hooks/use-i18n.ts +116 -50
- package/src/client/hooks/use-localized-to.ts +70 -27
- package/src/client/hooks/use-navbar.ts +69 -39
- package/src/client/hooks/use-page-nav.ts +28 -25
- package/src/client/hooks/use-routes.ts +63 -80
- package/src/client/hooks/use-search-highlight.ts +185 -0
- package/src/client/hooks/use-search.ts +12 -3
- package/src/client/hooks/use-sidebar.ts +183 -80
- package/src/client/hooks/use-tabs.ts +3 -4
- package/src/client/hooks/use-version.ts +44 -29
- package/src/client/index.ts +13 -87
- package/src/client/mdx.ts +2 -0
- package/src/client/primitives.ts +19 -0
- package/src/client/ssg/boltdocs-shell.tsx +68 -79
- package/src/client/ssg/create-routes.tsx +268 -72
- package/src/client/ssg/mdx-page.tsx +2 -1
- package/src/client/store/boltdocs-context.tsx +72 -20
- package/src/client/theme/neutral.css +74 -361
- package/src/client/theme/reset.css +189 -0
- package/src/client/types.ts +10 -2
- package/src/client/utils/path.ts +9 -0
- package/src/client/utils/react-to-text.ts +24 -24
- package/src/client/virtual.d.ts +1 -1
- package/src/shared/types.ts +82 -22
- package/dist/node-Bogvkxao.mjs +0 -101
- package/dist/node-CXaog6St.cjs +0 -101
- package/dist/search-dialog-CV3eJzMm.cjs +0 -6
- package/dist/search-dialog-DNTomKgu.js +0 -6
- package/dist/use-search-CS3gH19M.js +0 -6
- package/dist/use-search-DBpJZQuw.cjs +0 -6
- package/src/client/components/mdx/admonition.tsx +0 -91
- package/src/client/components/mdx/badge.tsx +0 -41
- package/src/client/components/mdx/button.tsx +0 -35
- package/src/client/components/mdx/component-preview.tsx +0 -37
- package/src/client/components/mdx/component-props.tsx +0 -83
- package/src/client/components/mdx/file-tree.tsx +0 -325
- package/src/client/components/mdx/hooks/use-component-preview.ts +0 -16
- package/src/client/components/mdx/hooks/useTable.ts +0 -74
- package/src/client/components/mdx/hooks/useTabs.ts +0 -68
- package/src/client/components/mdx/link.tsx +0 -38
- package/src/client/components/mdx/list.tsx +0 -192
- package/src/client/components/mdx/tabs.tsx +0 -135
- package/src/client/components/mdx/video.tsx +0 -68
- package/src/client/components/primitives/index.ts +0 -19
- package/src/client/components/primitives/navigation-menu.tsx +0 -114
- package/src/client/components/ui-base/head.tsx +0 -83
- package/src/client/components/ui-base/loading.tsx +0 -57
- package/src/client/components/ui-base/powered-by.tsx +0 -25
- package/src/client/hooks/use-onthispage.ts +0 -23
- package/src/client/utils/use-on-change.ts +0 -15
|
@@ -2,37 +2,43 @@ import { useBreadcrumbs } from '../../hooks/use-breadcrumbs'
|
|
|
2
2
|
import { Home } from 'lucide-react'
|
|
3
3
|
import { Breadcrumbs as BreadcrumbsRoot } from '../primitives/breadcrumbs'
|
|
4
4
|
import { cn } from '../../utils/cn'
|
|
5
|
-
import { useConfig } from '../../app/config-context'
|
|
6
5
|
|
|
7
6
|
export function Breadcrumbs() {
|
|
8
7
|
const { crumbs, activeRoute } = useBreadcrumbs()
|
|
9
|
-
const config = useConfig()
|
|
10
|
-
const themeConfig = config.theme || {}
|
|
11
|
-
|
|
12
8
|
if (crumbs.length === 0) return null
|
|
13
9
|
|
|
14
|
-
if (!themeConfig?.breadcrumbs) return null
|
|
15
|
-
|
|
16
10
|
return (
|
|
17
|
-
<BreadcrumbsRoot.Root>
|
|
11
|
+
<BreadcrumbsRoot.Root className="gap-2 text-xs sm:text-sm font-medium">
|
|
18
12
|
<BreadcrumbsRoot.Item>
|
|
19
|
-
<BreadcrumbsRoot.Link
|
|
13
|
+
<BreadcrumbsRoot.Link
|
|
14
|
+
href="/"
|
|
15
|
+
className="text-muted hover:text-body transition-colors flex items-center"
|
|
16
|
+
>
|
|
20
17
|
<Home size={14} />
|
|
21
18
|
</BreadcrumbsRoot.Link>
|
|
22
19
|
</BreadcrumbsRoot.Item>
|
|
23
|
-
{crumbs.map((crumb, i) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<BreadcrumbsRoot.
|
|
27
|
-
|
|
28
|
-
className=
|
|
29
|
-
'font-medium text-text-main': crumb.href === activeRoute?.path,
|
|
30
|
-
})}
|
|
20
|
+
{crumbs.map((crumb, i) => {
|
|
21
|
+
const isActive = crumb.href === activeRoute?.path
|
|
22
|
+
return (
|
|
23
|
+
<BreadcrumbsRoot.Item
|
|
24
|
+
key={`crumb-${crumb.href}-${crumb.label}-${i}`}
|
|
25
|
+
className="gap-2"
|
|
31
26
|
>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
<BreadcrumbsRoot.Separator className="text-muted/40" />
|
|
28
|
+
<BreadcrumbsRoot.Link
|
|
29
|
+
href={crumb.href}
|
|
30
|
+
className={cn(
|
|
31
|
+
'transition-colors',
|
|
32
|
+
isActive
|
|
33
|
+
? 'text-body font-semibold cursor-default pointer-events-none'
|
|
34
|
+
: 'text-muted hover:text-body',
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
{crumb.label}
|
|
38
|
+
</BreadcrumbsRoot.Link>
|
|
39
|
+
</BreadcrumbsRoot.Item>
|
|
40
|
+
)
|
|
41
|
+
})}
|
|
36
42
|
</BreadcrumbsRoot.Root>
|
|
37
43
|
)
|
|
38
44
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { useState } from 'react'
|
|
2
2
|
import { Copy, Check, ExternalLink, ChevronDown } from 'lucide-react'
|
|
3
|
-
import { Button
|
|
4
|
-
|
|
3
|
+
import { Button } from '../primitives/button'
|
|
4
|
+
import { ButtonGroup } from '../primitives/button-group'
|
|
5
|
+
import { Menu } from '../primitives/menu'
|
|
6
|
+
import { cn } from '../../utils/cn'
|
|
5
7
|
import type { ComponentRoute } from '../../types'
|
|
6
8
|
|
|
7
9
|
export interface CopyMarkdownProps {
|
|
8
10
|
content?: string
|
|
9
11
|
mdxRaw?: string
|
|
10
12
|
route?: ComponentRoute
|
|
11
|
-
config?: boolean | { text?: string; icon?: string }
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const useCopyMarkdown = (content: string) => {
|
|
@@ -33,63 +34,70 @@ const useCopyMarkdown = (content: string) => {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export function CopyMarkdown({ content, mdxRaw
|
|
37
|
+
export function CopyMarkdown({ content, mdxRaw }: CopyMarkdownProps) {
|
|
37
38
|
const displayContent = mdxRaw || content || ''
|
|
38
39
|
const { copied, handleCopy, handleOpenRaw } = useCopyMarkdown(displayContent)
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
const buttonText =
|
|
42
|
-
typeof config === 'object'
|
|
43
|
-
? config.text || 'Copy Markdown'
|
|
44
|
-
: 'Copy Markdown'
|
|
45
|
-
|
|
46
|
-
if (!isEnabled || !displayContent) return null
|
|
41
|
+
if (!displayContent) return null
|
|
47
42
|
|
|
48
43
|
return (
|
|
49
|
-
<div className="relative inline-flex z-100
|
|
50
|
-
<ButtonGroup className="rounded-xl border border-
|
|
44
|
+
<div className="relative inline-flex z-100 shrink-0 w-max">
|
|
45
|
+
<ButtonGroup className="rounded-xl border border-subtle bg-surface transition-all duration-300 hover:border-primary-500/50 group overflow-hidden">
|
|
46
|
+
{/* Mobile: icon-only copy button */}
|
|
47
|
+
<Button
|
|
48
|
+
onPress={handleCopy}
|
|
49
|
+
className={cn(
|
|
50
|
+
'md:hidden flex items-center justify-center w-8 h-8 bg-transparent outline-none select-none cursor-pointer border-none',
|
|
51
|
+
'text-muted transition-all duration-300 hover:bg-primary-500/5 hover:text-body',
|
|
52
|
+
copied && 'text-emerald-500 hover:bg-emerald-500/5',
|
|
53
|
+
)}
|
|
54
|
+
aria-label={copied ? 'Copied!' : 'Copy Markdown'}
|
|
55
|
+
>
|
|
56
|
+
{copied ? <Check size={14} /> : <Copy size={14} />}
|
|
57
|
+
</Button>
|
|
58
|
+
|
|
59
|
+
{/* Desktop: full copy button with label */}
|
|
51
60
|
<Button
|
|
52
|
-
variant="ghost"
|
|
53
61
|
onPress={handleCopy}
|
|
54
|
-
icon={copied ? <Check size={16} /> : <Copy size={16} />}
|
|
55
|
-
iconPosition="left"
|
|
56
62
|
className={cn(
|
|
57
|
-
'px-5 py-2 bg-transparent text-[0.8125rem] font-semibold h-9
|
|
58
|
-
'text-
|
|
63
|
+
'hidden md:flex items-center gap-2 px-5 py-2 bg-transparent text-[0.8125rem] font-semibold h-9 shrink-0 outline-none select-none cursor-pointer border-none',
|
|
64
|
+
'text-body transition-all duration-300 hover:bg-primary-500/5',
|
|
59
65
|
copied && 'text-emerald-500 hover:bg-emerald-500/5',
|
|
60
66
|
)}
|
|
61
67
|
>
|
|
62
|
-
{copied ?
|
|
68
|
+
{copied ? <Check size={16} /> : <Copy size={16} />}
|
|
69
|
+
{copied ? 'Copied!' : 'Copy Markdown'}
|
|
63
70
|
</Button>
|
|
64
71
|
|
|
65
72
|
<Menu.Trigger placement="bottom end">
|
|
66
73
|
<Button
|
|
67
|
-
variant="ghost"
|
|
68
|
-
isIconOnly
|
|
69
|
-
icon={<ChevronDown size={14} />}
|
|
70
74
|
className={cn(
|
|
71
|
-
'px-3.5 h-9 border-
|
|
75
|
+
'flex items-center justify-center px-2.5 md:px-3.5 h-8 md:h-9 border-none border-l border-subtle/50 text-muted rounded-none bg-transparent shrink-0 outline-none select-none cursor-pointer',
|
|
72
76
|
'transition-all duration-300 hover:bg-primary-500/5 hover:text-primary-500',
|
|
73
77
|
)}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
>
|
|
79
|
+
<ChevronDown size={14} />
|
|
80
|
+
</Button>
|
|
81
|
+
<Menu.Root className="w-52 bg-main border border-subtle rounded-xl p-1.5 shadow-md outline-none flex flex-col gap-0.5 animate-fade-in z-100">
|
|
82
|
+
<Menu.Item
|
|
83
|
+
onAction={handleCopy}
|
|
84
|
+
className="flex items-center px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group"
|
|
85
|
+
>
|
|
77
86
|
<Copy
|
|
78
87
|
size={16}
|
|
79
|
-
className="size-4
|
|
88
|
+
className="size-4 text-muted dark:group-hover:text-primary-500 group-hover:text-primary-400"
|
|
80
89
|
/>
|
|
81
|
-
<span className="
|
|
82
|
-
Copy Markdown
|
|
83
|
-
</span>
|
|
90
|
+
<span className="ml-2">Copy Markdown</span>
|
|
84
91
|
</Menu.Item>
|
|
85
|
-
<Menu.Item
|
|
92
|
+
<Menu.Item
|
|
93
|
+
onAction={handleOpenRaw}
|
|
94
|
+
className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-body dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 cursor-pointer select-none outline-none group"
|
|
95
|
+
>
|
|
86
96
|
<ExternalLink
|
|
87
97
|
size={16}
|
|
88
|
-
className="size-4
|
|
98
|
+
className="size-4 text-muted dark:group-hover:text-primary-500 group-hover:text-primary-400"
|
|
89
99
|
/>
|
|
90
|
-
<span className="
|
|
91
|
-
View as Markdown
|
|
92
|
-
</span>
|
|
100
|
+
<span className="ml-2">View as Markdown</span>
|
|
93
101
|
</Menu.Item>
|
|
94
102
|
</Menu.Root>
|
|
95
103
|
</Menu.Trigger>
|
|
@@ -1,52 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { ErrorBoundary as PrimitiveErrorBoundary } from '../primitives/error-boundary'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
4
|
+
interface ErrorBoundaryProps {
|
|
7
5
|
children?: ReactNode
|
|
8
6
|
fallback?: ReactNode
|
|
9
7
|
}
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
public state: State = { hasError: false }
|
|
18
|
-
|
|
19
|
-
public static getDerivedStateFromError(error: Error): State {
|
|
20
|
-
return { hasError: true, error }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
24
|
-
console.error('Uncaught error in Boltdocs Layout:', error, errorInfo)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public render() {
|
|
28
|
-
if (this.state.hasError) {
|
|
29
|
-
return (
|
|
30
|
-
this.props.fallback || (
|
|
31
|
-
<div className="flex flex-col items-center justify-center min-h-[40vh] text-center gap-4 px-4">
|
|
32
|
-
<div className="text-lg font-bold text-red-400">
|
|
33
|
-
Something went wrong
|
|
34
|
-
</div>
|
|
35
|
-
<p className="text-sm text-text-muted max-w-md">
|
|
36
|
-
{this.state.error?.message ||
|
|
37
|
-
'An unexpected error occurred while rendering this page.'}
|
|
38
|
-
</p>
|
|
39
|
-
<Button
|
|
40
|
-
className="rounded-lg border border-border-subtle bg-bg-surface px-5 py-2 text-sm font-medium text-text-main transition-colors hover:bg-bg-muted cursor-pointer"
|
|
41
|
-
onPress={() => this.setState({ hasError: false })}
|
|
42
|
-
>
|
|
43
|
-
Try again
|
|
44
|
-
</Button>
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return this.props.children
|
|
51
|
-
}
|
|
9
|
+
export function ErrorBoundary({ children, fallback }: ErrorBoundaryProps) {
|
|
10
|
+
return (
|
|
11
|
+
<PrimitiveErrorBoundary fallback={fallback}>
|
|
12
|
+
{children}
|
|
13
|
+
</PrimitiveErrorBoundary>
|
|
14
|
+
)
|
|
52
15
|
}
|
|
@@ -18,10 +18,12 @@ export function GithubStars({ repo }: { repo: string }) {
|
|
|
18
18
|
href={`https://github.com/${repo}`}
|
|
19
19
|
target="_blank"
|
|
20
20
|
rel="noopener noreferrer"
|
|
21
|
-
className="inline-flex items-center gap-2 rounded-
|
|
21
|
+
className="inline-flex items-center gap-2 rounded-xl border border-subtle bg-surface px-3 py-1.5 text-xs font-semibold text-muted dark:hover:bg-primary-300/50 hover:bg-primary-200/50 transition-colors duration-100 hover:border-primary-500/50 hover:text-body select-none outline-none"
|
|
22
22
|
>
|
|
23
|
-
<Github className="h-4 w-4" />
|
|
24
|
-
{stars &&
|
|
23
|
+
<Github className="h-4 w-4 text-body" />
|
|
24
|
+
{stars !== null && (
|
|
25
|
+
<span className="tabular-nums font-medium">{stars} stars</span>
|
|
26
|
+
)}
|
|
25
27
|
</a>
|
|
26
28
|
)
|
|
27
29
|
}
|
|
@@ -3,14 +3,14 @@ export { CopyMarkdown } from './copy-markdown'
|
|
|
3
3
|
export type { CopyMarkdownProps } from './copy-markdown'
|
|
4
4
|
export { ErrorBoundary } from './error-boundary'
|
|
5
5
|
export { GithubStars } from './github-stars'
|
|
6
|
-
export { Head } from './head'
|
|
7
|
-
export { Loading } from './loading'
|
|
8
6
|
export { Navbar } from './navbar'
|
|
9
7
|
export { NotFound } from './not-found'
|
|
10
8
|
export { OnThisPage } from './on-this-page'
|
|
11
9
|
export { PageNav } from './page-nav'
|
|
12
|
-
export { PoweredBy } from './powered-by'
|
|
13
10
|
export { SearchDialog } from './search-dialog'
|
|
14
11
|
export { Sidebar } from './sidebar'
|
|
15
12
|
export { Tabs } from './tabs'
|
|
13
|
+
export { Banner } from './banner'
|
|
16
14
|
export { ThemeToggle } from './theme-toggle'
|
|
15
|
+
export { LastUpdated } from './last-updated'
|
|
16
|
+
export { Callout } from '../mdx/callout'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface LastUpdatedProps {
|
|
2
|
+
date?: string | number | Date
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A subtle display for when the page was last updated.
|
|
7
|
+
* Small, opaque, and positioned at the bottom of the content with a thin top border divider.
|
|
8
|
+
*/
|
|
9
|
+
export function LastUpdated({ date }: LastUpdatedProps) {
|
|
10
|
+
if (!date) return null
|
|
11
|
+
|
|
12
|
+
const d = new Date(date)
|
|
13
|
+
if (isNaN(d.getTime())) return null
|
|
14
|
+
|
|
15
|
+
const formattedDate = d.toLocaleDateString(undefined, {
|
|
16
|
+
year: 'numeric',
|
|
17
|
+
month: 'long',
|
|
18
|
+
day: 'numeric',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="mt-16 pt-6 border-t border-subtle flex items-center justify-between text-xs text-muted select-none">
|
|
23
|
+
<span></span>
|
|
24
|
+
<span className="italic">Last updated on {formattedDate}</span>
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Suspense, lazy } from 'react'
|
|
1
|
+
import { Suspense, lazy, useState } from 'react'
|
|
2
|
+
import { cn } from '../../utils/cn'
|
|
2
3
|
import { useNavbar } from '../../hooks/use-navbar'
|
|
3
|
-
import { useVersion } from '../../hooks/use-version'
|
|
4
|
-
import { useI18n } from '../../hooks/use-i18n'
|
|
5
4
|
import { useRoutes } from '../../hooks/use-routes'
|
|
6
5
|
import NavbarPrimitive from '../primitives/navbar'
|
|
7
6
|
import { ThemeToggle } from './theme-toggle'
|
|
@@ -9,11 +8,12 @@ import { GithubStars } from './github-stars'
|
|
|
9
8
|
import { Tabs } from './tabs'
|
|
10
9
|
import { useLocation } from 'react-router-dom'
|
|
11
10
|
import type { BoltdocsSocialLink } from '../../../shared/types'
|
|
12
|
-
import { Menu } from '../primitives/menu'
|
|
13
11
|
import { Button } from '../primitives/button'
|
|
14
|
-
import {
|
|
12
|
+
import { Menu as MenuIcon, X } from 'lucide-react'
|
|
15
13
|
import { useLocalizedTo } from '../../hooks/use-localized-to'
|
|
16
14
|
import type { NavbarLink as NavbarLinkType } from '../../types'
|
|
15
|
+
import { useUI } from '../../app/ui-context'
|
|
16
|
+
import { VersionSelector, I18nSelector } from './version-i18n'
|
|
17
17
|
|
|
18
18
|
const SearchDialog = lazy(() =>
|
|
19
19
|
import('./search-dialog').then((m) => ({
|
|
@@ -23,63 +23,146 @@ const SearchDialog = lazy(() =>
|
|
|
23
23
|
|
|
24
24
|
export function Navbar() {
|
|
25
25
|
const { links, title, logo, logoProps, github, social, config } = useNavbar()
|
|
26
|
-
const { routes, allRoutes, currentVersion, currentLocale } = useRoutes()
|
|
26
|
+
const { routes, allRoutes, currentRoute, currentVersion, currentLocale } = useRoutes()
|
|
27
27
|
const { pathname } = useLocation()
|
|
28
|
+
const { isSidebarOpen, toggleSidebar } = useUI()
|
|
29
|
+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
|
30
|
+
|
|
28
31
|
const themeConfig = config.theme || {}
|
|
29
|
-
const isDocs =
|
|
32
|
+
const isDocs = !!currentRoute?.filePath
|
|
30
33
|
const hasTabs = themeConfig?.tabs && themeConfig.tabs.length > 0
|
|
31
34
|
|
|
32
35
|
return (
|
|
33
|
-
<NavbarPrimitive.Root
|
|
36
|
+
<NavbarPrimitive.Root
|
|
37
|
+
className={cn(
|
|
38
|
+
'border-b border-subtle bg-main/80 backdrop-blur-md',
|
|
39
|
+
hasTabs && 'border-b-0',
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
34
42
|
<NavbarPrimitive.Content>
|
|
35
43
|
<NavbarPrimitive.Left>
|
|
44
|
+
{isDocs && (
|
|
45
|
+
<Button
|
|
46
|
+
onPress={toggleSidebar}
|
|
47
|
+
className="mr-2 lg:hidden p-1.5 h-8 w-8 flex items-center justify-center bg-transparent border-none outline-none select-none cursor-pointer rounded-xl hover:bg-primary-50/50 transition-colors"
|
|
48
|
+
aria-label={isSidebarOpen ? 'Close sidebar' : 'Open sidebar'}
|
|
49
|
+
>
|
|
50
|
+
{isSidebarOpen ? (
|
|
51
|
+
<X className="w-5 h-5 text-body" />
|
|
52
|
+
) : (
|
|
53
|
+
<MenuIcon className="w-5 h-5 text-body" />
|
|
54
|
+
)}
|
|
55
|
+
</Button>
|
|
56
|
+
)}
|
|
36
57
|
{logo && (
|
|
37
58
|
<NavbarPrimitive.Logo
|
|
38
59
|
src={logo}
|
|
39
60
|
alt={logoProps?.alt || title}
|
|
40
61
|
width={logoProps?.width ?? 24}
|
|
41
62
|
height={logoProps?.height ?? 24}
|
|
63
|
+
href="site:/"
|
|
42
64
|
/>
|
|
43
65
|
)}
|
|
44
|
-
<NavbarPrimitive.Title>{title}</NavbarPrimitive.Title>
|
|
66
|
+
<NavbarPrimitive.Title href="site:/">{title}</NavbarPrimitive.Title>
|
|
45
67
|
|
|
46
|
-
|
|
68
|
+
<div className="hidden sm:block">
|
|
69
|
+
{config.versions && currentVersion && <VersionSelector />}
|
|
70
|
+
</div>
|
|
47
71
|
</NavbarPrimitive.Left>
|
|
48
72
|
<NavbarPrimitive.Center>
|
|
49
73
|
<Suspense
|
|
50
74
|
fallback={
|
|
51
|
-
<div className="h-9 w-32 animate-pulse rounded-md bg-
|
|
75
|
+
<div className="h-9 w-32 animate-pulse rounded-md bg-surface" />
|
|
52
76
|
}
|
|
53
77
|
>
|
|
54
78
|
<SearchDialog routes={routes || []} />
|
|
55
79
|
</Suspense>
|
|
56
80
|
</NavbarPrimitive.Center>
|
|
57
81
|
<NavbarPrimitive.Right>
|
|
82
|
+
<Suspense fallback={null}>
|
|
83
|
+
<div className="lg:hidden">
|
|
84
|
+
<SearchDialog routes={routes || []} />
|
|
85
|
+
</div>
|
|
86
|
+
</Suspense>
|
|
58
87
|
<NavbarPrimitive.Links>
|
|
59
88
|
{links.map((link) => (
|
|
60
89
|
<NavbarLinkItem key={link.href} link={link} />
|
|
61
90
|
))}
|
|
62
91
|
</NavbarPrimitive.Links>
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
|
|
93
|
+
<div className="hidden sm:flex items-center gap-2">
|
|
94
|
+
{config.i18n && currentLocale && <I18nSelector />}
|
|
95
|
+
<NavbarPrimitive.Split className="bg-subtle" />
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div className="hidden md:block">
|
|
99
|
+
<ThemeToggle />
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{github && (
|
|
103
|
+
<div className="hidden md:block">
|
|
104
|
+
<GithubStars repo={themeConfig?.githubRepo ?? ''} />
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
{social.length > 0 && (
|
|
108
|
+
<div className="hidden md:block">
|
|
109
|
+
<NavbarPrimitive.Split className="bg-subtle" />
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
<div className="hidden md:flex items-center gap-1">
|
|
69
113
|
{social.map(({ icon, link }: BoltdocsSocialLink) => (
|
|
70
114
|
<NavbarPrimitive.Socials
|
|
71
115
|
key={link}
|
|
72
116
|
icon={icon}
|
|
73
117
|
link={link}
|
|
74
|
-
className="p-1.5"
|
|
118
|
+
className="p-1.5 text-muted hover:text-body hover:bg-surface rounded-md transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
|
|
75
119
|
/>
|
|
76
120
|
))}
|
|
77
121
|
</div>
|
|
122
|
+
|
|
123
|
+
<NavbarPrimitive.More
|
|
124
|
+
onPress={() => setIsMobileMenuOpen(true)}
|
|
125
|
+
className="text-muted hover:text-body active:scale-90 transition-all focus-visible:ring-2 focus-visible:ring-primary-500/30"
|
|
126
|
+
/>
|
|
78
127
|
</NavbarPrimitive.Right>
|
|
79
128
|
</NavbarPrimitive.Content>
|
|
80
129
|
|
|
130
|
+
<NavbarPrimitive.MobileMenu
|
|
131
|
+
isOpen={isMobileMenuOpen}
|
|
132
|
+
onClose={() => setIsMobileMenuOpen(false)}
|
|
133
|
+
className="bg-main/98 backdrop-blur-2xl"
|
|
134
|
+
>
|
|
135
|
+
<div className="flex flex-col gap-1">
|
|
136
|
+
{links.map((link) => (
|
|
137
|
+
<NavbarMobileLinkItem
|
|
138
|
+
key={link.href}
|
|
139
|
+
link={link}
|
|
140
|
+
onClose={() => setIsMobileMenuOpen(false)}
|
|
141
|
+
/>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{social.length > 0 && (
|
|
146
|
+
<div className="mt-6">
|
|
147
|
+
<div className="px-4 mb-4 text-xs font-bold uppercase tracking-widest text-muted/50">
|
|
148
|
+
Connect
|
|
149
|
+
</div>
|
|
150
|
+
<div className="flex flex-wrap gap-2 px-2">
|
|
151
|
+
{social.map(({ icon, link }: BoltdocsSocialLink) => (
|
|
152
|
+
<NavbarPrimitive.Socials
|
|
153
|
+
key={link}
|
|
154
|
+
icon={icon}
|
|
155
|
+
link={link}
|
|
156
|
+
className="p-3 bg-surface border border-subtle rounded-xl flex-1 justify-center"
|
|
157
|
+
/>
|
|
158
|
+
))}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</NavbarPrimitive.MobileMenu>
|
|
163
|
+
|
|
81
164
|
{isDocs && hasTabs && themeConfig?.tabs && (
|
|
82
|
-
<div className="w-full border-b border-
|
|
165
|
+
<div className="w-full border-b border-subtle bg-main">
|
|
83
166
|
<Tabs tabs={themeConfig.tabs} routes={allRoutes || routes || []} />
|
|
84
167
|
</div>
|
|
85
168
|
)}
|
|
@@ -89,84 +172,95 @@ export function Navbar() {
|
|
|
89
172
|
|
|
90
173
|
function NavbarLinkItem({ link }: { link: NavbarLinkType }) {
|
|
91
174
|
const localizedHref = useLocalizedTo(link.href || '')
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const { currentVersionLabel, availableVersions, handleVersionChange } =
|
|
97
|
-
useVersion()
|
|
175
|
+
const { pathname } = useLocation()
|
|
176
|
+
const active =
|
|
177
|
+
pathname === localizedHref || pathname.startsWith(localizedHref + '/')
|
|
178
|
+
const hasItems = link.items && link.items.length > 0
|
|
98
179
|
|
|
99
|
-
if (
|
|
180
|
+
if (hasItems) {
|
|
181
|
+
return (
|
|
182
|
+
<NavbarPrimitive.Dropdown
|
|
183
|
+
label={
|
|
184
|
+
<span
|
|
185
|
+
className={cn(
|
|
186
|
+
'transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm px-2 py-1',
|
|
187
|
+
active ? 'text-primary-500' : 'text-muted hover:text-body',
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
{link.label as any}
|
|
191
|
+
</span>
|
|
192
|
+
}
|
|
193
|
+
>
|
|
194
|
+
{link.items?.map((item) => (
|
|
195
|
+
<NavbarPrimitive.DropdownItem
|
|
196
|
+
key={item.href}
|
|
197
|
+
href={useLocalizedTo(item.href || '')}
|
|
198
|
+
label={item.label as any}
|
|
199
|
+
/>
|
|
200
|
+
))}
|
|
201
|
+
</NavbarPrimitive.Dropdown>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
100
204
|
|
|
101
205
|
return (
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<span className="font-semibold text-[0.8125rem]">
|
|
112
|
-
{currentVersionLabel}
|
|
113
|
-
</span>
|
|
114
|
-
</Button>
|
|
115
|
-
<Menu.Root>
|
|
116
|
-
<Menu.Section items={availableVersions}>
|
|
117
|
-
{(version) => (
|
|
118
|
-
<Menu.Item
|
|
119
|
-
key={`${version.value ?? ''}`}
|
|
120
|
-
onPress={() => handleVersionChange(version.value)}
|
|
121
|
-
>
|
|
122
|
-
{version.label as string}
|
|
123
|
-
</Menu.Item>
|
|
124
|
-
)}
|
|
125
|
-
</Menu.Section>
|
|
126
|
-
</Menu.Root>
|
|
127
|
-
</Menu.Trigger>
|
|
206
|
+
<NavbarPrimitive.Link
|
|
207
|
+
{...(link as any)}
|
|
208
|
+
href={localizedHref}
|
|
209
|
+
active={active}
|
|
210
|
+
className={cn(
|
|
211
|
+
'transition-colors outline-none font-medium focus-visible:ring-2 focus-visible:ring-primary-500/30 rounded-sm',
|
|
212
|
+
active ? 'text-primary-500' : 'text-muted hover:text-body',
|
|
213
|
+
)}
|
|
214
|
+
/>
|
|
128
215
|
)
|
|
129
216
|
}
|
|
130
217
|
|
|
131
|
-
function
|
|
132
|
-
|
|
218
|
+
function NavbarMobileLinkItem({
|
|
219
|
+
link,
|
|
220
|
+
onClose,
|
|
221
|
+
}: {
|
|
222
|
+
link: NavbarLinkType
|
|
223
|
+
onClose: () => void
|
|
224
|
+
}) {
|
|
225
|
+
const localizedHref = useLocalizedTo(link.href || '')
|
|
226
|
+
const { pathname } = useLocation()
|
|
227
|
+
const active = pathname === localizedHref
|
|
228
|
+
const hasItems = link.items && link.items.length > 0
|
|
133
229
|
|
|
134
|
-
if (
|
|
230
|
+
if (hasItems) {
|
|
231
|
+
return (
|
|
232
|
+
<div className="flex flex-col gap-1">
|
|
233
|
+
<div
|
|
234
|
+
className={cn(
|
|
235
|
+
'px-3 py-2 text-sm transition-all',
|
|
236
|
+
active ? 'text-body' : 'text-muted/80 hover:text-body',
|
|
237
|
+
)}
|
|
238
|
+
>
|
|
239
|
+
{link.label as any}
|
|
240
|
+
</div>
|
|
241
|
+
<div className="flex flex-col gap-1 pl-4">
|
|
242
|
+
{link.items?.map((item) => (
|
|
243
|
+
<NavbarMobileLinkItem
|
|
244
|
+
key={item.href}
|
|
245
|
+
link={item}
|
|
246
|
+
onClose={onClose}
|
|
247
|
+
/>
|
|
248
|
+
))}
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
135
253
|
|
|
136
254
|
return (
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
<Languages className="w-3.5 h-3.5 text-primary-500" />
|
|
148
|
-
<span className="font-bold text-[0.75rem] uppercase opacity-90">
|
|
149
|
-
{currentLocale || 'en'}
|
|
150
|
-
</span>
|
|
151
|
-
</div>
|
|
152
|
-
</Button>
|
|
153
|
-
<Menu.Root>
|
|
154
|
-
<Menu.Section items={availableLocales}>
|
|
155
|
-
{(locale) => (
|
|
156
|
-
<Menu.Item
|
|
157
|
-
key={`${locale.value ?? ''}`}
|
|
158
|
-
onPress={() => handleLocaleChange(locale.value)}
|
|
159
|
-
>
|
|
160
|
-
<div className="flex items-center justify-between w-full gap-4">
|
|
161
|
-
<span>{locale.label as string}</span>
|
|
162
|
-
<span className="text-[10px] font-bold opacity-40 uppercase tracking-tighter">
|
|
163
|
-
{locale.value}
|
|
164
|
-
</span>
|
|
165
|
-
</div>
|
|
166
|
-
</Menu.Item>
|
|
167
|
-
)}
|
|
168
|
-
</Menu.Section>
|
|
169
|
-
</Menu.Root>
|
|
170
|
-
</Menu.Trigger>
|
|
255
|
+
<NavbarPrimitive.MobileLink
|
|
256
|
+
{...(link as any)}
|
|
257
|
+
href={localizedHref}
|
|
258
|
+
active={active}
|
|
259
|
+
onPress={onClose}
|
|
260
|
+
className={cn(
|
|
261
|
+
'transition-all',
|
|
262
|
+
active ? 'text-body' : 'text-muted/80 hover:text-body',
|
|
263
|
+
)}
|
|
264
|
+
/>
|
|
171
265
|
)
|
|
172
266
|
}
|