boltdocs 1.7.0 → 1.8.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/{SearchDialog-UOAW6IR3.css → SearchDialog-4ANHNJTL.css} +213 -28
- package/dist/{SearchDialog-YOXMFGH6.mjs → SearchDialog-6Z7CUAYJ.mjs} +8 -1
- package/dist/{chunk-MULKZFVN.mjs → chunk-SFVOGJ2W.mjs} +269 -165
- package/dist/client/index.css +211 -26
- package/dist/client/index.d.mts +25 -6
- package/dist/client/index.d.ts +25 -6
- package/dist/client/index.js +614 -336
- package/dist/client/index.mjs +134 -5
- package/dist/client/ssr.css +211 -26
- package/dist/client/ssr.d.mts +1 -1
- package/dist/client/ssr.d.ts +1 -1
- package/dist/client/ssr.js +378 -230
- package/dist/client/ssr.mjs +1 -1
- package/dist/node/index.d.mts +2 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +4 -1
- package/dist/node/index.mjs +4 -1
- package/dist/{types-CviV0GbX.d.ts → types-BbceAHA0.d.mts} +2 -0
- package/dist/{types-CviV0GbX.d.mts → types-BbceAHA0.d.ts} +2 -0
- package/package.json +1 -1
- package/src/client/app/index.tsx +8 -7
- package/src/client/index.ts +2 -0
- package/src/client/theme/components/mdx/Field.tsx +60 -0
- package/src/client/theme/components/mdx/Table.tsx +108 -10
- package/src/client/theme/components/mdx/index.ts +3 -0
- package/src/client/theme/components/mdx/mdx-components.css +174 -0
- package/src/client/theme/styles/variables.css +25 -1
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +46 -0
- package/src/client/theme/ui/ErrorBoundary/index.ts +1 -0
- package/src/client/theme/ui/Layout/Layout.tsx +8 -1
- package/src/client/theme/ui/Link/link-preview.css +1 -20
- package/src/client/theme/ui/Navbar/Tabs.tsx +37 -12
- package/src/client/theme/ui/Navbar/navbar.css +26 -18
- package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +1 -8
- package/src/client/theme/ui/ProgressBar/ProgressBar.css +17 -0
- package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +51 -0
- package/src/client/theme/ui/ProgressBar/index.ts +1 -0
- package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +11 -1
- package/src/client/types.ts +2 -0
- package/src/node/routes/index.ts +1 -0
- package/src/node/routes/parser.ts +11 -0
- package/src/node/routes/types.ts +2 -0
package/dist/client/ssr.mjs
CHANGED
package/dist/node/index.d.mts
CHANGED
|
@@ -80,6 +80,8 @@ interface RouteMeta {
|
|
|
80
80
|
icon?: string;
|
|
81
81
|
/** The tab this route belongs to, if tabs are configured */
|
|
82
82
|
tab?: string;
|
|
83
|
+
/** The extracted plain-text content of the page for search indexing */
|
|
84
|
+
_content?: string;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
declare function boltdocs(options?: BoltdocsPluginOptions): Promise<Plugin[]>;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -80,6 +80,8 @@ interface RouteMeta {
|
|
|
80
80
|
icon?: string;
|
|
81
81
|
/** The tab this route belongs to, if tabs are configured */
|
|
82
82
|
tab?: string;
|
|
83
|
+
/** The extracted plain-text content of the page for search indexing */
|
|
84
|
+
_content?: string;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
declare function boltdocs(options?: BoltdocsPluginOptions): Promise<Plugin[]>;
|
package/dist/node/index.js
CHANGED
|
@@ -529,6 +529,7 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
529
529
|
}
|
|
530
530
|
const sanitizedBadge = data.badge ? data.badge : void 0;
|
|
531
531
|
const icon = data.icon ? String(data.icon) : void 0;
|
|
532
|
+
const plainText = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/<[^>]+>/g, "").replace(/\{[^\}]+\}/g, "").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim();
|
|
532
533
|
return {
|
|
533
534
|
route: {
|
|
534
535
|
path: finalPath,
|
|
@@ -542,7 +543,8 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
542
543
|
version,
|
|
543
544
|
badge: sanitizedBadge,
|
|
544
545
|
icon,
|
|
545
|
-
tab: inferredTab
|
|
546
|
+
tab: inferredTab,
|
|
547
|
+
_content: plainText
|
|
546
548
|
},
|
|
547
549
|
relativeDir: cleanDirName,
|
|
548
550
|
isGroupIndex,
|
|
@@ -586,6 +588,7 @@ function compareByGroupPosition(a, b) {
|
|
|
586
588
|
// src/node/routes/index.ts
|
|
587
589
|
async function generateRoutes(docsDir, config, basePath = "/docs") {
|
|
588
590
|
docCache.load();
|
|
591
|
+
docCache.invalidateAll();
|
|
589
592
|
const files = await (0, import_fast_glob.default)(["**/*.md", "**/*.mdx"], {
|
|
590
593
|
cwd: docsDir,
|
|
591
594
|
absolute: true
|
package/dist/node/index.mjs
CHANGED
|
@@ -111,6 +111,7 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
111
111
|
}
|
|
112
112
|
const sanitizedBadge = data.badge ? data.badge : void 0;
|
|
113
113
|
const icon = data.icon ? String(data.icon) : void 0;
|
|
114
|
+
const plainText = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/<[^>]+>/g, "").replace(/\{[^\}]+\}/g, "").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim();
|
|
114
115
|
return {
|
|
115
116
|
route: {
|
|
116
117
|
path: finalPath,
|
|
@@ -124,7 +125,8 @@ function parseDocFile(file, docsDir, basePath, config) {
|
|
|
124
125
|
version,
|
|
125
126
|
badge: sanitizedBadge,
|
|
126
127
|
icon,
|
|
127
|
-
tab: inferredTab
|
|
128
|
+
tab: inferredTab,
|
|
129
|
+
_content: plainText
|
|
128
130
|
},
|
|
129
131
|
relativeDir: cleanDirName,
|
|
130
132
|
isGroupIndex,
|
|
@@ -168,6 +170,7 @@ function compareByGroupPosition(a, b) {
|
|
|
168
170
|
// src/node/routes/index.ts
|
|
169
171
|
async function generateRoutes(docsDir, config, basePath = "/docs") {
|
|
170
172
|
docCache.load();
|
|
173
|
+
docCache.invalidateAll();
|
|
171
174
|
const files = await fastGlob(["**/*.md", "**/*.mdx"], {
|
|
172
175
|
cwd: docsDir,
|
|
173
176
|
absolute: true
|
|
@@ -44,6 +44,8 @@ interface ComponentRoute {
|
|
|
44
44
|
};
|
|
45
45
|
/** Optional icon for the route's group */
|
|
46
46
|
groupIcon?: string;
|
|
47
|
+
/** The extracted plain-text content of the page for search indexing */
|
|
48
|
+
_content?: string;
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
51
|
* Configuration options for initializing the Boltdocs client app.
|
|
@@ -44,6 +44,8 @@ interface ComponentRoute {
|
|
|
44
44
|
};
|
|
45
45
|
/** Optional icon for the route's group */
|
|
46
46
|
groupIcon?: string;
|
|
47
|
+
/** The extracted plain-text content of the page for search indexing */
|
|
48
|
+
_content?: string;
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
51
|
* Configuration options for initializing the Boltdocs client app.
|
package/package.json
CHANGED
package/src/client/app/index.tsx
CHANGED
|
@@ -216,26 +216,27 @@ function ScrollHandler() {
|
|
|
216
216
|
const { pathname, hash } = useLocation();
|
|
217
217
|
|
|
218
218
|
useLayoutEffect(() => {
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
const container = document.querySelector(".boltdocs-content");
|
|
220
|
+
if (!container) return;
|
|
221
|
+
|
|
221
222
|
if (hash) {
|
|
222
223
|
const id = hash.replace("#", "");
|
|
223
224
|
const element = document.getElementById(id);
|
|
224
225
|
if (element) {
|
|
225
226
|
const offset = 80;
|
|
226
|
-
const
|
|
227
|
+
const containerRect = container.getBoundingClientRect().top;
|
|
227
228
|
const elementRect = element.getBoundingClientRect().top;
|
|
228
|
-
const elementPosition = elementRect -
|
|
229
|
-
const offsetPosition = elementPosition - offset;
|
|
229
|
+
const elementPosition = elementRect - containerRect;
|
|
230
|
+
const offsetPosition = elementPosition - offset + container.scrollTop;
|
|
230
231
|
|
|
231
|
-
|
|
232
|
+
container.scrollTo({
|
|
232
233
|
top: offsetPosition,
|
|
233
234
|
behavior: "smooth",
|
|
234
235
|
});
|
|
235
236
|
return;
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
|
-
|
|
239
|
+
container.scrollTo(0, 0);
|
|
239
240
|
}, [pathname, hash]);
|
|
240
241
|
|
|
241
242
|
return null;
|
package/src/client/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export {
|
|
|
30
30
|
List,
|
|
31
31
|
FileTree,
|
|
32
32
|
Table,
|
|
33
|
+
Field,
|
|
33
34
|
} from "./theme/components/mdx";
|
|
34
35
|
export type {
|
|
35
36
|
ButtonProps,
|
|
@@ -42,4 +43,5 @@ export type {
|
|
|
42
43
|
ListProps,
|
|
43
44
|
FileTreeProps,
|
|
44
45
|
TableProps,
|
|
46
|
+
FieldProps,
|
|
45
47
|
} from "./theme/components/mdx";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export interface FieldProps {
|
|
4
|
+
/** The name of the property or field */
|
|
5
|
+
name: string;
|
|
6
|
+
/** The data type of the field (e.g., "string", "number", "boolean") */
|
|
7
|
+
type?: string;
|
|
8
|
+
/** The default value if not provided */
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
/** Whether the field is required */
|
|
11
|
+
required?: boolean;
|
|
12
|
+
/** Description or additional content */
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
/** Optional anchor ID for linking */
|
|
15
|
+
id?: string;
|
|
16
|
+
/** Optional extra class name */
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A highly aesthetic and readable component for documenting API fields/properties.
|
|
22
|
+
*/
|
|
23
|
+
export function Field({
|
|
24
|
+
name,
|
|
25
|
+
type,
|
|
26
|
+
defaultValue,
|
|
27
|
+
required = false,
|
|
28
|
+
children,
|
|
29
|
+
id,
|
|
30
|
+
className = "",
|
|
31
|
+
}: FieldProps) {
|
|
32
|
+
return (
|
|
33
|
+
<div className={`ld-field ${className}`.trim()} id={id}>
|
|
34
|
+
<div className="ld-field__header">
|
|
35
|
+
<div className="ld-field__signature">
|
|
36
|
+
<code className="ld-field__name">{name}</code>
|
|
37
|
+
{type && (
|
|
38
|
+
<span className="ld-field__type-badge">
|
|
39
|
+
{type}
|
|
40
|
+
</span>
|
|
41
|
+
)}
|
|
42
|
+
{required && (
|
|
43
|
+
<span className="ld-field__required-badge">required</span>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{defaultValue && (
|
|
48
|
+
<div className="ld-field__default">
|
|
49
|
+
<span className="ld-field__default-label">Default:</span>
|
|
50
|
+
<code className="ld-field__default-value">{defaultValue}</code>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="ld-field__content">
|
|
56
|
+
{children}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -1,23 +1,72 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useState, useMemo } from "react";
|
|
2
|
+
import { ChevronUp, ChevronDown, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
|
|
2
3
|
|
|
3
4
|
export interface TableProps {
|
|
4
5
|
headers?: string[];
|
|
5
6
|
data?: (string | React.ReactNode)[][];
|
|
6
7
|
children?: React.ReactNode;
|
|
7
8
|
className?: string;
|
|
9
|
+
sortable?: boolean;
|
|
10
|
+
paginated?: boolean;
|
|
11
|
+
pageSize?: number;
|
|
8
12
|
}
|
|
9
13
|
|
|
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
14
|
export function Table({
|
|
16
15
|
headers,
|
|
17
16
|
data,
|
|
18
17
|
children,
|
|
19
18
|
className = "",
|
|
19
|
+
sortable = false,
|
|
20
|
+
paginated = false,
|
|
21
|
+
pageSize = 10,
|
|
20
22
|
}: TableProps) {
|
|
23
|
+
const [sortConfig, setSortConfig] = useState<{ key: number; direction: 'asc' | 'desc' } | null>(null);
|
|
24
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
25
|
+
|
|
26
|
+
const processedData = useMemo(() => {
|
|
27
|
+
if (!data) return [];
|
|
28
|
+
let items = [...data];
|
|
29
|
+
|
|
30
|
+
if (sortable && sortConfig !== null) {
|
|
31
|
+
items.sort((a, b) => {
|
|
32
|
+
const aVal = a[sortConfig.key];
|
|
33
|
+
const bVal = b[sortConfig.key];
|
|
34
|
+
|
|
35
|
+
// Simple string comparison for sorting
|
|
36
|
+
const aStr = typeof aVal === 'string' ? aVal : '';
|
|
37
|
+
const bStr = typeof bVal === 'string' ? bVal : '';
|
|
38
|
+
|
|
39
|
+
if (aStr < bStr) return sortConfig.direction === 'asc' ? -1 : 1;
|
|
40
|
+
if (aStr > bStr) return sortConfig.direction === 'asc' ? 1 : -1;
|
|
41
|
+
return 0;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return items;
|
|
46
|
+
}, [data, sortConfig, sortable]);
|
|
47
|
+
|
|
48
|
+
const totalPages = Math.ceil(processedData.length / pageSize);
|
|
49
|
+
const paginatedData = useMemo(() => {
|
|
50
|
+
if (!paginated) return processedData;
|
|
51
|
+
const start = (currentPage - 1) * pageSize;
|
|
52
|
+
return processedData.slice(start, start + pageSize);
|
|
53
|
+
}, [processedData, paginated, currentPage, pageSize]);
|
|
54
|
+
|
|
55
|
+
const requestSort = (index: number) => {
|
|
56
|
+
if (!sortable) return;
|
|
57
|
+
let direction: 'asc' | 'desc' = 'asc';
|
|
58
|
+
if (sortConfig && sortConfig.key === index && sortConfig.direction === 'asc') {
|
|
59
|
+
direction = 'desc';
|
|
60
|
+
}
|
|
61
|
+
setSortConfig({ key: index, direction });
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const renderSortIcon = (index: number) => {
|
|
65
|
+
if (!sortable) return null;
|
|
66
|
+
if (sortConfig?.key !== index) return <ChevronDown size={14} className="ld-table-sort-icon ld-table-sort-icon--hidden" />;
|
|
67
|
+
return sortConfig.direction === 'asc' ? <ChevronUp size={14} className="ld-table-sort-icon" /> : <ChevronDown size={14} className="ld-table-sort-icon" />;
|
|
68
|
+
};
|
|
69
|
+
|
|
21
70
|
const tableContent = children ? (
|
|
22
71
|
children
|
|
23
72
|
) : (
|
|
@@ -26,14 +75,23 @@ export function Table({
|
|
|
26
75
|
<thead>
|
|
27
76
|
<tr>
|
|
28
77
|
{headers.map((header, i) => (
|
|
29
|
-
<th
|
|
78
|
+
<th
|
|
79
|
+
key={i}
|
|
80
|
+
onClick={() => requestSort(i)}
|
|
81
|
+
className={sortable ? "ld-table-header--sortable" : ""}
|
|
82
|
+
>
|
|
83
|
+
<div className="ld-table-header-content">
|
|
84
|
+
{header}
|
|
85
|
+
{renderSortIcon(i)}
|
|
86
|
+
</div>
|
|
87
|
+
</th>
|
|
30
88
|
))}
|
|
31
89
|
</tr>
|
|
32
90
|
</thead>
|
|
33
91
|
)}
|
|
34
|
-
{
|
|
92
|
+
{paginatedData && (
|
|
35
93
|
<tbody>
|
|
36
|
-
{
|
|
94
|
+
{paginatedData.map((row, i) => (
|
|
37
95
|
<tr key={i}>
|
|
38
96
|
{row.map((cell, j) => (
|
|
39
97
|
<td key={j}>{cell}</td>
|
|
@@ -47,7 +105,47 @@ export function Table({
|
|
|
47
105
|
|
|
48
106
|
return (
|
|
49
107
|
<div className={`ld-table-container ${className}`.trim()}>
|
|
50
|
-
<
|
|
108
|
+
<div className="ld-table-wrapper">
|
|
109
|
+
<table className="ld-table">{tableContent}</table>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{paginated && totalPages > 1 && (
|
|
113
|
+
<div className="ld-table-pagination">
|
|
114
|
+
<div className="ld-table-pagination-info">
|
|
115
|
+
Page {currentPage} of {totalPages}
|
|
116
|
+
</div>
|
|
117
|
+
<div className="ld-table-pagination-controls">
|
|
118
|
+
<button
|
|
119
|
+
onClick={() => setCurrentPage(1)}
|
|
120
|
+
disabled={currentPage === 1}
|
|
121
|
+
className="ld-table-pagination-btn"
|
|
122
|
+
>
|
|
123
|
+
<ChevronsLeft size={16} />
|
|
124
|
+
</button>
|
|
125
|
+
<button
|
|
126
|
+
onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}
|
|
127
|
+
disabled={currentPage === 1}
|
|
128
|
+
className="ld-table-pagination-btn"
|
|
129
|
+
>
|
|
130
|
+
<ChevronLeft size={16} />
|
|
131
|
+
</button>
|
|
132
|
+
<button
|
|
133
|
+
onClick={() => setCurrentPage(prev => Math.min(prev + 1, totalPages))}
|
|
134
|
+
disabled={currentPage === totalPages}
|
|
135
|
+
className="ld-table-pagination-btn"
|
|
136
|
+
>
|
|
137
|
+
<ChevronRight size={16} />
|
|
138
|
+
</button>
|
|
139
|
+
<button
|
|
140
|
+
onClick={() => setCurrentPage(totalPages)}
|
|
141
|
+
disabled={currentPage === totalPages}
|
|
142
|
+
className="ld-table-pagination-btn"
|
|
143
|
+
>
|
|
144
|
+
<ChevronsRight size={16} />
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
51
149
|
</div>
|
|
52
150
|
);
|
|
53
151
|
}
|
|
@@ -547,7 +547,13 @@
|
|
|
547
547
|
border-radius: var(--ld-radius-lg);
|
|
548
548
|
overflow: hidden;
|
|
549
549
|
background: var(--ld-bg-soft);
|
|
550
|
+
display: flex;
|
|
551
|
+
flex-direction: column;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.ld-table-wrapper {
|
|
550
555
|
overflow-x: auto;
|
|
556
|
+
scrollbar-width: thin;
|
|
551
557
|
}
|
|
552
558
|
|
|
553
559
|
.ld-table {
|
|
@@ -570,6 +576,36 @@
|
|
|
570
576
|
font-size: 0.8125rem;
|
|
571
577
|
text-transform: uppercase;
|
|
572
578
|
letter-spacing: 0.04em;
|
|
579
|
+
white-space: nowrap;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.ld-table-header--sortable {
|
|
583
|
+
cursor: pointer;
|
|
584
|
+
transition: background-color 0.2s;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.ld-table-header--sortable:hover {
|
|
588
|
+
background-color: var(--ld-bg-soft);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.ld-table-header-content {
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: center;
|
|
594
|
+
gap: 0.5rem;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.ld-table-sort-icon {
|
|
598
|
+
opacity: 0.8;
|
|
599
|
+
color: var(--ld-color-primary);
|
|
600
|
+
transition: opacity 0.2s;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.ld-table-sort-icon--hidden {
|
|
604
|
+
opacity: 0;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.ld-table-header--sortable:hover .ld-table-sort-icon--hidden {
|
|
608
|
+
opacity: 0.3;
|
|
573
609
|
}
|
|
574
610
|
|
|
575
611
|
.ld-table td {
|
|
@@ -588,3 +624,141 @@
|
|
|
588
624
|
background: rgba(255, 255, 255, 0.05);
|
|
589
625
|
border-radius: 4px;
|
|
590
626
|
}
|
|
627
|
+
|
|
628
|
+
/* Pagination */
|
|
629
|
+
.ld-table-pagination {
|
|
630
|
+
display: flex;
|
|
631
|
+
align-items: center;
|
|
632
|
+
justify-content: space-between;
|
|
633
|
+
padding: 0.75rem 1rem;
|
|
634
|
+
background: var(--ld-bg-mute);
|
|
635
|
+
border-top: 1px solid var(--ld-border-subtle);
|
|
636
|
+
font-size: 0.75rem;
|
|
637
|
+
color: var(--ld-text-dim);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.ld-table-pagination-controls {
|
|
641
|
+
display: flex;
|
|
642
|
+
align-items: center;
|
|
643
|
+
gap: 0.25rem;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.ld-table-pagination-btn {
|
|
647
|
+
display: flex;
|
|
648
|
+
align-items: center;
|
|
649
|
+
justify-content: center;
|
|
650
|
+
width: 1.75rem;
|
|
651
|
+
height: 1.75rem;
|
|
652
|
+
border-radius: var(--ld-radius-md);
|
|
653
|
+
border: 1px solid var(--ld-border-subtle);
|
|
654
|
+
background: var(--ld-bg-soft);
|
|
655
|
+
color: var(--ld-text-muted);
|
|
656
|
+
cursor: pointer;
|
|
657
|
+
transition: all 0.2s;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.ld-table-pagination-btn:hover:not(:disabled) {
|
|
661
|
+
background: var(--ld-bg-mute);
|
|
662
|
+
color: var(--ld-text-main);
|
|
663
|
+
border-color: var(--ld-border-strong);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.ld-table-pagination-btn:disabled {
|
|
667
|
+
opacity: 0.4;
|
|
668
|
+
cursor: not-allowed;
|
|
669
|
+
}
|
|
670
|
+
/* ─── Field ───────────────────────────────────────────────── */
|
|
671
|
+
.ld-field {
|
|
672
|
+
margin: 1.5rem 0;
|
|
673
|
+
padding: 1.25rem;
|
|
674
|
+
border-radius: var(--ld-radius-lg);
|
|
675
|
+
border: 1px solid var(--ld-border-subtle);
|
|
676
|
+
background: var(--ld-bg-soft);
|
|
677
|
+
position: relative;
|
|
678
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.ld-field:hover {
|
|
682
|
+
border-color: var(--ld-color-primary);
|
|
683
|
+
box-shadow: 0 4px 20px -8px rgba(127, 19, 236, 0.15);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
.ld-field__header {
|
|
687
|
+
display: flex;
|
|
688
|
+
flex-wrap: wrap;
|
|
689
|
+
align-items: center;
|
|
690
|
+
justify-content: space-between;
|
|
691
|
+
gap: 1rem;
|
|
692
|
+
margin-bottom: 0.85rem;
|
|
693
|
+
padding-bottom: 0.85rem;
|
|
694
|
+
border-bottom: 1px solid var(--ld-border-subtle);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.ld-field__signature {
|
|
698
|
+
display: flex;
|
|
699
|
+
align-items: center;
|
|
700
|
+
gap: 0.75rem;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.ld-field__name {
|
|
704
|
+
font-family: var(--ld-font-mono);
|
|
705
|
+
font-size: 0.95rem;
|
|
706
|
+
font-weight: 700;
|
|
707
|
+
color: var(--ld-text-main);
|
|
708
|
+
background: rgba(127, 19, 236, 0.08);
|
|
709
|
+
padding: 0.2rem 0.6rem;
|
|
710
|
+
border-radius: var(--ld-radius-sm);
|
|
711
|
+
letter-spacing: -0.01em;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.ld-field__type-badge {
|
|
715
|
+
font-family: var(--ld-font-mono);
|
|
716
|
+
font-size: 0.75rem;
|
|
717
|
+
font-weight: 500;
|
|
718
|
+
color: var(--ld-color-primary);
|
|
719
|
+
background: var(--ld-color-primary-muted);
|
|
720
|
+
padding: 0.15rem 0.5rem;
|
|
721
|
+
border-radius: var(--ld-radius-full);
|
|
722
|
+
border: 1px solid rgba(127, 19, 236, 0.2);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
.ld-field__required-badge {
|
|
726
|
+
font-size: 0.7rem;
|
|
727
|
+
font-weight: 700;
|
|
728
|
+
text-transform: uppercase;
|
|
729
|
+
color: var(--ld-ui-danger-text);
|
|
730
|
+
background: var(--ld-ui-danger-bg);
|
|
731
|
+
padding: 0.1rem 0.4rem;
|
|
732
|
+
border-radius: var(--ld-radius-sm);
|
|
733
|
+
letter-spacing: 0.05em;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
.ld-field__default {
|
|
737
|
+
display: flex;
|
|
738
|
+
align-items: center;
|
|
739
|
+
gap: 0.5rem;
|
|
740
|
+
font-size: 0.8rem;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.ld-field__default-label {
|
|
744
|
+
color: var(--ld-text-dim);
|
|
745
|
+
font-weight: 500;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.ld-field__default-value {
|
|
749
|
+
font-family: var(--ld-font-mono);
|
|
750
|
+
color: var(--ld-text-main);
|
|
751
|
+
background: var(--ld-bg-mute);
|
|
752
|
+
padding: 0.15rem 0.4rem;
|
|
753
|
+
border-radius: var(--ld-radius-sm);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.ld-field__content {
|
|
757
|
+
font-size: 0.875rem;
|
|
758
|
+
line-height: 1.6;
|
|
759
|
+
color: var(--ld-text-muted);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.ld-field__content p {
|
|
763
|
+
margin: 0;
|
|
764
|
+
}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
--ld-code-text: #1f2937;
|
|
42
42
|
|
|
43
43
|
/* ─ Customization ─ */
|
|
44
|
-
--ld-navbar-bg:
|
|
44
|
+
--ld-navbar-bg: #ffffff;
|
|
45
45
|
--ld-navbar-blur: 12px;
|
|
46
46
|
--ld-sidebar-bg: transparent;
|
|
47
47
|
--ld-sidebar-blur: 0px;
|
|
@@ -160,4 +160,28 @@
|
|
|
160
160
|
--ld-radius-md: 8px;
|
|
161
161
|
--ld-radius-lg: 12px;
|
|
162
162
|
--ld-radius-full: 9999px;
|
|
163
|
+
|
|
164
|
+
/* ─ Custom Scrollbar ─ */
|
|
165
|
+
scrollbar-width: thin;
|
|
166
|
+
scrollbar-color: var(--ld-border-strong) transparent;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Chrome, Edge, and Safari */
|
|
170
|
+
*::-webkit-scrollbar {
|
|
171
|
+
width: 6px;
|
|
172
|
+
height: 6px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
*::-webkit-scrollbar-track {
|
|
176
|
+
background: transparent;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
*::-webkit-scrollbar-thumb {
|
|
180
|
+
background-color: var(--ld-border-strong);
|
|
181
|
+
border-radius: 20px;
|
|
182
|
+
border: transparent;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
186
|
+
background-color: var(--ld-text-dim);
|
|
163
187
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
fallback?: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface State {
|
|
9
|
+
hasError: boolean;
|
|
10
|
+
error?: Error;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
14
|
+
public state: State = {
|
|
15
|
+
hasError: false
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
public static getDerivedStateFromError(error: Error): State {
|
|
19
|
+
return { hasError: true, error };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
23
|
+
console.error("Uncaught error in Boltdocs Layout:", error, errorInfo);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public render() {
|
|
27
|
+
if (this.state.hasError) {
|
|
28
|
+
return this.props.fallback || (
|
|
29
|
+
<div className="boltdocs-error-boundary">
|
|
30
|
+
<div className="boltdocs-error-title">Something went wrong</div>
|
|
31
|
+
<p className="boltdocs-error-message">
|
|
32
|
+
{this.state.error?.message || "An unexpected error occurred while rendering this page."}
|
|
33
|
+
</p>
|
|
34
|
+
<button
|
|
35
|
+
className="boltdocs-error-retry"
|
|
36
|
+
onClick={() => this.setState({ hasError: false })}
|
|
37
|
+
>
|
|
38
|
+
Try again
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.props.children;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ErrorBoundary";
|
|
@@ -18,6 +18,8 @@ import { OnThisPage } from "../OnThisPage";
|
|
|
18
18
|
import { Head } from "../Head";
|
|
19
19
|
import { Breadcrumbs } from "../Breadcrumbs";
|
|
20
20
|
import { BackgroundGradient } from "../BackgroundGradient";
|
|
21
|
+
import { ProgressBar } from "../ProgressBar";
|
|
22
|
+
import { ErrorBoundary } from "../ErrorBoundary";
|
|
21
23
|
import "../../styles.css";
|
|
22
24
|
|
|
23
25
|
export interface ThemeLayoutProps {
|
|
@@ -108,6 +110,7 @@ export function ThemeLayout({
|
|
|
108
110
|
|
|
109
111
|
return (
|
|
110
112
|
<div className={`boltdocs-layout ${className}`} style={style}>
|
|
113
|
+
<ProgressBar />
|
|
111
114
|
{background !== undefined ? background : <BackgroundGradient />}
|
|
112
115
|
{head !== undefined ? (
|
|
113
116
|
head
|
|
@@ -142,7 +145,11 @@ export function ThemeLayout({
|
|
|
142
145
|
) : (
|
|
143
146
|
<Breadcrumbs routes={filteredRoutes} config={config} />
|
|
144
147
|
)}
|
|
145
|
-
<div className="boltdocs-page">
|
|
148
|
+
<div className="boltdocs-page">
|
|
149
|
+
<ErrorBoundary>
|
|
150
|
+
{children}
|
|
151
|
+
</ErrorBoundary>
|
|
152
|
+
</div>
|
|
146
153
|
|
|
147
154
|
{/* Prev / Next Navigation */}
|
|
148
155
|
{(prevPage || nextPage) && (
|