boltdocs 1.10.2 → 1.11.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 (225) hide show
  1. package/package.json +29 -7
  2. package/src/client/app/config-context.tsx +18 -0
  3. package/src/client/app/docs-layout.tsx +14 -0
  4. package/src/client/app/index.tsx +132 -260
  5. package/src/client/app/mdx-component.tsx +52 -0
  6. package/src/client/app/mdx-components-context.tsx +23 -0
  7. package/src/client/app/mdx-page.tsx +20 -0
  8. package/src/client/app/preload.tsx +38 -30
  9. package/src/client/app/router.tsx +30 -0
  10. package/src/client/app/scroll-handler.tsx +40 -0
  11. package/src/client/app/theme-context.tsx +75 -0
  12. package/src/client/components/default-layout.tsx +80 -0
  13. package/src/client/components/docs-layout.tsx +105 -0
  14. package/src/client/components/icons-dev.tsx +74 -0
  15. package/src/client/components/mdx/admonition.tsx +107 -0
  16. package/src/client/components/mdx/badge.tsx +41 -0
  17. package/src/client/components/mdx/button.tsx +35 -0
  18. package/src/client/components/mdx/card.tsx +124 -0
  19. package/src/client/components/mdx/code-block.tsx +119 -0
  20. package/src/client/components/mdx/component-preview.tsx +47 -0
  21. package/src/client/components/mdx/component-props.tsx +83 -0
  22. package/src/client/components/mdx/field.tsx +66 -0
  23. package/src/client/components/mdx/file-tree.tsx +287 -0
  24. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  25. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  26. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  27. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  28. package/src/client/components/mdx/image.tsx +23 -0
  29. package/src/client/components/mdx/index.ts +53 -0
  30. package/src/client/components/mdx/link.tsx +38 -0
  31. package/src/client/components/mdx/list.tsx +192 -0
  32. package/src/client/components/mdx/table.tsx +156 -0
  33. package/src/client/components/mdx/tabs.tsx +135 -0
  34. package/src/client/components/mdx/video.tsx +68 -0
  35. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  36. package/src/client/components/primitives/button-group.tsx +54 -0
  37. package/src/client/components/primitives/button.tsx +145 -0
  38. package/src/client/components/primitives/helpers/observer.ts +120 -0
  39. package/src/client/components/primitives/index.ts +17 -0
  40. package/src/client/components/primitives/link.tsx +122 -0
  41. package/src/client/components/primitives/menu.tsx +159 -0
  42. package/src/client/components/primitives/navbar.tsx +359 -0
  43. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  44. package/src/client/components/primitives/on-this-page.tsx +461 -0
  45. package/src/client/components/primitives/page-nav.tsx +87 -0
  46. package/src/client/components/primitives/popover.tsx +47 -0
  47. package/src/client/components/primitives/search-dialog.tsx +183 -0
  48. package/src/client/components/primitives/sidebar.tsx +154 -0
  49. package/src/client/components/primitives/tabs.tsx +90 -0
  50. package/src/client/components/primitives/tooltip.tsx +83 -0
  51. package/src/client/components/primitives/types.ts +11 -0
  52. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  53. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  54. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  55. package/src/client/components/ui-base/github-stars.tsx +27 -0
  56. package/src/client/components/ui-base/head.tsx +69 -0
  57. package/src/client/components/ui-base/loading.tsx +87 -0
  58. package/src/client/components/ui-base/navbar.tsx +138 -0
  59. package/src/client/components/ui-base/not-found.tsx +24 -0
  60. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  61. package/src/client/components/ui-base/page-nav.tsx +39 -0
  62. package/src/client/components/ui-base/powered-by.tsx +19 -0
  63. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  64. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  65. package/src/client/components/ui-base/sidebar.tsx +104 -0
  66. package/src/client/components/ui-base/tabs.tsx +65 -0
  67. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  68. package/src/client/hooks/index.ts +12 -0
  69. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  70. package/src/client/hooks/use-i18n.ts +84 -0
  71. package/src/client/hooks/use-localized-to.ts +95 -0
  72. package/src/client/hooks/use-location.ts +5 -0
  73. package/src/client/hooks/use-navbar.ts +60 -0
  74. package/src/client/hooks/use-onthispage.ts +23 -0
  75. package/src/client/hooks/use-page-nav.ts +22 -0
  76. package/src/client/hooks/use-routes.ts +72 -0
  77. package/src/client/hooks/use-search.ts +71 -0
  78. package/src/client/hooks/use-sidebar.ts +49 -0
  79. package/src/client/hooks/use-tabs.ts +43 -0
  80. package/src/client/hooks/use-version.ts +78 -0
  81. package/src/client/index.ts +55 -17
  82. package/src/client/integrations/codesandbox.ts +179 -0
  83. package/src/client/ssr.tsx +27 -16
  84. package/src/client/theme/neutral.css +360 -0
  85. package/src/client/types.ts +131 -27
  86. package/src/client/utils/cn.ts +6 -0
  87. package/src/client/utils/copy-clipboard.ts +22 -0
  88. package/src/client/utils/get-base-file-path.ts +21 -0
  89. package/src/client/utils/github.ts +121 -0
  90. package/src/client/utils/use-on-change.ts +15 -0
  91. package/src/client/virtual.d.ts +24 -0
  92. package/src/node/cache.ts +156 -156
  93. package/src/node/config.ts +159 -103
  94. package/src/node/index.ts +13 -13
  95. package/src/node/mdx.ts +213 -61
  96. package/src/node/plugin/entry.ts +29 -18
  97. package/src/node/plugin/html.ts +11 -11
  98. package/src/node/plugin/index.ts +161 -83
  99. package/src/node/plugin/types.ts +2 -4
  100. package/src/node/routes/cache.ts +6 -6
  101. package/src/node/routes/index.ts +206 -113
  102. package/src/node/routes/parser.ts +106 -81
  103. package/src/node/routes/sorter.ts +15 -15
  104. package/src/node/routes/types.ts +24 -24
  105. package/src/node/ssg/index.ts +46 -46
  106. package/src/node/ssg/meta.ts +4 -4
  107. package/src/node/ssg/options.ts +5 -5
  108. package/src/node/ssg/sitemap.ts +14 -14
  109. package/src/node/utils.ts +31 -31
  110. package/tsconfig.json +25 -20
  111. package/tsup.config.ts +23 -14
  112. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  113. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  114. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  115. package/dist/Video-KNTY5BNO.mjs +0 -6
  116. package/dist/cache-KNL5B4EE.mjs +0 -12
  117. package/dist/chunk-7SFUJWTB.mjs +0 -211
  118. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  119. package/dist/chunk-FMTOYQLO.mjs +0 -37
  120. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  121. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  122. package/dist/client/index.css +0 -2847
  123. package/dist/client/index.d.mts +0 -372
  124. package/dist/client/index.d.ts +0 -372
  125. package/dist/client/index.js +0 -3630
  126. package/dist/client/index.mjs +0 -697
  127. package/dist/client/ssr.css +0 -2847
  128. package/dist/client/ssr.d.mts +0 -27
  129. package/dist/client/ssr.d.ts +0 -27
  130. package/dist/client/ssr.js +0 -2928
  131. package/dist/client/ssr.mjs +0 -33
  132. package/dist/config-BsFQ-ErD.d.mts +0 -159
  133. package/dist/config-BsFQ-ErD.d.ts +0 -159
  134. package/dist/node/index.d.mts +0 -91
  135. package/dist/node/index.d.ts +0 -91
  136. package/dist/node/index.js +0 -1187
  137. package/dist/node/index.mjs +0 -762
  138. package/dist/types-Dj-bfnC3.d.mts +0 -74
  139. package/dist/types-Dj-bfnC3.d.ts +0 -74
  140. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  141. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  142. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  143. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  144. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  145. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  146. package/src/client/theme/components/Playground/index.ts +0 -1
  147. package/src/client/theme/components/Playground/playground.css +0 -238
  148. package/src/client/theme/components/Video/Video.tsx +0 -84
  149. package/src/client/theme/components/Video/index.ts +0 -1
  150. package/src/client/theme/components/Video/video.css +0 -41
  151. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  152. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  153. package/src/client/theme/components/mdx/Button.tsx +0 -50
  154. package/src/client/theme/components/mdx/Card.tsx +0 -80
  155. package/src/client/theme/components/mdx/Field.tsx +0 -60
  156. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  157. package/src/client/theme/components/mdx/List.tsx +0 -57
  158. package/src/client/theme/components/mdx/Table.tsx +0 -151
  159. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  160. package/src/client/theme/components/mdx/index.ts +0 -27
  161. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  162. package/src/client/theme/icons/bun.tsx +0 -62
  163. package/src/client/theme/icons/deno.tsx +0 -20
  164. package/src/client/theme/icons/discord.tsx +0 -12
  165. package/src/client/theme/icons/github.tsx +0 -15
  166. package/src/client/theme/icons/npm.tsx +0 -13
  167. package/src/client/theme/icons/pnpm.tsx +0 -72
  168. package/src/client/theme/icons/twitter.tsx +0 -12
  169. package/src/client/theme/styles/markdown.css +0 -394
  170. package/src/client/theme/styles/variables.css +0 -175
  171. package/src/client/theme/styles.css +0 -39
  172. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  173. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  174. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  175. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  176. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  177. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  178. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  179. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  180. package/src/client/theme/ui/Footer/footer.css +0 -32
  181. package/src/client/theme/ui/Head/Head.tsx +0 -69
  182. package/src/client/theme/ui/Head/index.ts +0 -1
  183. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  184. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  185. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  186. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  187. package/src/client/theme/ui/Layout/base.css +0 -106
  188. package/src/client/theme/ui/Layout/index.ts +0 -2
  189. package/src/client/theme/ui/Layout/pagination.css +0 -72
  190. package/src/client/theme/ui/Layout/responsive.css +0 -47
  191. package/src/client/theme/ui/Link/Link.tsx +0 -392
  192. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  193. package/src/client/theme/ui/Link/index.ts +0 -2
  194. package/src/client/theme/ui/Link/link-preview.css +0 -48
  195. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  196. package/src/client/theme/ui/Loading/index.ts +0 -1
  197. package/src/client/theme/ui/Loading/loading.css +0 -30
  198. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  199. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  200. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  201. package/src/client/theme/ui/Navbar/index.ts +0 -2
  202. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  203. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  204. package/src/client/theme/ui/NotFound/index.ts +0 -1
  205. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  206. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  207. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  208. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  209. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  210. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  211. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  212. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  213. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  214. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  215. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  216. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  217. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  218. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  219. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  220. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  221. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  222. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  223. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  224. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  225. package/src/client/utils.ts +0 -49
@@ -1,209 +0,0 @@
1
- import React, { useState, useEffect, useRef } from "react";
2
- import { createPortal } from "react-dom";
3
- import { Link } from "../Link";
4
- import { Search } from "lucide-react";
5
- import { ComponentRoute } from "../../../types";
6
-
7
- interface SearchResult {
8
- title: string;
9
- path: string;
10
- groupTitle?: string;
11
- isHeading?: boolean;
12
- }
13
-
14
- export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
15
- const [isOpen, setIsOpen] = useState(false);
16
- const [query, setQuery] = useState("");
17
- const inputRef = useRef<HTMLInputElement>(null);
18
-
19
- useEffect(() => {
20
- const handleKeyDown = (e: KeyboardEvent) => {
21
- if ((e.metaKey || e.ctrlKey) && e.key === "k") {
22
- e.preventDefault();
23
- setIsOpen((prev) => !prev);
24
- }
25
- if (e.key === "Escape" && isOpen) {
26
- setIsOpen(false);
27
- }
28
- };
29
- window.addEventListener("keydown", handleKeyDown);
30
- return () => window.removeEventListener("keydown", handleKeyDown);
31
- }, [isOpen]);
32
-
33
- useEffect(() => {
34
- if (isOpen) {
35
- setTimeout(() => inputRef.current?.focus(), 50);
36
- } else {
37
- setQuery("");
38
- }
39
- }, [isOpen]);
40
-
41
- const searchResults: SearchResult[] = React.useMemo(() => {
42
- if (!query) {
43
- return routes.slice(0, 10).map((r) => ({
44
- title: r.title,
45
- path: r.path,
46
- groupTitle: r.groupTitle,
47
- }));
48
- }
49
-
50
- const results: SearchResult[] = [];
51
- const lowerQuery = query.toLowerCase();
52
-
53
- for (const route of routes) {
54
- if (route.title && route.title.toLowerCase().includes(lowerQuery)) {
55
- results.push({
56
- title: route.title,
57
- path: route.path,
58
- groupTitle: route.groupTitle,
59
- });
60
- }
61
-
62
- if (route.headings) {
63
- for (const heading of route.headings) {
64
- if (heading.text.toLowerCase().includes(lowerQuery)) {
65
- results.push({
66
- title: heading.text,
67
- path: `${route.path}#${heading.id}`,
68
- groupTitle: route.title,
69
- isHeading: true,
70
- });
71
- }
72
- }
73
- }
74
-
75
- if (route._content && route._content.toLowerCase().includes(lowerQuery)) {
76
- // If it's a content match but not a title/heading match, add it
77
- // We only add the page itself for now
78
- results.push({
79
- title: route.title,
80
- path: route.path,
81
- groupTitle: route.groupTitle,
82
- });
83
- }
84
- }
85
-
86
- // Deduplicate results by path
87
- const uniqueResults = [];
88
- const seenPaths = new Set();
89
- for (const res of results) {
90
- if (!seenPaths.has(res.path)) {
91
- seenPaths.add(res.path);
92
- uniqueResults.push(res);
93
- }
94
- }
95
-
96
- return uniqueResults.slice(0, 10);
97
- }, [routes, query]);
98
-
99
- return (
100
- <>
101
- <div
102
- className="navbar-search"
103
- role="button"
104
- tabIndex={0}
105
- onClick={() => setIsOpen(true)}
106
- onKeyDown={(e) => {
107
- if (e.key === "Enter" || e.key === " ") {
108
- e.preventDefault();
109
- setIsOpen(true);
110
- }
111
- }}
112
- aria-label="Open search dialog"
113
- >
114
- <Search className="boltdocs-search-icon" size={18} />
115
- Search docs...
116
- <kbd>⌘K</kbd>
117
- </div>
118
-
119
- {isOpen &&
120
- createPortal(
121
- <div
122
- className="boltdocs-search-overlay"
123
- onPointerDown={() => setIsOpen(false)}
124
- >
125
- <div
126
- className="boltdocs-search-modal"
127
- role="dialog"
128
- aria-modal="true"
129
- aria-label="Search"
130
- onPointerDown={(e) => e.stopPropagation()}
131
- >
132
- <div className="boltdocs-search-header">
133
- <Search size={18} />
134
- <input
135
- ref={inputRef}
136
- type="text"
137
- aria-label="Search documentation input"
138
- placeholder="Search documentation..."
139
- value={query}
140
- onChange={(e) => setQuery(e.target.value)}
141
- />
142
- <button
143
- className="boltdocs-search-close"
144
- onClick={() => setIsOpen(false)}
145
- aria-label="Close search"
146
- >
147
- ESC
148
- </button>
149
- </div>
150
-
151
- <div className="boltdocs-search-results">
152
- {searchResults.length > 0 ? (
153
- searchResults.map((result) => (
154
- <Link
155
- key={result.path}
156
- to={result.path === "" ? "/" : result.path}
157
- className={`boltdocs-search-result-item ${result.isHeading ? "is-heading" : ""}`}
158
- onClick={(e) => {
159
- const isSamePath =
160
- result.path.split("#")[0] ===
161
- window.location.pathname;
162
- if (isSamePath && result.isHeading) {
163
- e.preventDefault();
164
- const id = result.path.split("#")[1];
165
- const el = document.getElementById(id);
166
- if (el) {
167
- const offset = 80;
168
- const bodyRect =
169
- document.body.getBoundingClientRect().top;
170
- const elementRect = el.getBoundingClientRect().top;
171
- const elementPosition = elementRect - bodyRect;
172
- const offsetPosition = elementPosition - offset;
173
-
174
- window.scrollTo({
175
- top: offsetPosition,
176
- behavior: "smooth",
177
- });
178
- window.history.pushState(null, "", `#${id}`);
179
- }
180
- }
181
- setIsOpen(false);
182
- }}
183
- >
184
- <span className="boltdocs-search-result-title">
185
- {result.isHeading ? (
186
- <span className="heading-indicator">#</span>
187
- ) : null}
188
- {result.title}
189
- </span>
190
- {result.groupTitle && (
191
- <span className="boltdocs-search-result-group">
192
- {result.groupTitle}
193
- </span>
194
- )}
195
- </Link>
196
- ))
197
- ) : (
198
- <div className="boltdocs-search-empty">
199
- No results found for "{query}"
200
- </div>
201
- )}
202
- </div>
203
- </div>
204
- </div>,
205
- document.body,
206
- )}
207
- </>
208
- );
209
- }
@@ -1 +0,0 @@
1
- export { SearchDialog } from "./SearchDialog";
@@ -1,152 +0,0 @@
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
- }
@@ -1,244 +0,0 @@
1
- import { 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 } from "lucide-react";
7
- import * as LucideIcons from "lucide-react";
8
-
9
- interface RouteItem {
10
- path: string;
11
- title: string;
12
- group?: string;
13
- groupTitle?: string;
14
- sidebarPosition?: number;
15
- badge?: string | { text: string; expires?: string };
16
- icon?: string;
17
- tab?: string;
18
- groupIcon?: string;
19
- }
20
-
21
- interface SidebarGroup {
22
- slug: string;
23
- title: string;
24
- routes: RouteItem[];
25
- icon?: string;
26
- }
27
-
28
- /**
29
- * Renders a small badge next to sidebar items if one exists and hasn't expired.
30
- */
31
- function renderBadge(badgeRaw: RouteItem["badge"]) {
32
- if (!badgeRaw) return null;
33
-
34
- let text = "";
35
- let expires = "";
36
-
37
- if (typeof badgeRaw === "string") {
38
- text = badgeRaw;
39
- } else {
40
- text = badgeRaw.text;
41
- expires = badgeRaw.expires || "";
42
- }
43
-
44
- // Check expiration
45
- if (expires) {
46
- const expireDate = new Date(expires);
47
- const now = new Date();
48
- // Reset time components for accurate day comparison
49
- expireDate.setHours(0, 0, 0, 0);
50
- now.setHours(0, 0, 0, 0);
51
- if (now > expireDate) {
52
- return null;
53
- }
54
- }
55
-
56
- if (!text) return null;
57
-
58
- let typeClass = "badge-default";
59
- const lowerText = text.toLowerCase();
60
- if (lowerText === "new") {
61
- typeClass = "badge-new";
62
- } else if (lowerText === "experimental") {
63
- typeClass = "badge-experimental";
64
- } else if (lowerText === "updated") {
65
- typeClass = "badge-updated";
66
- }
67
-
68
- return <span className={`sidebar-badge ${typeClass}`}>{text}</span>;
69
- }
70
-
71
- /**
72
- * Renders an icon from a string (Lucide name or SVG).
73
- */
74
- function renderIcon(iconName?: string, size = 16) {
75
- if (!iconName) return null;
76
-
77
- const trimmed = iconName.trim();
78
-
79
- // Check if it's a raw SVG
80
- if (trimmed.startsWith("<svg") || trimmed.includes("http")) {
81
- if (trimmed.startsWith("<svg")) {
82
- return (
83
- <span
84
- className="sidebar-icon svg-icon"
85
- dangerouslySetInnerHTML={{ __html: trimmed }}
86
- />
87
- );
88
- }
89
- return (
90
- <img
91
- src={trimmed}
92
- className="sidebar-icon"
93
- style={{ width: size, height: size }}
94
- alt=""
95
- />
96
- );
97
- }
98
-
99
- // Check if it's a Lucide icon
100
- const IconComponent = (LucideIcons as any)[iconName];
101
- if (IconComponent) {
102
- return <IconComponent size={size} className="sidebar-icon lucide-icon" />;
103
- }
104
-
105
- return null;
106
- }
107
-
108
- /**
109
- * The sidebar navigation component.
110
- * Groups documentation routes logically based on the `group` property.
111
- * Highlights the active link based on the current URL path.
112
- *
113
- * @param routes - Array of all generated routes to be displayed
114
- * @param config - Global configuration (which can contain sidebar overrides)
115
- */
116
- export function Sidebar({
117
- routes,
118
- config,
119
- }: {
120
- routes: RouteItem[];
121
- config: BoltdocsConfig;
122
- }) {
123
- const location = useLocation();
124
-
125
- // Find active tab based on the current route's metadata
126
- const currentRoute = routes.find((r) => r.path === location.pathname);
127
- const activeTabId = currentRoute?.tab?.toLowerCase();
128
-
129
- // Filter routes by active tab if any tab is active
130
- const filteredRoutes = activeTabId
131
- ? routes.filter((r) => {
132
- if (!r.tab) return true; // Fallback for untabbed routes
133
- return r.tab.toLowerCase() === activeTabId;
134
- })
135
- : routes;
136
-
137
- const ungrouped: RouteItem[] = [];
138
- const groupMap = new Map<string, SidebarGroup & { icon?: string }>();
139
-
140
- for (const route of filteredRoutes) {
141
- if (!route.group) {
142
- ungrouped.push(route);
143
- } else {
144
- if (!groupMap.has(route.group)) {
145
- groupMap.set(route.group, {
146
- slug: route.group,
147
- title: route.groupTitle || route.group,
148
- routes: [],
149
- icon: (route as any).groupIcon,
150
- });
151
- }
152
- groupMap.get(route.group)!.routes.push(route);
153
- }
154
- }
155
-
156
- const groups = Array.from(groupMap.values());
157
-
158
- return (
159
- <aside className="boltdocs-sidebar">
160
- <nav aria-label="Main Navigation">
161
- <ul className="sidebar-list">
162
- {ungrouped.map((route) => (
163
- <li key={route.path}>
164
- <Link
165
- to={route.path === "" ? "/" : route.path}
166
- className={`sidebar-link ${location.pathname === route.path ? "active" : ""}`}
167
- aria-current={
168
- location.pathname === route.path ? "page" : undefined
169
- }
170
- >
171
- <div className="sidebar-link-content">
172
- <div className="sidebar-link-title-container">
173
- {renderIcon((route as any).icon)}
174
- <span>{route.title}</span>
175
- </div>
176
- {renderBadge(route.badge)}
177
- </div>
178
- </Link>
179
- </li>
180
- ))}
181
- </ul>
182
-
183
- {groups.map((group) => (
184
- <SidebarGroupSection
185
- key={group.slug}
186
- group={group}
187
- currentPath={location.pathname}
188
- />
189
- ))}
190
- </nav>
191
- {config.themeConfig?.poweredBy !== false && <PoweredBy />}
192
- </aside>
193
- );
194
- }
195
-
196
- function SidebarGroupSection({
197
- group,
198
- currentPath,
199
- }: {
200
- group: SidebarGroup;
201
- currentPath: string;
202
- }) {
203
- const isActive = group.routes.some((r) => currentPath === r.path);
204
- const [open, setOpen] = useState(true);
205
-
206
- return (
207
- <div className="sidebar-group">
208
- <button
209
- className={`sidebar-group-header ${isActive ? "active" : ""}`}
210
- onClick={() => setOpen(!open)}
211
- aria-expanded={open}
212
- aria-controls={`sidebar-group-${group.slug}`}
213
- >
214
- <div className="sidebar-group-header-content">
215
- <span className="sidebar-group-title">{group.title}</span>
216
- </div>
217
- <span className={`sidebar-group-chevron ${open ? "open" : ""}`}>
218
- <ChevronRight size={16} />
219
- </span>
220
- </button>
221
- {open && (
222
- <ul className="sidebar-group-list" id={`sidebar-group-${group.slug}`}>
223
- {group.routes.map((route) => (
224
- <li key={route.path}>
225
- <Link
226
- to={route.path === "" ? "/" : route.path}
227
- className={`sidebar-link sidebar-link-nested ${currentPath === route.path ? "active" : ""}`}
228
- aria-current={currentPath === route.path ? "page" : undefined}
229
- >
230
- <div className="sidebar-link-content">
231
- <div className="sidebar-link-title-container">
232
- {renderIcon((route as any).icon)}
233
- <span>{route.title}</span>
234
- </div>
235
- {renderBadge(route.badge)}
236
- </div>
237
- </Link>
238
- </li>
239
- ))}
240
- </ul>
241
- )}
242
- </div>
243
- );
244
- }
@@ -1 +0,0 @@
1
- export { Sidebar } from "./Sidebar";