@kupcad/playground 0.1.6 → 0.1.8

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
@@ -55,8 +55,8 @@ browser IDE embed; it keeps nothing of the page's.
55
55
  resize them (`@kupcad/ui`'s `Splitter`), and the toolbar's Parameters button hides the
56
56
  parameters for the model's width; the sizes, and what is hidden or folded, are kept in
57
57
  `settings`, by default the page's localStorage (`localSettings`, the `playground.*` keys), and
58
- how the view looks too (the pane's `view.*` keys), all followed as they are set. A page 800px
59
- wide or less stacks the panes, each at a height of its own (the script a few lines, the model
58
+ how the view looks too (the pane's `view.*` keys), all followed as they are set. Given 800px
59
+ or less (a phone, or a column of a page: it goes by its own width) it stacks the panes, each at a height of its own (the script a few lines, the model
60
60
  4:3 of its width, at most 32em, the console and the parameters as tall as their lines), and
61
61
  the playground scrolls; on a phone the view's panel is under the model (`ViewerPane`'s).
62
62
  * **The console** (`@kupcad/ui`'s `ConsolePanel`, under the model): each run, what it printed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kupcad/playground",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "KupCAD in the browser: a script's editor, its model and its parameters, running on the core compiled to WebAssembly in a worker. One Svelte component a page embeds.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -24,10 +24,10 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@kupcad/editor": "0.1.6",
28
- "@kupcad/protocol": "0.1.6",
29
- "@kupcad/ui": "0.1.6",
30
- "@kupcad/wasm": "0.1.6"
27
+ "@kupcad/editor": "0.1.8",
28
+ "@kupcad/protocol": "0.1.8",
29
+ "@kupcad/ui": "0.1.8",
30
+ "@kupcad/wasm": "0.1.8"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@codemirror/language": "^6.13.1",
@@ -27,6 +27,7 @@
27
27
  linesOfLog,
28
28
  linesOfRun,
29
29
  localSettings,
30
+ paneKeys,
30
31
  ParamsPanel,
31
32
  plainValues,
32
33
  problemsOf,
@@ -38,7 +39,7 @@
38
39
  import { createKupcadWorker } from '@kupcad/wasm';
39
40
  import { download } from './download.js';
40
41
  import { marksOf } from './marks.js';
41
- import { Runner } from './runner.js';
42
+ import { DELAY_MS, Runner } from './runner.js';
42
43
 
43
44
  /** @import { Editor as EditorClass, EditorLanguage } from '@kupcad/editor' */
44
45
  /** @import { ExportFormat, Finding, ParamValues, Preview } from '@kupcad/protocol/types' */
@@ -67,6 +68,8 @@
67
68
  * (`.kc-control` gives them its look)
68
69
  * @property {readonly ShortcutGroup[]} [shortcuts] the host's own, listed before the viewer's
69
70
  * @property {boolean} [shortcutsOpen] whether the shortcuts' dialog shows; bindable
71
+ * @property {number} [runDelayMs] how long the script stays as it is after an edit before it
72
+ * runs (runner.js's DELAY_MS); a test gives 0, so a run that would come comes at once
70
73
  */
71
74
 
72
75
  /** @type {Props} */
@@ -82,20 +85,14 @@
82
85
  actions: hostActions,
83
86
  shortcuts = [],
84
87
  shortcutsOpen = $bindable(false),
88
+ runDelayMs = DELAY_MS,
85
89
  } = $props();
86
90
 
87
91
  /**
88
92
  * The settings' keys: the editor's and the parameters' widths, whether the parameters and the
89
93
  * view's panel show, the console's height and state.
90
94
  */
91
- const KEYS = {
92
- editor: 'playground.editor',
93
- params: 'playground.params',
94
- paramsOpen: 'playground.paramsOpen',
95
- panelOpen: 'playground.panelOpen',
96
- console: 'playground.console',
97
- open: 'playground.consoleOpen',
98
- };
95
+ const KEYS = { editor: 'playground.editor', ...paneKeys('playground') };
99
96
  /** The least the model is given, however wide the editor is made. */
100
97
  const MIN_VIEW = 320;
101
98
  const id = $props.id();
@@ -151,18 +148,22 @@
151
148
  onMount(() => {
152
149
  const made = kupcad ? null : createKupcadWorker(wasm ? { wasm, log } : { log });
153
150
  core = kupcad ?? made;
154
- runner = new Runner(/** @type {KupcadApi} */ (core), {
155
- onfindings: (next, of) => {
156
- if (runs && of === value) findings = next;
151
+ runner = new Runner(
152
+ /** @type {KupcadApi} */ (core),
153
+ {
154
+ onfindings: (next, of) => {
155
+ if (runs && of === value) findings = next;
156
+ },
157
+ onpreview: (preview, of) => {
158
+ if (!runs) return;
159
+ ran = { preview, source: of };
160
+ notice = '';
161
+ consoleLines = appendLines(consoleLines, linesOfRun(preview.header, name));
162
+ },
163
+ onerror: (error) => (notice = errorText(error)),
157
164
  },
158
- onpreview: (preview, of) => {
159
- if (!runs) return;
160
- ran = { preview, source: of };
161
- notice = '';
162
- consoleLines = appendLines(consoleLines, linesOfRun(preview.header, name));
163
- },
164
- onerror: (error) => (notice = errorText(error)),
165
- });
165
+ { delayMs: runDelayMs },
166
+ );
166
167
  if (value !== '') runner.run(value, {}, { now: true, files });
167
168
  return () => {
168
169
  runner?.stop();
@@ -251,9 +252,13 @@
251
252
  runner?.run(value, values, { now: true, files });
252
253
  }
253
254
 
254
- /** @param {Problem | ConsoleLine} place a problem or a console line with a line, clicked */
255
+ /**
256
+ * Shows the script where a problem or a console line is, clicked; one in a library (`file`),
257
+ * another file than the editor's, isn't shown.
258
+ * @param {{ line: number | null, column: number | null, file?: string | null }} place
259
+ */
255
260
  function reveal(place) {
256
- if (place.line !== null) editor?.reveal(place.line, place.column ?? 1);
261
+ if (place.line !== null && !place.file) editor?.reveal(place.line, place.column ?? 1);
257
262
  }
258
263
 
259
264
  /** @param {ExportFormat} format */
@@ -274,113 +279,122 @@
274
279
  }
275
280
  </script>
276
281
 
277
- <div
278
- class="kc-playground"
279
- bind:clientWidth={width}
280
- style:--editor-width="{editorWidth}px"
281
- style:--params-width="{paramsWidth}px"
282
- >
283
- <section class="code" aria-label="Script">
284
- {#key documents}
285
- <Editor
286
- bind:value
287
- bind:editor
288
- findings={marks}
289
- label={name}
290
- restore={restored}
291
- {language}
292
- onchange={edited}
293
- />
294
- {/key}
295
- {#if notice}<p class="notice" role="alert">{notice}</p>{/if}
296
- </section>
297
- <Splitter
298
- bind:size={editorWidth}
299
- min={240}
300
- max={width > 0 ? width - (paramsShown ? paramsWidth : 0) - MIN_VIEW : undefined}
301
- label="Resize the script"
302
- onchange={(size) => settings.set(KEYS.editor, size)}
303
- />
304
- <section class="view" aria-label="Model">
305
- <div class="model">
306
- <ViewerPane
307
- model={ran?.preview.model ?? null}
308
- subject="script {loads}"
309
- options={viewerOptions}
310
- bind:panel={() => panelOpen, (open) => settings.set(KEYS.panelOpen, open)}
311
- {status}
312
- {problems}
313
- onproblem={reveal}
314
- onscreenshot={screenshot}
315
- {settings}
316
- {shortcuts}
317
- bind:shortcutsOpen
318
- >
319
- {#snippet actions()}
320
- <ExportMenu kind={ran?.preview.model.kind ?? 'none'} onexport={exportChosen} />
321
- {@render hostActions?.()}
322
- {/snippet}
323
- {#snippet toggles()}
324
- {#if header && parameters.length > 0}
325
- <button
326
- type="button"
327
- class="kc-control"
328
- aria-controls="{id}-params"
329
- aria-expanded={paramsOpen}
330
- onclick={() => settings.set(KEYS.paramsOpen, !paramsOpen)}
331
- >
332
- Parameters
333
- </button>
334
- {/if}
335
- {/snippet}
336
- </ViewerPane>
337
- </div>
338
- {#if consoleOpen}
339
- <Splitter
340
- between="rows"
341
- pane="after"
342
- bind:size={consoleHeight}
343
- min={48}
344
- label="Resize the console"
345
- onchange={(size) => settings.set(KEYS.console, size)}
346
- />
347
- {/if}
348
- <ConsolePanel
349
- lines={consoleLines}
350
- height={consoleHeight}
351
- bind:open={() => consoleOpen, (open) => settings.set(KEYS.open, open)}
352
- onreveal={reveal}
353
- onclear={() => (consoleLines = [])}
282
+ <!-- The box the playground is laid out by: its own width, wide or a column of a page -->
283
+ <div class="kc-playground-box">
284
+ <div
285
+ class="kc-playground"
286
+ bind:clientWidth={width}
287
+ style:--editor-width="{editorWidth}px"
288
+ style:--params-width="{paramsWidth}px"
289
+ >
290
+ <section class="code" aria-label="Script">
291
+ {#key documents}
292
+ <Editor
293
+ bind:value
294
+ bind:editor
295
+ findings={marks}
296
+ label={name}
297
+ restore={restored}
298
+ {language}
299
+ onchange={edited}
300
+ />
301
+ {/key}
302
+ {#if notice}<p class="notice" role="alert">{notice}</p>{/if}
303
+ </section>
304
+ <Splitter
305
+ bind:size={editorWidth}
306
+ min={240}
307
+ max={width > 0 ? width - (paramsShown ? paramsWidth : 0) - MIN_VIEW : undefined}
308
+ label="Resize the script"
309
+ onchange={(size) => settings.set(KEYS.editor, size)}
354
310
  />
355
- </section>
356
- {#if header && parameters.length > 0}
357
- {#if paramsOpen}
358
- <Splitter
359
- pane="after"
360
- bind:size={paramsWidth}
361
- min={200}
362
- max={640}
363
- label="Resize the parameters"
364
- onchange={(size) => settings.set(KEYS.params, size)}
311
+ <section class="view" aria-label="Model">
312
+ <div class="model">
313
+ <ViewerPane
314
+ model={ran?.preview.model ?? null}
315
+ subject="script {loads}"
316
+ options={viewerOptions}
317
+ bind:panel={() => panelOpen, (open) => settings.set(KEYS.panelOpen, open)}
318
+ {status}
319
+ {problems}
320
+ onproblem={reveal}
321
+ onsource={(part) => part.source && reveal({ file: null, ...part.source })}
322
+ onscreenshot={screenshot}
323
+ {settings}
324
+ {shortcuts}
325
+ bind:shortcutsOpen
326
+ >
327
+ {#snippet actions()}
328
+ <ExportMenu kind={ran?.preview.model.kind ?? 'none'} onexport={exportChosen} />
329
+ {@render hostActions?.()}
330
+ {/snippet}
331
+ {#snippet toggles()}
332
+ {#if header && parameters.length > 0}
333
+ <button
334
+ type="button"
335
+ class="kc-control"
336
+ aria-controls="{id}-params"
337
+ aria-expanded={paramsOpen}
338
+ onclick={() => settings.set(KEYS.paramsOpen, !paramsOpen)}
339
+ >
340
+ Parameters
341
+ </button>
342
+ {/if}
343
+ {/snippet}
344
+ </ViewerPane>
345
+ </div>
346
+ {#if consoleOpen}
347
+ <Splitter
348
+ between="rows"
349
+ pane="after"
350
+ bind:size={consoleHeight}
351
+ min={48}
352
+ label="Resize the console"
353
+ onchange={(size) => settings.set(KEYS.console, size)}
354
+ />
355
+ {/if}
356
+ <ConsolePanel
357
+ lines={consoleLines}
358
+ height={consoleHeight}
359
+ bind:open={() => consoleOpen, (open) => settings.set(KEYS.open, open)}
360
+ onreveal={reveal}
361
+ onclear={() => (consoleLines = [])}
365
362
  />
366
- {/if}
367
- <!-- Hidden, not gone: what its fields hold stays, and the toggle has it to control -->
368
- <aside class="params" id="{id}-params" aria-label="Parameters" hidden={!paramsOpen}>
369
- <!-- Made again for each script loaded: no field keeps what was typed in the last's -->
370
- {#key loads}
371
- <ParamsPanel
372
- schema={header.params.schema}
373
- values={header.params.values}
374
- error={header.error}
375
- bind:overrides
376
- onchange={valuesChanged}
363
+ </section>
364
+ {#if header && parameters.length > 0}
365
+ {#if paramsOpen}
366
+ <Splitter
367
+ pane="after"
368
+ bind:size={paramsWidth}
369
+ min={200}
370
+ max={640}
371
+ label="Resize the parameters"
372
+ onchange={(size) => settings.set(KEYS.params, size)}
377
373
  />
378
- {/key}
379
- </aside>
380
- {/if}
374
+ {/if}
375
+ <!-- Hidden, not gone: what its fields hold stays, and the toggle has it to control -->
376
+ <aside class="params" id="{id}-params" aria-label="Parameters" hidden={!paramsOpen}>
377
+ <!-- Made again for each script loaded: no field keeps what was typed in the last's -->
378
+ {#key loads}
379
+ <ParamsPanel
380
+ schema={header.params.schema}
381
+ values={header.params.values}
382
+ error={header.error}
383
+ bind:overrides
384
+ onchange={valuesChanged}
385
+ />
386
+ {/key}
387
+ </aside>
388
+ {/if}
389
+ </div>
381
390
  </div>
382
391
 
383
392
  <style>
393
+ .kc-playground-box {
394
+ height: 100%;
395
+ min-height: 0;
396
+ container-type: inline-size;
397
+ }
384
398
  .kc-playground {
385
399
  display: flex;
386
400
  height: 100%;
@@ -428,10 +442,10 @@
428
442
  border-radius: var(--kc-radius);
429
443
  background: var(--kc-bg);
430
444
  }
431
- /* Narrow, the panes stacked, each at a height of its own, and the playground scrolled: the
432
- script a few lines, the model 4:3 of its width (at most 32em), the console and the
433
- parameters as tall as their lines */
434
- @media (max-width: 800px) {
445
+ /* Narrow (a phone, a column of a page), the panes stacked, each at a height of its own, and
446
+ the playground scrolled: the script a few lines, the model 4:3 of its width (at most 32em),
447
+ the console and the parameters as tall as their lines */
448
+ @container (max-width: 800px) {
435
449
  .kc-playground {
436
450
  flex-direction: column;
437
451
  overflow-y: auto;
package/src/marks.js CHANGED
@@ -8,7 +8,8 @@
8
8
 
9
9
  /**
10
10
  * @param {readonly Finding[]} findings `check`'s
11
- * @param {readonly Problem[]} problems the run's (`problemsOf(header)`): those with a line
11
+ * @param {readonly Problem[]} problems the run's (`problemsOf(header)`): those with a line in
12
+ * the script (not in a library, `file`)
12
13
  * @returns {Mark[]}
13
14
  */
14
15
  export function marksOf(findings, problems) {
@@ -16,7 +17,8 @@ export function marksOf(findings, problems) {
16
17
  /** @type {Mark[]} */
17
18
  const placed = [];
18
19
  for (const problem of problems) {
19
- if (problem.line === null || (checked && problem.severity === 'error')) continue;
20
+ if (problem.line === null || problem.file || (checked && problem.severity === 'error'))
21
+ continue;
20
22
  const { severity, message, line } = problem;
21
23
  placed.push({ severity, message, line, column: problem.column ?? 1 });
22
24
  }