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.
Files changed (41) hide show
  1. package/dist/{SearchDialog-R36WKAQ7.mjs → SearchDialog-5EDRACEG.mjs} +1 -1
  2. package/dist/{SearchDialog-PYF3QMYG.css → SearchDialog-X57WPTNN.css} +54 -126
  3. package/dist/cache-EHR7SXRU.mjs +12 -0
  4. package/dist/chunk-GSYECEZY.mjs +381 -0
  5. package/dist/{chunk-TWSRXUFF.mjs → chunk-NS7WHDYA.mjs} +229 -418
  6. package/dist/client/index.css +54 -126
  7. package/dist/client/index.d.mts +5 -4
  8. package/dist/client/index.d.ts +5 -4
  9. package/dist/client/index.js +555 -580
  10. package/dist/client/index.mjs +304 -16
  11. package/dist/client/ssr.css +54 -126
  12. package/dist/client/ssr.js +257 -580
  13. package/dist/client/ssr.mjs +1 -1
  14. package/dist/{config-D2XmHJYe.d.mts → config-BD5ZHz15.d.mts} +7 -0
  15. package/dist/{config-D2XmHJYe.d.ts → config-BD5ZHz15.d.ts} +7 -0
  16. package/dist/node/index.d.mts +2 -2
  17. package/dist/node/index.d.ts +2 -2
  18. package/dist/node/index.js +457 -118
  19. package/dist/node/index.mjs +93 -136
  20. package/package.json +2 -2
  21. package/src/client/app/index.tsx +25 -54
  22. package/src/client/theme/components/mdx/mdx-components.css +39 -20
  23. package/src/client/theme/styles/markdown.css +1 -1
  24. package/src/client/theme/styles.css +0 -1
  25. package/src/client/theme/ui/Layout/Layout.tsx +2 -13
  26. package/src/client/theme/ui/Layout/responsive.css +0 -4
  27. package/src/client/theme/ui/Link/Link.tsx +52 -0
  28. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -1
  29. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +45 -2
  30. package/src/client/theme/ui/Sidebar/Sidebar.tsx +44 -40
  31. package/src/client/theme/ui/Sidebar/sidebar.css +25 -58
  32. package/src/node/cache.ts +360 -46
  33. package/src/node/config.ts +7 -0
  34. package/src/node/mdx.ts +83 -4
  35. package/src/node/plugin/index.ts +3 -0
  36. package/src/node/routes/cache.ts +5 -1
  37. package/src/node/routes/index.ts +17 -2
  38. package/src/node/ssg/index.ts +4 -0
  39. package/dist/Playground-B2FA34BC.mjs +0 -6
  40. package/dist/chunk-WPT4MWTQ.mjs +0 -89
  41. 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
  );
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { Link } from "../Link";
3
2
  import { ArrowLeft } from "lucide-react";
4
3
 
@@ -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 (e.g. after some delay to ensure rendering)
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
- onCollapse,
79
+ isCollapsed,
80
+ onToggle,
80
81
  }: {
81
82
  routes: RouteItem[];
82
83
  config: BoltdocsConfig;
83
- onCollapse?: () => void;
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
- <nav aria-label="Main Navigation">
110
- <ul className="sidebar-list">
111
- {ungrouped.map((route) => (
112
- <li key={route.path}>
113
- <Link
114
- to={route.path === "" ? "/" : route.path}
115
- className={`sidebar-link ${location.pathname === route.path ? "active" : ""}`}
116
- aria-current={
117
- location.pathname === route.path ? "page" : undefined
118
- }
119
- >
120
- <div className="sidebar-link-content">
121
- <span>{route.title}</span>
122
- {renderBadge(route.badge)}
123
- </div>
124
- </Link>
125
- </li>
126
- ))}
127
- </ul>
128
-
129
- {groups.map((group) => (
130
- <SidebarGroupSection
131
- key={group.slug}
132
- group={group}
133
- currentPath={location.pathname}
134
- />
135
- ))}
136
- </nav>
137
-
138
- {onCollapse && (
139
- <div className="sidebar-footer">
111
+ {onToggle && (
112
+ <div className="sidebar-collapse">
140
113
  <button
141
114
  className="sidebar-collapse-btn"
142
- onClick={onCollapse}
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
- <ChevronLeft size={16} />
147
- <span>Collapse Sidebar</span>
119
+ <PanelLeft size={18} />
148
120
  </button>
149
121
  </div>
150
122
  )}
151
123
 
152
- {config.themeConfig?.poweredBy !== false && <PoweredBy />}
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: 0;
33
- padding-left: 0;
34
- padding-right: 0;
35
- border-right-color: transparent;
36
- opacity: 0;
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-toggle-floating {
42
- position: fixed;
43
- bottom: 2rem;
44
- left: 2rem;
45
- width: 2.75rem;
46
- height: 2.75rem;
47
- border-radius: 50%;
48
- background-color: var(--ld-surface);
49
- border: 1px solid var(--ld-border-strong);
50
- color: var(--ld-text-muted);
40
+ .sidebar-collapse {
41
+ width: 100%;
51
42
  display: flex;
52
43
  align-items: center;
53
- justify-content: center;
54
- cursor: pointer;
55
- z-index: 100;
56
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
57
- transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
58
- opacity: 0;
59
- pointer-events: none;
60
- transform: translateY(20px) scale(0.9);
44
+ justify-content: flex-end;
45
+ padding: 0 0.75rem 1rem;
46
+ transition: justify-content 0.3s ease;
61
47
  }
62
48
 
63
- .sidebar-toggle-floating:hover {
64
- background-color: var(--ld-bg-soft);
65
- color: var(--ld-text-main);
66
- transform: translateY(0px) scale(1.05);
67
- border-color: var(--ld-color-primary);
68
- }
69
-
70
- .sidebar-toggle-floating:active {
71
- transform: translateY(0px) scale(0.95);
72
- }
73
-
74
- .boltdocs-main-container.sidebar-collapsed .sidebar-toggle-floating {
75
- opacity: 1;
76
- pointer-events: auto;
77
- transform: translateY(0) scale(1);
78
- }
79
-
80
- .sidebar-footer {
81
- margin-top: 2rem;
82
- padding-top: 1rem;
83
- border-top: 1px solid var(--ld-border-subtle);
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
- gap: 0.5rem;
90
- width: 100%;
91
- padding: 0.5rem 0.75rem;
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
- color: var(--ld-text-muted);
96
- font-family: var(--ld-font-sans);
97
- font-size: 0.8125rem;
98
- font-weight: 500;
99
- cursor: pointer;
100
- transition: all 0.2s ease;
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-soft);
105
- color: var(--ld-color-primary);
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 {