@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 +2 -2
- package/package.json +5 -5
- package/src/Playground.svelte +141 -127
- package/src/marks.js +4 -2
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.
|
|
59
|
-
|
|
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.
|
|
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.
|
|
28
|
-
"@kupcad/protocol": "0.1.
|
|
29
|
-
"@kupcad/ui": "0.1.
|
|
30
|
-
"@kupcad/wasm": "0.1.
|
|
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",
|
package/src/Playground.svelte
CHANGED
|
@@ -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(
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
{/
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
432
|
-
script a few lines, the model 4:3 of its width (at most 32em),
|
|
433
|
-
parameters as tall as their lines */
|
|
434
|
-
@
|
|
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'))
|
|
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
|
}
|