@ttsc/playground 0.14.0-dev.20250530.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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -0
  3. package/lib/src/compiler/DEFAULT_ENTRY_FILE.d.ts +2 -0
  4. package/lib/src/compiler/DEFAULT_ENTRY_FILE.js +6 -0
  5. package/lib/src/compiler/DEFAULT_ENTRY_FILE.js.map +1 -0
  6. package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.d.ts +5 -0
  7. package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.js +9 -0
  8. package/lib/src/compiler/DEFAULT_LINT_PLUGIN_NAME.js.map +1 -0
  9. package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.d.ts +19 -0
  10. package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.js +23 -0
  11. package/lib/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.js.map +1 -0
  12. package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.d.ts +2 -0
  13. package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.js +6 -0
  14. package/lib/src/compiler/DEFAULT_TSCONFIG_PATH.js.map +1 -0
  15. package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.d.ts +6 -0
  16. package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.js +10 -0
  17. package/lib/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.js.map +1 -0
  18. package/lib/src/compiler/DEFAULT_WORK_DIR.d.ts +2 -0
  19. package/lib/src/compiler/DEFAULT_WORK_DIR.js +6 -0
  20. package/lib/src/compiler/DEFAULT_WORK_DIR.js.map +1 -0
  21. package/lib/src/compiler/buildTsconfigJSON.d.ts +9 -0
  22. package/lib/src/compiler/buildTsconfigJSON.js +24 -0
  23. package/lib/src/compiler/buildTsconfigJSON.js.map +1 -0
  24. package/lib/src/compiler/createTypiaSourcePackMount.d.ts +11 -0
  25. package/lib/src/compiler/createTypiaSourcePackMount.js +29 -0
  26. package/lib/src/compiler/createTypiaSourcePackMount.js.map +1 -0
  27. package/lib/src/compiler/createWorkerCompiler.d.ts +25 -0
  28. package/lib/src/compiler/createWorkerCompiler.js +227 -0
  29. package/lib/src/compiler/createWorkerCompiler.js.map +1 -0
  30. package/lib/src/compiler/installDependenciesIntoMemFS.d.ts +12 -0
  31. package/lib/src/compiler/installDependenciesIntoMemFS.js +25 -0
  32. package/lib/src/compiler/installDependenciesIntoMemFS.js.map +1 -0
  33. package/lib/src/compiler/installTypiaSourcePack.d.ts +11 -0
  34. package/lib/src/compiler/installTypiaSourcePack.js +21 -0
  35. package/lib/src/compiler/installTypiaSourcePack.js.map +1 -0
  36. package/lib/src/compiler/internal/joinUnder.d.ts +5 -0
  37. package/lib/src/compiler/internal/joinUnder.js +13 -0
  38. package/lib/src/compiler/internal/joinUnder.js.map +1 -0
  39. package/lib/src/compiler/internal/parseLintDiagnostics.d.ts +6 -0
  40. package/lib/src/compiler/internal/parseLintDiagnostics.js +57 -0
  41. package/lib/src/compiler/internal/parseLintDiagnostics.js.map +1 -0
  42. package/lib/src/compiler/internal/safeParseTypiaTransform.d.ts +11 -0
  43. package/lib/src/compiler/internal/safeParseTypiaTransform.js +21 -0
  44. package/lib/src/compiler/internal/safeParseTypiaTransform.js.map +1 -0
  45. package/lib/src/compiler/lineColumnOf.d.ts +5 -0
  46. package/lib/src/compiler/lineColumnOf.js +15 -0
  47. package/lib/src/compiler/lineColumnOf.js.map +1 -0
  48. package/lib/src/compiler/loadTypiaSourcePack.d.ts +8 -0
  49. package/lib/src/compiler/loadTypiaSourcePack.js +33 -0
  50. package/lib/src/compiler/loadTypiaSourcePack.js.map +1 -0
  51. package/lib/src/compiler/mapDiagnostic.d.ts +9 -0
  52. package/lib/src/compiler/mapDiagnostic.js +22 -0
  53. package/lib/src/compiler/mapDiagnostic.js.map +1 -0
  54. package/lib/src/compiler/normalizeError.d.ts +2 -0
  55. package/lib/src/compiler/normalizeError.js +14 -0
  56. package/lib/src/compiler/normalizeError.js.map +1 -0
  57. package/lib/src/compiler/normalizeNodeModulePath.d.ts +8 -0
  58. package/lib/src/compiler/normalizeNodeModulePath.js +19 -0
  59. package/lib/src/compiler/normalizeNodeModulePath.js.map +1 -0
  60. package/lib/src/compiler/pickEmittedJS.d.ts +6 -0
  61. package/lib/src/compiler/pickEmittedJS.js +21 -0
  62. package/lib/src/compiler/pickEmittedJS.js.map +1 -0
  63. package/lib/src/index.d.ts +47 -0
  64. package/lib/src/index.js +104 -0
  65. package/lib/src/index.js.map +1 -0
  66. package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.d.ts +6 -0
  67. package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.js +15 -0
  68. package/lib/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.js.map +1 -0
  69. package/lib/src/npm/collectExternalPackageNames.d.ts +5 -0
  70. package/lib/src/npm/collectExternalPackageNames.js +34 -0
  71. package/lib/src/npm/collectExternalPackageNames.js.map +1 -0
  72. package/lib/src/npm/installPlaygroundDependencies.d.ts +13 -0
  73. package/lib/src/npm/installPlaygroundDependencies.js +121 -0
  74. package/lib/src/npm/installPlaygroundDependencies.js.map +1 -0
  75. package/lib/src/npm/internal/npmRegistry.d.ts +57 -0
  76. package/lib/src/npm/internal/npmRegistry.js +216 -0
  77. package/lib/src/npm/internal/npmRegistry.js.map +1 -0
  78. package/lib/src/npm/packageNameFromSpecifier.d.ts +7 -0
  79. package/lib/src/npm/packageNameFromSpecifier.js +66 -0
  80. package/lib/src/npm/packageNameFromSpecifier.js.map +1 -0
  81. package/lib/src/react/ConsoleViewer.d.ts +7 -0
  82. package/lib/src/react/ConsoleViewer.js +57 -0
  83. package/lib/src/react/ConsoleViewer.js.map +1 -0
  84. package/lib/src/react/DEFAULT_OPTION_TOGGLES.d.ts +3 -0
  85. package/lib/src/react/DEFAULT_OPTION_TOGGLES.js +17 -0
  86. package/lib/src/react/DEFAULT_OPTION_TOGGLES.js.map +1 -0
  87. package/lib/src/react/DependencyProgressModal.d.ts +7 -0
  88. package/lib/src/react/DependencyProgressModal.js +16 -0
  89. package/lib/src/react/DependencyProgressModal.js.map +1 -0
  90. package/lib/src/react/DiagnosticsPanel.d.ts +4 -0
  91. package/lib/src/react/DiagnosticsPanel.js +15 -0
  92. package/lib/src/react/DiagnosticsPanel.js.map +1 -0
  93. package/lib/src/react/ExamplePicker.d.ts +9 -0
  94. package/lib/src/react/ExamplePicker.js +43 -0
  95. package/lib/src/react/ExamplePicker.js.map +1 -0
  96. package/lib/src/react/LintPane.d.ts +9 -0
  97. package/lib/src/react/LintPane.js +17 -0
  98. package/lib/src/react/LintPane.js.map +1 -0
  99. package/lib/src/react/OptionsPanel.d.ts +13 -0
  100. package/lib/src/react/OptionsPanel.js +43 -0
  101. package/lib/src/react/OptionsPanel.js.map +1 -0
  102. package/lib/src/react/PlaygroundShell.d.ts +2 -0
  103. package/lib/src/react/PlaygroundShell.js +609 -0
  104. package/lib/src/react/PlaygroundShell.js.map +1 -0
  105. package/lib/src/react/ResultViewer.d.ts +10 -0
  106. package/lib/src/react/ResultViewer.js +49 -0
  107. package/lib/src/react/ResultViewer.js.map +1 -0
  108. package/lib/src/react/SourceEditor.d.ts +2 -0
  109. package/lib/src/react/SourceEditor.js +74 -0
  110. package/lib/src/react/SourceEditor.js.map +1 -0
  111. package/lib/src/react/createCompilerClient.d.ts +14 -0
  112. package/lib/src/react/createCompilerClient.js +77 -0
  113. package/lib/src/react/createCompilerClient.js.map +1 -0
  114. package/lib/src/sandbox/createSandboxRequire.d.ts +11 -0
  115. package/lib/src/sandbox/createSandboxRequire.js +211 -0
  116. package/lib/src/sandbox/createSandboxRequire.js.map +1 -0
  117. package/lib/src/sandbox/loadTypiaRuntimePack.d.ts +7 -0
  118. package/lib/src/sandbox/loadTypiaRuntimePack.js +35 -0
  119. package/lib/src/sandbox/loadTypiaRuntimePack.js.map +1 -0
  120. package/lib/src/structures/IBuildTsconfigOptions.d.ts +19 -0
  121. package/lib/src/structures/IBuildTsconfigOptions.js +3 -0
  122. package/lib/src/structures/IBuildTsconfigOptions.js.map +1 -0
  123. package/lib/src/structures/ICompilerService.d.ts +84 -0
  124. package/lib/src/structures/ICompilerService.js +3 -0
  125. package/lib/src/structures/ICompilerService.js.map +1 -0
  126. package/lib/src/structures/IConsoleMessage.d.ts +10 -0
  127. package/lib/src/structures/IConsoleMessage.js +3 -0
  128. package/lib/src/structures/IConsoleMessage.js.map +1 -0
  129. package/lib/src/structures/ICreateCompilerClientOptions.d.ts +13 -0
  130. package/lib/src/structures/ICreateCompilerClientOptions.js +3 -0
  131. package/lib/src/structures/ICreateCompilerClientOptions.js.map +1 -0
  132. package/lib/src/structures/ICreateWorkerCompilerOptions.d.ts +35 -0
  133. package/lib/src/structures/ICreateWorkerCompilerOptions.js +3 -0
  134. package/lib/src/structures/ICreateWorkerCompilerOptions.js.map +1 -0
  135. package/lib/src/structures/IInstallTypiaSourcePackOptions.d.ts +18 -0
  136. package/lib/src/structures/IInstallTypiaSourcePackOptions.js +3 -0
  137. package/lib/src/structures/IInstallTypiaSourcePackOptions.js.map +1 -0
  138. package/lib/src/structures/ILintPluginConfig.d.ts +5 -0
  139. package/lib/src/structures/ILintPluginConfig.js +3 -0
  140. package/lib/src/structures/ILintPluginConfig.js.map +1 -0
  141. package/lib/src/structures/IOptionToggle.d.ts +12 -0
  142. package/lib/src/structures/IOptionToggle.js +3 -0
  143. package/lib/src/structures/IOptionToggle.js.map +1 -0
  144. package/lib/src/structures/IPlaygroundDependencyInstallOptions.d.ts +16 -0
  145. package/lib/src/structures/IPlaygroundDependencyInstallOptions.js +3 -0
  146. package/lib/src/structures/IPlaygroundDependencyInstallOptions.js.map +1 -0
  147. package/lib/src/structures/IPlaygroundDependencyInstallResult.d.ts +20 -0
  148. package/lib/src/structures/IPlaygroundDependencyInstallResult.js +3 -0
  149. package/lib/src/structures/IPlaygroundDependencyInstallResult.js.map +1 -0
  150. package/lib/src/structures/IPlaygroundDependencyPackage.d.ts +8 -0
  151. package/lib/src/structures/IPlaygroundDependencyPackage.js +3 -0
  152. package/lib/src/structures/IPlaygroundDependencyPackage.js.map +1 -0
  153. package/lib/src/structures/IPlaygroundDependencyProgress.d.ts +10 -0
  154. package/lib/src/structures/IPlaygroundDependencyProgress.js +3 -0
  155. package/lib/src/structures/IPlaygroundDependencyProgress.js.map +1 -0
  156. package/lib/src/structures/IPlaygroundDependencyProgressPhase.d.ts +5 -0
  157. package/lib/src/structures/IPlaygroundDependencyProgressPhase.js +3 -0
  158. package/lib/src/structures/IPlaygroundDependencyProgressPhase.js.map +1 -0
  159. package/lib/src/structures/IPlaygroundExample.d.ts +16 -0
  160. package/lib/src/structures/IPlaygroundExample.js +3 -0
  161. package/lib/src/structures/IPlaygroundExample.js.map +1 -0
  162. package/lib/src/structures/IPlaygroundShellProps.d.ts +70 -0
  163. package/lib/src/structures/IPlaygroundShellProps.js +3 -0
  164. package/lib/src/structures/IPlaygroundShellProps.js.map +1 -0
  165. package/lib/src/structures/ISourceEditorProps.d.ts +18 -0
  166. package/lib/src/structures/ISourceEditorProps.js +3 -0
  167. package/lib/src/structures/ISourceEditorProps.js.map +1 -0
  168. package/lib/src/structures/ITransformOptions.d.ts +15 -0
  169. package/lib/src/structures/ITransformOptions.js +3 -0
  170. package/lib/src/structures/ITransformOptions.js.map +1 -0
  171. package/lib/src/structures/ITypiaPluginConfig.d.ts +19 -0
  172. package/lib/src/structures/ITypiaPluginConfig.js +3 -0
  173. package/lib/src/structures/ITypiaPluginConfig.js.map +1 -0
  174. package/lib/src/structures/index.d.ts +18 -0
  175. package/lib/src/structures/index.js +35 -0
  176. package/lib/src/structures/index.js.map +1 -0
  177. package/package.json +66 -0
  178. package/src/compiler/DEFAULT_ENTRY_FILE.ts +2 -0
  179. package/src/compiler/DEFAULT_LINT_PLUGIN_NAME.ts +5 -0
  180. package/src/compiler/DEFAULT_PLAYGROUND_COMPILER_OPTIONS.ts +19 -0
  181. package/src/compiler/DEFAULT_TSCONFIG_PATH.ts +2 -0
  182. package/src/compiler/DEFAULT_TYPIA_PLUGIN_NAME.ts +6 -0
  183. package/src/compiler/DEFAULT_WORK_DIR.ts +2 -0
  184. package/src/compiler/buildTsconfigJSON.ts +22 -0
  185. package/src/compiler/createTypiaSourcePackMount.ts +32 -0
  186. package/src/compiler/createWorkerCompiler.ts +273 -0
  187. package/src/compiler/installDependenciesIntoMemFS.ts +28 -0
  188. package/src/compiler/installTypiaSourcePack.ts +24 -0
  189. package/src/compiler/internal/joinUnder.ts +8 -0
  190. package/src/compiler/internal/parseLintDiagnostics.ts +61 -0
  191. package/src/compiler/internal/safeParseTypiaTransform.ts +24 -0
  192. package/src/compiler/lineColumnOf.ts +14 -0
  193. package/src/compiler/loadTypiaSourcePack.ts +37 -0
  194. package/src/compiler/mapDiagnostic.ts +26 -0
  195. package/src/compiler/normalizeError.ts +12 -0
  196. package/src/compiler/normalizeNodeModulePath.ts +13 -0
  197. package/src/compiler/pickEmittedJS.ts +18 -0
  198. package/src/index.ts +57 -0
  199. package/src/npm/BUILT_IN_PLAYGROUND_PACKAGES.ts +11 -0
  200. package/src/npm/collectExternalPackageNames.ts +34 -0
  201. package/src/npm/installPlaygroundDependencies.ts +163 -0
  202. package/src/npm/internal/npmRegistry.ts +292 -0
  203. package/src/npm/packageNameFromSpecifier.ts +61 -0
  204. package/src/react/ConsoleViewer.tsx +121 -0
  205. package/src/react/DEFAULT_OPTION_TOGGLES.ts +15 -0
  206. package/src/react/DependencyProgressModal.tsx +73 -0
  207. package/src/react/DiagnosticsPanel.tsx +74 -0
  208. package/src/react/ExamplePicker.tsx +93 -0
  209. package/src/react/LintPane.tsx +58 -0
  210. package/src/react/OptionsPanel.tsx +132 -0
  211. package/src/react/PlaygroundShell.tsx +864 -0
  212. package/src/react/ResultViewer.tsx +75 -0
  213. package/src/react/SourceEditor.tsx +96 -0
  214. package/src/react/createCompilerClient.ts +80 -0
  215. package/src/sandbox/createSandboxRequire.ts +275 -0
  216. package/src/sandbox/loadTypiaRuntimePack.ts +36 -0
  217. package/src/structures/IBuildTsconfigOptions.ts +19 -0
  218. package/src/structures/ICompilerService.ts +96 -0
  219. package/src/structures/IConsoleMessage.ts +10 -0
  220. package/src/structures/ICreateCompilerClientOptions.ts +13 -0
  221. package/src/structures/ICreateWorkerCompilerOptions.ts +40 -0
  222. package/src/structures/IInstallTypiaSourcePackOptions.ts +18 -0
  223. package/src/structures/ILintPluginConfig.ts +5 -0
  224. package/src/structures/IOptionToggle.ts +12 -0
  225. package/src/structures/IPlaygroundDependencyInstallOptions.ts +17 -0
  226. package/src/structures/IPlaygroundDependencyInstallResult.ts +21 -0
  227. package/src/structures/IPlaygroundDependencyPackage.ts +8 -0
  228. package/src/structures/IPlaygroundDependencyProgress.ts +11 -0
  229. package/src/structures/IPlaygroundDependencyProgressPhase.ts +12 -0
  230. package/src/structures/IPlaygroundExample.ts +16 -0
  231. package/src/structures/IPlaygroundShellProps.ts +83 -0
  232. package/src/structures/ISourceEditorProps.ts +18 -0
  233. package/src/structures/ITransformOptions.ts +15 -0
  234. package/src/structures/ITypiaPluginConfig.ts +20 -0
  235. package/src/structures/index.ts +18 -0
@@ -0,0 +1,609 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.PlaygroundShell = PlaygroundShell;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const lz_string_1 = require("lz-string");
7
+ const react_1 = require("react");
8
+ const BUILT_IN_PLAYGROUND_PACKAGES_1 = require("../npm/BUILT_IN_PLAYGROUND_PACKAGES");
9
+ const collectExternalPackageNames_1 = require("../npm/collectExternalPackageNames");
10
+ const installPlaygroundDependencies_1 = require("../npm/installPlaygroundDependencies");
11
+ const ConsoleViewer_1 = require("./ConsoleViewer");
12
+ const createCompilerClient_1 = require("./createCompilerClient");
13
+ const DEFAULT_OPTION_TOGGLES_1 = require("./DEFAULT_OPTION_TOGGLES");
14
+ const DependencyProgressModal_1 = require("./DependencyProgressModal");
15
+ const DiagnosticsPanel_1 = require("./DiagnosticsPanel");
16
+ const ExamplePicker_1 = require("./ExamplePicker");
17
+ const LintPane_1 = require("./LintPane");
18
+ const OptionsPanel_1 = require("./OptionsPanel");
19
+ const ResultViewer_1 = require("./ResultViewer");
20
+ const SourceEditor_1 = require("./SourceEditor");
21
+ const DEFAULT_OPTIONS = {
22
+ typia: true,
23
+ lint: true,
24
+ };
25
+ const DEPENDENCY_INSTALL_QUIET_MS = 900;
26
+ const SHARE_URL_WARN_BYTES = 2000;
27
+ function PlaygroundShell({ workerUrl, defaultScript, examples = [], exampleGroupLabels, optionToggles = DEFAULT_OPTION_TOGGLES_1.DEFAULT_OPTION_TOGGLES, defaultOptions = DEFAULT_OPTIONS, staticEditorLibs, preinstalledPackages = BUILT_IN_PLAYGROUND_PACKAGES_1.BUILT_IN_PLAYGROUND_PACKAGES, executeBundle, brand, resultCaption = defaultResultCaption, }) {
28
+ const client = (0, react_1.useMemo)(() => (0, createCompilerClient_1.createCompilerClient)({ workerUrl }), [workerUrl]);
29
+ const createCompilerService = client.connect;
30
+ const [source, setSource] = (0, react_1.useState)(defaultScript);
31
+ const [target, setTarget] = (0, react_1.useState)("javascript");
32
+ const [options, setOptions] = (0, react_1.useState)(defaultOptions);
33
+ const [result, setResult] = (0, react_1.useState)(null);
34
+ const [lintDiagnostics, setLintDiagnostics] = (0, react_1.useState)([]);
35
+ const [optionsOpen, setOptionsOpen] = (0, react_1.useState)(false);
36
+ const [running, setRunning] = (0, react_1.useState)(false);
37
+ const [shareToast, setShareToast] = (0, react_1.useState)(false);
38
+ const [consoleMessages, setConsoleMessages] = (0, react_1.useState)([]);
39
+ const [executing, setExecuting] = (0, react_1.useState)(false);
40
+ const [bootError, setBootError] = (0, react_1.useState)(null);
41
+ const [bootPhase, setBootPhase] = (0, react_1.useState)("booting");
42
+ const [bundleError, setBundleError] = (0, react_1.useState)(null);
43
+ const [shareWarn, setShareWarn] = (0, react_1.useState)(null);
44
+ const [sourceFromURL, setSourceFromURL] = (0, react_1.useState)(false);
45
+ const [editorExtraLibs, setEditorExtraLibs] = (0, react_1.useState)({});
46
+ const [dependencyProgress, setDependencyProgress] = (0, react_1.useState)(null);
47
+ const [dependencyPackageNames, setDependencyPackageNames] = (0, react_1.useState)([]);
48
+ const debounce = (0, react_1.useRef)(null);
49
+ const shareToastTimer = (0, react_1.useRef)(null);
50
+ const dependencyProgressTimer = (0, react_1.useRef)(null);
51
+ const dependencyInstallChain = (0, react_1.useRef)(Promise.resolve());
52
+ const dependencyAbort = (0, react_1.useRef)(null);
53
+ // Mirror `preinstalledPackages` into a ref so the worker-teardown
54
+ // effect can read the current value without taking the prop as a
55
+ // dep — listing the array prop in deps would tear down the worker on
56
+ // every parent re-render that produces a fresh array reference.
57
+ const preinstalledPackagesRef = (0, react_1.useRef)(preinstalledPackages);
58
+ // The ref tracks names the wasm MemFS already has — preinstalled at boot
59
+ // (via `preinstalledPackages`) plus everything `installPlaygroundDependencies`
60
+ // has added across the session. A useEffect below merges fresh
61
+ // preinstalledPackages prop values into the ref so a parent that swaps
62
+ // the prop later does not race a now-stale Set.
63
+ const installedDependencyNames = (0, react_1.useRef)(new Set(preinstalledPackages));
64
+ // Accumulated runtime-file map produced by every successful
65
+ // installPlaygroundDependencies call. Threaded through to executeBundle so
66
+ // the in-page Execute sandbox's require can resolve any npm package the
67
+ // user installed (without it, `import {v4} from "uuid"` compiles fine but
68
+ // Execute throws because the worker mounts uuid into the wasm MemFS only).
69
+ const runtimeDependencyFiles = (0, react_1.useRef)({});
70
+ const sourceVersion = (0, react_1.useRef)(0);
71
+ const latestSource = (0, react_1.useRef)(source);
72
+ // Race guards: each pipeline (compile/run vs Execute) owns its own epoch.
73
+ // Sharing one epoch would make a fresh Execute click invalidate an in-
74
+ // flight compile (and vice versa) and leave the spinner/button flags
75
+ // stuck because the older pipeline's finally would see a stale epoch.
76
+ // `updateSource` bumps the COMPILE epoch only — typing aborts a stale
77
+ // compile but does not interrupt a click-driven Execute.
78
+ const compileEpoch = (0, react_1.useRef)(0);
79
+ const executeEpoch = (0, react_1.useRef)(0);
80
+ // Retry epoch: each Retry click bumps it; only the latest retry's
81
+ // async block is allowed to write boot state. Prior retries' bodies
82
+ // bail on epoch mismatch so a fast double-Retry can't leave a stale
83
+ // `setBootPhase("ready")` chasing a torn-down connection.
84
+ const retryEpoch = (0, react_1.useRef)(0);
85
+ const mergedExtraLibs = (0, react_1.useMemo)(() => ({ ...staticEditorLibs, ...editorExtraLibs }), [staticEditorLibs, editorExtraLibs]);
86
+ const updateSource = (0, react_1.useCallback)((next) => {
87
+ sourceVersion.current++;
88
+ latestSource.current = next;
89
+ // Bump BOTH epochs: typing must abort an in-flight compile (the
90
+ // result would be stale) AND an in-flight Execute (the bundled code
91
+ // would not match what's on screen). Without bumping executeEpoch,
92
+ // an Execute whose dependency install was aborted by this typing
93
+ // would silently treat the abort as success and bundle against an
94
+ // incomplete MemFS — emitting JS for packages the user just edited
95
+ // away from, or failing in confusing ways.
96
+ compileEpoch.current++;
97
+ executeEpoch.current++;
98
+ dependencyAbort.current?.abort(createAbortError("source changed"));
99
+ setDependencyProgress(null);
100
+ setDependencyPackageNames([]);
101
+ setSource(next);
102
+ }, []);
103
+ // ── Sync installedDependencyNames + preinstalledPackagesRef on prop change ──
104
+ // The refs capture the initial value on mount; without this effect a
105
+ // parent that swaps `preinstalledPackages` later would race a stale
106
+ // Set against the fresh prop used in `ignoredPackages` below.
107
+ (0, react_1.useEffect)(() => {
108
+ preinstalledPackagesRef.current = preinstalledPackages;
109
+ for (const name of preinstalledPackages) {
110
+ installedDependencyNames.current.add(name);
111
+ }
112
+ }, [preinstalledPackages]);
113
+ // ── Decode source from URL on mount ──
114
+ (0, react_1.useEffect)(() => {
115
+ const params = new URLSearchParams(window.location.search);
116
+ const encoded = params.get("script");
117
+ if (encoded) {
118
+ const decoded = (0, lz_string_1.decompressFromEncodedURIComponent)(encoded);
119
+ if (decoded) {
120
+ updateSource(decoded);
121
+ setSourceFromURL(true);
122
+ }
123
+ }
124
+ }, [updateSource]);
125
+ // ── Eagerly boot the worker so first compile is instant ──
126
+ (0, react_1.useEffect)(() => {
127
+ let cancelled = false;
128
+ setBootPhase("booting");
129
+ createCompilerService().then(() => {
130
+ if (!cancelled)
131
+ setBootPhase("ready");
132
+ }, (err) => {
133
+ if (!cancelled) {
134
+ setBootError(err);
135
+ setBootPhase("failed");
136
+ }
137
+ });
138
+ return () => {
139
+ cancelled = true;
140
+ };
141
+ }, [createCompilerService]);
142
+ const installDependenciesForSource = (0, react_1.useCallback)(async (input, version = sourceVersion.current) => {
143
+ const task = dependencyInstallChain.current.then(async () => {
144
+ const firstPassPackageNames = (0, collectExternalPackageNames_1.collectExternalPackageNames)(input, preinstalledPackages);
145
+ const firstPassMissing = firstPassPackageNames.filter((name) => !installedDependencyNames.current.has(name));
146
+ if (firstPassMissing.length === 0)
147
+ return null;
148
+ await wait(DEPENDENCY_INSTALL_QUIET_MS);
149
+ if (sourceVersion.current !== version)
150
+ return null;
151
+ const packageNames = (0, collectExternalPackageNames_1.collectExternalPackageNames)(latestSource.current, preinstalledPackages);
152
+ const missing = packageNames.filter((name) => !installedDependencyNames.current.has(name));
153
+ if (missing.length === 0)
154
+ return null;
155
+ if (dependencyProgressTimer.current !== null) {
156
+ window.clearTimeout(dependencyProgressTimer.current);
157
+ dependencyProgressTimer.current = null;
158
+ }
159
+ setDependencyPackageNames(missing);
160
+ const abort = new AbortController();
161
+ dependencyAbort.current = abort;
162
+ try {
163
+ const installed = await (0, installPlaygroundDependencies_1.installPlaygroundDependencies)(missing, {
164
+ installedPackages: installedDependencyNames.current,
165
+ ignoredPackages: preinstalledPackages,
166
+ signal: abort.signal,
167
+ onProgress: setDependencyProgress,
168
+ });
169
+ if (sourceVersion.current !== version)
170
+ return null;
171
+ if (Object.keys(installed.compilerFiles).length > 0) {
172
+ const service = await createCompilerService();
173
+ await service.installDependencies({
174
+ files: installed.compilerFiles,
175
+ packages: installed.packages.map(({ name, version }) => ({
176
+ name,
177
+ version,
178
+ })),
179
+ });
180
+ }
181
+ for (const pkg of installed.packages) {
182
+ installedDependencyNames.current.add(pkg.name);
183
+ }
184
+ if (Object.keys(installed.editorLibs).length > 0) {
185
+ setEditorExtraLibs((prev) => ({
186
+ ...prev,
187
+ ...installed.editorLibs,
188
+ }));
189
+ }
190
+ // Accumulate runtime files so the in-page Execute sandbox can
191
+ // resolve every package the user installed across this session.
192
+ runtimeDependencyFiles.current = {
193
+ ...runtimeDependencyFiles.current,
194
+ ...installed.runtimeFiles,
195
+ };
196
+ dependencyProgressTimer.current = window.setTimeout(() => {
197
+ setDependencyProgress(null);
198
+ setDependencyPackageNames([]);
199
+ dependencyProgressTimer.current = null;
200
+ }, 350);
201
+ return null;
202
+ }
203
+ catch (error) {
204
+ if (isAbortError(error)) {
205
+ setDependencyProgress(null);
206
+ setDependencyPackageNames([]);
207
+ return null;
208
+ }
209
+ setDependencyProgress({
210
+ phase: "error",
211
+ packageName: missing[0],
212
+ completed: 0,
213
+ total: missing.length,
214
+ message: describeUnknownError(error),
215
+ });
216
+ dependencyProgressTimer.current = window.setTimeout(() => {
217
+ setDependencyProgress(null);
218
+ setDependencyPackageNames([]);
219
+ dependencyProgressTimer.current = null;
220
+ }, 2400);
221
+ return error;
222
+ }
223
+ finally {
224
+ if (dependencyAbort.current === abort)
225
+ dependencyAbort.current = null;
226
+ }
227
+ });
228
+ dependencyInstallChain.current = task.then(() => { });
229
+ return task;
230
+ }, [createCompilerService, preinstalledPackages]);
231
+ // ── Run compile when source / options change ──
232
+ //
233
+ // `target` (the active tab) is intentionally NOT a trigger here: the
234
+ // compile produces the same result + lintDiagnostics regardless of
235
+ // which tab the user is looking at; the tab choice only swaps which
236
+ // pane is rendered. Re-running the wasm-heavy pipeline on every tab
237
+ // click would burn multiple seconds of work per click.
238
+ const run = (0, react_1.useCallback)(async (input, opts, version) => {
239
+ const epoch = ++compileEpoch.current;
240
+ setRunning(true);
241
+ try {
242
+ const dependencyError = await installDependenciesForSource(input, version);
243
+ if (compileEpoch.current !== epoch)
244
+ return;
245
+ if (dependencyError) {
246
+ setResult({
247
+ type: "error",
248
+ target: "javascript",
249
+ value: normalizeClientError(dependencyError),
250
+ });
251
+ // Keep prior lintDiagnostics intact — a dependency-install blip
252
+ // shouldn't wipe the user's most recent successful lint output.
253
+ return;
254
+ }
255
+ const service = await createCompilerService();
256
+ const next = await service.compile({
257
+ source: input,
258
+ options: opts,
259
+ });
260
+ if (compileEpoch.current !== epoch)
261
+ return;
262
+ setResult(next);
263
+ if (opts.lint !== false) {
264
+ const lint = await service.lint({ source: input, options: opts });
265
+ if (compileEpoch.current !== epoch)
266
+ return;
267
+ setLintDiagnostics(lint.diagnostics);
268
+ }
269
+ else {
270
+ setLintDiagnostics([]);
271
+ }
272
+ }
273
+ catch (err) {
274
+ if (compileEpoch.current !== epoch)
275
+ return;
276
+ // Surface the error in the diagnostics pane via an error result —
277
+ // a transient compile/lint/install rejection (tgrid timeout,
278
+ // message-channel disconnect) must NOT tear the playground into
279
+ // the fatal boot-error screen and force a worker rebuild. Only
280
+ // the eager boot useEffect may flip bootPhase to "failed".
281
+ setResult({
282
+ type: "error",
283
+ target: "javascript",
284
+ value: normalizeClientError(err),
285
+ });
286
+ // Leave lintDiagnostics alone — clearing them on a transient
287
+ // compile blip would wipe the user's last good lint output.
288
+ }
289
+ finally {
290
+ // Only the winning epoch clears the flag. Older pipelines that
291
+ // returned early on an epoch mismatch must NOT clear running, or
292
+ // a fresh in-flight compile would show "ready" while it's still
293
+ // working. compileEpoch is bumped only by updateSource and by the
294
+ // next run() — Execute uses its own executeEpoch — so this guard
295
+ // does not stick the spinner across pipeline boundaries.
296
+ if (compileEpoch.current === epoch)
297
+ setRunning(false);
298
+ }
299
+ }, [createCompilerService, installDependenciesForSource]);
300
+ (0, react_1.useEffect)(() => {
301
+ if (bootPhase !== "ready")
302
+ return;
303
+ if (debounce.current !== null)
304
+ window.clearTimeout(debounce.current);
305
+ const version = sourceVersion.current;
306
+ debounce.current = window.setTimeout(() => {
307
+ void run(source, options, version);
308
+ }, 280);
309
+ return () => {
310
+ if (debounce.current !== null)
311
+ window.clearTimeout(debounce.current);
312
+ };
313
+ // `target` (the active tab) is intentionally NOT a dep — see the
314
+ // comment on `run` above. Re-running the wasm pipeline per tab
315
+ // click would burn seconds of work for an identical result.
316
+ }, [source, options, run, bootPhase]);
317
+ const onShare = (0, react_1.useCallback)(() => {
318
+ const url = new URL(window.location.href);
319
+ url.searchParams.set("script", (0, lz_string_1.compressToEncodedURIComponent)(source));
320
+ const urlString = url.toString();
321
+ void navigator.clipboard.writeText(urlString);
322
+ window.history.replaceState(null, "", urlString);
323
+ setShareToast(true);
324
+ if (shareToastTimer.current !== null)
325
+ window.clearTimeout(shareToastTimer.current);
326
+ shareToastTimer.current = window.setTimeout(() => {
327
+ setShareToast(false);
328
+ shareToastTimer.current = null;
329
+ }, 1800);
330
+ if (urlString.length > SHARE_URL_WARN_BYTES) {
331
+ setShareWarn(`Share URL is ${urlString.length} bytes — some browsers truncate URLs past ~2KB. Consider sharing as a Gist instead.`);
332
+ }
333
+ else {
334
+ setShareWarn(null);
335
+ }
336
+ }, [source]);
337
+ (0, react_1.useEffect)(() => () => {
338
+ if (shareToastTimer.current !== null)
339
+ window.clearTimeout(shareToastTimer.current);
340
+ if (dependencyProgressTimer.current !== null)
341
+ window.clearTimeout(dependencyProgressTimer.current);
342
+ dependencyAbort.current?.abort(createAbortError("playground unmounted"));
343
+ }, []);
344
+ // Tear down the Worker (+ its wasm instance) when the component
345
+ // unmounts or workerUrl changes. Without this an SPA navigation away
346
+ // from the playground leaks one Worker per mount; a workerUrl swap
347
+ // leaks the previous Worker forever.
348
+ //
349
+ // Reset the dependency-tracking refs too. They named packages that
350
+ // existed in the previous worker's MemFS; the fresh worker boots with
351
+ // only `preinstalledPackages` mounted, so carrying the old names
352
+ // would make installDependenciesForSource skip the install (because
353
+ // `installedDependencyNames.current.has(name)` is still true) and
354
+ // the next compile would fail with `Cannot find module`.
355
+ //
356
+ // The effect depends on `[client]` ONLY — listing the array prop
357
+ // `preinstalledPackages` in deps would tear down the worker on every
358
+ // parent re-render that produces a fresh array reference. We read the
359
+ // current value through `preinstalledPackagesRef`, which the sync
360
+ // effect above keeps up to date.
361
+ (0, react_1.useEffect)(() => () => {
362
+ void client.reset();
363
+ installedDependencyNames.current = new Set(preinstalledPackagesRef.current);
364
+ runtimeDependencyFiles.current = {};
365
+ }, [client]);
366
+ const onPickExample = (0, react_1.useCallback)((id) => {
367
+ const example = examples.find((e) => e.id === id);
368
+ if (example) {
369
+ updateSource(example.source);
370
+ setSourceFromURL(false);
371
+ }
372
+ }, [examples, updateSource]);
373
+ const onReset = (0, react_1.useCallback)(() => {
374
+ updateSource(defaultScript);
375
+ setSourceFromURL(false);
376
+ }, [defaultScript, updateSource]);
377
+ const onExecute = (0, react_1.useCallback)(async () => {
378
+ if (!executeBundle)
379
+ return;
380
+ // Bump the executeEpoch so a second Execute click invalidates the
381
+ // in-flight Execute. compileEpoch is unaffected — the user can edit
382
+ // the source mid-Execute without the running compile being torn down.
383
+ const epoch = ++executeEpoch.current;
384
+ setExecuting(true);
385
+ setBundleError(null);
386
+ // Clear the previous run's console output up front. Without this the
387
+ // pane keeps showing the old logs labeled as the new run until the
388
+ // first push fires — and an early-return bundle-error path (or an
389
+ // install rejection) might never push at all, leaving stale output
390
+ // attributed to the in-flight Execute.
391
+ setConsoleMessages([]);
392
+ const messages = [];
393
+ const push = (type, args) => {
394
+ if (executeEpoch.current !== epoch)
395
+ return;
396
+ messages.push({ type, value: args });
397
+ setConsoleMessages([...messages]);
398
+ };
399
+ try {
400
+ // Snapshot source + version atomically from the always-fresh refs.
401
+ // Reading `source` (React state) here can be stale within a batch
402
+ // — installDependenciesForSource would compare against the fresh
403
+ // ref and bail, while bundle would still run against the stale
404
+ // source so newly-needed deps stay un-mounted. latestSource is
405
+ // updated synchronously by updateSource alongside sourceVersion.
406
+ const currentSource = latestSource.current;
407
+ const currentVersion = sourceVersion.current;
408
+ const dependencyError = await installDependenciesForSource(currentSource, currentVersion);
409
+ if (executeEpoch.current !== epoch)
410
+ return;
411
+ if (dependencyError) {
412
+ push("error", [dependencyError]);
413
+ return;
414
+ }
415
+ const service = await createCompilerService();
416
+ const compiled = await service.bundle({
417
+ source: currentSource,
418
+ options,
419
+ });
420
+ if (executeEpoch.current !== epoch)
421
+ return;
422
+ if (compiled.type === "error") {
423
+ const message = typeof compiled.value === "string"
424
+ ? compiled.value
425
+ : (compiled.value?.message ??
426
+ "Bundle failed");
427
+ setBundleError(message);
428
+ push("error", [compiled.value]);
429
+ return;
430
+ }
431
+ const code = compiled.value;
432
+ const sandboxConsole = {
433
+ log: (...args) => push("log", args),
434
+ info: (...args) => push("info", args),
435
+ warn: (...args) => push("warn", args),
436
+ error: (...args) => push("error", args),
437
+ debug: (...args) => push("debug", args),
438
+ dir: (...args) => push("dir", args),
439
+ table: (...args) => push("table", args),
440
+ };
441
+ try {
442
+ await executeBundle(code, {
443
+ console: sandboxConsole,
444
+ runtimeFiles: runtimeDependencyFiles.current,
445
+ });
446
+ }
447
+ catch (error) {
448
+ push("error", [error]);
449
+ }
450
+ }
451
+ catch (error) {
452
+ if (executeEpoch.current !== epoch)
453
+ return;
454
+ push("error", [error]);
455
+ }
456
+ finally {
457
+ // Only the winning epoch clears the flag — same rationale as
458
+ // compileEpoch above. With a separate executeEpoch this is robust
459
+ // against concurrent click + edit interleavings.
460
+ if (executeEpoch.current === epoch)
461
+ setExecuting(false);
462
+ }
463
+ // `source` is intentionally NOT a dep: the body snapshots
464
+ // `latestSource.current` (always fresh ref) rather than reading the
465
+ // React state, so including `source` here would re-create the
466
+ // callback per keystroke and propagate into the global keydown
467
+ // useEffect, churning event-listener add/remove every character.
468
+ }, [createCompilerService, executeBundle, installDependenciesForSource, options]);
469
+ const allDiagnostics = (0, react_1.useMemo)(() => {
470
+ const fromCompile = [];
471
+ if (result?.type === "failure") {
472
+ fromCompile.push(...result.diagnostics);
473
+ }
474
+ else if (result?.type === "error") {
475
+ // Host-level exceptions (worker transport blip, wasm rejection)
476
+ // surface as a synthetic diagnostic so the diagnostics strip
477
+ // doesn't say "0 errors" while the result pane shows an error.
478
+ const message = typeof result.value === "string"
479
+ ? result.value
480
+ : (result.value?.message ??
481
+ "ttsc: unexpected error");
482
+ fromCompile.push({
483
+ line: 1,
484
+ column: 1,
485
+ length: 1,
486
+ severity: "error",
487
+ message,
488
+ code: "TTSC_RUNTIME",
489
+ });
490
+ }
491
+ const set = new Set();
492
+ return [...fromCompile, ...lintDiagnostics].filter((d) => {
493
+ const key = `${d.line}:${d.column}:${d.code ?? ""}:${d.message}`;
494
+ if (set.has(key))
495
+ return false;
496
+ set.add(key);
497
+ return true;
498
+ });
499
+ }, [result, lintDiagnostics]);
500
+ (0, react_1.useEffect)(() => {
501
+ const onKey = (e) => {
502
+ const mod = e.metaKey || e.ctrlKey;
503
+ if (!mod)
504
+ return;
505
+ if (e.key === "Enter" && executeBundle) {
506
+ e.preventDefault();
507
+ void onExecute();
508
+ }
509
+ else if (e.key.toLowerCase() === "s") {
510
+ e.preventDefault();
511
+ onShare();
512
+ }
513
+ else if (e.key.toLowerCase() === "k") {
514
+ e.preventDefault();
515
+ document
516
+ .querySelector("button[data-playground-examples-toggle]")
517
+ ?.click();
518
+ }
519
+ };
520
+ window.addEventListener("keydown", onKey);
521
+ return () => window.removeEventListener("keydown", onKey);
522
+ }, [executeBundle, onExecute, onShare]);
523
+ if (bootPhase === "failed") {
524
+ return ((0, jsx_runtime_1.jsxs)("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", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-red-400 text-3xl", children: "\u26A0" }), (0, jsx_runtime_1.jsx)("h1", { className: "text-lg font-mono", children: "Playground failed to boot." }), (0, jsx_runtime_1.jsx)("pre", { className: "max-w-xl text-[12px] font-mono text-neutral-400 whitespace-pre-wrap break-words", children: (() => {
525
+ const e = bootError;
526
+ if (e instanceof Error)
527
+ return `${e.name}: ${e.message}`;
528
+ try {
529
+ return JSON.stringify(e, null, 2);
530
+ }
531
+ catch {
532
+ return String(e);
533
+ }
534
+ })() }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
535
+ // Each click bumps retryEpoch; only the latest retry's body
536
+ // writes boot state. A double-click cancels the prior retry's
537
+ // pending writes so it can't flip bootPhase to "ready"
538
+ // against a connection the new retry has already torn down.
539
+ const epoch = ++retryEpoch.current;
540
+ void (async () => {
541
+ await client.reset();
542
+ if (retryEpoch.current !== epoch)
543
+ return;
544
+ setBootError(null);
545
+ setBootPhase("booting");
546
+ try {
547
+ await createCompilerService();
548
+ if (retryEpoch.current !== epoch)
549
+ return;
550
+ setBootPhase("ready");
551
+ }
552
+ catch (err) {
553
+ if (retryEpoch.current !== epoch)
554
+ return;
555
+ setBootError(err);
556
+ setBootPhase("failed");
557
+ }
558
+ })();
559
+ }, 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", children: "Retry" })] }));
560
+ }
561
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col h-screen w-full bg-neutral-950 text-neutral-200 overflow-hidden", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 px-4 py-2.5 border-b border-neutral-800/70 bg-neutral-950 shrink-0", children: [brand, (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-700", children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-neutral-400", children: "Playground" }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-auto flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(ExamplePicker_1.ExamplePicker, { examples: examples, onPick: onPickExample, groupLabels: exampleGroupLabels }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setOptionsOpen((v) => !v), 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", children: "Options" }), (0, jsx_runtime_1.jsx)("button", { onClick: onReset, className: "px-3 py-1.5 text-xs font-mono text-neutral-400 hover:text-neutral-200 transition-colors", children: "Reset" }), (0, jsx_runtime_1.jsx)("button", { onClick: onShare, 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", children: shareToast ? "Copied ✓" : "Share" })] })] }), sourceFromURL && ((0, jsx_runtime_1.jsx)("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", children: "Source loaded from share URL. Hit Reset to return to the default example." })), shareWarn && ((0, jsx_runtime_1.jsx)("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", children: shareWarn })), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-0 border-b border-neutral-800/70 bg-neutral-950 shrink-0", children: [[
562
+ { id: "javascript", label: "Compiled JS" },
563
+ { id: "lint", label: "Lint" },
564
+ ].map((tab) => ((0, jsx_runtime_1.jsx)("button", { onClick: () => setTarget(tab.id), className: `px-4 py-2 text-[12px] font-mono border-b-2 transition-colors ${target === tab.id
565
+ ? "text-white border-blue-400"
566
+ : "text-neutral-500 border-transparent hover:text-neutral-300"}`, children: tab.label }, tab.id))), (0, jsx_runtime_1.jsx)("div", { className: "ml-auto px-4 text-[10px] font-mono text-neutral-600", children: bootPhase === "booting"
567
+ ? "booting wasm…"
568
+ : running
569
+ ? "compiling…"
570
+ : "ready" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-1 min-h-0 flex-col md:flex-row", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0 md:border-r border-neutral-800/70 flex flex-col h-1/2 md:h-full", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70 bg-neutral-950", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-[11px] font-mono text-neutral-500", children: "src/playground.ts" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[10px] font-mono text-neutral-700", children: [source.split("\n").length, " lines"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-h-0", children: (0, jsx_runtime_1.jsx)(SourceEditor_1.SourceEditor, { value: source, onChange: updateSource, extraLibs: mergedExtraLibs }) })] }), (0, jsx_runtime_1.jsxs)("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", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70 bg-neutral-950", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-[11px] font-mono text-neutral-500", children: target === "javascript"
571
+ ? resultCaption(options)
572
+ : "lint diagnostics" }), (0, jsx_runtime_1.jsx)("span", { className: "text-[10px] font-mono text-neutral-700", children: result?.type === "error" ? "error" : "" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-h-0", children: target === "lint" ? ((0, jsx_runtime_1.jsx)(LintPane_1.LintPane, { diagnostics: lintDiagnostics })) : ((0, jsx_runtime_1.jsx)(ResultViewer_1.ResultViewer, { language: result?.type === "error" ? "json" : "javascript", value: result === null
573
+ ? ""
574
+ : result.type === "error"
575
+ ? (JSON.stringify(result.value, null, 2) ??
576
+ String(result.value))
577
+ : result.value })) })] })] }), bundleError && ((0, jsx_runtime_1.jsxs)("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", children: ["Bundle failed \u2014 ", bundleError] })), executeBundle && ((0, jsx_runtime_1.jsxs)("div", { className: "shrink-0 border-t border-neutral-800/70 bg-neutral-950 flex flex-col h-48", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between px-4 py-1.5 border-b border-neutral-800/70", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-[11px] font-mono text-neutral-500", children: "console output" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [consoleMessages.length > 0 && ((0, jsx_runtime_1.jsx)("button", { onClick: () => setConsoleMessages([]), className: "px-2 py-1 text-[10px] font-mono text-neutral-500 hover:text-neutral-200 transition-colors", children: "Clear" })), (0, jsx_runtime_1.jsxs)("button", { onClick: onExecute, disabled: executing, 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", title: "Cmd/Ctrl+Enter", children: ["\u25B6 ", executing ? "Executing…" : "Execute"] })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-h-0", children: (0, jsx_runtime_1.jsx)(ConsoleViewer_1.ConsoleViewer, { messages: consoleMessages }) })] })), (0, jsx_runtime_1.jsx)(DiagnosticsPanel_1.DiagnosticsPanel, { diagnostics: allDiagnostics }), optionsOpen && ((0, jsx_runtime_1.jsx)(OptionsPanel_1.OptionsPanel, { options: options, onChange: setOptions, onClose: () => setOptionsOpen(false), toggles: optionToggles })), (0, jsx_runtime_1.jsx)(DependencyProgressModal_1.DependencyProgressModal, { progress: dependencyProgress, packages: dependencyPackageNames })] }));
578
+ }
579
+ function defaultResultCaption(_options) {
580
+ return "dist/playground.js";
581
+ }
582
+ function describeUnknownError(error) {
583
+ if (error instanceof Error)
584
+ return error.message;
585
+ try {
586
+ return JSON.stringify(error);
587
+ }
588
+ catch {
589
+ return String(error);
590
+ }
591
+ }
592
+ function normalizeClientError(error) {
593
+ if (error instanceof Error) {
594
+ return { name: error.name, message: error.message, stack: error.stack };
595
+ }
596
+ return { name: "Error", message: describeUnknownError(error) };
597
+ }
598
+ function wait(ms) {
599
+ return new Promise((resolve) => window.setTimeout(resolve, ms));
600
+ }
601
+ function createAbortError(reason) {
602
+ const error = new Error(`Dependency install aborted: ${reason}.`);
603
+ error.name = "AbortError";
604
+ return error;
605
+ }
606
+ function isAbortError(error) {
607
+ return error instanceof Error && error.name === "AbortError";
608
+ }
609
+ //# sourceMappingURL=PlaygroundShell.js.map