@alepha/react 0.15.0 → 0.15.1
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/auth/index.browser.js +603 -242
- package/dist/auth/index.browser.js.map +1 -1
- package/dist/auth/index.d.ts +6 -6
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1296 -922
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +128 -128
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +20 -20
- package/dist/core/index.js.map +1 -1
- package/dist/form/index.d.ts +36 -36
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +15 -15
- package/dist/form/index.js.map +1 -1
- package/dist/head/index.browser.js +20 -0
- package/dist/head/index.browser.js.map +1 -1
- package/dist/head/index.d.ts +73 -65
- package/dist/head/index.d.ts.map +1 -1
- package/dist/head/index.js +20 -0
- package/dist/head/index.js.map +1 -1
- package/dist/i18n/index.d.ts +37 -37
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/router/index.browser.js +605 -244
- package/dist/router/index.browser.js.map +1 -1
- package/dist/router/index.d.ts +539 -550
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1296 -922
- package/dist/router/index.js.map +1 -1
- package/dist/websocket/index.d.ts +38 -38
- package/dist/websocket/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/auth/__tests__/$auth.spec.ts +162 -147
- package/src/auth/index.ts +9 -3
- package/src/auth/services/ReactAuth.ts +15 -5
- package/src/core/hooks/useAction.ts +1 -2
- package/src/core/index.ts +4 -4
- package/src/form/errors/FormValidationError.ts +4 -6
- package/src/form/hooks/useFormState.ts +1 -1
- package/src/form/index.ts +1 -1
- package/src/form/services/FormModel.ts +31 -25
- package/src/head/helpers/SeoExpander.ts +2 -1
- package/src/head/hooks/useHead.spec.tsx +2 -2
- package/src/head/index.browser.ts +2 -2
- package/src/head/index.ts +4 -4
- package/src/head/interfaces/Head.ts +15 -3
- package/src/head/primitives/$head.ts +2 -5
- package/src/head/providers/BrowserHeadProvider.ts +55 -0
- package/src/head/providers/HeadProvider.ts +4 -1
- package/src/i18n/__tests__/integration.spec.tsx +1 -1
- package/src/i18n/components/Localize.spec.tsx +2 -2
- package/src/i18n/hooks/useI18n.browser.spec.tsx +2 -2
- package/src/i18n/index.ts +1 -1
- package/src/i18n/primitives/$dictionary.ts +1 -1
- package/src/i18n/providers/I18nProvider.spec.ts +1 -1
- package/src/i18n/providers/I18nProvider.ts +1 -1
- package/src/router/__tests__/page-head-browser.browser.spec.ts +5 -1
- package/src/router/__tests__/page-head.spec.ts +11 -7
- package/src/router/__tests__/seo-head.spec.ts +7 -3
- package/src/router/atoms/ssrManifestAtom.ts +2 -11
- package/src/router/components/ErrorViewer.tsx +626 -167
- package/src/router/components/Link.tsx +4 -2
- package/src/router/components/NestedView.tsx +7 -9
- package/src/router/components/NotFound.tsx +2 -2
- package/src/router/hooks/useQueryParams.ts +1 -1
- package/src/router/hooks/useRouter.ts +1 -1
- package/src/router/hooks/useRouterState.ts +1 -1
- package/src/router/index.browser.ts +10 -11
- package/src/router/index.shared.ts +7 -7
- package/src/router/index.ts +10 -7
- package/src/router/primitives/$page.browser.spec.tsx +6 -1
- package/src/router/primitives/$page.spec.tsx +7 -1
- package/src/router/primitives/$page.ts +5 -9
- package/src/router/providers/ReactBrowserProvider.ts +17 -6
- package/src/router/providers/ReactBrowserRouterProvider.ts +1 -1
- package/src/router/providers/ReactPageProvider.ts +4 -3
- package/src/router/providers/ReactServerProvider.ts +29 -37
- package/src/router/providers/ReactServerTemplateProvider.ts +300 -137
- package/src/router/providers/SSRManifestProvider.ts +17 -60
- package/src/router/services/ReactPageService.ts +4 -1
- package/src/router/services/ReactRouter.ts +6 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Alepha } from "alepha";
|
|
2
|
-
import { type CSSProperties, useState } from "react";
|
|
2
|
+
import { type CSSProperties, useEffect, useRef, useState } from "react";
|
|
3
3
|
|
|
4
4
|
interface ErrorViewerProps {
|
|
5
5
|
error: Error;
|
|
@@ -12,34 +12,169 @@ interface StackFrame {
|
|
|
12
12
|
line: string;
|
|
13
13
|
col: string;
|
|
14
14
|
raw: string;
|
|
15
|
+
isNodeModules: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
const isBrowser = typeof window !== "undefined";
|
|
19
|
+
|
|
17
20
|
/**
|
|
18
|
-
* Error viewer component
|
|
21
|
+
* Error viewer component - Terminal/brutalist aesthetic
|
|
19
22
|
*/
|
|
20
23
|
const ErrorViewer = ({ error, alepha }: ErrorViewerProps) => {
|
|
21
24
|
const [expanded, setExpanded] = useState(false);
|
|
25
|
+
const [showNodeModules, setShowNodeModules] = useState(false);
|
|
26
|
+
const [visible, setVisible] = useState(false);
|
|
27
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
28
|
+
|
|
22
29
|
const isProduction = alepha.isProduction();
|
|
23
30
|
|
|
31
|
+
// Animate in on mount
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const timer = setTimeout(() => setVisible(true), 10);
|
|
34
|
+
return () => clearTimeout(timer);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
// Keyboard shortcuts
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!isBrowser) return;
|
|
40
|
+
const handler = (e: KeyboardEvent) => {
|
|
41
|
+
if (e.key === "c" && !e.metaKey && !e.ctrlKey) {
|
|
42
|
+
copyToClipboard(error.stack || error.message);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
window.addEventListener("keydown", handler);
|
|
46
|
+
return () => window.removeEventListener("keydown", handler);
|
|
47
|
+
}, [error]);
|
|
48
|
+
|
|
24
49
|
if (isProduction) {
|
|
25
50
|
return <ErrorViewerProduction />;
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
const frames = parseStackTrace(error.stack);
|
|
29
|
-
const
|
|
30
|
-
const
|
|
54
|
+
const appFrames = frames.filter((f) => !f.isNodeModules);
|
|
55
|
+
const nodeModulesFrames = frames.filter((f) => f.isNodeModules);
|
|
56
|
+
const visibleAppFrames = expanded ? appFrames : appFrames.slice(0, 5);
|
|
57
|
+
const hiddenAppCount = appFrames.length - 5;
|
|
58
|
+
const timestamp = new Date().toLocaleTimeString("en-US", {
|
|
59
|
+
hour12: false,
|
|
60
|
+
hour: "2-digit",
|
|
61
|
+
minute: "2-digit",
|
|
62
|
+
second: "2-digit",
|
|
63
|
+
});
|
|
31
64
|
|
|
32
65
|
return (
|
|
33
|
-
<div
|
|
34
|
-
|
|
66
|
+
<div
|
|
67
|
+
ref={containerRef}
|
|
68
|
+
style={{
|
|
69
|
+
...styles.overlay,
|
|
70
|
+
opacity: visible ? 1 : 0,
|
|
71
|
+
}}
|
|
72
|
+
role="alertdialog"
|
|
73
|
+
aria-modal="true"
|
|
74
|
+
aria-labelledby="error-viewer-title"
|
|
75
|
+
>
|
|
76
|
+
{/* Scan lines effect */}
|
|
77
|
+
<div style={styles.scanlines} aria-hidden="true" />
|
|
78
|
+
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
...styles.container,
|
|
82
|
+
transform: visible ? "translateY(0)" : "translateY(-20px)",
|
|
83
|
+
opacity: visible ? 1 : 0,
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{/* Terminal header bar */}
|
|
87
|
+
<div style={styles.terminalBar}>
|
|
88
|
+
<div style={styles.terminalDots}>
|
|
89
|
+
<span style={{ ...styles.dot, backgroundColor: "#ff5f57" }} />
|
|
90
|
+
<span style={{ ...styles.dot, backgroundColor: "#febc2e" }} />
|
|
91
|
+
<span style={{ ...styles.dot, backgroundColor: "#28c840" }} />
|
|
92
|
+
</div>
|
|
93
|
+
<div style={styles.terminalTitle}>
|
|
94
|
+
<span style={styles.terminalTitleText}>error — {timestamp}</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div style={styles.terminalActions}>
|
|
97
|
+
<kbd style={styles.kbd}>C</kbd>
|
|
98
|
+
<span style={styles.kbdLabel}>copy</span>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{/* Error header */}
|
|
35
103
|
<Header error={error} />
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
104
|
+
|
|
105
|
+
{/* Stack trace */}
|
|
106
|
+
<div style={styles.stackSection}>
|
|
107
|
+
<div style={styles.stackHeader}>
|
|
108
|
+
<span style={styles.stackHeaderText}>STACK TRACE</span>
|
|
109
|
+
<span style={styles.stackCount}>
|
|
110
|
+
{appFrames.length} frames
|
|
111
|
+
{nodeModulesFrames.length > 0 &&
|
|
112
|
+
` · ${nodeModulesFrames.length} in node_modules`}
|
|
113
|
+
</span>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div style={styles.frameList}>
|
|
117
|
+
{visibleAppFrames.map((frame, i) => (
|
|
118
|
+
<StackFrameRow
|
|
119
|
+
key={`${frame.raw}-${i}`}
|
|
120
|
+
frame={frame}
|
|
121
|
+
index={i}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
|
|
125
|
+
{hiddenAppCount > 0 && !expanded && (
|
|
126
|
+
<ExpandButton
|
|
127
|
+
onClick={() => setExpanded(true)}
|
|
128
|
+
label={`Show ${hiddenAppCount} more frames`}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
|
|
132
|
+
{expanded && hiddenAppCount > 0 && (
|
|
133
|
+
<ExpandButton
|
|
134
|
+
onClick={() => setExpanded(false)}
|
|
135
|
+
label="Collapse"
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{nodeModulesFrames.length > 0 && (
|
|
140
|
+
<>
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={() => setShowNodeModules(!showNodeModules)}
|
|
144
|
+
style={styles.nodeModulesToggle}
|
|
145
|
+
>
|
|
146
|
+
<span style={styles.nodeModulesIcon}>
|
|
147
|
+
{showNodeModules ? "▼" : "▶"}
|
|
148
|
+
</span>
|
|
149
|
+
<span style={styles.nodeModulesLabel}>node_modules</span>
|
|
150
|
+
<span style={styles.nodeModulesCount}>
|
|
151
|
+
{nodeModulesFrames.length}
|
|
152
|
+
</span>
|
|
153
|
+
</button>
|
|
154
|
+
|
|
155
|
+
{showNodeModules && (
|
|
156
|
+
<div style={styles.nodeModulesFrames}>
|
|
157
|
+
{nodeModulesFrames.map((frame, i) => (
|
|
158
|
+
<StackFrameRow
|
|
159
|
+
key={`nm-${frame.raw}-${i}`}
|
|
160
|
+
frame={frame}
|
|
161
|
+
index={appFrames.length + i}
|
|
162
|
+
dimmed
|
|
163
|
+
/>
|
|
164
|
+
))}
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
</>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{/* Footer */}
|
|
173
|
+
<div style={styles.footer}>
|
|
174
|
+
<span style={styles.footerText}>
|
|
175
|
+
Press <kbd style={styles.kbdInline}>C</kbd> to copy stack trace
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
43
178
|
</div>
|
|
44
179
|
</div>
|
|
45
180
|
);
|
|
@@ -49,9 +184,6 @@ export default ErrorViewer;
|
|
|
49
184
|
|
|
50
185
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
51
186
|
|
|
52
|
-
/**
|
|
53
|
-
* Parse stack trace string into structured frames
|
|
54
|
-
*/
|
|
55
187
|
function parseStackTrace(stack?: string): StackFrame[] {
|
|
56
188
|
if (!stack) return [];
|
|
57
189
|
|
|
@@ -69,10 +201,9 @@ function parseStackTrace(stack?: string): StackFrame[] {
|
|
|
69
201
|
return frames;
|
|
70
202
|
}
|
|
71
203
|
|
|
72
|
-
/**
|
|
73
|
-
* Parse a single stack trace line into a structured frame
|
|
74
|
-
*/
|
|
75
204
|
function parseStackLine(line: string): StackFrame | null {
|
|
205
|
+
const isNodeModules = line.includes("node_modules") || line.includes("node:");
|
|
206
|
+
|
|
76
207
|
const withFn = line.match(/^at\s+(.+?)\s+\((.+):(\d+):(\d+)\)$/);
|
|
77
208
|
if (withFn) {
|
|
78
209
|
return {
|
|
@@ -81,6 +212,7 @@ function parseStackLine(line: string): StackFrame | null {
|
|
|
81
212
|
line: withFn[3],
|
|
82
213
|
col: withFn[4],
|
|
83
214
|
raw: line,
|
|
215
|
+
isNodeModules,
|
|
84
216
|
};
|
|
85
217
|
}
|
|
86
218
|
|
|
@@ -92,82 +224,74 @@ function parseStackLine(line: string): StackFrame | null {
|
|
|
92
224
|
line: withoutFn[2],
|
|
93
225
|
col: withoutFn[3],
|
|
94
226
|
raw: line,
|
|
227
|
+
isNodeModules,
|
|
95
228
|
};
|
|
96
229
|
}
|
|
97
230
|
|
|
98
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
fn: "",
|
|
233
|
+
file: line.replace(/^at\s+/, ""),
|
|
234
|
+
line: "",
|
|
235
|
+
col: "",
|
|
236
|
+
raw: line,
|
|
237
|
+
isNodeModules,
|
|
238
|
+
};
|
|
99
239
|
}
|
|
100
240
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
navigator.clipboard
|
|
106
|
-
|
|
107
|
-
|
|
241
|
+
function copyToClipboard(text: string): Promise<boolean> {
|
|
242
|
+
if (!isBrowser || !navigator.clipboard) {
|
|
243
|
+
return Promise.resolve(false);
|
|
244
|
+
}
|
|
245
|
+
return navigator.clipboard
|
|
246
|
+
.writeText(text)
|
|
247
|
+
.then(() => true)
|
|
248
|
+
.catch(() => false);
|
|
108
249
|
}
|
|
109
250
|
|
|
110
251
|
/**
|
|
111
|
-
* Header
|
|
252
|
+
* Header with error badge and message
|
|
112
253
|
*/
|
|
113
254
|
function Header({ error }: { error: Error }) {
|
|
114
255
|
const [copied, setCopied] = useState(false);
|
|
256
|
+
const [hovered, setHovered] = useState(false);
|
|
115
257
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
setCopied(
|
|
119
|
-
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (!copied) return;
|
|
260
|
+
const timer = setTimeout(() => setCopied(false), 2000);
|
|
261
|
+
return () => clearTimeout(timer);
|
|
262
|
+
}, [copied]);
|
|
263
|
+
|
|
264
|
+
const handleCopy = async () => {
|
|
265
|
+
const success = await copyToClipboard(error.stack || error.message);
|
|
266
|
+
if (success) setCopied(true);
|
|
120
267
|
};
|
|
121
268
|
|
|
122
269
|
return (
|
|
123
270
|
<div style={styles.header}>
|
|
124
|
-
<div style={styles.
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
{
|
|
271
|
+
<div style={styles.headerRow}>
|
|
272
|
+
{/* Glowing error indicator */}
|
|
273
|
+
<div style={styles.errorIndicator}>
|
|
274
|
+
<div style={styles.errorGlow} />
|
|
275
|
+
<div style={styles.errorBadge}>{error.name}</div>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
onClick={handleCopy}
|
|
281
|
+
onMouseEnter={() => setHovered(true)}
|
|
282
|
+
onMouseLeave={() => setHovered(false)}
|
|
283
|
+
style={{
|
|
284
|
+
...styles.copyBtn,
|
|
285
|
+
...(hovered ? styles.copyBtnHover : {}),
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
{copied ? "✓ Copied" : "Copy"}
|
|
128
289
|
</button>
|
|
129
290
|
</div>
|
|
130
|
-
<h1 style={styles.message}>{error.message}</h1>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
291
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
function StackTraceSection({
|
|
139
|
-
frames,
|
|
140
|
-
visibleFrames,
|
|
141
|
-
expanded,
|
|
142
|
-
hiddenCount,
|
|
143
|
-
onToggle,
|
|
144
|
-
}: {
|
|
145
|
-
frames: StackFrame[];
|
|
146
|
-
visibleFrames: StackFrame[];
|
|
147
|
-
expanded: boolean;
|
|
148
|
-
hiddenCount: number;
|
|
149
|
-
onToggle: () => void;
|
|
150
|
-
}) {
|
|
151
|
-
if (frames.length === 0) return null;
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<div style={styles.stackSection}>
|
|
155
|
-
<div style={styles.stackHeader}>Call Stack</div>
|
|
156
|
-
<div style={styles.frameList}>
|
|
157
|
-
{visibleFrames.map((frame, i) => (
|
|
158
|
-
<StackFrameRow key={i} frame={frame} index={i} />
|
|
159
|
-
))}
|
|
160
|
-
{!expanded && hiddenCount > 0 && (
|
|
161
|
-
<button type="button" onClick={onToggle} style={styles.expandBtn}>
|
|
162
|
-
Show {hiddenCount} more frames
|
|
163
|
-
</button>
|
|
164
|
-
)}
|
|
165
|
-
{expanded && hiddenCount > 0 && (
|
|
166
|
-
<button type="button" onClick={onToggle} style={styles.expandBtn}>
|
|
167
|
-
Show less
|
|
168
|
-
</button>
|
|
169
|
-
)}
|
|
170
|
-
</div>
|
|
292
|
+
<h1 id="error-viewer-title" style={styles.message}>
|
|
293
|
+
{error.message}
|
|
294
|
+
</h1>
|
|
171
295
|
</div>
|
|
172
296
|
);
|
|
173
297
|
}
|
|
@@ -175,239 +299,574 @@ function StackTraceSection({
|
|
|
175
299
|
/**
|
|
176
300
|
* Single stack frame row
|
|
177
301
|
*/
|
|
178
|
-
function StackFrameRow({
|
|
179
|
-
|
|
302
|
+
function StackFrameRow({
|
|
303
|
+
frame,
|
|
304
|
+
index,
|
|
305
|
+
dimmed = false,
|
|
306
|
+
}: {
|
|
307
|
+
frame: StackFrame;
|
|
308
|
+
index: number;
|
|
309
|
+
dimmed?: boolean;
|
|
310
|
+
}) {
|
|
311
|
+
const [hovered, setHovered] = useState(false);
|
|
312
|
+
const isFirst = index === 0 && !dimmed;
|
|
180
313
|
const fileName = frame.file.split("/").pop() || frame.file;
|
|
181
314
|
const dirPath = frame.file.substring(0, frame.file.length - fileName.length);
|
|
182
315
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
316
|
+
// Build vscode:// link for clickable paths
|
|
317
|
+
const vsCodeLink =
|
|
318
|
+
frame.file && frame.line
|
|
319
|
+
? `vscode://file${frame.file}:${frame.line}:${frame.col || 1}`
|
|
320
|
+
: null;
|
|
321
|
+
|
|
322
|
+
const content = (
|
|
323
|
+
<>
|
|
324
|
+
<div
|
|
325
|
+
style={{
|
|
326
|
+
...styles.frameIndex,
|
|
327
|
+
color: isFirst ? "#ff6b6b" : dimmed ? "#555" : "#666",
|
|
328
|
+
}}
|
|
329
|
+
>
|
|
330
|
+
{String(index + 1).padStart(2, "0")}
|
|
331
|
+
</div>
|
|
191
332
|
<div style={styles.frameContent}>
|
|
192
333
|
{frame.fn && (
|
|
193
|
-
<div
|
|
194
|
-
{
|
|
334
|
+
<div
|
|
335
|
+
style={{
|
|
336
|
+
...styles.fnName,
|
|
337
|
+
color: dimmed ? "#888" : "#f0f0f0",
|
|
338
|
+
}}
|
|
339
|
+
>
|
|
340
|
+
{formatFunctionName(frame.fn)}
|
|
195
341
|
</div>
|
|
196
342
|
)}
|
|
197
343
|
<div style={styles.filePath}>
|
|
198
|
-
<span style={styles.dirPath}
|
|
199
|
-
|
|
344
|
+
<span style={{ ...styles.dirPath, opacity: dimmed ? 0.6 : 0.8 }}>
|
|
345
|
+
{dirPath}
|
|
346
|
+
</span>
|
|
347
|
+
<span
|
|
348
|
+
style={{
|
|
349
|
+
...styles.fileName,
|
|
350
|
+
color: dimmed ? "#5a9aba" : "#7cc4eb",
|
|
351
|
+
}}
|
|
352
|
+
>
|
|
353
|
+
{fileName}
|
|
354
|
+
</span>
|
|
200
355
|
{frame.line && (
|
|
201
|
-
<span
|
|
202
|
-
|
|
356
|
+
<span
|
|
357
|
+
style={{
|
|
358
|
+
...styles.lineCol,
|
|
359
|
+
color: dimmed ? "#9a8a40" : "#e5b83a",
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
:{frame.line}
|
|
363
|
+
{frame.col && `:${frame.col}`}
|
|
203
364
|
</span>
|
|
204
365
|
)}
|
|
205
366
|
</div>
|
|
206
367
|
</div>
|
|
207
|
-
|
|
368
|
+
</>
|
|
208
369
|
);
|
|
370
|
+
|
|
371
|
+
const rowStyles: CSSProperties = {
|
|
372
|
+
...styles.frame,
|
|
373
|
+
...(isFirst ? styles.frameFirst : {}),
|
|
374
|
+
backgroundColor: hovered ? "rgba(255,255,255,0.03)" : "transparent",
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
if (vsCodeLink && isBrowser) {
|
|
378
|
+
return (
|
|
379
|
+
<a
|
|
380
|
+
href={vsCodeLink}
|
|
381
|
+
style={{ ...rowStyles, textDecoration: "none" }}
|
|
382
|
+
onMouseEnter={() => setHovered(true)}
|
|
383
|
+
onMouseLeave={() => setHovered(false)}
|
|
384
|
+
>
|
|
385
|
+
{content}
|
|
386
|
+
</a>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return <div style={rowStyles}>{content}</div>;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Format function name with syntax highlighting
|
|
395
|
+
*/
|
|
396
|
+
function formatFunctionName(fn: string): React.ReactNode {
|
|
397
|
+
// Highlight async/Object/Class prefixes
|
|
398
|
+
const asyncMatch = fn.match(/^(async\s+)?(.+)$/);
|
|
399
|
+
if (asyncMatch?.[1]) {
|
|
400
|
+
return (
|
|
401
|
+
<>
|
|
402
|
+
<span style={{ color: "#c678dd" }}>async </span>
|
|
403
|
+
<span>{asyncMatch[2]}</span>
|
|
404
|
+
</>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Highlight method calls like Object.method
|
|
409
|
+
const methodMatch = fn.match(/^(.+)\.([^.]+)$/);
|
|
410
|
+
if (methodMatch) {
|
|
411
|
+
return (
|
|
412
|
+
<>
|
|
413
|
+
<span style={{ color: "#e5c07b" }}>{methodMatch[1]}</span>
|
|
414
|
+
<span style={{ color: "#666" }}>.</span>
|
|
415
|
+
<span>{methodMatch[2]}</span>
|
|
416
|
+
</>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return fn;
|
|
209
421
|
}
|
|
210
422
|
|
|
211
423
|
/**
|
|
212
|
-
*
|
|
424
|
+
* Expand/collapse button
|
|
425
|
+
*/
|
|
426
|
+
function ExpandButton({
|
|
427
|
+
onClick,
|
|
428
|
+
label,
|
|
429
|
+
}: {
|
|
430
|
+
onClick: () => void;
|
|
431
|
+
label: string;
|
|
432
|
+
}) {
|
|
433
|
+
const [hovered, setHovered] = useState(false);
|
|
434
|
+
|
|
435
|
+
return (
|
|
436
|
+
<button
|
|
437
|
+
type="button"
|
|
438
|
+
onClick={onClick}
|
|
439
|
+
onMouseEnter={() => setHovered(true)}
|
|
440
|
+
onMouseLeave={() => setHovered(false)}
|
|
441
|
+
style={{
|
|
442
|
+
...styles.expandBtn,
|
|
443
|
+
backgroundColor: hovered ? "rgba(255,255,255,0.05)" : "transparent",
|
|
444
|
+
color: hovered ? "#aaa" : "#777",
|
|
445
|
+
}}
|
|
446
|
+
>
|
|
447
|
+
{label}
|
|
448
|
+
</button>
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Production error view - minimal, user-friendly
|
|
213
454
|
*/
|
|
214
455
|
function ErrorViewerProduction() {
|
|
456
|
+
const [hovered, setHovered] = useState(false);
|
|
457
|
+
|
|
458
|
+
const handleReload = () => {
|
|
459
|
+
if (isBrowser) window.location.reload();
|
|
460
|
+
};
|
|
461
|
+
|
|
215
462
|
return (
|
|
216
|
-
<div style={styles.overlay}>
|
|
463
|
+
<div style={styles.overlay} role="alertdialog" aria-modal="true">
|
|
217
464
|
<div style={styles.prodContainer}>
|
|
218
|
-
<div style={styles.prodIcon}
|
|
219
|
-
|
|
465
|
+
<div style={styles.prodIcon}>
|
|
466
|
+
<svg
|
|
467
|
+
width="32"
|
|
468
|
+
height="32"
|
|
469
|
+
viewBox="0 0 24 24"
|
|
470
|
+
fill="none"
|
|
471
|
+
stroke="currentColor"
|
|
472
|
+
strokeWidth="2"
|
|
473
|
+
>
|
|
474
|
+
<circle cx="12" cy="12" r="10" />
|
|
475
|
+
<line x1="12" y1="8" x2="12" y2="12" />
|
|
476
|
+
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
477
|
+
</svg>
|
|
478
|
+
</div>
|
|
479
|
+
<h1 style={styles.prodTitle}>Something went wrong</h1>
|
|
220
480
|
<p style={styles.prodMessage}>
|
|
221
|
-
|
|
481
|
+
We encountered an unexpected error. Please try again.
|
|
222
482
|
</p>
|
|
223
483
|
<button
|
|
224
484
|
type="button"
|
|
225
|
-
onClick={
|
|
226
|
-
|
|
485
|
+
onClick={handleReload}
|
|
486
|
+
onMouseEnter={() => setHovered(true)}
|
|
487
|
+
onMouseLeave={() => setHovered(false)}
|
|
488
|
+
style={{
|
|
489
|
+
...styles.prodButton,
|
|
490
|
+
backgroundColor: hovered ? "#333" : "#222",
|
|
491
|
+
borderColor: hovered ? "#555" : "#444",
|
|
492
|
+
}}
|
|
227
493
|
>
|
|
228
|
-
Reload
|
|
494
|
+
Reload page
|
|
229
495
|
</button>
|
|
230
496
|
</div>
|
|
231
497
|
</div>
|
|
232
498
|
);
|
|
233
499
|
}
|
|
234
500
|
|
|
501
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
502
|
+
// Styles
|
|
503
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
504
|
+
|
|
505
|
+
const MONO_FONT =
|
|
506
|
+
'ui-monospace, "JetBrains Mono", "Fira Code", SFMono-Regular, Menlo, Monaco, Consolas, monospace';
|
|
507
|
+
|
|
235
508
|
const styles: Record<string, CSSProperties> = {
|
|
236
509
|
overlay: {
|
|
237
510
|
position: "fixed",
|
|
238
511
|
inset: 0,
|
|
239
|
-
backgroundColor: "rgba(0, 0, 0, 0.
|
|
512
|
+
backgroundColor: "rgba(0, 0, 0, 0.92)",
|
|
240
513
|
display: "flex",
|
|
241
514
|
alignItems: "flex-start",
|
|
242
515
|
justifyContent: "center",
|
|
243
516
|
padding: "40px 20px",
|
|
244
517
|
overflow: "auto",
|
|
245
|
-
fontFamily:
|
|
246
|
-
|
|
518
|
+
fontFamily: MONO_FONT,
|
|
519
|
+
fontSize: "13px",
|
|
247
520
|
zIndex: 99999,
|
|
521
|
+
transition: "opacity 0.2s ease-out",
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
scanlines: {
|
|
525
|
+
position: "fixed",
|
|
526
|
+
inset: 0,
|
|
527
|
+
background:
|
|
528
|
+
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.1) 2px, rgba(0,0,0,0.1) 4px)",
|
|
529
|
+
pointerEvents: "none",
|
|
530
|
+
zIndex: 100000,
|
|
248
531
|
},
|
|
532
|
+
|
|
249
533
|
container: {
|
|
250
534
|
width: "100%",
|
|
251
|
-
maxWidth: "
|
|
252
|
-
backgroundColor: "#
|
|
253
|
-
borderRadius: "
|
|
535
|
+
maxWidth: "900px",
|
|
536
|
+
backgroundColor: "#0d0d0d",
|
|
537
|
+
borderRadius: "8px",
|
|
254
538
|
overflow: "hidden",
|
|
255
|
-
boxShadow: "0 25px
|
|
539
|
+
boxShadow: "0 0 0 1px #333, 0 25px 80px -12px rgba(0, 0, 0, 0.8)",
|
|
540
|
+
transition: "transform 0.3s ease-out, opacity 0.3s ease-out",
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
// Terminal bar
|
|
544
|
+
terminalBar: {
|
|
545
|
+
display: "flex",
|
|
546
|
+
alignItems: "center",
|
|
547
|
+
padding: "12px 16px",
|
|
548
|
+
backgroundColor: "#1a1a1a",
|
|
549
|
+
borderBottom: "1px solid #333",
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
terminalDots: {
|
|
553
|
+
display: "flex",
|
|
554
|
+
gap: "8px",
|
|
555
|
+
},
|
|
556
|
+
|
|
557
|
+
dot: {
|
|
558
|
+
width: "12px",
|
|
559
|
+
height: "12px",
|
|
560
|
+
borderRadius: "50%",
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
terminalTitle: {
|
|
564
|
+
flex: 1,
|
|
565
|
+
textAlign: "center",
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
terminalTitleText: {
|
|
569
|
+
color: "#777",
|
|
570
|
+
fontSize: "12px",
|
|
571
|
+
letterSpacing: "0.5px",
|
|
572
|
+
},
|
|
573
|
+
|
|
574
|
+
terminalActions: {
|
|
575
|
+
display: "flex",
|
|
576
|
+
alignItems: "center",
|
|
577
|
+
gap: "6px",
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
kbd: {
|
|
581
|
+
display: "inline-block",
|
|
582
|
+
padding: "2px 6px",
|
|
583
|
+
backgroundColor: "#2a2a2a",
|
|
584
|
+
borderRadius: "4px",
|
|
585
|
+
fontSize: "11px",
|
|
586
|
+
color: "#aaa",
|
|
587
|
+
border: "1px solid #444",
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
kbdInline: {
|
|
591
|
+
display: "inline-block",
|
|
592
|
+
padding: "1px 5px",
|
|
593
|
+
backgroundColor: "#222",
|
|
594
|
+
borderRadius: "3px",
|
|
595
|
+
fontSize: "11px",
|
|
596
|
+
color: "#888",
|
|
597
|
+
border: "1px solid #444",
|
|
598
|
+
marginLeft: "4px",
|
|
599
|
+
marginRight: "4px",
|
|
600
|
+
},
|
|
601
|
+
|
|
602
|
+
kbdLabel: {
|
|
603
|
+
color: "#777",
|
|
604
|
+
fontSize: "11px",
|
|
256
605
|
},
|
|
606
|
+
|
|
607
|
+
// Header
|
|
257
608
|
header: {
|
|
258
|
-
padding: "24px
|
|
609
|
+
padding: "24px",
|
|
259
610
|
borderBottom: "1px solid #333",
|
|
260
|
-
background: "linear-gradient(to bottom, #1f1f1f, #1a1a1a)",
|
|
261
611
|
},
|
|
262
|
-
|
|
612
|
+
|
|
613
|
+
headerRow: {
|
|
263
614
|
display: "flex",
|
|
264
615
|
alignItems: "center",
|
|
265
616
|
justifyContent: "space-between",
|
|
266
617
|
marginBottom: "16px",
|
|
267
618
|
},
|
|
268
|
-
|
|
619
|
+
|
|
620
|
+
errorIndicator: {
|
|
621
|
+
position: "relative",
|
|
622
|
+
display: "inline-flex",
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
errorGlow: {
|
|
626
|
+
position: "absolute",
|
|
627
|
+
inset: "-4px",
|
|
628
|
+
background:
|
|
629
|
+
"radial-gradient(ellipse at center, rgba(255,80,80,0.3) 0%, transparent 70%)",
|
|
630
|
+
borderRadius: "12px",
|
|
631
|
+
filter: "blur(8px)",
|
|
632
|
+
},
|
|
633
|
+
|
|
634
|
+
errorBadge: {
|
|
635
|
+
position: "relative",
|
|
269
636
|
display: "inline-block",
|
|
270
|
-
padding: "6px
|
|
271
|
-
backgroundColor: "#
|
|
272
|
-
color: "#
|
|
637
|
+
padding: "6px 14px",
|
|
638
|
+
backgroundColor: "#3d1a1a",
|
|
639
|
+
color: "#ff7b7b",
|
|
273
640
|
fontSize: "12px",
|
|
274
641
|
fontWeight: 600,
|
|
275
642
|
borderRadius: "6px",
|
|
276
|
-
|
|
643
|
+
border: "1px solid #5a2828",
|
|
644
|
+
letterSpacing: "0.5px",
|
|
277
645
|
},
|
|
646
|
+
|
|
278
647
|
copyBtn: {
|
|
279
|
-
padding: "8px
|
|
648
|
+
padding: "8px 14px",
|
|
280
649
|
backgroundColor: "transparent",
|
|
281
650
|
color: "#888",
|
|
282
|
-
fontSize: "
|
|
651
|
+
fontSize: "12px",
|
|
283
652
|
fontWeight: 500,
|
|
284
|
-
|
|
653
|
+
borderWidth: "1px",
|
|
654
|
+
borderStyle: "solid",
|
|
655
|
+
borderColor: "#444",
|
|
285
656
|
borderRadius: "6px",
|
|
286
657
|
cursor: "pointer",
|
|
287
658
|
transition: "all 0.15s",
|
|
659
|
+
fontFamily: MONO_FONT,
|
|
288
660
|
},
|
|
661
|
+
|
|
662
|
+
copyBtnHover: {
|
|
663
|
+
backgroundColor: "#252525",
|
|
664
|
+
color: "#bbb",
|
|
665
|
+
borderColor: "#555",
|
|
666
|
+
},
|
|
667
|
+
|
|
289
668
|
message: {
|
|
290
669
|
margin: 0,
|
|
291
|
-
fontSize: "
|
|
292
|
-
fontWeight:
|
|
293
|
-
color: "#
|
|
294
|
-
lineHeight: 1.
|
|
670
|
+
fontSize: "18px",
|
|
671
|
+
fontWeight: 400,
|
|
672
|
+
color: "#e8e8e8",
|
|
673
|
+
lineHeight: 1.6,
|
|
295
674
|
wordBreak: "break-word",
|
|
675
|
+
fontFamily: MONO_FONT,
|
|
296
676
|
},
|
|
677
|
+
|
|
678
|
+
// Stack section
|
|
297
679
|
stackSection: {
|
|
298
|
-
|
|
680
|
+
borderTop: "1px solid #2a2a2a",
|
|
299
681
|
},
|
|
682
|
+
|
|
300
683
|
stackHeader: {
|
|
301
|
-
|
|
302
|
-
|
|
684
|
+
display: "flex",
|
|
685
|
+
alignItems: "center",
|
|
686
|
+
justifyContent: "space-between",
|
|
687
|
+
padding: "14px 24px",
|
|
688
|
+
borderBottom: "1px solid #2a2a2a",
|
|
689
|
+
},
|
|
690
|
+
|
|
691
|
+
stackHeaderText: {
|
|
692
|
+
fontSize: "10px",
|
|
303
693
|
fontWeight: 600,
|
|
304
694
|
color: "#666",
|
|
305
|
-
|
|
306
|
-
letterSpacing: "0.1em",
|
|
307
|
-
borderBottom: "1px solid #2a2a2a",
|
|
695
|
+
letterSpacing: "1.5px",
|
|
308
696
|
},
|
|
697
|
+
|
|
698
|
+
stackCount: {
|
|
699
|
+
fontSize: "11px",
|
|
700
|
+
color: "#555",
|
|
701
|
+
},
|
|
702
|
+
|
|
309
703
|
frameList: {
|
|
310
704
|
display: "flex",
|
|
311
705
|
flexDirection: "column",
|
|
312
706
|
},
|
|
707
|
+
|
|
313
708
|
frame: {
|
|
314
709
|
display: "flex",
|
|
315
710
|
alignItems: "flex-start",
|
|
316
|
-
padding: "
|
|
317
|
-
borderBottom: "1px solid #
|
|
318
|
-
transition: "background-color 0.
|
|
711
|
+
padding: "12px 24px",
|
|
712
|
+
borderBottom: "1px solid #222",
|
|
713
|
+
transition: "background-color 0.1s",
|
|
714
|
+
cursor: "pointer",
|
|
319
715
|
},
|
|
716
|
+
|
|
320
717
|
frameFirst: {
|
|
321
|
-
backgroundColor: "rgba(
|
|
718
|
+
backgroundColor: "rgba(255, 80, 80, 0.08)",
|
|
719
|
+
borderLeft: "2px solid #ff6b6b",
|
|
322
720
|
},
|
|
721
|
+
|
|
323
722
|
frameIndex: {
|
|
324
723
|
width: "28px",
|
|
325
724
|
flexShrink: 0,
|
|
326
|
-
fontSize: "
|
|
725
|
+
fontSize: "11px",
|
|
327
726
|
fontWeight: 500,
|
|
328
|
-
|
|
329
|
-
fontFamily: "monospace",
|
|
727
|
+
fontFamily: MONO_FONT,
|
|
330
728
|
},
|
|
729
|
+
|
|
331
730
|
frameContent: {
|
|
332
731
|
flex: 1,
|
|
333
732
|
minWidth: 0,
|
|
334
733
|
},
|
|
734
|
+
|
|
335
735
|
fnName: {
|
|
336
|
-
fontSize: "
|
|
736
|
+
fontSize: "13px",
|
|
337
737
|
fontWeight: 500,
|
|
338
|
-
color: "#e5e5e5",
|
|
339
738
|
marginBottom: "4px",
|
|
340
|
-
fontFamily:
|
|
341
|
-
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
739
|
+
fontFamily: MONO_FONT,
|
|
342
740
|
},
|
|
741
|
+
|
|
343
742
|
filePath: {
|
|
344
|
-
fontSize: "
|
|
743
|
+
fontSize: "12px",
|
|
345
744
|
color: "#888",
|
|
346
|
-
fontFamily:
|
|
347
|
-
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
745
|
+
fontFamily: MONO_FONT,
|
|
348
746
|
wordBreak: "break-all",
|
|
349
747
|
},
|
|
748
|
+
|
|
350
749
|
dirPath: {
|
|
351
|
-
color: "#
|
|
750
|
+
color: "#666",
|
|
352
751
|
},
|
|
752
|
+
|
|
353
753
|
fileName: {
|
|
354
|
-
color: "#
|
|
754
|
+
color: "#7cc4eb",
|
|
355
755
|
},
|
|
756
|
+
|
|
356
757
|
lineCol: {
|
|
357
|
-
color: "#
|
|
758
|
+
color: "#e5b83a",
|
|
358
759
|
},
|
|
760
|
+
|
|
359
761
|
expandBtn: {
|
|
360
|
-
|
|
762
|
+
width: "100%",
|
|
763
|
+
padding: "14px 24px",
|
|
361
764
|
backgroundColor: "transparent",
|
|
362
|
-
color: "#
|
|
363
|
-
fontSize: "
|
|
765
|
+
color: "#777",
|
|
766
|
+
fontSize: "12px",
|
|
364
767
|
fontWeight: 500,
|
|
365
768
|
border: "none",
|
|
366
|
-
borderTop: "1px solid #
|
|
769
|
+
borderTop: "1px solid #2a2a2a",
|
|
367
770
|
cursor: "pointer",
|
|
368
771
|
textAlign: "left",
|
|
369
772
|
transition: "all 0.15s",
|
|
773
|
+
fontFamily: MONO_FONT,
|
|
774
|
+
},
|
|
775
|
+
|
|
776
|
+
nodeModulesToggle: {
|
|
777
|
+
display: "flex",
|
|
778
|
+
alignItems: "center",
|
|
779
|
+
gap: "10px",
|
|
780
|
+
width: "100%",
|
|
781
|
+
padding: "12px 24px",
|
|
782
|
+
backgroundColor: "#0a0a0a",
|
|
783
|
+
color: "#666",
|
|
784
|
+
fontSize: "11px",
|
|
785
|
+
fontWeight: 500,
|
|
786
|
+
border: "none",
|
|
787
|
+
borderTop: "1px solid #2a2a2a",
|
|
788
|
+
cursor: "pointer",
|
|
789
|
+
textAlign: "left",
|
|
790
|
+
fontFamily: MONO_FONT,
|
|
791
|
+
},
|
|
792
|
+
|
|
793
|
+
nodeModulesIcon: {
|
|
794
|
+
fontSize: "8px",
|
|
795
|
+
color: "#555",
|
|
796
|
+
},
|
|
797
|
+
|
|
798
|
+
nodeModulesLabel: {
|
|
799
|
+
flex: 1,
|
|
800
|
+
letterSpacing: "0.5px",
|
|
370
801
|
},
|
|
802
|
+
|
|
803
|
+
nodeModulesCount: {
|
|
804
|
+
color: "#555",
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
nodeModulesFrames: {
|
|
808
|
+
backgroundColor: "#080808",
|
|
809
|
+
},
|
|
810
|
+
|
|
811
|
+
footer: {
|
|
812
|
+
padding: "14px 24px",
|
|
813
|
+
borderTop: "1px solid #2a2a2a",
|
|
814
|
+
backgroundColor: "#0a0a0a",
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
footerText: {
|
|
818
|
+
fontSize: "11px",
|
|
819
|
+
color: "#555",
|
|
820
|
+
},
|
|
821
|
+
|
|
822
|
+
// Production styles
|
|
371
823
|
prodContainer: {
|
|
372
824
|
textAlign: "center",
|
|
373
825
|
padding: "60px 40px",
|
|
374
|
-
backgroundColor: "#
|
|
375
|
-
borderRadius: "
|
|
826
|
+
backgroundColor: "#0d0d0d",
|
|
827
|
+
borderRadius: "8px",
|
|
376
828
|
maxWidth: "400px",
|
|
829
|
+
border: "1px solid #333",
|
|
377
830
|
},
|
|
831
|
+
|
|
378
832
|
prodIcon: {
|
|
379
833
|
width: "64px",
|
|
380
834
|
height: "64px",
|
|
381
835
|
margin: "0 auto 24px",
|
|
382
|
-
|
|
383
|
-
borderRadius: "50%",
|
|
836
|
+
color: "#666",
|
|
384
837
|
display: "flex",
|
|
385
838
|
alignItems: "center",
|
|
386
839
|
justifyContent: "center",
|
|
387
|
-
fontSize: "32px",
|
|
388
|
-
fontWeight: 700,
|
|
389
|
-
color: "#fff",
|
|
390
840
|
},
|
|
841
|
+
|
|
391
842
|
prodTitle: {
|
|
392
843
|
margin: "0 0 12px",
|
|
393
|
-
fontSize: "
|
|
394
|
-
fontWeight:
|
|
395
|
-
color: "#
|
|
844
|
+
fontSize: "18px",
|
|
845
|
+
fontWeight: 500,
|
|
846
|
+
color: "#f0f0f0",
|
|
847
|
+
fontFamily: MONO_FONT,
|
|
396
848
|
},
|
|
849
|
+
|
|
397
850
|
prodMessage: {
|
|
398
851
|
margin: "0 0 28px",
|
|
399
|
-
fontSize: "
|
|
852
|
+
fontSize: "13px",
|
|
400
853
|
color: "#888",
|
|
401
854
|
lineHeight: 1.6,
|
|
855
|
+
fontFamily: MONO_FONT,
|
|
402
856
|
},
|
|
857
|
+
|
|
403
858
|
prodButton: {
|
|
404
859
|
padding: "12px 24px",
|
|
405
|
-
backgroundColor: "#
|
|
406
|
-
color: "#
|
|
407
|
-
fontSize: "
|
|
408
|
-
fontWeight:
|
|
409
|
-
|
|
410
|
-
|
|
860
|
+
backgroundColor: "#222",
|
|
861
|
+
color: "#bbb",
|
|
862
|
+
fontSize: "13px",
|
|
863
|
+
fontWeight: 500,
|
|
864
|
+
borderWidth: "1px",
|
|
865
|
+
borderStyle: "solid",
|
|
866
|
+
borderColor: "#444",
|
|
867
|
+
borderRadius: "6px",
|
|
411
868
|
cursor: "pointer",
|
|
869
|
+
transition: "all 0.15s",
|
|
870
|
+
fontFamily: MONO_FONT,
|
|
412
871
|
},
|
|
413
872
|
};
|