@ttsc/playground 0.19.0 → 0.19.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/lib/src/react/ConsoleViewer.js +17 -17
- package/lib/src/react/ConsoleViewer.js.map +1 -1
- package/lib/src/react/DependencyProgressModal.js +1 -1
- package/lib/src/react/DependencyProgressModal.js.map +1 -1
- package/lib/src/react/DiagnosticsPanel.js +2 -2
- package/lib/src/react/DiagnosticsPanel.js.map +1 -1
- package/lib/src/react/ExamplePicker.js +2 -2
- package/lib/src/react/ExamplePicker.js.map +1 -1
- package/lib/src/react/LintPane.js +3 -3
- package/lib/src/react/LintPane.js.map +1 -1
- package/lib/src/react/OptionsPanel.js +1 -1
- package/lib/src/react/OptionsPanel.js.map +1 -1
- package/lib/src/react/PlaygroundShell.js +14 -13
- package/lib/src/react/PlaygroundShell.js.map +1 -1
- package/lib/src/react/ResultViewer.d.ts +2 -2
- package/lib/src/react/ResultViewer.js +3 -3
- package/lib/src/react/ResultViewer.js.map +1 -1
- package/lib/src/react/SourceEditor.js +1 -1
- package/lib/src/react/SourceEditor.js.map +1 -1
- package/package.json +3 -3
- package/src/react/ConsoleViewer.tsx +18 -18
- package/src/react/DependencyProgressModal.tsx +11 -11
- package/src/react/DiagnosticsPanel.tsx +11 -11
- package/src/react/ExamplePicker.tsx +7 -7
- package/src/react/LintPane.tsx +5 -5
- package/src/react/OptionsPanel.tsx +10 -10
- package/src/react/PlaygroundShell.tsx +64 -74
- package/src/react/ResultViewer.tsx +4 -4
- package/src/react/SourceEditor.tsx +2 -3
|
@@ -15,7 +15,7 @@ export function ConsoleViewer({
|
|
|
15
15
|
}: ConsoleViewerProps) {
|
|
16
16
|
if (messages.length === 0)
|
|
17
17
|
return (
|
|
18
|
-
<div className="h-full w-full
|
|
18
|
+
<div className="flex h-full w-full items-center justify-center px-4 text-center font-mono text-[11px] text-slate-400">
|
|
19
19
|
{empty}
|
|
20
20
|
</div>
|
|
21
21
|
);
|
|
@@ -24,7 +24,7 @@ export function ConsoleViewer({
|
|
|
24
24
|
{messages.map((msg, i) => (
|
|
25
25
|
<div
|
|
26
26
|
key={i}
|
|
27
|
-
className="
|
|
27
|
+
className="flex gap-2 border-b border-[#d8e7f4] py-1 last:border-b-0"
|
|
28
28
|
>
|
|
29
29
|
<span
|
|
30
30
|
className={`shrink-0 text-[10px] uppercase tracking-wider w-12 ${typeColor(
|
|
@@ -33,7 +33,7 @@ export function ConsoleViewer({
|
|
|
33
33
|
>
|
|
34
34
|
{msg.type}
|
|
35
35
|
</span>
|
|
36
|
-
<span className="flex-1
|
|
36
|
+
<span className="flex-1 whitespace-pre-wrap break-words text-slate-700">
|
|
37
37
|
{(Array.isArray(msg.value) ? msg.value : [msg.value]).map(
|
|
38
38
|
(arg, idx) => (
|
|
39
39
|
<span key={idx}>
|
|
@@ -52,33 +52,33 @@ export function ConsoleViewer({
|
|
|
52
52
|
function typeColor(type: IConsoleMessage["type"]): string {
|
|
53
53
|
switch (type) {
|
|
54
54
|
case "error":
|
|
55
|
-
return "text-red-
|
|
55
|
+
return "text-red-600";
|
|
56
56
|
case "warn":
|
|
57
|
-
return "text-
|
|
57
|
+
return "text-amber-600";
|
|
58
58
|
case "info":
|
|
59
|
-
return "text-sky-
|
|
59
|
+
return "text-sky-700";
|
|
60
60
|
case "debug":
|
|
61
|
-
return "text-fuchsia-
|
|
61
|
+
return "text-fuchsia-700";
|
|
62
62
|
case "dir":
|
|
63
63
|
case "table":
|
|
64
|
-
return "text-cyan-
|
|
64
|
+
return "text-cyan-700";
|
|
65
65
|
default:
|
|
66
|
-
return "text-emerald-
|
|
66
|
+
return "text-emerald-700";
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function formatValue(value: unknown, depth = 0): JSX.Element {
|
|
71
71
|
if (typeof value === "string")
|
|
72
|
-
return <span className="text-amber-
|
|
72
|
+
return <span className="text-amber-700">{JSON.stringify(value)}</span>;
|
|
73
73
|
if (typeof value === "number")
|
|
74
|
-
return <span className="text-purple-
|
|
74
|
+
return <span className="text-purple-700">{String(value)}</span>;
|
|
75
75
|
if (typeof value === "boolean")
|
|
76
|
-
return <span className="text-sky-
|
|
77
|
-
if (value === null) return <span className="text-
|
|
76
|
+
return <span className="text-sky-700">{String(value)}</span>;
|
|
77
|
+
if (value === null) return <span className="text-slate-500">null</span>;
|
|
78
78
|
if (value === undefined)
|
|
79
|
-
return <span className="text-
|
|
79
|
+
return <span className="text-slate-500">undefined</span>;
|
|
80
80
|
if (typeof value === "function")
|
|
81
|
-
return <span className="text-
|
|
81
|
+
return <span className="text-slate-500">[Function]</span>;
|
|
82
82
|
if (Array.isArray(value))
|
|
83
83
|
return (
|
|
84
84
|
<span>
|
|
@@ -94,21 +94,21 @@ function formatValue(value: unknown, depth = 0): JSX.Element {
|
|
|
94
94
|
);
|
|
95
95
|
if (value instanceof Error)
|
|
96
96
|
return (
|
|
97
|
-
<span className="text-red-
|
|
97
|
+
<span className="text-red-700">
|
|
98
98
|
{value.name}: {value.message}
|
|
99
99
|
</span>
|
|
100
100
|
);
|
|
101
101
|
try {
|
|
102
102
|
const entries = Object.entries(value as Record<string, unknown>);
|
|
103
103
|
if (entries.length === 0) return <span>{"{}"}</span>;
|
|
104
|
-
if (depth > 4) return <span className="text-
|
|
104
|
+
if (depth > 4) return <span className="text-slate-500">[...]</span>;
|
|
105
105
|
return (
|
|
106
106
|
<span>
|
|
107
107
|
{"{"}
|
|
108
108
|
{entries.map(([k, v], idx) => (
|
|
109
109
|
<span key={k}>
|
|
110
110
|
{idx > 0 ? ", " : " "}
|
|
111
|
-
<span className="text-
|
|
111
|
+
<span className="text-[#3178c6]">{k}</span>:{" "}
|
|
112
112
|
{formatValue(v, depth + 1)}
|
|
113
113
|
</span>
|
|
114
114
|
))}
|
|
@@ -20,34 +20,34 @@ export function DependencyProgressModal({
|
|
|
20
20
|
: progress.packageName;
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-
|
|
24
|
-
<div className="w-full max-w-md rounded-
|
|
23
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-[#102a43]/35 px-4 backdrop-blur-sm">
|
|
24
|
+
<div className="w-full max-w-md rounded-2xl border border-[#b9d5ee] bg-white p-5 shadow-[0_28px_70px_rgba(35,90,151,0.25)]">
|
|
25
25
|
<div className="flex items-start justify-between gap-4">
|
|
26
26
|
<div>
|
|
27
|
-
<div className="text-[11px]
|
|
27
|
+
<div className="font-mono text-[11px] uppercase text-[#3178c6]">
|
|
28
28
|
Dependencies
|
|
29
29
|
</div>
|
|
30
|
-
<h2 className="mt-1
|
|
30
|
+
<h2 className="mt-1 font-mono text-base text-[#102a43]">
|
|
31
31
|
Installing npm packages
|
|
32
32
|
</h2>
|
|
33
33
|
</div>
|
|
34
|
-
<div className="text-[11px]
|
|
34
|
+
<div className="font-mono text-[11px] text-slate-500">
|
|
35
35
|
{progress.completed}/{total}
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
|
-
<div className="mt-4 h-2 overflow-hidden rounded-full bg-
|
|
39
|
+
<div className="mt-4 h-2 overflow-hidden rounded-full bg-[#e7f0f8]">
|
|
40
40
|
<div
|
|
41
|
-
className="h-full bg-
|
|
41
|
+
className="h-full bg-[#3178c6] transition-[width]"
|
|
42
42
|
style={{ width: `${Math.max(8, ratio * 100)}%` }}
|
|
43
43
|
/>
|
|
44
44
|
</div>
|
|
45
45
|
|
|
46
46
|
<div className="mt-4 space-y-1 font-mono">
|
|
47
47
|
{activePackage && (
|
|
48
|
-
<div className="text-[12px] text-
|
|
48
|
+
<div className="text-[12px] text-slate-700">{activePackage}</div>
|
|
49
49
|
)}
|
|
50
|
-
<div className="text-[11px] text-
|
|
50
|
+
<div className="text-[11px] text-slate-500">{progress.message}</div>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
53
|
{packages.length > 0 && (
|
|
@@ -55,13 +55,13 @@ export function DependencyProgressModal({
|
|
|
55
55
|
{packages.slice(0, 8).map((name) => (
|
|
56
56
|
<span
|
|
57
57
|
key={name}
|
|
58
|
-
className="rounded border border-
|
|
58
|
+
className="rounded border border-[#d2e4f4] bg-[#f7fbff] px-2 py-1 font-mono text-[10px] text-slate-600"
|
|
59
59
|
>
|
|
60
60
|
{name}
|
|
61
61
|
</span>
|
|
62
62
|
))}
|
|
63
63
|
{packages.length > 8 && (
|
|
64
|
-
<span className="rounded border border-
|
|
64
|
+
<span className="rounded border border-[#d2e4f4] bg-[#f7fbff] px-2 py-1 font-mono text-[10px] text-slate-500">
|
|
65
65
|
+{packages.length - 8}
|
|
66
66
|
</span>
|
|
67
67
|
)}
|
|
@@ -15,19 +15,19 @@ export function DiagnosticsPanel({
|
|
|
15
15
|
|
|
16
16
|
if (diagnostics.length === 0)
|
|
17
17
|
return (
|
|
18
|
-
<div className="shrink-0
|
|
18
|
+
<div className="flex shrink-0 items-center gap-3 border-t border-[#c7dff4] bg-[#eef6ff] px-4 py-2">
|
|
19
19
|
<span className="text-emerald-400 text-xs">●</span>
|
|
20
|
-
<span className="text-[12px]
|
|
20
|
+
<span className="font-mono text-[12px] text-slate-600">
|
|
21
21
|
0 errors · 0 warnings
|
|
22
22
|
</span>
|
|
23
23
|
</div>
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
return (
|
|
27
|
-
<div className="shrink-0 border-t border-
|
|
27
|
+
<div className="shrink-0 border-t border-[#c7dff4] bg-[#eef6ff]">
|
|
28
28
|
<button
|
|
29
29
|
onClick={() => setExpanded((v) => !v)}
|
|
30
|
-
className="w-full px-4 py-2
|
|
30
|
+
className="flex w-full items-center gap-3 px-4 py-2 text-left transition-colors hover:bg-[#e1effc]"
|
|
31
31
|
>
|
|
32
32
|
<span
|
|
33
33
|
className={`text-xs ${
|
|
@@ -36,20 +36,20 @@ export function DiagnosticsPanel({
|
|
|
36
36
|
>
|
|
37
37
|
●
|
|
38
38
|
</span>
|
|
39
|
-
<span className="text-[12px]
|
|
39
|
+
<span className="font-mono text-[12px] text-slate-700">
|
|
40
40
|
{errorCount} error{errorCount === 1 ? "" : "s"} · {warnCount} warning
|
|
41
41
|
{warnCount === 1 ? "" : "s"}
|
|
42
42
|
</span>
|
|
43
|
-
<span className="ml-auto text-[10px]
|
|
43
|
+
<span className="ml-auto font-mono text-[10px] text-slate-400">
|
|
44
44
|
{expanded ? "▲ collapse" : "▼ expand"}
|
|
45
45
|
</span>
|
|
46
46
|
</button>
|
|
47
47
|
{expanded && (
|
|
48
|
-
<div className="
|
|
48
|
+
<div className="max-h-48 overflow-auto border-t border-[#c7dff4]">
|
|
49
49
|
{diagnostics.map((d, i) => (
|
|
50
50
|
<div
|
|
51
51
|
key={i}
|
|
52
|
-
className="
|
|
52
|
+
className="flex gap-3 border-b border-[#d8e7f4] px-4 py-2 font-mono text-[12px] last:border-b-0"
|
|
53
53
|
>
|
|
54
54
|
<span
|
|
55
55
|
className={`shrink-0 ${
|
|
@@ -58,13 +58,13 @@ export function DiagnosticsPanel({
|
|
|
58
58
|
>
|
|
59
59
|
{d.severity === "error" ? "✗" : "!"}
|
|
60
60
|
</span>
|
|
61
|
-
<span className="shrink-0 text-
|
|
61
|
+
<span className="w-16 shrink-0 text-slate-500">
|
|
62
62
|
{d.line}:{d.column}
|
|
63
63
|
</span>
|
|
64
|
-
<span className="shrink-0 text-
|
|
64
|
+
<span className="w-16 shrink-0 text-slate-400">
|
|
65
65
|
{d.code ?? ""}
|
|
66
66
|
</span>
|
|
67
|
-
<span className="text-
|
|
67
|
+
<span className="text-slate-700">{d.message}</span>
|
|
68
68
|
</div>
|
|
69
69
|
))}
|
|
70
70
|
</div>
|
|
@@ -52,19 +52,19 @@ export function ExamplePicker({
|
|
|
52
52
|
<button
|
|
53
53
|
data-playground-examples-toggle
|
|
54
54
|
onClick={() => setOpen((v) => !v)}
|
|
55
|
-
className="px-3 py-1.5
|
|
55
|
+
className="rounded-md border border-[#b9d5ee] bg-white px-3 py-1.5 font-mono text-xs text-[#235a97] transition-colors hover:border-[#3178c6] hover:bg-[#eaf4ff]"
|
|
56
56
|
title="Cmd/Ctrl+K"
|
|
57
57
|
>
|
|
58
58
|
Examples ▾
|
|
59
59
|
</button>
|
|
60
60
|
{open && (
|
|
61
|
-
<div className="absolute right-0 top-full mt-2 w-80 rounded-
|
|
61
|
+
<div className="absolute right-0 top-full z-10 mt-2 w-80 overflow-hidden rounded-xl border border-[#b9d5ee] bg-white shadow-[0_14px_42px_rgba(49,120,198,0.18)]">
|
|
62
62
|
{Object.entries(grouped).map(([group, items]) => (
|
|
63
63
|
<div
|
|
64
64
|
key={group}
|
|
65
|
-
className="border-b border-
|
|
65
|
+
className="border-b border-[#d8e7f4] last:border-b-0"
|
|
66
66
|
>
|
|
67
|
-
<div className="px-3 py-1.5 text-[10px]
|
|
67
|
+
<div className="bg-[#f7fbff] px-3 py-1.5 font-mono text-[10px] uppercase tracking-wider text-slate-500">
|
|
68
68
|
{groupLabels?.[group] ?? group}
|
|
69
69
|
</div>
|
|
70
70
|
{items.map((item) => (
|
|
@@ -74,12 +74,12 @@ export function ExamplePicker({
|
|
|
74
74
|
onPick(item.id);
|
|
75
75
|
setOpen(false);
|
|
76
76
|
}}
|
|
77
|
-
className="w-full
|
|
77
|
+
className="w-full px-3 py-2 text-left transition-colors hover:bg-[#eaf4ff]"
|
|
78
78
|
>
|
|
79
|
-
<div className="text-[12px]
|
|
79
|
+
<div className="font-mono text-[12px] text-[#102a43]">
|
|
80
80
|
{item.title}
|
|
81
81
|
</div>
|
|
82
|
-
<div className="text-[10px] text-
|
|
82
|
+
<div className="mt-0.5 text-[10px] leading-snug text-slate-500">
|
|
83
83
|
{item.description}
|
|
84
84
|
</div>
|
|
85
85
|
</button>
|
package/src/react/LintPane.tsx
CHANGED
|
@@ -15,10 +15,10 @@ export function LintPane({
|
|
|
15
15
|
}) {
|
|
16
16
|
if (diagnostics.length === 0)
|
|
17
17
|
return (
|
|
18
|
-
<div className="flex flex-col items-center justify-center
|
|
18
|
+
<div className="flex h-full flex-col items-center justify-center gap-2 font-mono text-sm text-slate-500">
|
|
19
19
|
<span className="text-emerald-400 text-xl">✓</span>
|
|
20
20
|
<span>No lint diagnostics.</span>
|
|
21
|
-
<span className="text-[10px] text-
|
|
21
|
+
<span className="max-w-xs text-center text-[10px] text-slate-400">
|
|
22
22
|
{emptyHint}
|
|
23
23
|
</span>
|
|
24
24
|
</div>
|
|
@@ -28,7 +28,7 @@ export function LintPane({
|
|
|
28
28
|
{diagnostics.map((d, i) => (
|
|
29
29
|
<div
|
|
30
30
|
key={i}
|
|
31
|
-
className="flex gap-3
|
|
31
|
+
className="flex gap-3 rounded-lg border border-[#d2e4f4] bg-[#f7fbff] p-3"
|
|
32
32
|
>
|
|
33
33
|
<span
|
|
34
34
|
className={`mt-0.5 text-[10px] font-mono px-1.5 py-0.5 rounded shrink-0 ${
|
|
@@ -40,14 +40,14 @@ export function LintPane({
|
|
|
40
40
|
{d.severity}
|
|
41
41
|
</span>
|
|
42
42
|
<div className="flex-1 min-w-0">
|
|
43
|
-
<div className="flex items-center gap-2 text-[11px]
|
|
43
|
+
<div className="mb-1 flex items-center gap-2 font-mono text-[11px] text-slate-500">
|
|
44
44
|
<span>{d.code}</span>
|
|
45
45
|
<span>·</span>
|
|
46
46
|
<span>
|
|
47
47
|
{d.line}:{d.column}
|
|
48
48
|
</span>
|
|
49
49
|
</div>
|
|
50
|
-
<div className="text-[13px] text-
|
|
50
|
+
<div className="font-mono text-[13px] text-slate-700">
|
|
51
51
|
{d.message}
|
|
52
52
|
</div>
|
|
53
53
|
</div>
|
|
@@ -62,7 +62,7 @@ export function OptionsPanel({
|
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
64
|
<div
|
|
65
|
-
className="fixed inset-0 z-50 bg-
|
|
65
|
+
className="fixed inset-0 z-50 flex items-center justify-center bg-[#102a43]/35 backdrop-blur-sm"
|
|
66
66
|
onClick={onClose}
|
|
67
67
|
>
|
|
68
68
|
<div
|
|
@@ -70,19 +70,19 @@ export function OptionsPanel({
|
|
|
70
70
|
role="dialog"
|
|
71
71
|
aria-modal="true"
|
|
72
72
|
aria-labelledby="playground-options-title"
|
|
73
|
-
className="w-[480px] max-w-[90vw] rounded-2xl border border-
|
|
73
|
+
className="w-[480px] max-w-[90vw] rounded-2xl border border-[#b9d5ee] bg-white shadow-[0_30px_80px_rgba(35,90,151,0.25)]"
|
|
74
74
|
onClick={(e) => e.stopPropagation()}
|
|
75
75
|
>
|
|
76
|
-
<div className="flex items-center justify-between
|
|
76
|
+
<div className="flex items-center justify-between border-b border-[#c7dff4] bg-[#f7fbff] px-6 py-4">
|
|
77
77
|
<h2
|
|
78
78
|
id="playground-options-title"
|
|
79
|
-
className="text-base font-semibold text-
|
|
79
|
+
className="text-base font-semibold text-[#102a43]"
|
|
80
80
|
>
|
|
81
81
|
{title}
|
|
82
82
|
</h2>
|
|
83
83
|
<button
|
|
84
84
|
onClick={onClose}
|
|
85
|
-
className="text-
|
|
85
|
+
className="text-slate-400 transition-colors hover:text-[#235a97]"
|
|
86
86
|
aria-label="Close options"
|
|
87
87
|
>
|
|
88
88
|
✕
|
|
@@ -90,7 +90,7 @@ export function OptionsPanel({
|
|
|
90
90
|
</div>
|
|
91
91
|
<div className="p-6 space-y-6">
|
|
92
92
|
<div>
|
|
93
|
-
<div className="text-[10px]
|
|
93
|
+
<div className="mb-3 font-mono text-[10px] uppercase tracking-wider text-slate-500">
|
|
94
94
|
Plugins
|
|
95
95
|
</div>
|
|
96
96
|
<div className="space-y-3">
|
|
@@ -106,10 +106,10 @@ export function OptionsPanel({
|
|
|
106
106
|
className="mt-1 w-4 h-4 accent-blue-500"
|
|
107
107
|
/>
|
|
108
108
|
<div className="flex-1">
|
|
109
|
-
<div className="
|
|
109
|
+
<div className="font-mono text-sm text-slate-800 transition-colors group-hover:text-[#235a97]">
|
|
110
110
|
{t.label}
|
|
111
111
|
</div>
|
|
112
|
-
<div className="text-[11px] text-
|
|
112
|
+
<div className="text-[11px] leading-snug text-slate-500">
|
|
113
113
|
{t.description}
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
@@ -118,10 +118,10 @@ export function OptionsPanel({
|
|
|
118
118
|
</div>
|
|
119
119
|
</div>
|
|
120
120
|
</div>
|
|
121
|
-
<div className="
|
|
121
|
+
<div className="flex justify-end border-t border-[#c7dff4] bg-[#f7fbff] px-6 py-4">
|
|
122
122
|
<button
|
|
123
123
|
onClick={onClose}
|
|
124
|
-
className="px-4 py-2
|
|
124
|
+
className="rounded-md bg-[#3178c6] px-4 py-2 font-mono text-xs text-white transition-colors hover:bg-[#235a97]"
|
|
125
125
|
>
|
|
126
126
|
Done
|
|
127
127
|
</button>
|
|
@@ -15,7 +15,6 @@ import type { IPlaygroundDependencyProgress } from "../structures/IPlaygroundDep
|
|
|
15
15
|
import type { IPlaygroundShellProps } from "../structures/IPlaygroundShellProps";
|
|
16
16
|
import type { ITransformOptions } from "../structures/ITransformOptions";
|
|
17
17
|
import { ConsoleViewer } from "./ConsoleViewer";
|
|
18
|
-
import { createCompilerClient } from "./createCompilerClient";
|
|
19
18
|
import { DEFAULT_OPTION_TOGGLES } from "./DEFAULT_OPTION_TOGGLES";
|
|
20
19
|
import { DependencyProgressModal } from "./DependencyProgressModal";
|
|
21
20
|
import { DiagnosticsPanel } from "./DiagnosticsPanel";
|
|
@@ -24,6 +23,7 @@ import { LintPane } from "./LintPane";
|
|
|
24
23
|
import { OptionsPanel } from "./OptionsPanel";
|
|
25
24
|
import { ResultViewer } from "./ResultViewer";
|
|
26
25
|
import { SourceEditor } from "./SourceEditor";
|
|
26
|
+
import { createCompilerClient } from "./createCompilerClient";
|
|
27
27
|
|
|
28
28
|
const DEFAULT_OPTIONS: ITransformOptions = {
|
|
29
29
|
typia: true,
|
|
@@ -64,9 +64,7 @@ export function PlaygroundShell({
|
|
|
64
64
|
const [optionsOpen, setOptionsOpen] = useState(false);
|
|
65
65
|
const [running, setRunning] = useState(false);
|
|
66
66
|
const [shareToast, setShareToast] = useState(false);
|
|
67
|
-
const [consoleMessages, setConsoleMessages] = useState<IConsoleMessage[]>(
|
|
68
|
-
[],
|
|
69
|
-
);
|
|
67
|
+
const [consoleMessages, setConsoleMessages] = useState<IConsoleMessage[]>([]);
|
|
70
68
|
const [executing, setExecuting] = useState(false);
|
|
71
69
|
const [bootError, setBootError] = useState<unknown>(null);
|
|
72
70
|
const [bootPhase, setBootPhase] = useState<"booting" | "ready" | "failed">(
|
|
@@ -284,8 +282,7 @@ export function PlaygroundShell({
|
|
|
284
282
|
}, 2400);
|
|
285
283
|
return error;
|
|
286
284
|
} finally {
|
|
287
|
-
if (dependencyAbort.current === abort)
|
|
288
|
-
dependencyAbort.current = null;
|
|
285
|
+
if (dependencyAbort.current === abort) dependencyAbort.current = null;
|
|
289
286
|
}
|
|
290
287
|
});
|
|
291
288
|
dependencyInstallChain.current = task.then(() => {});
|
|
@@ -302,11 +299,7 @@ export function PlaygroundShell({
|
|
|
302
299
|
// pane is rendered. Re-running the wasm-heavy pipeline on every tab
|
|
303
300
|
// click would burn multiple seconds of work per click.
|
|
304
301
|
const run = useCallback(
|
|
305
|
-
async (
|
|
306
|
-
input: string,
|
|
307
|
-
opts: ITransformOptions,
|
|
308
|
-
version: number,
|
|
309
|
-
) => {
|
|
302
|
+
async (input: string, opts: ITransformOptions, version: number) => {
|
|
310
303
|
const epoch = ++compileEpoch.current;
|
|
311
304
|
setRunning(true);
|
|
312
305
|
try {
|
|
@@ -544,7 +537,12 @@ export function PlaygroundShell({
|
|
|
544
537
|
// React state, so including `source` here would re-create the
|
|
545
538
|
// callback per keystroke and propagate into the global keydown
|
|
546
539
|
// useEffect, churning event-listener add/remove every character.
|
|
547
|
-
}, [
|
|
540
|
+
}, [
|
|
541
|
+
createCompilerService,
|
|
542
|
+
executeBundle,
|
|
543
|
+
installDependenciesForSource,
|
|
544
|
+
options,
|
|
545
|
+
]);
|
|
548
546
|
|
|
549
547
|
const allDiagnostics = useMemo(() => {
|
|
550
548
|
const fromCompile: ICompilerService.IDiagnostic[] = [];
|
|
@@ -602,10 +600,10 @@ export function PlaygroundShell({
|
|
|
602
600
|
|
|
603
601
|
if (bootPhase === "failed") {
|
|
604
602
|
return (
|
|
605
|
-
<div className="flex
|
|
603
|
+
<div className="flex h-screen w-full flex-col items-center justify-center gap-5 bg-[#f7fbff] px-6 text-center text-[#102a43]">
|
|
606
604
|
<span className="text-red-400 text-3xl">⚠</span>
|
|
607
605
|
<h1 className="text-lg font-mono">Playground failed to boot.</h1>
|
|
608
|
-
<pre className="max-w-xl
|
|
606
|
+
<pre className="max-w-xl whitespace-pre-wrap break-words font-mono text-[12px] text-slate-500">
|
|
609
607
|
{(() => {
|
|
610
608
|
const e = bootError;
|
|
611
609
|
if (e instanceof Error) return `${e.name}: ${e.message}`;
|
|
@@ -639,7 +637,7 @@ export function PlaygroundShell({
|
|
|
639
637
|
}
|
|
640
638
|
})();
|
|
641
639
|
}}
|
|
642
|
-
className="px-5 py-2
|
|
640
|
+
className="rounded-md bg-[#3178c6] px-5 py-2 font-mono text-xs text-white shadow-[0_8px_22px_rgba(49,120,198,0.24)] transition-colors hover:bg-[#235a97]"
|
|
643
641
|
>
|
|
644
642
|
Retry
|
|
645
643
|
</button>
|
|
@@ -648,54 +646,28 @@ export function PlaygroundShell({
|
|
|
648
646
|
}
|
|
649
647
|
|
|
650
648
|
return (
|
|
651
|
-
<div className="flex
|
|
652
|
-
{/* ── Toolbar ── */}
|
|
653
|
-
<div className="flex items-center gap-3 px-4 py-2.5 border-b border-neutral-800/70 bg-neutral-950 shrink-0">
|
|
654
|
-
{brand}
|
|
655
|
-
<span className="text-neutral-700">/</span>
|
|
656
|
-
<span className="text-sm text-neutral-400">Playground</span>
|
|
657
|
-
|
|
658
|
-
<div className="ml-auto flex items-center gap-2">
|
|
659
|
-
<ExamplePicker
|
|
660
|
-
examples={examples}
|
|
661
|
-
onPick={onPickExample}
|
|
662
|
-
groupLabels={exampleGroupLabels}
|
|
663
|
-
/>
|
|
664
|
-
<button
|
|
665
|
-
onClick={() => setOptionsOpen((v) => !v)}
|
|
666
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-300 border border-neutral-800 rounded-md hover:border-neutral-600 hover:bg-neutral-900 transition-colors"
|
|
667
|
-
>
|
|
668
|
-
Options
|
|
669
|
-
</button>
|
|
670
|
-
<button
|
|
671
|
-
onClick={onReset}
|
|
672
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-400 hover:text-neutral-200 transition-colors"
|
|
673
|
-
>
|
|
674
|
-
Reset
|
|
675
|
-
</button>
|
|
676
|
-
<button
|
|
677
|
-
onClick={onShare}
|
|
678
|
-
className="px-3 py-1.5 text-xs font-mono text-neutral-900 bg-white rounded-md hover:shadow-[0_0_30px_rgba(255,255,255,0.2)] transition-shadow"
|
|
679
|
-
>
|
|
680
|
-
{shareToast ? "Copied ✓" : "Share"}
|
|
681
|
-
</button>
|
|
682
|
-
</div>
|
|
683
|
-
</div>
|
|
684
|
-
|
|
649
|
+
<div className="flex h-screen w-full flex-col overflow-hidden bg-white text-[#102a43]">
|
|
685
650
|
{sourceFromURL && (
|
|
686
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
651
|
+
<div className="shrink-0 border-b border-amber-300 bg-amber-50 px-4 py-1.5 font-mono text-[11px] text-amber-800">
|
|
687
652
|
Source loaded from share URL. Hit Reset to return to the default
|
|
688
653
|
example.
|
|
689
654
|
</div>
|
|
690
655
|
)}
|
|
691
656
|
|
|
692
657
|
{shareWarn && (
|
|
693
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
658
|
+
<div className="shrink-0 border-b border-amber-300 bg-amber-50 px-4 py-1.5 font-mono text-[11px] text-amber-800">
|
|
694
659
|
{shareWarn}
|
|
695
660
|
</div>
|
|
696
661
|
)}
|
|
697
662
|
|
|
698
|
-
<div className="flex items-center gap-0 border-b border-
|
|
663
|
+
<div className="flex shrink-0 flex-wrap items-center gap-0 border-b border-[#c7dff4] bg-white">
|
|
664
|
+
{brand ? (
|
|
665
|
+
<div className="flex items-center gap-2 border-r border-[#c7dff4] px-4 py-2">
|
|
666
|
+
{brand}
|
|
667
|
+
<span className="text-[#9db6cb]">/</span>
|
|
668
|
+
<span className="text-sm text-[#526b82]">Playground</span>
|
|
669
|
+
</div>
|
|
670
|
+
) : null}
|
|
699
671
|
{(
|
|
700
672
|
[
|
|
701
673
|
{ id: "javascript", label: "Compiled JS" },
|
|
@@ -707,29 +679,47 @@ export function PlaygroundShell({
|
|
|
707
679
|
onClick={() => setTarget(tab.id)}
|
|
708
680
|
className={`px-4 py-2 text-[12px] font-mono border-b-2 transition-colors ${
|
|
709
681
|
target === tab.id
|
|
710
|
-
? "
|
|
711
|
-
: "text-
|
|
682
|
+
? "border-[#3178c6] text-[#235a97]"
|
|
683
|
+
: "border-transparent text-slate-400 hover:text-[#3178c6]"
|
|
712
684
|
}`}
|
|
713
685
|
>
|
|
714
686
|
{tab.label}
|
|
715
687
|
</button>
|
|
716
688
|
))}
|
|
717
|
-
<div className="ml-auto
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
689
|
+
<div className="ml-auto flex w-full items-center justify-end gap-2 border-t border-[#d8e7f4] px-4 py-1.5 sm:w-auto sm:border-t-0 sm:pl-0">
|
|
690
|
+
<ExamplePicker
|
|
691
|
+
examples={examples}
|
|
692
|
+
onPick={onPickExample}
|
|
693
|
+
groupLabels={exampleGroupLabels}
|
|
694
|
+
/>
|
|
695
|
+
<button
|
|
696
|
+
onClick={() => setOptionsOpen((v) => !v)}
|
|
697
|
+
className="rounded-md border border-[#b9d5ee] bg-white px-3 py-1.5 font-mono text-xs text-[#235a97] transition-colors hover:border-[#3178c6] hover:bg-[#eaf4ff]"
|
|
698
|
+
>
|
|
699
|
+
Options
|
|
700
|
+
</button>
|
|
701
|
+
<button
|
|
702
|
+
onClick={onReset}
|
|
703
|
+
className="px-3 py-1.5 font-mono text-xs text-slate-500 transition-colors hover:text-[#235a97]"
|
|
704
|
+
>
|
|
705
|
+
Reset
|
|
706
|
+
</button>
|
|
707
|
+
<button
|
|
708
|
+
onClick={onShare}
|
|
709
|
+
className="rounded-md bg-[#3178c6] px-3 py-1.5 font-mono text-xs text-white shadow-[0_6px_16px_rgba(49,120,198,0.22)] transition-colors hover:bg-[#235a97]"
|
|
710
|
+
>
|
|
711
|
+
{shareToast ? "Copied ✓" : "Share"}
|
|
712
|
+
</button>
|
|
723
713
|
</div>
|
|
724
714
|
</div>
|
|
725
715
|
|
|
726
716
|
<div className="flex flex-1 min-h-0 flex-col md:flex-row">
|
|
727
|
-
<div className="flex-1 min-w-0
|
|
728
|
-
<div className="flex items-center justify-between
|
|
729
|
-
<span className="text-[11px]
|
|
717
|
+
<div className="flex h-1/2 min-w-0 flex-1 flex-col border-[#c7dff4] md:h-full md:border-r">
|
|
718
|
+
<div className="flex items-center justify-between border-b border-[#c7dff4] bg-[#eef6ff] px-4 py-1.5">
|
|
719
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
730
720
|
src/playground.ts
|
|
731
721
|
</span>
|
|
732
|
-
<span className="text-[10px]
|
|
722
|
+
<span className="font-mono text-[10px] text-slate-400">
|
|
733
723
|
{source.split("\n").length} lines
|
|
734
724
|
</span>
|
|
735
725
|
</div>
|
|
@@ -742,14 +732,14 @@ export function PlaygroundShell({
|
|
|
742
732
|
</div>
|
|
743
733
|
</div>
|
|
744
734
|
|
|
745
|
-
<div className="flex-1 min-w-0 flex flex-col
|
|
746
|
-
<div className="flex items-center justify-between
|
|
747
|
-
<span className="text-[11px]
|
|
735
|
+
<div className="flex h-1/2 min-w-0 flex-1 flex-col border-t border-[#c7dff4] md:h-full md:border-t-0">
|
|
736
|
+
<div className="flex items-center justify-between border-b border-[#c7dff4] bg-[#eef6ff] px-4 py-1.5">
|
|
737
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
748
738
|
{target === "javascript"
|
|
749
739
|
? resultCaption(options)
|
|
750
740
|
: "lint diagnostics"}
|
|
751
741
|
</span>
|
|
752
|
-
<span className="text-[10px]
|
|
742
|
+
<span className="font-mono text-[10px] text-slate-400">
|
|
753
743
|
{result?.type === "error" ? "error" : ""}
|
|
754
744
|
</span>
|
|
755
745
|
</div>
|
|
@@ -774,22 +764,22 @@ export function PlaygroundShell({
|
|
|
774
764
|
</div>
|
|
775
765
|
|
|
776
766
|
{bundleError && (
|
|
777
|
-
<div className="shrink-0 px-4 py-1.5 text-[11px]
|
|
767
|
+
<div className="shrink-0 border-t border-red-300 bg-red-50 px-4 py-1.5 font-mono text-[11px] text-red-700">
|
|
778
768
|
Bundle failed — {bundleError}
|
|
779
769
|
</div>
|
|
780
770
|
)}
|
|
781
771
|
|
|
782
772
|
{executeBundle && (
|
|
783
|
-
<div className="shrink-0 border-t border-
|
|
784
|
-
<div className="flex items-center justify-between px-4 py-1.5
|
|
785
|
-
<span className="text-[11px]
|
|
773
|
+
<div className="flex h-48 shrink-0 flex-col border-t border-[#c7dff4] bg-[#f7fbff]">
|
|
774
|
+
<div className="flex items-center justify-between border-b border-[#d8e7f4] px-4 py-1.5">
|
|
775
|
+
<span className="font-mono text-[11px] text-slate-500">
|
|
786
776
|
console output
|
|
787
777
|
</span>
|
|
788
778
|
<div className="flex items-center gap-2">
|
|
789
779
|
{consoleMessages.length > 0 && (
|
|
790
780
|
<button
|
|
791
781
|
onClick={() => setConsoleMessages([])}
|
|
792
|
-
className="px-2 py-1 text-[10px]
|
|
782
|
+
className="px-2 py-1 font-mono text-[10px] text-slate-500 transition-colors hover:text-[#235a97]"
|
|
793
783
|
>
|
|
794
784
|
Clear
|
|
795
785
|
</button>
|
|
@@ -797,7 +787,7 @@ export function PlaygroundShell({
|
|
|
797
787
|
<button
|
|
798
788
|
onClick={onExecute}
|
|
799
789
|
disabled={executing}
|
|
800
|
-
className="px-3 py-1 text-[11px]
|
|
790
|
+
className="rounded-md bg-[#3178c6] px-3 py-1 font-mono text-[11px] text-white transition-colors hover:bg-[#235a97] disabled:opacity-50"
|
|
801
791
|
title="Cmd/Ctrl+Enter"
|
|
802
792
|
>
|
|
803
793
|
▶ {executing ? "Executing…" : "Execute"}
|