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,152 @@
1
+ /* ═══════════════════════════════════════════════════════════
2
+ SEARCH MODAL
3
+ ═══════════════════════════════════════════════════════════ */
4
+ .boltdocs-search-overlay {
5
+ position: fixed;
6
+ top: 0;
7
+ left: 0;
8
+ right: 0;
9
+ bottom: 0;
10
+ background-color: rgba(0, 0, 0, 0.6);
11
+ backdrop-filter: blur(4px);
12
+ -webkit-backdrop-filter: blur(4px);
13
+ z-index: 999;
14
+ display: flex;
15
+ justify-content: center;
16
+ align-items: flex-start;
17
+ padding-top: 10vh;
18
+ }
19
+
20
+ .boltdocs-search-modal {
21
+ width: 100%;
22
+ max-width: 560px;
23
+ background-color: var(--ld-bg-main);
24
+ border: 1px solid var(--ld-border-subtle);
25
+ border-radius: var(--ld-radius-lg);
26
+ box-shadow: 0 16px 64px rgba(0, 0, 0, 0.4);
27
+ overflow: hidden;
28
+ display: flex;
29
+ flex-direction: column;
30
+ animation: boltdocs-search-appear 0.15s ease-out forwards;
31
+ }
32
+
33
+ @keyframes boltdocs-search-appear {
34
+ from {
35
+ opacity: 0;
36
+ transform: scale(0.96) translateY(-10px);
37
+ }
38
+ to {
39
+ opacity: 1;
40
+ transform: scale(1) translateY(0);
41
+ }
42
+ }
43
+
44
+ .boltdocs-search-header {
45
+ display: flex;
46
+ align-items: center;
47
+ padding: 1rem 1.25rem;
48
+ border-bottom: 1px solid transparent; /* Seamless header */
49
+ background-color: transparent;
50
+ gap: 0.75rem;
51
+ }
52
+
53
+ .boltdocs-search-header svg {
54
+ width: 18px;
55
+ height: 18px;
56
+ color: var(--ld-color-primary);
57
+ opacity: 0.8;
58
+ }
59
+
60
+ .boltdocs-search-header input {
61
+ flex: 1;
62
+ background: transparent;
63
+ border: none;
64
+ color: var(--ld-text-main);
65
+ font-size: 1.05rem;
66
+ font-weight: 500;
67
+ font-family: var(--ld-font-sans);
68
+ outline: none;
69
+ box-shadow: none;
70
+ }
71
+
72
+ .boltdocs-search-header input::placeholder {
73
+ color: var(--ld-text-dim);
74
+ }
75
+
76
+ .boltdocs-search-close {
77
+ background: var(--ld-bg-mute);
78
+ border: 1px solid var(--ld-border-subtle);
79
+ color: var(--ld-text-dim);
80
+ font-size: 0.6875rem;
81
+ padding: 0.2rem 0.4rem;
82
+ border-radius: 4px;
83
+ cursor: pointer;
84
+ font-family: var(--ld-font-sans);
85
+ transition: all 0.2s;
86
+ }
87
+
88
+ .boltdocs-search-close:hover {
89
+ background: var(--ld-border-strong);
90
+ color: var(--ld-text-main);
91
+ }
92
+
93
+ .boltdocs-search-results {
94
+ max-height: 50vh;
95
+ overflow-y: auto;
96
+ padding: 0.5rem;
97
+ }
98
+
99
+ .boltdocs-search-results::-webkit-scrollbar {
100
+ width: 6px;
101
+ }
102
+ .boltdocs-search-results::-webkit-scrollbar-thumb {
103
+ background: var(--ld-bg-mute);
104
+ border-radius: 4px;
105
+ }
106
+
107
+ .boltdocs-search-result-item {
108
+ display: flex;
109
+ flex-direction: column;
110
+ padding: 0.75rem 1rem;
111
+ border-radius: var(--ld-radius-md);
112
+ text-decoration: none;
113
+ transition: background-color 0.15s;
114
+ margin-bottom: 2px;
115
+ }
116
+
117
+ .boltdocs-search-result-item.is-heading {
118
+ padding-left: 2rem;
119
+ border-left: 2px solid transparent;
120
+ }
121
+
122
+ .heading-indicator {
123
+ color: var(--ld-text-muted);
124
+ font-weight: 500;
125
+ margin-right: 0.35rem;
126
+ font-family: var(--ld-font-mono);
127
+ }
128
+
129
+ .boltdocs-search-result-item:hover,
130
+ .boltdocs-search-result-item:focus {
131
+ background-color: var(--ld-bg-mute);
132
+ outline: none;
133
+ }
134
+
135
+ .boltdocs-search-result-title {
136
+ color: var(--ld-text-main);
137
+ font-weight: 500;
138
+ font-size: 0.95rem;
139
+ }
140
+
141
+ .boltdocs-search-result-group {
142
+ color: var(--ld-text-muted);
143
+ font-size: 0.75rem;
144
+ margin-top: 0.15rem;
145
+ }
146
+
147
+ .boltdocs-search-empty {
148
+ padding: 2rem;
149
+ text-align: center;
150
+ color: var(--ld-text-dim);
151
+ font-size: 0.9rem;
152
+ }
@@ -0,0 +1,200 @@
1
+ import React, { useState } from "react";
2
+ import { useLocation } from "react-router-dom";
3
+ import { Link } from "../Link";
4
+ import { BoltdocsConfig } from "../../../../node/config";
5
+ import { PoweredBy } from "../PoweredBy";
6
+ import { ChevronRight, ChevronLeft } from "lucide-react";
7
+
8
+ interface RouteItem {
9
+ path: string;
10
+ title: string;
11
+ group?: string;
12
+ groupTitle?: string;
13
+ sidebarPosition?: number;
14
+ badge?: string | { text: string; expires?: string };
15
+ }
16
+
17
+ interface SidebarGroup {
18
+ slug: string;
19
+ title: string;
20
+ routes: RouteItem[];
21
+ }
22
+
23
+ /**
24
+ * Renders a small badge next to sidebar items if one exists and hasn't expired.
25
+ */
26
+ function renderBadge(badgeRaw: RouteItem["badge"]) {
27
+ if (!badgeRaw) return null;
28
+
29
+ let text = "";
30
+ let expires = "";
31
+
32
+ if (typeof badgeRaw === "string") {
33
+ text = badgeRaw;
34
+ } else {
35
+ text = badgeRaw.text;
36
+ expires = badgeRaw.expires || "";
37
+ }
38
+
39
+ // Check expiration
40
+ if (expires) {
41
+ const expireDate = new Date(expires);
42
+ const now = new Date();
43
+ // Reset time components for accurate day comparison
44
+ expireDate.setHours(0, 0, 0, 0);
45
+ now.setHours(0, 0, 0, 0);
46
+ if (now > expireDate) {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ if (!text) return null;
52
+
53
+ if (!text) return null;
54
+
55
+ let typeClass = "badge-default";
56
+ const lowerText = text.toLowerCase();
57
+ if (lowerText === "new") {
58
+ typeClass = "badge-new";
59
+ } else if (lowerText === "experimental") {
60
+ typeClass = "badge-experimental";
61
+ } else if (lowerText === "updated") {
62
+ typeClass = "badge-updated";
63
+ }
64
+
65
+ return <span className={`sidebar-badge ${typeClass}`}>{text}</span>;
66
+ }
67
+
68
+ /**
69
+ * The sidebar navigation component.
70
+ * Groups documentation routes logically based on the `group` property.
71
+ * Highlights the active link based on the current URL path.
72
+ *
73
+ * @param routes - Array of all generated routes to be displayed
74
+ * @param config - Global configuration (which can contain sidebar overrides)
75
+ */
76
+ export function Sidebar({
77
+ routes,
78
+ config,
79
+ onCollapse,
80
+ }: {
81
+ routes: RouteItem[];
82
+ config: BoltdocsConfig;
83
+ onCollapse?: () => void;
84
+ }) {
85
+ const location = useLocation();
86
+
87
+ const ungrouped: RouteItem[] = [];
88
+ const groupMap = new Map<string, SidebarGroup>();
89
+
90
+ for (const route of routes) {
91
+ if (!route.group) {
92
+ ungrouped.push(route);
93
+ } else {
94
+ if (!groupMap.has(route.group)) {
95
+ groupMap.set(route.group, {
96
+ slug: route.group,
97
+ title: route.groupTitle || route.group,
98
+ routes: [],
99
+ });
100
+ }
101
+ groupMap.get(route.group)!.routes.push(route);
102
+ }
103
+ }
104
+
105
+ const groups = Array.from(groupMap.values());
106
+
107
+ return (
108
+ <aside className="boltdocs-sidebar">
109
+ <nav aria-label="Main Navigation">
110
+ <ul className="sidebar-list">
111
+ {ungrouped.map((route) => (
112
+ <li key={route.path}>
113
+ <Link
114
+ to={route.path === "" ? "/" : route.path}
115
+ className={`sidebar-link ${location.pathname === route.path ? "active" : ""}`}
116
+ aria-current={
117
+ location.pathname === route.path ? "page" : undefined
118
+ }
119
+ >
120
+ <div className="sidebar-link-content">
121
+ <span>{route.title}</span>
122
+ {renderBadge(route.badge)}
123
+ </div>
124
+ </Link>
125
+ </li>
126
+ ))}
127
+ </ul>
128
+
129
+ {groups.map((group) => (
130
+ <SidebarGroupSection
131
+ key={group.slug}
132
+ group={group}
133
+ currentPath={location.pathname}
134
+ />
135
+ ))}
136
+ </nav>
137
+
138
+ {onCollapse && (
139
+ <div className="sidebar-footer">
140
+ <button
141
+ className="sidebar-collapse-btn"
142
+ onClick={onCollapse}
143
+ aria-label="Collapse Sidebar"
144
+ title="Collapse Sidebar"
145
+ >
146
+ <ChevronLeft size={16} />
147
+ <span>Collapse Sidebar</span>
148
+ </button>
149
+ </div>
150
+ )}
151
+
152
+ {config.themeConfig?.poweredBy !== false && <PoweredBy />}
153
+ </aside>
154
+ );
155
+ }
156
+
157
+ function SidebarGroupSection({
158
+ group,
159
+ currentPath,
160
+ }: {
161
+ group: SidebarGroup;
162
+ currentPath: string;
163
+ }) {
164
+ const isActive = group.routes.some((r) => currentPath === r.path);
165
+ const [open, setOpen] = useState(true);
166
+
167
+ return (
168
+ <div className="sidebar-group">
169
+ <button
170
+ className={`sidebar-group-header ${isActive ? "active" : ""}`}
171
+ onClick={() => setOpen(!open)}
172
+ aria-expanded={open}
173
+ aria-controls={`sidebar-group-${group.slug}`}
174
+ >
175
+ <span className="sidebar-group-title">{group.title}</span>
176
+ <span className={`sidebar-group-chevron ${open ? "open" : ""}`}>
177
+ <ChevronRight size={16} />
178
+ </span>
179
+ </button>
180
+ {open && (
181
+ <ul className="sidebar-group-list" id={`sidebar-group-${group.slug}`}>
182
+ {group.routes.map((route) => (
183
+ <li key={route.path}>
184
+ <Link
185
+ to={route.path === "" ? "/" : route.path}
186
+ className={`sidebar-link sidebar-link-nested ${currentPath === route.path ? "active" : ""}`}
187
+ aria-current={currentPath === route.path ? "page" : undefined}
188
+ >
189
+ <div className="sidebar-link-content">
190
+ <span>{route.title}</span>
191
+ {renderBadge(route.badge)}
192
+ </div>
193
+ </Link>
194
+ </li>
195
+ ))}
196
+ </ul>
197
+ )}
198
+ </div>
199
+ );
200
+ }
@@ -0,0 +1 @@
1
+ export { Sidebar } from "./Sidebar";
@@ -0,0 +1,269 @@
1
+ /* ═══════════════════════════════════════════════════════════
2
+ SIDEBAR
3
+ ═══════════════════════════════════════════════════════════ */
4
+ .boltdocs-sidebar {
5
+ width: var(--ld-sidebar-width);
6
+ flex-shrink: 0;
7
+ background-color: var(--ld-sidebar-bg);
8
+ backdrop-filter: blur(var(--ld-sidebar-blur));
9
+ -webkit-backdrop-filter: blur(var(--ld-sidebar-blur));
10
+ border-right: 1px solid var(--ld-border-subtle);
11
+ padding: 1rem 0.6rem;
12
+ overflow-y: auto;
13
+ position: sticky;
14
+ top: var(--ld-navbar-height);
15
+ height: calc(100vh - var(--ld-navbar-height));
16
+ scrollbar-width: thin;
17
+ scrollbar-color: var(--ld-bg-mute) transparent;
18
+ display: flex;
19
+ flex-direction: column;
20
+ transition:
21
+ width 0.3s cubic-bezier(0.16, 1, 0.3, 1),
22
+ padding 0.3s cubic-bezier(0.16, 1, 0.3, 1),
23
+ opacity 0.2s ease;
24
+ }
25
+
26
+ .boltdocs-sidebar > nav {
27
+ flex: 1;
28
+ }
29
+
30
+ /* ─── Collapsible Sidebar ────────────────────────────────── */
31
+ .boltdocs-main-container.sidebar-collapsed .boltdocs-sidebar {
32
+ width: 0;
33
+ padding-left: 0;
34
+ padding-right: 0;
35
+ border-right-color: transparent;
36
+ opacity: 0;
37
+ pointer-events: none;
38
+ overflow: hidden;
39
+ }
40
+
41
+ .sidebar-toggle-floating {
42
+ position: fixed;
43
+ bottom: 2rem;
44
+ left: 2rem;
45
+ width: 2.75rem;
46
+ height: 2.75rem;
47
+ border-radius: 50%;
48
+ background-color: var(--ld-surface);
49
+ border: 1px solid var(--ld-border-strong);
50
+ color: var(--ld-text-muted);
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ cursor: pointer;
55
+ z-index: 100;
56
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
57
+ transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
58
+ opacity: 0;
59
+ pointer-events: none;
60
+ transform: translateY(20px) scale(0.9);
61
+ }
62
+
63
+ .sidebar-toggle-floating:hover {
64
+ background-color: var(--ld-bg-soft);
65
+ color: var(--ld-text-main);
66
+ transform: translateY(0px) scale(1.05);
67
+ border-color: var(--ld-color-primary);
68
+ }
69
+
70
+ .sidebar-toggle-floating:active {
71
+ transform: translateY(0px) scale(0.95);
72
+ }
73
+
74
+ .boltdocs-main-container.sidebar-collapsed .sidebar-toggle-floating {
75
+ opacity: 1;
76
+ pointer-events: auto;
77
+ transform: translateY(0) scale(1);
78
+ }
79
+
80
+ .sidebar-footer {
81
+ margin-top: 2rem;
82
+ padding-top: 1rem;
83
+ border-top: 1px solid var(--ld-border-subtle);
84
+ }
85
+
86
+ .sidebar-collapse-btn {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 0.5rem;
90
+ width: 100%;
91
+ padding: 0.5rem 0.75rem;
92
+ background: none;
93
+ border: none;
94
+ border-radius: var(--ld-radius-md);
95
+ color: var(--ld-text-muted);
96
+ font-family: var(--ld-font-sans);
97
+ font-size: 0.8125rem;
98
+ font-weight: 500;
99
+ cursor: pointer;
100
+ transition: all 0.2s ease;
101
+ }
102
+
103
+ .sidebar-collapse-btn:hover {
104
+ background-color: var(--ld-bg-soft);
105
+ color: var(--ld-color-primary);
106
+ }
107
+
108
+ .boltdocs-sidebar::-webkit-scrollbar {
109
+ width: 4px;
110
+ }
111
+
112
+ .boltdocs-sidebar::-webkit-scrollbar-track {
113
+ background: transparent;
114
+ }
115
+
116
+ .boltdocs-sidebar::-webkit-scrollbar-thumb {
117
+ background: var(--ld-bg-mute);
118
+ border-radius: 4px;
119
+ }
120
+
121
+ .sidebar-list {
122
+ list-style: none;
123
+ padding: 0;
124
+ margin: 0 0 0.5rem;
125
+ }
126
+
127
+ .sidebar-list li {
128
+ margin-bottom: 1px;
129
+ }
130
+
131
+ .sidebar-link {
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: space-between;
135
+ padding: 0.45rem 0.75rem;
136
+ color: var(--ld-text-muted);
137
+ text-decoration: none;
138
+ border-radius: var(--ld-radius-md);
139
+ font-size: 0.875rem;
140
+ font-weight: 500;
141
+ transition: all 0.2s ease;
142
+ margin: 0.15rem 0;
143
+ }
144
+
145
+ .sidebar-link-content {
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: space-between;
149
+ width: 100%;
150
+ gap: 0.5rem;
151
+ }
152
+
153
+ .sidebar-link-content > span:first-child {
154
+ flex: 1;
155
+ word-wrap: break-word;
156
+ }
157
+
158
+ .sidebar-badge {
159
+ font-size: 0.45rem;
160
+ font-weight: 500;
161
+ padding: 0.1rem 0.3rem;
162
+ border-radius: 9999px;
163
+ text-transform: uppercase;
164
+ letter-spacing: 0.05em;
165
+ white-space: nowrap;
166
+ line-height: 1;
167
+ display: inline-flex;
168
+ align-items: center;
169
+ flex-shrink: 0;
170
+ }
171
+
172
+ .sidebar-badge.badge-new {
173
+ color: #38bdf8;
174
+ background-color: rgba(56, 189, 248, 0.15);
175
+ border: 1px solid rgba(56, 189, 248, 0.25);
176
+ }
177
+
178
+ .sidebar-badge.badge-experimental {
179
+ color: #eab308; /* Yellow */
180
+ background-color: rgba(234, 179, 8, 0.15);
181
+ border: 1px solid rgba(234, 179, 8, 0.25);
182
+ }
183
+
184
+ .sidebar-badge.badge-updated {
185
+ color: #a1a1aa; /* Grey / Zinc 400 */
186
+ background-color: rgba(161, 161, 170, 0.15);
187
+ border: 1px solid rgba(161, 161, 170, 0.25);
188
+ }
189
+
190
+ .sidebar-badge.badge-default {
191
+ color: var(--ld-text-muted);
192
+ background-color: var(--ld-bg-mute);
193
+ border: 1px solid var(--ld-border-subtle);
194
+ }
195
+
196
+ .sidebar-link:hover {
197
+ color: var(--ld-color-primary-hover);
198
+ }
199
+
200
+ .sidebar-link.active {
201
+ color: var(--ld-color-primary);
202
+ font-weight: 600;
203
+ }
204
+
205
+ /* ─── Sidebar Groups ─────────────────────────────────────── */
206
+ .sidebar-group {
207
+ margin-top: 1.25rem;
208
+ }
209
+
210
+ .sidebar-group:first-child {
211
+ margin-top: 0;
212
+ }
213
+
214
+ .sidebar-group-header {
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: space-between;
218
+ width: 100%;
219
+ padding: 0.35rem 0.75rem;
220
+ background: none;
221
+ border: none;
222
+ border-radius: var(--ld-radius-md);
223
+ color: var(--ld-text-dim);
224
+ font-family: var(--ld-font-sans);
225
+ font-size: 0.6875rem;
226
+ font-weight: 700;
227
+ text-transform: uppercase;
228
+ letter-spacing: 0.08em;
229
+ cursor: pointer;
230
+ transition:
231
+ color 0.2s,
232
+ background-color 0.2s;
233
+ }
234
+
235
+ .sidebar-group-header:hover {
236
+ color: var(--ld-text-muted);
237
+ background-color: rgba(255, 255, 255, 0.03);
238
+ }
239
+
240
+ .sidebar-group-header.active {
241
+ color: var(--ld-color-primary);
242
+ }
243
+
244
+ .sidebar-group-chevron {
245
+ display: inline-flex;
246
+ align-items: center;
247
+ transition: transform 0.25s ease;
248
+ font-size: 0.75rem;
249
+ line-height: 1;
250
+ }
251
+
252
+ .sidebar-group-chevron.open {
253
+ transform: rotate(90deg);
254
+ }
255
+
256
+ .sidebar-group-list {
257
+ list-style: none;
258
+ padding: 0;
259
+ margin: 0.35rem 0 0;
260
+ overflow: hidden;
261
+ }
262
+
263
+ .sidebar-group-list li {
264
+ margin-bottom: 1px;
265
+ }
266
+
267
+ .sidebar-link-nested {
268
+ padding-left: 1.25rem;
269
+ }
@@ -0,0 +1,69 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Sun, Moon } from "lucide-react";
3
+
4
+ export function ThemeToggle() {
5
+ const [theme, setTheme] = useState("dark");
6
+ const [mounted, setMounted] = useState(false);
7
+
8
+ useEffect(() => {
9
+ setMounted(true);
10
+ // On mount, read from localStorage or matchMedia
11
+ const stored = localStorage.getItem("boltdocs-theme");
12
+ if (stored === "light" || stored === "dark") {
13
+ setTheme(stored);
14
+ } else {
15
+ const prefersDark = window.matchMedia(
16
+ "(prefers-color-scheme: dark)",
17
+ ).matches;
18
+ setTheme(prefersDark ? "dark" : "light");
19
+ }
20
+
21
+ // Listen to system changes if no localStorage is set
22
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
23
+ const handleChange = (e: MediaQueryListEvent) => {
24
+ if (!localStorage.getItem("boltdocs-theme")) {
25
+ setTheme(e.matches ? "dark" : "light");
26
+ }
27
+ };
28
+ mediaQuery.addEventListener("change", handleChange);
29
+ return () => mediaQuery.removeEventListener("change", handleChange);
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ if (!mounted) return;
34
+ const root = document.documentElement;
35
+ if (theme === "light") {
36
+ root.classList.add("theme-light");
37
+ root.dataset.theme = "light";
38
+ } else {
39
+ root.classList.remove("theme-light");
40
+ root.dataset.theme = "dark";
41
+ }
42
+ }, [theme, mounted]);
43
+
44
+ const toggleTheme = () => {
45
+ const newTheme = theme === "dark" ? "light" : "dark";
46
+ setTheme(newTheme);
47
+ localStorage.setItem("boltdocs-theme", newTheme);
48
+ };
49
+
50
+ // Prevent hydration mismatch by rendering a placeholder with same layout
51
+ if (!mounted) {
52
+ return (
53
+ <button className="navbar-icon-btn" aria-label="Toggle theme" disabled>
54
+ <span style={{ width: 20, height: 20, display: "inline-block" }}></span>
55
+ </button>
56
+ );
57
+ }
58
+
59
+ return (
60
+ <button
61
+ className="navbar-icon-btn"
62
+ onClick={toggleTheme}
63
+ aria-label="Toggle theme"
64
+ title={`Switch to ${theme === "dark" ? "light" : "dark"} theme`}
65
+ >
66
+ {theme === "dark" ? <Sun size={20} /> : <Moon size={20} />}
67
+ </button>
68
+ );
69
+ }
@@ -0,0 +1 @@
1
+ export { ThemeToggle } from "./ThemeToggle";