code-playpad 0.10.1 → 0.11.0

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 (57) hide show
  1. package/README.md +165 -8
  2. package/dist/THIRD-PARTY-NOTICES.txt +223 -0
  3. package/dist/cdn.js +2 -2
  4. package/dist/compiler-B408qd3N.js +151 -0
  5. package/dist/{config-CT3H6Oum.js → config-DB58UVsk.js} +1 -1
  6. package/dist/{cpp-CO2abWii.js → cpp-C77X5wRk.js} +1 -1
  7. package/dist/{cpp-Cs32J8JC.js → cpp-DuOQ_23Z.js} +1 -1
  8. package/dist/{dist-DXjodKfp.js → dist-B4dyQ4mN.js} +1 -1
  9. package/dist/{dist-BkpKCrXQ.js → dist-BFlbXnjn.js} +1 -1
  10. package/dist/dist-BoJapv5W.js +28 -0
  11. package/dist/dist-BqLa1O1y.js +6 -0
  12. package/dist/dist-CJRlQ049.js +6 -0
  13. package/dist/{dist-CJxiVI4a.js → dist-DBpvb9b1.js} +1 -1
  14. package/dist/{dist-BIgiGgKu.js → dist-DRocX-cv.js} +1 -1
  15. package/dist/dist-DfjaG4Ra.js +6 -0
  16. package/dist/{dist-D2igTc4m.js → dist-DviSDQoT.js} +1 -1
  17. package/dist/dist-F28mE6nM.js +8 -0
  18. package/dist/{dist-CNOK8CHE.js → dist-TxeNlzAy.js} +1 -1
  19. package/dist/dist-y6lcdYKh.js +14 -0
  20. package/dist/element-COcjM0hI.js +521 -0
  21. package/dist/element.d.ts +26 -0
  22. package/dist/element.js +1 -1
  23. package/dist/hints-DpBIeoeG.js +6 -0
  24. package/dist/index.d.ts +3 -3
  25. package/dist/index.js +1 -1
  26. package/dist/{java-DeBiVPXO.js → java-Df5HdXMz.js} +1 -1
  27. package/dist/{java-8qjVCjsN.js → java-DyH3mSiH.js} +1 -1
  28. package/dist/{javascript-DLPaJJzf.js → javascript-BtHVyxzg.js} +2 -2
  29. package/dist/{javascript-B1tVx7AD.js → javascript-CWolHiSP.js} +1 -1
  30. package/dist/languages.d.ts +1 -1
  31. package/dist/{pyodide-BOoYEfX1.js → pyodide-3MPS4ySA.js} +2 -2
  32. package/dist/{pyodide-C8ImkXdD.js → pyodide-EUDn__Gx.js} +1 -1
  33. package/dist/react-CL1KABFm.js +9 -0
  34. package/dist/react-Db_rGDYu.js +4 -0
  35. package/dist/react-runtime-19.2.8.js +240 -0
  36. package/dist/react-vendor.generated-DMxIoCMn.js +6 -0
  37. package/dist/react-version.generated-BNNJmf7J.js +6 -0
  38. package/dist/react.d.ts +5 -1
  39. package/dist/react.js +1 -1
  40. package/dist/runtime/config.d.ts +15 -1
  41. package/dist/runtime/react/boot.d.ts +9 -0
  42. package/dist/runtime/react/compiler.d.ts +30 -0
  43. package/dist/runtime/react-vendor.generated.d.ts +5 -0
  44. package/dist/runtime/react-version.generated.d.ts +3 -0
  45. package/dist/runtime/react.d.ts +2 -0
  46. package/dist/standalone.js +214 -17
  47. package/dist/{hints-CCMgezw9.js → types-CfcYqU2K.js} +1 -1
  48. package/dist/types.d.ts +23 -0
  49. package/dist/worker-runtime-DvpugeXD.js +9 -0
  50. package/dist/{worker-runtime-sUPz1J56.js → worker-runtime-PykJQLB7.js} +1 -1
  51. package/package.json +21 -6
  52. package/dist/dist--J_vhCIU.js +0 -14
  53. package/dist/dist-BNvM_BAY.js +0 -6
  54. package/dist/dist-D7jBL3Ib.js +0 -28
  55. package/dist/dist-DfFrsBkU.js +0 -8
  56. package/dist/element-CQGYVEF7.js +0 -324
  57. package/dist/worker-runtime-DfYet3xy.js +0 -9
@@ -0,0 +1,521 @@
1
+ async function e(e){try{return await t(e)}catch(t){return console.warn(`[code-playpad] CodeMirror unavailable, using plain textarea:`,t),n(e)}}async function t(e){let[t,n,r,i,a,o]=await Promise.all([import(`@codemirror/view`),import(`@codemirror/state`),import(`@codemirror/commands`),import(`@codemirror/language`),import(`@codemirror/autocomplete`),import(`@codemirror/theme-one-dark`)]),{EditorView:s,keymap:c,lineNumbers:l,highlightActiveLine:u,highlightActiveLineGutter:d,highlightSpecialChars:f,drawSelection:p,rectangularSelection:m,crosshairCursor:h}=t,{EditorState:g,Compartment:_}=n,{defaultKeymap:v,history:y,historyKeymap:b,indentWithTab:x}=r,{syntaxHighlighting:S,defaultHighlightStyle:C,indentOnInput:w,bracketMatching:T,foldGutter:E,foldKeymap:D}=i,{autocompletion:O,closeBrackets:k,closeBracketsKeymap:A,completionKeymap:j}=a,M=new _,N=new _,P=new _,F=[s.theme({"&":{color:`#1f2328`,backgroundColor:`#ffffff`},".cm-gutters":{backgroundColor:`#f6f8fa`,color:`#8b949e`,border:`none`},".cm-activeLine":{backgroundColor:`#f6f8fa88`},".cm-activeLineGutter":{backgroundColor:`#eaeef2`},".cm-cursor":{borderLeftColor:`#1f2328`},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, ::selection":{backgroundColor:`#cfe3ff`}}),S(C,{fallback:!0})],I=e=>e?o.oneDark:F,L=c.of([{key:`Mod-Enter`,preventDefault:!0,run:()=>(e.onRun?.(),!0)},{key:`Shift-Enter`,preventDefault:!0,run:()=>(e.onRun?.(),!0)}]),R=e.dark,z=e.readOnly,B=e.language??[],V=t=>g.create({doc:t,extensions:[l(),d(),f(),y(),E(),p(),g.allowMultipleSelections.of(!0),w(),T(),k(),O(),m(),h(),u(),L,c.of([...A,...v,...b,...D,...j,x]),s.lineWrapping,P.of(B),M.of(I(R)),N.of(g.readOnly.of(z)),s.updateListener.of(t=>{t.docChanged&&e.onChange?.(t.state.doc.toString())})]}),H=new s({parent:e.parent,state:V(e.doc)}),U=/* @__PURE__ */ new Map,W=e.name??``;return{fallback:!1,getCode:()=>H.state.doc.toString(),setCode(e){H.dispatch({changes:{from:0,to:H.state.doc.length,insert:e}})},openFile(e,t){if(e===W)return;U.set(W,H.state);let n=U.get(e);H.setState(n??V(t)),W=e,n&&H.dispatch({effects:[M.reconfigure(I(R)),N.reconfigure(g.readOnly.of(z)),P.reconfigure(B)]})},fileCode(e,t){if(e===W)return H.state.doc.toString();let n=U.get(e);return n?n.doc.toString():t},forgetFiles(e){for(let t of[...U.keys()])e.includes(t)||U.delete(t)},setTheme(e){R=e,H.dispatch({effects:M.reconfigure(I(R))})},setReadOnly(e){z=e,H.dispatch({effects:N.reconfigure(g.readOnly.of(z))})},setLanguage(e){B=e,H.dispatch({effects:P.reconfigure(B)})},focus:()=>H.focus(),destroy:()=>H.destroy()}}function n(e){let t=document.createElement(`textarea`);t.className=`cp-fallback-editor`,t.spellcheck=!1,t.value=e.doc,t.readOnly=e.readOnly,t.addEventListener(`input`,()=>e.onChange?.(t.value)),t.addEventListener(`keydown`,t=>{(t.metaKey||t.ctrlKey)&&t.key===`Enter`&&(t.preventDefault(),e.onRun?.())}),e.parent.appendChild(t);let n=/* @__PURE__ */ new Map,r=e.name??``;return{fallback:!0,getCode:()=>t.value,setCode:e=>{t.value=e},openFile(e,i){e!==r&&(n.set(r,t.value),t.value=n.get(e)??i,r=e)},fileCode(e,i){return e===r?t.value:n.get(e)??i},forgetFiles(e){for(let t of[...n.keys()])e.includes(t)||n.delete(t)},setTheme:()=>{},setReadOnly:e=>{t.readOnly=e},setLanguage:()=>{},focus:()=>t.focus(),destroy:()=>t.remove()}}var r=/* @__PURE__ */ new Map,i=/* @__PURE__ */ new Map;function a(e,t){r.set(e,t)}function o(){return[...r.keys()]}function s(e){let t=i.get(e);if(t)return t;let n=r.get(e);if(!n)return Promise.reject(/* @__PURE__ */ Error(`Unknown runtime "${e}". Registered: ${o().join(`, `)||`none`}`));let a=n().catch(t=>{throw i.delete(e),t});return i.set(e,a),a}a(`python`,()=>import(`./pyodide-EUDn__Gx.js`).then(e=>e.pythonRuntime)),a(`javascript`,()=>import(`./javascript-CWolHiSP.js`).then(e=>e.javascriptRuntime)),a(`typescript`,()=>import(`./javascript-CWolHiSP.js`).then(e=>e.typescriptRuntime)),a(`java`,()=>import(`./java-DyH3mSiH.js`).then(e=>e.javaRuntime)),a(`c`,()=>import(`./cpp-C77X5wRk.js`).then(e=>e.cRuntime)),a(`cpp`,()=>import(`./cpp-C77X5wRk.js`).then(e=>e.cppRuntime)),a(`react`,()=>import(`./react-Db_rGDYu.js`).then(e=>e.reactRuntime));var c=class extends Error{constructor(e=`Execution stopped`){super(e),this.name=`RunStopped`}},l=class extends Error{constructor(e=`Web Workers are unavailable in this page`){super(e),this.name=`WorkerUnavailable`}},u=/* @__PURE__ */ new WeakSet,d=String.raw`
2
+ :host {
3
+ --cp-bg: #ffffff;
4
+ --cp-fg: #1f2328;
5
+ --cp-muted: #656d76;
6
+ --cp-border: #d0d7de;
7
+ --cp-surface: #f6f8fa;
8
+ --cp-accent: #1f6feb;
9
+ --cp-accent-fg: #ffffff;
10
+ --cp-danger: #cf222e;
11
+ --cp-ok: #1a7f37;
12
+ --cp-radius: 8px;
13
+ --cp-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
14
+ --cp-font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
15
+
16
+ display: block;
17
+ margin: 1rem 0;
18
+ color-scheme: light;
19
+ contain: content;
20
+ }
21
+
22
+ :host([hidden]) { display: none; }
23
+
24
+ :host([data-resolved-theme="dark"]) {
25
+ --cp-bg: #0d1117;
26
+ --cp-fg: #e6edf3;
27
+ --cp-muted: #8b949e;
28
+ --cp-border: #30363d;
29
+ --cp-surface: #161b22;
30
+ --cp-accent: #388bfd;
31
+ --cp-danger: #ff7b72;
32
+ --cp-ok: #3fb950;
33
+ color-scheme: dark;
34
+ }
35
+
36
+ * { box-sizing: border-box; }
37
+
38
+ .cp-root {
39
+ border: 1px solid var(--cp-border);
40
+ border-radius: var(--cp-radius);
41
+ background: var(--cp-bg);
42
+ color: var(--cp-fg);
43
+ font-family: var(--cp-font-ui);
44
+ font-size: 14px;
45
+ overflow: hidden;
46
+ }
47
+
48
+ .cp-toolbar {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 8px;
52
+ padding: 6px 8px;
53
+ background: var(--cp-surface);
54
+ border-bottom: 1px solid var(--cp-border);
55
+ }
56
+
57
+ .cp-spacer { flex: 1 1 auto; }
58
+
59
+ .cp-status {
60
+ font-size: 12px;
61
+ color: var(--cp-muted);
62
+ white-space: nowrap;
63
+ overflow: hidden;
64
+ text-overflow: ellipsis;
65
+ max-width: 55%;
66
+ }
67
+ .cp-status[data-state="error"] { color: var(--cp-danger); }
68
+ .cp-status[data-state="ok"] { color: var(--cp-ok); }
69
+
70
+ button {
71
+ font: inherit;
72
+ font-size: 13px;
73
+ line-height: 1;
74
+ padding: 6px 12px;
75
+ border-radius: 6px;
76
+ border: 1px solid var(--cp-border);
77
+ background: var(--cp-bg);
78
+ color: var(--cp-fg);
79
+ cursor: pointer;
80
+ }
81
+ button:hover:not(:disabled) { border-color: var(--cp-accent); }
82
+ button:disabled { opacity: 0.5; cursor: default; }
83
+ button:focus-visible { outline: 2px solid var(--cp-accent); outline-offset: 1px; }
84
+
85
+ button.cp-primary {
86
+ background: var(--cp-accent);
87
+ border-color: var(--cp-accent);
88
+ color: var(--cp-accent-fg);
89
+ font-weight: 600;
90
+ }
91
+ button.cp-danger { color: var(--cp-danger); border-color: var(--cp-danger); }
92
+
93
+ .cp-tabs {
94
+ display: flex;
95
+ gap: 2px;
96
+ overflow-x: auto;
97
+ padding: 0 6px;
98
+ background: var(--cp-surface);
99
+ border-bottom: 1px solid var(--cp-border);
100
+ scrollbar-width: thin;
101
+ }
102
+ .cp-tabs[hidden] { display: none; }
103
+ .cp-tabs button {
104
+ border: 0;
105
+ border-bottom: 2px solid transparent;
106
+ border-radius: 0;
107
+ background: transparent;
108
+ color: var(--cp-muted);
109
+ font-family: var(--cp-font-mono);
110
+ font-size: 12px;
111
+ padding: 7px 10px;
112
+ white-space: nowrap;
113
+ }
114
+ .cp-tabs button:hover:not(:disabled) { color: var(--cp-fg); }
115
+ .cp-tabs button[aria-pressed='true'] {
116
+ color: var(--cp-fg);
117
+ border-bottom-color: var(--cp-accent);
118
+ background: var(--cp-bg);
119
+ }
120
+ .cp-tabs button.cp-entry::before {
121
+ content: '▸';
122
+ margin-right: 4px;
123
+ color: var(--cp-accent);
124
+ }
125
+
126
+ .cp-editor {
127
+ height: var(--cp-height, 220px);
128
+ overflow: hidden;
129
+ }
130
+ .cp-editor .cm-editor { height: 100%; }
131
+
132
+ /* Content-fitting mode. Until 0.2.0 height="auto" happened to work, because a
133
+ percentage height against an indefinite parent computes to auto — true, but
134
+ accidental, and one future rule away from silently reverting to 220px. These
135
+ rules make it a supported mode instead. */
136
+ :host([height='auto']) .cp-editor,
137
+ :host([height='auto']) .cp-editor .cm-editor,
138
+ :host([height='auto']) .cp-fallback-editor {
139
+ height: auto;
140
+ }
141
+
142
+ /* Bounds for that mode, following CodeMirror's own recipes.
143
+
144
+ max-height goes on .cm-editor: its theme already gives .cm-scroller
145
+ overflow-y: auto, so a capped editor scrolls rather than clips. Putting the
146
+ cap on .cp-editor instead would clip, since the scroller has already sized
147
+ itself to the content.
148
+
149
+ min-height goes on .cm-content and .cm-gutter, not on .cm-editor. Both give
150
+ the right outer height, but on .cm-editor the scroller stays at content
151
+ height — so the empty space below a one-line snippet is not part of the
152
+ editor and clicking it does nothing. That empty space is exactly what a
153
+ "write your answer here" box is for. */
154
+ .cp-editor .cm-editor { max-height: var(--cp-max-height, none); }
155
+
156
+ /* Three classes deep on purpose: CodeMirror's base theme sets
157
+ .cm-content { min-height: 100% } through a generated wrapper class, which
158
+ ties on specificity with a two-class selector and wins on order, since its
159
+ stylesheet is injected into the shadow root after this one. */
160
+ .cp-editor .cm-editor .cm-content,
161
+ .cp-editor .cm-editor .cm-gutter { min-height: var(--cp-min-height, 0); }
162
+ .cp-editor .cp-fallback-editor {
163
+ min-height: var(--cp-min-height, 0);
164
+ max-height: var(--cp-max-height, none);
165
+ }
166
+ .cp-editor .cm-scroller {
167
+ font-family: var(--cp-font-mono);
168
+ font-size: 13px;
169
+ line-height: 1.5;
170
+ }
171
+ .cp-editor .cm-editor.cm-focused { outline: none; }
172
+
173
+ .cp-fallback-editor {
174
+ width: 100%;
175
+ height: 100%;
176
+ border: 0;
177
+ padding: 8px;
178
+ resize: none;
179
+ background: var(--cp-bg);
180
+ color: var(--cp-fg);
181
+ font-family: var(--cp-font-mono);
182
+ font-size: 13px;
183
+ line-height: 1.5;
184
+ }
185
+ .cp-fallback-editor:focus { outline: none; }
186
+
187
+ .cp-stdin { border-top: 1px solid var(--cp-border); background: var(--cp-surface); }
188
+ .cp-stdin[hidden] { display: none; }
189
+ .cp-stdin.cp-attention {
190
+ box-shadow: inset 0 0 0 2px var(--cp-accent);
191
+ }
192
+ .cp-stdin label {
193
+ display: block;
194
+ padding: 6px 8px 0;
195
+ font-size: 11px;
196
+ text-transform: uppercase;
197
+ letter-spacing: 0.04em;
198
+ color: var(--cp-muted);
199
+ }
200
+ .cp-stdin textarea {
201
+ display: block;
202
+ width: 100%;
203
+ min-height: 52px;
204
+ border: 0;
205
+ padding: 4px 8px 8px;
206
+ background: transparent;
207
+ color: var(--cp-fg);
208
+ font-family: var(--cp-font-mono);
209
+ font-size: 13px;
210
+ resize: vertical;
211
+ }
212
+ .cp-stdin textarea:focus { outline: none; }
213
+
214
+ .cp-output {
215
+ border-top: 1px solid var(--cp-border);
216
+ padding: 8px 10px;
217
+ margin: 0;
218
+ max-height: 260px;
219
+ overflow: auto;
220
+ background: var(--cp-surface);
221
+ font-family: var(--cp-font-mono);
222
+ font-size: 13px;
223
+ line-height: 1.5;
224
+ white-space: pre-wrap;
225
+ word-break: break-word;
226
+ }
227
+ .cp-output[hidden] { display: none; }
228
+ .cp-output .cp-err { color: var(--cp-danger); }
229
+ .cp-output .cp-hint {
230
+ margin-top: 6px;
231
+ padding: 6px 8px;
232
+ border-left: 3px solid var(--cp-accent);
233
+ background: var(--cp-bg);
234
+ color: var(--cp-fg);
235
+ font-family: var(--cp-font-ui);
236
+ font-size: 12.5px;
237
+ white-space: normal;
238
+ }
239
+ .cp-output .cp-value { color: var(--cp-muted); }
240
+ .cp-output .cp-in { color: var(--cp-accent); }
241
+
242
+ .cp-prompt {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ gap: 4px;
246
+ }
247
+ .cp-prompt input {
248
+ font: inherit;
249
+ color: var(--cp-accent);
250
+ background: transparent;
251
+ border: 0;
252
+ border-bottom: 1px solid var(--cp-accent);
253
+ padding: 0 2px;
254
+ min-width: 12ch;
255
+ caret-color: var(--cp-accent);
256
+ }
257
+ .cp-prompt input:focus { outline: none; }
258
+ .cp-prompt button {
259
+ padding: 1px 6px;
260
+ font-size: 11px;
261
+ line-height: 1.4;
262
+ border-radius: 4px;
263
+ color: var(--cp-muted);
264
+ font-family: var(--cp-font-mono);
265
+ }
266
+ .cp-prompt button.cp-send { color: var(--cp-accent); border-color: var(--cp-accent); }
267
+
268
+ table.cp-table {
269
+ border-collapse: collapse;
270
+ margin: 6px 0;
271
+ font-size: 12px;
272
+ }
273
+ table.cp-table th, table.cp-table td {
274
+ border: 1px solid var(--cp-border);
275
+ padding: 3px 8px;
276
+ text-align: left;
277
+ }
278
+ table.cp-table th { background: var(--cp-bg); }
279
+
280
+ .cp-progress {
281
+ height: 2px;
282
+ background: linear-gradient(90deg, transparent, var(--cp-accent), transparent);
283
+ background-size: 40% 100%;
284
+ background-repeat: no-repeat;
285
+ animation: cp-slide 1.1s ease-in-out infinite;
286
+ }
287
+ .cp-progress[hidden] { display: none; }
288
+
289
+ /* language="react". The widget is the container: lesson columns are narrow even
290
+ on a large screen, so the viewport says nothing about the room there is. */
291
+ .cp-root.cp-has-preview { container-type: inline-size; }
292
+ .cp-preview-pane { display: none; }
293
+ .cp-has-preview .cp-preview-pane { display: flex; flex-direction: column; }
294
+ .cp-has-preview .cp-preview-pane .cp-output { flex: 1 1 auto; border-top: 0; min-height: 40px; }
295
+ .cp-preview {
296
+ position: relative;
297
+ flex: 0 0 var(--cp-preview-height, 320px);
298
+ height: var(--cp-preview-height, 320px);
299
+ border-top: 1px solid var(--cp-border);
300
+ background: var(--cp-surface);
301
+ overflow: hidden;
302
+ }
303
+ .cp-preview-frame {
304
+ display: block;
305
+ width: 100%;
306
+ height: 100%;
307
+ border: 0;
308
+ background: #ffffff;
309
+ }
310
+ .cp-preview-banner {
311
+ position: absolute;
312
+ left: 0;
313
+ right: 0;
314
+ bottom: 0;
315
+ max-height: 60%;
316
+ margin: 0;
317
+ padding: 10px 12px;
318
+ overflow: auto;
319
+ border-top: 3px solid var(--cp-danger);
320
+ background: var(--cp-bg);
321
+ color: var(--cp-danger);
322
+ font-family: var(--cp-font-mono);
323
+ font-size: 12.5px;
324
+ line-height: 1.5;
325
+ white-space: pre-wrap;
326
+ word-break: break-word;
327
+ }
328
+ .cp-preview-notice {
329
+ position: absolute;
330
+ top: 8px;
331
+ left: 8px;
332
+ right: 8px;
333
+ padding: 8px 10px;
334
+ border: 1px solid var(--cp-border);
335
+ border-left: 3px solid var(--cp-accent);
336
+ border-radius: 6px;
337
+ background: var(--cp-bg);
338
+ color: var(--cp-fg);
339
+ font-size: 13px;
340
+ white-space: pre-wrap;
341
+ }
342
+ .cp-preview-notice[hidden] { display: none; }
343
+ .cp-output .cp-warn { color: #9a6700; }
344
+ :host([data-resolved-theme="dark"]) .cp-output .cp-warn { color: #d29922; }
345
+ .cp-edited-note { font-size: 12px; color: var(--cp-muted); white-space: nowrap; }
346
+ .cp-edited-note[hidden] { display: none; }
347
+ .cp-tabs button.cp-edited::after { content: ' ●'; color: var(--cp-accent); }
348
+ .cp-tab-folder { opacity: 0.65; }
349
+
350
+ @container (max-width: 480px) {
351
+ .cp-tab-folder { display: none; }
352
+ }
353
+ /* The dividers between sections. */
354
+ .cp-split {
355
+ display: none;
356
+ flex: none;
357
+ background: var(--cp-surface);
358
+ border: solid var(--cp-border);
359
+ border-width: 0 1px;
360
+ touch-action: none;
361
+ cursor: col-resize;
362
+ }
363
+ .cp-split:hover, .cp-split:focus-visible { background: var(--cp-accent); outline: none; }
364
+ .cp-split-main, .cp-split-console {
365
+ width: auto;
366
+ height: 7px;
367
+ border-width: 1px 0;
368
+ cursor: row-resize;
369
+ }
370
+ .cp-has-preview .cp-split-main { display: block; }
371
+ .cp-has-preview .cp-split-console { display: block; }
372
+ .cp-split-console:has(+ .cp-output[hidden]) { display: none; }
373
+ .cp-dragging .cp-preview-frame { pointer-events: none; }
374
+ .cp-dragging { user-select: none; }
375
+ .cp-dragging-x { cursor: col-resize; }
376
+ .cp-dragging-y { cursor: row-resize; }
377
+ .cp-root.cp-editor-sized .cp-editor { height: var(--cp-editor-height) !important; flex: none !important; }
378
+ .cp-root.cp-editor-sized .cp-editor .cm-editor { height: 100% !important; max-height: none !important; }
379
+
380
+ /* Open in window. */
381
+ .cp-window-button { display: none; }
382
+ .cp-has-preview .cp-window-button { display: inline-block; }
383
+ :host([data-window]) .cp-window-button { display: none; }
384
+ .cp-window-note { display: none; align-items: center; gap: 8px; padding: 14px 12px; color: var(--cp-muted); }
385
+ .cp-root.cp-popped .cp-window-note { display: flex; }
386
+ .cp-root.cp-popped .cp-toolbar, .cp-root.cp-popped .cp-body { display: none; }
387
+ :host([data-window]) { height: 100%; margin: 0; contain: strict; }
388
+ :host([data-window]) .cp-root {
389
+ display: flex;
390
+ flex-direction: column;
391
+ height: 100%;
392
+ border: 0;
393
+ border-radius: 0;
394
+ }
395
+ :host([data-window]) .cp-body { flex: 1 1 0; min-height: 0; }
396
+ :host([data-window]) .cp-source, :host([data-window]) .cp-preview-pane { min-height: 0; }
397
+ :host([data-window]) .cp-root:not(.cp-editor-sized) .cp-editor { flex: 1 1 0; height: auto; min-height: 0; }
398
+ :host([data-window]) .cp-editor .cm-editor { height: 100%; max-height: none; }
399
+ :host([data-window]) .cp-preview-pane .cp-output { max-height: none; }
400
+ :host([data-window]) .cp-body { display: flex; flex-direction: column; }
401
+ :host([data-window]) .cp-source { flex: 1 1 0; display: flex; flex-direction: column; }
402
+ :host([data-window]) .cp-has-tree .cp-source { display: grid; }
403
+ :host([data-window]) .cp-preview-pane { flex: 1 1 0; }
404
+
405
+ /* file-view="tree": the files in a column beside the editor. */
406
+ .cp-root.cp-has-tree { container-type: inline-size; }
407
+ .cp-has-tree .cp-source {
408
+ display: grid;
409
+ grid-template-columns: var(--cp-tree-width, 200px) 7px minmax(0, 1fr);
410
+ grid-template-rows: 1fr auto;
411
+ }
412
+ .cp-has-tree .cp-source > :not(.cp-tabs, .cp-split-tree) { grid-column: 3; }
413
+ .cp-has-tree .cp-split-tree { display: block; grid-column: 2; grid-row: 1 / span 2; }
414
+ .cp-tabs.cp-tree {
415
+ display: block;
416
+ grid-column: 1;
417
+ grid-row: 1 / span 2;
418
+ padding: 6px 0;
419
+ overflow: auto;
420
+ border-bottom: 0;
421
+ }
422
+ .cp-tabs.cp-tree[hidden] { display: none; }
423
+ .cp-tabs.cp-tree button {
424
+ display: block;
425
+ width: 100%;
426
+ padding: 5px 8px 5px calc(22px + var(--cp-depth, 0) * 12px);
427
+ border-bottom: 0;
428
+ border-left: 2px solid transparent;
429
+ text-align: left;
430
+ overflow: hidden;
431
+ text-overflow: ellipsis;
432
+ }
433
+ .cp-tabs.cp-tree button[aria-pressed='true'] { border-left-color: var(--cp-accent); }
434
+ .cp-tabs.cp-tree button.cp-entry::before {
435
+ content: '▸';
436
+ display: inline-block;
437
+ width: 12px;
438
+ margin: 0 0 0 -12px;
439
+ }
440
+ .cp-tabs.cp-tree .cp-tree-folder {
441
+ padding-left: calc(8px + var(--cp-depth, 0) * 12px);
442
+ color: var(--cp-fg);
443
+ }
444
+ .cp-tabs.cp-tree .cp-tree-folder::before {
445
+ content: '⌄';
446
+ display: inline-block;
447
+ width: 14px;
448
+ color: var(--cp-muted);
449
+ }
450
+ .cp-tabs.cp-tree .cp-tree-folder[aria-expanded='false']::before { content: '›'; }
451
+ @container (max-width: 420px) {
452
+ .cp-has-tree .cp-source { display: block; }
453
+ .cp-has-tree .cp-split-tree { display: none; }
454
+ .cp-tabs.cp-tree { max-height: 150px; border-bottom: 1px solid var(--cp-border); }
455
+ }
456
+
457
+ @container (min-width: 760px) {
458
+ .cp-body { display: flex; align-items: stretch; }
459
+ .cp-source { flex: 0 0 var(--cp-source-width, calc(50% - 4px)); min-width: 0; }
460
+ .cp-preview-pane { flex: 1 1 0; min-width: 0; }
461
+ .cp-preview { border-top: 0; }
462
+ .cp-has-preview .cp-source:not(:has(.cp-tree)) { display: flex; flex-direction: column; }
463
+ .cp-has-preview .cp-source:not(:has(.cp-tree)) .cp-editor { flex: 1 1 auto; }
464
+ .cp-split.cp-split-main { width: 7px; height: auto; cursor: col-resize; border-width: 0 1px; }
465
+ :host([data-window]) .cp-body { flex-direction: row; }
466
+ :host([data-window]) .cp-source { flex: 0 0 var(--cp-source-width, calc(50% - 4px)); }
467
+ }
468
+
469
+ @keyframes cp-slide {
470
+ 0% { background-position: -40% 0; }
471
+ 100% { background-position: 140% 0; }
472
+ }
473
+
474
+ @media (prefers-reduced-motion: reduce) {
475
+ .cp-progress { animation: none; background-position: 0 0; background-size: 100% 100%; opacity: 0.4; }
476
+ }
477
+ `,f=e=>()=>import(`@codemirror/lang-javascript`).then(t=>t.javascript(e)),p={py:()=>import(`@codemirror/lang-python`).then(e=>e.python()),pyi:()=>import(`@codemirror/lang-python`).then(e=>e.python()),js:f({}),mjs:f({}),cjs:f({}),jsx:f({jsx:!0}),ts:f({typescript:!0}),mts:f({typescript:!0}),cts:f({typescript:!0}),tsx:f({typescript:!0,jsx:!0}),json:()=>import(`@codemirror/lang-json`).then(e=>e.json()),css:()=>import(`@codemirror/lang-css`).then(e=>e.css()),html:()=>import(`@codemirror/lang-html`).then(e=>e.html()),c:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),h:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),cpp:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),cc:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),cxx:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),hpp:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),hxx:()=>import(`@codemirror/lang-cpp`).then(e=>e.cpp()),java:()=>import(`@codemirror/lang-java`).then(e=>e.java())};async function m(e,t){let n=e.includes(`.`)?e.slice(e.lastIndexOf(`.`)+1).toLowerCase():``,r=t===`react`&&n===`js`?p.jsx:p[n];return r?r().catch(()=>null):null}var h=`code-playpad`,g=1,_=`snippets`,v=`progress`,y=null;function b(){return typeof indexedDB>`u`?Promise.reject(/* @__PURE__ */ Error(`IndexedDB unavailable`)):(y||=new Promise((e,t)=>{let n=indexedDB.open(h,g);n.onupgradeneeded=()=>{let e=n.result;e.objectStoreNames.contains(_)||e.createObjectStore(_),e.objectStoreNames.contains(v)||e.createObjectStore(v,{keyPath:`id`})},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)}).catch(e=>{throw y=null,e}),y)}function x(e,t,n){return b().then(r=>new Promise((i,a)=>{let o=n(r.transaction(e,t).objectStore(e));o.onsuccess=()=>i(o.result),o.onerror=()=>a(o.error)}))}function S(e,t){return x(_,`readwrite`,n=>n.put(t,e)).then(()=>void 0)}function C(e){return x(_,`readonly`,t=>t.get(e)).then(e=>e??null)}function w(e){return x(_,`readwrite`,t=>t.delete(e)).then(()=>void 0)}function T(e,t={}){let n={completed:!0,...t,id:e,updatedAt:Date.now()};return x(v,`readwrite`,e=>e.put(n)).then(()=>n)}function E(e){return x(v,`readonly`,t=>t.get(e)).then(e=>e??null)}function D(){return x(v,`readonly`,e=>e.getAll())}var O=`code-playpad`;function k(e){let t=e.replace(/\t/g,` `).split(`
478
+ `);for(;t.length&&t[0].trim()===``;)t.shift();for(;t.length&&t[t.length-1].trim()===``;)t.pop();let n=t.filter(e=>e.trim()!==``).reduce((e,t)=>Math.min(e,t.match(/^ */)[0].length),1/0);return(n===1/0?t:t.map(e=>e.slice(n))).join(`
479
+ `)}var A=2e5,j=`playpad-file`,M={python:`main.py`,javascript:`main.js`,typescript:`main.ts`,java:`Main.java`,c:`main.c`,cpp:`main.cpp`,react:`src/main.jsx`};function N(e){return M[e]??`main.txt`}var P=e=>e.tagName===`SCRIPT`&&e.getAttribute(`type`)===`text/plain`;function F(e){return[...e.children].filter(e=>e.tagName.toLowerCase()===j&&[...e.children].some(e=>!P(e))).map(e=>e.getAttribute(`name`)??``)}function I(e,t){let n={};for(let t of e.children){if(t.tagName.toLowerCase()!==j)continue;let e=t.getAttribute(`name`),r=[...t.children].find(P);e&&(n[e]=k((r??t).textContent??``))}if(Object.keys(n).length)return n;let r=[...e.childNodes].filter(e=>e.nodeType!==Node.ELEMENT_NODE||e.tagName.toLowerCase()!==j),i=r.some(e=>e.nodeType===Node.ELEMENT_NODE),a=k(r.filter(e=>!(i&&e.nodeType===Node.TEXT_NODE&&!(e.textContent??``).trim())).map(e=>e.textContent??``).join(``));return{[t]:a}}function L(e,t){try{let t=JSON.parse(e);if(t&&typeof t==`object`&&!Array.isArray(t)){let e=t;return Object.keys(e).length?e:null}}catch{}return e?{[t]:e}:null}var R=typeof HTMLElement>`u`?class{}:HTMLElement,z=class extends R{static get observedAttributes(){return[`language`,`height`,`min-height`,`max-height`,`preview-height`,`file-view`,`open-file`,`theme`,`readonly`,`autorun`,`stdin`,`persist`,`entry`,`practice`]}#e;#t;#n;#r;#i;#a;#o;#s;#c;#l;#u=null;#d=null;#f=null;#p=null;#m={};#h={};#g=`main.py`;#_=null;#v;#y=!1;#b=null;#x=!1;#S=!1;#C=null;#w=null;#T=null;#E=()=>this.#ue();#D=null;#O=!0;#k=[];#A=null;#j=null;#M=null;#N=0;#P=!1;#F=null;#I;#L;#R=null;#z=[];#B=/* @__PURE__ */ new Set;#V=null;#H=null;#U=null;constructor(){super(),this.#e=this.attachShadow({mode:`open`})}get code(){return this.#u?this.#u.getCode():this.#m[this.#g]??``}set code(e){this.#m[this.#g]=e,this.#X()}get practice(){if(this.#F)return this.#F;let e=this.getAttribute(`practice`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}set practice(e){this.#F=e}get files(){return this.#ne()}set files(e){let t=Object.keys(e);if(!t.length)return;let n=Object.keys(this.#h);n.length===1&&!this.#h[n[0]]&&(this.#h={...e}),this.#m={...e},this.#z=[],t.includes(this.#g)||(this.#g=this.#ee()),this.#u?.forgetFiles([]),this.#X(),this.#re(),this.#Y()}get entry(){return this.#ee()}set entry(e){this.setAttribute(`entry`,e)}openFile(e){if(e in this.#m){this.#u&&(this.#m[this.#g]=this.#u.getCode(),this.#u.openFile(e,this.#m[e]??``)),this.#g=e;for(let t=e.indexOf(`/`);t!==-1;t=e.indexOf(`/`,t+1))this.#B.delete(e.slice(0,t));this.#re(),this.#Y()}}get stdin(){return this.#l?this.#l.value:``}set stdin(e){this.#l&&(this.#l.value=e,this.#c.hidden=!1)}get language(){return this.getAttribute(`language`)||`python`}set language(e){this.setAttribute(`language`,e)}getCode(){return this.code}getFiles(){return this.#ne()}reset(){this.#m={...this.#h},this.#g=this.#ee(),this.#u?.forgetFiles([]),this.#X(),this.#re(),this.#pe(),this.#s.hidden=!0,this.#oe(),this.#R=null,this.#se(),this.#fe(this.#f?.ready?`${this.#f.label} ready`:``,`idle`)}async run(){if(this.#V)return this.#V.widget.run();if(this.#y)return;await this.#d;let e=this.#ne(),t=this.getAttribute(`entry`);if(t&&!(t in e))return this.#Q(t,e);this.#Z=!1;let n=this.#ee(),r=e[n]??``;this.#y=!0,this.#x=!1,this.#n.disabled=!0,this.#r.hidden=!1,this.#pe(),this.#s.hidden=!1;let i=this.practice;this.hasAttribute(`practice`)&&!i&&this.#me(`The practice attribute is not valid JSON.
480
+ `,`cp-err`),this.#ae()&&(this.#R=e,this.#se(),this.#z.length&&this.#me(`${this.#z.join(`, `)}: HTML tags in this file's markup were read as elements, so its code is incomplete. Write < as &lt;, or put the code in <script type="text/plain">. A <script> left unescaped runs on the host page.\n`,`cp-err`)),this.#be(`py-run`,{code:r,entry:n,files:e,language:this.language,...i?{practice:i}:{}});try{let t=await this.#J();if(this.#x)throw new c;if(i&&!t.supportsPractice)throw Error(`Practice mode is not available for ${t.label}.`);this.#fe(`Queued…`,`busy`),this.#o.hidden=!1;let a=t.run(r,{stdin:this.#l.value,files:e,entry:n,onStart:()=>this.#fe(`Running…`,`busy`),onProgress:e=>this.#fe(e.message,`busy`),onStdout:e=>this.#me(e),onStderr:e=>this.#me(e,`cp-err`),onEcho:e=>this.#me(e,`cp-in`),onStdinRequest:()=>this.#ge(),...i?{practice:i}:{},...this.#ae()?{preview:{host:this.#I,onStart:()=>this.#fe(`Running…`,`busy`),onConsole:(e,t)=>{this.#me(`${t}\n`,e===`error`?`cp-err`:e===`warn`?`cp-warn`:void 0),this.#xe(`playpad-console`,{level:e,text:t,language:this.language})},onRender:e=>this.#xe(`playpad-render`,{rootText:e,language:this.language}),onError:e=>{this.#fe(`Error`,`error`),this.#be(`py-error`,{...e,language:this.language})}}}:{}});this.#b={runtime:t,run:a};let o=await a;if(o.table&&this.#ye(o.table),o.value!==void 0&&this.#me(`${o.value}\n`,`cp-value`),o.hint&&this.#ve(o.hint,u.has(o)),o.failed=t.statusDecidesFailure?o.failed===!0:!!o.stderr,o.failed)this.#fe(`Error · ${o.durationMs} ms`,`error`),this.#be(`py-error`,{message:o.problem?.message??o.stderr??``,hint:o.hint,language:this.language,...o.problem??{}});else if(o.cases){let e=o.cases.filter(e=>e.expected!==void 0).length,t=o.passed===e;this.#fe(e?`${o.passed} of ${e} passed · ${o.durationMs} ms`:`Done in ${o.durationMs} ms`,t?`ok`:`error`)}else this.#fe(`Done in ${o.durationMs} ms`,`ok`);return this.#be(`py-output`,{...o,language:this.language}),o}catch(e){if(e instanceof c)this.#me(`
481
+ [stopped]
482
+ `,`cp-err`),this.#fe(`Stopped`,`error`);else{let t=e instanceof Error?e.message:String(e);this.#me(t+`
483
+ `,`cp-err`),this.#fe(`Failed to start`,`error`),this.#be(`py-error`,{message:t,language:this.language})}return}finally{this.#b=null,this.#_e(null),this.#he(),this.#y=!1,this.#n.disabled=!1,this.#r.hidden=!(this.#ae()&&this.#f?.hasLivePreview?.(this.#I)),this.#o.hidden=!0}}stop(){this.#V?this.#V.widget.stop():this.#b?this.#b.runtime.stop?.(this.#b.run):this.#y?this.#x=!0:this.#ae()&&this.#f?.hasLivePreview?.(this.#I)&&(this.#oe(),this.#r.hidden=!0,this.#fe(`Stopped`,`idle`))}openWindow(){if(this.#V)return this.#V.window.focus(),!0;if(this.#H)return!1;let e=window.open(``,`_blank`,`popup,width=1280,height=800`);if(!e)return this.#fe(`The browser blocked the new window`,`error`),!1;let t=this.#ne(),n=this.#g,r=document.createElement(this.localName);for(let e of[`language`,`entry`,`file-view`,`preview-height`,`theme`,`readonly`,`stdin`]){let t=this.getAttribute(e);t!==null&&r.setAttribute(e,t)}r.setAttribute(`data-window`,``),r.#H=this,r.#U={...this.#h},r.#_=t,this.#oe(),this.#r.hidden=!0,e.document.title=`Code playground`,e.document.documentElement.style.height=`100%`,e.document.body.style.cssText=`margin:0;height:100%`,e.document.body.append(r);let i=()=>this.closeWindow();return window.addEventListener(`pagehide`,i),this.#V={window:e,widget:r,onLeave:i,timer:setInterval(()=>{e.closed&&this.closeWindow()},300)},this.#e.querySelector(`.cp-root`)?.classList.add(`cp-popped`),r.#d?.then(()=>(r.openFile(n),r.run())),!0}closeWindow(){let e=this.#V;if(!e)return;this.#V=null,clearInterval(e.timer),window.removeEventListener(`pagehide`,e.onLeave);let{widget:t}=e,n=t.#ne(),r=t.#g;t.#oe(),t.remove(),e.window.closed||e.window.close(),this.#e.querySelector(`.cp-root`)?.classList.remove(`cp-popped`),this.files=n,this.openFile(r),this.#fe(``,`idle`)}prewarm(){return this.#J()}connectedCallback(){this.#P?this.#q():!this.textContent?.trim()&&document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>this.isConnected&&this.#W(),{once:!0}):this.#W()}disconnectedCallback(){this.#C?.disconnect(),this.#C=null,this.#w?.removeEventListener(`change`,this.#E),this.#w=null,this.#T?.disconnect(),this.#T=null,this.#D&&clearTimeout(this.#D),this.#_e(null),setTimeout(()=>{this.isConnected||(this.closeWindow(),this.#oe(),this.#u&&(this.#_=this.#ne(),this.#u.destroy(),this.#u=null,this.#d=null,this.#P=!1))},0)}attributeChangedCallback(e,t,n){if(this.#P)switch(e){case`theme`:this.#ue();break;case`height`:case`min-height`:case`max-height`:case`preview-height`:this.#de();break;case`readonly`:this.#u?.setReadOnly(n!==null);break;case`stdin`:this.#c.hidden=n===null&&!this.#l.value;break;case`entry`:case`file-view`:this.#re();break;case`language`:this.#oe(),this.#e.querySelector(`.cp-root`)?.classList.toggle(`cp-has-preview`,this.#ae()),this.#f=null,this.#p=null,this.#S=!1,this.#J().catch(()=>{})}}#W(){this.#P=!0,this.#h=this.#U??I(this,this.#$()),this.#z=this.#ae()?F(this):[],this.#m=this.#_??{...this.#h},this.#g=this.#te(),this.#G(),this.#ue(),this.#de();let t=this.getAttribute(`persist`);this.#d=(async()=>{if(t&&this.#_==null){let e=await C(t).catch(()=>null),n=e==null?null:L(e,this.#$());if(n){let e={},t=Object.keys(this.#h),r=Object.keys(n);for(let r of t)e[r]=n[r]??this.#h[r];t.length===1&&r.length===1&&!(t[0]in n)&&(e[t[0]]=n[r[0]]),this.#m=e,this.#g=this.#te()}}this.#re();let n=await s(this.language).catch(()=>null),r=n?await n.editorLanguage().catch(()=>null):null;this.#u=await e({parent:this.#t,doc:this.#m[this.#g]??``,name:this.#g,dark:this.#le(),readOnly:this.hasAttribute(`readonly`),language:r,onRun:()=>void this.run(),onChange:e=>this.#ce(e)}),this.#_=null,this.#Y()})(),this.#q()}#G(){let e=document.createElement(`style`);e.textContent=d;let t=document.createElement(`div`);t.className=this.#ae()?`cp-root cp-has-preview`:`cp-root`,t.innerHTML=`
484
+ <div class="cp-toolbar">
485
+ <button class="cp-primary" part="run-button" data-action="run">Run</button>
486
+ <button class="cp-danger" part="stop-button" data-action="stop" hidden>Stop</button>
487
+ <button data-action="reset">Reset</button>
488
+ <span class="cp-spacer"></span>
489
+ <button part="window-button" class="cp-window-button" data-action="window" title="Open in a separate window you can resize">Open in window</button>
490
+ <span class="cp-edited-note" hidden>Edited since the last run</span>
491
+ <span class="cp-status" data-state="idle"></span>
492
+ </div>
493
+ <div class="cp-progress" hidden></div>
494
+ <div class="cp-window-note">
495
+ <span>Running in a separate window.</span>
496
+ <button data-action="show-window">Show window</button>
497
+ <button data-action="close-window">Bring back here</button>
498
+ </div>
499
+ <div class="cp-body">
500
+ <div class="cp-source">
501
+ <div class="cp-tabs" part="tabs" hidden></div>
502
+ <div class="cp-split cp-split-tree" role="separator" tabindex="0" aria-orientation="vertical" aria-label="Resize the file tree"></div>
503
+ <div class="cp-editor"></div>
504
+ <div class="cp-stdin" hidden>
505
+ <label>Standard input — one value per line</label>
506
+ <textarea rows="2" spellcheck="false"></textarea>
507
+ </div>
508
+ </div>
509
+ <div class="cp-split cp-split-main" role="separator" tabindex="0" aria-label="Resize the code and the preview"></div>
510
+ <div class="cp-preview-pane">
511
+ <div class="cp-preview" part="preview"></div>
512
+ <div class="cp-split cp-split-console" role="separator" tabindex="0" aria-orientation="horizontal" aria-label="Resize the preview and the console"></div>
513
+ <pre class="cp-output" part="output" hidden></pre>
514
+ </div>
515
+ </div>`,this.#e.replaceChildren(e,t),this.#n=t.querySelector(`[data-action="run"]`),this.#r=t.querySelector(`[data-action="stop"]`),this.#i=t.querySelector(`[data-action="reset"]`),this.#a=t.querySelector(`.cp-status`),this.#o=t.querySelector(`.cp-progress`),this.#v=t.querySelector(`.cp-tabs`),this.#t=t.querySelector(`.cp-editor`),this.#c=t.querySelector(`.cp-stdin`),this.#l=t.querySelector(`.cp-stdin textarea`),this.#s=t.querySelector(`.cp-output`),this.#I=t.querySelector(`.cp-preview`),this.#L=t.querySelector(`.cp-edited-note`),this.#ae()||t.querySelector(`.cp-source`).after(this.#s),this.#c.hidden=!this.hasAttribute(`stdin`),this.#l.addEventListener(`input`,()=>this.#c.classList.remove(`cp-attention`)),this.#v.addEventListener(`click`,e=>{let t=e.target,n=t.closest(`button[data-file]`);n&&this.openFile(n.getAttribute(`data-file`));let r=t.closest(`button[data-folder]`)?.dataset.folder;r!==void 0&&(this.#B.delete(r)||this.#B.add(r),this.#re())}),this.#n.addEventListener(`click`,()=>void this.run()),this.#r.addEventListener(`click`,()=>this.stop()),this.#i.addEventListener(`click`,()=>this.reset()),t.querySelector(`[data-action="window"]`).addEventListener(`click`,()=>this.openWindow()),t.querySelector(`[data-action="show-window"]`).addEventListener(`click`,()=>this.openWindow()),t.querySelector(`[data-action="close-window"]`).addEventListener(`click`,()=>this.closeWindow()),this.#K(t);let n=()=>void this.#J().catch(()=>{});this.#t.addEventListener(`pointerdown`,n,{once:!0}),this.#t.addEventListener(`focusin`,n,{once:!0})}#K(e){let t=e.querySelector(`.cp-body`),n=e.querySelector(`.cp-source`),r=e.querySelector(`.cp-preview`),i=e.querySelector(`.cp-preview-pane`),a=this.#v,o=(e,t,n)=>Math.round(Math.max(t,Math.min(e,Math.max(t,n)))),s=(t,n)=>e.style.setProperty(t,`${n}px`),c=(t,n,r,i,a)=>{let o=()=>t.setAttribute(`aria-orientation`,n()===`x`?`vertical`:`horizontal`);t.addEventListener(`pointerdown`,a=>{a.preventDefault();let o=n()===`x`,s=o?a.clientX:a.clientY,c=r();t.setPointerCapture(a.pointerId),e.classList.add(`cp-dragging`,o?`cp-dragging-x`:`cp-dragging-y`);let l=e=>i(c+(o?e.clientX:e.clientY)-s),u=()=>{e.classList.remove(`cp-dragging`,`cp-dragging-x`,`cp-dragging-y`),t.removeEventListener(`pointermove`,l),t.removeEventListener(`pointerup`,u),t.removeEventListener(`pointercancel`,u)};t.addEventListener(`pointermove`,l),t.addEventListener(`pointerup`,u),t.addEventListener(`pointercancel`,u)}),t.addEventListener(`keydown`,e=>{let t={ArrowLeft:-24,ArrowUp:-24,ArrowRight:24,ArrowDown:24}[e.key];t&&(e.preventDefault(),i(r()+t))}),t.addEventListener(`dblclick`,a),t.addEventListener(`focus`,o),t.addEventListener(`pointerenter`,o)},l=()=>getComputedStyle(t).flexDirection===`row`&&getComputedStyle(t).display===`flex`;c(e.querySelector(`.cp-split-tree`),()=>`x`,()=>a.getBoundingClientRect().width,e=>s(`--cp-tree-width`,o(e,90,n.getBoundingClientRect().width-160)),()=>e.style.removeProperty(`--cp-tree-width`)),c(e.querySelector(`.cp-split-main`),()=>l()?`x`:`y`,()=>l()?n.getBoundingClientRect().width:this.#t.getBoundingClientRect().height,n=>{l()?s(`--cp-source-width`,o(n,200,t.getBoundingClientRect().width-200)):(s(`--cp-editor-height`,o(n,80,2e3)),e.classList.add(`cp-editor-sized`))},()=>{e.style.removeProperty(`--cp-source-width`),e.style.removeProperty(`--cp-editor-height`),e.classList.remove(`cp-editor-sized`)}),c(e.querySelector(`.cp-split-console`),()=>`y`,()=>r.getBoundingClientRect().height,e=>s(`--cp-preview-height`,o(e,60,l()||this.#H?i.getBoundingClientRect().height-40:2e3)),()=>e.style.removeProperty(`--cp-preview-height`))}#q(){this.#H||this.#C||typeof IntersectionObserver>`u`||(this.#C=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(this.#C?.disconnect(),this.#C=null,this.#J().then(()=>{this.hasAttribute(`autorun`)&&this.run()}).catch(()=>{}))},{rootMargin:`300px`}),this.#C.observe(this))}#J(){return this.#p||=(async()=>{let e=await s(this.language);this.#f=e,!e.ready&&!this.#Z&&(this.#o.hidden=!1,this.#fe(`Loading ${e.label}…`,`busy`));try{await e.load(e=>{!this.#y&&!this.#Z&&this.#fe(e.message,e.phase===`ready`?`ok`:`busy`)})}catch(e){this.#p=null,this.#fe(`Runtime unavailable`,`error`);let t=e instanceof Error?e.message:String(e);throw this.#me(t+`
516
+ `,`cp-err`),this.#s.hidden=!1,e}finally{this.#o.hidden=!0}return this.#Y(),this.#S||(this.#S=!0,this.#Z||this.#fe(`${e.label} ready`,`ok`),this.#be(`py-ready`,{language:e.id,label:e.label})),e})(),this.#p}async#Y(){if(!this.#u)return;let e=this.#g,t=await m(e,this.language);if(t){this.#g===e&&this.#u?.setLanguage(t);return}let n=e.includes(`.`)?null:await(this.#f??await s(this.language).catch(()=>null))?.editorLanguage().catch(()=>null);this.#g===e&&this.#u?.setLanguage(n??[])}#X(){this.#u?this.#u.setCode(this.#m[this.#g]??``):this.#d?.then(()=>{this.#u?.setCode(this.#m[this.#g]??``),this.#re()})}#Z=!1;#Q(e,t){let n=Object.keys(t),r=`This widget's entry is "${e}", but it has no file of that name${n.length?` (its files: ${n.join(`, `)})`:``}. Set entry to one of its files.\n`;this.#pe(),this.#s.hidden=!1,this.#me(r,`cp-err`),this.#Z=!0,this.#fe(`Error · entry not found`,`error`);let i={stdout:``,stderr:r,durationMs:0,failed:!0};return this.#be(`py-error`,{message:r,language:this.language}),this.#be(`py-output`,{...i,language:this.language}),i}#$(){return this.getAttribute(`entry`)||N(this.language)}#ee(){let e=Object.keys(this.#m),t=this.getAttribute(`entry`);if(t&&e.includes(t))return t;let n=this.#ae()&&e.includes(`index.html`)?`index.html`:N(this.language);return e.includes(n)?n:e[0]??n}#te(){let e=this.getAttribute(`open-file`);return e&&e in this.#m?e:this.#ee()}#ne(){if(this.#V)return this.#V.widget.#ne();let e={...this.#m};if(this.#u)for(let t of Object.keys(e))e[t]=this.#u.fileCode(t,e[t]);return e}#re(){let e=Object.keys(this.#m);this.#v.hidden=e.length<2;let t=!this.#v.hidden&&this.getAttribute(`file-view`)===`tree`;if(this.#v.classList.toggle(`cp-tree`,t),this.#e.querySelector(`.cp-root`)?.classList.toggle(`cp-has-tree`,t),this.#v.hidden){this.#v.replaceChildren();return}if(t){this.#ie(e),this.#se();return}let n=this.#ee();this.#v.replaceChildren(...e.map(e=>{let t=document.createElement(`button`);t.dataset.file=e;let r=e.lastIndexOf(`/`)+1;if(r){let n=document.createElement(`span`);n.className=`cp-tab-folder`,n.textContent=e.slice(0,r),t.setAttribute(`aria-label`,e),t.append(n)}return t.append(e.slice(r)),t.setAttribute(`aria-pressed`,String(e===this.#g)),e===n&&(t.classList.add(`cp-entry`),t.title=`entry point — this is the file Run executes`),t})),this.#se()}#ie(e){let t=this.#ee(),n=[],r=(i,a)=>{let o=e.filter(e=>e.startsWith(i)).map(e=>e.slice(i.length)),s=[...new Set(o.filter(e=>e.includes(`/`)).map(e=>e.slice(0,e.indexOf(`/`))))].sort();for(let e of s){let t=i+e,o=!this.#B.has(t),s=document.createElement(`button`);s.className=`cp-tree-folder`,s.dataset.folder=t,s.textContent=e,s.setAttribute(`aria-expanded`,String(o)),s.style.setProperty(`--cp-depth`,String(a)),n.push(s),o&&r(`${t}/`,a+1)}for(let e of o.filter(e=>!e.includes(`/`)).sort()){let r=i+e,o=document.createElement(`button`);o.dataset.file=r,o.textContent=e,o.setAttribute(`aria-label`,r),o.setAttribute(`aria-pressed`,String(r===this.#g)),o.style.setProperty(`--cp-depth`,String(a)),r===t&&(o.classList.add(`cp-entry`),o.title=`entry point — this is the file Run executes`),n.push(o)}};r(``,0),this.#v.replaceChildren(...n)}#ae(){return this.language===`react`}#oe(){this.#I&&(this.#f?.dispose?.(this.#I),this.#I.replaceChildren())}#se(){if(!this.#ae())return;let e=this.#ne();for(let t of this.#v.querySelectorAll(`button[data-file]`)){let n=t.dataset.file;t.classList.toggle(`cp-edited`,e[n]!==this.#h[n])}let t=this.#R;this.#L.hidden=!t||Object.keys(e).every(n=>e[n]===t[n])}#ce(e){this.#m[this.#g]=e,this.#se();let t=this.getAttribute(`persist`);if(!t)return;this.#D&&clearTimeout(this.#D);let n=JSON.stringify(this.#ne());this.#D=setTimeout(()=>void S(t,n).catch(()=>{}),400)}#le(){let e=this.getAttribute(`theme`)??`auto`;if(e===`dark`)return!0;if(e===`light`)return!1;let t=typeof document>`u`?``:getComputedStyle(document.documentElement).colorScheme,n=t.includes(`dark`);return n===t.includes(`light`)?!!this.#w?.matches:n}#ue(){!this.#w&&typeof matchMedia==`function`&&(this.#w=matchMedia(`(prefers-color-scheme: dark)`),this.#w.addEventListener(`change`,this.#E)),!this.#T&&typeof MutationObserver==`function`&&typeof document<`u`&&(this.#T=new MutationObserver(this.#E),this.#T.observe(document.documentElement,{attributes:!0,attributeFilter:[`class`,`style`,`data-theme`,`data-color-scheme`,`color-scheme`]}));let e=this.#le();this.setAttribute(`data-resolved-theme`,e?`dark`:`light`),this.#u?.setTheme(e)}#de(){for(let[e,t]of[[`height`,`--cp-height`],[`min-height`,`--cp-min-height`],[`max-height`,`--cp-max-height`],[`preview-height`,`--cp-preview-height`]]){let n=this.getAttribute(e);n===null||n===``?this.style.removeProperty(t):this.style.setProperty(t,/^\d+$/.test(n)?`${n}px`:n)}}#fe(e,t){this.#a.textContent=e,this.#a.dataset.state=t}#pe(){this.#M&&clearTimeout(this.#M),this.#M=null,this.#k=[],this.#N=0,this.#s.replaceChildren(),this.#O=!0}#me(e,t){if(!e)return;let n=t===`cp-err`&&!this.#O?`\n${e}`:e;this.#O=n.endsWith(`
517
+ `),this.#k.push({text:n,className:t}),this.#M??=setTimeout(()=>this.#he(),16)}#he(){if(this.#M&&clearTimeout(this.#M),this.#M=null,!this.#k.length)return;let e=this.#k;this.#k=[];let t=document.createDocumentFragment(),n=``,r,i=()=>{if(!n)return;let e=document.createElement(`span`);r&&(e.className=r),e.textContent=n,t.appendChild(e),n=``};for(let t of e){if(this.#N>=A)break;let e=t.text;this.#N+e.length>A&&(e=e.slice(0,A-this.#N)+`
518
+ … output truncated for display — the full text is in the playpad-output event
519
+ `),this.#N+=t.text.length,r!==t.className&&(i(),r=t.className),n+=e}i(),this.#s.hidden=!1;let a=this.#s.querySelector(`.cp-prompt`);a?this.#s.insertBefore(t,a):this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight}#ge(){return this.#he(),this.#_e(null),new Promise(e=>{let t=document.createElement(`form`);t.className=`cp-prompt`;let n=document.createElement(`input`);n.type=`text`,n.setAttribute(`aria-label`,`Program input — press Enter to send`),n.autocapitalize=`off`,n.autocomplete=`off`,n.spellcheck=!1;let r=document.createElement(`button`);r.type=`submit`,r.className=`cp-send`,r.textContent=`⏎`,r.title=`Send this line (Enter)`;let i=document.createElement(`button`);i.type=`button`,i.className=`cp-eof`,i.textContent=`EOF`,i.title=`End of input (Ctrl-D) — makes input() raise EOFError`;let a=n=>{this.#A===e&&(this.#A=null,this.#j=null,t.remove(),n===null?this.#me(`
520
+ `,`cp-in`):this.#me(n+`
521
+ `,`cp-in`),this.#he(),e(n))};t.addEventListener(`submit`,e=>{e.preventDefault(),a(n.value)}),i.addEventListener(`click`,()=>a(null)),n.addEventListener(`keydown`,e=>{(e.key===`Escape`||e.key===`d`&&e.ctrlKey)&&(e.preventDefault(),a(null))}),t.append(n,r,i),this.#s.hidden=!1,this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight,this.#A=e,this.#j=a,n.focus({preventScroll:!0})})}#_e(e){this.#j?.(e),this.#j=null,this.#A=null}#ve(e,t){this.#he();let n=document.createElement(`div`);n.className=`cp-hint`,n.textContent=e,this.#s.hidden=!1,this.#s.appendChild(n),this.#s.scrollTop=this.#s.scrollHeight,t&&(this.#c.hidden=!1,this.#c.classList.add(`cp-attention`))}#ye(e){this.#he();let t=document.createElement(`table`);t.className=`cp-table`;let n=t.createTHead().insertRow();for(let t of e.columns){let e=document.createElement(`th`);e.textContent=t,n.appendChild(e)}let r=t.createTBody();for(let t of e.rows){let e=r.insertRow();for(let n of t)e.insertCell().textContent=n==null?``:String(n)}this.#s.hidden=!1,this.#s.appendChild(t)}#be(e,t){this.#xe(e,t),this.#xe(`playpad-${e.slice(3)}`,t)}#xe(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0})),this.#H?.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}};function B(e=O){typeof window<`u`&&typeof customElements<`u`&&(customElements.get(e)||customElements.define(e,z))}B();export{E as a,T as c,l as d,s as f,e as h,w as i,u as l,a as m,B as n,C as o,o as p,D as r,S as s,z as t,c as u};
package/dist/element.d.ts CHANGED
@@ -16,6 +16,22 @@ export interface ErrorEventDetail {
16
16
  /** Plain-language guidance, when the runtime could work out the cause. */
17
17
  hint?: string;
18
18
  language: string;
19
+ /** React: where the error is, when known. */
20
+ file?: string;
21
+ line?: number;
22
+ column?: number;
23
+ }
24
+ /** React: one line of the preview's console. */
25
+ export interface ConsoleEventDetail {
26
+ level: 'log' | 'info' | 'warn' | 'error';
27
+ text: string;
28
+ language: string;
29
+ }
30
+ /** React: the text of the preview's #root, after it changes. Learner code can
31
+ * forge this, so it is a lesson check, not proof of completion. */
32
+ export interface RenderEventDetail {
33
+ rootText: string;
34
+ language: string;
19
35
  }
20
36
  /**
21
37
  * `class X extends HTMLElement` is evaluated when the module loads, so importing
@@ -64,6 +80,16 @@ export declare class CodePlayPadElement extends ElementBase {
64
80
  * The runtime is shared, so a bare runtime.stop() would kill whichever
65
81
  * widget happens to be running. */
66
82
  stop(): void;
83
+ /**
84
+ * Moves the project to a separate window that the reader can resize, and runs
85
+ * it there. The widget in that window is this page's code, so the page keeps
86
+ * getting its events. Edits come back when the window closes, or with
87
+ * closeWindow(). Call it from a click: browsers block other popups. Returns
88
+ * false when the browser blocked it.
89
+ */
90
+ openWindow(): boolean;
91
+ /** Closes the separate window, and brings its edits back to this widget. */
92
+ closeWindow(): void;
67
93
  /** Boot the runtime without running anything. Safe to call repeatedly. */
68
94
  prewarm(): Promise<Runtime>;
69
95
  connectedCallback(): void;
package/dist/element.js CHANGED
@@ -1 +1 @@
1
- export{t as CodePlayPadElement,n as defineCodePlayPad}from"./element-CQGYVEF7.js";
1
+ export{t as CodePlayPadElement,n as defineCodePlayPad}from"./element-COcjM0hI.js";
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * code-playpad — MIT © 2026 Seung Hun Lee
3
+ * Bundles CodeMirror 6 and Lezer (MIT) © Marijn Haverbeke and others.
4
+ * Full third-party licences: THIRD-PARTY-NOTICES.txt in this package.
5
+ */
6
+ var e=/* @__PURE__ */ new WeakSet;export{e as t};
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export { CodePlayPadElement, defineCodePlayPad } from './element.js';
2
- export type { ErrorEventDetail, OutputEventDetail, RunEventDetail } from './element.js';
2
+ export type { ConsoleEventDetail, ErrorEventDetail, OutputEventDetail, RenderEventDetail, RunEventDetail } from './element.js';
3
3
  export { createEditor } from './editor.js';
4
4
  export type { EditorHandle, EditorOptions } from './editor.js';
5
5
  export { getRuntime, listRuntimes, registerRuntime } from './runtime/registry.js';
6
- export { configurePython, configureCpp, configureTypeScript, pyodideAssetUrls, PYODIDE_VERSION } from './runtime/config.js';
6
+ export { configurePython, configureCpp, configureReact, REACT_VERSION, configureTypeScript, pyodideAssetUrls, PYODIDE_VERSION } from './runtime/config.js';
7
7
  export { RunStopped, WorkerUnavailable } from './types.js';
8
- export type { CaseResult, CaseStatus, Files, LoadProgress, PracticeCase, PracticeSpec, PracticeValue, Result, Runtime, RunOptions, Table } from './types.js';
8
+ export type { CaseResult, CaseStatus, Files, LoadProgress, PreviewBinding, PracticeCase, PracticeSpec, PracticeValue, Result, Runtime, RunOptions, Table } from './types.js';
9
9
  export { allProgress, clearSnippet, getProgress, loadSnippet, saveSnippet, setProgress } from './progress.js';
10
10
  export type { ProgressRecord } from './progress.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as e,c as t,d as n,f as r,h as i,i as a,m as o,n as s,o as c,p as l,r as u,s as d,t as f,u as p}from"./element-CQGYVEF7.js";import{c as m,i as h,n as g,r as _,t as v}from"./config-CT3H6Oum.js";export{f as CodePlayPadElement,v as PYODIDE_VERSION,p as RunStopped,n as WorkerUnavailable,u as allProgress,a as clearSnippet,g as configureCpp,_ as configurePython,h as configureTypeScript,i as createEditor,s as defineCodePlayPad,e as getProgress,r as getRuntime,l as listRuntimes,c as loadSnippet,m as pyodideAssetUrls,o as registerRuntime,d as saveSnippet,t as setProgress};
1
+ import{a as e,c as t,d as n,f as r,h as i,i as a,m as o,n as s,o as c,p as l,r as u,s as d,t as f,u as p}from"./element-COcjM0hI.js";import{t as m}from"./react-version.generated-BNNJmf7J.js";import{a as h,i as g,n as _,r as v,t as y,u as b}from"./config-DB58UVsk.js";export{f as CodePlayPadElement,y as PYODIDE_VERSION,m as REACT_VERSION,p as RunStopped,n as WorkerUnavailable,u as allProgress,a as clearSnippet,_ as configureCpp,v as configurePython,g as configureReact,h as configureTypeScript,i as createEditor,s as defineCodePlayPad,e as getProgress,r as getRuntime,l as listRuntimes,c as loadSnippet,b as pyodideAssetUrls,o as registerRuntime,d as saveSnippet,t as setProgress};