boltdocs 1.0.1 → 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 (64) hide show
  1. package/dist/{CodeBlock-37XMKCYY.mjs → CodeBlock-V3Z5EKGR.mjs} +0 -1
  2. package/dist/{PackageManagerTabs-4NWXLXQO.mjs → PackageManagerTabs-XEKI3L7P.mjs} +0 -2
  3. package/dist/{SearchDialog-FTOQZ763.mjs → SearchDialog-5EDRACEG.mjs} +1 -2
  4. package/dist/{SearchDialog-ZAZXYIFX.css → SearchDialog-X57WPTNN.css} +57 -129
  5. package/dist/{Video-I6QY4X7J.mjs → Video-KNTY5BNO.mjs} +0 -1
  6. package/dist/cache-EHR7SXRU.mjs +12 -0
  7. package/dist/chunk-GSYECEZY.mjs +381 -0
  8. package/dist/{chunk-ZFCOLEXN.mjs → chunk-NS7WHDYA.mjs} +234 -426
  9. package/dist/client/index.css +57 -129
  10. package/dist/client/index.d.mts +39 -8
  11. package/dist/client/index.d.ts +39 -8
  12. package/dist/client/index.js +557 -564
  13. package/dist/client/index.mjs +305 -18
  14. package/dist/client/ssr.css +57 -129
  15. package/dist/client/ssr.d.mts +1 -1
  16. package/dist/client/ssr.d.ts +1 -1
  17. package/dist/client/ssr.js +257 -558
  18. package/dist/client/ssr.mjs +1 -2
  19. package/dist/{config-D2XmHJYe.d.mts → config-BD5ZHz15.d.mts} +7 -0
  20. package/dist/{config-D2XmHJYe.d.ts → config-BD5ZHz15.d.ts} +7 -0
  21. package/dist/node/index.d.mts +2 -2
  22. package/dist/node/index.d.ts +2 -2
  23. package/dist/node/index.js +457 -118
  24. package/dist/node/index.mjs +144 -147
  25. package/dist/{index-CRQKWAeo.d.mts → types-CvrzTbEX.d.mts} +1 -28
  26. package/dist/{index-CRQKWAeo.d.ts → types-CvrzTbEX.d.ts} +1 -28
  27. package/package.json +2 -2
  28. package/src/client/app/index.tsx +32 -110
  29. package/src/client/app/preload.tsx +1 -1
  30. package/src/client/index.ts +1 -1
  31. package/src/client/ssr.tsx +2 -1
  32. package/src/client/theme/components/Playground/Playground.tsx +40 -2
  33. package/src/client/theme/components/mdx/mdx-components.css +39 -20
  34. package/src/client/theme/styles/markdown.css +4 -4
  35. package/src/client/theme/styles.css +0 -1
  36. package/src/client/theme/ui/Breadcrumbs/Breadcrumbs.tsx +1 -1
  37. package/src/client/theme/ui/LanguageSwitcher/LanguageSwitcher.tsx +1 -1
  38. package/src/client/theme/ui/Layout/Layout.tsx +3 -14
  39. package/src/client/theme/ui/Layout/responsive.css +0 -4
  40. package/src/client/theme/ui/Link/Link.tsx +52 -0
  41. package/src/client/theme/ui/Navbar/Navbar.tsx +1 -1
  42. package/src/client/theme/ui/NotFound/NotFound.tsx +0 -1
  43. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +45 -2
  44. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +1 -1
  45. package/src/client/theme/ui/Sidebar/Sidebar.tsx +44 -40
  46. package/src/client/theme/ui/Sidebar/sidebar.css +25 -58
  47. package/src/client/theme/ui/VersionSwitcher/VersionSwitcher.tsx +1 -1
  48. package/src/client/types.ts +50 -0
  49. package/src/node/cache.ts +360 -46
  50. package/src/node/config.ts +7 -0
  51. package/src/node/mdx.ts +83 -4
  52. package/src/node/plugin/index.ts +3 -0
  53. package/src/node/routes/cache.ts +5 -1
  54. package/src/node/routes/index.ts +17 -2
  55. package/src/node/ssg/index.ts +4 -0
  56. package/dist/Playground-OE2OE6B6.mjs +0 -7
  57. package/dist/chunk-PN4GCTYG.mjs +0 -67
  58. package/dist/chunk-X2TDGMTR.mjs +0 -64
  59. package/dist/chunk-X6BYQHVC.mjs +0 -12
  60. package/dist/node/cli/index.d.mts +0 -1
  61. package/dist/node/cli/index.d.ts +0 -1
  62. package/dist/node/cli/index.js +0 -199
  63. package/dist/node/cli/index.mjs +0 -154
  64. package/src/client/theme/styles/home.css +0 -60
@@ -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();
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
  import { Link } from "../Link";
4
4
  import { Search } from "lucide-react";
5
- import { ComponentRoute } from "../../../app";
5
+ import { ComponentRoute } from "../../../types";
6
6
 
7
7
  interface SearchResult {
8
8
  title: string;
@@ -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 {
@@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from "react";
2
2
  import { Layers, ChevronDown } from "lucide-react";
3
3
  import { useNavigate, useLocation } from "react-router-dom";
4
4
  import { BoltdocsVersionsConfig } from "../../../../node/config";
5
- import { ComponentRoute } from "../../../app";
5
+ import { ComponentRoute } from "../../../types";
6
6
 
7
7
  function getBaseFilePath(
8
8
  filePath: string,
@@ -0,0 +1,50 @@
1
+ import React from "react";
2
+
3
+ /**
4
+ * Metadata provided by the server for a specific route.
5
+ * Maps closely to the `RouteMeta` type in the Node environment.
6
+ */
7
+ export interface ComponentRoute {
8
+ /** The final URL path */
9
+ path: string;
10
+ /** The absolute filesystem path of the source file */
11
+ componentPath: string;
12
+ /** The page title */
13
+ title: string;
14
+ /** Explicit order in the sidebar */
15
+ sidebarPosition?: number;
16
+ /** The relative path from the docs directory */
17
+ filePath: string;
18
+ /** The group directory name */
19
+ group?: string;
20
+ /** The display title of the group */
21
+ groupTitle?: string;
22
+ /** Explicit order of the group in the sidebar */
23
+ groupPosition?: number;
24
+ /** Extracted markdown headings for search indexing */
25
+ headings?: { level: number; text: string; id: string }[];
26
+ /** The locale this route belongs to, if i18n is configured */
27
+ locale?: string;
28
+ /** The version this route belongs to, if versioning is configured */
29
+ version?: string;
30
+ }
31
+
32
+ /**
33
+ * Configuration options for initializing the Boltdocs client app.
34
+ */
35
+ export interface CreateBoltdocsAppOptions {
36
+ /** CSS selector for the DOM element where the app should mount (e.g. '#root') */
37
+ target: string;
38
+ /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
39
+ routes: ComponentRoute[];
40
+ /** Site configuration (`virtual:boltdocs-config`) */
41
+ config: any;
42
+ /** Dynamic import mapping from `import.meta.glob` for the documentation pages */
43
+ modules: Record<string, () => Promise<any>>;
44
+ /** The `import.meta.hot` instance necessary for fast refresh/HMR updates */
45
+ hot?: any;
46
+ /** Optional custom React component to render when visiting the root path ('/') */
47
+ homePage?: React.ComponentType;
48
+ /** Optional custom MDX components provided by plugins */
49
+ components?: Record<string, React.ComponentType<any>>;
50
+ }