code-playpad 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,13 +9,20 @@ No backend, no iframe, no sign-up.
9
9
  **[Live demo →](https://code-playpad.swengr.com)**
10
10
 
11
11
  ```html
12
- <script type="module" src="https://cdn.jsdelivr.net/npm/code-playpad@0.2.0"></script>
12
+ <script type="module" src="https://cdn.jsdelivr.net/npm/code-playpad@0.2.1"></script>
13
13
 
14
- <code-playpad height="180">
14
+ <code-playpad height="auto">
15
15
  print("hello from a static page")
16
16
  </code-playpad>
17
17
  ```
18
18
 
19
+ **Contents** — [Languages](#languages) · [Install](#install) · [Attributes](#attributes)
20
+ · [Sizing](#sizing) · [Several files](#several-files) · [Input](#input)
21
+ · [Controlling it from JavaScript](#controlling-it-from-javascript)
22
+ · [Events and grading](#events-and-grading) · [Saving work](#saving-work)
23
+ · [Light and dark](#light-and-dark) · [Styling](#styling) · [Framework recipes](#framework-recipes)
24
+ · [Production](#production) · [Worth knowing](#worth-knowing)
25
+
19
26
  ## Languages
20
27
 
21
28
  | `language` | Runtime | Downloaded at runtime |
@@ -24,16 +31,42 @@ No backend, no iframe, no sign-up.
24
31
  | `javascript` | The browser's own engine, in a Worker | nothing |
25
32
  | `typescript` | Same engine, types stripped before running | ~50 KB, once, cached |
26
33
 
27
- Each language loads **only on pages that use it**. A Python-only page never
28
- downloads a byte of the JavaScript runtime; a JavaScript-only page never touches
29
- Pyodide's 11 MB.
34
+ ```html
35
+ <code-playpad height="auto">print("python is the default")</code-playpad>
36
+
37
+ <code-playpad language="javascript" height="auto">
38
+ const doubled = [1, 2, 3].map((n) => n * 2);
39
+ console.log(doubled);
40
+ doubled.reduce((a, b) => a + b, 0)
41
+ </code-playpad>
42
+
43
+ <code-playpad language="typescript" height="auto">
44
+ type Point = { x: number; y: number };
45
+ const dist = (p: Point): number => Math.hypot(p.x, p.y);
46
+ dist({ x: 3, y: 4 })
47
+ </code-playpad>
48
+ ```
49
+
50
+ A trailing expression prints its value, like a REPL. Each language loads **only
51
+ on pages that use it**: a Python-only page never downloads a byte of the
52
+ JavaScript runtime, and a JavaScript-only page never touches Pyodide's 11 MB.
30
53
 
31
54
  TypeScript here erases types, it does not check them — a type error will not
32
55
  stop your code running. This is a playground, not `tsc`.
33
56
 
34
57
  ## Install
35
58
 
36
- The CDN snippet above needs nothing installed. For a bundler:
59
+ ### From a CDN — nothing to install
60
+
61
+ ```html
62
+ <script type="module" src="https://cdn.jsdelivr.net/npm/code-playpad@0.2.1"></script>
63
+ ```
64
+
65
+ This is the `standalone` build: CodeMirror is bundled in, so a static blog post
66
+ or a Markdown-rendered page needs only that one tag. Pin the version — an
67
+ unpinned URL follows `latest` and will change under your readers.
68
+
69
+ ### From npm, with a bundler
37
70
 
38
71
  ```bash
39
72
  npm install code-playpad @codemirror/state @codemirror/view @codemirror/commands \
@@ -41,59 +74,72 @@ npm install code-playpad @codemirror/state @codemirror/view @codemirror/commands
41
74
  ```
42
75
 
43
76
  CodeMirror is a peer dependency so you never end up with two copies of
44
- `@codemirror/state`. Language grammars (`@codemirror/lang-python`,
45
- `lang-javascript`, `lang-json`) are **optional** — install only the ones you
46
- use; a missing one degrades to plain text.
77
+ `@codemirror/state`. Language grammars are **optional** — install only what you
78
+ use (`@codemirror/lang-python`, `@codemirror/lang-javascript`,
79
+ `@codemirror/lang-json`); a missing one degrades to plain text rather than
80
+ throwing.
47
81
 
48
82
  ```js
49
83
  import 'code-playpad/element'; // registers <code-playpad>
50
84
  ```
51
85
 
52
- Importing for the side effect is enough. If your bundler is configured to drop
53
- side-effect-only imports, register explicitly instead:
86
+ If your bundler is configured to drop side-effect-only imports, register
87
+ explicitly instead:
54
88
 
55
89
  ```js
56
90
  import { defineCodePlayPad } from 'code-playpad/element';
57
91
  defineCodePlayPad();
58
92
  ```
59
93
 
60
- From React:
61
-
62
- ```jsx
63
- import { CodePlayPad } from 'code-playpad/react';
94
+ ### Entry points
64
95
 
65
- <CodePlayPad
66
- language="python"
67
- code={'print("hi")'}
68
- height={180}
69
- onOutput={(r) => setPassed(r.stdout.includes('hi'))}
70
- />
71
- ```
96
+ | Import | You get |
97
+ | --- | --- |
98
+ | `code-playpad/element` | registers `<code-playpad>`; smallest thing that works |
99
+ | `code-playpad` | the same, plus every helper below |
100
+ | `code-playpad/react` | the `<CodePlayPad>` React component |
101
+ | `code-playpad/standalone` | CodeMirror bundled in — what the CDN serves |
72
102
 
73
- Importing on a server is safe — the element degrades to a no-op without a DOM,
74
- so Next.js, Astro and SvelteKit need no dynamic import.
103
+ Importing on a server is safe: without a DOM the element degrades to a no-op, so
104
+ Next.js, Astro and SvelteKit need no dynamic import or `client:only` guard.
75
105
 
76
- ## API
106
+ ## Attributes
77
107
 
78
108
  | | |
79
109
  | --- | --- |
80
- | **attributes** | `language`, `height`, `min-height`, `max-height`, `theme` (`light` \| `dark` \| `auto`), `readonly`, `autorun`, `stdin`, `persist`, `entry` |
81
- | **properties** | `code`, `files`, `entry`, `stdin`, `language` |
82
- | **methods** | `run()`, `stop()`, `reset()`, `getCode()`, `getFiles()`, `openFile(name)`, `prewarm()` |
83
- | **events** | `py-ready`, `py-run`, `py-output`, `py-error` |
110
+ | `language` | `python` (default), `javascript`, `typescript` |
111
+ | `height` | number (px), any CSS length, or `auto` |
112
+ | `min-height` / `max-height` | bounds for `auto` |
113
+ | `theme` | `light`, `dark`, or `auto` (default — follows the OS) |
114
+ | `readonly` | editor cannot be typed into; Run still works |
115
+ | `autorun` | runs once, on its own, when scrolled into view |
116
+ | `stdin` | shows the standard-input box up front |
117
+ | `persist` | key under which edits are saved (see [Saving work](#saving-work)) |
118
+ | `entry` | which file Run executes, for multi-file widgets |
119
+
120
+ ```html
121
+ <!-- a worked example nobody should edit -->
122
+ <code-playpad height="auto" readonly>
123
+ print("read this, don't change it")
124
+ </code-playpad>
84
125
 
85
- Events bubble and cross the shadow boundary, so answer-checking lives outside
86
- the widget:
126
+ <!-- output visible before the reader does anything -->
127
+ <code-playpad height="auto" autorun>
128
+ print("this already ran")
129
+ </code-playpad>
87
130
 
88
- ```js
89
- document.addEventListener('py-output', (e) => {
90
- // e.detail: { stdout, stderr?, value?, table?, durationMs, language }
91
- grade(e.target.id, e.detail.stdout);
92
- });
131
+ <!-- always dark, regardless of the reader's OS setting -->
132
+ <code-playpad height="auto" theme="dark">
133
+ print("always dark")
134
+ </code-playpad>
93
135
  ```
94
136
 
95
- Note `language`, not `lang` — `lang` is a standard HTML attribute for the
96
- *human* language of an element's text.
137
+ `readonly` blocks typing but not execution, and the code can still be replaced
138
+ from JavaScript — useful for "show the answer" buttons.
139
+
140
+ Note it is `language`, not `lang`: `lang` is a standard HTML attribute for the
141
+ *human* language of an element's text, and putting `python` there confuses
142
+ screen readers.
97
143
 
98
144
  ## Sizing
99
145
 
@@ -125,11 +171,11 @@ length (`40vh`, `30em`).
125
171
 
126
172
  ## Several files
127
173
 
128
- A widget can hold a small project. Relative imports, packages, and data files
129
- all work, and tracebacks name the real file and line.
174
+ A widget can hold a small project. Relative imports, packages and data files all
175
+ work, and tracebacks name the real file and line.
130
176
 
131
177
  ```html
132
- <code-playpad entry="main.py" height="260">
178
+ <code-playpad entry="main.py" height="auto" max-height="420">
133
179
  <playpad-file name="main.py">
134
180
  from geometry import Circle
135
181
  print(Circle(1).area())
@@ -149,9 +195,34 @@ all work, and tracebacks name the real file and line.
149
195
  </code-playpad>
150
196
  ```
151
197
 
198
+ JavaScript and TypeScript work the same way, including `.json` imported as data:
199
+
200
+ ```html
201
+ <code-playpad language="javascript" entry="main.js" height="auto">
202
+ <playpad-file name="main.js">
203
+ import { total } from "./cart.js";
204
+ import items from "./items.json";
205
+
206
+ console.log(total(items));
207
+ </playpad-file>
208
+
209
+ <playpad-file name="cart.js">
210
+ export const total = (items) =>
211
+ items.reduce((sum, item) => sum + item.price, 0);
212
+ </playpad-file>
213
+
214
+ <playpad-file name="items.json">
215
+ [{ "price": 12 }, { "price": 30 }]
216
+ </playpad-file>
217
+ </code-playpad>
218
+ ```
219
+
220
+ A tab bar appears once there is more than one file, with `▸` marking the entry
221
+ point, and each file keeps its own undo history. Editing a module takes effect
222
+ on the next run — the import cache is cleared between runs.
223
+
152
224
  Highlighting follows the **file**, not the runtime: a `.json` beside your
153
225
  JavaScript is highlighted as JSON, a `.csv` beside your Python is plain text.
154
- `.json` files are importable as data, the same as in Node.
155
226
 
156
227
  ## Input
157
228
 
@@ -165,10 +236,297 @@ age? 36
165
236
  hello Ada, next year you turn 37
166
237
  ```
167
238
 
168
- `Enter` sends a line; **EOF** (or `Ctrl-D`) ends input. In JavaScript and
169
- TypeScript, `input()` is a global returning a promise, so `await` works at the
170
- top level. Echoed input is display-only — `Result.stdout` stays exactly what the
171
- program printed, so graders are unaffected.
239
+ `Enter` sends a line; **EOF** (or `Ctrl-D`) ends input, which is how you finish
240
+ a `while True:` loop.
241
+
242
+ In JavaScript and TypeScript, `input()` is a global returning a promise, so
243
+ `await` works at the top level:
244
+
245
+ ```html
246
+ <code-playpad language="javascript" height="auto">
247
+ const name = await input("your name? ");
248
+ console.log(`hello ${name}`);
249
+ </code-playpad>
250
+ ```
251
+
252
+ To script the answers instead of typing them — useful for a worked example —
253
+ pre-fill the box. Those lines are used before the reader is asked:
254
+
255
+ ```html
256
+ <code-playpad height="auto" stdin>
257
+ name = input("name? ")
258
+ print("hi", name)
259
+ </code-playpad>
260
+ ```
261
+
262
+ ```js
263
+ document.querySelector('code-playpad').stdin = 'Ada\n36';
264
+ ```
265
+
266
+ Echoed input is display-only: `stdout` stays exactly what the program printed,
267
+ so graders are unaffected.
268
+
269
+ ## Controlling it from JavaScript
270
+
271
+ ```js
272
+ const pad = document.querySelector('code-playpad');
273
+
274
+ pad.code = 'print("set from outside")'; // replace the visible file
275
+ const source = pad.getCode(); // read it back
276
+ const result = await pad.run(); // { stdout, stderr?, value?, durationMs }
277
+ pad.reset(); // back to the snippet the page shipped
278
+ pad.stop(); // kill a runaway program
279
+ await pad.prewarm(); // boot the runtime before the reader runs anything
280
+ ```
281
+
282
+ Multi-file widgets add:
283
+
284
+ ```js
285
+ pad.files = { // replace the whole project
286
+ 'main.py': 'from util import shout\nprint(shout("hi"))',
287
+ 'util.py': 'def shout(s): return s.upper() + "!"'
288
+ };
289
+ pad.entry = 'main.py'; // which file Run executes
290
+ pad.openFile('util.py'); // bring a tab on screen
291
+ const everything = pad.getFiles(); // { name: contents }
292
+ ```
293
+
294
+ `prewarm()` is worth calling when you know the reader is about to run something
295
+ — on a lesson page, for instance — so the ~11 MB Python download starts before
296
+ they press Run rather than after.
297
+
298
+ ## Events and grading
299
+
300
+ Four events bubble and cross the shadow boundary, so answer-checking lives
301
+ outside the widget rather than inside it:
302
+
303
+ | event | `event.detail` |
304
+ | --- | --- |
305
+ | `py-ready` | `{ language, label }` — runtime booted |
306
+ | `py-run` | `{ code, entry, files, language }` — a run started |
307
+ | `py-output` | `{ stdout, stderr?, value?, table?, durationMs, language }` |
308
+ | `py-error` | `{ message, hint?, language }` |
309
+
310
+ A complete exercise check:
311
+
312
+ ```html
313
+ <code-playpad id="ex1" height="auto" min-height="150" persist="ex1">
314
+ # Print the numbers 1 to 5, one per line.
315
+ </code-playpad>
316
+ <p id="ex1-result"></p>
317
+ ```
318
+
319
+ ```js
320
+ const expected = '1\n2\n3\n4\n5\n';
321
+
322
+ document.addEventListener('py-output', (event) => {
323
+ if (event.target.id !== 'ex1') return;
324
+
325
+ const el = document.getElementById('ex1-result');
326
+ if (event.detail.stderr) {
327
+ el.textContent = 'Your code raised an error — read the message above.';
328
+ } else if (event.detail.stdout === expected) {
329
+ el.textContent = '✅ Correct';
330
+ } else {
331
+ el.textContent = `❌ Expected ${JSON.stringify(expected)}, got ${JSON.stringify(event.detail.stdout)}`;
332
+ }
333
+ });
334
+ ```
335
+
336
+ `py-error` also carries a `hint` when the runtime can explain the failure in
337
+ plain language — for example when a program calls `input()` more times than the
338
+ standard-input box has lines.
339
+
340
+ ## Saving work
341
+
342
+ `persist` stores the whole file map in IndexedDB, so a reader's edits survive
343
+ navigation *and* a full reload:
344
+
345
+ ```html
346
+ <code-playpad height="auto" persist="lesson-3-exercise-1">
347
+ # Your code here
348
+ </code-playpad>
349
+ ```
350
+
351
+ The page's file list stays authoritative: edits come back, but files you add or
352
+ remove later follow the page, not the old snapshot.
353
+
354
+ For a course you usually also want completion state, which the package exports:
355
+
356
+ ```js
357
+ import { setProgress, getProgress, allProgress, loadSnippet, clearSnippet } from 'code-playpad';
358
+
359
+ // mark a lesson done when its exercise passes
360
+ document.addEventListener('py-output', async (event) => {
361
+ if (event.detail.stdout === expected) {
362
+ await setProgress('lesson-3', { completed: true, score: 100 });
363
+ }
364
+ });
365
+
366
+ await getProgress('lesson-3'); // { id, completed, score, updatedAt }
367
+ await allProgress(); // every record, for a progress bar
368
+ await loadSnippet('lesson-3-exercise-1'); // what the reader last wrote
369
+ await clearSnippet('lesson-3-exercise-1'); // a "start over" button
370
+ ```
371
+
372
+ All of it is per-browser: nothing leaves the reader's machine.
373
+
374
+ ## Light and dark
375
+
376
+ `theme` takes `light`, `dark`, or `auto` (the default).
377
+
378
+ ```html
379
+ <code-playpad theme="dark" height="auto">print("always dark")</code-playpad>
380
+ <code-playpad theme="light" height="auto">print("always light")</code-playpad>
381
+ <code-playpad height="auto">print("follows the page, then the OS")</code-playpad>
382
+ ```
383
+
384
+ **`auto` follows your site first, the operating system second.** If your page
385
+ has its own light/dark switch, the widget follows it as long as the switch sets
386
+ `color-scheme` on the root element — which is how most themes are written:
387
+
388
+ ```css
389
+ :root { color-scheme: light; }
390
+ :root[data-theme="dark"] { color-scheme: dark; }
391
+ ```
392
+
393
+ Flip `data-theme` (or a class, or an inline style) and every widget on the page
394
+ re-themes itself. Nothing else to wire up. With no `color-scheme` on the page,
395
+ `auto` falls back to the reader's OS setting via `prefers-color-scheme`.
396
+
397
+ If your theme switch does *not* set `color-scheme`, drive the widgets directly:
398
+
399
+ ```js
400
+ function setTheme(mode) { // mode: 'light' | 'dark'
401
+ document.documentElement.dataset.theme = mode;
402
+ for (const pad of document.querySelectorAll('code-playpad')) {
403
+ pad.setAttribute('theme', mode);
404
+ }
405
+ }
406
+ ```
407
+
408
+ An explicit `theme` attribute always wins over the page, so a deliberately dark
409
+ snippet stays dark on a light page.
410
+
411
+ ## Styling
412
+
413
+ The widget lives in a shadow root, so page CSS cannot leak in — and its own
414
+ styles cannot leak out. Two supported ways to restyle it.
415
+
416
+ **Custom properties** inherit through the shadow boundary:
417
+
418
+ ```css
419
+ code-playpad {
420
+ --cp-accent: #b5179e; /* buttons, focus, the caret */
421
+ --cp-radius: 14px;
422
+ --cp-font-mono: "JetBrains Mono", monospace;
423
+ }
424
+ ```
425
+
426
+ | | |
427
+ | --- | --- |
428
+ | colours | `--cp-bg`, `--cp-fg`, `--cp-muted`, `--cp-border`, `--cp-surface` |
429
+ | accents | `--cp-accent`, `--cp-accent-fg`, `--cp-danger`, `--cp-ok` |
430
+ | shape and type | `--cp-radius`, `--cp-font-mono`, `--cp-font-ui` |
431
+
432
+ **Parts** expose the pieces worth targeting:
433
+
434
+ ```css
435
+ code-playpad::part(run-button) { border-radius: 999px; }
436
+ code-playpad::part(stop-button) { font-weight: 700; }
437
+ code-playpad::part(output) { background: #fff7fb; }
438
+ code-playpad::part(tabs) { border-bottom-width: 2px; }
439
+ ```
440
+
441
+ Both work from an ordinary stylesheet, no `::shadow` hacks. For dark mode, the
442
+ `theme` attribute already follows the reader's OS by default.
443
+
444
+ ## Framework recipes
445
+
446
+ **Astro** — put the import in a page or layout script; the element does the rest.
447
+
448
+ ```astro
449
+ ---
450
+ // src/layouts/Lesson.astro
451
+ ---
452
+ <script>
453
+ import 'code-playpad/element';
454
+ </script>
455
+
456
+ <code-playpad height="auto" persist={Astro.props.id}>
457
+ {Astro.props.code}
458
+ </code-playpad>
459
+ ```
460
+
461
+ **React** — the wrapper assigns props through a ref, so React 18 behaves like 19.
462
+
463
+ ```jsx
464
+ import { useState } from 'react';
465
+ import { CodePlayPad } from 'code-playpad/react';
466
+
467
+ export function Exercise() {
468
+ const [passed, setPassed] = useState(false);
469
+
470
+ return (
471
+ <>
472
+ <CodePlayPad
473
+ language="python"
474
+ code={'# Your code here'}
475
+ height="auto"
476
+ minHeight={150}
477
+ persist="exercise-1"
478
+ onOutput={(r) => setPassed(r.stdout.trim() === '42')}
479
+ />
480
+ {passed && <p>✅ Correct</p>}
481
+ </>
482
+ );
483
+ }
484
+ ```
485
+
486
+ Props: `code`, `files`, `entry`, `language`, `height`, `stdin`, `theme`,
487
+ `readOnly`, `autoRun`, `persist`, `className`, `style`, and the callbacks
488
+ `onReady`, `onRun`, `onOutput`, `onError`.
489
+
490
+ **Next.js** — the element is SSR-safe, so a plain import works in a client
491
+ component:
492
+
493
+ ```jsx
494
+ 'use client';
495
+ import 'code-playpad/element';
496
+
497
+ export default function Lesson() {
498
+ return <code-playpad height="auto">{'print("hi")'}</code-playpad>;
499
+ }
500
+ ```
501
+
502
+ **Vue** — tell the compiler the tag is a custom element, then use it directly:
503
+
504
+ ```js
505
+ // vite.config.js
506
+ export default {
507
+ plugins: [vue({ template: { compilerOptions: { isCustomElement: (tag) => tag === 'code-playpad' } } })]
508
+ };
509
+ ```
510
+
511
+ **Plain HTML / Markdown** — the CDN tag plus the element. Because the code lives
512
+ in the element's text content, the snippet is still readable if JavaScript never
513
+ loads.
514
+
515
+ ## Production
516
+
517
+ Serve Pyodide from your own origin so you control its cache headers, rather than
518
+ depending on a public CDN:
519
+
520
+ ```js
521
+ import { configurePython, pyodideAssetUrls } from 'code-playpad';
522
+
523
+ configurePython({ indexURL: '/pyodide/' }); // before the first run
524
+ pyodideAssetUrls(); // the files worth <link rel=preload>-ing
525
+ ```
526
+
527
+ `configureTypeScript({ sucraseURL })` does the same for the TypeScript
528
+ transform. Give those files a long `Cache-Control`: one page's download then
529
+ warms every other page on the site.
172
530
 
173
531
  ## Worth knowing
174
532
 
@@ -180,12 +538,55 @@ program printed, so graders are unaffected.
180
538
  - **A program waiting on `input()` holds the shared runtime.** Other widgets
181
539
  show `Queued…` until it finishes or is stopped.
182
540
  - **Nothing boots on page load.** An IntersectionObserver starts a runtime as a
183
- widget nears the viewport; touching the editor also wakes it.
541
+ widget nears the viewport; touching the editor also wakes it. Use `prewarm()`
542
+ to start earlier on purpose.
184
543
  - **Python in the browser has limits**: no threads, no `multiprocessing`, no
185
544
  sockets, no `subprocess`. `requests` works but goes through browser `fetch`,
186
545
  so CORS applies.
187
- - **Serve Pyodide from your own origin in production** so you control its cache
188
- headers: `configurePython({ indexURL: '/pyodide/' })` before the first run.
546
+ - **If CodeMirror fails to load**, the widget falls back to a plain textarea that
547
+ still runs code, rather than showing nothing.
548
+
549
+ ## Version history
550
+
551
+ No breaking changes so far: markup and API written against 0.1.3 still work on
552
+ 0.2.1.
553
+
554
+ ### 0.2.1
555
+
556
+ - `theme="auto"` now follows the **page** before the operating system: if your
557
+ site's light/dark switch sets `color-scheme` on the root, widgets re-theme
558
+ with it. Previously a page could go dark while every widget stayed light.
559
+ - `minHeight` and `maxHeight` props on the React wrapper — 0.2.0 added them as
560
+ attributes but missed the wrapper.
561
+ - README expanded into full usage documentation: entry points, an example per
562
+ attribute, programmatic control, a complete grading example, the progress
563
+ store, styling, and framework recipes.
564
+
565
+ ### 0.2.0
566
+
567
+ - `height="auto"` is a supported content-fitting mode. It previously worked by
568
+ accident and could have broken silently.
569
+ - New `min-height` and `max-height` attributes. `max-height` scrolls rather than
570
+ clipping; `min-height` grows the editor itself, so the empty space below a
571
+ short snippet is still clickable — what an exercise answer box needs.
572
+
573
+ ### 0.1.4
574
+
575
+ - **Fixed:** a `<pre>` or other wrapper on its own line leaked its indentation
576
+ into the program, so two-line snippets failed with `IndentationError` while
577
+ single-line ones looked fine.
578
+ - **Fixed:** `sideEffects` named the re-export shims rather than the chunk that
579
+ registers the element, so a bundler could legitimately drop it and
580
+ `<code-playpad>` would never register in a production build.
581
+ - No runtime dependencies (an analytics package used only by the demo had crept
582
+ into `dependencies`).
583
+
584
+ ### 0.1.3
585
+
586
+ - Relicensed **MIT**. Earlier versions were published as `UNLICENSED`, which
587
+ granted nobody the right to use them.
588
+
589
+ Versions 0.1.0 to 0.1.2 were withdrawn and are not installable.
189
590
 
190
591
  ## Licence
191
592
 
@@ -1,4 +1,4 @@
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=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=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=new Map,i=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(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-B8qzHgzU.js`).then(e=>e.pythonRuntime)),a(`javascript`,()=>import(`./javascript-Cv8I8Gkw.js`).then(e=>e.javascriptRuntime)),a(`typescript`,()=>import(`./javascript-Cv8I8Gkw.js`).then(e=>e.typescriptRuntime));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=String.raw`
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=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=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=new Map,i=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(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-5ByQYqgo.js`).then(e=>e.pythonRuntime)),a(`javascript`,()=>import(`./javascript-RVtPk08Z.js`).then(e=>e.javascriptRuntime)),a(`typescript`,()=>import(`./javascript-RVtPk08Z.js`).then(e=>e.typescriptRuntime));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=String.raw`
2
2
  :host {
3
3
  --cp-bg: #ffffff;
4
4
  --cp-fg: #1f2328;
@@ -296,10 +296,10 @@ table.cp-table th { background: var(--cp-bg); }
296
296
  }
297
297
  `,d=e=>()=>import(`@codemirror/lang-javascript`).then(t=>t.javascript(e)),f={py:()=>import(`@codemirror/lang-python`).then(e=>e.python()),pyi:()=>import(`@codemirror/lang-python`).then(e=>e.python()),js:d({}),mjs:d({}),cjs:d({}),jsx:d({jsx:!0}),ts:d({typescript:!0}),mts:d({typescript:!0}),cts:d({typescript:!0}),tsx:d({typescript:!0,jsx:!0}),json:()=>import(`@codemirror/lang-json`).then(e=>e.json())};async function p(e){let t=f[e.includes(`.`)?e.slice(e.lastIndexOf(`.`)+1).toLowerCase():``];return t?t().catch(()=>null):null}var m=`code-playpad`,h=1,g=`snippets`,_=`progress`,v=null;function y(){return typeof indexedDB>`u`?Promise.reject(Error(`IndexedDB unavailable`)):(v||=new Promise((e,t)=>{let n=indexedDB.open(m,h);n.onupgradeneeded=()=>{let e=n.result;e.objectStoreNames.contains(g)||e.createObjectStore(g),e.objectStoreNames.contains(_)||e.createObjectStore(_,{keyPath:`id`})},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)}).catch(e=>{throw v=null,e}),v)}function b(e,t,n){return y().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 x(e,t){return b(g,`readwrite`,n=>n.put(t,e)).then(()=>void 0)}function S(e){return b(g,`readonly`,t=>t.get(e)).then(e=>e??null)}function C(e){return b(g,`readwrite`,t=>t.delete(e)).then(()=>void 0)}function w(e,t={}){let n={completed:!0,...t,id:e,updatedAt:Date.now()};return b(_,`readwrite`,e=>e.put(n)).then(()=>n)}function T(e){return b(_,`readonly`,t=>t.get(e)).then(e=>e??null)}function E(){return b(_,`readonly`,e=>e.getAll())}var D=`code-playpad`;function O(e){let t=e.replace(/\t/g,` `).split(`
298
298
  `);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(`
299
- `)}var k=2e5,A=`playpad-file`,j={python:`main.py`,javascript:`main.js`,typescript:`main.ts`};function M(e){return j[e]??`main.txt`}function N(e,t){let n={};for(let t of e.children){if(t.tagName.toLowerCase()!==A)continue;let e=t.getAttribute(`name`);e&&(n[e]=O(t.textContent??``))}if(Object.keys(n).length)return n;let r=[...e.childNodes].filter(e=>e.nodeType!==Node.ELEMENT_NODE||e.tagName.toLowerCase()!==A),i=r.some(e=>e.nodeType===Node.ELEMENT_NODE),a=O(r.filter(e=>!(i&&e.nodeType===Node.TEXT_NODE&&!(e.textContent??``).trim())).map(e=>e.textContent??``).join(``));return{[t]:a}}function P(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 F=typeof HTMLElement>`u`?class{}:HTMLElement,I=class extends F{static get observedAttributes(){return[`language`,`height`,`min-height`,`max-height`,`theme`,`readonly`,`autorun`,`stdin`,`persist`,`entry`]}#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=!1;#x=null;#S=null;#C=()=>this.#U();#w=null;#T=!0;#E=[];#D=null;#O=null;#k=null;#A=0;#j=!1;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.#L()}get files(){return this.#z()}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},t.includes(this.#g)||(this.#g=this.#R()),this.#u?.forgetFiles([]),this.#L(),this.#B(),this.#I()}get entry(){return this.#R()}set entry(e){this.setAttribute(`entry`,e)}openFile(e){e in this.#m&&(this.#u&&(this.#m[this.#g]=this.#u.getCode(),this.#u.openFile(e,this.#m[e]??``)),this.#g=e,this.#B(),this.#I())}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.#z()}reset(){this.#m={...this.#h},this.#g=this.#R(),this.#u?.forgetFiles([]),this.#L(),this.#B(),this.#K(),this.#s.hidden=!0,this.#G(this.#f?.ready?`${this.#f.label} ready`:``,`idle`)}async run(){if(this.#y)return;await this.#d;let e=this.#z(),t=this.#R(),n=e[t]??``;this.#y=!0,this.#n.disabled=!0,this.#r.hidden=!1,this.#K(),this.#s.hidden=!1,this.#$(`py-run`,{code:n,entry:t,files:e,language:this.language});try{let r=await this.#F();this.#G(`Queued…`,`busy`),this.#o.hidden=!1;let i=await r.run(n,{stdin:this.#l.value,files:e,entry:t,onStart:()=>this.#G(`Running…`,`busy`),onStdout:e=>this.#q(e),onStderr:e=>this.#q(e,`cp-err`),onEcho:e=>this.#q(e,`cp-in`),onStdinRequest:()=>this.#Y()});return i.table&&this.#Q(i.table),i.value!==void 0&&this.#q(`${i.value}\n`,`cp-value`),i.hint&&this.#Z(i.hint),i.stderr?(this.#G(`Error · ${i.durationMs} ms`,`error`),this.#$(`py-error`,{message:i.stderr,hint:i.hint,language:this.language})):this.#G(`Done in ${i.durationMs} ms`,`ok`),this.#$(`py-output`,{...i,language:this.language}),i}catch(e){if(e instanceof c)this.#q(`
299
+ `)}var k=2e5,A=`playpad-file`,j={python:`main.py`,javascript:`main.js`,typescript:`main.ts`};function M(e){return j[e]??`main.txt`}function N(e,t){let n={};for(let t of e.children){if(t.tagName.toLowerCase()!==A)continue;let e=t.getAttribute(`name`);e&&(n[e]=O(t.textContent??``))}if(Object.keys(n).length)return n;let r=[...e.childNodes].filter(e=>e.nodeType!==Node.ELEMENT_NODE||e.tagName.toLowerCase()!==A),i=r.some(e=>e.nodeType===Node.ELEMENT_NODE),a=O(r.filter(e=>!(i&&e.nodeType===Node.TEXT_NODE&&!(e.textContent??``).trim())).map(e=>e.textContent??``).join(``));return{[t]:a}}function P(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 F=typeof HTMLElement>`u`?class{}:HTMLElement,I=class extends F{static get observedAttributes(){return[`language`,`height`,`min-height`,`max-height`,`theme`,`readonly`,`autorun`,`stdin`,`persist`,`entry`]}#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=!1;#x=null;#S=null;#C=null;#w=()=>this.#W();#T=null;#E=!0;#D=[];#O=null;#k=null;#A=null;#j=0;#M=!1;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.#R()}get files(){return this.#B()}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},t.includes(this.#g)||(this.#g=this.#z()),this.#u?.forgetFiles([]),this.#R(),this.#V(),this.#L()}get entry(){return this.#z()}set entry(e){this.setAttribute(`entry`,e)}openFile(e){e in this.#m&&(this.#u&&(this.#m[this.#g]=this.#u.getCode(),this.#u.openFile(e,this.#m[e]??``)),this.#g=e,this.#V(),this.#L())}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.#B()}reset(){this.#m={...this.#h},this.#g=this.#z(),this.#u?.forgetFiles([]),this.#R(),this.#V(),this.#q(),this.#s.hidden=!0,this.#K(this.#f?.ready?`${this.#f.label} ready`:``,`idle`)}async run(){if(this.#y)return;await this.#d;let e=this.#B(),t=this.#z(),n=e[t]??``;this.#y=!0,this.#n.disabled=!0,this.#r.hidden=!1,this.#q(),this.#s.hidden=!1,this.#ee(`py-run`,{code:n,entry:t,files:e,language:this.language});try{let r=await this.#I();this.#K(`Queued…`,`busy`),this.#o.hidden=!1;let i=await r.run(n,{stdin:this.#l.value,files:e,entry:t,onStart:()=>this.#K(`Running…`,`busy`),onStdout:e=>this.#J(e),onStderr:e=>this.#J(e,`cp-err`),onEcho:e=>this.#J(e,`cp-in`),onStdinRequest:()=>this.#X()});return i.table&&this.#$(i.table),i.value!==void 0&&this.#J(`${i.value}\n`,`cp-value`),i.hint&&this.#Q(i.hint),i.stderr?(this.#K(`Error · ${i.durationMs} ms`,`error`),this.#ee(`py-error`,{message:i.stderr,hint:i.hint,language:this.language})):this.#K(`Done in ${i.durationMs} ms`,`ok`),this.#ee(`py-output`,{...i,language:this.language}),i}catch(e){if(e instanceof c)this.#J(`
300
300
  [stopped]
301
- `,`cp-err`),this.#G(`Stopped`,`error`);else{let t=e instanceof Error?e.message:String(e);this.#q(t+`
302
- `,`cp-err`),this.#G(`Failed to start`,`error`),this.#$(`py-error`,{message:t,language:this.language})}return}finally{this.#X(null),this.#J(),this.#y=!1,this.#n.disabled=!1,this.#r.hidden=!0,this.#o.hidden=!0}}stop(){this.#f?.stop?.()}prewarm(){return this.#F()}connectedCallback(){if(this.#j){this.#P();return}if(!this.textContent?.trim()&&document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>this.isConnected&&this.#M(),{once:!0});return}this.#M()}disconnectedCallback(){this.#x?.disconnect(),this.#x=null,this.#S?.removeEventListener(`change`,this.#C),this.#S=null,this.#w&&clearTimeout(this.#w),this.#X(null),setTimeout(()=>{this.isConnected||!this.#u||(this.#_=this.#z(),this.#u.destroy(),this.#u=null,this.#d=null,this.#j=!1)},0)}attributeChangedCallback(e,t,n){if(this.#j)switch(e){case`theme`:this.#U();break;case`height`:case`min-height`:case`max-height`:this.#W();break;case`readonly`:this.#u?.setReadOnly(n!==null);break;case`stdin`:this.#c.hidden=n===null&&!this.#l.value;break;case`entry`:this.#B();break;case`language`:this.#f=null,this.#p=null,this.#b=!1,this.#F().catch(()=>{})}}#M(){this.#j=!0,this.#h=N(this,M(this.language)),this.#m=this.#_??{...this.#h},this.#g=this.#R(),this.#N(),this.#U(),this.#W();let t=this.getAttribute(`persist`);this.#d=(async()=>{if(t&&this.#_==null){let e=await S(t).catch(()=>null),n=e==null?null:P(e,M(this.language));if(n){let e={};for(let t of Object.keys(this.#h))e[t]=n[t]??this.#h[t];this.#m=e,this.#g=this.#R()}}this.#B();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.#H(),readOnly:this.hasAttribute(`readonly`),language:r,onRun:()=>void this.run(),onChange:e=>this.#V(e)}),this.#_=null,this.#I()})(),this.#P()}#N(){let e=document.createElement(`style`);e.textContent=u;let t=document.createElement(`div`);t.className=`cp-root`,t.innerHTML=`
301
+ `,`cp-err`),this.#K(`Stopped`,`error`);else{let t=e instanceof Error?e.message:String(e);this.#J(t+`
302
+ `,`cp-err`),this.#K(`Failed to start`,`error`),this.#ee(`py-error`,{message:t,language:this.language})}return}finally{this.#Z(null),this.#Y(),this.#y=!1,this.#n.disabled=!1,this.#r.hidden=!0,this.#o.hidden=!0}}stop(){this.#f?.stop?.()}prewarm(){return this.#I()}connectedCallback(){if(this.#M){this.#F();return}if(!this.textContent?.trim()&&document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>this.isConnected&&this.#N(),{once:!0});return}this.#N()}disconnectedCallback(){this.#x?.disconnect(),this.#x=null,this.#S?.removeEventListener(`change`,this.#w),this.#S=null,this.#C?.disconnect(),this.#C=null,this.#T&&clearTimeout(this.#T),this.#Z(null),setTimeout(()=>{this.isConnected||!this.#u||(this.#_=this.#B(),this.#u.destroy(),this.#u=null,this.#d=null,this.#M=!1)},0)}attributeChangedCallback(e,t,n){if(this.#M)switch(e){case`theme`:this.#W();break;case`height`:case`min-height`:case`max-height`:this.#G();break;case`readonly`:this.#u?.setReadOnly(n!==null);break;case`stdin`:this.#c.hidden=n===null&&!this.#l.value;break;case`entry`:this.#V();break;case`language`:this.#f=null,this.#p=null,this.#b=!1,this.#I().catch(()=>{})}}#N(){this.#M=!0,this.#h=N(this,M(this.language)),this.#m=this.#_??{...this.#h},this.#g=this.#z(),this.#P(),this.#W(),this.#G();let t=this.getAttribute(`persist`);this.#d=(async()=>{if(t&&this.#_==null){let e=await S(t).catch(()=>null),n=e==null?null:P(e,M(this.language));if(n){let e={};for(let t of Object.keys(this.#h))e[t]=n[t]??this.#h[t];this.#m=e,this.#g=this.#z()}}this.#V();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.#U(),readOnly:this.hasAttribute(`readonly`),language:r,onRun:()=>void this.run(),onChange:e=>this.#H(e)}),this.#_=null,this.#L()})(),this.#F()}#P(){let e=document.createElement(`style`);e.textContent=u;let t=document.createElement(`div`);t.className=`cp-root`,t.innerHTML=`
303
303
  <div class="cp-toolbar">
304
304
  <button class="cp-primary" part="run-button" data-action="run">Run</button>
305
305
  <button class="cp-danger" part="stop-button" data-action="stop" hidden>Stop</button>
@@ -314,10 +314,10 @@ table.cp-table th { background: var(--cp-bg); }
314
314
  <label>Standard input — one value per line</label>
315
315
  <textarea rows="2" spellcheck="false"></textarea>
316
316
  </div>
317
- <pre class="cp-output" part="output" hidden></pre>`,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.#c.hidden=!this.hasAttribute(`stdin`),this.#l.addEventListener(`input`,()=>this.#c.classList.remove(`cp-attention`)),this.#v.addEventListener(`click`,e=>{let t=e.target.closest(`button[data-file]`);t&&this.openFile(t.getAttribute(`data-file`))}),this.#n.addEventListener(`click`,()=>void this.run()),this.#r.addEventListener(`click`,()=>this.stop()),this.#i.addEventListener(`click`,()=>this.reset());let n=()=>void this.#F().catch(()=>{});this.#t.addEventListener(`pointerdown`,n,{once:!0}),this.#t.addEventListener(`focusin`,n,{once:!0})}#P(){this.#x||typeof IntersectionObserver>`u`||(this.#x=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(this.#x?.disconnect(),this.#x=null,this.#F().then(()=>{this.hasAttribute(`autorun`)&&this.run()}).catch(()=>{}))},{rootMargin:`300px`}),this.#x.observe(this))}#F(){return this.#p||=(async()=>{let e=await s(this.language);this.#f=e,e.ready||(this.#o.hidden=!1,this.#G(`Loading ${e.label}…`,`busy`));try{await e.load(e=>{this.#y||this.#G(e.message,e.phase===`ready`?`ok`:`busy`)})}catch(e){this.#p=null,this.#G(`Runtime unavailable`,`error`);let t=e instanceof Error?e.message:String(e);throw this.#q(t+`
318
- `,`cp-err`),this.#s.hidden=!1,e}finally{this.#o.hidden=!0}return this.#I(),this.#b||(this.#b=!0,this.#G(`${e.label} ready`,`ok`),this.#$(`py-ready`,{language:e.id,label:e.label})),e})(),this.#p}async#I(){if(!this.#u)return;let e=this.#g,t=await p(e);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??[])}#L(){if(this.#u){this.#u.setCode(this.#m[this.#g]??``);return}this.#d?.then(()=>{this.#u?.setCode(this.#m[this.#g]??``),this.#B()})}#R(){let e=Object.keys(this.#m),t=this.getAttribute(`entry`);if(t&&e.includes(t))return t;let n=M(this.language);return e.includes(n)?n:e[0]??n}#z(){let e={...this.#m};if(this.#u)for(let t of Object.keys(e))e[t]=this.#u.fileCode(t,e[t]);return e}#B(){let e=Object.keys(this.#m);if(this.#v.hidden=e.length<2,this.#v.hidden){this.#v.replaceChildren();return}let t=this.#R();this.#v.replaceChildren(...e.map(e=>{let n=document.createElement(`button`);return n.dataset.file=e,n.textContent=e,n.setAttribute(`aria-pressed`,String(e===this.#g)),e===t&&(n.classList.add(`cp-entry`),n.title=`entry point — this is the file Run executes`),n}))}#V(e){this.#m[this.#g]=e;let t=this.getAttribute(`persist`);if(!t)return;this.#w&&clearTimeout(this.#w);let n=JSON.stringify(this.#z());this.#w=setTimeout(()=>void x(t,n).catch(()=>{}),400)}#H(){let e=this.getAttribute(`theme`)??`auto`;return e===`dark`||e!==`light`&&!!this.#S?.matches}#U(){!this.#S&&typeof matchMedia==`function`&&(this.#S=matchMedia(`(prefers-color-scheme: dark)`),this.#S.addEventListener(`change`,this.#C));let e=this.#H();this.setAttribute(`data-resolved-theme`,e?`dark`:`light`),this.#u?.setTheme(e)}#W(){for(let[e,t]of[[`height`,`--cp-height`],[`min-height`,`--cp-min-height`],[`max-height`,`--cp-max-height`]]){let n=this.getAttribute(e);n===null||n===``?this.style.removeProperty(t):this.style.setProperty(t,/^\d+$/.test(n)?`${n}px`:n)}}#G(e,t){this.#a.textContent=e,this.#a.dataset.state=t}#K(){this.#k&&clearTimeout(this.#k),this.#k=null,this.#E=[],this.#A=0,this.#s.replaceChildren(),this.#T=!0}#q(e,t){if(!e)return;let n=t===`cp-err`&&!this.#T?`\n${e}`:e;this.#T=n.endsWith(`
319
- `),this.#E.push({text:n,className:t}),this.#k??=setTimeout(()=>this.#J(),16)}#J(){if(this.#k&&clearTimeout(this.#k),this.#k=null,!this.#E.length)return;let e=this.#E;this.#E=[];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.#A>=k)break;let e=t.text;this.#A+e.length>k&&(e=e.slice(0,k-this.#A)+`
317
+ <pre class="cp-output" part="output" hidden></pre>`,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.#c.hidden=!this.hasAttribute(`stdin`),this.#l.addEventListener(`input`,()=>this.#c.classList.remove(`cp-attention`)),this.#v.addEventListener(`click`,e=>{let t=e.target.closest(`button[data-file]`);t&&this.openFile(t.getAttribute(`data-file`))}),this.#n.addEventListener(`click`,()=>void this.run()),this.#r.addEventListener(`click`,()=>this.stop()),this.#i.addEventListener(`click`,()=>this.reset());let n=()=>void this.#I().catch(()=>{});this.#t.addEventListener(`pointerdown`,n,{once:!0}),this.#t.addEventListener(`focusin`,n,{once:!0})}#F(){this.#x||typeof IntersectionObserver>`u`||(this.#x=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(this.#x?.disconnect(),this.#x=null,this.#I().then(()=>{this.hasAttribute(`autorun`)&&this.run()}).catch(()=>{}))},{rootMargin:`300px`}),this.#x.observe(this))}#I(){return this.#p||=(async()=>{let e=await s(this.language);this.#f=e,e.ready||(this.#o.hidden=!1,this.#K(`Loading ${e.label}…`,`busy`));try{await e.load(e=>{this.#y||this.#K(e.message,e.phase===`ready`?`ok`:`busy`)})}catch(e){this.#p=null,this.#K(`Runtime unavailable`,`error`);let t=e instanceof Error?e.message:String(e);throw this.#J(t+`
318
+ `,`cp-err`),this.#s.hidden=!1,e}finally{this.#o.hidden=!0}return this.#L(),this.#b||(this.#b=!0,this.#K(`${e.label} ready`,`ok`),this.#ee(`py-ready`,{language:e.id,label:e.label})),e})(),this.#p}async#L(){if(!this.#u)return;let e=this.#g,t=await p(e);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??[])}#R(){if(this.#u){this.#u.setCode(this.#m[this.#g]??``);return}this.#d?.then(()=>{this.#u?.setCode(this.#m[this.#g]??``),this.#V()})}#z(){let e=Object.keys(this.#m),t=this.getAttribute(`entry`);if(t&&e.includes(t))return t;let n=M(this.language);return e.includes(n)?n:e[0]??n}#B(){let e={...this.#m};if(this.#u)for(let t of Object.keys(e))e[t]=this.#u.fileCode(t,e[t]);return e}#V(){let e=Object.keys(this.#m);if(this.#v.hidden=e.length<2,this.#v.hidden){this.#v.replaceChildren();return}let t=this.#z();this.#v.replaceChildren(...e.map(e=>{let n=document.createElement(`button`);return n.dataset.file=e,n.textContent=e,n.setAttribute(`aria-pressed`,String(e===this.#g)),e===t&&(n.classList.add(`cp-entry`),n.title=`entry point — this is the file Run executes`),n}))}#H(e){this.#m[this.#g]=e;let t=this.getAttribute(`persist`);if(!t)return;this.#T&&clearTimeout(this.#T);let n=JSON.stringify(this.#B());this.#T=setTimeout(()=>void x(t,n).catch(()=>{}),400)}#U(){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.#S?.matches:n}#W(){!this.#S&&typeof matchMedia==`function`&&(this.#S=matchMedia(`(prefers-color-scheme: dark)`),this.#S.addEventListener(`change`,this.#w)),!this.#C&&typeof MutationObserver==`function`&&typeof document<`u`&&(this.#C=new MutationObserver(this.#w),this.#C.observe(document.documentElement,{attributes:!0,attributeFilter:[`class`,`style`,`data-theme`,`data-color-scheme`,`color-scheme`]}));let e=this.#U();this.setAttribute(`data-resolved-theme`,e?`dark`:`light`),this.#u?.setTheme(e)}#G(){for(let[e,t]of[[`height`,`--cp-height`],[`min-height`,`--cp-min-height`],[`max-height`,`--cp-max-height`]]){let n=this.getAttribute(e);n===null||n===``?this.style.removeProperty(t):this.style.setProperty(t,/^\d+$/.test(n)?`${n}px`:n)}}#K(e,t){this.#a.textContent=e,this.#a.dataset.state=t}#q(){this.#A&&clearTimeout(this.#A),this.#A=null,this.#D=[],this.#j=0,this.#s.replaceChildren(),this.#E=!0}#J(e,t){if(!e)return;let n=t===`cp-err`&&!this.#E?`\n${e}`:e;this.#E=n.endsWith(`
319
+ `),this.#D.push({text:n,className:t}),this.#A??=setTimeout(()=>this.#Y(),16)}#Y(){if(this.#A&&clearTimeout(this.#A),this.#A=null,!this.#D.length)return;let e=this.#D;this.#D=[];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.#j>=k)break;let e=t.text;this.#j+e.length>k&&(e=e.slice(0,k-this.#j)+`
320
320
  … output truncated for display — the full text is in the py-output event
321
- `),this.#A+=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}#Y(){return this.#J(),this.#X(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.#D===e&&(this.#D=null,this.#O=null,t.remove(),n===null?this.#q(`
322
- `,`cp-in`):this.#q(n+`
323
- `,`cp-in`),this.#J(),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.#D=e,this.#O=a,n.focus({preventScroll:!0})})}#X(e){this.#O?.(e),this.#O=null,this.#D=null}#Z(e){this.#J();let t=document.createElement(`div`);t.className=`cp-hint`,t.textContent=e,this.#s.hidden=!1,this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight,this.#c.hidden=!1,this.#c.classList.add(`cp-attention`)}#Q(e){this.#J();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)}#$(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}};function L(e=D){typeof window>`u`||typeof customElements>`u`||customElements.get(e)||customElements.define(e,I)}L();export{T as a,w as c,s as d,o as f,C as i,c as l,e as m,L as n,S as o,a as p,E as r,x as s,I as t,l as u};
321
+ `),this.#j+=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}#X(){return this.#Y(),this.#Z(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.#O===e&&(this.#O=null,this.#k=null,t.remove(),n===null?this.#J(`
322
+ `,`cp-in`):this.#J(n+`
323
+ `,`cp-in`),this.#Y(),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.#O=e,this.#k=a,n.focus({preventScroll:!0})})}#Z(e){this.#k?.(e),this.#k=null,this.#O=null}#Q(e){this.#Y();let t=document.createElement(`div`);t.className=`cp-hint`,t.textContent=e,this.#s.hidden=!1,this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight,this.#c.hidden=!1,this.#c.classList.add(`cp-attention`)}#$(e){this.#Y();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)}#ee(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}};function L(e=D){typeof window>`u`||typeof customElements>`u`||customElements.get(e)||customElements.define(e,I)}L();export{T as a,w as c,s as d,o as f,C as i,c as l,e as m,L as n,S as o,a as p,E as r,x as s,I as t,l as u};
package/dist/element.js CHANGED
@@ -1 +1 @@
1
- import{n as e,t}from"./element-DtCQVStG.js";export{t as CodePlayPadElement,e as defineCodePlayPad};
1
+ import{n as e,t}from"./element-VP5lhHx6.js";export{t as CodePlayPadElement,e as defineCodePlayPad};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as e,c as t,d as n,f as r,i,l 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-DtCQVStG.js";import{n as m,o as h,r as g,t as _}from"./config-DZqcFPEP.js";export{f as CodePlayPadElement,_ as PYODIDE_VERSION,a as RunStopped,p as WorkerUnavailable,u as allProgress,i as clearSnippet,m as configurePython,g as configureTypeScript,o as createEditor,s as defineCodePlayPad,e as getProgress,n as getRuntime,r as listRuntimes,c as loadSnippet,h as pyodideAssetUrls,l as registerRuntime,d as saveSnippet,t as setProgress};
1
+ import{a as e,c as t,d as n,f as r,i,l 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-VP5lhHx6.js";import{n as m,o as h,r as g,t as _}from"./config-DZqcFPEP.js";export{f as CodePlayPadElement,_ as PYODIDE_VERSION,a as RunStopped,p as WorkerUnavailable,u as allProgress,i as clearSnippet,m as configurePython,g as configureTypeScript,o as createEditor,s as defineCodePlayPad,e as getProgress,n as getRuntime,r as listRuntimes,c as loadSnippet,h as pyodideAssetUrls,l as registerRuntime,d as saveSnippet,t as setProgress};
@@ -1,4 +1,4 @@
1
- import{a as e}from"./config-DZqcFPEP.js";import{t}from"./worker-runtime-HpUlPmaH.js";var n=String.raw`
1
+ import{a as e}from"./config-DZqcFPEP.js";import{t}from"./worker-runtime-CJ3Zww5E.js";var n=String.raw`
2
2
  let currentId = null;
3
3
  let stdinLines = [];
4
4
  let stdinExhausted = false;
@@ -1,4 +1,4 @@
1
- import{i as e}from"./config-DZqcFPEP.js";import{t}from"./worker-runtime-HpUlPmaH.js";var n=`let pyodide=null,stdinLines=[],stdinExhausted=!1,currentId=null,interactive=!1,inputWaitMs=0,nextRequest=1;const pendingLines=new Map,MOUNT="/home/pyodide/playground";let written=new Set,internals=null;const REFRESH=["import importlib, os, sys","MOUNT = "+JSON.stringify(MOUNT),"os.makedirs(MOUNT, exist_ok=True)","sys.path.insert(0, MOUNT) if MOUNT not in sys.path else None","os.chdir(MOUNT)","for _name, _mod in list(sys.modules.items()):",' _file = getattr(_mod, "__file__", None) or ""',' _paths = list(getattr(_mod, "__path__", None) or [])'," if _file.startswith(MOUNT) or any(str(p).startswith(MOUNT) for p in _paths):"," del sys.modules[_name]","importlib.invalidate_caches()"].join(\`
1
+ import{i as e}from"./config-DZqcFPEP.js";import{t}from"./worker-runtime-CJ3Zww5E.js";var n=`let pyodide=null,stdinLines=[],stdinExhausted=!1,currentId=null,interactive=!1,inputWaitMs=0,nextRequest=1;const pendingLines=new Map,MOUNT="/home/pyodide/playground";let written=new Set,internals=null;const REFRESH=["import importlib, os, sys","MOUNT = "+JSON.stringify(MOUNT),"os.makedirs(MOUNT, exist_ok=True)","sys.path.insert(0, MOUNT) if MOUNT not in sys.path else None","os.chdir(MOUNT)","for _name, _mod in list(sys.modules.items()):",' _file = getattr(_mod, "__file__", None) or ""',' _paths = list(getattr(_mod, "__path__", None) or [])'," if _file.startswith(MOUNT) or any(str(p).startswith(MOUNT) for p in _paths):"," del sys.modules[_name]","importlib.invalidate_caches()"].join(\`
2
2
  \`);function flushStreams(){try{pyodide.runPython(\`import sys
3
3
  sys.stdout.flush()
4
4
  sys.stderr.flush()\`,{globals:internals})}catch{}flushOutput()}const INPUT_OVERRIDE=["import builtins, sys, js","from pyodide.ffi import run_sync","",'def _pg_input(prompt=""):',' if prompt != "":'," sys.stdout.write(str(prompt))"," sys.stdout.flush()"," line = run_sync(js._pgReadLine())"," # Pyodide maps JS null to its own JsNull singleton, not None, so test"," # for what we want rather than for the several ways EOF can arrive."," if not isinstance(line, str):",' raise EOFError("EOF when reading a line")'," return line","",'_pg_input.__name__ = "input"','_pg_input.__qualname__ = "input"',"_pg_input.__doc__ = builtins.input.__doc__","builtins.input = _pg_input"].join(\`
package/dist/react.d.ts CHANGED
@@ -13,6 +13,9 @@ export interface CodePlayPadProps {
13
13
  /** "python" (default), "javascript" or "typescript". */
14
14
  language?: string;
15
15
  height?: number | string;
16
+ /** Bounds for `height="auto"`. A number means pixels. */
17
+ minHeight?: number | string;
18
+ maxHeight?: number | string;
16
19
  theme?: 'light' | 'dark' | 'auto';
17
20
  readOnly?: boolean;
18
21
  autoRun?: boolean;
@@ -33,7 +36,7 @@ export interface CodePlayPadProps {
33
36
  * which keeps React 18 (no non-string prop support on custom elements) working
34
37
  * the same as React 19.
35
38
  */
36
- export declare function CodePlayPad({ code, files, entry, stdin, language, height, theme, readOnly, autoRun, persist, className, style, onReady, onRun, onOutput, onError }: CodePlayPadProps): import("react").JSX.Element;
39
+ export declare function CodePlayPad({ code, files, entry, stdin, language, height, minHeight, maxHeight, theme, readOnly, autoRun, persist, className, style, onReady, onRun, onOutput, onError }: CodePlayPadProps): import("react").JSX.Element;
37
40
  declare module 'react' {
38
41
  namespace JSX {
39
42
  interface IntrinsicElements {
package/dist/react.js CHANGED
@@ -1 +1 @@
1
- import{n as e}from"./element-DtCQVStG.js";import{useEffect as t,useRef as n}from"react";import{jsx as r}from"react/jsx-runtime";e();function i({code:e=``,files:i,entry:a,stdin:o,language:s=`python`,height:c,theme:l=`auto`,readOnly:u,autoRun:d,persist:f,className:p,style:m,onReady:h,onRun:g,onOutput:_,onError:v}){let y=n(null),b=n({onReady:h,onRun:g,onOutput:_,onError:v});b.current={onReady:h,onRun:g,onOutput:_,onError:v},t(()=>{let e=y.current;if(!e)return;let t=(t,n)=>{let r=e=>n(b.current)?.(e.detail);return e.addEventListener(t,r),()=>e.removeEventListener(t,r)},n=[t(`py-ready`,e=>e.onReady),t(`py-run`,e=>e.onRun),t(`py-output`,e=>e.onOutput),t(`py-error`,e=>e.onError)];return()=>n.forEach(e=>e())},[]);let x=i?JSON.stringify(i):null;return t(()=>{let t=y.current;t&&(x?t.files=JSON.parse(x):e!==t.code&&(t.code=e))},[e,x]),t(()=>{y.current&&o!==void 0&&(y.current.stdin=o)},[o]),r(`code-playpad`,{ref:y,language:s,theme:l,class:p,style:m,...c===void 0?{}:{height:String(c)},...u?{readonly:``}:{},...d?{autorun:``}:{},...f?{persist:f}:{},...a?{entry:a}:{},children:i?null:e})}export{i as CodePlayPad,i as default};
1
+ import{n as e}from"./element-VP5lhHx6.js";import{useEffect as t,useRef as n}from"react";import{jsx as r}from"react/jsx-runtime";e();function i({code:e=``,files:i,entry:a,stdin:o,language:s=`python`,height:c,minHeight:l,maxHeight:u,theme:d=`auto`,readOnly:f,autoRun:p,persist:m,className:h,style:g,onReady:_,onRun:v,onOutput:y,onError:b}){let x=n(null),S=n({onReady:_,onRun:v,onOutput:y,onError:b});S.current={onReady:_,onRun:v,onOutput:y,onError:b},t(()=>{let e=x.current;if(!e)return;let t=(t,n)=>{let r=e=>n(S.current)?.(e.detail);return e.addEventListener(t,r),()=>e.removeEventListener(t,r)},n=[t(`py-ready`,e=>e.onReady),t(`py-run`,e=>e.onRun),t(`py-output`,e=>e.onOutput),t(`py-error`,e=>e.onError)];return()=>n.forEach(e=>e())},[]);let C=i?JSON.stringify(i):null;return t(()=>{let t=x.current;t&&(C?t.files=JSON.parse(C):e!==t.code&&(t.code=e))},[e,C]),t(()=>{x.current&&o!==void 0&&(x.current.stdin=o)},[o]),r(`code-playpad`,{ref:x,language:s,theme:d,class:h,style:g,...c===void 0?{}:{height:String(c)},...l===void 0?{}:{"min-height":String(l)},...u===void 0?{}:{"max-height":String(u)},...f?{readonly:``}:{},...p?{autorun:``}:{},...m?{persist:m}:{},...a?{entry:a}:{},children:i?null:e})}export{i as CodePlayPad,i as default};
@@ -301,10 +301,10 @@ table.cp-table th { background: var(--cp-bg); }
301
301
  }
302
302
  `,h=e=>()=>import(`./dist-CqpVTRAq.js`).then(t=>t.javascript(e)),g={py:()=>import(`./dist-D_qJovVA.js`).then(e=>e.python()),pyi:()=>import(`./dist-D_qJovVA.js`).then(e=>e.python()),js:h({}),mjs:h({}),cjs:h({}),jsx:h({jsx:!0}),ts:h({typescript:!0}),mts:h({typescript:!0}),cts:h({typescript:!0}),tsx:h({typescript:!0,jsx:!0}),json:()=>import(`./dist-XlpaG2JF.js`).then(e=>e.json())};async function _(e){let t=g[e.includes(`.`)?e.slice(e.lastIndexOf(`.`)+1).toLowerCase():``];return t?t().catch(()=>null):null}var v=`code-playpad`,y=1,b=`snippets`,x=`progress`,S=null;function C(){return typeof indexedDB>`u`?Promise.reject(Error(`IndexedDB unavailable`)):(S||=new Promise((e,t)=>{let n=indexedDB.open(v,y);n.onupgradeneeded=()=>{let e=n.result;e.objectStoreNames.contains(b)||e.createObjectStore(b),e.objectStoreNames.contains(x)||e.createObjectStore(x,{keyPath:`id`})},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)}).catch(e=>{throw S=null,e}),S)}function w(e,t,n){return C().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 T(e,t){return w(b,`readwrite`,n=>n.put(t,e)).then(()=>void 0)}function E(e){return w(b,`readonly`,t=>t.get(e)).then(e=>e??null)}function D(e){return w(b,`readwrite`,t=>t.delete(e)).then(()=>void 0)}function O(e,t={}){let n={completed:!0,...t,id:e,updatedAt:Date.now()};return w(x,`readwrite`,e=>e.put(n)).then(()=>n)}function k(e){return w(x,`readonly`,t=>t.get(e)).then(e=>e??null)}function A(){return w(x,`readonly`,e=>e.getAll())}var j=`code-playpad`;function M(e){let t=e.replace(/\t/g,` `).split(`
303
303
  `);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(`
304
- `)}var N=2e5,P=`playpad-file`,F={python:`main.py`,javascript:`main.js`,typescript:`main.ts`};function I(e){return F[e]??`main.txt`}function L(e,t){let n={};for(let t of e.children){if(t.tagName.toLowerCase()!==P)continue;let e=t.getAttribute(`name`);e&&(n[e]=M(t.textContent??``))}if(Object.keys(n).length)return n;let r=[...e.childNodes].filter(e=>e.nodeType!==Node.ELEMENT_NODE||e.tagName.toLowerCase()!==P),i=r.some(e=>e.nodeType===Node.ELEMENT_NODE),a=M(r.filter(e=>!(i&&e.nodeType===Node.TEXT_NODE&&!(e.textContent??``).trim())).map(e=>e.textContent??``).join(``));return{[t]:a}}function R(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 z=typeof HTMLElement>`u`?class{}:HTMLElement,B=class extends z{static get observedAttributes(){return[`language`,`height`,`min-height`,`max-height`,`theme`,`readonly`,`autorun`,`stdin`,`persist`,`entry`]}#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=!1;#x=null;#S=null;#C=()=>this.#U();#w=null;#T=!0;#E=[];#D=null;#O=null;#k=null;#A=0;#j=!1;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.#L()}get files(){return this.#z()}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},t.includes(this.#g)||(this.#g=this.#R()),this.#u?.forgetFiles([]),this.#L(),this.#B(),this.#I()}get entry(){return this.#R()}set entry(e){this.setAttribute(`entry`,e)}openFile(e){e in this.#m&&(this.#u&&(this.#m[this.#g]=this.#u.getCode(),this.#u.openFile(e,this.#m[e]??``)),this.#g=e,this.#B(),this.#I())}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.#z()}reset(){this.#m={...this.#h},this.#g=this.#R(),this.#u?.forgetFiles([]),this.#L(),this.#B(),this.#K(),this.#s.hidden=!0,this.#G(this.#f?.ready?`${this.#f.label} ready`:``,`idle`)}async run(){if(this.#y)return;await this.#d;let e=this.#z(),t=this.#R(),n=e[t]??``;this.#y=!0,this.#n.disabled=!0,this.#r.hidden=!1,this.#K(),this.#s.hidden=!1,this.#$(`py-run`,{code:n,entry:t,files:e,language:this.language});try{let r=await this.#F();this.#G(`Queued…`,`busy`),this.#o.hidden=!1;let i=await r.run(n,{stdin:this.#l.value,files:e,entry:t,onStart:()=>this.#G(`Running…`,`busy`),onStdout:e=>this.#q(e),onStderr:e=>this.#q(e,`cp-err`),onEcho:e=>this.#q(e,`cp-in`),onStdinRequest:()=>this.#Y()});return i.table&&this.#Q(i.table),i.value!==void 0&&this.#q(`${i.value}\n`,`cp-value`),i.hint&&this.#Z(i.hint),i.stderr?(this.#G(`Error · ${i.durationMs} ms`,`error`),this.#$(`py-error`,{message:i.stderr,hint:i.hint,language:this.language})):this.#G(`Done in ${i.durationMs} ms`,`ok`),this.#$(`py-output`,{...i,language:this.language}),i}catch(e){if(e instanceof i)this.#q(`
304
+ `)}var N=2e5,P=`playpad-file`,F={python:`main.py`,javascript:`main.js`,typescript:`main.ts`};function I(e){return F[e]??`main.txt`}function L(e,t){let n={};for(let t of e.children){if(t.tagName.toLowerCase()!==P)continue;let e=t.getAttribute(`name`);e&&(n[e]=M(t.textContent??``))}if(Object.keys(n).length)return n;let r=[...e.childNodes].filter(e=>e.nodeType!==Node.ELEMENT_NODE||e.tagName.toLowerCase()!==P),i=r.some(e=>e.nodeType===Node.ELEMENT_NODE),a=M(r.filter(e=>!(i&&e.nodeType===Node.TEXT_NODE&&!(e.textContent??``).trim())).map(e=>e.textContent??``).join(``));return{[t]:a}}function R(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 z=typeof HTMLElement>`u`?class{}:HTMLElement,B=class extends z{static get observedAttributes(){return[`language`,`height`,`min-height`,`max-height`,`theme`,`readonly`,`autorun`,`stdin`,`persist`,`entry`]}#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=!1;#x=null;#S=null;#C=null;#w=()=>this.#W();#T=null;#E=!0;#D=[];#O=null;#k=null;#A=null;#j=0;#M=!1;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.#R()}get files(){return this.#B()}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},t.includes(this.#g)||(this.#g=this.#z()),this.#u?.forgetFiles([]),this.#R(),this.#V(),this.#L()}get entry(){return this.#z()}set entry(e){this.setAttribute(`entry`,e)}openFile(e){e in this.#m&&(this.#u&&(this.#m[this.#g]=this.#u.getCode(),this.#u.openFile(e,this.#m[e]??``)),this.#g=e,this.#V(),this.#L())}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.#B()}reset(){this.#m={...this.#h},this.#g=this.#z(),this.#u?.forgetFiles([]),this.#R(),this.#V(),this.#q(),this.#s.hidden=!0,this.#K(this.#f?.ready?`${this.#f.label} ready`:``,`idle`)}async run(){if(this.#y)return;await this.#d;let e=this.#B(),t=this.#z(),n=e[t]??``;this.#y=!0,this.#n.disabled=!0,this.#r.hidden=!1,this.#q(),this.#s.hidden=!1,this.#ee(`py-run`,{code:n,entry:t,files:e,language:this.language});try{let r=await this.#I();this.#K(`Queued…`,`busy`),this.#o.hidden=!1;let i=await r.run(n,{stdin:this.#l.value,files:e,entry:t,onStart:()=>this.#K(`Running…`,`busy`),onStdout:e=>this.#J(e),onStderr:e=>this.#J(e,`cp-err`),onEcho:e=>this.#J(e,`cp-in`),onStdinRequest:()=>this.#X()});return i.table&&this.#$(i.table),i.value!==void 0&&this.#J(`${i.value}\n`,`cp-value`),i.hint&&this.#Q(i.hint),i.stderr?(this.#K(`Error · ${i.durationMs} ms`,`error`),this.#ee(`py-error`,{message:i.stderr,hint:i.hint,language:this.language})):this.#K(`Done in ${i.durationMs} ms`,`ok`),this.#ee(`py-output`,{...i,language:this.language}),i}catch(e){if(e instanceof i)this.#J(`
305
305
  [stopped]
306
- `,`cp-err`),this.#G(`Stopped`,`error`);else{let t=e instanceof Error?e.message:String(e);this.#q(t+`
307
- `,`cp-err`),this.#G(`Failed to start`,`error`),this.#$(`py-error`,{message:t,language:this.language})}return}finally{this.#X(null),this.#J(),this.#y=!1,this.#n.disabled=!1,this.#r.hidden=!0,this.#o.hidden=!0}}stop(){this.#f?.stop?.()}prewarm(){return this.#F()}connectedCallback(){if(this.#j){this.#P();return}if(!this.textContent?.trim()&&document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>this.isConnected&&this.#M(),{once:!0});return}this.#M()}disconnectedCallback(){this.#x?.disconnect(),this.#x=null,this.#S?.removeEventListener(`change`,this.#C),this.#S=null,this.#w&&clearTimeout(this.#w),this.#X(null),setTimeout(()=>{this.isConnected||!this.#u||(this.#_=this.#z(),this.#u.destroy(),this.#u=null,this.#d=null,this.#j=!1)},0)}attributeChangedCallback(e,t,n){if(this.#j)switch(e){case`theme`:this.#U();break;case`height`:case`min-height`:case`max-height`:this.#W();break;case`readonly`:this.#u?.setReadOnly(n!==null);break;case`stdin`:this.#c.hidden=n===null&&!this.#l.value;break;case`entry`:this.#B();break;case`language`:this.#f=null,this.#p=null,this.#b=!1,this.#F().catch(()=>{})}}#M(){this.#j=!0,this.#h=L(this,I(this.language)),this.#m=this.#_??{...this.#h},this.#g=this.#R(),this.#N(),this.#U(),this.#W();let e=this.getAttribute(`persist`);this.#d=(async()=>{if(e&&this.#_==null){let t=await E(e).catch(()=>null),n=t==null?null:R(t,I(this.language));if(n){let e={};for(let t of Object.keys(this.#h))e[t]=n[t]??this.#h[t];this.#m=e,this.#g=this.#R()}}this.#B();let t=await p(this.language).catch(()=>null),n=t?await t.editorLanguage().catch(()=>null):null;this.#u=await o({parent:this.#t,doc:this.#m[this.#g]??``,name:this.#g,dark:this.#H(),readOnly:this.hasAttribute(`readonly`),language:n,onRun:()=>void this.run(),onChange:e=>this.#V(e)}),this.#_=null,this.#I()})(),this.#P()}#N(){let e=document.createElement(`style`);e.textContent=m;let t=document.createElement(`div`);t.className=`cp-root`,t.innerHTML=`
306
+ `,`cp-err`),this.#K(`Stopped`,`error`);else{let t=e instanceof Error?e.message:String(e);this.#J(t+`
307
+ `,`cp-err`),this.#K(`Failed to start`,`error`),this.#ee(`py-error`,{message:t,language:this.language})}return}finally{this.#Z(null),this.#Y(),this.#y=!1,this.#n.disabled=!1,this.#r.hidden=!0,this.#o.hidden=!0}}stop(){this.#f?.stop?.()}prewarm(){return this.#I()}connectedCallback(){if(this.#M){this.#F();return}if(!this.textContent?.trim()&&document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>this.isConnected&&this.#N(),{once:!0});return}this.#N()}disconnectedCallback(){this.#x?.disconnect(),this.#x=null,this.#S?.removeEventListener(`change`,this.#w),this.#S=null,this.#C?.disconnect(),this.#C=null,this.#T&&clearTimeout(this.#T),this.#Z(null),setTimeout(()=>{this.isConnected||!this.#u||(this.#_=this.#B(),this.#u.destroy(),this.#u=null,this.#d=null,this.#M=!1)},0)}attributeChangedCallback(e,t,n){if(this.#M)switch(e){case`theme`:this.#W();break;case`height`:case`min-height`:case`max-height`:this.#G();break;case`readonly`:this.#u?.setReadOnly(n!==null);break;case`stdin`:this.#c.hidden=n===null&&!this.#l.value;break;case`entry`:this.#V();break;case`language`:this.#f=null,this.#p=null,this.#b=!1,this.#I().catch(()=>{})}}#N(){this.#M=!0,this.#h=L(this,I(this.language)),this.#m=this.#_??{...this.#h},this.#g=this.#z(),this.#P(),this.#W(),this.#G();let e=this.getAttribute(`persist`);this.#d=(async()=>{if(e&&this.#_==null){let t=await E(e).catch(()=>null),n=t==null?null:R(t,I(this.language));if(n){let e={};for(let t of Object.keys(this.#h))e[t]=n[t]??this.#h[t];this.#m=e,this.#g=this.#z()}}this.#V();let t=await p(this.language).catch(()=>null),n=t?await t.editorLanguage().catch(()=>null):null;this.#u=await o({parent:this.#t,doc:this.#m[this.#g]??``,name:this.#g,dark:this.#U(),readOnly:this.hasAttribute(`readonly`),language:n,onRun:()=>void this.run(),onChange:e=>this.#H(e)}),this.#_=null,this.#L()})(),this.#F()}#P(){let e=document.createElement(`style`);e.textContent=m;let t=document.createElement(`div`);t.className=`cp-root`,t.innerHTML=`
308
308
  <div class="cp-toolbar">
309
309
  <button class="cp-primary" part="run-button" data-action="run">Run</button>
310
310
  <button class="cp-danger" part="stop-button" data-action="stop" hidden>Stop</button>
@@ -319,10 +319,10 @@ table.cp-table th { background: var(--cp-bg); }
319
319
  <label>Standard input — one value per line</label>
320
320
  <textarea rows="2" spellcheck="false"></textarea>
321
321
  </div>
322
- <pre class="cp-output" part="output" hidden></pre>`,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.#c.hidden=!this.hasAttribute(`stdin`),this.#l.addEventListener(`input`,()=>this.#c.classList.remove(`cp-attention`)),this.#v.addEventListener(`click`,e=>{let t=e.target.closest(`button[data-file]`);t&&this.openFile(t.getAttribute(`data-file`))}),this.#n.addEventListener(`click`,()=>void this.run()),this.#r.addEventListener(`click`,()=>this.stop()),this.#i.addEventListener(`click`,()=>this.reset());let n=()=>void this.#F().catch(()=>{});this.#t.addEventListener(`pointerdown`,n,{once:!0}),this.#t.addEventListener(`focusin`,n,{once:!0})}#P(){this.#x||typeof IntersectionObserver>`u`||(this.#x=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(this.#x?.disconnect(),this.#x=null,this.#F().then(()=>{this.hasAttribute(`autorun`)&&this.run()}).catch(()=>{}))},{rootMargin:`300px`}),this.#x.observe(this))}#F(){return this.#p||=(async()=>{let e=await p(this.language);this.#f=e,e.ready||(this.#o.hidden=!1,this.#G(`Loading ${e.label}…`,`busy`));try{await e.load(e=>{this.#y||this.#G(e.message,e.phase===`ready`?`ok`:`busy`)})}catch(e){this.#p=null,this.#G(`Runtime unavailable`,`error`);let t=e instanceof Error?e.message:String(e);throw this.#q(t+`
323
- `,`cp-err`),this.#s.hidden=!1,e}finally{this.#o.hidden=!0}return this.#I(),this.#b||(this.#b=!0,this.#G(`${e.label} ready`,`ok`),this.#$(`py-ready`,{language:e.id,label:e.label})),e})(),this.#p}async#I(){if(!this.#u)return;let e=this.#g,t=await _(e);if(t){this.#g===e&&this.#u?.setLanguage(t);return}let n=e.includes(`.`)?null:await(this.#f??await p(this.language).catch(()=>null))?.editorLanguage().catch(()=>null);this.#g===e&&this.#u?.setLanguage(n??[])}#L(){if(this.#u){this.#u.setCode(this.#m[this.#g]??``);return}this.#d?.then(()=>{this.#u?.setCode(this.#m[this.#g]??``),this.#B()})}#R(){let e=Object.keys(this.#m),t=this.getAttribute(`entry`);if(t&&e.includes(t))return t;let n=I(this.language);return e.includes(n)?n:e[0]??n}#z(){let e={...this.#m};if(this.#u)for(let t of Object.keys(e))e[t]=this.#u.fileCode(t,e[t]);return e}#B(){let e=Object.keys(this.#m);if(this.#v.hidden=e.length<2,this.#v.hidden){this.#v.replaceChildren();return}let t=this.#R();this.#v.replaceChildren(...e.map(e=>{let n=document.createElement(`button`);return n.dataset.file=e,n.textContent=e,n.setAttribute(`aria-pressed`,String(e===this.#g)),e===t&&(n.classList.add(`cp-entry`),n.title=`entry point — this is the file Run executes`),n}))}#V(e){this.#m[this.#g]=e;let t=this.getAttribute(`persist`);if(!t)return;this.#w&&clearTimeout(this.#w);let n=JSON.stringify(this.#z());this.#w=setTimeout(()=>void T(t,n).catch(()=>{}),400)}#H(){let e=this.getAttribute(`theme`)??`auto`;return e===`dark`||e!==`light`&&!!this.#S?.matches}#U(){!this.#S&&typeof matchMedia==`function`&&(this.#S=matchMedia(`(prefers-color-scheme: dark)`),this.#S.addEventListener(`change`,this.#C));let e=this.#H();this.setAttribute(`data-resolved-theme`,e?`dark`:`light`),this.#u?.setTheme(e)}#W(){for(let[e,t]of[[`height`,`--cp-height`],[`min-height`,`--cp-min-height`],[`max-height`,`--cp-max-height`]]){let n=this.getAttribute(e);n===null||n===``?this.style.removeProperty(t):this.style.setProperty(t,/^\d+$/.test(n)?`${n}px`:n)}}#G(e,t){this.#a.textContent=e,this.#a.dataset.state=t}#K(){this.#k&&clearTimeout(this.#k),this.#k=null,this.#E=[],this.#A=0,this.#s.replaceChildren(),this.#T=!0}#q(e,t){if(!e)return;let n=t===`cp-err`&&!this.#T?`\n${e}`:e;this.#T=n.endsWith(`
324
- `),this.#E.push({text:n,className:t}),this.#k??=setTimeout(()=>this.#J(),16)}#J(){if(this.#k&&clearTimeout(this.#k),this.#k=null,!this.#E.length)return;let e=this.#E;this.#E=[];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.#A>=N)break;let e=t.text;this.#A+e.length>N&&(e=e.slice(0,N-this.#A)+`
322
+ <pre class="cp-output" part="output" hidden></pre>`,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.#c.hidden=!this.hasAttribute(`stdin`),this.#l.addEventListener(`input`,()=>this.#c.classList.remove(`cp-attention`)),this.#v.addEventListener(`click`,e=>{let t=e.target.closest(`button[data-file]`);t&&this.openFile(t.getAttribute(`data-file`))}),this.#n.addEventListener(`click`,()=>void this.run()),this.#r.addEventListener(`click`,()=>this.stop()),this.#i.addEventListener(`click`,()=>this.reset());let n=()=>void this.#I().catch(()=>{});this.#t.addEventListener(`pointerdown`,n,{once:!0}),this.#t.addEventListener(`focusin`,n,{once:!0})}#F(){this.#x||typeof IntersectionObserver>`u`||(this.#x=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(this.#x?.disconnect(),this.#x=null,this.#I().then(()=>{this.hasAttribute(`autorun`)&&this.run()}).catch(()=>{}))},{rootMargin:`300px`}),this.#x.observe(this))}#I(){return this.#p||=(async()=>{let e=await p(this.language);this.#f=e,e.ready||(this.#o.hidden=!1,this.#K(`Loading ${e.label}…`,`busy`));try{await e.load(e=>{this.#y||this.#K(e.message,e.phase===`ready`?`ok`:`busy`)})}catch(e){this.#p=null,this.#K(`Runtime unavailable`,`error`);let t=e instanceof Error?e.message:String(e);throw this.#J(t+`
323
+ `,`cp-err`),this.#s.hidden=!1,e}finally{this.#o.hidden=!0}return this.#L(),this.#b||(this.#b=!0,this.#K(`${e.label} ready`,`ok`),this.#ee(`py-ready`,{language:e.id,label:e.label})),e})(),this.#p}async#L(){if(!this.#u)return;let e=this.#g,t=await _(e);if(t){this.#g===e&&this.#u?.setLanguage(t);return}let n=e.includes(`.`)?null:await(this.#f??await p(this.language).catch(()=>null))?.editorLanguage().catch(()=>null);this.#g===e&&this.#u?.setLanguage(n??[])}#R(){if(this.#u){this.#u.setCode(this.#m[this.#g]??``);return}this.#d?.then(()=>{this.#u?.setCode(this.#m[this.#g]??``),this.#V()})}#z(){let e=Object.keys(this.#m),t=this.getAttribute(`entry`);if(t&&e.includes(t))return t;let n=I(this.language);return e.includes(n)?n:e[0]??n}#B(){let e={...this.#m};if(this.#u)for(let t of Object.keys(e))e[t]=this.#u.fileCode(t,e[t]);return e}#V(){let e=Object.keys(this.#m);if(this.#v.hidden=e.length<2,this.#v.hidden){this.#v.replaceChildren();return}let t=this.#z();this.#v.replaceChildren(...e.map(e=>{let n=document.createElement(`button`);return n.dataset.file=e,n.textContent=e,n.setAttribute(`aria-pressed`,String(e===this.#g)),e===t&&(n.classList.add(`cp-entry`),n.title=`entry point — this is the file Run executes`),n}))}#H(e){this.#m[this.#g]=e;let t=this.getAttribute(`persist`);if(!t)return;this.#T&&clearTimeout(this.#T);let n=JSON.stringify(this.#B());this.#T=setTimeout(()=>void T(t,n).catch(()=>{}),400)}#U(){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.#S?.matches:n}#W(){!this.#S&&typeof matchMedia==`function`&&(this.#S=matchMedia(`(prefers-color-scheme: dark)`),this.#S.addEventListener(`change`,this.#w)),!this.#C&&typeof MutationObserver==`function`&&typeof document<`u`&&(this.#C=new MutationObserver(this.#w),this.#C.observe(document.documentElement,{attributes:!0,attributeFilter:[`class`,`style`,`data-theme`,`data-color-scheme`,`color-scheme`]}));let e=this.#U();this.setAttribute(`data-resolved-theme`,e?`dark`:`light`),this.#u?.setTheme(e)}#G(){for(let[e,t]of[[`height`,`--cp-height`],[`min-height`,`--cp-min-height`],[`max-height`,`--cp-max-height`]]){let n=this.getAttribute(e);n===null||n===``?this.style.removeProperty(t):this.style.setProperty(t,/^\d+$/.test(n)?`${n}px`:n)}}#K(e,t){this.#a.textContent=e,this.#a.dataset.state=t}#q(){this.#A&&clearTimeout(this.#A),this.#A=null,this.#D=[],this.#j=0,this.#s.replaceChildren(),this.#E=!0}#J(e,t){if(!e)return;let n=t===`cp-err`&&!this.#E?`\n${e}`:e;this.#E=n.endsWith(`
324
+ `),this.#D.push({text:n,className:t}),this.#A??=setTimeout(()=>this.#Y(),16)}#Y(){if(this.#A&&clearTimeout(this.#A),this.#A=null,!this.#D.length)return;let e=this.#D;this.#D=[];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.#j>=N)break;let e=t.text;this.#j+e.length>N&&(e=e.slice(0,N-this.#j)+`
325
325
  … output truncated for display — the full text is in the py-output event
326
- `),this.#A+=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}#Y(){return this.#J(),this.#X(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.#D===e&&(this.#D=null,this.#O=null,t.remove(),n===null?this.#q(`
327
- `,`cp-in`):this.#q(n+`
328
- `,`cp-in`),this.#J(),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.#D=e,this.#O=a,n.focus({preventScroll:!0})})}#X(e){this.#O?.(e),this.#O=null,this.#D=null}#Z(e){this.#J();let t=document.createElement(`div`);t.className=`cp-hint`,t.textContent=e,this.#s.hidden=!1,this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight,this.#c.hidden=!1,this.#c.classList.add(`cp-attention`)}#Q(e){this.#J();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)}#$(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}};function V(e=j){typeof window>`u`||typeof customElements>`u`||customElements.get(e)||customElements.define(e,B)}V();export{B as CodePlayPadElement,a as PYODIDE_VERSION,i as RunStopped,e as WorkerUnavailable,A as allProgress,D as clearSnippet,t as configurePython,r as configureTypeScript,o as createEditor,V as defineCodePlayPad,k as getProgress,p as getRuntime,f as listRuntimes,E as loadSnippet,n as pyodideAssetUrls,d as registerRuntime,T as saveSnippet,O as setProgress};
326
+ `),this.#j+=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}#X(){return this.#Y(),this.#Z(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.#O===e&&(this.#O=null,this.#k=null,t.remove(),n===null?this.#J(`
327
+ `,`cp-in`):this.#J(n+`
328
+ `,`cp-in`),this.#Y(),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.#O=e,this.#k=a,n.focus({preventScroll:!0})})}#Z(e){this.#k?.(e),this.#k=null,this.#O=null}#Q(e){this.#Y();let t=document.createElement(`div`);t.className=`cp-hint`,t.textContent=e,this.#s.hidden=!1,this.#s.appendChild(t),this.#s.scrollTop=this.#s.scrollHeight,this.#c.hidden=!1,this.#c.classList.add(`cp-attention`)}#$(e){this.#Y();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)}#ee(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}};function V(e=j){typeof window>`u`||typeof customElements>`u`||customElements.get(e)||customElements.define(e,B)}V();export{B as CodePlayPadElement,a as PYODIDE_VERSION,i as RunStopped,e as WorkerUnavailable,A as allProgress,D as clearSnippet,t as configurePython,r as configureTypeScript,o as createEditor,V as defineCodePlayPad,k as getProgress,p as getRuntime,f as listRuntimes,E as loadSnippet,n as pyodideAssetUrls,d as registerRuntime,T as saveSnippet,O as setProgress};
@@ -1,4 +1,4 @@
1
- import{l as e,u as t}from"./element-DtCQVStG.js";var n=1,r=class{#e=null;#t=null;#n=`module`;#r=null;#i=!1;#a=!1;#o=``;#s=new Set;#c=[];#l=null;#u=!1;initPayload(){return{}}get ready(){return this.#i}get interactiveStdin(){return this.#a}get version(){return this.#o}load(e){e&&this.#s.add(e);let n=()=>{e&&this.#s.delete(e)};if(!this.#r){let e=new Promise((e,n)=>{let r;try{if(typeof Worker>`u`)throw Error(`Worker constructor missing`);let e=new Blob([this.source],{type:`text/javascript`});this.#t=URL.createObjectURL(e),r=new Worker(this.#t,{type:this.#n})}catch(e){n(new t(`Could not start a Web Worker: ${e.message}`));return}this.#e=r,r.onmessage=t=>{let r=t.data;if(r.type===`ready`){this.#o=r.version,this.#a=r.interactiveStdin===!0,this.#i=!0,this.#p({phase:`ready`,message:`${this.label} ready`}),e();return}if(r.type===`fatal`&&!this.#i){this.#d(r.message,e,n);return}this.#m(r)},r.onerror=t=>{let r=t.message||`Worker failed to start`;this.#i?this.#l&&this.#g(this.#l,Error(r)):this.#d(r,e,n)},r.postMessage({type:`init`,...this.initPayload()})});this.#r=e,e.catch(()=>{this.#r===e&&(this.#r=null)})}return this.#r.then(n,e=>{throw n(),e})}run(e,t={}){let r,i=new Promise(e=>{r=e});return new Promise((a,o)=>{this.#c.push({id:n++,code:e,options:t,stdout:``,stderr:``,resolve:a,reject:o,done:i,markDone:r}),this.#_()})}stop(){let t=this.#e!==null;this.#f(),this.#l&&this.#g(this.#l,new e),t&&this.load().catch(()=>{})}#d(e,t,n){if(this.#f(),this.#n===`module`){this.#n=`classic`,this.load().then(t,n);return}n(Error(e))}#f(){this.#e?.terminate(),this.#e=null,this.#t&&=(URL.revokeObjectURL(this.#t),null),this.#r=null,this.#i=!1}#p(e){for(let t of this.#s)t(e)}#m(e){if(e.type===`progress`){this.#p({phase:e.phase,message:e.message});return}let t=this.#l;if(!(!t||!(`id`in e)||e.id!==t.id))switch(e.type){case`stdout`:t.stdout+=e.text,t.options.onStdout?.(e.text);break;case`echo`:t.options.onEcho?.(e.text);break;case`stdin-request`:this.#h(t,e.request);break;case`stderr`:t.stderr+=e.text,t.options.onStderr?.(e.text);break;case`result`:t.resolve({stdout:t.stdout,stderr:t.stderr||void 0,value:e.value,durationMs:Math.round(e.durationMs)}),t.markDone();break;case`error`:case`fatal`:{let n=(t.stderr&&!t.stderr.endsWith(`
1
+ import{l as e,u as t}from"./element-VP5lhHx6.js";var n=1,r=class{#e=null;#t=null;#n=`module`;#r=null;#i=!1;#a=!1;#o=``;#s=new Set;#c=[];#l=null;#u=!1;initPayload(){return{}}get ready(){return this.#i}get interactiveStdin(){return this.#a}get version(){return this.#o}load(e){e&&this.#s.add(e);let n=()=>{e&&this.#s.delete(e)};if(!this.#r){let e=new Promise((e,n)=>{let r;try{if(typeof Worker>`u`)throw Error(`Worker constructor missing`);let e=new Blob([this.source],{type:`text/javascript`});this.#t=URL.createObjectURL(e),r=new Worker(this.#t,{type:this.#n})}catch(e){n(new t(`Could not start a Web Worker: ${e.message}`));return}this.#e=r,r.onmessage=t=>{let r=t.data;if(r.type===`ready`){this.#o=r.version,this.#a=r.interactiveStdin===!0,this.#i=!0,this.#p({phase:`ready`,message:`${this.label} ready`}),e();return}if(r.type===`fatal`&&!this.#i){this.#d(r.message,e,n);return}this.#m(r)},r.onerror=t=>{let r=t.message||`Worker failed to start`;this.#i?this.#l&&this.#g(this.#l,Error(r)):this.#d(r,e,n)},r.postMessage({type:`init`,...this.initPayload()})});this.#r=e,e.catch(()=>{this.#r===e&&(this.#r=null)})}return this.#r.then(n,e=>{throw n(),e})}run(e,t={}){let r,i=new Promise(e=>{r=e});return new Promise((a,o)=>{this.#c.push({id:n++,code:e,options:t,stdout:``,stderr:``,resolve:a,reject:o,done:i,markDone:r}),this.#_()})}stop(){let t=this.#e!==null;this.#f(),this.#l&&this.#g(this.#l,new e),t&&this.load().catch(()=>{})}#d(e,t,n){if(this.#f(),this.#n===`module`){this.#n=`classic`,this.load().then(t,n);return}n(Error(e))}#f(){this.#e?.terminate(),this.#e=null,this.#t&&=(URL.revokeObjectURL(this.#t),null),this.#r=null,this.#i=!1}#p(e){for(let t of this.#s)t(e)}#m(e){if(e.type===`progress`){this.#p({phase:e.phase,message:e.message});return}let t=this.#l;if(!(!t||!(`id`in e)||e.id!==t.id))switch(e.type){case`stdout`:t.stdout+=e.text,t.options.onStdout?.(e.text);break;case`echo`:t.options.onEcho?.(e.text);break;case`stdin-request`:this.#h(t,e.request);break;case`stderr`:t.stderr+=e.text,t.options.onStderr?.(e.text);break;case`result`:t.resolve({stdout:t.stdout,stderr:t.stderr||void 0,value:e.value,durationMs:Math.round(e.durationMs)}),t.markDone();break;case`error`:case`fatal`:{let n=(t.stderr&&!t.stderr.endsWith(`
2
2
  `)?`
3
3
  `:``)+e.message+`
4
4
  `;t.stderr+=n,t.options.onStderr?.(n),t.resolve({stdout:t.stdout,stderr:t.stderr,hint:`hint`in e?e.hint:void 0,durationMs:Math.round(`durationMs`in e?e.durationMs:0)}),t.markDone();break}}}#h(e,t){let n=this.#e,r=r=>{this.#e===n&&this.#l===e&&n?.postMessage({type:`stdin-response`,request:t,text:r})},i=e.options.onStdinRequest;if(!i){r(null);return}i().then(r,()=>r(null))}#g(e,t){e.reject(t),e.markDone()}async#_(){if(!this.#u){this.#u=!0;try{for(;this.#c.length;){let e=this.#c.shift();this.#l=e;try{await this.load(),e.options.onStart?.(),this.#e.postMessage({type:`run`,id:e.id,code:e.code,stdin:e.options.stdin,files:e.options.files,entry:e.options.entry}),await e.done}catch(t){this.#g(e,t)}finally{this.#l=null}}}finally{this.#u=!1}}}};export{r as t};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-playpad",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Embeddable browser code playground: a CodeMirror editor that runs Python, JavaScript and TypeScript in the browser. No backend. One shared worker per page, blocking input(), multi-file projects, lazy per-language loading.",
5
5
  "type": "module",
6
6
  "license": "MIT",