boltdocs 1.5.0 → 1.7.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/{PackageManagerTabs-XW3AVXVX.mjs → PackageManagerTabs-NVT7G625.mjs} +1 -1
- package/dist/{SearchDialog-CEVPEMT3.css → SearchDialog-UOAW6IR3.css} +361 -113
- package/dist/{SearchDialog-5ISK64QY.mjs → SearchDialog-YOXMFGH6.mjs} +1 -1
- package/dist/{chunk-S5G55FBI.mjs → chunk-7SFUJWTB.mjs} +4 -4
- package/dist/{chunk-FMQ4HRKZ.mjs → chunk-MULKZFVN.mjs} +873 -759
- package/dist/client/index.css +361 -113
- package/dist/client/index.d.mts +39 -8
- package/dist/client/index.d.ts +39 -8
- package/dist/client/index.js +722 -467
- package/dist/client/index.mjs +197 -58
- package/dist/client/ssr.css +361 -113
- package/dist/client/ssr.d.mts +3 -1
- package/dist/client/ssr.d.ts +3 -1
- package/dist/client/ssr.js +533 -412
- package/dist/client/ssr.mjs +3 -2
- package/dist/{config-DkZg5aCf.d.ts → config-D68h41CA.d.mts} +21 -2
- package/dist/{config-DkZg5aCf.d.mts → config-D68h41CA.d.ts} +21 -2
- package/dist/node/index.d.mts +10 -2
- package/dist/node/index.d.ts +10 -2
- package/dist/node/index.js +45 -21
- package/dist/node/index.mjs +45 -21
- package/dist/{types-DGIo1VKD.d.ts → types-CviV0GbX.d.mts} +13 -0
- package/dist/{types-DGIo1VKD.d.mts → types-CviV0GbX.d.ts} +13 -0
- package/package.json +1 -1
- package/src/client/app/index.tsx +8 -4
- package/src/client/index.ts +4 -0
- package/src/client/ssr.tsx +4 -1
- package/src/client/theme/components/mdx/FileTree.tsx +229 -0
- package/src/client/theme/components/mdx/Table.tsx +53 -0
- package/src/client/theme/components/mdx/Tabs.tsx +1 -4
- package/src/client/theme/components/mdx/index.ts +6 -0
- package/src/client/theme/components/mdx/mdx-components.css +158 -0
- package/src/client/theme/icons/pnpm.tsx +5 -5
- package/src/client/theme/styles/markdown.css +8 -3
- package/src/client/theme/styles/variables.css +10 -9
- package/src/client/theme/ui/Layout/Layout.tsx +2 -10
- package/src/client/theme/ui/Layout/base.css +15 -3
- package/src/client/theme/ui/Link/Link.tsx +2 -2
- package/src/client/theme/ui/Link/LinkPreview.tsx +9 -14
- package/src/client/theme/ui/Link/link-preview.css +30 -27
- package/src/client/theme/ui/Navbar/Navbar.tsx +65 -17
- package/src/client/theme/ui/Navbar/Tabs.tsx +74 -0
- package/src/client/theme/ui/Navbar/navbar.css +111 -5
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +65 -49
- package/src/client/theme/ui/OnThisPage/toc.css +30 -10
- package/src/client/theme/ui/Sidebar/Sidebar.tsx +97 -57
- package/src/client/theme/ui/Sidebar/sidebar.css +61 -67
- package/src/client/types.ts +10 -0
- package/src/node/config.ts +19 -1
- package/src/node/plugin/entry.ts +5 -1
- package/src/node/plugin/index.ts +2 -1
- package/src/node/routes/index.ts +12 -1
- package/src/node/routes/parser.ts +21 -7
- package/src/node/routes/types.ts +9 -1
- package/src/node/ssg/index.ts +2 -1
- package/src/node/ssg/options.ts +2 -0
- package/src/client/theme/icons/yarn.tsx +0 -16
package/src/client/ssr.tsx
CHANGED
|
@@ -14,6 +14,8 @@ export interface RenderOptions {
|
|
|
14
14
|
routes: ComponentRoute[];
|
|
15
15
|
/** Site configuration (`virtual:boltdocs-config`) */
|
|
16
16
|
config: any;
|
|
17
|
+
/** The name of the documentation directory (e.g. 'docs') */
|
|
18
|
+
docsDirName: string;
|
|
17
19
|
/** Optional custom React component to render when visiting the root path ('/') */
|
|
18
20
|
homePage?: React.ComponentType;
|
|
19
21
|
/** Preloaded modules (since SSR cannot use dynamic imports easily) */
|
|
@@ -25,7 +27,7 @@ export interface RenderOptions {
|
|
|
25
27
|
* This is called by the Node SSG script during the Vite build process.
|
|
26
28
|
*/
|
|
27
29
|
export async function render(options: RenderOptions): Promise<string> {
|
|
28
|
-
const { path, routes, config, modules, homePage } = options;
|
|
30
|
+
const { path, routes, config, modules, homePage, docsDirName } = options;
|
|
29
31
|
|
|
30
32
|
// For SSR, we must resolve modules synchronously. We create a mock 'loader'
|
|
31
33
|
// that instantly returns the module since the SSG script already loaded it.
|
|
@@ -40,6 +42,7 @@ export async function render(options: RenderOptions): Promise<string> {
|
|
|
40
42
|
<AppShell
|
|
41
43
|
initialRoutes={routes}
|
|
42
44
|
initialConfig={config}
|
|
45
|
+
docsDirName={docsDirName}
|
|
43
46
|
modules={resolvedModules}
|
|
44
47
|
homePage={homePage}
|
|
45
48
|
/>
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import React, { Children, isValidElement, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Folder,
|
|
4
|
+
FileText,
|
|
5
|
+
File,
|
|
6
|
+
FileCode,
|
|
7
|
+
FileImage,
|
|
8
|
+
ChevronRight,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
|
|
11
|
+
export interface FileTreeProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getTextContent(node: React.ReactNode): string {
|
|
16
|
+
if (typeof node === "string") return node;
|
|
17
|
+
if (typeof node === "number") return node.toString();
|
|
18
|
+
if (Array.isArray(node)) return node.map(getTextContent).join("");
|
|
19
|
+
if (isValidElement(node)) {
|
|
20
|
+
return getTextContent((node.props as any).children);
|
|
21
|
+
}
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getFileIcon(filename: string) {
|
|
26
|
+
const name = filename.toLowerCase();
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
name.endsWith(".ts") ||
|
|
30
|
+
name.endsWith(".tsx") ||
|
|
31
|
+
name.endsWith(".js") ||
|
|
32
|
+
name.endsWith(".jsx") ||
|
|
33
|
+
name.endsWith(".json") ||
|
|
34
|
+
name.endsWith(".mjs") ||
|
|
35
|
+
name.endsWith(".cjs") ||
|
|
36
|
+
name.endsWith(".astro") ||
|
|
37
|
+
name.endsWith(".vue") ||
|
|
38
|
+
name.endsWith(".svelte")
|
|
39
|
+
) {
|
|
40
|
+
return (
|
|
41
|
+
<FileCode size={16} strokeWidth={2} className="ld-file-tree__icon-file" />
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (name.endsWith(".md") || name.endsWith(".mdx") || name.endsWith(".txt")) {
|
|
46
|
+
return (
|
|
47
|
+
<FileText size={16} strokeWidth={2} className="ld-file-tree__icon-file" />
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
name.endsWith(".png") ||
|
|
53
|
+
name.endsWith(".jpg") ||
|
|
54
|
+
name.endsWith(".jpeg") ||
|
|
55
|
+
name.endsWith(".svg") ||
|
|
56
|
+
name.endsWith(".gif")
|
|
57
|
+
) {
|
|
58
|
+
return (
|
|
59
|
+
<FileImage
|
|
60
|
+
size={16}
|
|
61
|
+
strokeWidth={2}
|
|
62
|
+
className="ld-file-tree__icon-file"
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return <File size={16} strokeWidth={2} className="ld-file-tree__icon-file" />;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Helper to reliably check for ul and li elements, including MDX wrappers
|
|
71
|
+
function isListElement(node: any, tag: "ul" | "li"): boolean {
|
|
72
|
+
if (typeof node.type === "string") {
|
|
73
|
+
return node.type === tag;
|
|
74
|
+
}
|
|
75
|
+
if (typeof node.type === "function") {
|
|
76
|
+
return node.type.name === tag || node.type.name?.toLowerCase() === tag;
|
|
77
|
+
}
|
|
78
|
+
// MDX specific wrapper detection
|
|
79
|
+
if (node.props && node.props.originalType === tag) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (node.props && node.props.mdxType === tag) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function FolderNode({
|
|
89
|
+
labelText,
|
|
90
|
+
nestedNodes,
|
|
91
|
+
depth,
|
|
92
|
+
}: {
|
|
93
|
+
labelText: string;
|
|
94
|
+
nestedNodes: React.ReactNode[];
|
|
95
|
+
depth: number;
|
|
96
|
+
}) {
|
|
97
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<li className="ld-file-tree__item">
|
|
101
|
+
<div
|
|
102
|
+
className="ld-file-tree__label ld-file-tree__label--folder"
|
|
103
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
104
|
+
style={{ cursor: "pointer" }}
|
|
105
|
+
>
|
|
106
|
+
<span className="ld-file-tree__icon ld-file-tree__icon--chevron">
|
|
107
|
+
<ChevronRight
|
|
108
|
+
size={14}
|
|
109
|
+
className={`ld-file-tree__chevron ${isOpen ? "ld-file-tree__chevron--open" : ""}`}
|
|
110
|
+
strokeWidth={3}
|
|
111
|
+
/>
|
|
112
|
+
</span>
|
|
113
|
+
<span className="ld-file-tree__icon">
|
|
114
|
+
<Folder
|
|
115
|
+
size={16}
|
|
116
|
+
strokeWidth={2}
|
|
117
|
+
className="ld-file-tree__icon-folder"
|
|
118
|
+
fill="currentColor"
|
|
119
|
+
fillOpacity={0.15}
|
|
120
|
+
/>
|
|
121
|
+
</span>
|
|
122
|
+
<span className="ld-file-tree__name">{labelText}</span>
|
|
123
|
+
</div>
|
|
124
|
+
{isOpen && nestedNodes.length > 0 && (
|
|
125
|
+
<div className="ld-file-tree__nested">
|
|
126
|
+
{nestedNodes.map((child, index) => (
|
|
127
|
+
<React.Fragment key={index}>
|
|
128
|
+
{parseNode(child, depth)}
|
|
129
|
+
</React.Fragment>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
</li>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseNode(node: React.ReactNode, depth: number = 0): React.ReactNode {
|
|
138
|
+
if (!isValidElement(node)) {
|
|
139
|
+
return node;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isListElement(node, "ul")) {
|
|
143
|
+
return (
|
|
144
|
+
<ul
|
|
145
|
+
className={`ld-file-tree__list ${depth === 0 ? "ld-file-tree__list--root" : ""}`}
|
|
146
|
+
>
|
|
147
|
+
{Children.map((node.props as any).children, (child, index) => (
|
|
148
|
+
<React.Fragment key={index}>
|
|
149
|
+
{parseNode(child, depth + 1)}
|
|
150
|
+
</React.Fragment>
|
|
151
|
+
))}
|
|
152
|
+
</ul>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (isListElement(node, "li")) {
|
|
157
|
+
const children = Children.toArray((node.props as any).children);
|
|
158
|
+
|
|
159
|
+
// Find nested list indicating a directory
|
|
160
|
+
const nestedListIndex = children.findIndex(
|
|
161
|
+
(child) => isValidElement(child) && isListElement(child, "ul"),
|
|
162
|
+
);
|
|
163
|
+
const hasNested = nestedListIndex !== -1;
|
|
164
|
+
|
|
165
|
+
// Separate text label from nested items
|
|
166
|
+
const labelNodes = hasNested
|
|
167
|
+
? children.slice(0, nestedListIndex)
|
|
168
|
+
: children;
|
|
169
|
+
const nestedNodes = hasNested ? children.slice(nestedListIndex) : [];
|
|
170
|
+
|
|
171
|
+
const rawLabelContent = getTextContent(labelNodes).trim();
|
|
172
|
+
const isExplicitDir = rawLabelContent.endsWith("/");
|
|
173
|
+
const labelText = isExplicitDir
|
|
174
|
+
? rawLabelContent.slice(0, -1)
|
|
175
|
+
: rawLabelContent;
|
|
176
|
+
|
|
177
|
+
const isFolder = hasNested || isExplicitDir;
|
|
178
|
+
|
|
179
|
+
if (isFolder) {
|
|
180
|
+
return (
|
|
181
|
+
<FolderNode
|
|
182
|
+
labelText={labelText}
|
|
183
|
+
nestedNodes={nestedNodes}
|
|
184
|
+
depth={depth}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<li className="ld-file-tree__item">
|
|
191
|
+
<div className="ld-file-tree__label ld-file-tree__label--file">
|
|
192
|
+
<span className="ld-file-tree__icon ld-file-tree__icon--spacer"></span>
|
|
193
|
+
<span className="ld-file-tree__icon">{getFileIcon(labelText)}</span>
|
|
194
|
+
<span className="ld-file-tree__name">{labelText}</span>
|
|
195
|
+
</div>
|
|
196
|
+
</li>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// If node is e.g. a paragraph injected by MDX wrapping the list
|
|
201
|
+
if ((node.props as any).children) {
|
|
202
|
+
return Children.map((node.props as any).children, (child, index) => (
|
|
203
|
+
<React.Fragment key={index}>{parseNode(child, depth)}</React.Fragment>
|
|
204
|
+
));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return node;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* FileTree component displays a customized, styled tree structure for markdown lists.
|
|
212
|
+
*
|
|
213
|
+
* ```mdx
|
|
214
|
+
* <FileTree>
|
|
215
|
+
* - src/
|
|
216
|
+
* - index.ts
|
|
217
|
+
* - components/
|
|
218
|
+
* - Button.tsx
|
|
219
|
+
* - package.json
|
|
220
|
+
* </FileTree>
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export function FileTree({ children }: FileTreeProps) {
|
|
224
|
+
return (
|
|
225
|
+
<div className="ld-file-tree" dir="ltr">
|
|
226
|
+
{Children.map(children, (child) => parseNode(child, 0))}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export interface TableProps {
|
|
4
|
+
headers?: string[];
|
|
5
|
+
data?: (string | React.ReactNode)[][];
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A consistent, themed table component for documentation.
|
|
12
|
+
* Can be used by passing structured 'headers' and 'data' props,
|
|
13
|
+
* or by wrapping standard <thead>/<tbody> elements.
|
|
14
|
+
*/
|
|
15
|
+
export function Table({
|
|
16
|
+
headers,
|
|
17
|
+
data,
|
|
18
|
+
children,
|
|
19
|
+
className = "",
|
|
20
|
+
}: TableProps) {
|
|
21
|
+
const tableContent = children ? (
|
|
22
|
+
children
|
|
23
|
+
) : (
|
|
24
|
+
<>
|
|
25
|
+
{headers && (
|
|
26
|
+
<thead>
|
|
27
|
+
<tr>
|
|
28
|
+
{headers.map((header, i) => (
|
|
29
|
+
<th key={i}>{header}</th>
|
|
30
|
+
))}
|
|
31
|
+
</tr>
|
|
32
|
+
</thead>
|
|
33
|
+
)}
|
|
34
|
+
{data && (
|
|
35
|
+
<tbody>
|
|
36
|
+
{data.map((row, i) => (
|
|
37
|
+
<tr key={i}>
|
|
38
|
+
{row.map((cell, j) => (
|
|
39
|
+
<td key={j}>{cell}</td>
|
|
40
|
+
))}
|
|
41
|
+
</tr>
|
|
42
|
+
))}
|
|
43
|
+
</tbody>
|
|
44
|
+
)}
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className={`ld-table-container ${className}`.trim()}>
|
|
50
|
+
<table className="ld-table">{tableContent}</table>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -4,7 +4,6 @@ import { NPM } from "../../icons/npm";
|
|
|
4
4
|
import { Pnpm } from "../../icons/pnpm";
|
|
5
5
|
import { Bun } from "../../icons/bun";
|
|
6
6
|
import { Deno } from "../../icons/deno";
|
|
7
|
-
import { Yarn } from "../../icons/yarn";
|
|
8
7
|
|
|
9
8
|
/* ─── Tab (individual panel) ──────────────────────────────── */
|
|
10
9
|
export interface TabProps {
|
|
@@ -43,9 +42,8 @@ export interface TabsProps {
|
|
|
43
42
|
|
|
44
43
|
const getIconForLabel = (label: string) => {
|
|
45
44
|
const l = label.toLowerCase();
|
|
46
|
-
if (l.includes("npm")) return <NPM />;
|
|
47
45
|
if (l.includes("pnpm")) return <Pnpm />;
|
|
48
|
-
if (l.includes("
|
|
46
|
+
if (l.includes("npm")) return <NPM />;
|
|
49
47
|
if (l.includes("bun")) return <Bun />;
|
|
50
48
|
if (l.includes("deno")) return <Deno />;
|
|
51
49
|
return null;
|
|
@@ -58,7 +56,6 @@ const getIconForLabel = (label: string) => {
|
|
|
58
56
|
* <Tabs>
|
|
59
57
|
* <Tab label="npm">npm install boltdocs</Tab>
|
|
60
58
|
* <Tab label="pnpm">pnpm add boltdocs</Tab>
|
|
61
|
-
* <Tab label="yarn">yarn add boltdocs</Tab>
|
|
62
59
|
* </Tabs>
|
|
63
60
|
* ```
|
|
64
61
|
*/
|
|
@@ -16,3 +16,9 @@ export type { AdmonitionProps } from "./Admonition";
|
|
|
16
16
|
|
|
17
17
|
export { List } from "./List";
|
|
18
18
|
export type { ListProps } from "./List";
|
|
19
|
+
|
|
20
|
+
export { FileTree } from "./FileTree";
|
|
21
|
+
export type { FileTreeProps } from "./FileTree";
|
|
22
|
+
|
|
23
|
+
export { Table } from "./Table";
|
|
24
|
+
export type { TableProps } from "./Table";
|
|
@@ -430,3 +430,161 @@
|
|
|
430
430
|
.ld-list--arrow .ld-list__icon {
|
|
431
431
|
color: var(--ld-color-primary);
|
|
432
432
|
}
|
|
433
|
+
|
|
434
|
+
/* ─── FileTree ────────────────────────────────────────────── */
|
|
435
|
+
.ld-file-tree {
|
|
436
|
+
margin: 1.5rem 0;
|
|
437
|
+
padding: 1rem;
|
|
438
|
+
border-radius: var(--ld-radius-lg);
|
|
439
|
+
border: 1px solid var(--ld-border-subtle);
|
|
440
|
+
background: var(--ld-bg-soft);
|
|
441
|
+
font-family: var(--ld-font-mono);
|
|
442
|
+
font-size: 0.875rem;
|
|
443
|
+
overflow-x: auto;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.ld-file-tree__list {
|
|
447
|
+
list-style: none !important;
|
|
448
|
+
margin: 0 !important;
|
|
449
|
+
padding: 0;
|
|
450
|
+
position: relative;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.ld-file-tree__list:not(.ld-file-tree__list--root) {
|
|
454
|
+
padding-left: 1.25rem;
|
|
455
|
+
margin-top: 0.25rem !important;
|
|
456
|
+
position: relative;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Vertical line for nested folders */
|
|
460
|
+
.ld-file-tree__list:not(.ld-file-tree__list--root)::before {
|
|
461
|
+
content: "";
|
|
462
|
+
position: absolute;
|
|
463
|
+
top: 0;
|
|
464
|
+
bottom: 0;
|
|
465
|
+
left: 0.45rem; /* align with folder icon */
|
|
466
|
+
width: 1px;
|
|
467
|
+
background-color: var(--ld-border-subtle);
|
|
468
|
+
z-index: 0;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.ld-file-tree__item {
|
|
472
|
+
position: relative;
|
|
473
|
+
margin: 0.2rem 0 !important;
|
|
474
|
+
padding: 0 !important;
|
|
475
|
+
display: block !important;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.ld-file-tree__label {
|
|
479
|
+
display: inline-flex;
|
|
480
|
+
align-items: center;
|
|
481
|
+
gap: 0.5rem;
|
|
482
|
+
color: var(--ld-text-muted);
|
|
483
|
+
user-select: none;
|
|
484
|
+
border-radius: var(--ld-radius-sm);
|
|
485
|
+
padding: 0.25rem 0.6rem 0.25rem 0.4rem;
|
|
486
|
+
transition: all 0.2s ease;
|
|
487
|
+
position: relative;
|
|
488
|
+
z-index: 1;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.ld-file-tree__label:hover {
|
|
492
|
+
background: var(--ld-bg-mute);
|
|
493
|
+
color: var(--ld-text-main);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.ld-file-tree__label--folder {
|
|
497
|
+
color: var(--ld-text-main);
|
|
498
|
+
font-weight: 500;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.ld-file-tree__icon {
|
|
502
|
+
display: inline-flex;
|
|
503
|
+
align-items: center;
|
|
504
|
+
justify-content: center;
|
|
505
|
+
opacity: 0.8;
|
|
506
|
+
flex-shrink: 0;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.ld-file-tree__icon-folder {
|
|
510
|
+
color: var(--ld-color-primary);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.ld-file-tree__name {
|
|
514
|
+
white-space: nowrap;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.ld-file-tree__icon--chevron {
|
|
518
|
+
width: 14px;
|
|
519
|
+
height: 14px;
|
|
520
|
+
opacity: 0.5;
|
|
521
|
+
transition: opacity 0.2s ease;
|
|
522
|
+
display: flex !important;
|
|
523
|
+
align-items: center;
|
|
524
|
+
justify-content: center;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.ld-file-tree__label:hover .ld-file-tree__icon--chevron {
|
|
528
|
+
opacity: 1;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.ld-file-tree__chevron {
|
|
532
|
+
transition: transform 0.2s ease;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.ld-file-tree__chevron--open {
|
|
536
|
+
transform: rotate(90deg);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.ld-file-tree__icon--spacer {
|
|
540
|
+
width: 14px;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/* ─── Table ───────────────────────────────────────────────── */
|
|
544
|
+
.ld-table-container {
|
|
545
|
+
margin: 1.5rem 0;
|
|
546
|
+
border: 1px solid var(--ld-border-subtle);
|
|
547
|
+
border-radius: var(--ld-radius-lg);
|
|
548
|
+
overflow: hidden;
|
|
549
|
+
background: var(--ld-bg-soft);
|
|
550
|
+
overflow-x: auto;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.ld-table {
|
|
554
|
+
width: 100%;
|
|
555
|
+
border-collapse: collapse;
|
|
556
|
+
text-align: left;
|
|
557
|
+
font-size: 0.875rem;
|
|
558
|
+
line-height: 1.5;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.ld-table thead {
|
|
562
|
+
background: var(--ld-bg-mute);
|
|
563
|
+
border-bottom: 1px solid var(--ld-border-subtle);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.ld-table th {
|
|
567
|
+
padding: 0.75rem 1rem;
|
|
568
|
+
font-weight: 600;
|
|
569
|
+
color: var(--ld-text-main);
|
|
570
|
+
font-size: 0.8125rem;
|
|
571
|
+
text-transform: uppercase;
|
|
572
|
+
letter-spacing: 0.04em;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.ld-table td {
|
|
576
|
+
padding: 0.875rem 1rem;
|
|
577
|
+
color: var(--ld-text-muted);
|
|
578
|
+
border-bottom: 1px solid var(--ld-border-subtle);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.ld-table tr:last-child td {
|
|
582
|
+
border-bottom: none;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.ld-table code {
|
|
586
|
+
font-size: 0.8rem;
|
|
587
|
+
padding: 0.2rem 0.4rem;
|
|
588
|
+
background: rgba(255, 255, 255, 0.05);
|
|
589
|
+
border-radius: 4px;
|
|
590
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { SVGProps } from "react";
|
|
2
2
|
|
|
3
3
|
const Pnpm = (props: SVGProps<SVGSVGElement>) => (
|
|
4
4
|
<svg
|
|
@@ -54,16 +54,16 @@ const Pnpm = (props: SVGProps<SVGSVGElement>) => (
|
|
|
54
54
|
<use xlinkHref="#pnpm_dark__dqv5133G8" fill="#f9ad00" />
|
|
55
55
|
</g>
|
|
56
56
|
<g>
|
|
57
|
-
<use xlinkHref="#pnpm_dark__b1Lv79ypvm" fill="
|
|
57
|
+
<use xlinkHref="#pnpm_dark__b1Lv79ypvm" fill="#ffffff" />
|
|
58
58
|
</g>
|
|
59
59
|
<g>
|
|
60
|
-
<use xlinkHref="#pnpm_dark__hy1IZWwLX" fill="
|
|
60
|
+
<use xlinkHref="#pnpm_dark__hy1IZWwLX" fill="#ffffff" />
|
|
61
61
|
</g>
|
|
62
62
|
<g>
|
|
63
|
-
<use xlinkHref="#pnpm_dark__akQfjxQes" fill="
|
|
63
|
+
<use xlinkHref="#pnpm_dark__akQfjxQes" fill="#ffffff" />
|
|
64
64
|
</g>
|
|
65
65
|
<g>
|
|
66
|
-
<use xlinkHref="#pnpm_dark__bdSrwE5pk" fill="
|
|
66
|
+
<use xlinkHref="#pnpm_dark__bdSrwE5pk" fill="#ffffff" />
|
|
67
67
|
</g>
|
|
68
68
|
</g>
|
|
69
69
|
</svg>
|
|
@@ -3,19 +3,24 @@
|
|
|
3
3
|
═══════════════════════════════════════════════════════════ */
|
|
4
4
|
.boltdocs-content {
|
|
5
5
|
flex: 1;
|
|
6
|
-
padding: 2rem 2.5rem
|
|
6
|
+
padding: 2rem 2.5rem 6rem; /* Extra padding bottom for fixed layout */
|
|
7
7
|
max-width: var(--ld-content-max-width);
|
|
8
8
|
margin: 0 auto;
|
|
9
9
|
min-width: 0;
|
|
10
|
+
height: 100%;
|
|
11
|
+
overflow-y: auto;
|
|
12
|
+
scrollbar-width: none;
|
|
13
|
+
scroll-behavior: smooth;
|
|
10
14
|
transition:
|
|
11
15
|
max-width 0.3s cubic-bezier(0.16, 1, 0.3, 1),
|
|
12
16
|
padding 0.3s ease;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
.boltdocs-
|
|
16
|
-
|
|
19
|
+
.boltdocs-content::-webkit-scrollbar {
|
|
20
|
+
display: none;
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
|
|
19
24
|
/* ─── Breadcrumbs ────────────────────────────────────────── */
|
|
20
25
|
.boltdocs-breadcrumbs {
|
|
21
26
|
margin-bottom: 1.5rem;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
:root[data-theme="light"],
|
|
9
9
|
:root.theme-light {
|
|
10
10
|
--ld-bg-main: #ffffff;
|
|
11
|
-
--ld-bg-soft: #
|
|
11
|
+
--ld-bg-soft: #eeeeee;
|
|
12
12
|
--ld-bg-mute: #f3f4f6;
|
|
13
13
|
|
|
14
14
|
--ld-surface: #ffffff;
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
--ld-sidebar-bg: transparent;
|
|
47
47
|
--ld-sidebar-blur: 0px;
|
|
48
48
|
--ld-glow-1-bg: var(--ld-color-primary-glow);
|
|
49
|
-
--ld-glow-2-bg: rgba(
|
|
49
|
+
--ld-glow-2-bg: rgba(215, 59, 246, 0.15);
|
|
50
50
|
|
|
51
51
|
/* ─ UI Components (overridable independently from layout) ─ */
|
|
52
52
|
--ld-ui-btn-primary-bg: var(--ld-btn-primary-bg);
|
|
@@ -76,10 +76,10 @@
|
|
|
76
76
|
|
|
77
77
|
:root {
|
|
78
78
|
/* ─ Base palette ─ */
|
|
79
|
-
--ld-bg-main: #
|
|
80
|
-
--ld-bg-soft:
|
|
81
|
-
--ld-bg-mute: #
|
|
82
|
-
--ld-surface: #
|
|
79
|
+
--ld-bg-main: #000;
|
|
80
|
+
--ld-bg-soft: #12121272;
|
|
81
|
+
--ld-bg-mute: #090909;
|
|
82
|
+
--ld-surface: #1a1a1a;
|
|
83
83
|
--ld-border-subtle: rgba(255, 255, 255, 0.06);
|
|
84
84
|
--ld-border-strong: rgba(255, 255, 255, 0.12);
|
|
85
85
|
|
|
@@ -107,17 +107,17 @@
|
|
|
107
107
|
--ld-gradient-to: rgba(255, 255, 255, 0.7);
|
|
108
108
|
|
|
109
109
|
/* ─ Code ─ */
|
|
110
|
-
--ld-code-bg: #
|
|
110
|
+
--ld-code-bg: #050505;
|
|
111
111
|
--ld-code-header: #111119;
|
|
112
112
|
--ld-code-text: #d4d4d4;
|
|
113
113
|
|
|
114
114
|
/* ─ Customization ─ */
|
|
115
|
-
--ld-navbar-bg:
|
|
115
|
+
--ld-navbar-bg: #000;
|
|
116
116
|
--ld-navbar-blur: 12px;
|
|
117
117
|
--ld-sidebar-bg: transparent;
|
|
118
118
|
--ld-sidebar-blur: 0px;
|
|
119
119
|
--ld-glow-1-bg: var(--ld-color-primary-glow);
|
|
120
|
-
--ld-glow-2-bg: rgba(
|
|
120
|
+
--ld-glow-2-bg: rgba(246, 59, 187, 0.15);
|
|
121
121
|
|
|
122
122
|
/* ─ UI Components (dark) ─ */
|
|
123
123
|
--ld-ui-btn-primary-bg: var(--ld-btn-primary-bg);
|
|
@@ -152,6 +152,7 @@
|
|
|
152
152
|
|
|
153
153
|
/* ─ Dimensions ─ */
|
|
154
154
|
--ld-navbar-height: 3.5rem;
|
|
155
|
+
--ld-header-height: var(--ld-navbar-height); /* Default sync */
|
|
155
156
|
--ld-sidebar-width: 14.5rem;
|
|
156
157
|
--ld-toc-width: 13rem;
|
|
157
158
|
--ld-content-max-width: 820px;
|
|
@@ -63,7 +63,6 @@ export function ThemeLayout({
|
|
|
63
63
|
className = "",
|
|
64
64
|
style,
|
|
65
65
|
}: ThemeLayoutProps) {
|
|
66
|
-
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
|
67
66
|
const siteTitle = config.themeConfig?.title || "Boltdocs";
|
|
68
67
|
const siteDescription = config.themeConfig?.description || "";
|
|
69
68
|
const location = useLocation();
|
|
@@ -130,18 +129,11 @@ export function ThemeLayout({
|
|
|
130
129
|
currentVersion={currentVersion}
|
|
131
130
|
/>
|
|
132
131
|
)}
|
|
133
|
-
<div
|
|
134
|
-
className={`boltdocs-main-container ${!isSidebarOpen ? "sidebar-collapsed" : ""}`}
|
|
135
|
-
>
|
|
132
|
+
<div className="boltdocs-main-container">
|
|
136
133
|
{sidebar !== undefined ? (
|
|
137
134
|
sidebar
|
|
138
135
|
) : (
|
|
139
|
-
<Sidebar
|
|
140
|
-
routes={filteredRoutes}
|
|
141
|
-
config={config}
|
|
142
|
-
isCollapsed={!isSidebarOpen}
|
|
143
|
-
onToggle={() => setIsSidebarOpen(!isSidebarOpen)}
|
|
144
|
-
/>
|
|
136
|
+
<Sidebar routes={filteredRoutes} config={config} />
|
|
145
137
|
)}
|
|
146
138
|
|
|
147
139
|
<main className="boltdocs-content">
|
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
box-sizing: border-box;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
html,
|
|
8
9
|
body {
|
|
9
10
|
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
height: 100%;
|
|
13
|
+
overflow: hidden; /* Lock the window scroll */
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
body {
|
|
10
17
|
font-family: var(--ld-font-sans);
|
|
11
18
|
background-color: var(--ld-bg-main);
|
|
12
19
|
color: var(--ld-text-main);
|
|
@@ -27,7 +34,9 @@ a {
|
|
|
27
34
|
.boltdocs-layout {
|
|
28
35
|
display: flex;
|
|
29
36
|
flex-direction: column;
|
|
30
|
-
|
|
37
|
+
height: 100vh;
|
|
38
|
+
width: 100vw;
|
|
39
|
+
overflow: hidden;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
.boltdocs-main-container {
|
|
@@ -36,8 +45,11 @@ a {
|
|
|
36
45
|
width: 100%;
|
|
37
46
|
max-width: 1440px;
|
|
38
47
|
margin: 0 auto;
|
|
39
|
-
padding: 0
|
|
40
|
-
/*
|
|
48
|
+
padding: 0 0.5rem;
|
|
49
|
+
/* Fixed height layout */
|
|
50
|
+
height: calc(100vh - var(--ld-header-height));
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
align-items: stretch; /* Sidebars fill height naturally */
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
/* ─── Background Glow ────────────────────────────────────── */
|