boltdocs 1.4.1 → 1.6.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-O3V36MXA.css → SearchDialog-3QICRMWF.css} +145 -5
- package/dist/{SearchDialog-FBNGKRPK.mjs → SearchDialog-J3KNRGNO.mjs} +1 -1
- package/dist/{chunk-S5G55FBI.mjs → chunk-7SFUJWTB.mjs} +4 -4
- package/dist/{chunk-D7YBQG6H.mjs → chunk-HSPDIRTW.mjs} +312 -134
- package/dist/client/index.css +145 -5
- package/dist/client/index.d.mts +22 -5
- package/dist/client/index.d.ts +22 -5
- package/dist/client/index.js +725 -459
- package/dist/client/index.mjs +182 -61
- package/dist/client/ssr.css +145 -5
- package/dist/client/ssr.d.mts +1 -1
- package/dist/client/ssr.d.ts +1 -1
- package/dist/client/ssr.js +544 -395
- package/dist/client/ssr.mjs +1 -1
- package/dist/{config-BD5ZHz15.d.mts → config-DkZg5aCf.d.mts} +2 -0
- package/dist/{config-BD5ZHz15.d.ts → config-DkZg5aCf.d.ts} +2 -0
- package/dist/node/index.d.mts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +5 -1
- package/dist/node/index.mjs +5 -1
- package/dist/{types-CvrzTbEX.d.mts → types-DGIo1VKD.d.mts} +2 -0
- package/dist/{types-CvrzTbEX.d.ts → types-DGIo1VKD.d.ts} +2 -0
- package/package.json +1 -1
- package/src/client/app/index.tsx +2 -12
- package/src/client/app/preload.tsx +3 -1
- package/src/client/index.ts +2 -0
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +0 -11
- package/src/client/theme/components/mdx/FileTree.tsx +229 -0
- package/src/client/theme/components/mdx/Tabs.tsx +1 -4
- package/src/client/theme/components/mdx/index.ts +3 -0
- package/src/client/theme/components/mdx/mdx-components.css +109 -0
- package/src/client/theme/icons/pnpm.tsx +5 -5
- package/src/client/theme/styles/markdown.css +1 -5
- package/src/client/theme/ui/Link/Link.tsx +156 -18
- package/src/client/theme/ui/Link/LinkPreview.tsx +64 -0
- package/src/client/theme/ui/Link/link-preview.css +64 -0
- package/src/client/types.ts +2 -0
- package/src/node/config.ts +2 -0
- package/src/node/routes/parser.ts +14 -1
- package/dist/CodeBlock-QYIKJMEB.mjs +0 -7
- package/dist/chunk-KS5B3O6W.mjs +0 -43
- package/src/client/theme/icons/yarn.tsx +0 -16
package/dist/client/ssr.mjs
CHANGED
|
@@ -54,6 +54,8 @@ interface BoltdocsThemeConfig {
|
|
|
54
54
|
githubRepo?: string;
|
|
55
55
|
/** Whether to show the 'Powered by LiteDocs' badge in the sidebar (default: true) */
|
|
56
56
|
poweredBy?: boolean;
|
|
57
|
+
/** Whether to show a preview tooltip on internal links hover (default: true) */
|
|
58
|
+
linkPreview?: boolean;
|
|
57
59
|
/** Granular layout customization props */
|
|
58
60
|
layoutProps?: {
|
|
59
61
|
navbar?: any;
|
|
@@ -54,6 +54,8 @@ interface BoltdocsThemeConfig {
|
|
|
54
54
|
githubRepo?: string;
|
|
55
55
|
/** Whether to show the 'Powered by LiteDocs' badge in the sidebar (default: true) */
|
|
56
56
|
poweredBy?: boolean;
|
|
57
|
+
/** Whether to show a preview tooltip on internal links hover (default: true) */
|
|
58
|
+
linkPreview?: boolean;
|
|
57
59
|
/** Granular layout customization props */
|
|
58
60
|
layoutProps?: {
|
|
59
61
|
navbar?: any;
|
package/dist/node/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { B as BoltdocsConfig } from '../config-
|
|
3
|
-
export { a as BoltdocsThemeConfig } from '../config-
|
|
2
|
+
import { B as BoltdocsConfig } from '../config-DkZg5aCf.mjs';
|
|
3
|
+
export { a as BoltdocsThemeConfig } from '../config-DkZg5aCf.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Configuration options specifically for the Boltdocs Vite plugin.
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { B as BoltdocsConfig } from '../config-
|
|
3
|
-
export { a as BoltdocsThemeConfig } from '../config-
|
|
2
|
+
import { B as BoltdocsConfig } from '../config-DkZg5aCf.js';
|
|
3
|
+
export { a as BoltdocsThemeConfig } from '../config-DkZg5aCf.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Configuration options specifically for the Boltdocs Vite plugin.
|
package/dist/node/index.js
CHANGED
|
@@ -514,7 +514,11 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
514
514
|
headings.push({ level, text: escapeHtml(text), id });
|
|
515
515
|
}
|
|
516
516
|
const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
|
|
517
|
-
|
|
517
|
+
let sanitizedDescription = data.description ? escapeHtml(data.description) : "";
|
|
518
|
+
if (!sanitizedDescription && content) {
|
|
519
|
+
const summary = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim().slice(0, 160);
|
|
520
|
+
sanitizedDescription = escapeHtml(summary);
|
|
521
|
+
}
|
|
518
522
|
const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
|
|
519
523
|
return {
|
|
520
524
|
route: {
|
package/dist/node/index.mjs
CHANGED
|
@@ -96,7 +96,11 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
96
96
|
headings.push({ level, text: escapeHtml(text), id });
|
|
97
97
|
}
|
|
98
98
|
const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
|
|
99
|
-
|
|
99
|
+
let sanitizedDescription = data.description ? escapeHtml(data.description) : "";
|
|
100
|
+
if (!sanitizedDescription && content) {
|
|
101
|
+
const summary = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim().slice(0, 160);
|
|
102
|
+
sanitizedDescription = escapeHtml(summary);
|
|
103
|
+
}
|
|
100
104
|
const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
|
|
101
105
|
return {
|
|
102
106
|
route: {
|
|
@@ -27,6 +27,8 @@ interface ComponentRoute {
|
|
|
27
27
|
text: string;
|
|
28
28
|
id: string;
|
|
29
29
|
}[];
|
|
30
|
+
/** The page summary or description */
|
|
31
|
+
description?: string;
|
|
30
32
|
/** The locale this route belongs to, if i18n is configured */
|
|
31
33
|
locale?: string;
|
|
32
34
|
/** The version this route belongs to, if versioning is configured */
|
|
@@ -27,6 +27,8 @@ interface ComponentRoute {
|
|
|
27
27
|
text: string;
|
|
28
28
|
id: string;
|
|
29
29
|
}[];
|
|
30
|
+
/** The page summary or description */
|
|
31
|
+
description?: string;
|
|
30
32
|
/** The locale this route belongs to, if i18n is configured */
|
|
31
33
|
locale?: string;
|
|
32
34
|
/** The version this route belongs to, if versioning is configured */
|
package/package.json
CHANGED
package/src/client/app/index.tsx
CHANGED
|
@@ -27,11 +27,7 @@ export function useConfig() {
|
|
|
27
27
|
return useContext(ConfigContext);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
import("../theme/components/CodeBlock").then((m) => ({
|
|
32
|
-
default: m.CodeBlock,
|
|
33
|
-
})),
|
|
34
|
-
);
|
|
30
|
+
import { CodeBlock } from "../theme/components/CodeBlock";
|
|
35
31
|
const Video = lazy(() =>
|
|
36
32
|
import("../theme/components/Video").then((m) => ({ default: m.Video })),
|
|
37
33
|
);
|
|
@@ -77,13 +73,7 @@ const mdxComponents = {
|
|
|
77
73
|
h4: (props: any) => <Heading level={4} {...props} />,
|
|
78
74
|
h5: (props: any) => <Heading level={5} {...props} />,
|
|
79
75
|
h6: (props: any) => <Heading level={6} {...props} />,
|
|
80
|
-
pre: (props: any) => {
|
|
81
|
-
return (
|
|
82
|
-
<Suspense fallback={<div className="code-block-skeleton" />}>
|
|
83
|
-
<CodeBlock {...props}>{props.children}</CodeBlock>
|
|
84
|
-
</Suspense>
|
|
85
|
-
);
|
|
86
|
-
},
|
|
76
|
+
pre: (props: any) => <CodeBlock {...props}>{props.children}</CodeBlock>,
|
|
87
77
|
video: (props: any) => (
|
|
88
78
|
<Suspense fallback={<div className="video-skeleton" />}>
|
|
89
79
|
<Video {...props} />
|
|
@@ -3,10 +3,12 @@ import { ComponentRoute } from "../types";
|
|
|
3
3
|
|
|
4
4
|
interface PreloadContextType {
|
|
5
5
|
preload: (path: string) => void;
|
|
6
|
+
routes: ComponentRoute[];
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
const PreloadContext = createContext<PreloadContextType>({
|
|
9
10
|
preload: () => {},
|
|
11
|
+
routes: [],
|
|
10
12
|
});
|
|
11
13
|
|
|
12
14
|
export function usePreload() {
|
|
@@ -49,7 +51,7 @@ export function PreloadProvider({
|
|
|
49
51
|
);
|
|
50
52
|
|
|
51
53
|
return (
|
|
52
|
-
<PreloadContext.Provider value={{ preload }}>
|
|
54
|
+
<PreloadContext.Provider value={{ preload, routes }}>
|
|
53
55
|
{children}
|
|
54
56
|
</PreloadContext.Provider>
|
|
55
57
|
);
|
package/src/client/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export {
|
|
|
28
28
|
Danger,
|
|
29
29
|
InfoBox,
|
|
30
30
|
List,
|
|
31
|
+
FileTree,
|
|
31
32
|
} from "./theme/components/mdx";
|
|
32
33
|
export type {
|
|
33
34
|
ButtonProps,
|
|
@@ -38,4 +39,5 @@ export type {
|
|
|
38
39
|
TabProps,
|
|
39
40
|
AdmonitionProps,
|
|
40
41
|
ListProps,
|
|
42
|
+
FileTreeProps,
|
|
41
43
|
} from "./theme/components/mdx";
|
|
@@ -16,17 +16,6 @@ export function CodeBlock({ children, ...props }: CodeBlockProps) {
|
|
|
16
16
|
const [copied, setCopied] = useState(false);
|
|
17
17
|
const preRef = useRef<HTMLPreElement>(null);
|
|
18
18
|
|
|
19
|
-
// Extract language from the child <code> element's data-language or className
|
|
20
|
-
let language = "";
|
|
21
|
-
if (React.isValidElement(children)) {
|
|
22
|
-
const childProps = children.props as any;
|
|
23
|
-
language = childProps?.["data-language"] || "";
|
|
24
|
-
if (!language && childProps?.className) {
|
|
25
|
-
const match = childProps.className.match(/language-(\w+)/);
|
|
26
|
-
if (match) language = match[1];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
const handleCopy = useCallback(async () => {
|
|
31
20
|
const code = preRef.current?.textContent || "";
|
|
32
21
|
copyToClipboard(code);
|
|
@@ -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
|
+
}
|
|
@@ -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
|
*/
|
|
@@ -430,3 +430,112 @@
|
|
|
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
|
+
}
|
|
@@ -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>
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
right: 0.75rem;
|
|
219
219
|
z-index: 50;
|
|
220
220
|
padding: 0.4rem;
|
|
221
|
-
background-color:
|
|
221
|
+
background-color: var(--ld-surface);
|
|
222
222
|
backdrop-filter: blur(8px);
|
|
223
223
|
-webkit-backdrop-filter: blur(8px);
|
|
224
224
|
border: 1px solid var(--ld-border-subtle);
|
|
@@ -227,14 +227,10 @@
|
|
|
227
227
|
cursor: pointer;
|
|
228
228
|
transition: all 0.2s ease;
|
|
229
229
|
opacity: 0;
|
|
230
|
-
visibility: hidden;
|
|
231
|
-
pointer-events: none;
|
|
232
230
|
}
|
|
233
231
|
|
|
234
232
|
.code-block-wrapper:hover .code-block-copy {
|
|
235
233
|
opacity: 1;
|
|
236
|
-
visibility: visible;
|
|
237
|
-
pointer-events: auto;
|
|
238
234
|
}
|
|
239
235
|
|
|
240
236
|
.code-block-copy:hover {
|