@ttsc/playground 0.19.0 → 0.19.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.
@@ -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 flex items-center justify-center text-neutral-600 font-mono text-[11px] px-4 text-center">
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="py-1 border-b border-neutral-900/70 last:border-b-0 flex gap-2"
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 break-words whitespace-pre-wrap text-neutral-200">
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-400";
55
+ return "text-red-600";
56
56
  case "warn":
57
- return "text-yellow-400";
57
+ return "text-amber-600";
58
58
  case "info":
59
- return "text-sky-400";
59
+ return "text-sky-700";
60
60
  case "debug":
61
- return "text-fuchsia-400";
61
+ return "text-fuchsia-700";
62
62
  case "dir":
63
63
  case "table":
64
- return "text-cyan-400";
64
+ return "text-cyan-700";
65
65
  default:
66
- return "text-emerald-400";
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-200">{JSON.stringify(value)}</span>;
72
+ return <span className="text-amber-700">{JSON.stringify(value)}</span>;
73
73
  if (typeof value === "number")
74
- return <span className="text-purple-300">{String(value)}</span>;
74
+ return <span className="text-purple-700">{String(value)}</span>;
75
75
  if (typeof value === "boolean")
76
- return <span className="text-sky-300">{String(value)}</span>;
77
- if (value === null) return <span className="text-neutral-500">null</span>;
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-neutral-500">undefined</span>;
79
+ return <span className="text-slate-500">undefined</span>;
80
80
  if (typeof value === "function")
81
- return <span className="text-neutral-500">[Function]</span>;
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-300">
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-neutral-500">[...]</span>;
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-blue-300">{k}</span>:{" "}
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-black/70 px-4">
24
- <div className="w-full max-w-md rounded-lg border border-neutral-700 bg-neutral-950 p-5 shadow-2xl">
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] font-mono uppercase text-blue-300">
27
+ <div className="font-mono text-[11px] uppercase text-[#3178c6]">
28
28
  Dependencies
29
29
  </div>
30
- <h2 className="mt-1 text-base font-mono text-white">
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] font-mono text-neutral-500">
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-neutral-800">
39
+ <div className="mt-4 h-2 overflow-hidden rounded-full bg-[#e7f0f8]">
40
40
  <div
41
- className="h-full bg-blue-400 transition-[width]"
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-neutral-200">{activePackage}</div>
48
+ <div className="text-[12px] text-slate-700">{activePackage}</div>
49
49
  )}
50
- <div className="text-[11px] text-neutral-400">{progress.message}</div>
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-neutral-800 bg-neutral-900 px-2 py-1 text-[10px] font-mono text-neutral-300"
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-neutral-800 bg-neutral-900 px-2 py-1 text-[10px] font-mono text-neutral-500">
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 px-4 py-2 border-t border-neutral-800/70 bg-neutral-950 flex items-center gap-3">
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] font-mono text-neutral-400">
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-neutral-800/70 bg-neutral-950">
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 flex items-center gap-3 hover:bg-neutral-900/50 transition-colors text-left"
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] font-mono text-neutral-300">
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] font-mono text-neutral-600">
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="border-t border-neutral-800/70 max-h-48 overflow-auto">
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="px-4 py-2 flex gap-3 text-[12px] font-mono border-b border-neutral-900 last:border-b-0"
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-neutral-500 w-16">
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-neutral-600 w-16">
64
+ <span className="w-16 shrink-0 text-slate-400">
65
65
  {d.code ?? ""}
66
66
  </span>
67
- <span className="text-neutral-200">{d.message}</span>
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 text-xs font-mono text-neutral-300 border border-neutral-800 rounded-md hover:border-neutral-600 hover:bg-neutral-900 transition-colors"
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-lg border border-neutral-800 bg-neutral-950 shadow-[0_10px_40px_rgba(0,0,0,0.6)] z-10 overflow-hidden">
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-neutral-900 last:border-b-0"
65
+ className="border-b border-[#d8e7f4] last:border-b-0"
66
66
  >
67
- <div className="px-3 py-1.5 text-[10px] font-mono uppercase tracking-wider text-neutral-600">
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 text-left px-3 py-2 hover:bg-neutral-900 transition-colors"
77
+ className="w-full px-3 py-2 text-left transition-colors hover:bg-[#eaf4ff]"
78
78
  >
79
- <div className="text-[12px] font-mono text-neutral-100">
79
+ <div className="font-mono text-[12px] text-[#102a43]">
80
80
  {item.title}
81
81
  </div>
82
- <div className="text-[10px] text-neutral-500 mt-0.5 leading-snug">
82
+ <div className="mt-0.5 text-[10px] leading-snug text-slate-500">
83
83
  {item.description}
84
84
  </div>
85
85
  </button>
@@ -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 h-full text-neutral-500 font-mono text-sm gap-2">
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-neutral-600 max-w-xs text-center">
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 p-3 rounded-md bg-neutral-900/60 border border-neutral-800/80"
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] font-mono text-neutral-500 mb-1">
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-neutral-200 font-mono">
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-black/60 backdrop-blur-sm flex items-center justify-center"
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-neutral-800 bg-neutral-950 shadow-[0_30px_80px_rgba(0,0,0,0.7)]"
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 px-6 py-4 border-b border-neutral-800/70">
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-white"
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-neutral-500 hover:text-white transition-colors"
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] font-mono uppercase tracking-wider text-neutral-600 mb-3">
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="text-sm font-mono text-neutral-100 group-hover:text-white transition-colors">
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-neutral-500 leading-snug">
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="px-6 py-4 border-t border-neutral-800/70 flex justify-end">
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 text-xs font-mono text-neutral-900 bg-white rounded-md hover:shadow-[0_0_30px_rgba(255,255,255,0.2)] transition-shadow"
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
- }, [createCompilerService, executeBundle, installDependenciesForSource, options]);
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 flex-col h-screen w-full items-center justify-center bg-neutral-950 text-neutral-200 gap-5 px-6 text-center">
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 text-[12px] font-mono text-neutral-400 whitespace-pre-wrap break-words">
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 text-xs font-mono text-neutral-900 bg-white rounded-md hover:shadow-[0_0_30px_rgba(255,255,255,0.2)] transition-shadow"
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 flex-col h-screen w-full bg-neutral-950 text-neutral-200 overflow-hidden">
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] font-mono text-amber-200 bg-amber-500/10 border-b border-amber-700/40">
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] font-mono text-amber-200 bg-amber-500/10 border-b border-amber-700/40">
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-neutral-800/70 bg-neutral-950 shrink-0">
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
- ? "text-white border-blue-400"
711
- : "text-neutral-500 border-transparent hover:text-neutral-300"
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 px-4 text-[10px] font-mono text-neutral-600">
718
- {bootPhase === "booting"
719
- ? "booting wasm…"
720
- : running
721
- ? "compiling…"
722
- : "ready"}
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 md:border-r border-neutral-800/70 flex flex-col h-1/2 md:h-full">
728
- <div className="flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70 bg-neutral-950">
729
- <span className="text-[11px] font-mono text-neutral-500">
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] font-mono text-neutral-700">
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 h-1/2 md:h-full border-t md:border-t-0 border-neutral-800/70">
746
- <div className="flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70 bg-neutral-950">
747
- <span className="text-[11px] font-mono text-neutral-500">
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] font-mono text-neutral-700">
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] font-mono text-red-300 bg-red-500/10 border-t border-red-700/40">
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-neutral-800/70 bg-neutral-950 flex flex-col h-48">
784
- <div className="flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70">
785
- <span className="text-[11px] font-mono text-neutral-500">
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] font-mono text-neutral-500 hover:text-neutral-200 transition-colors"
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] font-mono text-neutral-900 bg-emerald-400 rounded-md hover:bg-emerald-300 transition-colors disabled:opacity-50"
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"}