boltdocs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/CodeBlock-37XMKCYY.mjs +7 -0
  2. package/dist/PackageManagerTabs-4NWXLXQO.mjs +314 -0
  3. package/dist/Playground-OE2OE6B6.mjs +7 -0
  4. package/dist/SearchDialog-FTOQZ763.mjs +187 -0
  5. package/dist/SearchDialog-ZAZXYIFX.css +2147 -0
  6. package/dist/Video-I6QY4X7J.mjs +7 -0
  7. package/dist/chunk-2YRDWM6O.mjs +56 -0
  8. package/dist/chunk-PN4GCTYG.mjs +67 -0
  9. package/dist/chunk-X2TDGMTR.mjs +64 -0
  10. package/dist/chunk-X6BYQHVC.mjs +12 -0
  11. package/dist/chunk-Z7JHYNAS.mjs +57 -0
  12. package/dist/chunk-ZFCOLEXN.mjs +1644 -0
  13. package/dist/client/index.css +2147 -0
  14. package/dist/client/index.d.mts +298 -0
  15. package/dist/client/index.d.ts +298 -0
  16. package/dist/client/index.js +2793 -0
  17. package/dist/client/index.mjs +63 -0
  18. package/dist/client/ssr.css +2147 -0
  19. package/dist/client/ssr.d.mts +25 -0
  20. package/dist/client/ssr.d.ts +25 -0
  21. package/dist/client/ssr.js +2727 -0
  22. package/dist/client/ssr.mjs +32 -0
  23. package/dist/config-D2XmHJYe.d.mts +122 -0
  24. package/dist/config-D2XmHJYe.d.ts +122 -0
  25. package/dist/index-CRQKWAeo.d.mts +82 -0
  26. package/dist/index-CRQKWAeo.d.ts +82 -0
  27. package/dist/node/cli/index.d.mts +1 -0
  28. package/dist/node/cli/index.d.ts +1 -0
  29. package/dist/node/cli/index.js +199 -0
  30. package/dist/node/cli/index.mjs +154 -0
  31. package/dist/node/index.d.mts +79 -0
  32. package/dist/node/index.d.ts +79 -0
  33. package/dist/node/index.js +797 -0
  34. package/dist/node/index.mjs +719 -0
  35. package/package.json +79 -0
  36. package/src/client/app/index.tsx +422 -0
  37. package/src/client/app/preload.tsx +56 -0
  38. package/src/client/index.ts +40 -0
  39. package/src/client/ssr.tsx +50 -0
  40. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +76 -0
  41. package/src/client/theme/components/CodeBlock/index.ts +1 -0
  42. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +154 -0
  43. package/src/client/theme/components/PackageManagerTabs/index.ts +1 -0
  44. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +64 -0
  45. package/src/client/theme/components/Playground/Playground.tsx +86 -0
  46. package/src/client/theme/components/Playground/index.ts +1 -0
  47. package/src/client/theme/components/Playground/playground.css +168 -0
  48. package/src/client/theme/components/Video/Video.tsx +84 -0
  49. package/src/client/theme/components/Video/index.ts +1 -0
  50. package/src/client/theme/components/Video/video.css +41 -0
  51. package/src/client/theme/components/mdx/Admonition.tsx +80 -0
  52. package/src/client/theme/components/mdx/Badge.tsx +31 -0
  53. package/src/client/theme/components/mdx/Button.tsx +50 -0
  54. package/src/client/theme/components/mdx/Card.tsx +80 -0
  55. package/src/client/theme/components/mdx/List.tsx +57 -0
  56. package/src/client/theme/components/mdx/Tabs.tsx +94 -0
  57. package/src/client/theme/components/mdx/index.ts +18 -0
  58. package/src/client/theme/components/mdx/mdx-components.css +405 -0
  59. package/src/client/theme/icons/bun.tsx +62 -0
  60. package/src/client/theme/icons/deno.tsx +20 -0
  61. package/src/client/theme/icons/discord.tsx +12 -0
  62. package/src/client/theme/icons/github.tsx +15 -0
  63. package/src/client/theme/icons/npm.tsx +13 -0
  64. package/src/client/theme/icons/pnpm.tsx +72 -0
  65. package/src/client/theme/icons/twitter.tsx +12 -0
  66. package/src/client/theme/styles/home.css +60 -0
  67. package/src/client/theme/styles/markdown.css +343 -0
  68. package/src/client/theme/styles/variables.css +162 -0
  69. package/src/client/theme/styles.css +38 -0
  70. package/src/client/theme/ui/BackgroundGradient/BackgroundGradient.tsx +10 -0
  71. package/src/client/theme/ui/BackgroundGradient/index.ts +1 -0
  72. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +68 -0
  73. package/src/client/theme/ui/Breadcrumbs/index.ts +1 -0
  74. package/src/client/theme/ui/Footer/footer.css +32 -0
  75. package/src/client/theme/ui/Head/Head.tsx +69 -0
  76. package/src/client/theme/ui/Head/index.ts +1 -0
  77. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +125 -0
  78. package/src/client/theme/ui/LanguageSwitcher/index.ts +1 -0
  79. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +98 -0
  80. package/src/client/theme/ui/Layout/Layout.tsx +213 -0
  81. package/src/client/theme/ui/Layout/base.css +76 -0
  82. package/src/client/theme/ui/Layout/index.ts +2 -0
  83. package/src/client/theme/ui/Layout/pagination.css +72 -0
  84. package/src/client/theme/ui/Layout/responsive.css +40 -0
  85. package/src/client/theme/ui/Link/Link.tsx +202 -0
  86. package/src/client/theme/ui/Link/index.ts +2 -0
  87. package/src/client/theme/ui/Loading/Loading.tsx +10 -0
  88. package/src/client/theme/ui/Loading/index.ts +1 -0
  89. package/src/client/theme/ui/Loading/loading.css +30 -0
  90. package/src/client/theme/ui/Navbar/GithubStars.tsx +27 -0
  91. package/src/client/theme/ui/Navbar/Navbar.tsx +145 -0
  92. package/src/client/theme/ui/Navbar/index.ts +2 -0
  93. package/src/client/theme/ui/Navbar/navbar.css +233 -0
  94. package/src/client/theme/ui/NotFound/NotFound.tsx +20 -0
  95. package/src/client/theme/ui/NotFound/index.ts +1 -0
  96. package/src/client/theme/ui/NotFound/not-found.css +64 -0
  97. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +192 -0
  98. package/src/client/theme/ui/OnThisPage/index.ts +1 -0
  99. package/src/client/theme/ui/OnThisPage/toc.css +132 -0
  100. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +18 -0
  101. package/src/client/theme/ui/PoweredBy/index.ts +1 -0
  102. package/src/client/theme/ui/PoweredBy/powered-by.css +76 -0
  103. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +199 -0
  104. package/src/client/theme/ui/SearchDialog/index.ts +1 -0
  105. package/src/client/theme/ui/SearchDialog/search.css +152 -0
  106. package/src/client/theme/ui/Sidebar/Sidebar.tsx +200 -0
  107. package/src/client/theme/ui/Sidebar/index.ts +1 -0
  108. package/src/client/theme/ui/Sidebar/sidebar.css +269 -0
  109. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +69 -0
  110. package/src/client/theme/ui/ThemeToggle/index.ts +1 -0
  111. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +136 -0
  112. package/src/client/theme/ui/VersionSwitcher/index.ts +1 -0
  113. package/src/client/utils.ts +26 -0
  114. package/src/node/cache.ts +94 -0
  115. package/src/node/cli/commands/config.ts +15 -0
  116. package/src/node/cli/commands/generate-css.ts +24 -0
  117. package/src/node/cli/constants.ts +70 -0
  118. package/src/node/cli/index.ts +22 -0
  119. package/src/node/config.ts +185 -0
  120. package/src/node/index.ts +21 -0
  121. package/src/node/mdx.ts +41 -0
  122. package/src/node/plugin/entry.ts +58 -0
  123. package/src/node/plugin/html.ts +55 -0
  124. package/src/node/plugin/index.ts +190 -0
  125. package/src/node/plugin/types.ts +11 -0
  126. package/src/node/routes/cache.ts +24 -0
  127. package/src/node/routes/index.ts +152 -0
  128. package/src/node/routes/parser.ts +127 -0
  129. package/src/node/routes/sorter.ts +42 -0
  130. package/src/node/routes/types.ts +49 -0
  131. package/src/node/ssg/index.ts +110 -0
  132. package/src/node/ssg/meta.ts +34 -0
  133. package/src/node/ssg/options.ts +13 -0
  134. package/src/node/ssg/sitemap.ts +54 -0
  135. package/src/node/utils.ts +134 -0
  136. package/tsconfig.json +20 -0
  137. package/tsup.config.ts +22 -0
@@ -0,0 +1,69 @@
1
+ import { useEffect } from "react";
2
+ import { useLocation } from "react-router-dom";
3
+
4
+ interface HeadProps {
5
+ siteTitle: string;
6
+ siteDescription?: string;
7
+ routes: Array<{ path: string; title: string; description?: string }>;
8
+ }
9
+
10
+ export function Head({ siteTitle, siteDescription, routes }: HeadProps) {
11
+ const location = useLocation();
12
+
13
+ useEffect(() => {
14
+ // Find the current route's metadata
15
+ const currentRoute = routes.find((r) => r.path === location.pathname);
16
+ const pageTitle = currentRoute?.title;
17
+ const pageDescription = currentRoute?.description || siteDescription || "";
18
+
19
+ // Update document title
20
+ document.title = pageTitle ? `${pageTitle} | ${siteTitle}` : siteTitle;
21
+
22
+ // Update or create meta description
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;
32
+
33
+ // Update OG tags
34
+ setMetaTag("property", "og:title", document.title);
35
+ setMetaTag("property", "og:description", pageDescription);
36
+ setMetaTag("property", "og:type", "article");
37
+ setMetaTag("property", "og:url", window.location.href);
38
+
39
+ // Twitter card
40
+ setMetaTag("name", "twitter:card", "summary");
41
+ setMetaTag("name", "twitter:title", document.title);
42
+ setMetaTag("name", "twitter:description", pageDescription);
43
+
44
+ // Canonical URL
45
+ let canonical = document.querySelector(
46
+ 'link[rel="canonical"]',
47
+ ) as HTMLLinkElement | null;
48
+ if (!canonical) {
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]);
55
+
56
+ return null; // This component only manages <head>, no visual output
57
+ }
58
+
59
+ function setMetaTag(attr: "name" | "property", key: string, content: string) {
60
+ let tag = document.querySelector(
61
+ `meta[${attr}="${key}"]`,
62
+ ) as HTMLMetaElement | null;
63
+ if (!tag) {
64
+ tag = document.createElement("meta");
65
+ tag.setAttribute(attr, key);
66
+ document.head.appendChild(tag);
67
+ }
68
+ tag.content = content;
69
+ }
@@ -0,0 +1 @@
1
+ export { Head } from "./Head";
@@ -0,0 +1,125 @@
1
+ import React, { useState, useRef, useEffect } from "react";
2
+ import { Globe, ChevronDown } from "lucide-react";
3
+ import { useNavigate, useLocation } from "react-router-dom";
4
+ import { BoltdocsI18nConfig } from "../../../../node/config";
5
+ import { ComponentRoute } from "../../../app";
6
+
7
+ function getBaseFilePath(
8
+ filePath: string,
9
+ version: string | undefined,
10
+ locale: string | undefined,
11
+ ): string {
12
+ let path = filePath;
13
+ if (version && (path === version || path.startsWith(version + "/"))) {
14
+ path = path === version ? "index.md" : path.slice(version.length + 1);
15
+ }
16
+ if (locale && (path === locale || path.startsWith(locale + "/"))) {
17
+ path = path === locale ? "index.md" : path.slice(locale.length + 1);
18
+ }
19
+ return path;
20
+ }
21
+
22
+ export function LanguageSwitcher({
23
+ i18n,
24
+ currentLocale,
25
+ allRoutes,
26
+ }: {
27
+ i18n: BoltdocsI18nConfig;
28
+ currentLocale: string;
29
+ allRoutes: ComponentRoute[];
30
+ }) {
31
+ const [isOpen, setIsOpen] = useState(false);
32
+ const dropdownRef = useRef<HTMLDivElement>(null);
33
+ const navigate = useNavigate();
34
+ const location = useLocation();
35
+
36
+ useEffect(() => {
37
+ function handleClickOutside(event: MouseEvent) {
38
+ if (
39
+ dropdownRef.current &&
40
+ !dropdownRef.current.contains(event.target as Node)
41
+ ) {
42
+ setIsOpen(false);
43
+ }
44
+ }
45
+ document.addEventListener("mousedown", handleClickOutside);
46
+ return () => document.removeEventListener("mousedown", handleClickOutside);
47
+ }, []);
48
+
49
+ const handleSelect = (locale: string) => {
50
+ setIsOpen(false);
51
+ if (locale === currentLocale) return;
52
+
53
+ const currentRoute = allRoutes.find((r) => r.path === location.pathname);
54
+ let targetPath = "/";
55
+
56
+ if (currentRoute) {
57
+ const baseFile = getBaseFilePath(
58
+ currentRoute.filePath,
59
+ currentRoute.version,
60
+ currentRoute.locale,
61
+ );
62
+ const targetRoute = allRoutes.find(
63
+ (r) =>
64
+ getBaseFilePath(r.filePath, r.version, r.locale) === baseFile &&
65
+ (r.locale || i18n.defaultLocale) === locale &&
66
+ r.version === currentRoute.version,
67
+ );
68
+ if (targetRoute) {
69
+ targetPath = targetRoute.path;
70
+ } else {
71
+ const defaultIndexRoute = allRoutes.find(
72
+ (r) =>
73
+ getBaseFilePath(r.filePath, r.version, r.locale) === "index.md" &&
74
+ (r.locale || i18n.defaultLocale) === locale &&
75
+ r.version === currentRoute.version,
76
+ );
77
+ targetPath = defaultIndexRoute
78
+ ? defaultIndexRoute.path
79
+ : locale === i18n.defaultLocale
80
+ ? currentRoute.version
81
+ ? `/${currentRoute.version}`
82
+ : "/"
83
+ : currentRoute.version
84
+ ? `/${currentRoute.version}/${locale}`
85
+ : `/${locale}`;
86
+ }
87
+ } else {
88
+ targetPath = locale === i18n.defaultLocale ? "/" : `/${locale}`;
89
+ }
90
+
91
+ navigate(targetPath);
92
+ };
93
+
94
+ return (
95
+ <div className="boltdocs-language-switcher" ref={dropdownRef}>
96
+ <button
97
+ className="language-btn"
98
+ onClick={() => setIsOpen(!isOpen)}
99
+ aria-label="Switch language"
100
+ aria-expanded={isOpen}
101
+ aria-haspopup="listbox"
102
+ >
103
+ <Globe size={18} />
104
+ <span className="language-label">
105
+ {i18n.locales[currentLocale] || currentLocale}
106
+ </span>
107
+ <ChevronDown size={14} />
108
+ </button>
109
+
110
+ {isOpen && (
111
+ <div className="language-dropdown">
112
+ {Object.entries(i18n.locales).map(([key, label]) => (
113
+ <button
114
+ key={key}
115
+ className={`language-option ${key === currentLocale ? "active" : ""}`}
116
+ onClick={() => handleSelect(key)}
117
+ >
118
+ {label}
119
+ </button>
120
+ ))}
121
+ </div>
122
+ )}
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1 @@
1
+ export { LanguageSwitcher } from "./LanguageSwitcher";
@@ -0,0 +1,98 @@
1
+ .boltdocs-language-switcher {
2
+ position: relative;
3
+ display: flex;
4
+ align-items: center;
5
+ }
6
+
7
+ .language-btn {
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 0.35rem;
11
+ padding: 0.4rem 0.6rem;
12
+ background: transparent;
13
+ color: var(--ld-text-muted);
14
+ border: none;
15
+ border-radius: var(--ld-radius-md);
16
+ font-size: 0.8125rem;
17
+ font-weight: 500;
18
+ cursor: pointer;
19
+ transition:
20
+ background-color 0.2s,
21
+ color 0.2s;
22
+ }
23
+
24
+ .language-btn svg {
25
+ width: 15px;
26
+ height: 15px;
27
+ opacity: 0.8;
28
+ }
29
+
30
+ .language-btn:hover {
31
+ background-color: var(--ld-bg-mute);
32
+ color: var(--ld-text-main);
33
+ }
34
+
35
+ .language-dropdown {
36
+ position: absolute;
37
+ top: 100%;
38
+ right: 0;
39
+ margin-top: 0.35rem;
40
+ min-width: 130px;
41
+ background-color: rgba(15, 15, 24, 0.85); /* Smooth translucent dark match */
42
+ backdrop-filter: blur(12px);
43
+ -webkit-backdrop-filter: blur(12px);
44
+ border: 1px solid var(--ld-border-strong);
45
+ border-radius: var(--ld-radius-md);
46
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
47
+ padding: 0.35rem;
48
+ z-index: 200;
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: 0.15rem;
52
+ animation: languageDropdownIn 0.15s ease-out;
53
+ }
54
+
55
+ @keyframes languageDropdownIn {
56
+ from {
57
+ opacity: 0;
58
+ transform: translateY(-4px);
59
+ }
60
+ to {
61
+ opacity: 1;
62
+ transform: translateY(0);
63
+ }
64
+ }
65
+
66
+ /* Light mode fallback */
67
+ [data-theme="light"] .language-dropdown,
68
+ .theme-light .language-dropdown {
69
+ background-color: rgba(255, 255, 255, 0.9);
70
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
71
+ }
72
+
73
+ .language-option {
74
+ display: block;
75
+ width: 100%;
76
+ text-align: left;
77
+ padding: 0.4rem 0.6rem;
78
+ background: transparent;
79
+ border: none;
80
+ border-radius: var(--ld-radius-sm);
81
+ color: var(--ld-text-muted);
82
+ font-size: 0.8125rem;
83
+ font-weight: 500;
84
+ cursor: pointer;
85
+ transition:
86
+ background-color 0.2s,
87
+ color 0.2s;
88
+ }
89
+
90
+ .language-option:hover {
91
+ background-color: var(--ld-bg-mute);
92
+ color: var(--ld-text-main);
93
+ }
94
+
95
+ .language-option.active {
96
+ background-color: var(--ld-color-primary-muted);
97
+ color: var(--ld-color-primary);
98
+ }
@@ -0,0 +1,213 @@
1
+ import React, { useState } from "react";
2
+ import { useLocation } from "react-router-dom";
3
+ import { Link } from "../Link";
4
+ import { ChevronLeft, ChevronRight, Menu } from "lucide-react";
5
+ import { usePreload } from "../../../app/preload";
6
+ import { BoltdocsConfig } from "../../../../node/config";
7
+ import { ComponentRoute } from "../../../app";
8
+ export { Navbar } from "../Navbar";
9
+ export { Sidebar } from "../Sidebar";
10
+ export { OnThisPage } from "../OnThisPage";
11
+ export { Head } from "../Head";
12
+ export { Breadcrumbs } from "../Breadcrumbs";
13
+ export { BackgroundGradient } from "../BackgroundGradient";
14
+
15
+ import { Navbar } from "../Navbar";
16
+ import { Sidebar } from "../Sidebar";
17
+ import { OnThisPage } from "../OnThisPage";
18
+ import { Head } from "../Head";
19
+ import { Breadcrumbs } from "../Breadcrumbs";
20
+ import { BackgroundGradient } from "../BackgroundGradient";
21
+ import "../../styles.css";
22
+
23
+ export interface ThemeLayoutProps {
24
+ config: BoltdocsConfig;
25
+ routes: ComponentRoute[];
26
+ children: React.ReactNode;
27
+ /** Custom navbar component (slots) */
28
+ navbar?: React.ReactNode;
29
+ /** Custom sidebar component (slots) */
30
+ sidebar?: React.ReactNode;
31
+ /** Custom table of contents (OnThisPage) component (slots) */
32
+ toc?: React.ReactNode;
33
+ /** Custom background component (slots) */
34
+ background?: React.ReactNode;
35
+ /** Custom head/metadata component (slots) */
36
+ head?: React.ReactNode;
37
+ /** Custom breadcrumbs component (slots) */
38
+ breadcrumbs?: React.ReactNode;
39
+ /** Custom class name for the root layout element */
40
+ className?: string;
41
+ /** Custom styles for the root layout element */
42
+ style?: React.CSSProperties;
43
+ }
44
+
45
+ /**
46
+ * The main structural layout for documentation pages.
47
+ * Integrates the Navbar, Sidebar, and OnThisPage components into a cohesive shell.
48
+ * It also manages mobile interaction states like the sidebar overlay toggle.
49
+ *
50
+ * @param config - The global Boltdocs configuration object
51
+ * @param routes - The array of available doc routes (used to render the sidebar)
52
+ */
53
+ export function ThemeLayout({
54
+ config,
55
+ routes,
56
+ children,
57
+ navbar,
58
+ sidebar,
59
+ toc,
60
+ background,
61
+ head,
62
+ breadcrumbs,
63
+ className = "",
64
+ style,
65
+ }: ThemeLayoutProps) {
66
+ const [isSidebarOpen, setIsSidebarOpen] = useState(true);
67
+ const siteTitle = config.themeConfig?.title || "Boltdocs";
68
+ const siteDescription = config.themeConfig?.description || "";
69
+ const location = useLocation();
70
+
71
+ // Compute prev/next pages and locale
72
+ const currentIndex = routes.findIndex((r) => r.path === location.pathname);
73
+ const currentRoute = routes[currentIndex];
74
+ // Determine current locale (fallback to default)
75
+ const currentLocale = config.i18n
76
+ ? currentRoute?.locale || config.i18n.defaultLocale
77
+ : undefined;
78
+
79
+ // Determine current version (fallback to default)
80
+ const currentVersion = config.versions
81
+ ? currentRoute?.version || config.versions.defaultVersion
82
+ : undefined;
83
+
84
+ // Filter routes for sidebar, search, and navigation to only ones in the current locale and version
85
+ const filteredRoutes = routes.filter((r) => {
86
+ const localeMatch = config.i18n
87
+ ? (r.locale || config.i18n.defaultLocale) === currentLocale
88
+ : true;
89
+ const versionMatch = config.versions
90
+ ? (r.version || config.versions.defaultVersion) === currentVersion
91
+ : true;
92
+ return localeMatch && versionMatch;
93
+ });
94
+
95
+ const localIndex = filteredRoutes.findIndex(
96
+ (r) => r.path === location.pathname,
97
+ );
98
+ const prevPage = localIndex > 0 ? filteredRoutes[localIndex - 1] : null;
99
+ const nextPage =
100
+ localIndex >= 0 && localIndex < filteredRoutes.length - 1
101
+ ? filteredRoutes[localIndex + 1]
102
+ : null;
103
+
104
+ const { preload } = usePreload();
105
+ React.useEffect(() => {
106
+ if (prevPage?.path) preload(prevPage.path);
107
+ if (nextPage?.path) preload(nextPage.path);
108
+ }, [prevPage, nextPage, preload]);
109
+
110
+ return (
111
+ <div className={`boltdocs-layout ${className}`} style={style}>
112
+ {background !== undefined ? background : <BackgroundGradient />}
113
+ {head !== undefined ? (
114
+ head
115
+ ) : (
116
+ <Head
117
+ siteTitle={siteTitle}
118
+ siteDescription={siteDescription}
119
+ routes={routes}
120
+ />
121
+ )}
122
+ {navbar !== undefined ? (
123
+ navbar
124
+ ) : (
125
+ <Navbar
126
+ config={config}
127
+ routes={filteredRoutes}
128
+ allRoutes={routes}
129
+ currentLocale={currentLocale}
130
+ currentVersion={currentVersion}
131
+ />
132
+ )}
133
+ <div
134
+ className={`boltdocs-main-container ${!isSidebarOpen ? "sidebar-collapsed" : ""}`}
135
+ >
136
+ {sidebar !== undefined ? (
137
+ sidebar
138
+ ) : (
139
+ <Sidebar
140
+ routes={filteredRoutes}
141
+ config={config}
142
+ onCollapse={() => setIsSidebarOpen(false)}
143
+ />
144
+ )}
145
+
146
+ {/* Floating Expand Button when Sidebar is Collapsed */}
147
+ {sidebar === undefined && (
148
+ <button
149
+ className="sidebar-toggle-floating"
150
+ onClick={() => setIsSidebarOpen(true)}
151
+ aria-label="Expand Sidebar"
152
+ title="Expand Sidebar"
153
+ >
154
+ <Menu size={20} />
155
+ </button>
156
+ )}
157
+
158
+ <main className="boltdocs-content">
159
+ {breadcrumbs !== undefined ? (
160
+ breadcrumbs
161
+ ) : (
162
+ <Breadcrumbs routes={filteredRoutes} config={config} />
163
+ )}
164
+ <div className="boltdocs-page">{children}</div>
165
+
166
+ {/* Prev / Next Navigation */}
167
+ {(prevPage || nextPage) && (
168
+ <nav className="page-nav" aria-label="Pagination">
169
+ {prevPage ? (
170
+ <Link
171
+ to={prevPage.path || "/"}
172
+ className="page-nav-link page-nav-link--prev"
173
+ >
174
+ <div className="page-nav-info">
175
+ <span className="page-nav-label">Previous</span>
176
+ <span className="page-nav-title">{prevPage.title}</span>
177
+ </div>
178
+ <ChevronLeft className="page-nav-arrow" size={16} />
179
+ </Link>
180
+ ) : (
181
+ <span />
182
+ )}
183
+ {nextPage ? (
184
+ <Link
185
+ to={nextPage.path || "/"}
186
+ className="page-nav-link page-nav-link--next"
187
+ >
188
+ <div className="page-nav-info">
189
+ <span className="page-nav-label">Next</span>
190
+ <span className="page-nav-title">{nextPage.title}</span>
191
+ </div>
192
+ <ChevronRight className="page-nav-arrow" size={16} />
193
+ </Link>
194
+ ) : (
195
+ <span />
196
+ )}
197
+ </nav>
198
+ )}
199
+ </main>
200
+ {toc !== undefined ? (
201
+ toc
202
+ ) : (
203
+ <OnThisPage
204
+ headings={routes[currentIndex]?.headings}
205
+ editLink={config.themeConfig?.editLink}
206
+ communityHelp={config.themeConfig?.communityHelp}
207
+ filePath={routes[currentIndex]?.filePath}
208
+ />
209
+ )}
210
+ </div>
211
+ </div>
212
+ );
213
+ }
@@ -0,0 +1,76 @@
1
+ /* ─── Reset ──────────────────────────────────────────────── */
2
+ *,
3
+ *::before,
4
+ *::after {
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ body {
9
+ margin: 0;
10
+ font-family: var(--ld-font-sans);
11
+ background-color: var(--ld-bg-main);
12
+ color: var(--ld-text-main);
13
+ line-height: 1.7;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
16
+ }
17
+
18
+ a {
19
+ text-decoration: none;
20
+
21
+ &:hover {
22
+ text-decoration: none;
23
+ }
24
+ }
25
+
26
+ /* ─── Layout Shell ───────────────────────────────────────── */
27
+ .boltdocs-layout {
28
+ display: flex;
29
+ flex-direction: column;
30
+ min-height: 100vh;
31
+ }
32
+
33
+ .boltdocs-main-container {
34
+ display: flex;
35
+ flex: 1;
36
+ width: 100%;
37
+ max-width: 1440px;
38
+ margin: 0 auto;
39
+ padding: 0 1.5rem;
40
+ /* aligns with navbar */
41
+ }
42
+
43
+ /* ─── Background Glow ────────────────────────────────────── */
44
+ .boltdocs-background-glow {
45
+ position: fixed;
46
+ top: 0;
47
+ left: 0;
48
+ right: 0;
49
+ height: 100vh;
50
+ overflow: hidden;
51
+ z-index: -1;
52
+ pointer-events: none;
53
+ }
54
+
55
+ .glow-shape {
56
+ position: absolute;
57
+ border-radius: 50%;
58
+ filter: blur(100px);
59
+ opacity: 0.15;
60
+ top: -100px;
61
+ }
62
+
63
+ .glow-1 {
64
+ width: 600px;
65
+ height: 600px;
66
+ background: var(--ld-glow-1-bg);
67
+ left: -200px;
68
+ }
69
+
70
+ .glow-2 {
71
+ width: 800px;
72
+ height: 800px;
73
+ background: var(--ld-glow-2-bg);
74
+ right: -300px;
75
+ top: -200px;
76
+ }
@@ -0,0 +1,2 @@
1
+ export { ThemeLayout } from "./Layout";
2
+ export type { ThemeLayoutProps } from "./Layout";
@@ -0,0 +1,72 @@
1
+ /* ═══════════════════════════════════════════════════════════
2
+ PREV / NEXT NAVIGATION
3
+ ═══════════════════════════════════════════════════════════ */
4
+ .page-nav {
5
+ display: grid;
6
+ grid-template-columns: 1fr 1fr;
7
+ gap: 0.75rem;
8
+ margin-top: 2.5rem;
9
+ padding-top: 1.5rem;
10
+ border-top: 1px solid var(--ld-border-subtle);
11
+ }
12
+
13
+ .page-nav-link {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ gap: 0.75rem;
18
+ padding: 0.875rem 1rem;
19
+ border-radius: var(--ld-radius-md);
20
+ border: 1px solid var(--ld-border-subtle);
21
+ text-decoration: none;
22
+ transition: all 0.2s;
23
+ }
24
+
25
+ .page-nav-link:hover {
26
+ border-color: var(--ld-color-primary);
27
+ background-color: var(--ld-color-primary-muted);
28
+ }
29
+
30
+ .page-nav-link--prev {
31
+ flex-direction: row-reverse;
32
+ }
33
+
34
+ .page-nav-link--next {
35
+ text-align: left;
36
+ }
37
+
38
+ .page-nav-info {
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: 0.15rem;
42
+ min-width: 0;
43
+ }
44
+
45
+ .page-nav-label {
46
+ font-size: 0.6875rem;
47
+ font-weight: 600;
48
+ text-transform: uppercase;
49
+ letter-spacing: 0.05em;
50
+ color: var(--ld-text-dim);
51
+ display: block;
52
+ }
53
+
54
+ .page-nav-title {
55
+ font-size: 0.8125rem;
56
+ font-weight: 600;
57
+ color: var(--ld-text-main);
58
+ display: block;
59
+ white-space: nowrap;
60
+ overflow: hidden;
61
+ text-overflow: ellipsis;
62
+ }
63
+
64
+ .page-nav-arrow {
65
+ color: var(--ld-text-dim);
66
+ flex-shrink: 0;
67
+ transition: color 0.2s;
68
+ }
69
+
70
+ .page-nav-link:hover .page-nav-arrow {
71
+ color: var(--ld-color-primary);
72
+ }
@@ -0,0 +1,40 @@
1
+ /* ═══════════════════════════════════════════════════════════
2
+ RESPONSIVE
3
+ ═══════════════════════════════════════════════════════════ */
4
+ @media (max-width: 1100px) {
5
+ .boltdocs-on-this-page {
6
+ display: none;
7
+ }
8
+ }
9
+
10
+ @media (max-width: 768px) {
11
+ .boltdocs-sidebar {
12
+ display: none;
13
+ }
14
+
15
+ .sidebar-toggle-floating {
16
+ display: none !important;
17
+ }
18
+
19
+ .boltdocs-content {
20
+ padding: 1.5rem 1rem 3rem;
21
+ }
22
+
23
+ .hero-title {
24
+ font-size: 2.25rem;
25
+ }
26
+
27
+ .ld-cards--2,
28
+ .ld-cards--3,
29
+ .ld-cards--4 {
30
+ grid-template-columns: 1fr;
31
+ }
32
+
33
+ .navbar-search {
34
+ display: none;
35
+ }
36
+
37
+ .page-nav {
38
+ flex-direction: column;
39
+ }
40
+ }