boltdocs 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{SearchDialog-UOAW6IR3.css → SearchDialog-4ANHNJTL.css} +213 -28
- package/dist/{SearchDialog-YOXMFGH6.mjs → SearchDialog-6Z7CUAYJ.mjs} +8 -1
- package/dist/{chunk-MULKZFVN.mjs → chunk-SFVOGJ2W.mjs} +269 -165
- package/dist/client/index.css +211 -26
- package/dist/client/index.d.mts +25 -6
- package/dist/client/index.d.ts +25 -6
- package/dist/client/index.js +614 -336
- package/dist/client/index.mjs +134 -5
- package/dist/client/ssr.css +211 -26
- package/dist/client/ssr.d.mts +1 -1
- package/dist/client/ssr.d.ts +1 -1
- package/dist/client/ssr.js +378 -230
- package/dist/client/ssr.mjs +1 -1
- package/dist/node/index.d.mts +2 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +4 -1
- package/dist/node/index.mjs +4 -1
- package/dist/{types-CviV0GbX.d.ts → types-BbceAHA0.d.mts} +2 -0
- package/dist/{types-CviV0GbX.d.mts → types-BbceAHA0.d.ts} +2 -0
- package/package.json +1 -1
- package/src/client/app/index.tsx +8 -7
- package/src/client/index.ts +2 -0
- package/src/client/theme/components/mdx/Field.tsx +60 -0
- package/src/client/theme/components/mdx/Table.tsx +108 -10
- package/src/client/theme/components/mdx/index.ts +3 -0
- package/src/client/theme/components/mdx/mdx-components.css +174 -0
- package/src/client/theme/styles/variables.css +25 -1
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +46 -0
- package/src/client/theme/ui/ErrorBoundary/index.ts +1 -0
- package/src/client/theme/ui/Layout/Layout.tsx +8 -1
- package/src/client/theme/ui/Link/link-preview.css +1 -20
- package/src/client/theme/ui/Navbar/Tabs.tsx +37 -12
- package/src/client/theme/ui/Navbar/navbar.css +26 -18
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +1 -8
- package/src/client/theme/ui/ProgressBar/ProgressBar.css +17 -0
- package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +51 -0
- package/src/client/theme/ui/ProgressBar/index.ts +1 -0
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +11 -1
- package/src/client/types.ts +2 -0
- package/src/node/routes/index.ts +1 -0
- package/src/node/routes/parser.ts +11 -0
- package/src/node/routes/types.ts +2 -0
|
@@ -19,11 +19,9 @@
|
|
|
19
19
|
.boltdocs-link-preview-content {
|
|
20
20
|
padding: 0.85rem 1rem;
|
|
21
21
|
background-color: var(--ld-navbar-bg);
|
|
22
|
-
backdrop-filter: blur(20px);
|
|
23
|
-
-webkit-backdrop-filter: blur(20px);
|
|
24
22
|
border: 1px solid var(--ld-border-subtle);
|
|
25
23
|
border-radius: var(--ld-radius-lg);
|
|
26
|
-
box-shadow:
|
|
24
|
+
box-shadow:
|
|
27
25
|
0 10px 30px -10px rgba(0, 0, 0, 0.2),
|
|
28
26
|
0 4px 10px -5px rgba(0, 0, 0, 0.1);
|
|
29
27
|
}
|
|
@@ -48,20 +46,3 @@
|
|
|
48
46
|
-webkit-box-orient: vertical;
|
|
49
47
|
overflow: hidden;
|
|
50
48
|
}
|
|
51
|
-
|
|
52
|
-
/* Dark mode refinements */
|
|
53
|
-
[data-theme="dark"] .boltdocs-link-preview-content {
|
|
54
|
-
background-color: rgba(15, 15, 20, 0.8);
|
|
55
|
-
border-color: rgba(255, 255, 255, 0.08);
|
|
56
|
-
box-shadow:
|
|
57
|
-
0 20px 40px -15px rgba(0, 0, 0, 0.5),
|
|
58
|
-
0 8px 16px -8px rgba(0, 0, 0, 0.3);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
[data-theme="dark"] .boltdocs-link-preview-title {
|
|
62
|
-
color: #fff;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
[data-theme="dark"] .boltdocs-link-preview-summary {
|
|
66
|
-
color: #94a3b8;
|
|
67
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useLocation } from "react-router-dom";
|
|
3
3
|
import { Link } from "../Link";
|
|
4
4
|
import * as Icons from "lucide-react";
|
|
@@ -16,15 +16,40 @@ interface TabsProps {
|
|
|
16
16
|
|
|
17
17
|
export function Tabs({ tabs, routes }: TabsProps) {
|
|
18
18
|
const location = useLocation();
|
|
19
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
20
|
+
const tabRefs = useRef<(HTMLAnchorElement | null)[]>([]);
|
|
21
|
+
const [indicatorStyle, setIndicatorStyle] = useState<React.CSSProperties>({
|
|
22
|
+
opacity: 0,
|
|
23
|
+
transform: "translateX(0) scaleX(0)",
|
|
24
|
+
width: 0,
|
|
25
|
+
});
|
|
26
|
+
|
|
19
27
|
const currentRoute = routes.find((r) => r.path === location.pathname);
|
|
20
28
|
const currentTabId = currentRoute?.tab?.toLowerCase();
|
|
21
29
|
|
|
30
|
+
// Find the active index - default to 0 if no tab detected
|
|
31
|
+
const activeIndex = tabs.findIndex((tab) =>
|
|
32
|
+
currentTabId ? currentTabId === tab.id.toLowerCase() : false
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const finalActiveIndex = activeIndex === -1 ? 0 : activeIndex;
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const activeTab = tabRefs.current[finalActiveIndex];
|
|
39
|
+
if (activeTab) {
|
|
40
|
+
setIndicatorStyle({
|
|
41
|
+
opacity: 1,
|
|
42
|
+
width: activeTab.offsetWidth,
|
|
43
|
+
transform: `translateX(${activeTab.offsetLeft}px)`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}, [finalActiveIndex, tabs, location.pathname]);
|
|
47
|
+
|
|
22
48
|
if (!tabs || tabs.length === 0) return null;
|
|
23
49
|
|
|
24
50
|
const renderTabIcon = (iconName?: string) => {
|
|
25
51
|
if (!iconName) return null;
|
|
26
52
|
|
|
27
|
-
// 1. Raw SVG
|
|
28
53
|
if (iconName.trim().startsWith("<svg")) {
|
|
29
54
|
return (
|
|
30
55
|
<span
|
|
@@ -34,33 +59,31 @@ export function Tabs({ tabs, routes }: TabsProps) {
|
|
|
34
59
|
);
|
|
35
60
|
}
|
|
36
61
|
|
|
37
|
-
// 2. Lucide Icon
|
|
38
62
|
const LucideIcon = (Icons as any)[iconName];
|
|
39
63
|
if (LucideIcon) {
|
|
40
64
|
return <LucideIcon size={16} className="tab-icon lucide-icon" />;
|
|
41
65
|
}
|
|
42
66
|
|
|
43
|
-
// 3. Fallback to image URL
|
|
44
67
|
return <img src={iconName} alt="" className="tab-icon img-icon" />;
|
|
45
68
|
};
|
|
46
69
|
|
|
47
70
|
return (
|
|
48
71
|
<div className="boltdocs-tabs-container">
|
|
49
|
-
<div className="boltdocs-tabs">
|
|
72
|
+
<div className="boltdocs-tabs" ref={containerRef}>
|
|
50
73
|
{tabs.map((tab, index) => {
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Find the first route for this tab to link to it
|
|
57
|
-
const firstRoute = routes.find(r => r.tab && r.tab.toLowerCase() === tab.id.toLowerCase());
|
|
74
|
+
const isActive = index === finalActiveIndex;
|
|
75
|
+
const firstRoute = routes.find(
|
|
76
|
+
(r) => r.tab && r.tab.toLowerCase() === tab.id.toLowerCase()
|
|
77
|
+
);
|
|
58
78
|
const linkTo = firstRoute ? firstRoute.path : "#";
|
|
59
79
|
|
|
60
80
|
return (
|
|
61
81
|
<Link
|
|
62
82
|
key={tab.id}
|
|
63
83
|
to={linkTo}
|
|
84
|
+
ref={(el) => {
|
|
85
|
+
tabRefs.current[index] = el;
|
|
86
|
+
}}
|
|
64
87
|
className={`boltdocs-tab-item ${isActive ? "active" : ""}`}
|
|
65
88
|
>
|
|
66
89
|
{renderTabIcon(tab.icon)}
|
|
@@ -68,6 +91,8 @@ export function Tabs({ tabs, routes }: TabsProps) {
|
|
|
68
91
|
</Link>
|
|
69
92
|
);
|
|
70
93
|
})}
|
|
94
|
+
{/* Sliding Indicator */}
|
|
95
|
+
<div className="boltdocs-tab-indicator" style={indicatorStyle} />
|
|
71
96
|
</div>
|
|
72
97
|
</div>
|
|
73
98
|
);
|
|
@@ -250,10 +250,22 @@
|
|
|
250
250
|
|
|
251
251
|
/* Tabs Styles */
|
|
252
252
|
.boltdocs-tabs-container {
|
|
253
|
-
|
|
253
|
+
position: relative;
|
|
254
254
|
background: var(--ld-navbar-bg);
|
|
255
255
|
padding: 0;
|
|
256
|
-
height: 46px;
|
|
256
|
+
height: 46px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Border pseudo-element to allow indicator overlap */
|
|
260
|
+
.boltdocs-tabs-container::after {
|
|
261
|
+
content: "";
|
|
262
|
+
position: absolute;
|
|
263
|
+
bottom: 0px;
|
|
264
|
+
left: 0;
|
|
265
|
+
right: 0;
|
|
266
|
+
height: 1px;
|
|
267
|
+
background: var(--ld-border-subtle);
|
|
268
|
+
z-index: 10;
|
|
257
269
|
}
|
|
258
270
|
|
|
259
271
|
.boltdocs-tabs {
|
|
@@ -264,6 +276,8 @@
|
|
|
264
276
|
overflow-x: auto;
|
|
265
277
|
scrollbar-width: none;
|
|
266
278
|
padding: 0 1.5rem;
|
|
279
|
+
position: relative;
|
|
280
|
+
height: 100%;
|
|
267
281
|
}
|
|
268
282
|
|
|
269
283
|
.boltdocs-tabs::-webkit-scrollbar {
|
|
@@ -272,7 +286,7 @@
|
|
|
272
286
|
|
|
273
287
|
.boltdocs-tab-item {
|
|
274
288
|
padding: 0.85rem 0;
|
|
275
|
-
padding-bottom: calc(0.85rem + 1px);
|
|
289
|
+
padding-bottom: calc(0.85rem + 1px);
|
|
276
290
|
font-size: 0.875rem;
|
|
277
291
|
font-weight: 500;
|
|
278
292
|
color: var(--ld-text-muted);
|
|
@@ -284,6 +298,7 @@
|
|
|
284
298
|
align-items: center;
|
|
285
299
|
gap: 0.6rem;
|
|
286
300
|
opacity: 0.7;
|
|
301
|
+
z-index: 20;
|
|
287
302
|
}
|
|
288
303
|
|
|
289
304
|
.boltdocs-tab-item:hover {
|
|
@@ -317,23 +332,16 @@
|
|
|
317
332
|
text-shadow: 0 0 10px rgba(255, 255, 255, 0.2);
|
|
318
333
|
}
|
|
319
334
|
|
|
320
|
-
.boltdocs-tab-
|
|
321
|
-
content: "";
|
|
335
|
+
.boltdocs-tab-indicator {
|
|
322
336
|
position: absolute;
|
|
323
|
-
bottom: 0px;
|
|
337
|
+
bottom: 0px; /* Aligned with the border pseudo-element */
|
|
324
338
|
left: 0;
|
|
325
|
-
right: 0;
|
|
326
339
|
height: 3px;
|
|
327
|
-
background: var(--ld-primary);
|
|
340
|
+
background: var(--ld-color-primary);
|
|
328
341
|
border-radius: 2px 2px 0 0;
|
|
329
|
-
box-shadow: 0
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.boltdocs-tab-item.active::after {
|
|
337
|
-
opacity: 1;
|
|
338
|
-
transform: scaleX(1);
|
|
342
|
+
box-shadow: 0 -2px 15px var(--ld-color-primary-glow);
|
|
343
|
+
transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1), width 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
|
|
344
|
+
z-index: 100; /* Higher than border pseudo-element */
|
|
345
|
+
pointer-events: none;
|
|
346
|
+
transform-origin: left;
|
|
339
347
|
}
|
|
@@ -169,14 +169,7 @@ export function OnThisPage({
|
|
|
169
169
|
e.preventDefault();
|
|
170
170
|
const el = document.getElementById(id);
|
|
171
171
|
if (el) {
|
|
172
|
-
|
|
173
|
-
const bodyRect = document.body.getBoundingClientRect().top;
|
|
174
|
-
const elementRect = el.getBoundingClientRect().top;
|
|
175
|
-
const elementPosition = elementRect - bodyRect;
|
|
176
|
-
const offsetPosition = elementPosition - offset;
|
|
177
|
-
|
|
178
|
-
window.scrollTo({
|
|
179
|
-
top: offsetPosition,
|
|
172
|
+
el.scrollIntoView({
|
|
180
173
|
behavior: "smooth",
|
|
181
174
|
});
|
|
182
175
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.boltdocs-progress-container {
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 2px;
|
|
7
|
+
z-index: 1000;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
background: transparent;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.boltdocs-progress-bar {
|
|
13
|
+
height: 100%;
|
|
14
|
+
background: linear-gradient(90deg, var(--ld-color-primary), var(--ld-gradient-to, var(--ld-color-primary)));
|
|
15
|
+
box-shadow: 0 0 8px var(--ld-color-primary-glow);
|
|
16
|
+
transition: width 0.1s ease-out;
|
|
17
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import "./ProgressBar.css";
|
|
3
|
+
|
|
4
|
+
export function ProgressBar() {
|
|
5
|
+
const [progress, setProgress] = useState(0);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let container: Element | null = null;
|
|
9
|
+
let timer: any;
|
|
10
|
+
|
|
11
|
+
const handleScroll = () => {
|
|
12
|
+
if (!container) return;
|
|
13
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
14
|
+
if (scrollHeight <= clientHeight) {
|
|
15
|
+
setProgress(0);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const scrollPercent = (scrollTop / (scrollHeight - clientHeight)) * 100;
|
|
19
|
+
setProgress(Math.min(100, Math.max(0, scrollPercent)));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const attachListener = () => {
|
|
23
|
+
container = document.querySelector(".boltdocs-content");
|
|
24
|
+
if (container) {
|
|
25
|
+
container.addEventListener("scroll", handleScroll);
|
|
26
|
+
handleScroll();
|
|
27
|
+
if (timer) clearInterval(timer);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (!attachListener()) {
|
|
34
|
+
timer = setInterval(attachListener, 100);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
if (container) container.removeEventListener("scroll", handleScroll);
|
|
39
|
+
if (timer) clearInterval(timer);
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="boltdocs-progress-container">
|
|
45
|
+
<div
|
|
46
|
+
className="boltdocs-progress-bar"
|
|
47
|
+
style={{ width: `${progress}%` }}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ProgressBar";
|
|
@@ -49,7 +49,7 @@ export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
|
|
|
49
49
|
|
|
50
50
|
const results: SearchResult[] = [];
|
|
51
51
|
const lowerQuery = query.toLowerCase();
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
for (const route of routes) {
|
|
54
54
|
if (route.title && route.title.toLowerCase().includes(lowerQuery)) {
|
|
55
55
|
results.push({
|
|
@@ -71,6 +71,16 @@ export function SearchDialog({ routes }: { routes: ComponentRoute[] }) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
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
|
+
}
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
// Deduplicate results by path
|
package/src/client/types.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface ComponentRoute {
|
|
|
37
37
|
badge?: string | { text: string; expires?: string };
|
|
38
38
|
/** Optional icon for the route's group */
|
|
39
39
|
groupIcon?: string;
|
|
40
|
+
/** The extracted plain-text content of the page for search indexing */
|
|
41
|
+
_content?: string;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
/**
|
package/src/node/routes/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export async function generateRoutes(
|
|
|
29
29
|
): Promise<RouteMeta[]> {
|
|
30
30
|
// Load persistent cache on first call
|
|
31
31
|
docCache.load();
|
|
32
|
+
docCache.invalidateAll(); // FORCE RE-PARSE to pick up new _content field
|
|
32
33
|
|
|
33
34
|
const files = await fastGlob(["**/*.md", "**/*.mdx"], {
|
|
34
35
|
cwd: docsDir,
|
|
@@ -151,6 +151,16 @@ export function parseDocFile(
|
|
|
151
151
|
const sanitizedBadge = data.badge ? data.badge : undefined;
|
|
152
152
|
const icon = data.icon ? String(data.icon) : undefined;
|
|
153
153
|
|
|
154
|
+
// Extract full content as plain text for search indexing
|
|
155
|
+
const plainText = content
|
|
156
|
+
.replace(/^#+.*$/gm, "") // Remove headers
|
|
157
|
+
.replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1") // Simplify links
|
|
158
|
+
.replace(/<[^>]+>/g, "") // Remove HTML/JSX tags
|
|
159
|
+
.replace(/\{[^\}]+\}/g, "") // Remove JS expressions/curly braces
|
|
160
|
+
.replace(/[_*`]/g, "") // Remove formatting
|
|
161
|
+
.replace(/\n+/g, " ") // Normalize whitespace
|
|
162
|
+
.trim();
|
|
163
|
+
|
|
154
164
|
return {
|
|
155
165
|
route: {
|
|
156
166
|
path: finalPath,
|
|
@@ -165,6 +175,7 @@ export function parseDocFile(
|
|
|
165
175
|
badge: sanitizedBadge,
|
|
166
176
|
icon,
|
|
167
177
|
tab: inferredTab,
|
|
178
|
+
_content: plainText,
|
|
168
179
|
},
|
|
169
180
|
relativeDir: cleanDirName,
|
|
170
181
|
isGroupIndex,
|
package/src/node/routes/types.ts
CHANGED