boltdocs 2.5.5 → 2.6.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 +2 -2
- package/dist/client/index.cjs +6 -0
- package/dist/client/index.d.cts +1560 -0
- package/dist/client/index.d.ts +1219 -922
- package/dist/client/index.js +6 -1
- package/dist/client/theme/neutral.css +428 -0
- package/dist/node/cli-entry.cjs +8 -0
- package/dist/node/cli-entry.d.cts +2 -0
- package/dist/node/cli-entry.d.mts +2 -1
- package/dist/node/cli-entry.mjs +7 -5
- package/dist/node/index.cjs +6 -0
- package/dist/node/index.d.cts +519 -0
- package/dist/node/index.d.mts +374 -422
- package/dist/node/index.mjs +6 -1
- package/dist/node-BgvNl2Ay.mjs +89 -0
- package/dist/node-vkbb0MK7.cjs +89 -0
- package/dist/package-CR0HF9x3.mjs +6 -0
- package/dist/package-Dgmsc_l5.cjs +6 -0
- package/dist/search-dialog-3lvKsbVG.js +6 -0
- package/dist/search-dialog-DMK5OpgH.cjs +6 -0
- package/dist/use-search-C9bxCqfF.js +6 -0
- package/dist/use-search-DcfZSunO.cjs +6 -0
- package/package.json +26 -25
- package/src/client/app/config-context.tsx +38 -5
- package/src/client/app/doc-page.tsx +34 -0
- package/src/client/app/mdx-component.tsx +2 -3
- package/src/client/app/mdx-components-context.tsx +27 -2
- package/src/client/app/routes-context.tsx +34 -0
- package/src/client/app/scroll-handler.tsx +7 -4
- package/src/client/app/theme-context.tsx +71 -67
- package/src/client/components/default-layout.tsx +34 -33
- package/src/client/components/docs-layout.tsx +1 -2
- package/src/client/components/icons-dev.tsx +36 -5
- package/src/client/components/mdx/admonition.tsx +11 -27
- package/src/client/components/mdx/badge.tsx +1 -1
- package/src/client/components/mdx/button.tsx +3 -3
- package/src/client/components/mdx/card.tsx +1 -1
- package/src/client/components/mdx/code-block.tsx +90 -80
- package/src/client/components/mdx/component-preview.tsx +1 -5
- package/src/client/components/mdx/component-props.tsx +1 -1
- package/src/client/components/mdx/field.tsx +4 -5
- package/src/client/components/mdx/file-tree.tsx +6 -3
- package/src/client/components/mdx/hooks/use-code-block.ts +2 -2
- package/src/client/components/mdx/image.tsx +1 -1
- package/src/client/components/mdx/link.tsx +2 -2
- package/src/client/components/mdx/list.tsx +1 -1
- package/src/client/components/mdx/table.tsx +1 -1
- package/src/client/components/mdx/tabs.tsx +1 -1
- package/src/client/components/primitives/breadcrumbs.tsx +1 -7
- package/src/client/components/primitives/button-group.tsx +1 -1
- package/src/client/components/primitives/button.tsx +1 -1
- package/src/client/components/primitives/code-block.tsx +113 -0
- package/src/client/components/primitives/link.tsx +23 -41
- package/src/client/components/primitives/menu.tsx +5 -6
- package/src/client/components/primitives/navbar.tsx +6 -18
- package/src/client/components/primitives/navigation-menu.tsx +4 -4
- package/src/client/components/primitives/on-this-page.tsx +6 -10
- package/src/client/components/primitives/page-nav.tsx +4 -9
- package/src/client/components/primitives/popover.tsx +1 -1
- package/src/client/components/primitives/search-dialog.tsx +3 -6
- package/src/client/components/primitives/sidebar.tsx +80 -22
- package/src/client/components/primitives/skeleton.tsx +1 -1
- package/src/client/components/primitives/tabs.tsx +4 -11
- package/src/client/components/primitives/tooltip.tsx +3 -3
- package/src/client/components/ui-base/breadcrumbs.tsx +4 -6
- package/src/client/components/ui-base/copy-markdown.tsx +2 -7
- package/src/client/components/ui-base/github-stars.tsx +2 -2
- package/src/client/components/ui-base/head.tsx +58 -51
- package/src/client/components/ui-base/loading.tsx +2 -2
- package/src/client/components/ui-base/navbar.tsx +12 -14
- package/src/client/components/ui-base/not-found.tsx +1 -1
- package/src/client/components/ui-base/on-this-page.tsx +6 -6
- package/src/client/components/ui-base/page-nav.tsx +4 -8
- package/src/client/components/ui-base/search-dialog.tsx +10 -8
- package/src/client/components/ui-base/sidebar.tsx +76 -23
- package/src/client/components/ui-base/tabs.tsx +9 -8
- package/src/client/components/ui-base/theme-toggle.tsx +2 -2
- package/src/client/hooks/use-i18n.ts +3 -3
- package/src/client/hooks/use-localized-to.ts +1 -1
- package/src/client/hooks/use-navbar.ts +8 -6
- package/src/client/hooks/use-routes.ts +19 -11
- package/src/client/hooks/use-search.ts +1 -1
- package/src/client/hooks/use-sidebar.ts +48 -2
- package/src/client/hooks/use-tabs.ts +6 -2
- package/src/client/hooks/use-version.ts +3 -3
- package/src/client/index.ts +22 -22
- package/src/client/ssg/boltdocs-shell.tsx +127 -0
- package/src/client/ssg/create-routes.tsx +179 -0
- package/src/client/ssg/index.ts +3 -0
- package/src/client/ssg/mdx-page.tsx +37 -0
- package/src/client/store/boltdocs-context.tsx +66 -0
- package/src/client/theme/neutral.css +90 -50
- package/src/client/types.ts +5 -33
- package/src/client/utils/react-to-text.ts +34 -0
- package/CHANGELOG.md +0 -98
- package/dist/cache-3FOEPC2P.mjs +0 -1
- package/dist/chunk-5B5NKOW6.mjs +0 -77
- package/dist/chunk-J2PTDWZM.mjs +0 -1
- package/dist/chunk-TP5KMRD3.mjs +0 -1
- package/dist/chunk-Y4RE5KI7.mjs +0 -1
- package/dist/client/index.d.mts +0 -1263
- package/dist/client/index.mjs +0 -1
- package/dist/client/ssr.d.mts +0 -78
- package/dist/client/ssr.d.ts +0 -78
- package/dist/client/ssr.js +0 -1
- package/dist/client/ssr.mjs +0 -1
- package/dist/node/cli-entry.d.ts +0 -1
- package/dist/node/cli-entry.js +0 -82
- package/dist/node/index.d.ts +0 -567
- package/dist/node/index.js +0 -77
- package/dist/package-QFIAETHR.mjs +0 -1
- package/dist/search-dialog-O6VLVSOA.mjs +0 -1
- package/src/client/app/index.tsx +0 -345
- package/src/client/app/mdx-page.tsx +0 -15
- package/src/client/app/preload.tsx +0 -66
- package/src/client/app/router.tsx +0 -30
- package/src/client/integrations/codesandbox.ts +0 -179
- package/src/client/integrations/index.ts +0 -1
- package/src/client/ssr.tsx +0 -65
- package/src/client/store/use-boltdocs-store.ts +0 -44
- package/src/node/cache.ts +0 -408
- package/src/node/cli/build.ts +0 -53
- package/src/node/cli/dev.ts +0 -22
- package/src/node/cli/doctor.ts +0 -243
- package/src/node/cli/index.ts +0 -9
- package/src/node/cli/ui.ts +0 -54
- package/src/node/cli-entry.ts +0 -24
- package/src/node/config.ts +0 -382
- package/src/node/errors.ts +0 -44
- package/src/node/index.ts +0 -84
- package/src/node/mdx/cache.ts +0 -12
- package/src/node/mdx/highlighter.ts +0 -47
- package/src/node/mdx/index.ts +0 -122
- package/src/node/mdx/rehype-shiki.ts +0 -62
- package/src/node/mdx/remark-code-meta.ts +0 -35
- package/src/node/mdx/remark-shiki.ts +0 -61
- package/src/node/plugin/entry.ts +0 -87
- package/src/node/plugin/html.ts +0 -99
- package/src/node/plugin/index.ts +0 -478
- package/src/node/plugin/types.ts +0 -9
- package/src/node/plugins/index.ts +0 -17
- package/src/node/plugins/plugin-errors.ts +0 -62
- package/src/node/plugins/plugin-lifecycle.ts +0 -117
- package/src/node/plugins/plugin-sandbox.ts +0 -59
- package/src/node/plugins/plugin-store.ts +0 -54
- package/src/node/plugins/plugin-types.ts +0 -107
- package/src/node/plugins/plugin-validator.ts +0 -105
- package/src/node/routes/cache.ts +0 -28
- package/src/node/routes/index.ts +0 -293
- package/src/node/routes/parser.ts +0 -262
- package/src/node/routes/sorter.ts +0 -42
- package/src/node/routes/types.ts +0 -61
- package/src/node/schema/config.ts +0 -195
- package/src/node/schema/frontmatter.ts +0 -17
- package/src/node/search/index.ts +0 -55
- package/src/node/security/constants/index.ts +0 -10
- package/src/node/security/csp.ts +0 -31
- package/src/node/security/headers.ts +0 -27
- package/src/node/ssg/index.ts +0 -205
- package/src/node/ssg/meta.ts +0 -33
- package/src/node/ssg/options.ts +0 -15
- package/src/node/ssg/robots.ts +0 -53
- package/src/node/ssg/sitemap.ts +0 -55
- package/src/node/utils.ts +0 -349
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -56
|
@@ -1,69 +1,76 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
2
1
|
import { useLocation } from 'react-router-dom'
|
|
2
|
+
import { Helmet } from 'react-helmet-async'
|
|
3
|
+
import { useConfig } from '../../app/config-context'
|
|
3
4
|
|
|
4
5
|
interface HeadProps {
|
|
5
6
|
siteTitle: string
|
|
6
7
|
siteDescription?: string
|
|
7
|
-
routes: Array<{ path: string; title: string; description?: string }>
|
|
8
|
+
routes: Array<{ path: string; title: string; description?: string; seo?: Record<string, any> }>
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export function Head({ siteTitle, siteDescription, routes }: HeadProps) {
|
|
11
12
|
const location = useLocation()
|
|
13
|
+
const config = useConfig()
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const pageDescription = currentRoute?.description || siteDescription || ''
|
|
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 || ''
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
document.title = pageTitle ? `${pageTitle} | ${siteTitle}` : siteTitle
|
|
20
|
+
const finalTitle = pageTitle ? `${pageTitle} | ${siteTitle}` : siteTitle
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
let metaDesc = document.querySelector(
|
|
24
|
-
'meta[name="description"]',
|
|
25
|
-
) as HTMLMetaElement | null
|
|
26
|
-
if (!metaDesc) {
|
|
27
|
-
metaDesc = document.createElement('meta')
|
|
28
|
-
metaDesc.name = 'description'
|
|
29
|
-
document.head.appendChild(metaDesc)
|
|
30
|
-
}
|
|
31
|
-
metaDesc.content = pageDescription
|
|
22
|
+
const seo = currentRoute?.seo || {}
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
setMetaTag('property', 'og:description', pageDescription)
|
|
36
|
-
setMetaTag('property', 'og:type', 'article')
|
|
37
|
-
setMetaTag('property', 'og:url', window.location.href)
|
|
24
|
+
// Merge custom global metatags
|
|
25
|
+
const globalMetatags = config?.seo?.metatags || {}
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
setMetaTag('name', 'twitter:description', pageDescription)
|
|
27
|
+
// Calculate specific ones
|
|
28
|
+
const defaultOgImage = config?.seo?.thumbnails?.background
|
|
29
|
+
const ogImage = seo['og:image'] || defaultOgImage
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
canonical = document.createElement('link')
|
|
50
|
-
canonical.rel = 'canonical'
|
|
51
|
-
document.head.appendChild(canonical)
|
|
52
|
-
}
|
|
53
|
-
canonical.href = window.location.origin + location.pathname
|
|
54
|
-
}, [location.pathname, siteTitle, siteDescription, routes])
|
|
31
|
+
return (
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
<Helmet>
|
|
34
|
+
<title>{finalTitle}</title>
|
|
35
|
+
<meta name="description" content={pageDescription} />
|
|
55
36
|
|
|
56
|
-
|
|
57
|
-
}
|
|
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} />
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
document.head.appendChild(tag)
|
|
67
|
-
}
|
|
68
|
-
tag.content = content
|
|
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
|
+
)
|
|
69
76
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { Suspense, lazy } from 'react'
|
|
2
|
-
import { useNavbar } from '
|
|
3
|
-
import { useVersion } from '
|
|
4
|
-
import { useI18n } from '
|
|
5
|
-
import { useRoutes } from '
|
|
6
|
-
import NavbarPrimitive from '
|
|
2
|
+
import { useNavbar } from '../../hooks/use-navbar'
|
|
3
|
+
import { useVersion } from '../../hooks/use-version'
|
|
4
|
+
import { useI18n } from '../../hooks/use-i18n'
|
|
5
|
+
import { useRoutes } from '../../hooks/use-routes'
|
|
6
|
+
import NavbarPrimitive from '../primitives/navbar'
|
|
7
7
|
import { ThemeToggle } from './theme-toggle'
|
|
8
8
|
import { GithubStars } from './github-stars'
|
|
9
9
|
import { Tabs } from './tabs'
|
|
10
10
|
import { useLocation } from 'react-router-dom'
|
|
11
|
-
import type { BoltdocsSocialLink } from '
|
|
12
|
-
import { Menu } from '
|
|
13
|
-
import { Button } from '
|
|
11
|
+
import type { BoltdocsSocialLink } from '../../../shared/types'
|
|
12
|
+
import { Menu } from '../primitives/menu'
|
|
13
|
+
import { Button } from '../primitives/button'
|
|
14
14
|
import { ChevronDown, Languages } from 'lucide-react'
|
|
15
|
-
import { useLocalizedTo } from '
|
|
16
|
-
import type { NavbarLink as NavbarLinkType } from '
|
|
15
|
+
import { useLocalizedTo } from '../../hooks/use-localized-to'
|
|
16
|
+
import type { NavbarLink as NavbarLinkType } from '../../types'
|
|
17
17
|
|
|
18
18
|
const SearchDialog = lazy(() =>
|
|
19
19
|
import('./search-dialog').then((m) => ({
|
|
@@ -57,9 +57,7 @@ export function Navbar() {
|
|
|
57
57
|
<NavbarPrimitive.Right>
|
|
58
58
|
<NavbarPrimitive.Links>
|
|
59
59
|
{links.map((link) => (
|
|
60
|
-
|
|
61
|
-
<NavbarLinkItem key={link.href} link={link} />
|
|
62
|
-
</>
|
|
60
|
+
<NavbarLinkItem key={link.href} link={link} />
|
|
63
61
|
))}
|
|
64
62
|
</NavbarPrimitive.Links>
|
|
65
63
|
{config.i18n && currentLocale && <NavbarI18n />}
|
|
@@ -147,7 +145,7 @@ function NavbarI18n() {
|
|
|
147
145
|
>
|
|
148
146
|
<div className="flex items-center gap-1.5">
|
|
149
147
|
<Languages className="w-3.5 h-3.5 text-primary-500" />
|
|
150
|
-
<span className="font-bold text-[0.75rem]
|
|
148
|
+
<span className="font-bold text-[0.75rem] uppercase opacity-90">
|
|
151
149
|
{currentLocale || 'en'}
|
|
152
150
|
</span>
|
|
153
151
|
</div>
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
AnchorProvider,
|
|
4
4
|
ScrollProvider,
|
|
5
5
|
useActiveAnchor,
|
|
6
|
-
} from '
|
|
7
|
-
import
|
|
8
|
-
import { useOnThisPage } from '
|
|
9
|
-
import type { OnThisPageProps } from '
|
|
6
|
+
} from '../primitives/on-this-page'
|
|
7
|
+
import { useRef, useEffect, useState, useCallback, useMemo } from 'react'
|
|
8
|
+
import { useOnThisPage } from '../../hooks/use-onthispage'
|
|
9
|
+
import type { OnThisPageProps } from '../../types'
|
|
10
10
|
import { Pencil, CircleHelp, TextAlignStart } from 'lucide-react'
|
|
11
11
|
|
|
12
12
|
export function OnThisPage({
|
|
@@ -17,7 +17,7 @@ export function OnThisPage({
|
|
|
17
17
|
}: OnThisPageProps) {
|
|
18
18
|
const { headings } = useOnThisPage(rawHeadings)
|
|
19
19
|
|
|
20
|
-
const toc =
|
|
20
|
+
const toc = useMemo(
|
|
21
21
|
() =>
|
|
22
22
|
headings.map((h) => ({ title: h.text, url: `#${h.id}`, depth: h.level })),
|
|
23
23
|
[headings],
|
|
@@ -115,7 +115,7 @@ function OnThisPageInner({
|
|
|
115
115
|
|
|
116
116
|
{(editLink || communityHelp) && (
|
|
117
117
|
<div className="mt-8 pt-8 border-t border-border-subtle space-y-4">
|
|
118
|
-
<p className="text-xs font-bold uppercase
|
|
118
|
+
<p className="text-xs font-bold uppercase text-text-main">
|
|
119
119
|
Need help?
|
|
120
120
|
</p>
|
|
121
121
|
<ul className="space-y-3">
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { usePageNav } from '
|
|
2
|
-
import { PageNav as PageNavPrimitive } from '
|
|
1
|
+
import { usePageNav } from '../../hooks/use-page-nav'
|
|
2
|
+
import { PageNav as PageNavPrimitive } from '../primitives/page-nav'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Component to display the previous and next page navigation buttons.
|
|
@@ -14,9 +14,7 @@ export function PageNav() {
|
|
|
14
14
|
<PageNavPrimitive.Root className="animate-in fade-in slide-in-from-bottom-4 duration-700">
|
|
15
15
|
{prevPage ? (
|
|
16
16
|
<PageNavPrimitive.Link to={prevPage.path} direction="prev">
|
|
17
|
-
<PageNavPrimitive.Title>
|
|
18
|
-
Previous
|
|
19
|
-
</PageNavPrimitive.Title>
|
|
17
|
+
<PageNavPrimitive.Title>Previous</PageNavPrimitive.Title>
|
|
20
18
|
<PageNavPrimitive.Description>
|
|
21
19
|
{prevPage.title}
|
|
22
20
|
</PageNavPrimitive.Description>
|
|
@@ -27,9 +25,7 @@ export function PageNav() {
|
|
|
27
25
|
|
|
28
26
|
{nextPage && (
|
|
29
27
|
<PageNavPrimitive.Link to={nextPage.path} direction="next">
|
|
30
|
-
<PageNavPrimitive.Title>
|
|
31
|
-
Next
|
|
32
|
-
</PageNavPrimitive.Title>
|
|
28
|
+
<PageNavPrimitive.Title>Next</PageNavPrimitive.Title>
|
|
33
29
|
<PageNavPrimitive.Description>
|
|
34
30
|
{nextPage.title}
|
|
35
31
|
</PageNavPrimitive.Description>
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { useEffect, useCallback } from 'react'
|
|
2
|
-
import { useSearch } from '
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
} from '@components/primitives/search-dialog'
|
|
6
|
-
import Navbar from '@components/primitives/navbar'
|
|
2
|
+
import { useSearch } from '../../hooks/use-search'
|
|
3
|
+
import { SearchDialog as SearchDialogPrimitive } from '../primitives/search-dialog'
|
|
4
|
+
import Navbar from '../primitives/navbar'
|
|
7
5
|
import { useNavigate } from 'react-router-dom'
|
|
8
|
-
import type { ComponentRoute } from '
|
|
6
|
+
import type { ComponentRoute } from '../../types'
|
|
9
7
|
interface SearchResult {
|
|
10
8
|
id: string
|
|
11
9
|
title: string
|
|
@@ -73,8 +71,12 @@ export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
|
|
|
73
71
|
>
|
|
74
72
|
<SearchDialogPrimitive.Item.Icon isHeading={item.isHeading} />
|
|
75
73
|
<div className="flex flex-col justify-center gap-0.5">
|
|
76
|
-
<SearchDialogPrimitive.Item.Title>
|
|
77
|
-
|
|
74
|
+
<SearchDialogPrimitive.Item.Title>
|
|
75
|
+
{item.title}
|
|
76
|
+
</SearchDialogPrimitive.Item.Title>
|
|
77
|
+
<SearchDialogPrimitive.Item.Bio>
|
|
78
|
+
{item.bio}
|
|
79
|
+
</SearchDialogPrimitive.Item.Bio>
|
|
78
80
|
</div>
|
|
79
81
|
</SearchDialogPrimitive.Item>
|
|
80
82
|
)}
|
|
@@ -1,52 +1,105 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo } from 'react'
|
|
2
|
-
import { useSidebar } from '
|
|
3
|
-
import { Sidebar as SidebarPrimitive } from '
|
|
2
|
+
import { useSidebar } from '../../hooks/use-sidebar'
|
|
3
|
+
import { Sidebar as SidebarPrimitive } from '../primitives/sidebar'
|
|
4
4
|
import { PoweredBy } from './powered-by'
|
|
5
5
|
import * as LucideIcons from 'lucide-react'
|
|
6
|
-
import
|
|
7
|
-
import type {
|
|
6
|
+
import virtualIcons from 'virtual:boltdocs-icons'
|
|
7
|
+
import type { ComponentRoute } from '../../types'
|
|
8
|
+
import type { BoltdocsConfig } from '../../../shared/types'
|
|
8
9
|
|
|
9
10
|
function getIcon(iconName?: string): React.ElementType | undefined {
|
|
10
11
|
if (!iconName) return undefined
|
|
11
|
-
const icons = LucideIcons as unknown as Record<
|
|
12
|
-
|
|
12
|
+
const icons = { ...LucideIcons, ...virtualIcons } as unknown as Record<
|
|
13
|
+
string,
|
|
14
|
+
React.ElementType
|
|
15
|
+
>
|
|
16
|
+
const IconComponent = icons[iconName] || icons[iconName + 'Icon']
|
|
13
17
|
return IconComponent || undefined
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
function
|
|
17
|
-
|
|
20
|
+
function SidebarSubRouteGroup({
|
|
21
|
+
route,
|
|
18
22
|
activePath,
|
|
19
23
|
getIcon,
|
|
20
24
|
}: {
|
|
21
|
-
|
|
22
|
-
slug: string
|
|
23
|
-
title: string
|
|
24
|
-
routes: ComponentRoute[]
|
|
25
|
-
icon?: string
|
|
26
|
-
}
|
|
25
|
+
route: ComponentRoute
|
|
27
26
|
activePath: string
|
|
28
27
|
getIcon: (iconName?: string) => React.ElementType | undefined
|
|
29
28
|
}) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
const isCurrent =
|
|
30
|
+
activePath ===
|
|
31
|
+
(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path)
|
|
32
|
+
|
|
33
|
+
const hasActiveSubRoute = useMemo(
|
|
34
|
+
() => route.subRoutes?.some((r) => r.path === activePath),
|
|
35
|
+
[route.subRoutes, activePath],
|
|
33
36
|
)
|
|
34
37
|
|
|
35
|
-
const [isOpen, setIsOpen] = useState(
|
|
38
|
+
const [isOpen, setIsOpen] = useState(hasActiveSubRoute || isCurrent)
|
|
36
39
|
|
|
37
40
|
useEffect(() => {
|
|
38
|
-
if (
|
|
41
|
+
if (hasActiveSubRoute || isCurrent) {
|
|
39
42
|
setIsOpen(true)
|
|
40
43
|
}
|
|
41
|
-
}, [
|
|
44
|
+
}, [hasActiveSubRoute, isCurrent])
|
|
42
45
|
|
|
43
46
|
return (
|
|
44
|
-
<SidebarPrimitive.
|
|
45
|
-
|
|
47
|
+
<SidebarPrimitive.SubGroup
|
|
48
|
+
label={route.title}
|
|
49
|
+
href={route.path}
|
|
50
|
+
active={isCurrent}
|
|
51
|
+
icon={getIcon(route.icon)}
|
|
52
|
+
badge={route.badge}
|
|
46
53
|
isOpen={isOpen}
|
|
47
54
|
onToggle={() => setIsOpen(!isOpen)}
|
|
48
55
|
>
|
|
56
|
+
{route.subRoutes?.map((subRoute: ComponentRoute) => {
|
|
57
|
+
const isSubCurrent =
|
|
58
|
+
activePath ===
|
|
59
|
+
(subRoute.path.endsWith('/') ? subRoute.path.slice(0, -1) : subRoute.path)
|
|
60
|
+
return (
|
|
61
|
+
<SidebarPrimitive.Link
|
|
62
|
+
key={subRoute.path}
|
|
63
|
+
label={subRoute.title}
|
|
64
|
+
href={subRoute.path}
|
|
65
|
+
active={isSubCurrent}
|
|
66
|
+
icon={getIcon(subRoute.icon)}
|
|
67
|
+
badge={subRoute.badge}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
})}
|
|
71
|
+
</SidebarPrimitive.SubGroup>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function SidebarGroupSection({
|
|
76
|
+
group,
|
|
77
|
+
activePath,
|
|
78
|
+
getIcon,
|
|
79
|
+
}: {
|
|
80
|
+
group: {
|
|
81
|
+
slug: string
|
|
82
|
+
title: string
|
|
83
|
+
routes: ComponentRoute[]
|
|
84
|
+
icon?: string
|
|
85
|
+
}
|
|
86
|
+
activePath: string
|
|
87
|
+
getIcon: (iconName?: string) => React.ElementType | undefined
|
|
88
|
+
}) {
|
|
89
|
+
return (
|
|
90
|
+
<SidebarPrimitive.Group title={group.title} icon={getIcon(group.icon)}>
|
|
49
91
|
{group.routes.map((route: ComponentRoute) => {
|
|
92
|
+
if (route.subRoutes && route.subRoutes.length > 0) {
|
|
93
|
+
return (
|
|
94
|
+
<SidebarSubRouteGroup
|
|
95
|
+
key={route.path}
|
|
96
|
+
route={route}
|
|
97
|
+
activePath={activePath}
|
|
98
|
+
getIcon={getIcon}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
50
103
|
const isCurrent =
|
|
51
104
|
activePath ===
|
|
52
105
|
(route.path.endsWith('/') ? route.path.slice(0, -1) : route.path)
|
|
@@ -98,7 +151,7 @@ export function Sidebar({
|
|
|
98
151
|
)}
|
|
99
152
|
|
|
100
153
|
{groups.map((group) => (
|
|
101
|
-
<
|
|
154
|
+
<SidebarGroupSection
|
|
102
155
|
key={group.slug}
|
|
103
156
|
group={group}
|
|
104
157
|
activePath={activePath}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useTabs as useTabsHook } from '
|
|
2
|
-
import { Tabs as T } from '
|
|
3
|
-
import { Link } from '
|
|
4
|
-
import type { BoltdocsTab, ComponentRoute } from '
|
|
1
|
+
import { useTabs as useTabsHook } from '../../hooks/use-tabs'
|
|
2
|
+
import { Tabs as T } from '../primitives/tabs'
|
|
3
|
+
import { Link } from '../primitives/link'
|
|
4
|
+
import type { BoltdocsTab, ComponentRoute } from '../../types'
|
|
5
5
|
import * as Icons from 'lucide-react'
|
|
6
|
-
import { getTranslated } from '
|
|
7
|
-
import { useRoutes } from '
|
|
6
|
+
import { getTranslated } from '../../utils/i18n'
|
|
7
|
+
import { useRoutes } from '../../hooks/use-routes'
|
|
8
8
|
|
|
9
9
|
export function Tabs({
|
|
10
10
|
tabs,
|
|
@@ -50,10 +50,11 @@ export function Tabs({
|
|
|
50
50
|
ref={(el: HTMLAnchorElement | null) => {
|
|
51
51
|
tabRefs.current[index] = el
|
|
52
52
|
}}
|
|
53
|
-
className={`relative flex items-center gap-2 px-4 py-3 text-sm font-medium transition-colors outline-none ${
|
|
53
|
+
className={`relative flex items-center gap-2 px-4 py-3 text-sm font-medium transition-colors outline-none ${
|
|
54
|
+
isActive
|
|
54
55
|
? 'text-primary-500'
|
|
55
56
|
: 'text-text-muted hover:text-text-main'
|
|
56
|
-
|
|
57
|
+
}`}
|
|
57
58
|
>
|
|
58
59
|
{renderTabIcon(tab.icon)}
|
|
59
60
|
<span>{getTranslated(tab.text, currentLocale)}</span>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { Sun, Moon, Monitor } from 'lucide-react'
|
|
3
|
-
import { useTheme } from '
|
|
3
|
+
import { useTheme } from '../../app/theme-context'
|
|
4
4
|
import { Button } from 'react-aria-components'
|
|
5
|
-
import { Menu } from '
|
|
5
|
+
import { Menu } from '../primitives/menu'
|
|
6
6
|
|
|
7
7
|
export function ThemeToggle() {
|
|
8
8
|
const { theme, setTheme } = useTheme()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useNavigate } from 'react-router-dom'
|
|
2
|
-
import { getBaseFilePath } from '
|
|
2
|
+
import { getBaseFilePath } from '../utils/get-base-file-path'
|
|
3
3
|
import { useRoutes } from './use-routes'
|
|
4
|
-
import {
|
|
4
|
+
import { useBoltdocsContext } from '../store/boltdocs-context'
|
|
5
5
|
|
|
6
6
|
export interface LocaleOption {
|
|
7
7
|
key: string
|
|
@@ -25,7 +25,7 @@ export function useI18n(): UseI18nReturn {
|
|
|
25
25
|
const routeContext = useRoutes()
|
|
26
26
|
const { allRoutes, currentRoute, currentLocale, config } = routeContext
|
|
27
27
|
const i18n = config.i18n
|
|
28
|
-
const setLocale =
|
|
28
|
+
const { setLocale } = useBoltdocsContext()
|
|
29
29
|
|
|
30
30
|
const handleLocaleChange = (locale: string) => {
|
|
31
31
|
if (!i18n || locale === currentLocale) return
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useLocation } from 'react-router-dom'
|
|
2
|
-
import { useConfig } from '
|
|
3
|
-
import { useTheme } from '
|
|
4
|
-
import type { NavbarLink } from '
|
|
5
|
-
import { getTranslated } from '
|
|
2
|
+
import { useConfig } from '../app/config-context'
|
|
3
|
+
import { useTheme } from '../app/theme-context'
|
|
4
|
+
import type { NavbarLink } from '../types'
|
|
5
|
+
import { getTranslated } from '../utils/i18n'
|
|
6
6
|
import { useRoutes } from './use-routes'
|
|
7
7
|
|
|
8
8
|
export function useNavbar() {
|
|
9
9
|
const config = useConfig()
|
|
10
|
-
const { theme } = useTheme()
|
|
10
|
+
const { theme, resolvedTheme } = useTheme()
|
|
11
11
|
const location = useLocation()
|
|
12
12
|
const { currentLocale } = useRoutes()
|
|
13
13
|
|
|
@@ -67,11 +67,13 @@ export function useNavbar() {
|
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
const logo = themeConfig.logo
|
|
70
|
+
// Use resolvedTheme so 'system' correctly maps to 'dark' or 'light'
|
|
71
|
+
// based on the OS preference, instead of always falling back to 'light'.
|
|
70
72
|
const logoSrc = !logo
|
|
71
73
|
? null
|
|
72
74
|
: typeof logo === 'string'
|
|
73
75
|
? logo
|
|
74
|
-
:
|
|
76
|
+
: resolvedTheme === 'dark'
|
|
75
77
|
? (logo as any).dark
|
|
76
78
|
: (logo as any).light
|
|
77
79
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useLocation } from 'react-router-dom'
|
|
2
|
-
import { useConfig } from '
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useConfig } from '../app/config-context'
|
|
3
|
+
import { useRoutesContext } from '../app/routes-context'
|
|
4
|
+
import { useBoltdocsContext } from '../store/boltdocs-context'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Hook to access the framework's routing state.
|
|
@@ -9,17 +9,25 @@ import { useBoltdocsStore } from '../store/use-boltdocs-store'
|
|
|
9
9
|
* version and locale.
|
|
10
10
|
*/
|
|
11
11
|
export function useRoutes() {
|
|
12
|
-
const { routes: allRoutes } =
|
|
12
|
+
const { routes: allRoutes } = useRoutesContext()
|
|
13
13
|
const config = useConfig()
|
|
14
14
|
const location = useLocation()
|
|
15
15
|
|
|
16
16
|
// Use Zustand store for active state
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const {
|
|
18
|
+
hasHydrated,
|
|
19
|
+
currentLocale: currentLocaleStore,
|
|
20
|
+
currentVersion: currentVersionStore,
|
|
21
|
+
} = useBoltdocsContext()
|
|
22
|
+
|
|
23
|
+
const normalize = (p: string) =>
|
|
24
|
+
p.endsWith('/') && p.length > 1 ? p.slice(0, -1) : p
|
|
25
|
+
const currentPath = normalize(location.pathname)
|
|
20
26
|
|
|
21
27
|
// Find the current route matching the pathname
|
|
22
|
-
const currentRoute = allRoutes
|
|
28
|
+
const currentRoute = allRoutes?.find?.(
|
|
29
|
+
(r) => normalize(r.path) === currentPath,
|
|
30
|
+
)
|
|
23
31
|
|
|
24
32
|
// Derive current locale and version
|
|
25
33
|
// Priority: URL (currentRoute) > Zustand Store (Persistence) > Config Default
|
|
@@ -36,7 +44,7 @@ export function useRoutes() {
|
|
|
36
44
|
: undefined
|
|
37
45
|
|
|
38
46
|
// Filter routes to those matching the current version and locale
|
|
39
|
-
const routes = allRoutes
|
|
47
|
+
const routes = allRoutes?.filter?.((r) => {
|
|
40
48
|
const localeMatch = config.i18n
|
|
41
49
|
? (r.locale || config.i18n.defaultLocale) === currentLocale
|
|
42
50
|
: true
|
|
@@ -53,7 +61,7 @@ export function useRoutes() {
|
|
|
53
61
|
const isCurrentRoutePrefixed = !!currentRoute?.locale
|
|
54
62
|
const isRoutePrefixed = !!r.locale
|
|
55
63
|
|
|
56
|
-
const hasAlternate = allRoutes
|
|
64
|
+
const hasAlternate = allRoutes?.some?.(
|
|
57
65
|
(alt) =>
|
|
58
66
|
alt !== r &&
|
|
59
67
|
alt.filePath === r.filePath &&
|
|
@@ -78,7 +86,7 @@ export function useRoutes() {
|
|
|
78
86
|
config.i18n?.locales[currentLocale as string] ||
|
|
79
87
|
currentLocale
|
|
80
88
|
|
|
81
|
-
const currentVersionConfig = config.versions?.versions
|
|
89
|
+
const currentVersionConfig = config.versions?.versions?.find?.(
|
|
82
90
|
(v) => v.path === currentVersion,
|
|
83
91
|
)
|
|
84
92
|
const currentVersionLabel = currentVersionConfig?.label || currentVersion
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState, useMemo, useEffect } from 'react'
|
|
2
2
|
import { Index } from 'flexsearch'
|
|
3
3
|
import { useRoutes } from './use-routes'
|
|
4
|
-
import type { ComponentRoute } from '
|
|
4
|
+
import type { ComponentRoute } from '../types'
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import searchData from 'virtual:boltdocs-search'
|
|
7
7
|
|