boltdocs 1.10.2 → 2.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 (250) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/dist/cache-7G6D532T.mjs +1 -0
  4. package/dist/chunk-A4HQPEPU.mjs +1 -0
  5. package/dist/chunk-BA5NH5HU.mjs +1 -0
  6. package/dist/chunk-BQCD3DWG.mjs +1 -0
  7. package/dist/chunk-H63UMKYF.mjs +1 -0
  8. package/dist/chunk-IWHRQHS7.mjs +1 -0
  9. package/dist/chunk-JZXLCA2E.mjs +1 -0
  10. package/dist/chunk-MFU7Q6WF.mjs +1 -0
  11. package/dist/chunk-QYPNX5UN.mjs +1 -0
  12. package/dist/chunk-XEAPSFMB.mjs +1 -0
  13. package/dist/client/components/mdx/index.d.mts +209 -0
  14. package/dist/client/components/mdx/index.d.ts +209 -0
  15. package/dist/client/components/mdx/index.js +1 -0
  16. package/dist/client/components/mdx/index.mjs +1 -0
  17. package/dist/client/hooks/index.d.mts +133 -0
  18. package/dist/client/hooks/index.d.ts +133 -0
  19. package/dist/client/hooks/index.js +1 -0
  20. package/dist/client/hooks/index.mjs +1 -0
  21. package/dist/client/index.d.mts +138 -298
  22. package/dist/client/index.d.ts +138 -298
  23. package/dist/client/index.js +1 -3630
  24. package/dist/client/index.mjs +1 -697
  25. package/dist/client/ssr.d.mts +7 -3
  26. package/dist/client/ssr.d.ts +7 -3
  27. package/dist/client/ssr.js +1 -2928
  28. package/dist/client/ssr.mjs +1 -33
  29. package/dist/{config-BsFQ-ErD.d.ts → config-CX4l-ZNp.d.mts} +42 -35
  30. package/dist/{config-BsFQ-ErD.d.mts → config-CX4l-ZNp.d.ts} +42 -35
  31. package/dist/node/index.d.mts +2 -4
  32. package/dist/node/index.d.ts +2 -4
  33. package/dist/node/index.js +31 -1161
  34. package/dist/node/index.mjs +31 -736
  35. package/dist/search-dialog-EB3N4TYM.mjs +1 -0
  36. package/dist/types-BuZWFT7r.d.ts +159 -0
  37. package/dist/types-CvT-SGbK.d.mts +159 -0
  38. package/dist/use-routes-5bAtAAYX.d.mts +30 -0
  39. package/dist/use-routes-BefRXY3v.d.ts +30 -0
  40. package/package.json +34 -12
  41. package/src/client/app/config-context.tsx +18 -0
  42. package/src/client/app/docs-layout.tsx +14 -0
  43. package/src/client/app/index.tsx +137 -262
  44. package/src/client/app/mdx-component.tsx +52 -0
  45. package/src/client/app/mdx-components-context.tsx +23 -0
  46. package/src/client/app/mdx-page.tsx +20 -0
  47. package/src/client/app/preload.tsx +38 -30
  48. package/src/client/app/router.tsx +30 -0
  49. package/src/client/app/scroll-handler.tsx +40 -0
  50. package/src/client/app/theme-context.tsx +75 -0
  51. package/src/client/components/default-layout.tsx +80 -0
  52. package/src/client/components/docs-layout.tsx +105 -0
  53. package/src/client/components/icons-dev.tsx +74 -0
  54. package/src/client/components/mdx/admonition.tsx +107 -0
  55. package/src/client/components/mdx/badge.tsx +41 -0
  56. package/src/client/components/mdx/button.tsx +35 -0
  57. package/src/client/components/mdx/card.tsx +124 -0
  58. package/src/client/components/mdx/code-block.tsx +119 -0
  59. package/src/client/components/mdx/component-preview.tsx +47 -0
  60. package/src/client/components/mdx/component-props.tsx +83 -0
  61. package/src/client/components/mdx/field.tsx +66 -0
  62. package/src/client/components/mdx/file-tree.tsx +287 -0
  63. package/src/client/components/mdx/hooks/use-code-block.ts +56 -0
  64. package/src/client/components/mdx/hooks/use-component-preview.ts +16 -0
  65. package/src/client/components/mdx/hooks/useTable.ts +74 -0
  66. package/src/client/components/mdx/hooks/useTabs.ts +68 -0
  67. package/src/client/components/mdx/image.tsx +23 -0
  68. package/src/client/components/mdx/index.ts +53 -0
  69. package/src/client/components/mdx/link.tsx +38 -0
  70. package/src/client/components/mdx/list.tsx +192 -0
  71. package/src/client/components/mdx/table.tsx +156 -0
  72. package/src/client/components/mdx/tabs.tsx +135 -0
  73. package/src/client/components/mdx/video.tsx +68 -0
  74. package/src/client/components/primitives/breadcrumbs.tsx +79 -0
  75. package/src/client/components/primitives/button-group.tsx +54 -0
  76. package/src/client/components/primitives/button.tsx +145 -0
  77. package/src/client/components/primitives/helpers/observer.ts +120 -0
  78. package/src/client/components/primitives/index.ts +17 -0
  79. package/src/client/components/primitives/link.tsx +122 -0
  80. package/src/client/components/primitives/menu.tsx +159 -0
  81. package/src/client/components/primitives/navbar.tsx +359 -0
  82. package/src/client/components/primitives/navigation-menu.tsx +116 -0
  83. package/src/client/components/primitives/on-this-page.tsx +461 -0
  84. package/src/client/components/primitives/page-nav.tsx +87 -0
  85. package/src/client/components/primitives/popover.tsx +47 -0
  86. package/src/client/components/primitives/search-dialog.tsx +183 -0
  87. package/src/client/components/primitives/sidebar.tsx +154 -0
  88. package/src/client/components/primitives/tabs.tsx +90 -0
  89. package/src/client/components/primitives/tooltip.tsx +83 -0
  90. package/src/client/components/primitives/types.ts +11 -0
  91. package/src/client/components/ui-base/breadcrumbs.tsx +42 -0
  92. package/src/client/components/ui-base/copy-markdown.tsx +112 -0
  93. package/src/client/components/ui-base/error-boundary.tsx +52 -0
  94. package/src/client/components/ui-base/github-stars.tsx +27 -0
  95. package/src/client/components/ui-base/head.tsx +69 -0
  96. package/src/client/components/ui-base/loading.tsx +87 -0
  97. package/src/client/components/ui-base/navbar.tsx +138 -0
  98. package/src/client/components/ui-base/not-found.tsx +24 -0
  99. package/src/client/components/ui-base/on-this-page.tsx +152 -0
  100. package/src/client/components/ui-base/page-nav.tsx +39 -0
  101. package/src/client/components/ui-base/powered-by.tsx +19 -0
  102. package/src/client/components/ui-base/progress-bar.tsx +67 -0
  103. package/src/client/components/ui-base/search-dialog.tsx +82 -0
  104. package/src/client/components/ui-base/sidebar.tsx +104 -0
  105. package/src/client/components/ui-base/tabs.tsx +65 -0
  106. package/src/client/components/ui-base/theme-toggle.tsx +32 -0
  107. package/src/client/hooks/index.ts +12 -0
  108. package/src/client/hooks/use-breadcrumbs.ts +22 -0
  109. package/src/client/hooks/use-i18n.ts +84 -0
  110. package/src/client/hooks/use-localized-to.ts +95 -0
  111. package/src/client/hooks/use-location.ts +5 -0
  112. package/src/client/hooks/use-navbar.ts +60 -0
  113. package/src/client/hooks/use-onthispage.ts +23 -0
  114. package/src/client/hooks/use-page-nav.ts +22 -0
  115. package/src/client/hooks/use-routes.ts +72 -0
  116. package/src/client/hooks/use-search.ts +71 -0
  117. package/src/client/hooks/use-sidebar.ts +49 -0
  118. package/src/client/hooks/use-tabs.ts +43 -0
  119. package/src/client/hooks/use-version.ts +78 -0
  120. package/src/client/index.ts +55 -17
  121. package/src/client/integrations/codesandbox.ts +179 -0
  122. package/src/client/ssr.tsx +27 -16
  123. package/src/client/theme/neutral.css +360 -0
  124. package/src/client/types.ts +131 -27
  125. package/src/client/utils/cn.ts +6 -0
  126. package/src/client/utils/copy-clipboard.ts +22 -0
  127. package/src/client/utils/get-base-file-path.ts +21 -0
  128. package/src/client/utils/github.ts +121 -0
  129. package/src/client/utils/use-on-change.ts +15 -0
  130. package/src/client/virtual.d.ts +24 -0
  131. package/src/node/cache.ts +156 -156
  132. package/src/node/config.ts +159 -103
  133. package/src/node/index.ts +13 -13
  134. package/src/node/mdx.ts +213 -61
  135. package/src/node/plugin/entry.ts +29 -18
  136. package/src/node/plugin/html.ts +11 -11
  137. package/src/node/plugin/index.ts +161 -84
  138. package/src/node/plugin/types.ts +2 -4
  139. package/src/node/routes/cache.ts +6 -6
  140. package/src/node/routes/index.ts +206 -113
  141. package/src/node/routes/parser.ts +102 -82
  142. package/src/node/routes/sorter.ts +15 -15
  143. package/src/node/routes/types.ts +24 -24
  144. package/src/node/ssg/index.ts +73 -47
  145. package/src/node/ssg/meta.ts +4 -4
  146. package/src/node/ssg/options.ts +5 -5
  147. package/src/node/ssg/sitemap.ts +14 -14
  148. package/src/node/utils.ts +54 -31
  149. package/tsconfig.json +25 -20
  150. package/tsup.config.ts +23 -14
  151. package/dist/PackageManagerTabs-NVT7G625.mjs +0 -99
  152. package/dist/SearchDialog-AGVF6JBO.mjs +0 -194
  153. package/dist/SearchDialog-YPDOM7Q6.css +0 -2847
  154. package/dist/Video-KNTY5BNO.mjs +0 -6
  155. package/dist/cache-KNL5B4EE.mjs +0 -12
  156. package/dist/chunk-7SFUJWTB.mjs +0 -211
  157. package/dist/chunk-FFBNU6IJ.mjs +0 -386
  158. package/dist/chunk-FMTOYQLO.mjs +0 -37
  159. package/dist/chunk-TKLQWU7H.mjs +0 -1920
  160. package/dist/chunk-Z7JHYNAS.mjs +0 -57
  161. package/dist/client/index.css +0 -2847
  162. package/dist/client/ssr.css +0 -2847
  163. package/dist/types-Dj-bfnC3.d.mts +0 -74
  164. package/dist/types-Dj-bfnC3.d.ts +0 -74
  165. package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -61
  166. package/src/client/theme/components/CodeBlock/index.ts +0 -1
  167. package/src/client/theme/components/PackageManagerTabs/PackageManagerTabs.tsx +0 -131
  168. package/src/client/theme/components/PackageManagerTabs/index.ts +0 -1
  169. package/src/client/theme/components/PackageManagerTabs/pkg-tabs.css +0 -64
  170. package/src/client/theme/components/Playground/Playground.tsx +0 -180
  171. package/src/client/theme/components/Playground/index.ts +0 -1
  172. package/src/client/theme/components/Playground/playground.css +0 -238
  173. package/src/client/theme/components/Video/Video.tsx +0 -84
  174. package/src/client/theme/components/Video/index.ts +0 -1
  175. package/src/client/theme/components/Video/video.css +0 -41
  176. package/src/client/theme/components/mdx/Admonition.tsx +0 -80
  177. package/src/client/theme/components/mdx/Badge.tsx +0 -31
  178. package/src/client/theme/components/mdx/Button.tsx +0 -50
  179. package/src/client/theme/components/mdx/Card.tsx +0 -80
  180. package/src/client/theme/components/mdx/Field.tsx +0 -60
  181. package/src/client/theme/components/mdx/FileTree.tsx +0 -229
  182. package/src/client/theme/components/mdx/List.tsx +0 -57
  183. package/src/client/theme/components/mdx/Table.tsx +0 -151
  184. package/src/client/theme/components/mdx/Tabs.tsx +0 -123
  185. package/src/client/theme/components/mdx/index.ts +0 -27
  186. package/src/client/theme/components/mdx/mdx-components.css +0 -764
  187. package/src/client/theme/icons/bun.tsx +0 -62
  188. package/src/client/theme/icons/deno.tsx +0 -20
  189. package/src/client/theme/icons/discord.tsx +0 -12
  190. package/src/client/theme/icons/github.tsx +0 -15
  191. package/src/client/theme/icons/npm.tsx +0 -13
  192. package/src/client/theme/icons/pnpm.tsx +0 -72
  193. package/src/client/theme/icons/twitter.tsx +0 -12
  194. package/src/client/theme/styles/markdown.css +0 -394
  195. package/src/client/theme/styles/variables.css +0 -175
  196. package/src/client/theme/styles.css +0 -39
  197. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +0 -68
  198. package/src/client/theme/ui/Breadcrumbs/index.ts +0 -1
  199. package/src/client/theme/ui/CopyMarkdown/CopyMarkdown.tsx +0 -82
  200. package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -112
  201. package/src/client/theme/ui/CopyMarkdown/index.ts +0 -1
  202. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +0 -50
  203. package/src/client/theme/ui/ErrorBoundary/error-boundary.css +0 -55
  204. package/src/client/theme/ui/ErrorBoundary/index.ts +0 -1
  205. package/src/client/theme/ui/Footer/footer.css +0 -32
  206. package/src/client/theme/ui/Head/Head.tsx +0 -69
  207. package/src/client/theme/ui/Head/index.ts +0 -1
  208. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +0 -125
  209. package/src/client/theme/ui/LanguageSwitcher/index.ts +0 -1
  210. package/src/client/theme/ui/LanguageSwitcher/language-switcher.css +0 -98
  211. package/src/client/theme/ui/Layout/Layout.tsx +0 -203
  212. package/src/client/theme/ui/Layout/base.css +0 -106
  213. package/src/client/theme/ui/Layout/index.ts +0 -2
  214. package/src/client/theme/ui/Layout/pagination.css +0 -72
  215. package/src/client/theme/ui/Layout/responsive.css +0 -47
  216. package/src/client/theme/ui/Link/Link.tsx +0 -392
  217. package/src/client/theme/ui/Link/LinkPreview.tsx +0 -59
  218. package/src/client/theme/ui/Link/index.ts +0 -2
  219. package/src/client/theme/ui/Link/link-preview.css +0 -48
  220. package/src/client/theme/ui/Loading/Loading.tsx +0 -10
  221. package/src/client/theme/ui/Loading/index.ts +0 -1
  222. package/src/client/theme/ui/Loading/loading.css +0 -30
  223. package/src/client/theme/ui/Navbar/GithubStars.tsx +0 -27
  224. package/src/client/theme/ui/Navbar/Navbar.tsx +0 -193
  225. package/src/client/theme/ui/Navbar/Tabs.tsx +0 -99
  226. package/src/client/theme/ui/Navbar/index.ts +0 -2
  227. package/src/client/theme/ui/Navbar/navbar.css +0 -347
  228. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -19
  229. package/src/client/theme/ui/NotFound/index.ts +0 -1
  230. package/src/client/theme/ui/NotFound/not-found.css +0 -64
  231. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +0 -244
  232. package/src/client/theme/ui/OnThisPage/index.ts +0 -1
  233. package/src/client/theme/ui/OnThisPage/toc.css +0 -152
  234. package/src/client/theme/ui/PoweredBy/PoweredBy.tsx +0 -18
  235. package/src/client/theme/ui/PoweredBy/index.ts +0 -1
  236. package/src/client/theme/ui/PoweredBy/powered-by.css +0 -76
  237. package/src/client/theme/ui/ProgressBar/ProgressBar.css +0 -17
  238. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +0 -51
  239. package/src/client/theme/ui/ProgressBar/index.ts +0 -1
  240. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +0 -209
  241. package/src/client/theme/ui/SearchDialog/index.ts +0 -1
  242. package/src/client/theme/ui/SearchDialog/search.css +0 -152
  243. package/src/client/theme/ui/Sidebar/Sidebar.tsx +0 -244
  244. package/src/client/theme/ui/Sidebar/index.ts +0 -1
  245. package/src/client/theme/ui/Sidebar/sidebar.css +0 -230
  246. package/src/client/theme/ui/ThemeToggle/ThemeToggle.tsx +0 -69
  247. package/src/client/theme/ui/ThemeToggle/index.ts +0 -1
  248. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +0 -136
  249. package/src/client/theme/ui/VersionSwitcher/index.ts +0 -1
  250. 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";