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
|
@@ -97,25 +97,44 @@ function useLocalizedTo(to: RouterLinkProps["to"]) {
|
|
|
97
97
|
return finalPath === basePath ? basePath : finalPath;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
import { LinkPreview } from "./LinkPreview";
|
|
101
|
+
|
|
100
102
|
export interface LinkProps extends Omit<RouterLinkProps, "prefetch"> {
|
|
101
103
|
/** Should prefetch the page on hover? Options: 'hover' | 'none'. Default 'hover' */
|
|
102
104
|
boltdocsPrefetch?: "hover" | "none";
|
|
105
|
+
/** Should show a preview tooltip on hover? Default true */
|
|
106
|
+
boltdocsPreview?: boolean;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
106
110
|
(props, ref) => {
|
|
107
111
|
const {
|
|
108
112
|
boltdocsPrefetch = "hover",
|
|
113
|
+
boltdocsPreview = true,
|
|
109
114
|
onMouseEnter,
|
|
115
|
+
onMouseLeave,
|
|
110
116
|
onFocus,
|
|
117
|
+
onBlur,
|
|
111
118
|
onClick,
|
|
112
119
|
to,
|
|
113
120
|
...rest
|
|
114
121
|
} = props;
|
|
115
122
|
const localizedTo = useLocalizedTo(to);
|
|
116
|
-
const { preload } = usePreload();
|
|
123
|
+
const { preload, routes } = usePreload();
|
|
124
|
+
const config = useConfig();
|
|
117
125
|
const navigate = useNavigate();
|
|
118
126
|
|
|
127
|
+
const shouldShowPreview =
|
|
128
|
+
boltdocsPreview && config?.themeConfig?.linkPreview !== false;
|
|
129
|
+
|
|
130
|
+
const [preview, setPreview] = React.useState<{
|
|
131
|
+
visible: boolean;
|
|
132
|
+
x: number;
|
|
133
|
+
y: number;
|
|
134
|
+
title: string;
|
|
135
|
+
summary?: string;
|
|
136
|
+
}>({ visible: false, x: 0, y: 0, title: "" });
|
|
137
|
+
|
|
119
138
|
const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
120
139
|
onMouseEnter?.(e);
|
|
121
140
|
if (
|
|
@@ -125,6 +144,37 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
|
125
144
|
) {
|
|
126
145
|
preload(localizedTo);
|
|
127
146
|
}
|
|
147
|
+
|
|
148
|
+
if (
|
|
149
|
+
shouldShowPreview &&
|
|
150
|
+
typeof localizedTo === "string" &&
|
|
151
|
+
localizedTo.startsWith("/")
|
|
152
|
+
) {
|
|
153
|
+
const cleanPath = localizedTo.split("#")[0].split("?")[0];
|
|
154
|
+
const route = routes.find(
|
|
155
|
+
(r) => r.path === cleanPath || (cleanPath === "/" && r.path === ""),
|
|
156
|
+
);
|
|
157
|
+
if (route) {
|
|
158
|
+
setPreview({
|
|
159
|
+
visible: true,
|
|
160
|
+
x: e.clientX,
|
|
161
|
+
y: e.clientY,
|
|
162
|
+
title: route.title,
|
|
163
|
+
summary: route.description,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const handleMouseMove = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
170
|
+
if (preview.visible) {
|
|
171
|
+
setPreview((prev) => ({ ...prev, x: e.clientX, y: e.clientY }));
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const handleMouseLeave = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
176
|
+
onMouseLeave?.(e);
|
|
177
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
128
178
|
};
|
|
129
179
|
|
|
130
180
|
const handleFocus = (e: React.FocusEvent<HTMLAnchorElement>) => {
|
|
@@ -138,9 +188,15 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
|
138
188
|
}
|
|
139
189
|
};
|
|
140
190
|
|
|
191
|
+
const handleBlur = (e: React.FocusEvent<HTMLAnchorElement>) => {
|
|
192
|
+
onBlur?.(e);
|
|
193
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
194
|
+
};
|
|
195
|
+
|
|
141
196
|
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
142
197
|
// Allow user onClick to handle defaults or custom logic
|
|
143
198
|
onClick?.(e);
|
|
199
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
144
200
|
|
|
145
201
|
// If default prevented or not a simple left click, don't handle
|
|
146
202
|
if (
|
|
@@ -164,14 +220,28 @@ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
|
|
|
164
220
|
};
|
|
165
221
|
|
|
166
222
|
return (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
223
|
+
<>
|
|
224
|
+
<RouterLink
|
|
225
|
+
ref={ref}
|
|
226
|
+
to={localizedTo}
|
|
227
|
+
onMouseEnter={handleMouseEnter}
|
|
228
|
+
onMouseMove={handleMouseMove}
|
|
229
|
+
onMouseLeave={handleMouseLeave}
|
|
230
|
+
onFocus={handleFocus}
|
|
231
|
+
onBlur={handleBlur}
|
|
232
|
+
onClick={handleClick}
|
|
233
|
+
{...rest}
|
|
234
|
+
/>
|
|
235
|
+
{shouldShowPreview && (
|
|
236
|
+
<LinkPreview
|
|
237
|
+
isVisible={preview.visible}
|
|
238
|
+
title={preview.title}
|
|
239
|
+
summary={preview.summary}
|
|
240
|
+
x={preview.x}
|
|
241
|
+
y={preview.y}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
</>
|
|
175
245
|
);
|
|
176
246
|
},
|
|
177
247
|
);
|
|
@@ -180,23 +250,40 @@ Link.displayName = "Link";
|
|
|
180
250
|
export interface NavLinkProps extends Omit<RouterNavLinkProps, "prefetch"> {
|
|
181
251
|
/** Should prefetch the page on hover? Options: 'hover' | 'none'. Default 'hover' */
|
|
182
252
|
boltdocsPrefetch?: "hover" | "none";
|
|
253
|
+
/** Should show a preview tooltip on hover? Default true */
|
|
254
|
+
boltdocsPreview?: boolean;
|
|
183
255
|
}
|
|
184
256
|
|
|
185
257
|
export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
186
258
|
(props, ref) => {
|
|
187
259
|
const {
|
|
188
260
|
boltdocsPrefetch = "hover",
|
|
261
|
+
boltdocsPreview = true,
|
|
189
262
|
onMouseEnter,
|
|
263
|
+
onMouseLeave,
|
|
190
264
|
onFocus,
|
|
265
|
+
onBlur,
|
|
191
266
|
onClick,
|
|
192
267
|
to,
|
|
193
268
|
...rest
|
|
194
269
|
} = props;
|
|
195
270
|
|
|
196
271
|
const localizedTo = useLocalizedTo(to);
|
|
197
|
-
const { preload } = usePreload();
|
|
272
|
+
const { preload, routes } = usePreload();
|
|
273
|
+
const config = useConfig();
|
|
198
274
|
const navigate = useNavigate();
|
|
199
275
|
|
|
276
|
+
const shouldShowPreview =
|
|
277
|
+
boltdocsPreview && config?.themeConfig?.linkPreview !== false;
|
|
278
|
+
|
|
279
|
+
const [preview, setPreview] = React.useState<{
|
|
280
|
+
visible: boolean;
|
|
281
|
+
x: number;
|
|
282
|
+
y: number;
|
|
283
|
+
title: string;
|
|
284
|
+
summary?: string;
|
|
285
|
+
}>({ visible: false, x: 0, y: 0, title: "" });
|
|
286
|
+
|
|
200
287
|
const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
201
288
|
onMouseEnter?.(e);
|
|
202
289
|
if (
|
|
@@ -206,6 +293,37 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
|
206
293
|
) {
|
|
207
294
|
preload(localizedTo);
|
|
208
295
|
}
|
|
296
|
+
|
|
297
|
+
if (
|
|
298
|
+
shouldShowPreview &&
|
|
299
|
+
typeof localizedTo === "string" &&
|
|
300
|
+
localizedTo.startsWith("/")
|
|
301
|
+
) {
|
|
302
|
+
const cleanPath = localizedTo.split("#")[0].split("?")[0];
|
|
303
|
+
const route = routes.find(
|
|
304
|
+
(r) => r.path === cleanPath || (cleanPath === "/" && r.path === ""),
|
|
305
|
+
);
|
|
306
|
+
if (route) {
|
|
307
|
+
setPreview({
|
|
308
|
+
visible: true,
|
|
309
|
+
x: e.clientX,
|
|
310
|
+
y: e.clientY,
|
|
311
|
+
title: route.title,
|
|
312
|
+
summary: route.description,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const handleMouseMove = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
319
|
+
if (preview.visible) {
|
|
320
|
+
setPreview((prev) => ({ ...prev, x: e.clientX, y: e.clientY }));
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const handleMouseLeave = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
325
|
+
onMouseLeave?.(e);
|
|
326
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
209
327
|
};
|
|
210
328
|
|
|
211
329
|
const handleFocus = (e: React.FocusEvent<HTMLAnchorElement>) => {
|
|
@@ -219,8 +337,14 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
|
219
337
|
}
|
|
220
338
|
};
|
|
221
339
|
|
|
340
|
+
const handleBlur = (e: React.FocusEvent<HTMLAnchorElement>) => {
|
|
341
|
+
onBlur?.(e);
|
|
342
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
343
|
+
};
|
|
344
|
+
|
|
222
345
|
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
223
346
|
onClick?.(e);
|
|
347
|
+
setPreview((prev) => ({ ...prev, visible: false }));
|
|
224
348
|
if (
|
|
225
349
|
e.defaultPrevented ||
|
|
226
350
|
e.button !== 0 ||
|
|
@@ -240,14 +364,28 @@ export const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
|
|
|
240
364
|
};
|
|
241
365
|
|
|
242
366
|
return (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
367
|
+
<>
|
|
368
|
+
<RouterNavLink
|
|
369
|
+
ref={ref}
|
|
370
|
+
to={localizedTo}
|
|
371
|
+
onMouseEnter={handleMouseEnter}
|
|
372
|
+
onMouseMove={handleMouseMove}
|
|
373
|
+
onMouseLeave={handleMouseLeave}
|
|
374
|
+
onFocus={handleFocus}
|
|
375
|
+
onBlur={handleBlur}
|
|
376
|
+
onClick={handleClick}
|
|
377
|
+
{...rest}
|
|
378
|
+
/>
|
|
379
|
+
{shouldShowPreview && (
|
|
380
|
+
<LinkPreview
|
|
381
|
+
isVisible={preview.visible}
|
|
382
|
+
title={preview.title}
|
|
383
|
+
summary={preview.summary}
|
|
384
|
+
x={preview.x}
|
|
385
|
+
y={preview.y}
|
|
386
|
+
/>
|
|
387
|
+
)}
|
|
388
|
+
</>
|
|
251
389
|
);
|
|
252
390
|
},
|
|
253
391
|
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useEffect, useState, useRef } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import "./link-preview.css";
|
|
4
|
+
|
|
5
|
+
interface LinkPreviewProps {
|
|
6
|
+
isVisible: boolean;
|
|
7
|
+
title: string;
|
|
8
|
+
summary?: string;
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function LinkPreview({
|
|
14
|
+
isVisible,
|
|
15
|
+
title,
|
|
16
|
+
summary,
|
|
17
|
+
x,
|
|
18
|
+
y,
|
|
19
|
+
}: LinkPreviewProps) {
|
|
20
|
+
const [mounted, setMounted] = useState(false);
|
|
21
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
22
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setMounted(true);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (isVisible && ref.current) {
|
|
30
|
+
const rect = ref.current.getBoundingClientRect();
|
|
31
|
+
const padding = 15;
|
|
32
|
+
|
|
33
|
+
let top = y + padding;
|
|
34
|
+
let left = x + padding;
|
|
35
|
+
|
|
36
|
+
// Keep within viewport
|
|
37
|
+
if (left + rect.width > window.innerWidth) {
|
|
38
|
+
left = x - rect.width - padding;
|
|
39
|
+
}
|
|
40
|
+
if (top + rect.height > window.innerHeight) {
|
|
41
|
+
top = y - rect.height - padding;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setPosition({ top, left });
|
|
45
|
+
}
|
|
46
|
+
}, [isVisible, x, y]);
|
|
47
|
+
|
|
48
|
+
if (!mounted) return null;
|
|
49
|
+
|
|
50
|
+
return createPortal(
|
|
51
|
+
<div
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={`boltdocs-link-preview ${isVisible ? "is-visible" : ""}`}
|
|
54
|
+
style={{
|
|
55
|
+
top: position.top,
|
|
56
|
+
left: position.left,
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<span className="boltdocs-link-preview-title">{title}</span>
|
|
60
|
+
{summary && <p className="boltdocs-link-preview-summary">{summary}</p>}
|
|
61
|
+
</div>,
|
|
62
|
+
document.body,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.boltdocs-link-preview {
|
|
2
|
+
position: fixed;
|
|
3
|
+
z-index: 1000;
|
|
4
|
+
width: 320px;
|
|
5
|
+
padding: 1rem;
|
|
6
|
+
background-color: var(--ld-navbar-bg);
|
|
7
|
+
backdrop-filter: blur(var(--ld-navbar-blur));
|
|
8
|
+
-webkit-backdrop-filter: blur(var(--ld-navbar-blur));
|
|
9
|
+
border: 1px solid var(--ld-border-subtle);
|
|
10
|
+
border-radius: var(--ld-radius-md);
|
|
11
|
+
box-shadow:
|
|
12
|
+
0 10px 25px -5px rgba(0, 0, 0, 0.1),
|
|
13
|
+
0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
opacity: 0;
|
|
16
|
+
transform: translateY(10px) scale(0.95);
|
|
17
|
+
transition:
|
|
18
|
+
opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
|
19
|
+
transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
20
|
+
font-family: var(--ld-font-sans);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.boltdocs-link-preview.is-visible {
|
|
24
|
+
opacity: 1;
|
|
25
|
+
transform: translateY(0) scale(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.boltdocs-link-preview-title {
|
|
29
|
+
display: block;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
font-size: 0.95rem;
|
|
32
|
+
color: var(--ld-text-main);
|
|
33
|
+
margin-bottom: 0.5rem;
|
|
34
|
+
line-height: 1.4;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.boltdocs-link-preview-summary {
|
|
38
|
+
display: block;
|
|
39
|
+
font-size: 0.85rem;
|
|
40
|
+
color: var(--ld-text-muted);
|
|
41
|
+
line-height: 1.5;
|
|
42
|
+
display: -webkit-box;
|
|
43
|
+
-webkit-line-clamp: 4;
|
|
44
|
+
line-clamp: 4;
|
|
45
|
+
-webkit-box-orient: vertical;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Dark mode adjustments */
|
|
50
|
+
[data-theme="dark"] .boltdocs-link-preview {
|
|
51
|
+
background-color: var(--ld-navbar-bg);
|
|
52
|
+
border-color: var(--ld-border-subtle);
|
|
53
|
+
box-shadow:
|
|
54
|
+
0 20px 25px -5px rgba(0, 0, 0, 0.3),
|
|
55
|
+
0 10px 10px -5px rgba(0, 0, 0, 0.2);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-theme="dark"] .boltdocs-link-preview-title {
|
|
59
|
+
color: #f8fafc;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
[data-theme="dark"] .boltdocs-link-preview-summary {
|
|
63
|
+
color: #94a3b8;
|
|
64
|
+
}
|
package/src/client/types.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface ComponentRoute {
|
|
|
23
23
|
groupPosition?: number;
|
|
24
24
|
/** Extracted markdown headings for search indexing */
|
|
25
25
|
headings?: { level: number; text: string; id: string }[];
|
|
26
|
+
/** The page summary or description */
|
|
27
|
+
description?: string;
|
|
26
28
|
/** The locale this route belongs to, if i18n is configured */
|
|
27
29
|
locale?: string;
|
|
28
30
|
/** The version this route belongs to, if versioning is configured */
|
package/src/node/config.ts
CHANGED
|
@@ -53,6 +53,8 @@ export interface BoltdocsThemeConfig {
|
|
|
53
53
|
githubRepo?: string;
|
|
54
54
|
/** Whether to show the 'Powered by LiteDocs' badge in the sidebar (default: true) */
|
|
55
55
|
poweredBy?: boolean;
|
|
56
|
+
/** Whether to show a preview tooltip on internal links hover (default: true) */
|
|
57
|
+
linkPreview?: boolean;
|
|
56
58
|
/** Granular layout customization props */
|
|
57
59
|
layoutProps?: {
|
|
58
60
|
navbar?: any;
|
|
@@ -122,9 +122,22 @@ export function parseDocFile(
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
|
|
125
|
-
|
|
125
|
+
let sanitizedDescription = data.description
|
|
126
126
|
? escapeHtml(data.description)
|
|
127
127
|
: "";
|
|
128
|
+
|
|
129
|
+
// If no description is provided, extract a summary from the content
|
|
130
|
+
if (!sanitizedDescription && content) {
|
|
131
|
+
const summary = content
|
|
132
|
+
.replace(/^#+.*$/gm, "") // Remove headers
|
|
133
|
+
.replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1") // Simplify links
|
|
134
|
+
.replace(/[_*`]/g, "") // Remove formatting
|
|
135
|
+
.replace(/\n+/g, " ") // Normalize whitespace
|
|
136
|
+
.trim()
|
|
137
|
+
.slice(0, 160);
|
|
138
|
+
sanitizedDescription = escapeHtml(summary);
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
const sanitizedBadge = data.badge ? escapeHtml(data.badge) : undefined;
|
|
129
142
|
|
|
130
143
|
return {
|
package/dist/chunk-KS5B3O6W.mjs
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
copyToClipboard
|
|
3
|
-
} from "./chunk-FMTOYQLO.mjs";
|
|
4
|
-
|
|
5
|
-
// src/client/theme/components/CodeBlock/CodeBlock.tsx
|
|
6
|
-
import React, { useState, useRef, useCallback } from "react";
|
|
7
|
-
import { Copy, Check } from "lucide-react";
|
|
8
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
function CodeBlock({ children, ...props }) {
|
|
10
|
-
const [copied, setCopied] = useState(false);
|
|
11
|
-
const preRef = useRef(null);
|
|
12
|
-
let language = "";
|
|
13
|
-
if (React.isValidElement(children)) {
|
|
14
|
-
const childProps = children.props;
|
|
15
|
-
language = childProps?.["data-language"] || "";
|
|
16
|
-
if (!language && childProps?.className) {
|
|
17
|
-
const match = childProps.className.match(/language-(\w+)/);
|
|
18
|
-
if (match) language = match[1];
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
const handleCopy = useCallback(async () => {
|
|
22
|
-
const code = preRef.current?.textContent || "";
|
|
23
|
-
copyToClipboard(code);
|
|
24
|
-
setCopied(true);
|
|
25
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
26
|
-
}, []);
|
|
27
|
-
return /* @__PURE__ */ jsxs("div", { className: "code-block-wrapper", children: [
|
|
28
|
-
/* @__PURE__ */ jsx(
|
|
29
|
-
"button",
|
|
30
|
-
{
|
|
31
|
-
className: `code-block-copy ${copied ? "copied" : ""}`,
|
|
32
|
-
onClick: handleCopy,
|
|
33
|
-
"aria-label": "Copy code",
|
|
34
|
-
children: copied ? /* @__PURE__ */ jsx(Check, { size: 16 }) : /* @__PURE__ */ jsx(Copy, { size: 16 })
|
|
35
|
-
}
|
|
36
|
-
),
|
|
37
|
-
/* @__PURE__ */ jsx("pre", { ref: preRef, ...props, children })
|
|
38
|
-
] });
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export {
|
|
42
|
-
CodeBlock
|
|
43
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { SVGProps } from "react";
|
|
2
|
-
|
|
3
|
-
const Yarn = (props: SVGProps<SVGSVGElement>) => (
|
|
4
|
-
<svg {...props} viewBox="0 0 256 256">
|
|
5
|
-
<path
|
|
6
|
-
fill="#2C8EBB"
|
|
7
|
-
d="M128 0C57.307 0 0 57.307 0 128s57.307 128 128 128 128-57.307 128-128S198.693 0 128 0zm0 234.667C69.195 234.667 21.333 186.805 21.333 128S69.195 21.333 128 21.333 234.667 69.195 234.667 128 186.805 234.667 128 234.667z"
|
|
8
|
-
/>
|
|
9
|
-
<path
|
|
10
|
-
fill="#2C8EBB"
|
|
11
|
-
d="M173.045 74.053c-4.632-4.632-12.144-4.632-16.776 0L128 102.323l-28.269-28.27c-4.632-4.632-12.144-4.632-16.776 0-4.632 4.632-4.632 12.144 0 16.776L111.224 119.1l-28.269 28.269c-4.632 4.632-4.632 12.144 0 16.776 2.316 2.316 5.352 3.474 8.388 3.474s6.072-1.158 8.388-3.474L128 135.877l28.269 28.268c2.316 2.316 5.352 3.474 8.388 3.474s6.072-1.158 8.388-3.474c4.632-4.632 4.632-12.144 0-16.776L144.776 119.1l28.269-28.271 c4.632-4.632 4.632-12.144 0-16.776z"
|
|
12
|
-
/>
|
|
13
|
-
</svg>
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
export { Yarn };
|