boltdocs 1.0.4 → 1.3.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-R36WKAQ7.mjs → SearchDialog-5EDRACEG.mjs} +1 -1
- package/dist/{SearchDialog-PYF3QMYG.css → SearchDialog-X57WPTNN.css} +54 -126
- package/dist/cache-EHR7SXRU.mjs +12 -0
- package/dist/chunk-GSYECEZY.mjs +381 -0
- package/dist/{chunk-TWSRXUFF.mjs → chunk-NS7WHDYA.mjs} +229 -418
- package/dist/client/index.css +54 -126
- package/dist/client/index.d.mts +5 -4
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +555 -580
- package/dist/client/index.mjs +304 -16
- package/dist/client/ssr.css +54 -126
- package/dist/client/ssr.js +257 -580
- package/dist/client/ssr.mjs +1 -1
- package/dist/{config-D2XmHJYe.d.mts → config-BD5ZHz15.d.mts} +7 -0
- package/dist/{config-D2XmHJYe.d.ts → config-BD5ZHz15.d.ts} +7 -0
- package/dist/node/index.d.mts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +457 -118
- package/dist/node/index.mjs +93 -136
- package/package.json +2 -2
- package/src/client/app/index.tsx +25 -54
- package/src/client/theme/components/mdx/mdx-components.css +39 -20
- package/src/client/theme/styles/markdown.css +1 -1
- package/src/client/theme/styles.css +0 -1
- package/src/client/theme/ui/Layout/Layout.tsx +2 -13
- package/src/client/theme/ui/Layout/responsive.css +0 -4
- package/src/client/theme/ui/Link/Link.tsx +52 -0
- package/src/client/theme/ui/NotFound/NotFound.tsx +0 -1
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +45 -2
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +44 -40
- package/src/client/theme/ui/Sidebar/sidebar.css +25 -58
- package/src/node/cache.ts +360 -46
- package/src/node/config.ts +7 -0
- package/src/node/mdx.ts +83 -4
- package/src/node/plugin/index.ts +3 -0
- package/src/node/routes/cache.ts +5 -1
- package/src/node/routes/index.ts +17 -2
- package/src/node/ssg/index.ts +4 -0
- package/dist/Playground-B2FA34BC.mjs +0 -6
- package/dist/chunk-WPT4MWTQ.mjs +0 -89
- package/src/client/theme/styles/home.css +0 -60
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
LinkProps as RouterLinkProps,
|
|
6
6
|
NavLinkProps as RouterNavLinkProps,
|
|
7
7
|
useLocation,
|
|
8
|
+
useNavigate,
|
|
8
9
|
} from "react-router-dom";
|
|
9
10
|
import { usePreload } from "../../../app/preload";
|
|
10
11
|
import { useConfig } from "../../../app";
|
|
@@ -107,11 +108,13 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
|
107
108
|
boltdocsPrefetch = "hover",
|
|
108
109
|
onMouseEnter,
|
|
109
110
|
onFocus,
|
|
111
|
+
onClick,
|
|
110
112
|
to,
|
|
111
113
|
...rest
|
|
112
114
|
} = props;
|
|
113
115
|
const localizedTo = useLocalizedTo(to);
|
|
114
116
|
const { preload } = usePreload();
|
|
117
|
+
const navigate = useNavigate();
|
|
115
118
|
|
|
116
119
|
const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
117
120
|
onMouseEnter?.(e);
|
|
@@ -135,12 +138,38 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
|
135
138
|
}
|
|
136
139
|
};
|
|
137
140
|
|
|
141
|
+
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
142
|
+
// Allow user onClick to handle defaults or custom logic
|
|
143
|
+
onClick?.(e);
|
|
144
|
+
|
|
145
|
+
// If default prevented or not a simple left click, don't handle
|
|
146
|
+
if (
|
|
147
|
+
e.defaultPrevented ||
|
|
148
|
+
e.button !== 0 ||
|
|
149
|
+
e.metaKey ||
|
|
150
|
+
e.ctrlKey ||
|
|
151
|
+
e.shiftKey ||
|
|
152
|
+
e.altKey
|
|
153
|
+
) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Intercept navigation to wrap in startTransition
|
|
158
|
+
if (typeof localizedTo === "string" && !localizedTo.startsWith("http")) {
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
React.startTransition(() => {
|
|
161
|
+
navigate(localizedTo);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
138
166
|
return (
|
|
139
167
|
<RouterLink
|
|
140
168
|
ref={ref}
|
|
141
169
|
to={localizedTo}
|
|
142
170
|
onMouseEnter={handleMouseEnter}
|
|
143
171
|
onFocus={handleFocus}
|
|
172
|
+
onClick={handleClick}
|
|
144
173
|
{...rest}
|
|
145
174
|
/>
|
|
146
175
|
);
|
|
@@ -159,12 +188,14 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
|
159
188
|
boltdocsPrefetch = "hover",
|
|
160
189
|
onMouseEnter,
|
|
161
190
|
onFocus,
|
|
191
|
+
onClick,
|
|
162
192
|
to,
|
|
163
193
|
...rest
|
|
164
194
|
} = props;
|
|
165
195
|
|
|
166
196
|
const localizedTo = useLocalizedTo(to);
|
|
167
197
|
const { preload } = usePreload();
|
|
198
|
+
const navigate = useNavigate();
|
|
168
199
|
|
|
169
200
|
const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
170
201
|
onMouseEnter?.(e);
|
|
@@ -188,12 +219,33 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
|
188
219
|
}
|
|
189
220
|
};
|
|
190
221
|
|
|
222
|
+
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
223
|
+
onClick?.(e);
|
|
224
|
+
if (
|
|
225
|
+
e.defaultPrevented ||
|
|
226
|
+
e.button !== 0 ||
|
|
227
|
+
e.metaKey ||
|
|
228
|
+
e.ctrlKey ||
|
|
229
|
+
e.shiftKey ||
|
|
230
|
+
e.altKey
|
|
231
|
+
) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (typeof localizedTo === "string" && !localizedTo.startsWith("http")) {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
React.startTransition(() => {
|
|
237
|
+
navigate(localizedTo);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
191
242
|
return (
|
|
192
243
|
<RouterNavLink
|
|
193
244
|
ref={ref}
|
|
194
245
|
to={localizedTo}
|
|
195
246
|
onMouseEnter={handleMouseEnter}
|
|
196
247
|
onFocus={handleFocus}
|
|
248
|
+
onClick={handleClick}
|
|
197
249
|
{...rest}
|
|
198
250
|
/>
|
|
199
251
|
);
|
|
@@ -70,7 +70,6 @@ export function OnThisPage({
|
|
|
70
70
|
|
|
71
71
|
if (visibleEntries.length > 0) {
|
|
72
72
|
// If we have visible entries, find the one closest to the top of the viewport
|
|
73
|
-
// But with a priority for ones that just entered from the top
|
|
74
73
|
const closest = visibleEntries.reduce((prev, curr) => {
|
|
75
74
|
return Math.abs(curr.boundingClientRect.top - 100) <
|
|
76
75
|
Math.abs(prev.boundingClientRect.top - 100)
|
|
@@ -98,15 +97,59 @@ export function OnThisPage({
|
|
|
98
97
|
// Initial observation
|
|
99
98
|
observeHeadings();
|
|
100
99
|
|
|
101
|
-
// Re-observe if content changes
|
|
100
|
+
// Re-observe if content changes
|
|
102
101
|
const timeoutId = setTimeout(observeHeadings, 1000);
|
|
103
102
|
|
|
103
|
+
// Scroll listener to detect bottom of page
|
|
104
|
+
const handleScroll = () => {
|
|
105
|
+
const scrollPosition = window.innerHeight + window.pageYOffset;
|
|
106
|
+
const bodyHeight = document.documentElement.scrollHeight;
|
|
107
|
+
|
|
108
|
+
// If we're within 50px of the bottom, activate the last heading
|
|
109
|
+
if (scrollPosition >= bodyHeight - 50) {
|
|
110
|
+
setActiveId(headings[headings.length - 1].id);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
115
|
+
|
|
104
116
|
return () => {
|
|
105
117
|
observerRef.current?.disconnect();
|
|
106
118
|
clearTimeout(timeoutId);
|
|
119
|
+
window.removeEventListener("scroll", handleScroll);
|
|
107
120
|
};
|
|
108
121
|
}, [headings, location.pathname]);
|
|
109
122
|
|
|
123
|
+
// Autoscroll TOC list when activeId changes
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (!activeId || !listRef.current) return;
|
|
126
|
+
|
|
127
|
+
const activeLink = listRef.current.querySelector(
|
|
128
|
+
`a[href="#${activeId}"]`,
|
|
129
|
+
) as HTMLElement;
|
|
130
|
+
|
|
131
|
+
if (activeLink) {
|
|
132
|
+
const container = listRef.current.closest(
|
|
133
|
+
".boltdocs-on-this-page",
|
|
134
|
+
) as HTMLElement;
|
|
135
|
+
if (!container) return;
|
|
136
|
+
|
|
137
|
+
const linkRect = activeLink.getBoundingClientRect();
|
|
138
|
+
const containerRect = container.getBoundingClientRect();
|
|
139
|
+
|
|
140
|
+
const isVisible =
|
|
141
|
+
linkRect.top >= containerRect.top &&
|
|
142
|
+
linkRect.bottom <= containerRect.bottom;
|
|
143
|
+
|
|
144
|
+
if (!isVisible) {
|
|
145
|
+
activeLink.scrollIntoView({
|
|
146
|
+
behavior: "smooth",
|
|
147
|
+
block: "nearest",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}, [activeId]);
|
|
152
|
+
|
|
110
153
|
const handleClick = useCallback(
|
|
111
154
|
(e: React.MouseEvent<HTMLAnchorElement>, id: string) => {
|
|
112
155
|
e.preventDefault();
|
|
@@ -3,7 +3,7 @@ import { useLocation } from "react-router-dom";
|
|
|
3
3
|
import { Link } from "../Link";
|
|
4
4
|
import { BoltdocsConfig } from "../../../../node/config";
|
|
5
5
|
import { PoweredBy } from "../PoweredBy";
|
|
6
|
-
import { ChevronRight, ChevronLeft } from "lucide-react";
|
|
6
|
+
import { ChevronRight, ChevronLeft, PanelLeft } from "lucide-react";
|
|
7
7
|
|
|
8
8
|
interface RouteItem {
|
|
9
9
|
path: string;
|
|
@@ -76,11 +76,13 @@ function renderBadge(badgeRaw: RouteItem["badge"]) {
|
|
|
76
76
|
export function Sidebar({
|
|
77
77
|
routes,
|
|
78
78
|
config,
|
|
79
|
-
|
|
79
|
+
isCollapsed,
|
|
80
|
+
onToggle,
|
|
80
81
|
}: {
|
|
81
82
|
routes: RouteItem[];
|
|
82
83
|
config: BoltdocsConfig;
|
|
83
|
-
|
|
84
|
+
isCollapsed?: boolean;
|
|
85
|
+
onToggle?: () => void;
|
|
84
86
|
}) {
|
|
85
87
|
const location = useLocation();
|
|
86
88
|
|
|
@@ -106,50 +108,52 @@ export function Sidebar({
|
|
|
106
108
|
|
|
107
109
|
return (
|
|
108
110
|
<aside className="boltdocs-sidebar">
|
|
109
|
-
|
|
110
|
-
<
|
|
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">
|
|
111
|
+
{onToggle && (
|
|
112
|
+
<div className="sidebar-collapse">
|
|
140
113
|
<button
|
|
141
114
|
className="sidebar-collapse-btn"
|
|
142
|
-
onClick={
|
|
143
|
-
aria-label="Collapse Sidebar"
|
|
144
|
-
title="Collapse Sidebar"
|
|
115
|
+
onClick={onToggle}
|
|
116
|
+
aria-label={isCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
117
|
+
title={isCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
145
118
|
>
|
|
146
|
-
<
|
|
147
|
-
<span>Collapse Sidebar</span>
|
|
119
|
+
<PanelLeft size={18} />
|
|
148
120
|
</button>
|
|
149
121
|
</div>
|
|
150
122
|
)}
|
|
151
123
|
|
|
152
|
-
{
|
|
124
|
+
{!isCollapsed && (
|
|
125
|
+
<>
|
|
126
|
+
<nav aria-label="Main Navigation">
|
|
127
|
+
<ul className="sidebar-list">
|
|
128
|
+
{ungrouped.map((route) => (
|
|
129
|
+
<li key={route.path}>
|
|
130
|
+
<Link
|
|
131
|
+
to={route.path === "" ? "/" : route.path}
|
|
132
|
+
className={`sidebar-link ${location.pathname === route.path ? "active" : ""}`}
|
|
133
|
+
aria-current={
|
|
134
|
+
location.pathname === route.path ? "page" : undefined
|
|
135
|
+
}
|
|
136
|
+
>
|
|
137
|
+
<div className="sidebar-link-content">
|
|
138
|
+
<span>{route.title}</span>
|
|
139
|
+
{renderBadge(route.badge)}
|
|
140
|
+
</div>
|
|
141
|
+
</Link>
|
|
142
|
+
</li>
|
|
143
|
+
))}
|
|
144
|
+
</ul>
|
|
145
|
+
|
|
146
|
+
{groups.map((group) => (
|
|
147
|
+
<SidebarGroupSection
|
|
148
|
+
key={group.slug}
|
|
149
|
+
group={group}
|
|
150
|
+
currentPath={location.pathname}
|
|
151
|
+
/>
|
|
152
|
+
))}
|
|
153
|
+
</nav>
|
|
154
|
+
{config.themeConfig?.poweredBy !== false && <PoweredBy />}
|
|
155
|
+
</>
|
|
156
|
+
)}
|
|
153
157
|
</aside>
|
|
154
158
|
);
|
|
155
159
|
}
|
|
@@ -29,80 +29,47 @@
|
|
|
29
29
|
|
|
30
30
|
/* ─── Collapsible Sidebar ────────────────────────────────── */
|
|
31
31
|
.boltdocs-main-container.sidebar-collapsed .boltdocs-sidebar {
|
|
32
|
-
width:
|
|
33
|
-
padding
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
pointer-events: none;
|
|
32
|
+
width: 54px;
|
|
33
|
+
padding: 1rem 0;
|
|
34
|
+
border-right: 1px solid var(--ld-border-subtle);
|
|
35
|
+
opacity: 1;
|
|
36
|
+
pointer-events: auto;
|
|
38
37
|
overflow: hidden;
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
.sidebar-
|
|
42
|
-
|
|
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);
|
|
40
|
+
.sidebar-collapse {
|
|
41
|
+
width: 100%;
|
|
51
42
|
display: flex;
|
|
52
43
|
align-items: center;
|
|
53
|
-
justify-content:
|
|
54
|
-
|
|
55
|
-
|
|
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);
|
|
44
|
+
justify-content: flex-end;
|
|
45
|
+
padding: 0 0.75rem 1rem;
|
|
46
|
+
transition: justify-content 0.3s ease;
|
|
61
47
|
}
|
|
62
48
|
|
|
63
|
-
.sidebar-
|
|
64
|
-
|
|
65
|
-
|
|
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);
|
|
49
|
+
.boltdocs-main-container.sidebar-collapsed .sidebar-collapse {
|
|
50
|
+
justify-content: center;
|
|
51
|
+
padding: 0 0 1rem;
|
|
84
52
|
}
|
|
85
53
|
|
|
86
54
|
.sidebar-collapse-btn {
|
|
55
|
+
color: var(--ld-text-muted);
|
|
56
|
+
border: none;
|
|
57
|
+
box-shadow: none;
|
|
58
|
+
background-color: transparent;
|
|
59
|
+
cursor: pointer;
|
|
87
60
|
display: flex;
|
|
88
61
|
align-items: center;
|
|
89
|
-
|
|
90
|
-
width:
|
|
91
|
-
|
|
92
|
-
background: none;
|
|
93
|
-
border: none;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
width: 32px;
|
|
64
|
+
height: 32px;
|
|
94
65
|
border-radius: var(--ld-radius-md);
|
|
95
|
-
|
|
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;
|
|
66
|
+
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
|
101
67
|
}
|
|
102
68
|
|
|
103
69
|
.sidebar-collapse-btn:hover {
|
|
104
|
-
background-color: var(--ld-bg-
|
|
105
|
-
color: var(--ld-
|
|
70
|
+
background-color: var(--ld-bg-mute);
|
|
71
|
+
color: var(--ld-text-main);
|
|
72
|
+
transform: scale(1.05);
|
|
106
73
|
}
|
|
107
74
|
|
|
108
75
|
.boltdocs-sidebar::-webkit-scrollbar {
|