boltdocs 1.10.1 → 1.10.2
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-MEWGAONO.mjs → SearchDialog-AGVF6JBO.mjs} +1 -1
- package/dist/{SearchDialog-BEVZQ74P.css → SearchDialog-YPDOM7Q6.css} +196 -28
- package/dist/{chunk-OZLYRXAD.mjs → chunk-TKLQWU7H.mjs} +177 -171
- package/dist/client/index.css +196 -28
- package/dist/client/index.d.mts +3 -10
- package/dist/client/index.d.ts +3 -10
- package/dist/client/index.js +377 -341
- package/dist/client/index.mjs +91 -52
- package/dist/client/ssr.css +196 -28
- package/dist/client/ssr.js +190 -192
- package/dist/client/ssr.mjs +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +0 -1
- package/src/client/theme/components/CodeBlock/CodeBlock.tsx +22 -1
- package/src/client/theme/components/Playground/Playground.tsx +104 -48
- package/src/client/theme/components/Playground/playground.css +88 -18
- package/src/client/theme/styles/markdown.css +53 -0
- package/src/client/theme/styles/variables.css +0 -12
- package/src/client/theme/styles.css +1 -0
- package/src/client/theme/ui/CopyMarkdown/copy-markdown.css +0 -2
- package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +19 -15
- package/src/client/theme/ui/ErrorBoundary/error-boundary.css +55 -0
- package/src/client/theme/ui/Layout/Layout.tsx +5 -10
- package/src/client/theme/ui/Layout/base.css +2 -1
- package/src/client/theme/ui/Layout/responsive.css +11 -0
- package/src/client/theme/ui/BackgroundGradient/BackgroundGradient.tsx +0 -10
- package/src/client/theme/ui/BackgroundGradient/index.ts +0 -1
package/dist/client/ssr.mjs
CHANGED
package/package.json
CHANGED
package/src/client/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ export { Sidebar } from "./theme/ui/Sidebar";
|
|
|
8
8
|
export { OnThisPage } from "./theme/ui/OnThisPage";
|
|
9
9
|
export { Head } from "./theme/ui/Head";
|
|
10
10
|
export { Breadcrumbs } from "./theme/ui/Breadcrumbs";
|
|
11
|
-
export { BackgroundGradient } from "./theme/ui/BackgroundGradient";
|
|
12
11
|
export { Playground } from "./theme/components/Playground";
|
|
13
12
|
export { NotFound } from "./theme/ui/NotFound";
|
|
14
13
|
export { Loading } from "./theme/ui/Loading";
|
|
@@ -14,6 +14,8 @@ interface CodeBlockProps {
|
|
|
14
14
|
*/
|
|
15
15
|
export function CodeBlock({ children, ...props }: CodeBlockProps) {
|
|
16
16
|
const [copied, setCopied] = useState(false);
|
|
17
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
18
|
+
const [isExpandable, setIsExpandable] = useState(false);
|
|
17
19
|
const preRef = useRef<HTMLPreElement>(null);
|
|
18
20
|
|
|
19
21
|
const handleCopy = useCallback(async () => {
|
|
@@ -23,8 +25,17 @@ export function CodeBlock({ children, ...props }: CodeBlockProps) {
|
|
|
23
25
|
setTimeout(() => setCopied(false), 2000);
|
|
24
26
|
}, []);
|
|
25
27
|
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (preRef.current) {
|
|
30
|
+
const codeLength = preRef.current.textContent?.length || 0;
|
|
31
|
+
setIsExpandable(codeLength > 500);
|
|
32
|
+
}
|
|
33
|
+
}, [children]);
|
|
34
|
+
|
|
35
|
+
const shouldTruncate = isExpandable && !isExpanded;
|
|
36
|
+
|
|
26
37
|
return (
|
|
27
|
-
<div className=
|
|
38
|
+
<div className={`code-block-wrapper ${shouldTruncate ? "is-truncated" : ""}`}>
|
|
28
39
|
<button
|
|
29
40
|
className={`code-block-copy ${copied ? "copied" : ""}`}
|
|
30
41
|
onClick={handleCopy}
|
|
@@ -35,6 +46,16 @@ export function CodeBlock({ children, ...props }: CodeBlockProps) {
|
|
|
35
46
|
<pre ref={preRef} {...props}>
|
|
36
47
|
{children}
|
|
37
48
|
</pre>
|
|
49
|
+
{isExpandable && (
|
|
50
|
+
<div className="code-block-expand-wrapper">
|
|
51
|
+
<button
|
|
52
|
+
className="code-block-expand-btn"
|
|
53
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
54
|
+
>
|
|
55
|
+
{isExpanded ? "Show less" : "Expand code"}
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
38
59
|
</div>
|
|
39
60
|
);
|
|
40
61
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useState, useMemo } from "react";
|
|
2
2
|
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
|
|
3
3
|
import { Copy, Check, Terminal, Play } from "lucide-react";
|
|
4
|
+
import { CodeBlock } from "../CodeBlock";
|
|
4
5
|
|
|
5
6
|
interface PlaygroundProps {
|
|
6
7
|
code?: string;
|
|
7
8
|
children?: string | React.ReactNode;
|
|
9
|
+
preview?: React.ReactNode;
|
|
8
10
|
scope?: Record<string, any>;
|
|
9
11
|
readonly?: boolean;
|
|
10
12
|
noInline?: boolean;
|
|
@@ -16,7 +18,7 @@ interface PlaygroundProps {
|
|
|
16
18
|
* appending a `render(<ComponentName />)` call.
|
|
17
19
|
*/
|
|
18
20
|
function prepareCode(raw: string): { code: string; noInline: boolean } {
|
|
19
|
-
const trimmed = raw.trim();
|
|
21
|
+
const trimmed = (raw || "").trim();
|
|
20
22
|
|
|
21
23
|
// Match: export default function Name(...)
|
|
22
24
|
const fnMatch = trimmed.match(/export\s+default\s+function\s+(\w+)/);
|
|
@@ -41,33 +43,38 @@ function prepareCode(raw: string): { code: string; noInline: boolean } {
|
|
|
41
43
|
return { code: trimmed, noInline: false };
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
/**
|
|
45
|
-
* A live React playground component.
|
|
46
|
-
* Features a split layout with a live editor and a preview section.
|
|
47
|
-
*
|
|
48
|
-
* Supports `export default function App()` style code out of the box.
|
|
49
|
-
*/
|
|
50
46
|
export function Playground({
|
|
51
|
-
code,
|
|
47
|
+
code: propsCode,
|
|
52
48
|
children,
|
|
49
|
+
preview,
|
|
53
50
|
scope = {},
|
|
54
51
|
readonly = false,
|
|
55
52
|
noInline: forceNoInline,
|
|
56
53
|
}: PlaygroundProps) {
|
|
57
54
|
// Extract code from either `code` prop or `children`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
const initialCode = useMemo(() => {
|
|
56
|
+
let base = propsCode || "";
|
|
57
|
+
if (!base && typeof children === "string") {
|
|
58
|
+
base = children;
|
|
59
|
+
}
|
|
60
|
+
return base.trim();
|
|
61
|
+
}, [propsCode, children]);
|
|
62
62
|
|
|
63
|
-
const prepared = prepareCode(initialCode);
|
|
63
|
+
const prepared = useMemo(() => prepareCode(initialCode), [initialCode]);
|
|
64
64
|
const useNoInline = forceNoInline ?? prepared.noInline;
|
|
65
65
|
|
|
66
66
|
const [copied, setCopied] = useState(false);
|
|
67
67
|
const [activeCode, setActiveCode] = useState(prepared.code);
|
|
68
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
69
|
+
|
|
70
|
+
// Sync activeCode when initialCode changes (e.g. in static mode)
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
setActiveCode(prepared.code);
|
|
73
|
+
}, [prepared.code]);
|
|
68
74
|
|
|
69
75
|
const handleCopy = () => {
|
|
70
|
-
|
|
76
|
+
const textToCopy = !!preview ? initialCode : activeCode;
|
|
77
|
+
navigator.clipboard.writeText(textToCopy);
|
|
71
78
|
setCopied(true);
|
|
72
79
|
setTimeout(() => setCopied(false), 2000);
|
|
73
80
|
};
|
|
@@ -75,50 +82,99 @@ export function Playground({
|
|
|
75
82
|
// Provide React generically
|
|
76
83
|
const extendedScope = { React, ...scope };
|
|
77
84
|
|
|
85
|
+
const charLimit = 800; // Adjust as needed
|
|
86
|
+
const isExpandable = (propsCode || initialCode).length > charLimit;
|
|
87
|
+
const shouldTruncate = isExpandable && !isExpanded;
|
|
88
|
+
|
|
89
|
+
const isStatic = !!preview;
|
|
90
|
+
|
|
91
|
+
// Transformer to prevent ReferenceError: require is not defined
|
|
92
|
+
// only used in static mode to allow highlighting without execution
|
|
93
|
+
const staticTransform = (code: string) => {
|
|
94
|
+
// Return empty or a simple dummy to avoid running the code
|
|
95
|
+
return "render(<div style={{display:'none'}} />)";
|
|
96
|
+
};
|
|
97
|
+
|
|
78
98
|
return (
|
|
79
|
-
<div className=
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
<div className={`boltdocs-playground ${shouldTruncate ? "is-truncated" : ""}`} data-readonly={readonly || isStatic}>
|
|
100
|
+
<div className="playground-split-container">
|
|
101
|
+
{/* Preview Side - Now on top */}
|
|
102
|
+
<div className="playground-panel playground-preview-panel">
|
|
103
|
+
<div className="playground-panel-header">
|
|
104
|
+
<div className="playground-panel-title">
|
|
105
|
+
<Play size={14} />
|
|
106
|
+
<span>Preview</span>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="playground-panel-content playground-preview">
|
|
110
|
+
{isStatic ? (
|
|
111
|
+
preview
|
|
112
|
+
) : (
|
|
113
|
+
<LiveProvider
|
|
114
|
+
code={activeCode}
|
|
115
|
+
scope={extendedScope}
|
|
116
|
+
theme={undefined}
|
|
117
|
+
noInline={useNoInline}
|
|
118
|
+
>
|
|
119
|
+
<LivePreview />
|
|
120
|
+
<LiveError className="playground-error" />
|
|
121
|
+
</LiveProvider>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{/* Editor Side - Now on bottom */}
|
|
127
|
+
<div className="playground-panel playground-editor-panel">
|
|
128
|
+
{!isStatic && (
|
|
89
129
|
<div className="playground-panel-header">
|
|
90
130
|
<div className="playground-panel-title">
|
|
91
131
|
<Terminal size={14} />
|
|
92
132
|
<span>{readonly ? "Code Example" : "Live Editor"}</span>
|
|
93
133
|
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
<div className="playground-panel-content playground-editor">
|
|
137
|
+
{/* Copy button moved inside code area */}
|
|
138
|
+
<button
|
|
139
|
+
className="playground-copy-btn-inner"
|
|
140
|
+
onClick={handleCopy}
|
|
141
|
+
title="Copy code"
|
|
142
|
+
>
|
|
143
|
+
{copied ? <Check size={14} /> : <Copy size={14} />}
|
|
144
|
+
</button>
|
|
145
|
+
|
|
146
|
+
{isStatic ? (
|
|
147
|
+
<LiveProvider
|
|
148
|
+
code={initialCode}
|
|
149
|
+
noInline={true}
|
|
150
|
+
transformCode={staticTransform}
|
|
151
|
+
>
|
|
152
|
+
<LiveEditor disabled />
|
|
153
|
+
</LiveProvider>
|
|
154
|
+
) : (
|
|
155
|
+
<LiveProvider
|
|
156
|
+
code={activeCode}
|
|
157
|
+
scope={extendedScope}
|
|
158
|
+
theme={undefined}
|
|
159
|
+
noInline={useNoInline}
|
|
160
|
+
>
|
|
161
|
+
<LiveEditor disabled={readonly} onChange={setActiveCode} />
|
|
162
|
+
</LiveProvider>
|
|
163
|
+
)}
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
{isExpandable && (
|
|
167
|
+
<div className="playground-expand-wrapper">
|
|
94
168
|
<button
|
|
95
|
-
className="playground-
|
|
96
|
-
onClick={
|
|
97
|
-
title="Copy code"
|
|
169
|
+
className="playground-expand-btn"
|
|
170
|
+
onClick={() => setIsExpanded(!isExpanded)}
|
|
98
171
|
>
|
|
99
|
-
{
|
|
172
|
+
{isExpanded ? "Show less" : "Expand code"}
|
|
100
173
|
</button>
|
|
101
174
|
</div>
|
|
102
|
-
|
|
103
|
-
<LiveEditor disabled={readonly} onChange={setActiveCode} />
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
|
|
107
|
-
{/* Preview Side */}
|
|
108
|
-
<div className="playground-panel playground-preview-panel">
|
|
109
|
-
<div className="playground-panel-header">
|
|
110
|
-
<div className="playground-panel-title">
|
|
111
|
-
<Play size={14} />
|
|
112
|
-
<span>Preview</span>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
<div className="playground-panel-content playground-preview">
|
|
116
|
-
<LivePreview />
|
|
117
|
-
<LiveError className="playground-error" />
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
175
|
+
)}
|
|
120
176
|
</div>
|
|
121
|
-
</
|
|
177
|
+
</div>
|
|
122
178
|
</div>
|
|
123
179
|
);
|
|
124
180
|
}
|
|
@@ -19,14 +19,6 @@
|
|
|
19
19
|
width: 100%;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
@media (min-width: 1024px) {
|
|
23
|
-
.playground-split-container {
|
|
24
|
-
flex-direction: row;
|
|
25
|
-
min-height: 350px;
|
|
26
|
-
align-items: stretch;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
22
|
.playground-panel {
|
|
31
23
|
display: flex;
|
|
32
24
|
flex-direction: column;
|
|
@@ -35,15 +27,62 @@
|
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
.playground-editor-panel {
|
|
38
|
-
border-
|
|
30
|
+
border-top: 1px solid var(--ld-border-subtle);
|
|
39
31
|
background: var(--ld-code-bg);
|
|
32
|
+
position: relative;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.boltdocs-playground.is-truncated .playground-editor {
|
|
36
|
+
max-height: 250px;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.boltdocs-playground[data-readonly="true"]:not(.is-truncated) .playground-editor {
|
|
41
|
+
max-height: 600px; /* Limit height even if not truncated manually */
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
.playground-expand-wrapper {
|
|
45
|
+
position: absolute;
|
|
46
|
+
bottom: 0;
|
|
47
|
+
left: 0;
|
|
48
|
+
right: 0;
|
|
49
|
+
height: 80px;
|
|
50
|
+
background: linear-gradient(to top, var(--ld-code-bg) 20%, transparent);
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: flex-end;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
padding-bottom: 1rem;
|
|
55
|
+
z-index: 10;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.playground-expand-btn {
|
|
59
|
+
background-color: var(--ld-surface);
|
|
60
|
+
border: 1px solid var(--ld-border-subtle);
|
|
61
|
+
border-radius: 20px;
|
|
62
|
+
color: var(--ld-text-main);
|
|
63
|
+
padding: 0.5rem 1.25rem;
|
|
64
|
+
font-size: 0.8125rem;
|
|
65
|
+
font-weight: 500;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
transition: all 0.2s ease;
|
|
68
|
+
backdrop-filter: blur(8px);
|
|
69
|
+
-webkit-backdrop-filter: blur(8px);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.playground-expand-btn:hover {
|
|
73
|
+
background-color: var(--ld-border-subtle);
|
|
74
|
+
transform: translateY(-1px);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* When expanded, maybe show the button at the bottom without overlay */
|
|
78
|
+
.boltdocs-playground:not(.is-truncated) .playground-expand-wrapper {
|
|
79
|
+
position: relative;
|
|
80
|
+
height: auto;
|
|
81
|
+
background: none;
|
|
82
|
+
display: flex;
|
|
83
|
+
justify-content: center;
|
|
84
|
+
padding: 1rem 0;
|
|
85
|
+
border-top: 1px solid var(--ld-border-subtle);
|
|
47
86
|
}
|
|
48
87
|
|
|
49
88
|
.playground-preview-panel {
|
|
@@ -130,18 +169,18 @@
|
|
|
130
169
|
|
|
131
170
|
/* Preview Specifics */
|
|
132
171
|
.playground-preview {
|
|
133
|
-
padding:
|
|
172
|
+
padding: 2.5rem;
|
|
134
173
|
display: flex;
|
|
135
174
|
align-items: center;
|
|
136
175
|
justify-content: center;
|
|
137
|
-
background-color:
|
|
176
|
+
background-color: #000; /* Dark background as in the image */
|
|
138
177
|
background-image: radial-gradient(
|
|
139
|
-
|
|
178
|
+
rgba(255, 255, 255, 0.05) 1.5px,
|
|
140
179
|
transparent 1.5px
|
|
141
180
|
);
|
|
142
181
|
background-size: 24px 24px;
|
|
143
182
|
color: var(--ld-text-main);
|
|
144
|
-
min-height:
|
|
183
|
+
min-height: 300px;
|
|
145
184
|
}
|
|
146
185
|
|
|
147
186
|
/* Error Specifics */
|
|
@@ -166,3 +205,34 @@
|
|
|
166
205
|
margin: 2rem 0;
|
|
167
206
|
border: 1px solid var(--ld-border-subtle);
|
|
168
207
|
}
|
|
208
|
+
/* Copy Button - Inner */
|
|
209
|
+
.playground-copy-btn-inner {
|
|
210
|
+
position: absolute;
|
|
211
|
+
top: 1rem;
|
|
212
|
+
right: 1.5rem;
|
|
213
|
+
z-index: 20;
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
justify-content: center;
|
|
217
|
+
width: 32px;
|
|
218
|
+
height: 32px;
|
|
219
|
+
border-radius: 6px;
|
|
220
|
+
border: 1px solid var(--ld-border-subtle);
|
|
221
|
+
background: var(--ld-surface);
|
|
222
|
+
color: var(--ld-text-dim);
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
transition: all 0.2s ease;
|
|
225
|
+
backdrop-filter: blur(8px);
|
|
226
|
+
-webkit-backdrop-filter: blur(8px);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.playground-copy-btn-inner:hover {
|
|
230
|
+
background: var(--ld-border-subtle);
|
|
231
|
+
color: var(--ld-text-main);
|
|
232
|
+
transform: translateY(-1px);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Obsolete Static Code Integration - Cleaning up since we use LiveEditor now */
|
|
236
|
+
.playground-static-code {
|
|
237
|
+
display: none;
|
|
238
|
+
}
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
font-weight: 700;
|
|
78
78
|
letter-spacing: -0.025em;
|
|
79
79
|
color: var(--ld-text-main);
|
|
80
|
+
padding-right: 160px; /* Space for CopyMarkdown button */
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
.boltdocs-page h1 + p {
|
|
@@ -267,6 +268,58 @@
|
|
|
267
268
|
padding: 1.25rem 1rem !important;
|
|
268
269
|
}
|
|
269
270
|
|
|
271
|
+
.code-block-wrapper.is-truncated pre {
|
|
272
|
+
max-height: 250px;
|
|
273
|
+
overflow: hidden;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.code-block-expand-wrapper {
|
|
277
|
+
position: absolute;
|
|
278
|
+
bottom: 0;
|
|
279
|
+
left: 0;
|
|
280
|
+
right: 0;
|
|
281
|
+
height: 80px;
|
|
282
|
+
background: linear-gradient(to top, var(--ld-code-bg) 20%, transparent);
|
|
283
|
+
display: none;
|
|
284
|
+
align-items: flex-end;
|
|
285
|
+
justify-content: center;
|
|
286
|
+
padding-bottom: 1rem;
|
|
287
|
+
z-index: 10;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.code-block-wrapper.is-truncated .code-block-expand-wrapper {
|
|
291
|
+
display: flex;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.code-block-expand-btn {
|
|
295
|
+
background-color: var(--ld-surface);
|
|
296
|
+
border: 1px solid var(--ld-border-subtle);
|
|
297
|
+
border-radius: 20px;
|
|
298
|
+
color: var(--ld-text-main);
|
|
299
|
+
padding: 0.5rem 1.25rem;
|
|
300
|
+
font-size: 0.8125rem;
|
|
301
|
+
font-weight: 500;
|
|
302
|
+
cursor: pointer;
|
|
303
|
+
transition: all 0.2s ease;
|
|
304
|
+
backdrop-filter: blur(8px);
|
|
305
|
+
-webkit-backdrop-filter: blur(8px);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.code-block-expand-btn:hover {
|
|
309
|
+
background-color: var(--ld-border-subtle);
|
|
310
|
+
transform: translateY(-1px);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.code-block-wrapper:not(.is-truncated) .code-block-expand-wrapper {
|
|
314
|
+
position: relative;
|
|
315
|
+
height: auto;
|
|
316
|
+
background: none;
|
|
317
|
+
display: flex;
|
|
318
|
+
justify-content: center;
|
|
319
|
+
padding: 1rem 0;
|
|
320
|
+
border-top: 1px solid var(--ld-border-subtle);
|
|
321
|
+
}
|
|
322
|
+
|
|
270
323
|
.code-block-wrapper pre > code .line {
|
|
271
324
|
padding: 0 1.25rem;
|
|
272
325
|
}
|
|
@@ -32,10 +32,6 @@
|
|
|
32
32
|
--ld-btn-secondary-bg: #ffffff;
|
|
33
33
|
--ld-btn-secondary-text: #111827;
|
|
34
34
|
|
|
35
|
-
/* ─ Gradients ─ */
|
|
36
|
-
--ld-gradient-from: var(--ld-color-primary);
|
|
37
|
-
--ld-gradient-to: #a855f7;
|
|
38
|
-
|
|
39
35
|
--ld-code-bg: #f3f4f6;
|
|
40
36
|
--ld-code-header: #e5e7eb;
|
|
41
37
|
--ld-code-text: #1f2937;
|
|
@@ -45,8 +41,6 @@
|
|
|
45
41
|
--ld-navbar-blur: 12px;
|
|
46
42
|
--ld-sidebar-bg: transparent;
|
|
47
43
|
--ld-sidebar-blur: 0px;
|
|
48
|
-
--ld-glow-1-bg: var(--ld-color-primary-glow);
|
|
49
|
-
--ld-glow-2-bg: rgba(215, 59, 246, 0.15);
|
|
50
44
|
|
|
51
45
|
/* ─ UI Components (overridable independently from layout) ─ */
|
|
52
46
|
--ld-ui-btn-primary-bg: var(--ld-btn-primary-bg);
|
|
@@ -102,10 +96,6 @@
|
|
|
102
96
|
--ld-btn-secondary-bg: #1a1a2e;
|
|
103
97
|
--ld-btn-secondary-text: #e4e4ed;
|
|
104
98
|
|
|
105
|
-
/* ─ Gradients ─ */
|
|
106
|
-
--ld-gradient-from: #ffffff;
|
|
107
|
-
--ld-gradient-to: rgba(255, 255, 255, 0.7);
|
|
108
|
-
|
|
109
99
|
/* ─ Code ─ */
|
|
110
100
|
--ld-code-bg: #050505;
|
|
111
101
|
--ld-code-header: #111119;
|
|
@@ -116,8 +106,6 @@
|
|
|
116
106
|
--ld-navbar-blur: 12px;
|
|
117
107
|
--ld-sidebar-bg: transparent;
|
|
118
108
|
--ld-sidebar-blur: 0px;
|
|
119
|
-
--ld-glow-1-bg: var(--ld-color-primary-glow);
|
|
120
|
-
--ld-glow-2-bg: rgba(246, 59, 187, 0.15);
|
|
121
109
|
|
|
122
110
|
/* ─ UI Components (dark) ─ */
|
|
123
111
|
--ld-ui-btn-primary-bg: var(--ld-btn-primary-bg);
|
|
@@ -12,12 +12,10 @@
|
|
|
12
12
|
border-radius: var(--ld-radius-full);
|
|
13
13
|
overflow: hidden;
|
|
14
14
|
transition: all 0.2s ease;
|
|
15
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
.copy-btn-group:hover {
|
|
19
18
|
border-color: var(--ld-color-primary-hover);
|
|
20
|
-
box-shadow: 0 4px 20px rgba(59, 130, 246, 0.15);
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
.copy-btn {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
import "./error-boundary.css";
|
|
2
3
|
|
|
3
4
|
interface Props {
|
|
4
5
|
children?: ReactNode;
|
|
@@ -12,7 +13,7 @@ interface State {
|
|
|
12
13
|
|
|
13
14
|
export class ErrorBoundary extends Component<Props, State> {
|
|
14
15
|
public state: State = {
|
|
15
|
-
hasError: false
|
|
16
|
+
hasError: false,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
public static getDerivedStateFromError(error: Error): State {
|
|
@@ -25,19 +26,22 @@ export class ErrorBoundary extends Component<Props, State> {
|
|
|
25
26
|
|
|
26
27
|
public render() {
|
|
27
28
|
if (this.state.hasError) {
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
<div className="boltdocs-error-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
return (
|
|
30
|
+
this.props.fallback || (
|
|
31
|
+
<div className="boltdocs-error-boundary">
|
|
32
|
+
<div className="boltdocs-error-title">Something went wrong</div>
|
|
33
|
+
<p className="boltdocs-error-message">
|
|
34
|
+
{this.state.error?.message ||
|
|
35
|
+
"An unexpected error occurred while rendering this page."}
|
|
36
|
+
</p>
|
|
37
|
+
<button
|
|
38
|
+
className="boltdocs-error-retry"
|
|
39
|
+
onClick={() => this.setState({ hasError: false })}
|
|
40
|
+
>
|
|
41
|
+
Try again
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
41
45
|
);
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
.boltdocs-error-boundary {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
padding: 3rem 2rem;
|
|
7
|
+
margin: 2rem 0;
|
|
8
|
+
background-color: var(--ld-bg-soft);
|
|
9
|
+
border: 1px solid var(--ld-ui-danger-border);
|
|
10
|
+
border-radius: var(--ld-radius-lg);
|
|
11
|
+
text-align: center;
|
|
12
|
+
backdrop-filter: blur(8px);
|
|
13
|
+
-webkit-backdrop-filter: blur(8px);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.boltdocs-error-title {
|
|
17
|
+
font-size: 1.5rem;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
color: var(--ld-ui-danger-text);
|
|
20
|
+
margin-bottom: 0.75rem;
|
|
21
|
+
font-family: var(--ld-font-sans);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.boltdocs-error-message {
|
|
25
|
+
font-size: 1rem;
|
|
26
|
+
color: var(--ld-text-muted);
|
|
27
|
+
max-width: 100%;
|
|
28
|
+
margin-bottom: 1.5rem;
|
|
29
|
+
line-height: 1.6;
|
|
30
|
+
overflow-wrap: break-word;
|
|
31
|
+
word-break: break-word;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.boltdocs-error-retry {
|
|
35
|
+
padding: 0.6rem 1.25rem;
|
|
36
|
+
background-color: var(--ld-ui-btn-primary-bg);
|
|
37
|
+
color: var(--ld-ui-btn-primary-text);
|
|
38
|
+
border: none;
|
|
39
|
+
border-radius: var(--ld-radius-full);
|
|
40
|
+
font-size: 0.875rem;
|
|
41
|
+
font-weight: 600;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
transition: all 0.2s ease;
|
|
44
|
+
box-shadow: 0 4px 12px var(--ld-color-primary-glow);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.boltdocs-error-retry:hover {
|
|
48
|
+
background-color: var(--ld-color-primary-hover);
|
|
49
|
+
transform: translateY(-1px);
|
|
50
|
+
box-shadow: 0 6px 16px var(--ld-color-primary-glow);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.boltdocs-error-retry:active {
|
|
54
|
+
transform: translateY(0);
|
|
55
|
+
}
|