chalk-talk 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/assets/player.html +3 -3
- package/embed-mermaid.js +419 -419
- package/embed.js +5 -5
- package/markdown.js +1 -1
- package/mount.global.js +446 -446
- package/mount.js +1 -1
- package/package.json +1 -1
- package/player.js +4 -4
- package/scripts/contract.mjs +276 -17
- package/scripts/mermaid.mjs +1 -1
- package/scripts/syntax.mjs +3 -1
package/scripts/contract.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GENERATED by tools/assets.mjs from the drawing engine v0.
|
|
1
|
+
// GENERATED by tools/assets.mjs from the drawing engine v0.253.0. Do not edit.
|
|
2
2
|
var WORDS = ["and", "or", "not"];
|
|
3
3
|
|
|
4
4
|
var PROP_NAMES = [
|
|
@@ -31,7 +31,24 @@ var PROP_NAMES = [
|
|
|
31
31
|
"bulb",
|
|
32
32
|
"gear",
|
|
33
33
|
"database",
|
|
34
|
-
"robot"
|
|
34
|
+
"robot",
|
|
35
|
+
// Onda B (spec 2026-11-01, chalk-talk): logistics, cloud, school
|
|
36
|
+
"ship",
|
|
37
|
+
"container",
|
|
38
|
+
"warehouse",
|
|
39
|
+
"box",
|
|
40
|
+
"pallet",
|
|
41
|
+
"shop",
|
|
42
|
+
"shelf",
|
|
43
|
+
"field",
|
|
44
|
+
"cup",
|
|
45
|
+
"function",
|
|
46
|
+
"api-gateway",
|
|
47
|
+
"cache",
|
|
48
|
+
"cluster",
|
|
49
|
+
"board",
|
|
50
|
+
"book",
|
|
51
|
+
"pencil"
|
|
35
52
|
];
|
|
36
53
|
var PROP_ALIASES = {
|
|
37
54
|
pessoa: "person",
|
|
@@ -69,7 +86,9 @@ var C = {
|
|
|
69
86
|
bus: "#E9C46A",
|
|
70
87
|
green: "#7DBE8A",
|
|
71
88
|
dark: "#4A4A4A",
|
|
72
|
-
metal: "#C9D3DD"
|
|
89
|
+
metal: "#C9D3DD",
|
|
90
|
+
board: "#2F4F3F",
|
|
91
|
+
chalk: "#F2EFE6"
|
|
73
92
|
};
|
|
74
93
|
var rect = (x, y, w, h, fill, main) => ({
|
|
75
94
|
kind: "polygon",
|
|
@@ -275,6 +294,115 @@ var PROPS = {
|
|
|
275
294
|
line([0.28, -0.55], [0.42, -0.35]),
|
|
276
295
|
rect(-0.2, -0.2, 0.12, 0.2, C.device),
|
|
277
296
|
rect(0.08, -0.2, 0.12, 0.2, C.device)
|
|
297
|
+
],
|
|
298
|
+
// Onda B (spec 2026-11-01, chalk-talk): logistics
|
|
299
|
+
ship: [
|
|
300
|
+
{ kind: "polygon", points: [[-0.48, -0.3], [0.48, -0.3], [0.36, -0.04], [-0.4, -0.04]], fill: C.car, main: true },
|
|
301
|
+
rect(-0.42, -0.62, 0.14, 0.32, C.wall),
|
|
302
|
+
rect(-0.24, -0.5, 0.2, 0.2, C.roof),
|
|
303
|
+
rect(-0.02, -0.5, 0.2, 0.2, C.bus),
|
|
304
|
+
rect(0.2, -0.5, 0.2, 0.2, C.green),
|
|
305
|
+
{ kind: "curve", points: [[-0.5, 0], [-0.3, -0.04], [-0.1, 0], [0.1, -0.04], [0.3, 0], [0.5, -0.04]] }
|
|
306
|
+
],
|
|
307
|
+
container: [
|
|
308
|
+
rect(-0.46, -0.52, 0.92, 0.52, C.roof, true),
|
|
309
|
+
...[-0.3, -0.15, 0, 0.15, 0.3].map((x) => line([x, -0.48], [x, -0.04]))
|
|
310
|
+
],
|
|
311
|
+
warehouse: [
|
|
312
|
+
{ kind: "polygon", points: [[-0.46, 0], [0.46, 0], [0.46, -0.55], [0, -0.82], [-0.46, -0.55]], fill: C.wall, main: true },
|
|
313
|
+
rect(-0.2, -0.38, 0.4, 0.38, C.metal),
|
|
314
|
+
line([-0.2, -0.26], [0.2, -0.26]),
|
|
315
|
+
line([-0.2, -0.13], [0.2, -0.13])
|
|
316
|
+
],
|
|
317
|
+
box: [
|
|
318
|
+
rect(-0.36, -0.5, 0.52, 0.5, C.wood, true),
|
|
319
|
+
{ kind: "polygon", points: [[-0.36, -0.5], [0.16, -0.5], [0.36, -0.64], [-0.16, -0.64]], fill: C.wood },
|
|
320
|
+
{ kind: "polygon", points: [[0.16, 0], [0.36, -0.14], [0.36, -0.64], [0.16, -0.5]], fill: C.wood },
|
|
321
|
+
line([-0.1, -0.5], [0.1, -0.64])
|
|
322
|
+
],
|
|
323
|
+
pallet: [
|
|
324
|
+
rect(-0.46, -0.12, 0.92, 0.06, C.wood),
|
|
325
|
+
...[-0.42, -0.05, 0.32].map((x) => rect(x, -0.06, 0.1, 0.06, C.wood)),
|
|
326
|
+
rect(-0.42, -0.5, 0.4, 0.38, C.wood, true),
|
|
327
|
+
rect(0.02, -0.5, 0.4, 0.38, C.wood),
|
|
328
|
+
rect(-0.2, -0.82, 0.4, 0.32, C.wood)
|
|
329
|
+
],
|
|
330
|
+
shop: [
|
|
331
|
+
rect(-0.42, -0.6, 0.84, 0.6, C.wall, true),
|
|
332
|
+
{ kind: "polygon", points: [[-0.48, -0.6], [0.48, -0.6], [0.42, -0.78], [-0.42, -0.78]], fill: C.roof },
|
|
333
|
+
rect(-0.34, -0.48, 0.34, 0.26, C.screen),
|
|
334
|
+
rect(0.08, -0.42, 0.24, 0.42, C.wood)
|
|
335
|
+
],
|
|
336
|
+
shelf: [
|
|
337
|
+
rect(-0.4, -0.92, 0.8, 0.92, C.paper, true),
|
|
338
|
+
line([-0.4, -0.62], [0.4, -0.62]),
|
|
339
|
+
line([-0.4, -0.32], [0.4, -0.32]),
|
|
340
|
+
rect(-0.32, -0.8, 0.16, 0.18, C.roof),
|
|
341
|
+
rect(-0.1, -0.78, 0.14, 0.16, C.bus),
|
|
342
|
+
rect(0.1, -0.5, 0.22, 0.18, C.green),
|
|
343
|
+
rect(-0.3, -0.2, 0.24, 0.2, C.wood)
|
|
344
|
+
],
|
|
345
|
+
field: [
|
|
346
|
+
line([-0.48, 0], [0.48, 0]),
|
|
347
|
+
...[-0.3, 0, 0.3].flatMap((x) => [
|
|
348
|
+
{ kind: "curve", points: [[x, 0], [x - 0.02, -0.25], [x, -0.5]] },
|
|
349
|
+
{ kind: "circle", c: [x - 0.08, -0.32], r: 0.07, fill: C.green, main: true },
|
|
350
|
+
{ kind: "circle", c: [x + 0.08, -0.42], r: 0.07, fill: C.green, main: true },
|
|
351
|
+
{ kind: "circle", c: [x, -0.52], r: 0.06, fill: C.roof }
|
|
352
|
+
])
|
|
353
|
+
],
|
|
354
|
+
cup: [
|
|
355
|
+
{ kind: "polygon", points: [[-0.26, -0.52], [0.26, -0.52], [0.2, -0.1], [-0.2, -0.1]], fill: C.paper, main: true },
|
|
356
|
+
{ kind: "curve", points: [[0.24, -0.44], [0.4, -0.38], [0.22, -0.2]] },
|
|
357
|
+
{ kind: "polygon", points: [[-0.46, -0.1], [0.46, -0.1], [0.36, 0], [-0.36, 0]], fill: C.paper },
|
|
358
|
+
{ kind: "curve", points: [[-0.08, -0.62], [-0.02, -0.72], [-0.08, -0.84]] },
|
|
359
|
+
{ kind: "curve", points: [[0.08, -0.62], [0.14, -0.72], [0.08, -0.84]] }
|
|
360
|
+
],
|
|
361
|
+
// cloud
|
|
362
|
+
function: [
|
|
363
|
+
rect(-0.38, -0.72, 0.76, 0.62, C.screen, true),
|
|
364
|
+
line([-0.14, -0.62], [0, -0.62], [0.14, -0.2]),
|
|
365
|
+
line([0.02, -0.42], [-0.14, -0.2])
|
|
366
|
+
],
|
|
367
|
+
"api-gateway": [
|
|
368
|
+
rect(-0.12, -0.8, 0.24, 0.8, C.metal, true),
|
|
369
|
+
line([-0.48, -0.55], [-0.16, -0.55]),
|
|
370
|
+
line([-0.24, -0.62], [-0.16, -0.55], [-0.24, -0.48]),
|
|
371
|
+
line([0.16, -0.3], [0.48, -0.3]),
|
|
372
|
+
line([0.4, -0.37], [0.48, -0.3], [0.4, -0.23])
|
|
373
|
+
],
|
|
374
|
+
cache: [
|
|
375
|
+
rect(-0.34, -0.66, 0.68, 0.66, C.metal, true),
|
|
376
|
+
{ kind: "polygon", points: [[0.04, -0.58], [-0.12, -0.3], [0, -0.3], [-0.06, -0.08], [0.14, -0.38], [0.02, -0.38]], fill: C.gold }
|
|
377
|
+
],
|
|
378
|
+
// a cluster: three machines inside a rounded outline (a generic drawing, not any product's logo)
|
|
379
|
+
cluster: [
|
|
380
|
+
{ kind: "curve", points: [[-0.4, -0.8], [0, -0.86], [0.4, -0.8], [0.48, -0.45], [0.4, -0.1], [0, -0.04], [-0.4, -0.1], [-0.48, -0.45], [-0.4, -0.8]] },
|
|
381
|
+
rect(-0.3, -0.7, 0.24, 0.2, C.screen, true),
|
|
382
|
+
rect(0.06, -0.7, 0.24, 0.2, C.screen, true),
|
|
383
|
+
rect(-0.12, -0.38, 0.24, 0.2, C.screen, true)
|
|
384
|
+
],
|
|
385
|
+
// school: the chalk on the board is light, so it is drawn as thin light strips
|
|
386
|
+
board: [
|
|
387
|
+
rect(-0.48, -0.9, 0.96, 0.6, C.board, true),
|
|
388
|
+
rect(-0.34, -0.76, 0.42, 0.03, C.chalk),
|
|
389
|
+
rect(-0.34, -0.62, 0.58, 0.03, C.chalk),
|
|
390
|
+
rect(-0.34, -0.48, 0.3, 0.03, C.chalk),
|
|
391
|
+
line([-0.3, -0.3], [-0.36, 0]),
|
|
392
|
+
line([0.3, -0.3], [0.36, 0])
|
|
393
|
+
],
|
|
394
|
+
book: [
|
|
395
|
+
{ kind: "polygon", points: [[0, -0.62], [-0.46, -0.7], [-0.46, -0.1], [0, -0.04]], fill: C.paper, main: true },
|
|
396
|
+
{ kind: "polygon", points: [[0, -0.62], [0.46, -0.7], [0.46, -0.1], [0, -0.04]], fill: C.paper, main: true },
|
|
397
|
+
line([-0.36, -0.52], [-0.1, -0.48]),
|
|
398
|
+
line([-0.36, -0.38], [-0.1, -0.34]),
|
|
399
|
+
line([0.1, -0.48], [0.36, -0.52]),
|
|
400
|
+
line([0.1, -0.34], [0.36, -0.38])
|
|
401
|
+
],
|
|
402
|
+
pencil: [
|
|
403
|
+
{ kind: "polygon", points: [[-0.38, -0.12], [0.22, -0.72], [0.34, -0.6], [-0.26, 0]], fill: C.bus, main: true },
|
|
404
|
+
{ kind: "polygon", points: [[0.22, -0.72], [0.34, -0.6], [0.44, -0.86]], fill: C.wood },
|
|
405
|
+
{ kind: "polygon", points: [[-0.38, -0.12], [-0.26, 0], [-0.46, -0.02]], fill: C.roof }
|
|
278
406
|
]
|
|
279
407
|
};
|
|
280
408
|
var MOODS = ["happy", "sad", "surprised", "worried", "idea"];
|
|
@@ -288,6 +416,8 @@ var hasFace = (name) => {
|
|
|
288
416
|
return !!key && key in HEADS;
|
|
289
417
|
};
|
|
290
418
|
|
|
419
|
+
var MAX_DEFS = 8;
|
|
420
|
+
var MAX_DEF_SHAPES = 24;
|
|
291
421
|
function defaultValues(scene) {
|
|
292
422
|
const values = {};
|
|
293
423
|
for (const [k, p] of Object.entries(scene.params ?? {})) values[k] = p.value;
|
|
@@ -322,10 +452,11 @@ var STEP_TYPES = [
|
|
|
322
452
|
"brace",
|
|
323
453
|
"note",
|
|
324
454
|
"table",
|
|
325
|
-
"bars"
|
|
455
|
+
"bars",
|
|
456
|
+
"chain"
|
|
326
457
|
];
|
|
327
458
|
var PLACEABLE = ["text", "prop", "card", "counter", "circle", "ellipse"];
|
|
328
|
-
var ARRANGEMENTS = ["row", "column", "grid"];
|
|
459
|
+
var ARRANGEMENTS = ["row", "column", "grid", "chain"];
|
|
329
460
|
var MAX_REPEAT = 50;
|
|
330
461
|
function object(value2, name) {
|
|
331
462
|
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error(`${name}: expected an object.`);
|
|
@@ -476,8 +607,9 @@ function validateStep(step2, name, ids, inside, inRow = false) {
|
|
|
476
607
|
optionalText(step2.ask, `${name}.ask`);
|
|
477
608
|
break;
|
|
478
609
|
case "prop":
|
|
479
|
-
if (!isPropName(step2.name)) {
|
|
480
|
-
|
|
610
|
+
if (!isPropName(step2.name) && !ids.figures.has(String(step2.name))) {
|
|
611
|
+
const own = ids.figures.size ? `, or one the recipe defines (${[...ids.figures].join(", ")})` : "";
|
|
612
|
+
throw new Error(`${name}.name: unknown prop "${String(step2.name)}"; use one of ${PROP_NAMES.join(", ")}${own}.`);
|
|
481
613
|
}
|
|
482
614
|
pointOrPlaced(step2, "at", name, placed);
|
|
483
615
|
if (step2.size !== void 0) {
|
|
@@ -550,6 +682,27 @@ function validateStep(step2, name, ids, inside, inRow = false) {
|
|
|
550
682
|
validateStep(inner, `${name}.steps[${j}]`, ids, step2.type);
|
|
551
683
|
});
|
|
552
684
|
break;
|
|
685
|
+
case "chain":
|
|
686
|
+
if (arranged) throw new Error(`${name}: a chain cannot go inside a ${inside}.`);
|
|
687
|
+
pointOrPlaced(step2, "at", name, false);
|
|
688
|
+
if (step2.gap !== void 0 && step2.gap !== "auto") finite(step2.gap, `${name}.gap`, 0, 400);
|
|
689
|
+
choice(step2.direction, ["right", "down"], `${name}.direction`);
|
|
690
|
+
if (step2.wrap !== void 0 && typeof step2.wrap !== "boolean") throw new Error(`${name}.wrap: expected true or false.`);
|
|
691
|
+
optionalText(step2.forward, `${name}.forward`);
|
|
692
|
+
optionalText(step2.back, `${name}.back`);
|
|
693
|
+
if (step2.size !== void 0) {
|
|
694
|
+
numeric(step2.size, `${name}.size`);
|
|
695
|
+
if (typeof step2.size === "number") finite(step2.size, `${name}.size`, 10, 1e3);
|
|
696
|
+
}
|
|
697
|
+
if (!Array.isArray(step2.steps) || step2.steps.length < 2 || step2.steps.length > 12) {
|
|
698
|
+
throw new Error(`${name}.steps: a chain joins 2 to 12 pieces.`);
|
|
699
|
+
}
|
|
700
|
+
step2.steps.forEach((inner, j) => {
|
|
701
|
+
object(inner, `${name}.steps[${j}]`);
|
|
702
|
+
if (!PLACEABLE.includes(inner.type)) throw new Error(`${name}.steps[${j}]: a chain holds only ${PLACEABLE.join(", ")}.`);
|
|
703
|
+
validateStep(inner, `${name}.steps[${j}]`, ids, "chain");
|
|
704
|
+
});
|
|
705
|
+
break;
|
|
553
706
|
case "axis":
|
|
554
707
|
case "timeline":
|
|
555
708
|
point(step2.from, `${name}.from`);
|
|
@@ -636,6 +789,38 @@ function validateStep(step2, name, ids, inside, inRow = false) {
|
|
|
636
789
|
}
|
|
637
790
|
if (typeof step2.id === "string") (inside === "repeat" ? ids.repeated : ids.exact).add(step2.id);
|
|
638
791
|
}
|
|
792
|
+
function validateFigure(key, def) {
|
|
793
|
+
const name = `defs.${key}`;
|
|
794
|
+
if (!/^[a-z][a-z0-9-]{1,30}$/.test(key)) throw new Error(`${name}: a figure's name is 2 to 31 lowercase letters, digits or -, starting with a letter.`);
|
|
795
|
+
if (propKey(key) || Object.hasOwn(PROP_ALIASES, key) || STEP_TYPES.includes(key) || ["define", "title", "param", "switch", "var"].includes(key)) {
|
|
796
|
+
throw new Error(`${name}: "${key}" is already a prop or a step; choose another name.`);
|
|
797
|
+
}
|
|
798
|
+
object(def, name);
|
|
799
|
+
if (!Array.isArray(def.shapes) || def.shapes.length === 0 || def.shapes.length > MAX_DEF_SHAPES) {
|
|
800
|
+
throw new Error(`${name}.shapes: expected 1 to ${MAX_DEF_SHAPES} shapes.`);
|
|
801
|
+
}
|
|
802
|
+
const grid = (v, at) => {
|
|
803
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 0 || v > 100) throw new Error(`${at}: expected a whole number from 0 to 100 (the figure's grid, y down; it stands at 50,100).`);
|
|
804
|
+
};
|
|
805
|
+
def.shapes.forEach((shape, j) => {
|
|
806
|
+
const at = `${name}.shapes[${j}]`;
|
|
807
|
+
object(shape, at);
|
|
808
|
+
if (shape.fill !== void 0 && (typeof shape.fill !== "string" || !/^(#[0-9a-fA-F]{3,8}|[a-z]{3,20})$/.test(shape.fill))) throw new Error(`${at}.fill: a colour, as hex or a plain name.`);
|
|
809
|
+
if (shape.kind === "circle") {
|
|
810
|
+
if (!Array.isArray(shape.c) || shape.c.length !== 2) throw new Error(`${at}.c: expected [x, y].`);
|
|
811
|
+
shape.c.forEach((v, k) => grid(v, `${at}.c[${k}]`));
|
|
812
|
+
grid(shape.r, `${at}.r`);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (!["polygon", "polyline", "curve"].includes(shape.kind)) throw new Error(`${at}.kind: choose one of polygon, polyline, curve, circle.`);
|
|
816
|
+
const least = shape.kind === "polygon" ? 3 : 2;
|
|
817
|
+
if (!Array.isArray(shape.points) || shape.points.length < least || shape.points.length > 40) throw new Error(`${at}.points: a ${shape.kind} takes ${least} to 40 points.`);
|
|
818
|
+
shape.points.forEach((p, k) => {
|
|
819
|
+
if (!Array.isArray(p) || p.length !== 2) throw new Error(`${at}.points[${k}]: expected [x, y].`);
|
|
820
|
+
p.forEach((v, n) => grid(v, `${at}.points[${k}][${n}]`));
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
}
|
|
639
824
|
function validateScene(data) {
|
|
640
825
|
object(data, "The recipe");
|
|
641
826
|
const s = data;
|
|
@@ -701,7 +886,17 @@ function validateScene(data) {
|
|
|
701
886
|
if (!Array.isArray(s.steps) || s.steps.length === 0 || s.steps.length > 250) {
|
|
702
887
|
throw new Error("A recipe needs 1 to 250 steps.");
|
|
703
888
|
}
|
|
704
|
-
const
|
|
889
|
+
const figures = /* @__PURE__ */ new Set();
|
|
890
|
+
if (s.defs !== void 0) {
|
|
891
|
+
object(s.defs, "defs");
|
|
892
|
+
const entries = Object.entries(s.defs);
|
|
893
|
+
if (entries.length > MAX_DEFS) throw new Error(`defs: at most ${MAX_DEFS} figures.`);
|
|
894
|
+
for (const [key, def] of entries) {
|
|
895
|
+
validateFigure(key, def);
|
|
896
|
+
figures.add(key);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
const ids = { exact: /* @__PURE__ */ new Set(), repeated: /* @__PURE__ */ new Set(), count: 0, figures };
|
|
705
900
|
s.steps.forEach((step2, i) => validateStep(step2, `Step ${i + 1}`, ids, null));
|
|
706
901
|
return {
|
|
707
902
|
...s,
|
|
@@ -818,12 +1013,13 @@ var STEP_WORDS = /* @__PURE__ */ new Set([
|
|
|
818
1013
|
"note",
|
|
819
1014
|
"table",
|
|
820
1015
|
"bars",
|
|
821
|
-
"json"
|
|
1016
|
+
"json",
|
|
1017
|
+
"chain"
|
|
822
1018
|
]);
|
|
823
1019
|
var SIDES = { "right-of": "right", "left-of": "left", above: "above", below: "below" };
|
|
824
1020
|
var STYLE = ["color", "fill", "roughness", "bowing", "fillStyle", "opacity"];
|
|
825
1021
|
var SPACED = /* @__PURE__ */ new Set(["gap", "size", "r", "rx", "ry", "w", "h", "columns", "ms", "thickness"]);
|
|
826
|
-
var FLAGS = /* @__PURE__ */ new Set(["dashed", "flip", "noheader"]);
|
|
1022
|
+
var FLAGS = /* @__PURE__ */ new Set(["dashed", "flip", "noheader", "wrap"]);
|
|
827
1023
|
function attrs(tokens, step2, no) {
|
|
828
1024
|
const style = {};
|
|
829
1025
|
for (let i = 0; i < tokens.length; i++) {
|
|
@@ -833,6 +1029,7 @@ function attrs(tokens, step2, no) {
|
|
|
833
1029
|
if (FLAGS.has(t)) {
|
|
834
1030
|
if (t === "dashed") style.dashed = true;
|
|
835
1031
|
else if (t === "flip") step2.flip = true;
|
|
1032
|
+
else if (t === "wrap") step2.wrap = true;
|
|
836
1033
|
else step2.header = false;
|
|
837
1034
|
continue;
|
|
838
1035
|
}
|
|
@@ -845,7 +1042,7 @@ function attrs(tokens, step2, no) {
|
|
|
845
1042
|
else if (key === "nudge") step2.nudge = point2(raw, no).map(Number);
|
|
846
1043
|
else if (key === "ticks") step2.ticks = raw.includes(",") ? parts(raw).map(Number) : Number(raw);
|
|
847
1044
|
else if (key === "labels") step2.labels = parts(raw).map(unquote);
|
|
848
|
-
else if (key === "id" || key === "anchor" || key === "font" || key === "mood" || key === "along" || key === "side" || key === "kind" || key === "var" || key === "align" || key === "label" || key === "format" || key === "fn") step2[key] = unquote(raw);
|
|
1045
|
+
else if (key === "id" || key === "anchor" || key === "font" || key === "mood" || key === "along" || key === "side" || key === "kind" || key === "var" || key === "align" || key === "label" || key === "format" || key === "fn" || key === "forward" || key === "back" || key === "direction") step2[key] = unquote(raw);
|
|
849
1046
|
else step2[key] = value(raw, no);
|
|
850
1047
|
}
|
|
851
1048
|
if (Object.keys(style).length) step2.style = style;
|
|
@@ -974,7 +1171,8 @@ function step(line2, props) {
|
|
|
974
1171
|
}
|
|
975
1172
|
case "row":
|
|
976
1173
|
case "column":
|
|
977
|
-
case "grid":
|
|
1174
|
+
case "grid":
|
|
1175
|
+
case "chain": {
|
|
978
1176
|
const s = { type: w };
|
|
979
1177
|
const more = placement(rest, s, "at", no);
|
|
980
1178
|
attrs(more, s, no);
|
|
@@ -1063,6 +1261,50 @@ function step(line2, props) {
|
|
|
1063
1261
|
}
|
|
1064
1262
|
throw new ChalkTextError(no, `\xAB${w}\xBB is not a step, a prop or an id followed by -> / says.`);
|
|
1065
1263
|
}
|
|
1264
|
+
function links(line2) {
|
|
1265
|
+
const { no, tokens } = line2;
|
|
1266
|
+
const out = [];
|
|
1267
|
+
let from = tokens[0];
|
|
1268
|
+
let i = 1;
|
|
1269
|
+
while (tokens[i] === "->") {
|
|
1270
|
+
const to = tokens[i + 1];
|
|
1271
|
+
if (!to || isQuoted(to)) throw new ChalkTextError(no, 'write a -> b "label" -> c "label".');
|
|
1272
|
+
const s = { type: "link", from, to };
|
|
1273
|
+
i += 2;
|
|
1274
|
+
if (tokens[i] && isQuoted(tokens[i])) s.label = unquote(tokens[i++]);
|
|
1275
|
+
out.push(s);
|
|
1276
|
+
from = to;
|
|
1277
|
+
}
|
|
1278
|
+
const more = tokens.slice(i);
|
|
1279
|
+
return out.map((s) => attrs([...more], s, no));
|
|
1280
|
+
}
|
|
1281
|
+
function figureShape(line2) {
|
|
1282
|
+
const { no, tokens } = line2;
|
|
1283
|
+
const [kind, ...rest] = tokens;
|
|
1284
|
+
const grid = (t) => {
|
|
1285
|
+
const m = t?.match(/^(\d{1,3}),(\d{1,3})$/);
|
|
1286
|
+
if (!m || Number(m[1]) > 100 || Number(m[2]) > 100) throw new ChalkTextError(no, `on a figure's grid a point is two whole numbers from 0 to 100, like 24,48 (got ${t ?? "nothing"}).`);
|
|
1287
|
+
return [Number(m[1]), Number(m[2])];
|
|
1288
|
+
};
|
|
1289
|
+
const shape = { kind };
|
|
1290
|
+
let more = rest;
|
|
1291
|
+
if (kind === "circle") {
|
|
1292
|
+
if (more[0] === "at") more = more.slice(1);
|
|
1293
|
+
shape.c = grid(more[0]);
|
|
1294
|
+
more = more.slice(1);
|
|
1295
|
+
} else if (kind === "polygon" || kind === "polyline" || kind === "curve") {
|
|
1296
|
+
const pts = more.filter((t) => t.includes(",") && !t.includes("="));
|
|
1297
|
+
shape.points = pts.map(grid);
|
|
1298
|
+
more = more.slice(pts.length);
|
|
1299
|
+
} else throw new ChalkTextError(no, `a figure is made of polygon, polyline, curve and circle lines, not \xAB${kind}\xBB.`);
|
|
1300
|
+
for (const t of more) {
|
|
1301
|
+
const [k, v] = t.split("=");
|
|
1302
|
+
if (k === "fill" && v) shape.fill = unquote(v);
|
|
1303
|
+
else if (k === "r" && v && /^\d{1,3}$/.test(v)) shape.r = Number(v);
|
|
1304
|
+
else throw new ChalkTextError(no, `did not understand \xAB${t}\xBB in a figure (only fill=\u2026 and, on a circle, r=\u2026).`);
|
|
1305
|
+
}
|
|
1306
|
+
return shape;
|
|
1307
|
+
}
|
|
1066
1308
|
function parseChalk(text2, props) {
|
|
1067
1309
|
const names = new Set(props);
|
|
1068
1310
|
const scene = { id: "lesson", title: "", width: 900, height: 560 };
|
|
@@ -1136,6 +1378,13 @@ function parseChalk(text2, props) {
|
|
|
1136
1378
|
scene.learning = { ...scene.learning, [part]: unquote(t ?? "") };
|
|
1137
1379
|
continue;
|
|
1138
1380
|
}
|
|
1381
|
+
case "define": {
|
|
1382
|
+
const name = rest[0];
|
|
1383
|
+
if (!name) throw new ChalkTextError(no, "write define <name> and its shapes under it.");
|
|
1384
|
+
scene.defs = { ...scene.defs, [name]: { shapes: line2.children.map(figureShape) } };
|
|
1385
|
+
names.add(name);
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1139
1388
|
case "quiz": {
|
|
1140
1389
|
scene.quiz = {
|
|
1141
1390
|
question: unquote(rest[0] ?? ""),
|
|
@@ -1150,10 +1399,11 @@ function parseChalk(text2, props) {
|
|
|
1150
1399
|
continue;
|
|
1151
1400
|
}
|
|
1152
1401
|
}
|
|
1153
|
-
const
|
|
1402
|
+
const made = rest[0] === "->" && rest.indexOf("->", 1) > 0 ? links(line2) : [step(line2, names)];
|
|
1403
|
+
const [s] = made;
|
|
1154
1404
|
if (chapter !== void 0) [s.chapter, chapter] = [chapter, void 0];
|
|
1155
1405
|
if (say !== void 0) [s.say, say] = [say, void 0];
|
|
1156
|
-
steps.push(
|
|
1406
|
+
steps.push(...made);
|
|
1157
1407
|
}
|
|
1158
1408
|
scene.steps = steps;
|
|
1159
1409
|
return scene;
|
|
@@ -1174,8 +1424,8 @@ function printAttrs(s, skip) {
|
|
|
1174
1424
|
else return null;
|
|
1175
1425
|
}
|
|
1176
1426
|
} else if (k === "nudge") out.push(`nudge=${v.join(",")}`);
|
|
1177
|
-
else if (k === "flip") {
|
|
1178
|
-
if (v === true) out.push(
|
|
1427
|
+
else if (k === "flip" || k === "wrap") {
|
|
1428
|
+
if (v === true) out.push(k);
|
|
1179
1429
|
} else if (typeof v === "number" || typeof v === "string") out.push(`${k}=${typeof v === "number" ? v : num(v)}`);
|
|
1180
1430
|
else return null;
|
|
1181
1431
|
}
|
|
@@ -1231,7 +1481,8 @@ function printStep(s, props, indent) {
|
|
|
1231
1481
|
}
|
|
1232
1482
|
case "row":
|
|
1233
1483
|
case "column":
|
|
1234
|
-
case "grid":
|
|
1484
|
+
case "grid":
|
|
1485
|
+
case "chain": {
|
|
1235
1486
|
const head = line2(`${s.type} ${place(s, "at")}`, ["steps", ...placedSkip]);
|
|
1236
1487
|
if (head[0].trimStart().startsWith("json")) return head;
|
|
1237
1488
|
return [...head, ...s.steps.flatMap((c) => printStep(c, props, `${indent} `))];
|
|
@@ -1247,6 +1498,7 @@ function printStep(s, props, indent) {
|
|
|
1247
1498
|
}
|
|
1248
1499
|
function printChalk(scene, props) {
|
|
1249
1500
|
const names = new Set(props);
|
|
1501
|
+
for (const name of Object.keys(scene.defs ?? {})) names.add(name);
|
|
1250
1502
|
const out = [];
|
|
1251
1503
|
if (scene.id !== void 0) out.push(`id ${scene.id}`);
|
|
1252
1504
|
if (scene.title !== void 0) out.push(`title ${scene.title}`);
|
|
@@ -1258,6 +1510,13 @@ function printChalk(scene, props) {
|
|
|
1258
1510
|
for (const [k, sw] of Object.entries(scene.switches ?? {})) out.push(`switch ${k} ${q(sw.label)} ${sw.on ? "on" : "off"}`);
|
|
1259
1511
|
for (const [k, v] of Object.entries(scene.vars ?? {})) out.push(`var ${k} = ${num(v)}`);
|
|
1260
1512
|
for (const [k, v] of Object.entries(scene.learning ?? {})) out.push(`learning ${k} ${q(v)}`);
|
|
1513
|
+
for (const [name, def] of Object.entries(scene.defs ?? {})) {
|
|
1514
|
+
out.push("", `define ${name}`);
|
|
1515
|
+
for (const sh of def.shapes) {
|
|
1516
|
+
const fill = sh.fill !== void 0 ? ` fill=${sh.fill}` : "";
|
|
1517
|
+
out.push(sh.kind === "circle" ? ` circle at ${sh.c.join(",")} r=${sh.r}${fill}` : ` ${sh.kind} ${sh.points.map((p) => p.join(",")).join(" ")}${fill}`);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1261
1520
|
for (const s of scene.steps ?? []) {
|
|
1262
1521
|
if (s.chapter !== void 0 || s.say !== void 0) out.push("");
|
|
1263
1522
|
if (s.chapter !== void 0) out.push(`# ${s.chapter}`);
|