@flatkit/sugarflat 0.5.0 → 0.6.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 +40 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +206 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -132,7 +132,46 @@ cards) swap places when the activity loads, so playing it again is a new attempt
|
|
|
132
132
|
does the order of a `steps` sequence. The draw comes from `random()`: pass the player a `seed` to get the
|
|
133
133
|
same layout again (`flatc --play` always does). `meta[].shuffle` says a block asked for it.
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
## Controls
|
|
136
|
+
|
|
137
|
+
A control is an INPUT, not a task: it writes a value, it has no end, and the document's `completed` does
|
|
138
|
+
not wait for it (`meta[].control` is `true`, `doneVar` empty). Its value is the variable `<name>_value`;
|
|
139
|
+
it sends `change` with `{ block, value }` when the value moves. They are the recipes activities kept
|
|
140
|
+
copying by hand — the bounds, the rounding, the repeat while held.
|
|
141
|
+
|
|
142
|
+
| | |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `stepper <name> { min <n> max <n> [step <n>] [start <n>] minus at x,y plus at x,y [counter at x,y] }` | − / + buttons: a step at the press, then a repeat while held (after 0.4 s, every 0.12 s). Enter or Space on the focused button steps once |
|
|
145
|
+
| `slider <name> { min <n> max <n> [step <n>] [start <n>] rail x,y to x,y [minus at x,y plus at x,y] [counter at x,y] }` | a handle on a horizontal or vertical rail (the first point is the minimum): press the rail or drag. `change` is sent once, at the release |
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
slider volume {
|
|
149
|
+
min 0
|
|
150
|
+
max 1
|
|
151
|
+
step 0.05
|
|
152
|
+
rail 200,150 to 500,150
|
|
153
|
+
minus at 110,150
|
|
154
|
+
plus at 590,150
|
|
155
|
+
counter at 350,250
|
|
156
|
+
}
|
|
157
|
+
raw scene {
|
|
158
|
+
layer "decor" {
|
|
159
|
+
group "Speaker" at 680,150 { layer "a" { circle 0 0 20 fill #cc3333 } }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
raw {
|
|
163
|
+
object "Speaker" {
|
|
164
|
+
scaleX = 1 + volume_value
|
|
165
|
+
scaleY = 1 + volume_value
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The − / + ends of a slider are its **keyboard** way in: a scene cannot read which object holds the focus,
|
|
171
|
+
so arrow keys on a focused handle cannot be written in FlatInk today. Objects: `<name>_Minus`,
|
|
172
|
+
`<name>_Plus`, `<name>_Rail`, `<name>_Handle`, `<name>_Value` (all in `meta[].objects`).
|
|
173
|
+
|
|
174
|
+
Each gesture emits `send "correct" / "incorrect" / "step" / "completed"` with a record payload naming the index
|
|
136
175
|
(`{ item = 2 }`), so a host reads which one without depending on what the theme drew. `desugar()` returns
|
|
137
176
|
the labels behind those indices, and the prompt, in `meta` — the host displays them, the gesture never
|
|
138
177
|
draws them:
|
package/dist/index.d.ts
CHANGED
|
@@ -104,8 +104,11 @@ type GestureMeta = {
|
|
|
104
104
|
targets: string[];
|
|
105
105
|
/** Every object id it emitted, prefixed — what a skin binds to, without having to guess the scheme. */
|
|
106
106
|
objects: string[];
|
|
107
|
-
/** The variable that turns 1 when this block is finished. */
|
|
107
|
+
/** The variable that turns 1 when this block is finished. Empty for a `control`. */
|
|
108
108
|
doneVar: string;
|
|
109
|
+
/** `true` for an INPUT (a stepper, a slider): it writes a value, it has no end, and the document's
|
|
110
|
+
* `completed` does not wait for it. Its value is the variable `<name>_value`. */
|
|
111
|
+
control?: boolean;
|
|
109
112
|
/** `true` when the block asked for `shuffle`: its elements do not stand where the source put them. */
|
|
110
113
|
shuffle?: boolean;
|
|
111
114
|
};
|
package/dist/index.js
CHANGED
|
@@ -82,7 +82,8 @@ function shuffler(p, spots) {
|
|
|
82
82
|
y: (i) => `${sy}[${i}]`
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
-
var
|
|
85
|
+
var WORDS = "prompt|target|item|total|chip|step|min|max|start|minus|plus|counter|rail|handle";
|
|
86
|
+
var RUNON = new RegExp(`^(?:${WORDS})\\b.*\\s{2,}(?:${WORDS})\\b`);
|
|
86
87
|
function lines(kind, name, body) {
|
|
87
88
|
const out = body.split("\n").map((l) => l.replace(/\/\/.*$/, "").trim()).filter(Boolean);
|
|
88
89
|
for (const line of out) {
|
|
@@ -290,6 +291,199 @@ function expandSteps(name, body, theme, ctx) {
|
|
|
290
291
|
meta: { keyword: "steps", name, prompt, items: steps.map((s) => s.label), targets: [], objects: steps.map((_s, i) => `${p}S${i}`), doneVar, ...sh ? { shuffle: true } : {} }
|
|
291
292
|
};
|
|
292
293
|
}
|
|
294
|
+
var NUMBER = "(-?\\d+(?:\\.\\d+)?)";
|
|
295
|
+
function range(kind, name, got) {
|
|
296
|
+
if (got.min === void 0 || got.max === void 0) throw new Error(`${kind} "${name}": it needs its bounds \u2014 \`min <n>\` and \`max <n>\`, one per line`);
|
|
297
|
+
if (Number(got.min) >= Number(got.max)) throw new Error(`${kind} "${name}": min (${got.min}) must be below max (${got.max})`);
|
|
298
|
+
const step = got.step ?? "1";
|
|
299
|
+
if (Number(step) <= 0) throw new Error(`${kind} "${name}": step must be above 0`);
|
|
300
|
+
const start = got.start ?? got.min;
|
|
301
|
+
if (Number(start) < Number(got.min) || Number(start) > Number(got.max)) throw new Error(`${kind} "${name}": start (${start}) is outside ${got.min}..${got.max}`);
|
|
302
|
+
const places = Math.max(0, ...[got.min, got.max, step, start].map((v) => (v.split(".")[1] ?? "").length));
|
|
303
|
+
return { min: got.min, max: got.max, step, start, places, k: 10 ** places };
|
|
304
|
+
}
|
|
305
|
+
function stepping(p, b, v, r) {
|
|
306
|
+
const sum = r.places ? `round((${v} + d * ${r.step}) * ${r.k}) / ${r.k}` : `${v} + d * ${r.step}`;
|
|
307
|
+
return {
|
|
308
|
+
vars: [`var ${p}to = 0`, `var ${p}held = 0`, `var ${p}next = 0`, `var ${p}byPress = 0`, `var ${p}upAt = 0`],
|
|
309
|
+
fn: [
|
|
310
|
+
`fn ${p}stepBy(d) {`,
|
|
311
|
+
` ${p}to = clamp(${sum}, ${r.min}, ${r.max})`,
|
|
312
|
+
` if ${p}to != ${v} {`,
|
|
313
|
+
` ${v} = ${p}to`,
|
|
314
|
+
` send "change", { block = ${b}, value = ${v} }`,
|
|
315
|
+
" }",
|
|
316
|
+
"}",
|
|
317
|
+
""
|
|
318
|
+
],
|
|
319
|
+
button: (id, d, order) => [
|
|
320
|
+
`object "${id}" {`,
|
|
321
|
+
` focusable order ${order}`,
|
|
322
|
+
// The mouse: a step at the press, then the repeat. The click that follows the release is that same
|
|
323
|
+
// press — only a click with no press before it (the keyboard) steps.
|
|
324
|
+
" when pressed {",
|
|
325
|
+
` ${p}stepBy(${d})`,
|
|
326
|
+
` ${p}held = ${d}`,
|
|
327
|
+
` ${p}next = clock + 0.4`,
|
|
328
|
+
` ${p}byPress = 1`,
|
|
329
|
+
" }",
|
|
330
|
+
" when released {",
|
|
331
|
+
` ${p}held = 0`,
|
|
332
|
+
` ${p}upAt = clock`,
|
|
333
|
+
" }",
|
|
334
|
+
" when clicked {",
|
|
335
|
+
` if ${p}byPress == 1 {`,
|
|
336
|
+
` ${p}byPress = 0`,
|
|
337
|
+
" } else {",
|
|
338
|
+
` ${p}stepBy(${d})`,
|
|
339
|
+
" }",
|
|
340
|
+
" }",
|
|
341
|
+
// At its bound a button is dimmed: which way is still open is STATE, as the live step of a sequence is.
|
|
342
|
+
` opacity = ${d < 0 ? `${v} <= ${r.min}` : `${v} >= ${r.max}`} ? 0.45 : 1`,
|
|
343
|
+
"}",
|
|
344
|
+
""
|
|
345
|
+
],
|
|
346
|
+
tick: [
|
|
347
|
+
` if ${p}held != 0 && clock >= ${p}next {`,
|
|
348
|
+
` ${p}stepBy(${p}held)`,
|
|
349
|
+
` ${p}next = clock + 0.12`,
|
|
350
|
+
" }",
|
|
351
|
+
// A press released OUTSIDE the button gets no click: forget it, or the next key press would be eaten.
|
|
352
|
+
` if ${p}held == 0 && ${p}byPress == 1 && clock > ${p}upAt + 0.05 {`,
|
|
353
|
+
` ${p}byPress = 0`,
|
|
354
|
+
" }"
|
|
355
|
+
]
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function readout(p, v, r, at, theme) {
|
|
359
|
+
const size = theme.size("counter") ?? { w: 180, h: 56 };
|
|
360
|
+
const art = (theme.draw("counter", "{}", size) ?? []).map((l) => l.startsWith("text ") ? `${l} bind "${v}"${r.places ? ` decimals ${r.places}` : ""}` : l);
|
|
361
|
+
return container(`${p}Value`, at.x, at.y, art);
|
|
362
|
+
}
|
|
363
|
+
var AT = "(-?[\\d.]+),(-?[\\d.]+)";
|
|
364
|
+
function expandStepper(name, body, theme, ctx) {
|
|
365
|
+
const { prefix: p, index: b } = ctx;
|
|
366
|
+
const got = {};
|
|
367
|
+
let prompt = "";
|
|
368
|
+
let minus, plus, counter;
|
|
369
|
+
for (const line of lines("stepper", name, body)) {
|
|
370
|
+
let x;
|
|
371
|
+
if (x = line.match(/^prompt\s+"(.*)"$/)) prompt = x[1];
|
|
372
|
+
else if (x = line.match(new RegExp(`^(min|max|step|start)\\s+${NUMBER}$`))) got[x[1]] = x[2];
|
|
373
|
+
else if (x = line.match(new RegExp(`^(minus|plus|counter)\\s+at\\s+${AT}$`))) {
|
|
374
|
+
const at = { x: x[2], y: x[3] };
|
|
375
|
+
if (x[1] === "minus") minus = at;
|
|
376
|
+
else if (x[1] === "plus") plus = at;
|
|
377
|
+
else counter = at;
|
|
378
|
+
} else throw new Error(`stepper "${name}": unrecognised line: ${line}`);
|
|
379
|
+
}
|
|
380
|
+
const r = range("stepper", name, got);
|
|
381
|
+
if (!minus || !plus) throw new Error(`stepper "${name}": it needs its two buttons \u2014 \`minus at x,y\` and \`plus at x,y\``);
|
|
382
|
+
const v = `${p}value`;
|
|
383
|
+
const st = stepping(p, b, v, r);
|
|
384
|
+
const vars = [`// ${p}\u2014 stepper "${q(name)}"`, ...prompt ? [`// prompt: ${q(prompt)}`] : [], `var ${v} = ${r.start}`, ...st.vars];
|
|
385
|
+
const layers = [
|
|
386
|
+
` layer "${p}stepper" {`,
|
|
387
|
+
...container(`${p}Minus`, minus.x, minus.y, theme.draw("chip", "\u2212"), theme.size("chip")),
|
|
388
|
+
...container(`${p}Plus`, plus.x, plus.y, theme.draw("chip", "+"), theme.size("chip")),
|
|
389
|
+
...counter ? readout(p, v, r, counter, theme) : [],
|
|
390
|
+
" }"
|
|
391
|
+
];
|
|
392
|
+
const behavior = [...st.fn, ...st.button(`${p}Minus`, -1, focusBase(b) + 1), ...st.button(`${p}Plus`, 1, focusBase(b) + 2), "every frame {", ...st.tick, "}", ""];
|
|
393
|
+
return {
|
|
394
|
+
vars,
|
|
395
|
+
layers,
|
|
396
|
+
behavior,
|
|
397
|
+
meta: { keyword: "stepper", name, prompt, items: [], targets: [], objects: [`${p}Minus`, `${p}Plus`, ...counter ? [`${p}Value`] : []], doneVar: "", control: true }
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function expandSlider(name, body, theme, ctx) {
|
|
401
|
+
const { prefix: p, index: b } = ctx;
|
|
402
|
+
const got = {};
|
|
403
|
+
let prompt = "";
|
|
404
|
+
let rail;
|
|
405
|
+
let minus, plus, counter;
|
|
406
|
+
for (const line of lines("slider", name, body)) {
|
|
407
|
+
let x;
|
|
408
|
+
if (x = line.match(/^prompt\s+"(.*)"$/)) prompt = x[1];
|
|
409
|
+
else if (x = line.match(new RegExp(`^(min|max|step|start)\\s+${NUMBER}$`))) got[x[1]] = x[2];
|
|
410
|
+
else if (x = line.match(new RegExp(`^rail\\s+${AT}\\s+to\\s+${AT}$`))) rail = { x0: Number(x[1]), y0: Number(x[2]), x1: Number(x[3]), y1: Number(x[4]) };
|
|
411
|
+
else if (x = line.match(new RegExp(`^(minus|plus|counter)\\s+at\\s+${AT}$`))) {
|
|
412
|
+
const at = { x: x[2], y: x[3] };
|
|
413
|
+
if (x[1] === "minus") minus = at;
|
|
414
|
+
else if (x[1] === "plus") plus = at;
|
|
415
|
+
else counter = at;
|
|
416
|
+
} else throw new Error(`slider "${name}": unrecognised line: ${line}`);
|
|
417
|
+
}
|
|
418
|
+
const r = range("slider", name, got);
|
|
419
|
+
if (!rail) throw new Error(`slider "${name}": it needs its rail \u2014 \`rail x,y to x,y\` (the first point is the minimum)`);
|
|
420
|
+
const horizontal = rail.y0 === rail.y1 && rail.x0 !== rail.x1;
|
|
421
|
+
if (!horizontal && !(rail.x0 === rail.x1 && rail.y0 !== rail.y1)) throw new Error(`slider "${name}": the rail is horizontal or vertical \u2014 \`rail 160,170 to 360,170\` or \`rail 100,300 to 100,100\``);
|
|
422
|
+
if (!!minus !== !!plus) throw new Error(`slider "${name}": the two ends go together \u2014 \`minus at x,y\` and \`plus at x,y\``);
|
|
423
|
+
const v = `${p}value`;
|
|
424
|
+
const st = minus && plus ? stepping(p, b, v, r) : null;
|
|
425
|
+
const [a0, a1] = horizontal ? [rail.x0, rail.x1] : [rail.y0, rail.y1];
|
|
426
|
+
const len = Math.abs(a1 - a0);
|
|
427
|
+
const vars = [`// ${p}\u2014 slider "${q(name)}"`, ...prompt ? [`// prompt: ${q(prompt)}`] : [], `var ${v} = ${r.start}`, `var ${p}grab = 0`, `var ${p}from = 0`, ...st ? st.vars : []];
|
|
428
|
+
const band = horizontal ? { w: len + 48, h: 56 } : { w: 56, h: len + 48 };
|
|
429
|
+
const railSize = horizontal ? { w: len + 24, h: 20 } : { w: 20, h: len + 24 };
|
|
430
|
+
const layers = [
|
|
431
|
+
` layer "${p}slider" {`,
|
|
432
|
+
...container(`${p}Rail`, String((rail.x0 + rail.x1) / 2), String((rail.y0 + rail.y1) / 2), theme.draw("target", "", railSize), band),
|
|
433
|
+
` group "${p}Handle" at ${rail.x0},${rail.y0} pivot 0,0 nohit {`,
|
|
434
|
+
' layer "art" {',
|
|
435
|
+
...theme.draw("item", "", { w: 44, h: 44 }).map((l) => ` ${l}`),
|
|
436
|
+
" }",
|
|
437
|
+
" }",
|
|
438
|
+
...minus && plus ? [...container(`${p}Minus`, minus.x, minus.y, theme.draw("chip", "\u2212"), theme.size("chip")), ...container(`${p}Plus`, plus.x, plus.y, theme.draw("chip", "+"), theme.size("chip"))] : [],
|
|
439
|
+
...counter ? readout(p, v, r, counter, theme) : [],
|
|
440
|
+
" }"
|
|
441
|
+
];
|
|
442
|
+
const raw = `${r.min} + round((a - ${a0}) / (${a1} - ${a0}) * (${r.max} - ${r.min}) / ${r.step}) * ${r.step}`;
|
|
443
|
+
const axis = horizontal ? "mouse.x" : "mouse.y";
|
|
444
|
+
const share = `(${v} - ${r.min}) / (${r.max} - ${r.min})`;
|
|
445
|
+
const behavior = [
|
|
446
|
+
`fn ${p}setAt(a) {`,
|
|
447
|
+
// `a`: the pointer along the rail's axis
|
|
448
|
+
` ${v} = clamp(${r.places ? `round((${raw}) * ${r.k}) / ${r.k}` : raw}, ${r.min}, ${r.max})`,
|
|
449
|
+
"}",
|
|
450
|
+
"",
|
|
451
|
+
`object "${p}Rail" {`,
|
|
452
|
+
" when pressed {",
|
|
453
|
+
` ${p}grab = 1`,
|
|
454
|
+
` ${p}from = ${v}`,
|
|
455
|
+
` ${p}setAt(${axis})`,
|
|
456
|
+
" }",
|
|
457
|
+
" when dragged {",
|
|
458
|
+
` if ${p}grab == 1 {`,
|
|
459
|
+
` ${p}setAt(${axis})`,
|
|
460
|
+
" }",
|
|
461
|
+
" }",
|
|
462
|
+
" when released {",
|
|
463
|
+
` ${p}grab = 0`,
|
|
464
|
+
` if ${v} != ${p}from {`,
|
|
465
|
+
` send "change", { block = ${b}, value = ${v} }`,
|
|
466
|
+
" }",
|
|
467
|
+
" }",
|
|
468
|
+
"}",
|
|
469
|
+
"",
|
|
470
|
+
// The handle's place always comes from the value. Held, it grows a little: that it is held is STATE.
|
|
471
|
+
`object "${p}Handle" {`,
|
|
472
|
+
` x = ${rail.x0} + (${rail.x1} - ${rail.x0}) * ${share}`,
|
|
473
|
+
` y = ${rail.y0} + (${rail.y1} - ${rail.y0}) * ${share}`,
|
|
474
|
+
` scaleX = ${p}grab == 1 ? 1.15 : 1`,
|
|
475
|
+
` scaleY = ${p}grab == 1 ? 1.15 : 1`,
|
|
476
|
+
"}",
|
|
477
|
+
"",
|
|
478
|
+
...st ? [...st.fn, ...st.button(`${p}Minus`, -1, focusBase(b) + 1), ...st.button(`${p}Plus`, 1, focusBase(b) + 2), "every frame {", ...st.tick, "}", ""] : []
|
|
479
|
+
];
|
|
480
|
+
return {
|
|
481
|
+
vars,
|
|
482
|
+
layers,
|
|
483
|
+
behavior,
|
|
484
|
+
meta: { keyword: "slider", name, prompt, items: [], targets: [], objects: [`${p}Rail`, `${p}Handle`, ...minus && plus ? [`${p}Minus`, `${p}Plus`] : [], ...counter ? [`${p}Value`] : []], doneVar: "", control: true }
|
|
485
|
+
};
|
|
486
|
+
}
|
|
293
487
|
var footprint = (theme, role) => {
|
|
294
488
|
const { w, h } = theme.size(role);
|
|
295
489
|
return `${w}x${h}`;
|
|
@@ -311,6 +505,16 @@ function gestures(opts = {}) {
|
|
|
311
505
|
keyword: "steps",
|
|
312
506
|
summary: `steps <name> { prompt "\u2026" [shuffle] step "\u2026" at x,y } \u2014 a gated sequence; a step tapped ahead of its turn sends "incorrect". Footprint: card ${footprint(theme, "card")}`,
|
|
313
507
|
expand: (name, body, _doc, ctx) => expandSteps(name, body, theme, ctx)
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
keyword: "stepper",
|
|
511
|
+
summary: `stepper <name> { prompt "\u2026" min <n> max <n> [step <n>] [start <n>] minus at x,y plus at x,y [counter at x,y] } \u2014 a CONTROL (not a task): \u2212 / + buttons writing <name>_value, repeating while held; sends "change". Footprint: chip ${footprint(theme, "chip")}`,
|
|
512
|
+
expand: (name, body, _doc, ctx) => expandStepper(name, body, theme, ctx)
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
keyword: "slider",
|
|
516
|
+
summary: 'slider <name> { prompt "\u2026" min <n> max <n> [step <n>] [start <n>] rail x,y to x,y [minus at x,y plus at x,y] [counter at x,y] } \u2014 a CONTROL: a handle on a horizontal or vertical rail (first point = min) writing <name>_value; press the rail or drag; sends "change" at the release. The \u2212 / + ends are the keyboard way in. Footprint: handle 44x44; the rail takes touches in a band 56 wide',
|
|
517
|
+
expand: (name, body, _doc, ctx) => expandSlider(name, body, theme, ctx)
|
|
314
518
|
}
|
|
315
519
|
];
|
|
316
520
|
}
|
|
@@ -422,7 +626,7 @@ function findBlocks(src, gestures2) {
|
|
|
422
626
|
}
|
|
423
627
|
function assemble(blocks, under, over, doc) {
|
|
424
628
|
const out = [`size ${doc.width} ${doc.height}`, `timeline ${doc.fps} ${doc.durationFrames}`];
|
|
425
|
-
const done = blocks.map((b) => b.expansion.meta.doneVar);
|
|
629
|
+
const done = blocks.filter((b) => !b.expansion.meta.control).map((b) => b.expansion.meta.doneVar);
|
|
426
630
|
if (done.length) out.push("var allDone = 0");
|
|
427
631
|
for (const b of blocks) out.push(...b.expansion.vars);
|
|
428
632
|
out.push("", "scene {");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/document.ts","../src/ident.ts","../src/theme.ts","../src/gestures.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// document.ts — the document a sugar block expands into.\n//\n// Two things a sugar MUST know and an author MUST be able to read: the canvas the gesture is laid out\n// for, and the timeline it rides. Both were literals buried inside an expander, which cost twice:\n// • the expansion never emitted `size` at all, so it fell back on the compiler's 800x600 default\n// while the board was painted for 760x620 -- everything past x=760 was clipped in silence, on the\n// whole corpus, for months (nothing says so: a missing `size` still compiles);\n// • prompting a model for the right coordinates meant reading the generator's source.\n// Exposed here, overridable per call, and stamped onto every expansion by `ensureHeader`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The canvas + timeline a sugar block expands into. */\nexport type DocumentSpec = {\n width: number\n height: number\n fps: number\n /** Timeline length. Long enough that `time` does not wrap mid-activity (the compiler warns under 120). */\n durationFrames: number\n}\n\n/** The board the gestures are laid out for. Override per call when a host uses another canvas. */\nexport const DEFAULT_DOCUMENT: DocumentSpec = { width: 760, height: 620, fps: 24, durationFrames: 120 }\n\n/** `true` if the source already declares its canvas — `size` must be the FIRST statement of a program. */\nexport const hasSizeHeader = (src: string): boolean => /^[ \\t]*size\\s+-?[\\d.]+\\s+-?[\\d.]+/m.test(src)\n\n/** `true` if the source already declares a root timeline. */\nexport const hasTimelineHeader = (src: string): boolean => /^[ \\t]*timeline\\s+\\d+/m.test(src)\n\n/**\n * Prepend whatever header the expansion is missing. `size` is REQUIRED by the format and silently\n * defaulted by the compiler when absent, so emitting it is not a nicety: it is the difference between\n * the document the author designed and the one that gets drawn.\n */\nexport function ensureHeader(flatink: string, doc: DocumentSpec = DEFAULT_DOCUMENT): string {\n const head: string[] = []\n if (!hasSizeHeader(flatink)) head.push(`size ${doc.width} ${doc.height}`)\n if (!hasTimelineHeader(flatink)) head.push(`timeline ${doc.fps} ${doc.durationFrames}`)\n return head.length ? `${head.join('\\n')}\\n${flatink}` : flatink\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// ident.ts — a human label -> a FlatInk identifier.\n//\n// A sugar lets an author name things in their own language (\"chene\", \"Vegetaux\", \"21 janvier 1793\"),\n// and those names become identifiers (`var`, `object`). FlatInk identifiers are `[A-Za-z0-9_]`, so an\n// accent does not error -- the lexer stops at it and reads the tail. `chene` written with its accent\n// became `var <accented>X`, read as `neX`: two variables where the author wrote one, and no diagnostic.\n// Transliterate here, once, rather than in every gesture.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A FlatInk identifier derived from a human label: accents folded to ASCII, anything else to `_`, and\n * a leading digit prefixed (an identifier may not start with one). Stable and idempotent, so the same\n * label always yields the same name — the sugar's variables and the author's references must agree.\n */\nexport function ident(label: string): string {\n const ascii = label.normalize('NFD').replace(/\\p{Diacritic}/gu, '').replace(/[^A-Za-z0-9_]/g, '_')\n return /^[0-9]/.test(ascii) ? `n${ascii}` : ascii\n}\n\n/** `true` if the label survives `ident` unchanged — i.e. it is already a usable identifier. */\nexport const isIdent = (label: string): boolean => ident(label) === label && label.length > 0\n","// ─────────────────────────────────────────────────────────────────────────────\n// theme.ts — where appearance lives, so that gestures do not carry any.\n//\n// A gesture emits STRUCTURE and BEHAVIOR: named groups at the coordinates the author gave, their drop\n// boxes, their state, their handlers. It must never decide what any of it LOOKS like — a sugar that\n// ships a look hands every activity built on it the same face, and that has already happened once.\n//\n// So appearance is a parameter. `BLANK` draws nothing at all, which is what makes the claim testable\n// rather than aspirational: expand a gesture with `BLANK` and the output must not contain one colour,\n// one font, or one stroke. `GREYBOX` is a deliberately plain default so a freshly written block is\n// playable immediately — it is provisional by design, and swapping it is a single argument.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** What a drawn thing is FOR. Four roles cover every gesture; a theme need not distinguish more. */\nexport type Role =\n | 'item' // something the learner moves\n | 'target' // where an item belongs\n | 'card' // a panel the learner reads and taps\n | 'chip' // a small tappable value\n | 'counter' // a running value the learner reads (`compose … counter at x,y`); its text shows `{}`\n\nexport type Theme = {\n readonly name: string\n /** Footprint of a role. The gesture uses it for drop boxes; the theme must draw within it. */\n size(role: Role): { w: number; h: number }\n /** Drawing statements for a role, in LOCAL coordinates centred on 0,0. May be empty. `size`, when given,\n * is the footprint the author asked for (`target … size w,h`); a theme draws within it. */\n draw(role: Role, label: string, size?: { w: number; h: number }): string[]\n}\n\n/** Every role, in a stable order — for a reference card, or to sweep a theme. */\nexport const ROLES: Role[] = ['item', 'target', 'card', 'chip', 'counter']\n\nconst SIZES: Record<Role, { w: number; h: number }> = {\n item: { w: 92, h: 92 },\n target: { w: 208, h: 118 },\n card: { w: 180, h: 140 },\n chip: { w: 96, h: 96 },\n counter: { w: 180, h: 56 },\n}\n\n/**\n * Draws nothing. The gesture still emits every group, every box and every handler, so an author (or a\n * skin) supplies the whole appearance — and the test suite uses it to prove the gestures themselves\n * carry no visual opinion.\n */\nexport const BLANK: Theme = {\n name: 'blank',\n size: (role) => SIZES[role],\n draw: () => [],\n}\n\n/** Rounded rectangle path, centred on the origin — the greybox's only shape vocabulary. */\nconst plate = (w: number, h: number, r: number): string => `rect ${-w / 2} ${-h / 2} ${w} ${h} ${r}`\n\n/**\n * A plain, provisional greybox: readable, anonymous, and meant to be replaced. It exists so `place x {\n * … }` is playable the moment it is written; it is not a design, and anything built for an audience\n * should pass its own theme (or redraw beside the gesture with `raw { … }`).\n */\nexport const GREYBOX: Theme = {\n name: 'greybox',\n size: (role) => SIZES[role],\n draw(role, label, size) {\n const { w, h } = size ?? SIZES[role]\n const text = (size: number, boxW: number, boxH: number, colour: string, y: number) =>\n `text \"${label.replace(/\"/g, \"'\")}\" at ${-boxW / 2},${y} font \"system-ui, sans-serif\" size ${size} align center line 1.2 color ${colour} box ${boxW} ${boxH} wrap`\n switch (role) {\n // Labels are 20 px at least (flatink/flatink#57, the stories' accessibility rule), inside the same\n // footprints: the item's disc grows within its 92 px to make room.\n case 'item':\n return [`circle 0 0 ${w / 2 - 4} fill #e8c46b`, `circle 0 0 ${w / 2 - 4} nofill stroke #b8923f 2`, text(20, w - 4, 50, '#4a3a1e', -13)]\n case 'target':\n return [`${plate(w - 8, h - 8, 14)} fill #2e3a4e`, `${plate(w - 8, h - 8, 14)} nofill stroke #5a6f8a 2 dash 8,6`, text(20, w - 28, 26, '#aeb6c4', -h / 2 + 10)]\n case 'card':\n return [`${plate(w, h, 16)} fill #2b3447`, `${plate(w, h, 16)} nofill stroke #44516a 2`, text(20, w - 20, h - 36, '#d7deea', -h / 2 + 22)]\n case 'chip':\n return [`circle 0 0 ${w / 2 - 8} fill #e8c46b`, `circle 0 0 ${w / 2 - 8} nofill stroke #b8923f 3`, text(22, w - 16, 28, '#4a3a1e', -13)]\n case 'counter':\n return [`${plate(w, h, 12)} fill #2b3447`, text(24, w - 16, 30, '#d7deea', -15)]\n }\n },\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// gestures.ts — the three primitives.\n//\n// Five named gestures came in (`tri`, `ordonner`, `placement`, `composer`, `etapes`). Read side by\n// side, the first three share ONE implementation: the only thing that differed between them was which\n// shape their targets were drawn as -- rectangles, squares or dotted discs. That is appearance, and\n// appearance is the theme's business, so they collapse into a single `place`.\n//\n// Each gesture emits state, structure and behavior. Not one colour, not one font: `BLANK` proves it.\n// Coordinates DO appear, and that is not an opinion -- they are the ones the author wrote in the block.\n//\n// A gesture returns PARTS, never a finished program: its `var` lines, its layers, its behavior. That is\n// what lets a document hold several of them -- a program has exactly one `scene`, so merging text after\n// the fact was always going to be surgery. Assembling parts is not.\n// ─────────────────────────────────────────────────────────────────────────────\nimport type { Expansion, Gesture, GestureContext } from './index'\nimport { ident } from './ident'\nimport { GREYBOX, ROLES, type Role, type Theme } from './theme'\n\n/** Everything a gesture needs beyond the author's text. */\nexport type GestureOptions = { theme?: Theme }\n\nconst q = (s: string) => s.replace(/\"/g, \"'\")\n\n/** Statements of a named group placed at `x,y`, drawn by the theme, optionally a drop box. */\nfunction container(name: string, x: string, y: string, art: string[], hitbox?: { w: number; h: number }): string[] {\n const box = hitbox ? ` hitbox ${hitbox.w} ${hitbox.h}` : ''\n return [` group \"${name}\" at ${x},${y} pivot 0,0${box} {`, ' layer \"art\" {', ...art.map((l) => ` ${l}`), ' }', ' }']\n}\n\n/**\n * `shuffle`: the spots the author declared, handed out in another order when the activity loads. The\n * spots live in two arrays (`<p>sx`, `<p>sy`) that a `when loaded` shuffles in place (Fisher-Yates over\n * `random()` — reproducible under a seed, see the player's `seed` option); element `i` then stands at\n * `<p>sx[i]`, `<p>sy[i]`. Fewer than two spots: nothing to shuffle, and nothing is emitted.\n */\nfunction shuffler(p: string, spots: { x: string; y: string }[]): { vars: string[]; load: string[]; x: (i: number) => string; y: (i: number) => string } | null {\n const n = spots.length\n if (n < 2) return null\n const sx = `${p}sx`, sy = `${p}sy`, j = `${p}sj`, t = `${p}st`, k = `${p}sk`\n return {\n vars: [`var ${sx} = [${spots.map((s) => s.x).join(', ')}]`, `var ${sy} = [${spots.map((s) => s.y).join(', ')}]`, `var ${j} = 0`, `var ${t} = 0`],\n load: [\n ` repeat ${k} from 0 to ${n - 2} {`,\n ` ${j} = ${k} + floor(random() * (${n} - ${k}))`,\n ` ${t} = ${sx}[${k}]`, ` ${sx}[${k}] = ${sx}[${j}]`, ` ${sx}[${j}] = ${t}`,\n ` ${t} = ${sy}[${k}]`, ` ${sy}[${k}] = ${sy}[${j}]`, ` ${sy}[${j}] = ${t}`,\n ' }',\n ],\n x: (i) => `${sx}[${i}]`,\n y: (i) => `${sy}[${i}]`,\n }\n}\n\n/** A line holding two statements: one keyword, then another after a run of spaces. */\nconst RUNON = /^(?:prompt|target|item|total|chip|step)\\b.*\\s{2,}(?:prompt|target|item|total|chip|step)\\b/\n\n/**\n * Split a block body into its non-empty, comment-free lines — ONE STATEMENT PER LINE, like FlatInk\n * itself. A body written inline is a run-on, and it deserves its own message: the grammar sketch in\n * `summary` shows the shape on one line, so that is the natural thing to copy.\n */\nfunction lines(kind: string, name: string, body: string): string[] {\n const out = body.split('\\n').map((l) => l.replace(/\\/\\/.*$/, '').trim()).filter(Boolean)\n for (const line of out) {\n if (!RUNON.test(line)) continue\n const laid = line.split(/\\s{2,}/).filter(Boolean).map((st) => ` ${st}`).join('\\n')\n throw new Error(`${kind} \"${name}\": two statements on one line — a block takes ONE per line, like FlatInk itself:\\n ${kind} ${name} {\\n${laid}\\n }`)\n }\n return out\n}\n\n// ── place ────────────────────────────────────────────────────────────────────\n// The drop gesture: items belong on targets. Several items may share a target (a sort) or each may have\n// its own (an ordering, a labelled diagram) -- that falls out of the data, it is not a separate gesture.\n\ntype PlaceModel = {\n prompt: string\n shuffle: boolean\n /** `targets size w,h`: the footprint of every target of the block that does not give its own. */\n targetSize?: { w: number; h: number }\n targets: { id: string; label: string; x: string; y: string; size?: { w: number; h: number } }[]\n items: { id: string; label: string; target: string; x: string; y: string }[]\n}\n\nfunction parsePlace(name: string, body: string, p: string): PlaceModel {\n const m: PlaceModel = { prompt: '', shuffle: false, targets: [], items: [] }\n // An object is named after its label, and `ident` keeps letters and digits only: `-1` and `+1` fold to\n // the same name, `< 1`, `= 1` and `> 1` too. The first keeps the plain name (so nothing moves for\n // labels that never collided — a skin addresses these objects by name); the next ones take `_2`, `_3`…\n const taken = new Set<string>()\n const own = (base: string): string => {\n let id = base\n for (let k = 2; taken.has(id); k++) id = `${base}_${k}`\n taken.add(id)\n return id\n }\n const targetOf = new Map<string, string>() // target LABEL -> its object: an item names a target by label\n for (const line of lines('place', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) m.prompt = x[1]\n else if (line === 'shuffle') m.shuffle = true\n else if ((x = line.match(/^targets\\s+size\\s+(\\d+),(\\d+)$/))) m.targetSize = { w: Number(x[1]), h: Number(x[2]) }\n else if ((x = line.match(/^target\\s+(?:\"([^\"]+)\"|(\\S+))\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)(?:\\s+size\\s+(\\d+),(\\d+))?$/))) {\n const label = x[1] ?? x[2]\n if (targetOf.has(label)) throw new Error(`place \"${name}\": two targets are labelled \"${label}\" — an item pointing at it could mean either`)\n const id = own(`${p}T${ident(label)}`)\n targetOf.set(label, id)\n // The size of a target is the author's (flatink/flatink#56): five fixed-size ones could not stand in a\n // row on a 760 px scene.\n m.targets.push({ id, label, x: x[3], y: x[4], ...(x[5] ? { size: { w: Number(x[5]), h: Number(x[6]) } } : {}) })\n } else if (/^targets?\\b.*\\bsize\\b/.test(line)) {\n throw new Error(`place \"${name}\": a size is two whole numbers, width,height — \\`target T at x,y size 120,80\\`, or \\`targets size 120,80\\` for the whole block: ${line}`)\n } else if ((x = line.match(/^item\\s+(?:\"([^\"]+)\"|(\\S+))\\s*->\\s*(?:\"([^\"]+)\"|(\\S+))\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) {\n const label = x[1] ?? x[2]\n m.items.push({ id: own(`${p}I${ident(label)}`), label, target: x[3] ?? x[4], x: x[5], y: x[6] })\n } else throw new Error(`place \"${name}\": unrecognised line: ${line}`)\n }\n if (!m.targets.length) throw new Error(`place \"${name}\": no target — an item has nowhere to go`)\n if (!m.items.length) throw new Error(`place \"${name}\": no item — nothing for the learner to do`)\n // Resolved once every line is read: a target may be declared after the item that names it.\n for (const it of m.items) {\n // By label; failing that by the identifier the label folds to, as long as ONE target answers to it\n // (`Vegetaux` for a target written with its accents — what this lookup accepted before).\n const folded = m.targets.filter((t) => ident(t.label) === ident(it.target))\n const id = targetOf.get(it.target) ?? (folded.length === 1 ? folded[0].id : undefined)\n if (!id) throw new Error(`place \"${name}\": item \"${it.label}\" points at an unknown target`)\n it.target = id\n }\n return m\n}\n\n/** Each block owns a range of 1000 focus ranks, so Tab goes through the blocks in their order whatever their\n * kind — a sequence numbered its cards from 1 and a compose ranked nothing, which mixed them with the others. */\nconst focusBase = (block: number): number => block * 1000\n\nfunction expandPlace(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n const m = parsePlace(name, body, p)\n const n = m.items.length\n const vars = [`// ${p}— place \"${q(name)}\"`, ...(m.prompt ? [`// prompt: ${q(m.prompt)}`] : []), `var ${p}progress = 0`, `var ${doneVar} = 0`]\n for (const it of m.items) vars.push(`var ${it.id}X = ${it.x}`, `var ${it.id}Y = ${it.y}`, `var ${it.id}Placed = 0`)\n const sh = m.shuffle ? shuffler(p, m.items) : null\n if (sh) vars.push(...sh.vars)\n // The second way in, for a keyboard (and for a finger that would rather tap twice than drag): pick an\n // item, then pick a target. `<p>sel` = the picked item, 1-based; 0 = none.\n const sel = `${p}sel`\n vars.push(`var ${sel} = 0`)\n // Tab order: the items of this block, then its targets — and the blocks one after the other.\n const itemOrder = focusBase(b) + 1, targetOrder = focusBase(b) + 2\n /** What putting item `i` on target `t` does — the SAME outcome whether it was dragged or picked. */\n const put = (it: PlaceModel['items'][number], i: number, t: PlaceModel['targets'][number], ind: string): string[] => t.id === it.target\n ? [\n `${ind}if ${it.id}Placed < 0.5 {`,\n `${ind} ${it.id}Placed = 1`, `${ind} ${p}progress = ${p}progress + 1`,\n // Snap onto the target it was put on, reading the target's LIVE position -- so a skin may move\n // the target anywhere and the item still lands on it.\n `${ind} ${it.id}X = ${t.id}.x`, `${ind} ${it.id}Y = ${t.id}.y`,\n `${ind} if ${sel} == ${i + 1} {`, `${ind} ${sel} = 0`, `${ind} }`, // placed: no longer picked, however it got there\n `${ind} send \"correct\", { block = ${b}, item = ${i} }`,\n `${ind} if ${p}progress == ${n} {`, `${ind} ${doneVar} = 1`, `${ind} send \"part\", { block = ${b} }`, `${ind} }`,\n `${ind}}`,\n ]\n : [\n `${ind}${it.id}X = ${sh ? sh.x(i) : it.x}`, `${ind}${it.id}Y = ${sh ? sh.y(i) : it.y}`, // back where it started\n `${ind}send \"incorrect\", { block = ${b}, item = ${i} }`,\n ]\n\n const layers = [` layer \"${p}targets\" {`]\n for (const t of m.targets) {\n const size = t.size ?? m.targetSize ?? theme.size('target')\n layers.push(...container(t.id, t.x, t.y, theme.draw('target', t.label, size), size))\n }\n layers.push(' }', ` layer \"${p}items\" {`)\n for (const it of m.items) layers.push(...container(it.id, it.x, it.y, theme.draw('item', it.label)))\n layers.push(' }')\n\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, ...m.items.flatMap((it, i) => [` ${it.id}X = ${sh.x(i)}`, ` ${it.id}Y = ${sh.y(i)}`]), '}', '')\n m.items.forEach((it, i) => {\n behavior.push(`object \"${it.id}\" {`, ` focusable order ${itemOrder}`)\n behavior.push(` drag ${it.id}X, ${it.id}Y { enabled ${it.id}Placed == 0 }`)\n behavior.push(' when clicked {', ` if ${it.id}Placed < 0.5 {`, ` ${sel} = ${i + 1}`, ' }', ' }')\n // Its own target first, then the others — the order these handlers have always been declared in.\n for (const t of [...m.targets.filter((x) => x.id === it.target), ...m.targets.filter((x) => x.id !== it.target)]) behavior.push(` when dropped on ${t.id} at pointer {`, ...put(it, i, t, ' '), ' }')\n behavior.push(` x = ${it.id}X`, ` y = ${it.id}Y`)\n // Which item is picked is STATE, like the live step of a sequence: it stands out a little. A theme\n // that disagrees rebinds the scale itself.\n behavior.push(` scaleX = ${sel} == ${i + 1} ? 1.08 : 1`, ` scaleY = ${sel} == ${i + 1} ? 1.08 : 1`, '}', '')\n })\n for (const t of m.targets) {\n behavior.push(`object \"${t.id}\" {`, ` focusable order ${targetOrder}`, ' when clicked {')\n // Only an item still to place: one picked and then dragged home must not be sent back by a later tap.\n m.items.forEach((it, i) => behavior.push(` if ${sel} == ${i + 1} && ${it.id}Placed < 0.5 {`, ` ${sel} = 0`, ...put(it, i, t, ' '), ' }'))\n behavior.push(' }', '}', '')\n }\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'place', name, prompt: m.prompt, items: m.items.map((i) => i.label), targets: m.targets.map((t) => t.label), objects: [...m.targets, ...m.items].map((o) => o.id), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n// ── compose ──────────────────────────────────────────────────────────────────\n// Tap values until they add up to a target. Overshooting resets the total -- the whole point of the\n// exercise is that going over is a mistake you feel, not one you are prevented from making.\n\nfunction expandCompose(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n let target = 0, total = ''\n const chips: { value: string; label?: string; x: string; y: string }[] = []\n let prompt = ''\n let shuffle = false\n let unit = ''\n let counter: { x: string; y: string } | undefined\n // flatink/flatink#55: decimal values (money without counting in cents), a label or a unit on the chips, and\n // an opt-in `counter` showing where the learner stands.\n const NUM = '(\\\\d+(?:\\\\.\\\\d+)?)'\n for (const line of lines('compose', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if (line === 'shuffle') shuffle = true\n else if ((x = line.match(new RegExp(`^total\\\\s+${NUM}$`)))) { total = x[1]; target = Number(x[1]) }\n else if ((x = line.match(new RegExp(`^chip\\\\s+${NUM}(?:\\\\s+\"([^\"]*)\")?\\\\s+at\\\\s+(-?[\\\\d.]+),(-?[\\\\d.]+)$`)))) chips.push({ value: x[1], ...(x[2] !== undefined ? { label: x[2] } : {}), x: x[3], y: x[4] })\n else if ((x = line.match(/^unit\\s+\"(.*)\"$/))) unit = x[1]\n else if ((x = line.match(/^counter\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) counter = { x: x[1], y: x[2] }\n else throw new Error(`compose \"${name}\": unrecognised line: ${line}`)\n }\n // Decimals add up in floating point (0.1 + 0.2 is not 0.3): the running total is rounded to the places the\n // author wrote, and reaching / overshooting the total is decided with that rounding.\n const places = Math.max(0, ...[total, ...chips.map((c) => c.value)].map((v) => (v.split('.')[1] ?? '').length))\n const k = 10 ** places\n if (!target) throw new Error(`compose \"${name}\": no \\`total <n>\\` — there is nothing to reach`)\n if (!chips.length) throw new Error(`compose \"${name}\": no chip — nothing for the learner to tap`)\n\n const vars = [`// ${p}— compose \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${p}total = 0`, `var ${doneVar} = 0`]\n const layers = [` layer \"${p}chips\" {`]\n chips.forEach((c, i) => layers.push(...container(`${p}C${i}`, c.x, c.y, theme.draw('chip', c.label ?? (unit ? `${c.value} ${unit}` : c.value)), theme.size('chip'))))\n if (counter) {\n // The theme draws the counter; its text shows the running total where it writes `{}`. A theme that\n // predates the role draws nothing — the group is still there for a skin.\n const size = theme.size('counter') ?? { w: 180, h: 56 }\n const art = (theme.draw('counter', `{} / ${total}${unit ? ` ${unit}` : ''}`, size) ?? []).map((l) => (l.startsWith('text ') ? `${l} bind \"${p}total\"${places ? ` decimals ${places}` : ''}` : l))\n layers.push(...container(`${p}Total`, counter.x, counter.y, art))\n }\n layers.push(' }')\n\n const sh = shuffle ? shuffler(p, chips) : null\n if (sh) vars.push(...sh.vars)\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, '}', '')\n chips.forEach((c, i) => {\n behavior.push(`object \"${p}C${i}\" {`, ` focusable order ${focusBase(b) + 1}`, ...(sh ? [` x = ${sh.x(i)}`, ` y = ${sh.y(i)}`] : []), ' when clicked {', ` if ${doneVar} < 0.5 {`)\n const sum = places ? `round((${p}total + ${c.value}) * ${k}) / ${k}` : `${p}total + ${c.value}`\n behavior.push(` if ${sum} > ${total} {`)\n behavior.push(` ${p}total = 0`, ` send \"incorrect\", { block = ${b}, item = ${i} }`)\n behavior.push(' } else {')\n behavior.push(` ${p}total = ${sum}`, ` send \"correct\", { block = ${b}, item = ${i} }`)\n behavior.push(` if ${p}total == ${total} {`, ` ${doneVar} = 1`, ` send \"part\", { block = ${b} }`, ' }')\n behavior.push(' }', ' }', ' }', '}', '')\n })\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'compose', name, prompt, items: chips.map((c) => c.value), targets: [], objects: chips.map((_c, i) => `${p}C${i}`), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n// ── steps ────────────────────────────────────────────────────────────────────\n// A gated sequence: step i only responds when it is the current one. A step AHEAD of the current one is a\n// mistake the host may count (`incorrect`, like `place` and `compose` — flatink/flatink#58); nothing else\n// moves, so the sequence stays usable as an escape-room stage. A step already done stays silent.\n\nfunction expandSteps(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n let prompt = ''\n let shuffle = false\n const steps: { label: string; x: string; y: string }[] = []\n for (const line of lines('steps', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if (line === 'shuffle') shuffle = true\n else if ((x = line.match(/^step\\s+\"(.*)\"\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) steps.push({ label: x[1], x: x[2], y: x[3] })\n else throw new Error(`steps \"${name}\": unrecognised line: ${line}`)\n }\n if (!steps.length) throw new Error(`steps \"${name}\": no step — the sequence is empty`)\n\n const vars = [`// ${p}— steps \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${p}step = 0`, `var ${doneVar} = 0`]\n const layers = [` layer \"${p}steps\" {`]\n steps.forEach((s, i) => layers.push(...container(`${p}S${i}`, s.x, s.y, theme.draw('card', s.label), theme.size('card'))))\n layers.push(' }')\n\n // `shuffle` moves the CARDS; the order of the sequence stays the declared one.\n const sh = shuffle ? shuffler(p, steps) : null\n if (sh) vars.push(...sh.vars)\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, '}', '')\n steps.forEach((_s, i) => {\n // `order`: Tab walks the cards in the order of the SEQUENCE, wherever they stand (`shuffle` moves them).\n behavior.push(`object \"${p}S${i}\" {`, ` focusable order ${focusBase(b) + 1 + i}`, ...(sh ? [` x = ${sh.x(i)}`, ` y = ${sh.y(i)}`] : []), ' when clicked {', ` if ${p}step == ${i} {`, ` ${p}step = ${p}step + 1`, ` send \"step\", { block = ${b}, item = ${i} }`)\n if (i === steps.length - 1) behavior.push(` ${doneVar} = 1`, ` send \"part\", { block = ${b} }`)\n behavior.push(' }')\n if (i > 0) behavior.push(` if ${p}step < ${i} {`, ` send \"incorrect\", { block = ${b}, item = ${i} }`, ' }')\n behavior.push(' }')\n // The only thing the gesture says about looks: a step that is not current is dimmed, because\n // \"which one is live\" is STATE, not decoration. A theme that disagrees rebinds opacity itself.\n behavior.push(` opacity = ${p}step == ${i} ? 1 : 0.45`, '}', '')\n })\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'steps', name, prompt, items: steps.map((s) => s.label), targets: [], objects: steps.map((_s, i) => `${p}S${i}`), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n/** `208x118` — the footprint of a role under a theme, for a summary or a prompt. */\nconst footprint = (theme: Theme, role: Role): string => {\n const { w, h } = theme.size(role)\n return `${w}x${h}`\n}\n\n/**\n * Build the shipped gestures against a theme. `GREYBOX` unless the caller supplies one.\n *\n * Each `summary` carries the FOOTPRINTS of the roles it places, because the author writes positions and\n * two overlapping surfaces make a drop ambiguous. Measured on ten model-written activities: a prompt\n * giving only \"space them out\" produced overlapping hitboxes in 4 of 10; the same prompt with the\n * numbers, 0 of 10. The sizes were public all along — nothing said you had to go and read them.\n */\nexport function gestures(opts: GestureOptions = {}): Gesture[] {\n const theme = opts.theme ?? GREYBOX\n return [\n {\n keyword: 'place',\n summary: `place <name> { prompt \"…\" [shuffle] [targets size w,h] target <T> at x,y [size w,h] item <i> -> <T> at x,y } — drag items onto where they belong. Footprints: target ${footprint(theme, 'target')} unless sized, item ${footprint(theme, 'item')}; keep centres at least one footprint apart or the drop is ambiguous`,\n expand: (name, body, _doc, ctx) => expandPlace(name, body, theme, ctx),\n },\n {\n keyword: 'compose',\n summary: `compose <name> { prompt \"…\" [shuffle] [unit \"…\"] total <n> chip <v> [\"label\"] at x,y [counter at x,y] } — tap values until they add up (decimals allowed); overshooting resets; \\`counter\\` shows the running total. Footprint: chip ${footprint(theme, 'chip')}`,\n expand: (name, body, _doc, ctx) => expandCompose(name, body, theme, ctx),\n },\n {\n keyword: 'steps',\n summary: `steps <name> { prompt \"…\" [shuffle] step \"…\" at x,y } — a gated sequence; a step tapped ahead of its turn sends \"incorrect\". Footprint: card ${footprint(theme, 'card')}`,\n expand: (name, body, _doc, ctx) => expandSteps(name, body, theme, ctx),\n },\n ]\n}\n\n/**\n * The paste-ready reference for a model writing sugar: the grammar of every gesture, the footprints it\n * must respect, and the canvas it is laying out on. This is the artefact `summary` was being used as by\n * hand — assembled here so nobody has to know that the sizes live on the theme.\n */\nexport function sugarCard(opts: GestureOptions & { document?: { width: number; height: number } } = {}): string {\n const theme = opts.theme ?? GREYBOX\n const doc = opts.document ?? { width: 760, height: 620 }\n return [\n '# FlatInk sugar — a document is one or more blocks',\n '',\n `Canvas: ${doc.width}x${doc.height}, origin top-left. Coordinates are the CENTRE of each thing.`,\n 'Anything you do not describe is not drawn.',\n '',\n '## Blocks — ONE statement per line, like FlatInk itself',\n '',\n 'place <name> { compose <name> { steps <name> {',\n ' prompt \"…\" prompt \"…\" prompt \"…\"',\n ' target <T> at x,y total <n> step \"…\" at x,y',\n ' item <i> -> <T> at x,y chip <v> at x,y }',\n '} }',\n '',\n ...gestures({ theme }).map((g) => `- ${g.summary}`),\n '',\n 'Several blocks may share a document; give each a DISTINCT name. They are laid out in the order you',\n 'write them, each keeps its own state, and each emits `part` when its own portion is finished. The',\n 'document emits `completed` once, when every block is done.',\n '',\n '## Footprints — respect them or two things overlap and the drop becomes ambiguous',\n ...ROLES.map((role) => `- ${role}: ${footprint(theme, role)}`),\n '',\n `A row of targets fits about ${Math.floor(doc.width / (theme.size('target').w + 20))} across; a row of items about ${Math.floor(doc.width / (theme.size('item').w + 20))}.`,\n 'Leave a gap the size of the thing itself between two centres. Two blocks must not share the same area.',\n '',\n '## Escape hatch — three forms, and they are not interchangeable',\n 'The blocks draw NOTHING beyond the pieces above. Everything else goes through one of these:',\n '',\n 'raw scene under { layer \"bg\" { … } } <- BACKGROUND and decor. Drawn BEHIND everything.',\n 'raw scene { layer \"banner\" { … } } <- overlays drawn ON TOP: a title, a frame, a caption.',\n 'raw { var lives = 3 object \"X\" { … } } <- header and behavior: var, fn, asset, object blocks.',\n '',\n 'A `layer` written in a plain `raw { … }` lands OUTSIDE the scene and is a compile error — anything',\n 'that is drawn belongs in one of the two `raw scene` forms. Any of them may be written on one line.',\n ].join('\\n')\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @flatkit/sugarflat — declarative sugar for FlatInk.\n//\n// A compact block expands into INSPECTABLE `.flatink`. The expansion is the artefact of record: the\n// sugar is an authoring tool, never a runtime, and anything it writes can be written by hand instead.\n//\n// WHY IT LIVES IN THIS REPO. Not to put authoring opinions into the language — it is a separate\n// package, out of the lockstep group, and `@flatkit/compiler` never depends on it. It lives here so\n// the two move together: a grammar change breaks these tests in the same CI run. That is the whole\n// argument, and it was demonstrated rather than argued — the previous out-of-tree sugar never emitted\n// the format's REQUIRED `size` line, on its entire corpus, for months, unnoticed.\n//\n// THE LINE THIS PACKAGE DOES NOT CROSS: no visual opinion. A gesture emits state and behavior; it\n// never emits a coordinate, a colour or a font. The language already ships interaction opinions\n// (`drag`, `when dropped on … at pointer`, `hitbox`, `match`, `feedback`, `spring`) — what it has\n// never shipped is a look, and a sugar that shipped one would hand every activity the same face.\n// ─────────────────────────────────────────────────────────────────────────────\nimport { DEFAULT_DOCUMENT, ensureHeader, type DocumentSpec } from './document'\nimport { gestures } from './gestures'\nimport { ident } from './ident'\n\nexport { DEFAULT_DOCUMENT, ensureHeader, hasSizeHeader, hasTimelineHeader, type DocumentSpec } from './document'\nexport { ident, isIdent } from './ident'\nexport { gestures, sugarCard, type GestureOptions } from './gestures'\nexport { BLANK, GREYBOX, ROLES, type Role, type Theme } from './theme'\n\n/**\n * What a gesture parsed out of the author's block, beside the DSL it wrote.\n *\n * A host needs this. The expansion deliberately carries no text a host can read back — the prompt is the\n * learner's instruction and the payloads are indices (`send \"correct\", { item = 2 }`), so without the\n * labels there is nothing to display and nothing to map an index onto. Re-parsing the block by hand to\n * recover data the package already parsed is not an integration; it is a second parser waiting to drift.\n */\nexport type GestureMeta = {\n keyword: string\n /** The block's name — also the prefix on every name it emits. */\n name: string\n /** The learner's instruction, verbatim. Drawn by the host or a theme — never by the gesture. */\n prompt: string\n /** Labels in payload order: `{ block = b, item = 2 }` is `blocks[b].items[2]`. */\n items: string[]\n /** Target labels, in the order they were declared. Empty when the gesture has none. */\n targets: string[]\n /** Every object id it emitted, prefixed — what a skin binds to, without having to guess the scheme. */\n objects: string[]\n /** The variable that turns 1 when this block is finished. */\n doneVar: string\n /** `true` when the block asked for `shuffle`: its elements do not stand where the source put them. */\n shuffle?: boolean\n}\n\n/** Where a block sits in the document. Names are prefixed so two blocks never collide. */\nexport type GestureContext = {\n /** 0-based position, and the `block` field of every payload it sends. */\n index: number\n /** `<name>_` — put it in front of every var and every object id. */\n prefix: string\n /** The variable to set to 1 when the block is done; the document watches it. */\n doneVar: string\n}\n\n/** What a gesture returns: PARTS, never a finished program — a document has exactly one `scene`. */\nexport type Expansion = {\n /** `var` declarations. They go in the header, where `var` is legal. */\n vars: string[]\n /** Layer statements, indented for the inside of `scene { … }`. */\n layers: string[]\n /** `object` blocks and scene-wide handlers. */\n behavior: string[]\n meta: GestureMeta\n}\n\n/** A sugar block: the keyword that opens it, and how it expands. */\nexport type Gesture = {\n /** The opening keyword, e.g. `place`. Matched only at column 0, followed by a name and `{`. */\n keyword: string\n /** One line, for diagnostics and for the reference a model is prompted with. */\n summary: string\n /** `body` is the text between the braces; `name` the block's name. */\n expand: (name: string, body: string, doc: DocumentSpec, ctx: GestureContext) => Expansion\n}\n\nexport type DesugarOptions = {\n /** The canvas + timeline the expansion targets. */\n document?: DocumentSpec\n /** Gestures to recognise. Defaults to the registered set. */\n gestures?: Gesture[]\n}\n\nexport type DesugarResult = {\n /** The expanded (or passed-through) FlatInk. */\n flatink: string\n /** Which gesture matched — `raw` for an escape-only source, `null` for plain FlatInk. */\n kind: string | null\n expanded: boolean\n /** What each block understood, in document order: `{ block = 2 }` refers to `meta[2]`. Empty when\n * nothing was expanded, since there is no block to have understood anything from. */\n meta: GestureMeta[]\n}\n\n/** Raised when a source opens a block that looks like sugar but no gesture claims it. */\nexport class UnknownSugarError extends Error {\n constructor(readonly keyword: string, known: string[]) {\n super(\n `unknown sugar block \"${keyword}\" — no gesture claims it, so nothing was expanded. ` +\n `Known blocks: ${known.length ? known.join(', ') : '(none registered)'}. ` +\n `If this is meant to be plain FlatInk, it is not: \"${keyword}\" is not a FlatInk statement either.`,\n )\n this.name = 'UnknownSugarError'\n }\n}\n\n/** The gestures this package ships, drawn by the default theme. Each entry is a public promise. */\nexport const GESTURES: Gesture[] = gestures()\n\n/** A block opens at column 0: `<keyword> <name> {`. The name may be quoted (so it can hold spaces). */\nconst BLOCK_OPEN = /^([a-zA-Z][\\w-]*)[ \\t]+(?:\"([^\"]+)\"|([^\\s{\"]+))[ \\t]*\\{/m\n\n/**\n * The escape hatch, in three forms. A sugar that cannot be opted out of stops being scaffolding and\n * becomes a cage: the previous generation of this idea flattened every activity into the same shape\n * until the template was pulled apart. Anything a gesture writes must remain writable by hand, beside it.\n *\n * raw { … } verbatim at TOP LEVEL — header (`asset`, `var`, `fn`) and `object` blocks\n * raw scene { … } verbatim inside the generated scene, ON TOP of it — banners, overlays\n * raw scene under { … } verbatim inside the generated scene, UNDERNEATH it — backgrounds\n *\n * `under` is not a nicety. A gesture emits no appearance by design, so a background is the FIRST thing\n * any skin needs — and spliced on top, an opaque full-canvas rect hides the entire activity while\n * `checkProgram` reports a clean program. Ten activities shipped that way, read as broken assets.\n */\nconst RAW_OPEN = /^raw(?:[ \\t]+scene(?:[ \\t]+(under))?)?[ \\t]*\\{/gm\n\ntype RawParts = { top: string[]; over: string[]; under: string[]; rest: string }\n\n/**\n * Pull every `raw …` block out of `text`. Brace-counted rather than line-anchored, so the one-line form\n * works — the README and `sugarCard()` both write `raw { … }` on one line, and a regex demanding a\n * newline after `{` left the keyword to travel on to the compiler, which reported `unexpected statement\n * \"raw\"`: a sugar defect described in FlatInk's vocabulary, the very thing `UnknownSugarError` fixed.\n */\nfunction takeRaw(text: string): RawParts {\n const out: RawParts = { top: [], over: [], under: [], rest: '' }\n let cursor = 0\n RAW_OPEN.lastIndex = 0\n for (let m = RAW_OPEN.exec(text); m; m = RAW_OPEN.exec(text)) {\n const openBrace = m.index + m[0].length - 1\n const end = blockEnd(text, openBrace)\n if (end < 0) break // unbalanced: leave it alone, the compiler will say so\n const raw = text.slice(openBrace + 1, end)\n // One-line form (`raw { var x = 1 }`) keeps the spaces that hugged the braces; the multi-line form\n // keeps its own indentation, which is the author's.\n const body = raw.includes('\\n') ? raw.replace(/^\\r?\\n/, '').replace(/\\r?\\n[ \\t]*$/, '') : raw.trim()\n const bucket = m[0].includes('scene') ? (m[1] ? out.under : out.over) : out.top\n bucket.push(body)\n out.rest += text.slice(cursor, m.index)\n cursor = end + 1\n RAW_OPEN.lastIndex = cursor\n }\n out.rest += text.slice(cursor)\n return out\n}\n\n/** Index of the `}` closing the brace at `openBrace`, or -1 if unbalanced. */\nfunction blockEnd(text: string, openBrace: number): number {\n let depth = 1\n for (let i = openBrace + 1; i < text.length; i++) {\n if (text[i] === '{') depth++\n else if (text[i] === '}' && --depth === 0) return i\n }\n return -1\n}\n\nconst RAW_NOTE = '// -- raw (escape hatch: verbatim FlatInk) --'\n\n/** Raised when two blocks share a name — every name they emit would collide. */\nexport class DuplicateBlockError extends Error {\n constructor(name: string) {\n super(`two blocks are both named \"${name}\" — the name prefixes every variable and object a block emits, so they would overwrite each other. Give each block its own name.`)\n this.name = 'DuplicateBlockError'\n }\n}\n\n/** One gesture block found in the source, with its span so the text around it can be recovered. */\ntype Found = { gesture: Gesture; name: string; body: string; start: number; end: number }\n\n/** Every gesture block in `src`, in document order. */\nfunction findBlocks(src: string, gestures: Gesture[]): Found[] {\n const re = new RegExp(BLOCK_OPEN.source, 'gm')\n const found: Found[] = []\n for (let m = re.exec(src); m; m = re.exec(src)) {\n const gesture = gestures.find((g) => g.keyword === m![1])\n if (!gesture) {\n // A keyword that opens a block at column 0 and is neither sugar nor FlatInk is a block we do not\n // know — say so with the list, rather than downstream in another vocabulary.\n if (m[1] !== 'raw' && !FLATINK_BLOCKS.has(m[1])) throw new UnknownSugarError(m[1], gestures.map((g) => g.keyword))\n continue\n }\n const braceAt = m.index + m[0].length - 1\n const close = blockEnd(src, braceAt)\n if (close < 0) break\n found.push({ gesture, name: m[2] ?? m[3] ?? gesture.keyword, body: src.slice(braceAt + 1, close), start: m.index, end: close + 1 })\n re.lastIndex = close + 1\n }\n return found\n}\n\n/**\n * Assemble the blocks into ONE program.\n *\n * A document has exactly one `scene`, one header and one behavior half, so composing gestures is not a\n * matter of concatenating their output — it is a matter of never producing whole programs in the first\n * place. Each block contributes parts; this puts them in the three places the format has.\n *\n * Two things the assembly decides, and they are the reason \"several blocks\" is a design and not a loop:\n *\n * • **Names are prefixed by the block's name, always** — including when a document holds one block.\n * Prefixing only on collision would mean the same source compiles to different names depending on\n * whether a sibling exists, so a skin written against one block would break the day a second arrived.\n * Every id is in `meta[].objects`, so nothing has to be guessed.\n * • **`completed` belongs to the DOCUMENT.** Each block emits `part` with its own index when its\n * portion is finished; `completed` fires once, when every block is done. With a single block the two\n * coincide, which is what `completed` always meant.\n */\nfunction assemble(blocks: { expansion: Expansion }[], under: string[], over: string[], doc: DocumentSpec): string {\n const out: string[] = [`size ${doc.width} ${doc.height}`, `timeline ${doc.fps} ${doc.durationFrames}`]\n const done = blocks.map((b) => b.expansion.meta.doneVar)\n if (done.length) out.push('var allDone = 0')\n for (const b of blocks) out.push(...b.expansion.vars)\n\n out.push('', 'scene {')\n if (under.length) out.push(' // -- raw scene under (verbatim, drawn BEHIND every block) --', ...under)\n for (const b of blocks) out.push(...b.expansion.layers)\n if (over.length) out.push(' // -- raw scene (verbatim, drawn ON TOP of every block) --', ...over)\n out.push('}', '')\n\n for (const b of blocks) out.push(...b.expansion.behavior)\n // `completed` = the whole document. One block: it fires the moment that block does, which is what it\n // has always meant. Several: only once all of them have. The `allDone` guard matters -- an unguarded\n // `send` inside `every frame` would emit it again on every frame for the rest of the session.\n if (done.length) {\n out.push('every frame {', ' if allDone < 0.5 {', ` if ${done.join(' + ')} >= ${done.length} {`, ' allDone = 1', ' send \"completed\"', ' }', ' }', '}')\n }\n return out.join('\\n')\n}\n\n/** Block keywords that belong to FlatINK itself — a source opening with one of these is a program. */\nconst FLATINK_BLOCKS = new Set(['scene', 'object', 'symbol', 'layer', 'group', 'match', 'each', 'states', 'params', 'when', 'every', 'repeat', 'fn', 'at'])\n\n/**\n * Expand a sugar source into `.flatink`.\n *\n * Plain FlatInk passes through untouched. A block nobody claims RAISES rather than passing through: a\n * silent fall-through sent the author's block downstream as if it were FlatInk, to fail a hundred lines\n * later on messages that only talk about FlatInk.\n *\n * A document may hold SEVERAL blocks. They are laid out in the order they are written, each keeps its own\n * state under its own name, and each emits `part` when its portion is finished; `completed` fires once,\n * when every block is done.\n *\n * ```ts\n * import { desugar } from '@flatkit/sugarflat'\n * import { checkProgram } from '@flatkit/compiler'\n *\n * const { flatink, meta } = desugar(srcFromAnLLM)\n * const { ok, report } = checkProgram(flatink) // the expansion is the artefact of record\n * ```\n */\nexport function desugar(src: string, opts: DesugarOptions = {}): DesugarResult {\n const doc = opts.document ?? DEFAULT_DOCUMENT\n const gestures = opts.gestures ?? GESTURES\n\n const found = findBlocks(src, gestures)\n if (!found.length) {\n // Plain FlatInk, plus any `raw { … }` used on its own. `raw scene` has no generated scene to splice\n // into here, so it would silently vanish -- say so instead.\n const parts = takeRaw(src)\n if (parts.over.length || parts.under.length) {\n throw new Error('`raw scene { … }` needs a gesture block to splice into — this source has none. Write a plain `scene { … }` yourself.')\n }\n const out = [parts.rest.trim(), ...(parts.top.length ? [RAW_NOTE, ...parts.top] : [])].filter(Boolean).join('\\n')\n return { flatink: parts.top.length ? out : src, kind: parts.top.length ? 'raw' : null, expanded: parts.top.length > 0, meta: [] }\n }\n\n const seen = new Set<string>()\n for (const b of found) {\n const key = ident(b.name)\n if (seen.has(key)) throw new DuplicateBlockError(b.name)\n seen.add(key)\n }\n\n // Everything OUTSIDE the blocks travels with them: `raw` in its three forms, and any plain FlatInk the\n // author wrote beside them. Dropping it made the escape hatch a no-op -- decor was written, the\n // expansion compiled, `checkProgram` said ok, and nothing was there.\n const outside: RawParts = { top: [], over: [], under: [], rest: '' }\n const absorb = (text: string) => {\n const part = takeRaw(text)\n outside.top.push(...part.top)\n outside.over.push(...part.over)\n outside.under.push(...part.under)\n outside.rest += part.rest\n }\n let cursor = 0\n for (const b of found) { absorb(src.slice(cursor, b.start)); cursor = b.end }\n absorb(src.slice(cursor))\n\n const blocks = found.map((b, index) => {\n const prefix = `${ident(b.name)}_`\n return { expansion: b.gesture.expand(b.name, b.body, doc, { index, prefix, doneVar: `${prefix}done` }) }\n })\n\n // Top-level content goes in the HEADER position, wherever the author wrote it: `var` (like `asset`,\n // `use`, `def`) is a header declaration and a parse error after `scene`, while `object`/`fn` are legal\n // on either side. Moving an `object` up changes nothing -- behavior binds by name, not by position.\n const header = [outside.rest.trim(), ...(outside.top.length ? [RAW_NOTE, ...outside.top] : [])].filter(Boolean).join('\\n')\n const program = assemble(blocks, outside.under, outside.over, doc)\n return {\n flatink: ensureHeader(header ? `${header}\\n\\n${program}` : program, doc),\n kind: found.map((b) => b.gesture.keyword).join('+'),\n expanded: true,\n meta: blocks.map((b) => b.expansion.meta),\n }\n}\n"],"mappings":"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","names":["size","gestures"]}
|
|
1
|
+
{"version":3,"sources":["../src/document.ts","../src/ident.ts","../src/theme.ts","../src/gestures.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// document.ts — the document a sugar block expands into.\n//\n// Two things a sugar MUST know and an author MUST be able to read: the canvas the gesture is laid out\n// for, and the timeline it rides. Both were literals buried inside an expander, which cost twice:\n// • the expansion never emitted `size` at all, so it fell back on the compiler's 800x600 default\n// while the board was painted for 760x620 -- everything past x=760 was clipped in silence, on the\n// whole corpus, for months (nothing says so: a missing `size` still compiles);\n// • prompting a model for the right coordinates meant reading the generator's source.\n// Exposed here, overridable per call, and stamped onto every expansion by `ensureHeader`.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** The canvas + timeline a sugar block expands into. */\nexport type DocumentSpec = {\n width: number\n height: number\n fps: number\n /** Timeline length. Long enough that `time` does not wrap mid-activity (the compiler warns under 120). */\n durationFrames: number\n}\n\n/** The board the gestures are laid out for. Override per call when a host uses another canvas. */\nexport const DEFAULT_DOCUMENT: DocumentSpec = { width: 760, height: 620, fps: 24, durationFrames: 120 }\n\n/** `true` if the source already declares its canvas — `size` must be the FIRST statement of a program. */\nexport const hasSizeHeader = (src: string): boolean => /^[ \\t]*size\\s+-?[\\d.]+\\s+-?[\\d.]+/m.test(src)\n\n/** `true` if the source already declares a root timeline. */\nexport const hasTimelineHeader = (src: string): boolean => /^[ \\t]*timeline\\s+\\d+/m.test(src)\n\n/**\n * Prepend whatever header the expansion is missing. `size` is REQUIRED by the format and silently\n * defaulted by the compiler when absent, so emitting it is not a nicety: it is the difference between\n * the document the author designed and the one that gets drawn.\n */\nexport function ensureHeader(flatink: string, doc: DocumentSpec = DEFAULT_DOCUMENT): string {\n const head: string[] = []\n if (!hasSizeHeader(flatink)) head.push(`size ${doc.width} ${doc.height}`)\n if (!hasTimelineHeader(flatink)) head.push(`timeline ${doc.fps} ${doc.durationFrames}`)\n return head.length ? `${head.join('\\n')}\\n${flatink}` : flatink\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// ident.ts — a human label -> a FlatInk identifier.\n//\n// A sugar lets an author name things in their own language (\"chene\", \"Vegetaux\", \"21 janvier 1793\"),\n// and those names become identifiers (`var`, `object`). FlatInk identifiers are `[A-Za-z0-9_]`, so an\n// accent does not error -- the lexer stops at it and reads the tail. `chene` written with its accent\n// became `var <accented>X`, read as `neX`: two variables where the author wrote one, and no diagnostic.\n// Transliterate here, once, rather than in every gesture.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A FlatInk identifier derived from a human label: accents folded to ASCII, anything else to `_`, and\n * a leading digit prefixed (an identifier may not start with one). Stable and idempotent, so the same\n * label always yields the same name — the sugar's variables and the author's references must agree.\n */\nexport function ident(label: string): string {\n const ascii = label.normalize('NFD').replace(/\\p{Diacritic}/gu, '').replace(/[^A-Za-z0-9_]/g, '_')\n return /^[0-9]/.test(ascii) ? `n${ascii}` : ascii\n}\n\n/** `true` if the label survives `ident` unchanged — i.e. it is already a usable identifier. */\nexport const isIdent = (label: string): boolean => ident(label) === label && label.length > 0\n","// ─────────────────────────────────────────────────────────────────────────────\n// theme.ts — where appearance lives, so that gestures do not carry any.\n//\n// A gesture emits STRUCTURE and BEHAVIOR: named groups at the coordinates the author gave, their drop\n// boxes, their state, their handlers. It must never decide what any of it LOOKS like — a sugar that\n// ships a look hands every activity built on it the same face, and that has already happened once.\n//\n// So appearance is a parameter. `BLANK` draws nothing at all, which is what makes the claim testable\n// rather than aspirational: expand a gesture with `BLANK` and the output must not contain one colour,\n// one font, or one stroke. `GREYBOX` is a deliberately plain default so a freshly written block is\n// playable immediately — it is provisional by design, and swapping it is a single argument.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** What a drawn thing is FOR. Four roles cover every gesture; a theme need not distinguish more. */\nexport type Role =\n | 'item' // something the learner moves\n | 'target' // where an item belongs\n | 'card' // a panel the learner reads and taps\n | 'chip' // a small tappable value\n | 'counter' // a running value the learner reads (`compose … counter at x,y`); its text shows `{}`\n\nexport type Theme = {\n readonly name: string\n /** Footprint of a role. The gesture uses it for drop boxes; the theme must draw within it. */\n size(role: Role): { w: number; h: number }\n /** Drawing statements for a role, in LOCAL coordinates centred on 0,0. May be empty. `size`, when given,\n * is the footprint the author asked for (`target … size w,h`); a theme draws within it. */\n draw(role: Role, label: string, size?: { w: number; h: number }): string[]\n}\n\n/** Every role, in a stable order — for a reference card, or to sweep a theme. */\nexport const ROLES: Role[] = ['item', 'target', 'card', 'chip', 'counter']\n\nconst SIZES: Record<Role, { w: number; h: number }> = {\n item: { w: 92, h: 92 },\n target: { w: 208, h: 118 },\n card: { w: 180, h: 140 },\n chip: { w: 96, h: 96 },\n counter: { w: 180, h: 56 },\n}\n\n/**\n * Draws nothing. The gesture still emits every group, every box and every handler, so an author (or a\n * skin) supplies the whole appearance — and the test suite uses it to prove the gestures themselves\n * carry no visual opinion.\n */\nexport const BLANK: Theme = {\n name: 'blank',\n size: (role) => SIZES[role],\n draw: () => [],\n}\n\n/** Rounded rectangle path, centred on the origin — the greybox's only shape vocabulary. */\nconst plate = (w: number, h: number, r: number): string => `rect ${-w / 2} ${-h / 2} ${w} ${h} ${r}`\n\n/**\n * A plain, provisional greybox: readable, anonymous, and meant to be replaced. It exists so `place x {\n * … }` is playable the moment it is written; it is not a design, and anything built for an audience\n * should pass its own theme (or redraw beside the gesture with `raw { … }`).\n */\nexport const GREYBOX: Theme = {\n name: 'greybox',\n size: (role) => SIZES[role],\n draw(role, label, size) {\n const { w, h } = size ?? SIZES[role]\n const text = (size: number, boxW: number, boxH: number, colour: string, y: number) =>\n `text \"${label.replace(/\"/g, \"'\")}\" at ${-boxW / 2},${y} font \"system-ui, sans-serif\" size ${size} align center line 1.2 color ${colour} box ${boxW} ${boxH} wrap`\n switch (role) {\n // Labels are 20 px at least (flatink/flatink#57, the stories' accessibility rule), inside the same\n // footprints: the item's disc grows within its 92 px to make room.\n case 'item':\n return [`circle 0 0 ${w / 2 - 4} fill #e8c46b`, `circle 0 0 ${w / 2 - 4} nofill stroke #b8923f 2`, text(20, w - 4, 50, '#4a3a1e', -13)]\n case 'target':\n return [`${plate(w - 8, h - 8, 14)} fill #2e3a4e`, `${plate(w - 8, h - 8, 14)} nofill stroke #5a6f8a 2 dash 8,6`, text(20, w - 28, 26, '#aeb6c4', -h / 2 + 10)]\n case 'card':\n return [`${plate(w, h, 16)} fill #2b3447`, `${plate(w, h, 16)} nofill stroke #44516a 2`, text(20, w - 20, h - 36, '#d7deea', -h / 2 + 22)]\n case 'chip':\n return [`circle 0 0 ${w / 2 - 8} fill #e8c46b`, `circle 0 0 ${w / 2 - 8} nofill stroke #b8923f 3`, text(22, w - 16, 28, '#4a3a1e', -13)]\n case 'counter':\n return [`${plate(w, h, 12)} fill #2b3447`, text(24, w - 16, 30, '#d7deea', -15)]\n }\n },\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// gestures.ts — the three primitives.\n//\n// Five named gestures came in (`tri`, `ordonner`, `placement`, `composer`, `etapes`). Read side by\n// side, the first three share ONE implementation: the only thing that differed between them was which\n// shape their targets were drawn as -- rectangles, squares or dotted discs. That is appearance, and\n// appearance is the theme's business, so they collapse into a single `place`.\n//\n// Each gesture emits state, structure and behavior. Not one colour, not one font: `BLANK` proves it.\n// Coordinates DO appear, and that is not an opinion -- they are the ones the author wrote in the block.\n//\n// A gesture returns PARTS, never a finished program: its `var` lines, its layers, its behavior. That is\n// what lets a document hold several of them -- a program has exactly one `scene`, so merging text after\n// the fact was always going to be surgery. Assembling parts is not.\n// ─────────────────────────────────────────────────────────────────────────────\nimport type { Expansion, Gesture, GestureContext } from './index'\nimport { ident } from './ident'\nimport { GREYBOX, ROLES, type Role, type Theme } from './theme'\n\n/** Everything a gesture needs beyond the author's text. */\nexport type GestureOptions = { theme?: Theme }\n\nconst q = (s: string) => s.replace(/\"/g, \"'\")\n\n/** Statements of a named group placed at `x,y`, drawn by the theme, optionally a drop box. */\nfunction container(name: string, x: string, y: string, art: string[], hitbox?: { w: number; h: number }): string[] {\n const box = hitbox ? ` hitbox ${hitbox.w} ${hitbox.h}` : ''\n return [` group \"${name}\" at ${x},${y} pivot 0,0${box} {`, ' layer \"art\" {', ...art.map((l) => ` ${l}`), ' }', ' }']\n}\n\n/**\n * `shuffle`: the spots the author declared, handed out in another order when the activity loads. The\n * spots live in two arrays (`<p>sx`, `<p>sy`) that a `when loaded` shuffles in place (Fisher-Yates over\n * `random()` — reproducible under a seed, see the player's `seed` option); element `i` then stands at\n * `<p>sx[i]`, `<p>sy[i]`. Fewer than two spots: nothing to shuffle, and nothing is emitted.\n */\nfunction shuffler(p: string, spots: { x: string; y: string }[]): { vars: string[]; load: string[]; x: (i: number) => string; y: (i: number) => string } | null {\n const n = spots.length\n if (n < 2) return null\n const sx = `${p}sx`, sy = `${p}sy`, j = `${p}sj`, t = `${p}st`, k = `${p}sk`\n return {\n vars: [`var ${sx} = [${spots.map((s) => s.x).join(', ')}]`, `var ${sy} = [${spots.map((s) => s.y).join(', ')}]`, `var ${j} = 0`, `var ${t} = 0`],\n load: [\n ` repeat ${k} from 0 to ${n - 2} {`,\n ` ${j} = ${k} + floor(random() * (${n} - ${k}))`,\n ` ${t} = ${sx}[${k}]`, ` ${sx}[${k}] = ${sx}[${j}]`, ` ${sx}[${j}] = ${t}`,\n ` ${t} = ${sy}[${k}]`, ` ${sy}[${k}] = ${sy}[${j}]`, ` ${sy}[${j}] = ${t}`,\n ' }',\n ],\n x: (i) => `${sx}[${i}]`,\n y: (i) => `${sy}[${i}]`,\n }\n}\n\n/** A line holding two statements: one keyword, then another after a run of spaces. */\nconst WORDS = 'prompt|target|item|total|chip|step|min|max|start|minus|plus|counter|rail|handle'\nconst RUNON = new RegExp(`^(?:${WORDS})\\\\b.*\\\\s{2,}(?:${WORDS})\\\\b`)\n\n/**\n * Split a block body into its non-empty, comment-free lines — ONE STATEMENT PER LINE, like FlatInk\n * itself. A body written inline is a run-on, and it deserves its own message: the grammar sketch in\n * `summary` shows the shape on one line, so that is the natural thing to copy.\n */\nfunction lines(kind: string, name: string, body: string): string[] {\n const out = body.split('\\n').map((l) => l.replace(/\\/\\/.*$/, '').trim()).filter(Boolean)\n for (const line of out) {\n if (!RUNON.test(line)) continue\n const laid = line.split(/\\s{2,}/).filter(Boolean).map((st) => ` ${st}`).join('\\n')\n throw new Error(`${kind} \"${name}\": two statements on one line — a block takes ONE per line, like FlatInk itself:\\n ${kind} ${name} {\\n${laid}\\n }`)\n }\n return out\n}\n\n// ── place ────────────────────────────────────────────────────────────────────\n// The drop gesture: items belong on targets. Several items may share a target (a sort) or each may have\n// its own (an ordering, a labelled diagram) -- that falls out of the data, it is not a separate gesture.\n\ntype PlaceModel = {\n prompt: string\n shuffle: boolean\n /** `targets size w,h`: the footprint of every target of the block that does not give its own. */\n targetSize?: { w: number; h: number }\n targets: { id: string; label: string; x: string; y: string; size?: { w: number; h: number } }[]\n items: { id: string; label: string; target: string; x: string; y: string }[]\n}\n\nfunction parsePlace(name: string, body: string, p: string): PlaceModel {\n const m: PlaceModel = { prompt: '', shuffle: false, targets: [], items: [] }\n // An object is named after its label, and `ident` keeps letters and digits only: `-1` and `+1` fold to\n // the same name, `< 1`, `= 1` and `> 1` too. The first keeps the plain name (so nothing moves for\n // labels that never collided — a skin addresses these objects by name); the next ones take `_2`, `_3`…\n const taken = new Set<string>()\n const own = (base: string): string => {\n let id = base\n for (let k = 2; taken.has(id); k++) id = `${base}_${k}`\n taken.add(id)\n return id\n }\n const targetOf = new Map<string, string>() // target LABEL -> its object: an item names a target by label\n for (const line of lines('place', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) m.prompt = x[1]\n else if (line === 'shuffle') m.shuffle = true\n else if ((x = line.match(/^targets\\s+size\\s+(\\d+),(\\d+)$/))) m.targetSize = { w: Number(x[1]), h: Number(x[2]) }\n else if ((x = line.match(/^target\\s+(?:\"([^\"]+)\"|(\\S+))\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)(?:\\s+size\\s+(\\d+),(\\d+))?$/))) {\n const label = x[1] ?? x[2]\n if (targetOf.has(label)) throw new Error(`place \"${name}\": two targets are labelled \"${label}\" — an item pointing at it could mean either`)\n const id = own(`${p}T${ident(label)}`)\n targetOf.set(label, id)\n // The size of a target is the author's (flatink/flatink#56): five fixed-size ones could not stand in a\n // row on a 760 px scene.\n m.targets.push({ id, label, x: x[3], y: x[4], ...(x[5] ? { size: { w: Number(x[5]), h: Number(x[6]) } } : {}) })\n } else if (/^targets?\\b.*\\bsize\\b/.test(line)) {\n throw new Error(`place \"${name}\": a size is two whole numbers, width,height — \\`target T at x,y size 120,80\\`, or \\`targets size 120,80\\` for the whole block: ${line}`)\n } else if ((x = line.match(/^item\\s+(?:\"([^\"]+)\"|(\\S+))\\s*->\\s*(?:\"([^\"]+)\"|(\\S+))\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) {\n const label = x[1] ?? x[2]\n m.items.push({ id: own(`${p}I${ident(label)}`), label, target: x[3] ?? x[4], x: x[5], y: x[6] })\n } else throw new Error(`place \"${name}\": unrecognised line: ${line}`)\n }\n if (!m.targets.length) throw new Error(`place \"${name}\": no target — an item has nowhere to go`)\n if (!m.items.length) throw new Error(`place \"${name}\": no item — nothing for the learner to do`)\n // Resolved once every line is read: a target may be declared after the item that names it.\n for (const it of m.items) {\n // By label; failing that by the identifier the label folds to, as long as ONE target answers to it\n // (`Vegetaux` for a target written with its accents — what this lookup accepted before).\n const folded = m.targets.filter((t) => ident(t.label) === ident(it.target))\n const id = targetOf.get(it.target) ?? (folded.length === 1 ? folded[0].id : undefined)\n if (!id) throw new Error(`place \"${name}\": item \"${it.label}\" points at an unknown target`)\n it.target = id\n }\n return m\n}\n\n/** Each block owns a range of 1000 focus ranks, so Tab goes through the blocks in their order whatever their\n * kind — a sequence numbered its cards from 1 and a compose ranked nothing, which mixed them with the others. */\nconst focusBase = (block: number): number => block * 1000\n\nfunction expandPlace(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n const m = parsePlace(name, body, p)\n const n = m.items.length\n const vars = [`// ${p}— place \"${q(name)}\"`, ...(m.prompt ? [`// prompt: ${q(m.prompt)}`] : []), `var ${p}progress = 0`, `var ${doneVar} = 0`]\n for (const it of m.items) vars.push(`var ${it.id}X = ${it.x}`, `var ${it.id}Y = ${it.y}`, `var ${it.id}Placed = 0`)\n const sh = m.shuffle ? shuffler(p, m.items) : null\n if (sh) vars.push(...sh.vars)\n // The second way in, for a keyboard (and for a finger that would rather tap twice than drag): pick an\n // item, then pick a target. `<p>sel` = the picked item, 1-based; 0 = none.\n const sel = `${p}sel`\n vars.push(`var ${sel} = 0`)\n // Tab order: the items of this block, then its targets — and the blocks one after the other.\n const itemOrder = focusBase(b) + 1, targetOrder = focusBase(b) + 2\n /** What putting item `i` on target `t` does — the SAME outcome whether it was dragged or picked. */\n const put = (it: PlaceModel['items'][number], i: number, t: PlaceModel['targets'][number], ind: string): string[] => t.id === it.target\n ? [\n `${ind}if ${it.id}Placed < 0.5 {`,\n `${ind} ${it.id}Placed = 1`, `${ind} ${p}progress = ${p}progress + 1`,\n // Snap onto the target it was put on, reading the target's LIVE position -- so a skin may move\n // the target anywhere and the item still lands on it.\n `${ind} ${it.id}X = ${t.id}.x`, `${ind} ${it.id}Y = ${t.id}.y`,\n `${ind} if ${sel} == ${i + 1} {`, `${ind} ${sel} = 0`, `${ind} }`, // placed: no longer picked, however it got there\n `${ind} send \"correct\", { block = ${b}, item = ${i} }`,\n `${ind} if ${p}progress == ${n} {`, `${ind} ${doneVar} = 1`, `${ind} send \"part\", { block = ${b} }`, `${ind} }`,\n `${ind}}`,\n ]\n : [\n `${ind}${it.id}X = ${sh ? sh.x(i) : it.x}`, `${ind}${it.id}Y = ${sh ? sh.y(i) : it.y}`, // back where it started\n `${ind}send \"incorrect\", { block = ${b}, item = ${i} }`,\n ]\n\n const layers = [` layer \"${p}targets\" {`]\n for (const t of m.targets) {\n const size = t.size ?? m.targetSize ?? theme.size('target')\n layers.push(...container(t.id, t.x, t.y, theme.draw('target', t.label, size), size))\n }\n layers.push(' }', ` layer \"${p}items\" {`)\n for (const it of m.items) layers.push(...container(it.id, it.x, it.y, theme.draw('item', it.label)))\n layers.push(' }')\n\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, ...m.items.flatMap((it, i) => [` ${it.id}X = ${sh.x(i)}`, ` ${it.id}Y = ${sh.y(i)}`]), '}', '')\n m.items.forEach((it, i) => {\n behavior.push(`object \"${it.id}\" {`, ` focusable order ${itemOrder}`)\n behavior.push(` drag ${it.id}X, ${it.id}Y { enabled ${it.id}Placed == 0 }`)\n behavior.push(' when clicked {', ` if ${it.id}Placed < 0.5 {`, ` ${sel} = ${i + 1}`, ' }', ' }')\n // Its own target first, then the others — the order these handlers have always been declared in.\n for (const t of [...m.targets.filter((x) => x.id === it.target), ...m.targets.filter((x) => x.id !== it.target)]) behavior.push(` when dropped on ${t.id} at pointer {`, ...put(it, i, t, ' '), ' }')\n behavior.push(` x = ${it.id}X`, ` y = ${it.id}Y`)\n // Which item is picked is STATE, like the live step of a sequence: it stands out a little. A theme\n // that disagrees rebinds the scale itself.\n behavior.push(` scaleX = ${sel} == ${i + 1} ? 1.08 : 1`, ` scaleY = ${sel} == ${i + 1} ? 1.08 : 1`, '}', '')\n })\n for (const t of m.targets) {\n behavior.push(`object \"${t.id}\" {`, ` focusable order ${targetOrder}`, ' when clicked {')\n // Only an item still to place: one picked and then dragged home must not be sent back by a later tap.\n m.items.forEach((it, i) => behavior.push(` if ${sel} == ${i + 1} && ${it.id}Placed < 0.5 {`, ` ${sel} = 0`, ...put(it, i, t, ' '), ' }'))\n behavior.push(' }', '}', '')\n }\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'place', name, prompt: m.prompt, items: m.items.map((i) => i.label), targets: m.targets.map((t) => t.label), objects: [...m.targets, ...m.items].map((o) => o.id), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n// ── compose ──────────────────────────────────────────────────────────────────\n// Tap values until they add up to a target. Overshooting resets the total -- the whole point of the\n// exercise is that going over is a mistake you feel, not one you are prevented from making.\n\nfunction expandCompose(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n let target = 0, total = ''\n const chips: { value: string; label?: string; x: string; y: string }[] = []\n let prompt = ''\n let shuffle = false\n let unit = ''\n let counter: { x: string; y: string } | undefined\n // flatink/flatink#55: decimal values (money without counting in cents), a label or a unit on the chips, and\n // an opt-in `counter` showing where the learner stands.\n const NUM = '(\\\\d+(?:\\\\.\\\\d+)?)'\n for (const line of lines('compose', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if (line === 'shuffle') shuffle = true\n else if ((x = line.match(new RegExp(`^total\\\\s+${NUM}$`)))) { total = x[1]; target = Number(x[1]) }\n else if ((x = line.match(new RegExp(`^chip\\\\s+${NUM}(?:\\\\s+\"([^\"]*)\")?\\\\s+at\\\\s+(-?[\\\\d.]+),(-?[\\\\d.]+)$`)))) chips.push({ value: x[1], ...(x[2] !== undefined ? { label: x[2] } : {}), x: x[3], y: x[4] })\n else if ((x = line.match(/^unit\\s+\"(.*)\"$/))) unit = x[1]\n else if ((x = line.match(/^counter\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) counter = { x: x[1], y: x[2] }\n else throw new Error(`compose \"${name}\": unrecognised line: ${line}`)\n }\n // Decimals add up in floating point (0.1 + 0.2 is not 0.3): the running total is rounded to the places the\n // author wrote, and reaching / overshooting the total is decided with that rounding.\n const places = Math.max(0, ...[total, ...chips.map((c) => c.value)].map((v) => (v.split('.')[1] ?? '').length))\n const k = 10 ** places\n if (!target) throw new Error(`compose \"${name}\": no \\`total <n>\\` — there is nothing to reach`)\n if (!chips.length) throw new Error(`compose \"${name}\": no chip — nothing for the learner to tap`)\n\n const vars = [`// ${p}— compose \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${p}total = 0`, `var ${doneVar} = 0`]\n const layers = [` layer \"${p}chips\" {`]\n chips.forEach((c, i) => layers.push(...container(`${p}C${i}`, c.x, c.y, theme.draw('chip', c.label ?? (unit ? `${c.value} ${unit}` : c.value)), theme.size('chip'))))\n if (counter) {\n // The theme draws the counter; its text shows the running total where it writes `{}`. A theme that\n // predates the role draws nothing — the group is still there for a skin.\n const size = theme.size('counter') ?? { w: 180, h: 56 }\n const art = (theme.draw('counter', `{} / ${total}${unit ? ` ${unit}` : ''}`, size) ?? []).map((l) => (l.startsWith('text ') ? `${l} bind \"${p}total\"${places ? ` decimals ${places}` : ''}` : l))\n layers.push(...container(`${p}Total`, counter.x, counter.y, art))\n }\n layers.push(' }')\n\n const sh = shuffle ? shuffler(p, chips) : null\n if (sh) vars.push(...sh.vars)\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, '}', '')\n chips.forEach((c, i) => {\n behavior.push(`object \"${p}C${i}\" {`, ` focusable order ${focusBase(b) + 1}`, ...(sh ? [` x = ${sh.x(i)}`, ` y = ${sh.y(i)}`] : []), ' when clicked {', ` if ${doneVar} < 0.5 {`)\n const sum = places ? `round((${p}total + ${c.value}) * ${k}) / ${k}` : `${p}total + ${c.value}`\n behavior.push(` if ${sum} > ${total} {`)\n behavior.push(` ${p}total = 0`, ` send \"incorrect\", { block = ${b}, item = ${i} }`)\n behavior.push(' } else {')\n behavior.push(` ${p}total = ${sum}`, ` send \"correct\", { block = ${b}, item = ${i} }`)\n behavior.push(` if ${p}total == ${total} {`, ` ${doneVar} = 1`, ` send \"part\", { block = ${b} }`, ' }')\n behavior.push(' }', ' }', ' }', '}', '')\n })\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'compose', name, prompt, items: chips.map((c) => c.value), targets: [], objects: chips.map((_c, i) => `${p}C${i}`), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n// ── steps ────────────────────────────────────────────────────────────────────\n// A gated sequence: step i only responds when it is the current one. A step AHEAD of the current one is a\n// mistake the host may count (`incorrect`, like `place` and `compose` — flatink/flatink#58); nothing else\n// moves, so the sequence stays usable as an escape-room stage. A step already done stays silent.\n\nfunction expandSteps(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b, doneVar } = ctx\n let prompt = ''\n let shuffle = false\n const steps: { label: string; x: string; y: string }[] = []\n for (const line of lines('steps', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if (line === 'shuffle') shuffle = true\n else if ((x = line.match(/^step\\s+\"(.*)\"\\s+at\\s+(-?[\\d.]+),(-?[\\d.]+)$/))) steps.push({ label: x[1], x: x[2], y: x[3] })\n else throw new Error(`steps \"${name}\": unrecognised line: ${line}`)\n }\n if (!steps.length) throw new Error(`steps \"${name}\": no step — the sequence is empty`)\n\n const vars = [`// ${p}— steps \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${p}step = 0`, `var ${doneVar} = 0`]\n const layers = [` layer \"${p}steps\" {`]\n steps.forEach((s, i) => layers.push(...container(`${p}S${i}`, s.x, s.y, theme.draw('card', s.label), theme.size('card'))))\n layers.push(' }')\n\n // `shuffle` moves the CARDS; the order of the sequence stays the declared one.\n const sh = shuffle ? shuffler(p, steps) : null\n if (sh) vars.push(...sh.vars)\n const behavior: string[] = []\n if (sh) behavior.push('when loaded {', ...sh.load, '}', '')\n steps.forEach((_s, i) => {\n // `order`: Tab walks the cards in the order of the SEQUENCE, wherever they stand (`shuffle` moves them).\n behavior.push(`object \"${p}S${i}\" {`, ` focusable order ${focusBase(b) + 1 + i}`, ...(sh ? [` x = ${sh.x(i)}`, ` y = ${sh.y(i)}`] : []), ' when clicked {', ` if ${p}step == ${i} {`, ` ${p}step = ${p}step + 1`, ` send \"step\", { block = ${b}, item = ${i} }`)\n if (i === steps.length - 1) behavior.push(` ${doneVar} = 1`, ` send \"part\", { block = ${b} }`)\n behavior.push(' }')\n if (i > 0) behavior.push(` if ${p}step < ${i} {`, ` send \"incorrect\", { block = ${b}, item = ${i} }`, ' }')\n behavior.push(' }')\n // The only thing the gesture says about looks: a step that is not current is dimmed, because\n // \"which one is live\" is STATE, not decoration. A theme that disagrees rebinds opacity itself.\n behavior.push(` opacity = ${p}step == ${i} ? 1 : 0.45`, '}', '')\n })\n return {\n vars,\n layers,\n behavior,\n meta: { keyword: 'steps', name, prompt, items: steps.map((s) => s.label), targets: [], objects: steps.map((_s, i) => `${p}S${i}`), doneVar, ...(sh ? { shuffle: true } : {}) },\n }\n}\n\n// ── controls ─────────────────────────────────────────────────────────────────\n// Inputs, not tasks (flatink/flatink#39): they write `<name>_value`, send `change` when it moves, and have\n// no \"done\". They are the recipes every activity copied by hand — the bounds, the rounding, the repeat.\n\nconst NUMBER = '(-?\\\\d+(?:\\\\.\\\\d+)?)'\n/** The `min` / `max` / `step` / `start` lines of a control, checked; `k` rounds to the places written. */\nfunction range(kind: string, name: string, got: { min?: string; max?: string; step?: string; start?: string }): { min: string; max: string; step: string; start: string; places: number; k: number } {\n if (got.min === undefined || got.max === undefined) throw new Error(`${kind} \"${name}\": it needs its bounds — \\`min <n>\\` and \\`max <n>\\`, one per line`)\n if (Number(got.min) >= Number(got.max)) throw new Error(`${kind} \"${name}\": min (${got.min}) must be below max (${got.max})`)\n const step = got.step ?? '1'\n if (Number(step) <= 0) throw new Error(`${kind} \"${name}\": step must be above 0`)\n const start = got.start ?? got.min\n if (Number(start) < Number(got.min) || Number(start) > Number(got.max)) throw new Error(`${kind} \"${name}\": start (${start}) is outside ${got.min}..${got.max}`)\n const places = Math.max(0, ...[got.min, got.max, step, start].map((v) => (v.split('.')[1] ?? '').length))\n return { min: got.min, max: got.max, step, start, places, k: 10 ** places }\n}\n\ntype Range = ReturnType<typeof range>\ntype At = { x: string; y: string }\n\n/**\n * The − / + mechanics, shared by `stepper` and by a `slider`'s optional ends. A press steps at once, then\n * repeats while held (after 0.4 s, every 0.12 s); the keyboard (Enter / Space on the focused button) steps\n * once per key press.\n */\nfunction stepping(p: string, b: number, v: string, r: Range): { vars: string[]; fn: string[]; button: (id: string, d: number, order: number) => string[]; tick: string[] } {\n const sum = r.places ? `round((${v} + d * ${r.step}) * ${r.k}) / ${r.k}` : `${v} + d * ${r.step}`\n return {\n vars: [`var ${p}to = 0`, `var ${p}held = 0`, `var ${p}next = 0`, `var ${p}byPress = 0`, `var ${p}upAt = 0`],\n fn: [\n `fn ${p}stepBy(d) {`,\n ` ${p}to = clamp(${sum}, ${r.min}, ${r.max})`,\n ` if ${p}to != ${v} {`, ` ${v} = ${p}to`, ` send \"change\", { block = ${b}, value = ${v} }`, ' }',\n '}', ''],\n button: (id, d, order) => [\n `object \"${id}\" {`, ` focusable order ${order}`,\n // The mouse: a step at the press, then the repeat. The click that follows the release is that same\n // press — only a click with no press before it (the keyboard) steps.\n ' when pressed {', ` ${p}stepBy(${d})`, ` ${p}held = ${d}`, ` ${p}next = clock + 0.4`, ` ${p}byPress = 1`, ' }',\n ' when released {', ` ${p}held = 0`, ` ${p}upAt = clock`, ' }',\n ' when clicked {', ` if ${p}byPress == 1 {`, ` ${p}byPress = 0`, ' } else {', ` ${p}stepBy(${d})`, ' }', ' }',\n // At its bound a button is dimmed: which way is still open is STATE, as the live step of a sequence is.\n ` opacity = ${d < 0 ? `${v} <= ${r.min}` : `${v} >= ${r.max}`} ? 0.45 : 1`, '}', ''],\n tick: [\n ` if ${p}held != 0 && clock >= ${p}next {`, ` ${p}stepBy(${p}held)`, ` ${p}next = clock + 0.12`, ' }',\n // A press released OUTSIDE the button gets no click: forget it, or the next key press would be eaten.\n ` if ${p}held == 0 && ${p}byPress == 1 && clock > ${p}upAt + 0.05 {`, ` ${p}byPress = 0`, ' }'],\n }\n}\n\n/** The optional readout of a control: the theme's `counter`, its text bound to the value. */\nfunction readout(p: string, v: string, r: Range, at: At, theme: Theme): string[] {\n const size = theme.size('counter') ?? { w: 180, h: 56 }\n const art = (theme.draw('counter', '{}', size) ?? []).map((l) => (l.startsWith('text ') ? `${l} bind \"${v}\"${r.places ? ` decimals ${r.places}` : ''}` : l))\n return container(`${p}Value`, at.x, at.y, art)\n}\n\nconst AT = '(-?[\\\\d.]+),(-?[\\\\d.]+)'\n\nfunction expandStepper(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b } = ctx\n const got: { min?: string; max?: string; step?: string; start?: string } = {}\n let prompt = ''\n let minus: At | undefined, plus: At | undefined, counter: At | undefined\n for (const line of lines('stepper', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if ((x = line.match(new RegExp(`^(min|max|step|start)\\\\s+${NUMBER}$`)))) got[x[1] as 'min'] = x[2]\n else if ((x = line.match(new RegExp(`^(minus|plus|counter)\\\\s+at\\\\s+${AT}$`)))) {\n const at = { x: x[2], y: x[3] }\n if (x[1] === 'minus') minus = at; else if (x[1] === 'plus') plus = at; else counter = at\n } else throw new Error(`stepper \"${name}\": unrecognised line: ${line}`)\n }\n const r = range('stepper', name, got)\n if (!minus || !plus) throw new Error(`stepper \"${name}\": it needs its two buttons — \\`minus at x,y\\` and \\`plus at x,y\\``)\n\n const v = `${p}value`\n const st = stepping(p, b, v, r)\n const vars = [`// ${p}— stepper \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${v} = ${r.start}`, ...st.vars]\n const layers = [` layer \"${p}stepper\" {`,\n ...container(`${p}Minus`, minus.x, minus.y, theme.draw('chip', '\\u2212'), theme.size('chip')),\n ...container(`${p}Plus`, plus.x, plus.y, theme.draw('chip', '+'), theme.size('chip')),\n ...(counter ? readout(p, v, r, counter, theme) : []),\n ' }']\n const behavior = [...st.fn, ...st.button(`${p}Minus`, -1, focusBase(b) + 1), ...st.button(`${p}Plus`, 1, focusBase(b) + 2), 'every frame {', ...st.tick, '}', '']\n return {\n vars, layers, behavior,\n meta: { keyword: 'stepper', name, prompt, items: [], targets: [], objects: [`${p}Minus`, `${p}Plus`, ...(counter ? [`${p}Value`] : [])], doneVar: '', control: true },\n }\n}\n\n// A handle on a rail. A press on the rail jumps there, a drag follows (the pointer stays captured off the\n// rail); the value snaps to its step. `change` is sent once, at the release, if the value moved.\nfunction expandSlider(name: string, body: string, theme: Theme, ctx: GestureContext): Expansion {\n const { prefix: p, index: b } = ctx\n const got: { min?: string; max?: string; step?: string; start?: string } = {}\n let prompt = ''\n let rail: { x0: number; y0: number; x1: number; y1: number } | undefined\n let minus: At | undefined, plus: At | undefined, counter: At | undefined\n for (const line of lines('slider', name, body)) {\n let x: RegExpMatchArray | null\n if ((x = line.match(/^prompt\\s+\"(.*)\"$/))) prompt = x[1]\n else if ((x = line.match(new RegExp(`^(min|max|step|start)\\\\s+${NUMBER}$`)))) got[x[1] as 'min'] = x[2]\n else if ((x = line.match(new RegExp(`^rail\\\\s+${AT}\\\\s+to\\\\s+${AT}$`)))) rail = { x0: Number(x[1]), y0: Number(x[2]), x1: Number(x[3]), y1: Number(x[4]) }\n else if ((x = line.match(new RegExp(`^(minus|plus|counter)\\\\s+at\\\\s+${AT}$`)))) {\n const at = { x: x[2], y: x[3] }\n if (x[1] === 'minus') minus = at; else if (x[1] === 'plus') plus = at; else counter = at\n } else throw new Error(`slider \"${name}\": unrecognised line: ${line}`)\n }\n const r = range('slider', name, got)\n if (!rail) throw new Error(`slider \"${name}\": it needs its rail — \\`rail x,y to x,y\\` (the first point is the minimum)`)\n const horizontal = rail.y0 === rail.y1 && rail.x0 !== rail.x1\n if (!horizontal && !(rail.x0 === rail.x1 && rail.y0 !== rail.y1)) throw new Error(`slider \"${name}\": the rail is horizontal or vertical — \\`rail 160,170 to 360,170\\` or \\`rail 100,300 to 100,100\\``)\n if (!!minus !== !!plus) throw new Error(`slider \"${name}\": the two ends go together — \\`minus at x,y\\` and \\`plus at x,y\\``)\n\n const v = `${p}value`\n const st = minus && plus ? stepping(p, b, v, r) : null\n const [a0, a1] = horizontal ? [rail.x0, rail.x1] : [rail.y0, rail.y1]\n const len = Math.abs(a1 - a0)\n const vars = [`// ${p}— slider \"${q(name)}\"`, ...(prompt ? [`// prompt: ${q(prompt)}`] : []), `var ${v} = ${r.start}`, `var ${p}grab = 0`, `var ${p}from = 0`, ...(st ? st.vars : [])]\n\n // The rail takes the touch (a generous band around it); the handle is drawn on top and lets it through.\n const band = horizontal ? { w: len + 48, h: 56 } : { w: 56, h: len + 48 }\n const railSize = horizontal ? { w: len + 24, h: 20 } : { w: 20, h: len + 24 }\n const layers = [` layer \"${p}slider\" {`,\n ...container(`${p}Rail`, String((rail.x0 + rail.x1) / 2), String((rail.y0 + rail.y1) / 2), theme.draw('target', '', railSize), band),\n ` group \"${p}Handle\" at ${rail.x0},${rail.y0} pivot 0,0 nohit {`, ' layer \"art\" {', ...theme.draw('item', '', { w: 44, h: 44 }).map((l) => ` ${l}`), ' }', ' }',\n ...(minus && plus ? [...container(`${p}Minus`, minus.x, minus.y, theme.draw('chip', '\\u2212'), theme.size('chip')), ...container(`${p}Plus`, plus.x, plus.y, theme.draw('chip', '+'), theme.size('chip'))] : []),\n ...(counter ? readout(p, v, r, counter, theme) : []),\n ' }']\n\n const raw = `${r.min} + round((a - ${a0}) / (${a1} - ${a0}) * (${r.max} - ${r.min}) / ${r.step}) * ${r.step}`\n const axis = horizontal ? 'mouse.x' : 'mouse.y'\n const share = `(${v} - ${r.min}) / (${r.max} - ${r.min})`\n const behavior = [\n `fn ${p}setAt(a) {`, // `a`: the pointer along the rail's axis\n ` ${v} = clamp(${r.places ? `round((${raw}) * ${r.k}) / ${r.k}` : raw}, ${r.min}, ${r.max})`,\n '}', '',\n `object \"${p}Rail\" {`,\n ' when pressed {', ` ${p}grab = 1`, ` ${p}from = ${v}`, ` ${p}setAt(${axis})`, ' }',\n ' when dragged {', ` if ${p}grab == 1 {`, ` ${p}setAt(${axis})`, ' }', ' }',\n ' when released {', ` ${p}grab = 0`, ` if ${v} != ${p}from {`, ` send \"change\", { block = ${b}, value = ${v} }`, ' }', ' }',\n '}', '',\n // The handle's place always comes from the value. Held, it grows a little: that it is held is STATE.\n `object \"${p}Handle\" {`,\n ` x = ${rail.x0} + (${rail.x1} - ${rail.x0}) * ${share}`, ` y = ${rail.y0} + (${rail.y1} - ${rail.y0}) * ${share}`,\n ` scaleX = ${p}grab == 1 ? 1.15 : 1`, ` scaleY = ${p}grab == 1 ? 1.15 : 1`,\n '}', '',\n ...(st ? [...st.fn, ...st.button(`${p}Minus`, -1, focusBase(b) + 1), ...st.button(`${p}Plus`, 1, focusBase(b) + 2), 'every frame {', ...st.tick, '}', ''] : []),\n ]\n return {\n vars, layers, behavior,\n meta: { keyword: 'slider', name, prompt, items: [], targets: [], objects: [`${p}Rail`, `${p}Handle`, ...(minus && plus ? [`${p}Minus`, `${p}Plus`] : []), ...(counter ? [`${p}Value`] : [])], doneVar: '', control: true },\n }\n}\n\n/** `208x118` — the footprint of a role under a theme, for a summary or a prompt. */\nconst footprint = (theme: Theme, role: Role): string => {\n const { w, h } = theme.size(role)\n return `${w}x${h}`\n}\n\n/**\n * Build the shipped gestures against a theme. `GREYBOX` unless the caller supplies one.\n *\n * Each `summary` carries the FOOTPRINTS of the roles it places, because the author writes positions and\n * two overlapping surfaces make a drop ambiguous. Measured on ten model-written activities: a prompt\n * giving only \"space them out\" produced overlapping hitboxes in 4 of 10; the same prompt with the\n * numbers, 0 of 10. The sizes were public all along — nothing said you had to go and read them.\n */\nexport function gestures(opts: GestureOptions = {}): Gesture[] {\n const theme = opts.theme ?? GREYBOX\n return [\n {\n keyword: 'place',\n summary: `place <name> { prompt \"…\" [shuffle] [targets size w,h] target <T> at x,y [size w,h] item <i> -> <T> at x,y } — drag items onto where they belong. Footprints: target ${footprint(theme, 'target')} unless sized, item ${footprint(theme, 'item')}; keep centres at least one footprint apart or the drop is ambiguous`,\n expand: (name, body, _doc, ctx) => expandPlace(name, body, theme, ctx),\n },\n {\n keyword: 'compose',\n summary: `compose <name> { prompt \"…\" [shuffle] [unit \"…\"] total <n> chip <v> [\"label\"] at x,y [counter at x,y] } — tap values until they add up (decimals allowed); overshooting resets; \\`counter\\` shows the running total. Footprint: chip ${footprint(theme, 'chip')}`,\n expand: (name, body, _doc, ctx) => expandCompose(name, body, theme, ctx),\n },\n {\n keyword: 'steps',\n summary: `steps <name> { prompt \"…\" [shuffle] step \"…\" at x,y } — a gated sequence; a step tapped ahead of its turn sends \"incorrect\". Footprint: card ${footprint(theme, 'card')}`,\n expand: (name, body, _doc, ctx) => expandSteps(name, body, theme, ctx),\n },\n {\n keyword: 'stepper',\n summary: `stepper <name> { prompt \"…\" min <n> max <n> [step <n>] [start <n>] minus at x,y plus at x,y [counter at x,y] } — a CONTROL (not a task): − / + buttons writing <name>_value, repeating while held; sends \"change\". Footprint: chip ${footprint(theme, 'chip')}`,\n expand: (name, body, _doc, ctx) => expandStepper(name, body, theme, ctx),\n },\n {\n keyword: 'slider',\n summary: 'slider <name> { prompt \"…\" min <n> max <n> [step <n>] [start <n>] rail x,y to x,y [minus at x,y plus at x,y] [counter at x,y] } — a CONTROL: a handle on a horizontal or vertical rail (first point = min) writing <name>_value; press the rail or drag; sends \"change\" at the release. The − / + ends are the keyboard way in. Footprint: handle 44x44; the rail takes touches in a band 56 wide',\n expand: (name, body, _doc, ctx) => expandSlider(name, body, theme, ctx),\n },\n ]\n}\n\n/**\n * The paste-ready reference for a model writing sugar: the grammar of every gesture, the footprints it\n * must respect, and the canvas it is laying out on. This is the artefact `summary` was being used as by\n * hand — assembled here so nobody has to know that the sizes live on the theme.\n */\nexport function sugarCard(opts: GestureOptions & { document?: { width: number; height: number } } = {}): string {\n const theme = opts.theme ?? GREYBOX\n const doc = opts.document ?? { width: 760, height: 620 }\n return [\n '# FlatInk sugar — a document is one or more blocks',\n '',\n `Canvas: ${doc.width}x${doc.height}, origin top-left. Coordinates are the CENTRE of each thing.`,\n 'Anything you do not describe is not drawn.',\n '',\n '## Blocks — ONE statement per line, like FlatInk itself',\n '',\n 'place <name> { compose <name> { steps <name> {',\n ' prompt \"…\" prompt \"…\" prompt \"…\"',\n ' target <T> at x,y total <n> step \"…\" at x,y',\n ' item <i> -> <T> at x,y chip <v> at x,y }',\n '} }',\n '',\n ...gestures({ theme }).map((g) => `- ${g.summary}`),\n '',\n 'Several blocks may share a document; give each a DISTINCT name. They are laid out in the order you',\n 'write them, each keeps its own state, and each emits `part` when its own portion is finished. The',\n 'document emits `completed` once, when every block is done.',\n '',\n '## Footprints — respect them or two things overlap and the drop becomes ambiguous',\n ...ROLES.map((role) => `- ${role}: ${footprint(theme, role)}`),\n '',\n `A row of targets fits about ${Math.floor(doc.width / (theme.size('target').w + 20))} across; a row of items about ${Math.floor(doc.width / (theme.size('item').w + 20))}.`,\n 'Leave a gap the size of the thing itself between two centres. Two blocks must not share the same area.',\n '',\n '## Escape hatch — three forms, and they are not interchangeable',\n 'The blocks draw NOTHING beyond the pieces above. Everything else goes through one of these:',\n '',\n 'raw scene under { layer \"bg\" { … } } <- BACKGROUND and decor. Drawn BEHIND everything.',\n 'raw scene { layer \"banner\" { … } } <- overlays drawn ON TOP: a title, a frame, a caption.',\n 'raw { var lives = 3 object \"X\" { … } } <- header and behavior: var, fn, asset, object blocks.',\n '',\n 'A `layer` written in a plain `raw { … }` lands OUTSIDE the scene and is a compile error — anything',\n 'that is drawn belongs in one of the two `raw scene` forms. Any of them may be written on one line.',\n ].join('\\n')\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// @flatkit/sugarflat — declarative sugar for FlatInk.\n//\n// A compact block expands into INSPECTABLE `.flatink`. The expansion is the artefact of record: the\n// sugar is an authoring tool, never a runtime, and anything it writes can be written by hand instead.\n//\n// WHY IT LIVES IN THIS REPO. Not to put authoring opinions into the language — it is a separate\n// package, out of the lockstep group, and `@flatkit/compiler` never depends on it. It lives here so\n// the two move together: a grammar change breaks these tests in the same CI run. That is the whole\n// argument, and it was demonstrated rather than argued — the previous out-of-tree sugar never emitted\n// the format's REQUIRED `size` line, on its entire corpus, for months, unnoticed.\n//\n// THE LINE THIS PACKAGE DOES NOT CROSS: no visual opinion. A gesture emits state and behavior; it\n// never emits a coordinate, a colour or a font. The language already ships interaction opinions\n// (`drag`, `when dropped on … at pointer`, `hitbox`, `match`, `feedback`, `spring`) — what it has\n// never shipped is a look, and a sugar that shipped one would hand every activity the same face.\n// ─────────────────────────────────────────────────────────────────────────────\nimport { DEFAULT_DOCUMENT, ensureHeader, type DocumentSpec } from './document'\nimport { gestures } from './gestures'\nimport { ident } from './ident'\n\nexport { DEFAULT_DOCUMENT, ensureHeader, hasSizeHeader, hasTimelineHeader, type DocumentSpec } from './document'\nexport { ident, isIdent } from './ident'\nexport { gestures, sugarCard, type GestureOptions } from './gestures'\nexport { BLANK, GREYBOX, ROLES, type Role, type Theme } from './theme'\n\n/**\n * What a gesture parsed out of the author's block, beside the DSL it wrote.\n *\n * A host needs this. The expansion deliberately carries no text a host can read back — the prompt is the\n * learner's instruction and the payloads are indices (`send \"correct\", { item = 2 }`), so without the\n * labels there is nothing to display and nothing to map an index onto. Re-parsing the block by hand to\n * recover data the package already parsed is not an integration; it is a second parser waiting to drift.\n */\nexport type GestureMeta = {\n keyword: string\n /** The block's name — also the prefix on every name it emits. */\n name: string\n /** The learner's instruction, verbatim. Drawn by the host or a theme — never by the gesture. */\n prompt: string\n /** Labels in payload order: `{ block = b, item = 2 }` is `blocks[b].items[2]`. */\n items: string[]\n /** Target labels, in the order they were declared. Empty when the gesture has none. */\n targets: string[]\n /** Every object id it emitted, prefixed — what a skin binds to, without having to guess the scheme. */\n objects: string[]\n /** The variable that turns 1 when this block is finished. Empty for a `control`. */\n doneVar: string\n /** `true` for an INPUT (a stepper, a slider): it writes a value, it has no end, and the document's\n * `completed` does not wait for it. Its value is the variable `<name>_value`. */\n control?: boolean\n /** `true` when the block asked for `shuffle`: its elements do not stand where the source put them. */\n shuffle?: boolean\n}\n\n/** Where a block sits in the document. Names are prefixed so two blocks never collide. */\nexport type GestureContext = {\n /** 0-based position, and the `block` field of every payload it sends. */\n index: number\n /** `<name>_` — put it in front of every var and every object id. */\n prefix: string\n /** The variable to set to 1 when the block is done; the document watches it. */\n doneVar: string\n}\n\n/** What a gesture returns: PARTS, never a finished program — a document has exactly one `scene`. */\nexport type Expansion = {\n /** `var` declarations. They go in the header, where `var` is legal. */\n vars: string[]\n /** Layer statements, indented for the inside of `scene { … }`. */\n layers: string[]\n /** `object` blocks and scene-wide handlers. */\n behavior: string[]\n meta: GestureMeta\n}\n\n/** A sugar block: the keyword that opens it, and how it expands. */\nexport type Gesture = {\n /** The opening keyword, e.g. `place`. Matched only at column 0, followed by a name and `{`. */\n keyword: string\n /** One line, for diagnostics and for the reference a model is prompted with. */\n summary: string\n /** `body` is the text between the braces; `name` the block's name. */\n expand: (name: string, body: string, doc: DocumentSpec, ctx: GestureContext) => Expansion\n}\n\nexport type DesugarOptions = {\n /** The canvas + timeline the expansion targets. */\n document?: DocumentSpec\n /** Gestures to recognise. Defaults to the registered set. */\n gestures?: Gesture[]\n}\n\nexport type DesugarResult = {\n /** The expanded (or passed-through) FlatInk. */\n flatink: string\n /** Which gesture matched — `raw` for an escape-only source, `null` for plain FlatInk. */\n kind: string | null\n expanded: boolean\n /** What each block understood, in document order: `{ block = 2 }` refers to `meta[2]`. Empty when\n * nothing was expanded, since there is no block to have understood anything from. */\n meta: GestureMeta[]\n}\n\n/** Raised when a source opens a block that looks like sugar but no gesture claims it. */\nexport class UnknownSugarError extends Error {\n constructor(readonly keyword: string, known: string[]) {\n super(\n `unknown sugar block \"${keyword}\" — no gesture claims it, so nothing was expanded. ` +\n `Known blocks: ${known.length ? known.join(', ') : '(none registered)'}. ` +\n `If this is meant to be plain FlatInk, it is not: \"${keyword}\" is not a FlatInk statement either.`,\n )\n this.name = 'UnknownSugarError'\n }\n}\n\n/** The gestures this package ships, drawn by the default theme. Each entry is a public promise. */\nexport const GESTURES: Gesture[] = gestures()\n\n/** A block opens at column 0: `<keyword> <name> {`. The name may be quoted (so it can hold spaces). */\nconst BLOCK_OPEN = /^([a-zA-Z][\\w-]*)[ \\t]+(?:\"([^\"]+)\"|([^\\s{\"]+))[ \\t]*\\{/m\n\n/**\n * The escape hatch, in three forms. A sugar that cannot be opted out of stops being scaffolding and\n * becomes a cage: the previous generation of this idea flattened every activity into the same shape\n * until the template was pulled apart. Anything a gesture writes must remain writable by hand, beside it.\n *\n * raw { … } verbatim at TOP LEVEL — header (`asset`, `var`, `fn`) and `object` blocks\n * raw scene { … } verbatim inside the generated scene, ON TOP of it — banners, overlays\n * raw scene under { … } verbatim inside the generated scene, UNDERNEATH it — backgrounds\n *\n * `under` is not a nicety. A gesture emits no appearance by design, so a background is the FIRST thing\n * any skin needs — and spliced on top, an opaque full-canvas rect hides the entire activity while\n * `checkProgram` reports a clean program. Ten activities shipped that way, read as broken assets.\n */\nconst RAW_OPEN = /^raw(?:[ \\t]+scene(?:[ \\t]+(under))?)?[ \\t]*\\{/gm\n\ntype RawParts = { top: string[]; over: string[]; under: string[]; rest: string }\n\n/**\n * Pull every `raw …` block out of `text`. Brace-counted rather than line-anchored, so the one-line form\n * works — the README and `sugarCard()` both write `raw { … }` on one line, and a regex demanding a\n * newline after `{` left the keyword to travel on to the compiler, which reported `unexpected statement\n * \"raw\"`: a sugar defect described in FlatInk's vocabulary, the very thing `UnknownSugarError` fixed.\n */\nfunction takeRaw(text: string): RawParts {\n const out: RawParts = { top: [], over: [], under: [], rest: '' }\n let cursor = 0\n RAW_OPEN.lastIndex = 0\n for (let m = RAW_OPEN.exec(text); m; m = RAW_OPEN.exec(text)) {\n const openBrace = m.index + m[0].length - 1\n const end = blockEnd(text, openBrace)\n if (end < 0) break // unbalanced: leave it alone, the compiler will say so\n const raw = text.slice(openBrace + 1, end)\n // One-line form (`raw { var x = 1 }`) keeps the spaces that hugged the braces; the multi-line form\n // keeps its own indentation, which is the author's.\n const body = raw.includes('\\n') ? raw.replace(/^\\r?\\n/, '').replace(/\\r?\\n[ \\t]*$/, '') : raw.trim()\n const bucket = m[0].includes('scene') ? (m[1] ? out.under : out.over) : out.top\n bucket.push(body)\n out.rest += text.slice(cursor, m.index)\n cursor = end + 1\n RAW_OPEN.lastIndex = cursor\n }\n out.rest += text.slice(cursor)\n return out\n}\n\n/** Index of the `}` closing the brace at `openBrace`, or -1 if unbalanced. */\nfunction blockEnd(text: string, openBrace: number): number {\n let depth = 1\n for (let i = openBrace + 1; i < text.length; i++) {\n if (text[i] === '{') depth++\n else if (text[i] === '}' && --depth === 0) return i\n }\n return -1\n}\n\nconst RAW_NOTE = '// -- raw (escape hatch: verbatim FlatInk) --'\n\n/** Raised when two blocks share a name — every name they emit would collide. */\nexport class DuplicateBlockError extends Error {\n constructor(name: string) {\n super(`two blocks are both named \"${name}\" — the name prefixes every variable and object a block emits, so they would overwrite each other. Give each block its own name.`)\n this.name = 'DuplicateBlockError'\n }\n}\n\n/** One gesture block found in the source, with its span so the text around it can be recovered. */\ntype Found = { gesture: Gesture; name: string; body: string; start: number; end: number }\n\n/** Every gesture block in `src`, in document order. */\nfunction findBlocks(src: string, gestures: Gesture[]): Found[] {\n const re = new RegExp(BLOCK_OPEN.source, 'gm')\n const found: Found[] = []\n for (let m = re.exec(src); m; m = re.exec(src)) {\n const gesture = gestures.find((g) => g.keyword === m![1])\n if (!gesture) {\n // A keyword that opens a block at column 0 and is neither sugar nor FlatInk is a block we do not\n // know — say so with the list, rather than downstream in another vocabulary.\n if (m[1] !== 'raw' && !FLATINK_BLOCKS.has(m[1])) throw new UnknownSugarError(m[1], gestures.map((g) => g.keyword))\n continue\n }\n const braceAt = m.index + m[0].length - 1\n const close = blockEnd(src, braceAt)\n if (close < 0) break\n found.push({ gesture, name: m[2] ?? m[3] ?? gesture.keyword, body: src.slice(braceAt + 1, close), start: m.index, end: close + 1 })\n re.lastIndex = close + 1\n }\n return found\n}\n\n/**\n * Assemble the blocks into ONE program.\n *\n * A document has exactly one `scene`, one header and one behavior half, so composing gestures is not a\n * matter of concatenating their output — it is a matter of never producing whole programs in the first\n * place. Each block contributes parts; this puts them in the three places the format has.\n *\n * Two things the assembly decides, and they are the reason \"several blocks\" is a design and not a loop:\n *\n * • **Names are prefixed by the block's name, always** — including when a document holds one block.\n * Prefixing only on collision would mean the same source compiles to different names depending on\n * whether a sibling exists, so a skin written against one block would break the day a second arrived.\n * Every id is in `meta[].objects`, so nothing has to be guessed.\n * • **`completed` belongs to the DOCUMENT.** Each block emits `part` with its own index when its\n * portion is finished; `completed` fires once, when every block is done. With a single block the two\n * coincide, which is what `completed` always meant.\n */\nfunction assemble(blocks: { expansion: Expansion }[], under: string[], over: string[], doc: DocumentSpec): string {\n const out: string[] = [`size ${doc.width} ${doc.height}`, `timeline ${doc.fps} ${doc.durationFrames}`]\n const done = blocks.filter((b) => !b.expansion.meta.control).map((b) => b.expansion.meta.doneVar) // a control has no end\n if (done.length) out.push('var allDone = 0')\n for (const b of blocks) out.push(...b.expansion.vars)\n\n out.push('', 'scene {')\n if (under.length) out.push(' // -- raw scene under (verbatim, drawn BEHIND every block) --', ...under)\n for (const b of blocks) out.push(...b.expansion.layers)\n if (over.length) out.push(' // -- raw scene (verbatim, drawn ON TOP of every block) --', ...over)\n out.push('}', '')\n\n for (const b of blocks) out.push(...b.expansion.behavior)\n // `completed` = the whole document. One block: it fires the moment that block does, which is what it\n // has always meant. Several: only once all of them have. The `allDone` guard matters -- an unguarded\n // `send` inside `every frame` would emit it again on every frame for the rest of the session.\n if (done.length) {\n out.push('every frame {', ' if allDone < 0.5 {', ` if ${done.join(' + ')} >= ${done.length} {`, ' allDone = 1', ' send \"completed\"', ' }', ' }', '}')\n }\n return out.join('\\n')\n}\n\n/** Block keywords that belong to FlatINK itself — a source opening with one of these is a program. */\nconst FLATINK_BLOCKS = new Set(['scene', 'object', 'symbol', 'layer', 'group', 'match', 'each', 'states', 'params', 'when', 'every', 'repeat', 'fn', 'at'])\n\n/**\n * Expand a sugar source into `.flatink`.\n *\n * Plain FlatInk passes through untouched. A block nobody claims RAISES rather than passing through: a\n * silent fall-through sent the author's block downstream as if it were FlatInk, to fail a hundred lines\n * later on messages that only talk about FlatInk.\n *\n * A document may hold SEVERAL blocks. They are laid out in the order they are written, each keeps its own\n * state under its own name, and each emits `part` when its portion is finished; `completed` fires once,\n * when every block is done.\n *\n * ```ts\n * import { desugar } from '@flatkit/sugarflat'\n * import { checkProgram } from '@flatkit/compiler'\n *\n * const { flatink, meta } = desugar(srcFromAnLLM)\n * const { ok, report } = checkProgram(flatink) // the expansion is the artefact of record\n * ```\n */\nexport function desugar(src: string, opts: DesugarOptions = {}): DesugarResult {\n const doc = opts.document ?? DEFAULT_DOCUMENT\n const gestures = opts.gestures ?? GESTURES\n\n const found = findBlocks(src, gestures)\n if (!found.length) {\n // Plain FlatInk, plus any `raw { … }` used on its own. `raw scene` has no generated scene to splice\n // into here, so it would silently vanish -- say so instead.\n const parts = takeRaw(src)\n if (parts.over.length || parts.under.length) {\n throw new Error('`raw scene { … }` needs a gesture block to splice into — this source has none. Write a plain `scene { … }` yourself.')\n }\n const out = [parts.rest.trim(), ...(parts.top.length ? [RAW_NOTE, ...parts.top] : [])].filter(Boolean).join('\\n')\n return { flatink: parts.top.length ? out : src, kind: parts.top.length ? 'raw' : null, expanded: parts.top.length > 0, meta: [] }\n }\n\n const seen = new Set<string>()\n for (const b of found) {\n const key = ident(b.name)\n if (seen.has(key)) throw new DuplicateBlockError(b.name)\n seen.add(key)\n }\n\n // Everything OUTSIDE the blocks travels with them: `raw` in its three forms, and any plain FlatInk the\n // author wrote beside them. Dropping it made the escape hatch a no-op -- decor was written, the\n // expansion compiled, `checkProgram` said ok, and nothing was there.\n const outside: RawParts = { top: [], over: [], under: [], rest: '' }\n const absorb = (text: string) => {\n const part = takeRaw(text)\n outside.top.push(...part.top)\n outside.over.push(...part.over)\n outside.under.push(...part.under)\n outside.rest += part.rest\n }\n let cursor = 0\n for (const b of found) { absorb(src.slice(cursor, b.start)); cursor = b.end }\n absorb(src.slice(cursor))\n\n const blocks = found.map((b, index) => {\n const prefix = `${ident(b.name)}_`\n return { expansion: b.gesture.expand(b.name, b.body, doc, { index, prefix, doneVar: `${prefix}done` }) }\n })\n\n // Top-level content goes in the HEADER position, wherever the author wrote it: `var` (like `asset`,\n // `use`, `def`) is a header declaration and a parse error after `scene`, while `object`/`fn` are legal\n // on either side. Moving an `object` up changes nothing -- behavior binds by name, not by position.\n const header = [outside.rest.trim(), ...(outside.top.length ? [RAW_NOTE, ...outside.top] : [])].filter(Boolean).join('\\n')\n const program = assemble(blocks, outside.under, outside.over, doc)\n return {\n flatink: ensureHeader(header ? `${header}\\n\\n${program}` : program, doc),\n kind: found.map((b) => b.gesture.keyword).join('+'),\n expanded: true,\n meta: blocks.map((b) => b.expansion.meta),\n }\n}\n"],"mappings":";AAsBO,IAAM,mBAAiC,EAAE,OAAO,KAAK,QAAQ,KAAK,KAAK,IAAI,gBAAgB,IAAI;AAG/F,IAAM,gBAAgB,CAAC,QAAyB,qCAAqC,KAAK,GAAG;AAG7F,IAAM,oBAAoB,CAAC,QAAyB,yBAAyB,KAAK,GAAG;AAOrF,SAAS,aAAa,SAAiB,MAAoB,kBAA0B;AAC1F,QAAM,OAAiB,CAAC;AACxB,MAAI,CAAC,cAAc,OAAO,EAAG,MAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,IAAI,MAAM,EAAE;AACxE,MAAI,CAAC,kBAAkB,OAAO,EAAG,MAAK,KAAK,YAAY,IAAI,GAAG,IAAI,IAAI,cAAc,EAAE;AACtF,SAAO,KAAK,SAAS,GAAG,KAAK,KAAK,IAAI,CAAC;AAAA,EAAK,OAAO,KAAK;AAC1D;;;ACzBO,SAAS,MAAM,OAAuB;AAC3C,QAAM,QAAQ,MAAM,UAAU,KAAK,EAAE,QAAQ,mBAAmB,EAAE,EAAE,QAAQ,kBAAkB,GAAG;AACjG,SAAO,SAAS,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK;AAC9C;AAGO,IAAM,UAAU,CAAC,UAA2B,MAAM,KAAK,MAAM,SAAS,MAAM,SAAS;;;ACUrF,IAAM,QAAgB,CAAC,QAAQ,UAAU,QAAQ,QAAQ,SAAS;AAEzE,IAAM,QAAgD;AAAA,EACpD,MAAM,EAAE,GAAG,IAAI,GAAG,GAAG;AAAA,EACrB,QAAQ,EAAE,GAAG,KAAK,GAAG,IAAI;AAAA,EACzB,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;AAAA,EACvB,MAAM,EAAE,GAAG,IAAI,GAAG,GAAG;AAAA,EACrB,SAAS,EAAE,GAAG,KAAK,GAAG,GAAG;AAC3B;AAOO,IAAM,QAAe;AAAA,EAC1B,MAAM;AAAA,EACN,MAAM,CAAC,SAAS,MAAM,IAAI;AAAA,EAC1B,MAAM,MAAM,CAAC;AACf;AAGA,IAAM,QAAQ,CAAC,GAAW,GAAW,MAAsB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AAO3F,IAAM,UAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,MAAM,CAAC,SAAS,MAAM,IAAI;AAAA,EAC1B,KAAK,MAAM,OAAO,MAAM;AACtB,UAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,MAAM,IAAI;AACnC,UAAM,OAAO,CAACA,OAAc,MAAc,MAAc,QAAgB,MACtE,SAAS,MAAM,QAAQ,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,sCAAsCA,KAAI,gCAAgC,MAAM,QAAQ,IAAI,IAAI,IAAI;AAC7J,YAAQ,MAAM;AAAA;AAAA;AAAA,MAGZ,KAAK;AACH,eAAO,CAAC,cAAc,IAAI,IAAI,CAAC,iBAAiB,cAAc,IAAI,IAAI,CAAC,4BAA4B,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,GAAG,CAAC;AAAA,MACxI,KAAK;AACH,eAAO,CAAC,GAAG,MAAM,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC,iBAAiB,GAAG,MAAM,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC,qCAAqC,KAAK,IAAI,IAAI,IAAI,IAAI,WAAW,CAAC,IAAI,IAAI,EAAE,CAAC;AAAA,MAChK,KAAK;AACH,eAAO,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC,iBAAiB,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC,4BAA4B,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,WAAW,CAAC,IAAI,IAAI,EAAE,CAAC;AAAA,MAC3I,KAAK;AACH,eAAO,CAAC,cAAc,IAAI,IAAI,CAAC,iBAAiB,cAAc,IAAI,IAAI,CAAC,4BAA4B,KAAK,IAAI,IAAI,IAAI,IAAI,WAAW,GAAG,CAAC;AAAA,MACzI,KAAK;AACH,eAAO,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC,iBAAiB,KAAK,IAAI,IAAI,IAAI,IAAI,WAAW,GAAG,CAAC;AAAA,IACnF;AAAA,EACF;AACF;;;AC5DA,IAAM,IAAI,CAAC,MAAc,EAAE,QAAQ,MAAM,GAAG;AAG5C,SAAS,UAAU,MAAc,GAAW,GAAW,KAAe,QAA6C;AACjH,QAAM,MAAM,SAAS,WAAW,OAAO,CAAC,IAAI,OAAO,CAAC,KAAK;AACzD,SAAO,CAAC,cAAc,IAAI,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,MAAM,uBAAuB,GAAG,IAAI,IAAI,CAAC,MAAM,WAAW,CAAC,EAAE,GAAG,WAAW,OAAO;AAC5I;AAQA,SAAS,SAAS,GAAW,OAAkI;AAC7J,QAAM,IAAI,MAAM;AAChB,MAAI,IAAI,EAAG,QAAO;AAClB,QAAM,KAAK,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;AACxE,SAAO;AAAA,IACL,MAAM,CAAC,OAAO,EAAE,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,OAAO,EAAE,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,OAAO,CAAC,QAAQ,OAAO,CAAC,MAAM;AAAA,IAC/I,MAAM;AAAA,MACJ,YAAY,CAAC,cAAc,IAAI,CAAC;AAAA,MAChC,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,MAAM,CAAC;AAAA,MAC/C,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,MAAK,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;AAAA,MAAK,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;AAAA,MACjF,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,MAAK,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;AAAA,MAAK,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC;AAAA,MACjF;AAAA,IACF;AAAA,IACA,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC;AAAA,IACpB,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC;AAAA,EACtB;AACF;AAGA,IAAM,QAAQ;AACd,IAAM,QAAQ,IAAI,OAAO,OAAO,KAAK,mBAAmB,KAAK,MAAM;AAOnE,SAAS,MAAM,MAAc,MAAc,MAAwB;AACjE,QAAM,MAAM,KAAK,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,WAAW,EAAE,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACvF,aAAW,QAAQ,KAAK;AACtB,QAAI,CAAC,MAAM,KAAK,IAAI,EAAG;AACvB,UAAM,OAAO,KAAK,MAAM,QAAQ,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,OAAO,OAAO,EAAE,EAAE,EAAE,KAAK,IAAI;AACpF,UAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI;AAAA,IAAuF,IAAI,IAAI,IAAI;AAAA,EAAO,IAAI;AAAA,IAAO;AAAA,EACvJ;AACA,SAAO;AACT;AAeA,SAAS,WAAW,MAAc,MAAc,GAAuB;AACrE,QAAM,IAAgB,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,CAAC,GAAG,OAAO,CAAC,EAAE;AAI3E,QAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAM,MAAM,CAAC,SAAyB;AACpC,QAAI,KAAK;AACT,aAAS,IAAI,GAAG,MAAM,IAAI,EAAE,GAAG,IAAK,MAAK,GAAG,IAAI,IAAI,CAAC;AACrD,UAAM,IAAI,EAAE;AACZ,WAAO;AAAA,EACT;AACA,QAAM,WAAW,oBAAI,IAAoB;AACzC,aAAW,QAAQ,MAAM,SAAS,MAAM,IAAI,GAAG;AAC7C,QAAI;AACJ,QAAK,IAAI,KAAK,MAAM,mBAAmB,EAAI,GAAE,SAAS,EAAE,CAAC;AAAA,aAChD,SAAS,UAAW,GAAE,UAAU;AAAA,aAC/B,IAAI,KAAK,MAAM,gCAAgC,EAAI,GAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE;AAAA,aACrG,IAAI,KAAK,MAAM,uFAAuF,GAAI;AAClH,YAAM,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC;AACzB,UAAI,SAAS,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,UAAU,IAAI,gCAAgC,KAAK,mDAA8C;AAC1I,YAAM,KAAK,IAAI,GAAG,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE;AACrC,eAAS,IAAI,OAAO,EAAE;AAGtB,QAAE,QAAQ,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,EAAG,CAAC;AAAA,IACjH,WAAW,wBAAwB,KAAK,IAAI,GAAG;AAC7C,YAAM,IAAI,MAAM,UAAU,IAAI,wIAAmI,IAAI,EAAE;AAAA,IACzK,WAAY,IAAI,KAAK,MAAM,sFAAsF,GAAI;AACnH,YAAM,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC;AACzB,QAAE,MAAM,KAAK,EAAE,IAAI,IAAI,GAAG,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE,GAAG,OAAO,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;AAAA,IACjG,MAAO,OAAM,IAAI,MAAM,UAAU,IAAI,yBAAyB,IAAI,EAAE;AAAA,EACtE;AACA,MAAI,CAAC,EAAE,QAAQ,OAAQ,OAAM,IAAI,MAAM,UAAU,IAAI,+CAA0C;AAC/F,MAAI,CAAC,EAAE,MAAM,OAAQ,OAAM,IAAI,MAAM,UAAU,IAAI,iDAA4C;AAE/F,aAAW,MAAM,EAAE,OAAO;AAGxB,UAAM,SAAS,EAAE,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,KAAK,MAAM,MAAM,GAAG,MAAM,CAAC;AAC1E,UAAM,KAAK,SAAS,IAAI,GAAG,MAAM,MAAM,OAAO,WAAW,IAAI,OAAO,CAAC,EAAE,KAAK;AAC5E,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,UAAU,IAAI,YAAY,GAAG,KAAK,+BAA+B;AAC1F,OAAG,SAAS;AAAA,EACd;AACA,SAAO;AACT;AAIA,IAAM,YAAY,CAAC,UAA0B,QAAQ;AAErD,SAAS,YAAY,MAAc,MAAc,OAAc,KAAgC;AAC7F,QAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,IAAI;AACzC,QAAM,IAAI,WAAW,MAAM,MAAM,CAAC;AAClC,QAAM,IAAI,EAAE,MAAM;AAClB,QAAM,OAAO,CAAC,MAAM,CAAC,iBAAY,EAAE,IAAI,CAAC,KAAK,GAAI,EAAE,SAAS,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,GAAI,OAAO,CAAC,gBAAgB,OAAO,OAAO,MAAM;AAC7I,aAAW,MAAM,EAAE,MAAO,MAAK,KAAK,OAAO,GAAG,EAAE,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,YAAY;AAClH,QAAM,KAAK,EAAE,UAAU,SAAS,GAAG,EAAE,KAAK,IAAI;AAC9C,MAAI,GAAI,MAAK,KAAK,GAAG,GAAG,IAAI;AAG5B,QAAM,MAAM,GAAG,CAAC;AAChB,OAAK,KAAK,OAAO,GAAG,MAAM;AAE1B,QAAM,YAAY,UAAU,CAAC,IAAI,GAAG,cAAc,UAAU,CAAC,IAAI;AAEjE,QAAM,MAAM,CAAC,IAAiC,GAAW,GAAkC,QAA0B,EAAE,OAAO,GAAG,SAC7H;AAAA,IACE,GAAG,GAAG,MAAM,GAAG,EAAE;AAAA,IACjB,GAAG,GAAG,KAAK,GAAG,EAAE;AAAA,IAAc,GAAG,GAAG,KAAK,CAAC,cAAc,CAAC;AAAA;AAAA;AAAA,IAGzD,GAAG,GAAG,KAAK,GAAG,EAAE,OAAO,EAAE,EAAE;AAAA,IAAM,GAAG,GAAG,KAAK,GAAG,EAAE,OAAO,EAAE,EAAE;AAAA,IAC5D,GAAG,GAAG,QAAQ,GAAG,OAAO,IAAI,CAAC;AAAA,IAAM,GAAG,GAAG,OAAO,GAAG;AAAA,IAAQ,GAAG,GAAG;AAAA;AAAA,IACjE,GAAG,GAAG,+BAA+B,CAAC,YAAY,CAAC;AAAA,IACnD,GAAG,GAAG,QAAQ,CAAC,eAAe,CAAC;AAAA,IAAM,GAAG,GAAG,OAAO,OAAO;AAAA,IAAQ,GAAG,GAAG,8BAA8B,CAAC;AAAA,IAAM,GAAG,GAAG;AAAA,IAClH,GAAG,GAAG;AAAA,EACR,IACA;AAAA,IACE,GAAG,GAAG,GAAG,GAAG,EAAE,OAAO,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC;AAAA,IAAI,GAAG,GAAG,GAAG,GAAG,EAAE,OAAO,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC;AAAA;AAAA,IACpF,GAAG,GAAG,+BAA+B,CAAC,YAAY,CAAC;AAAA,EACrD;AAEJ,QAAM,SAAS,CAAC,YAAY,CAAC,YAAY;AACzC,aAAW,KAAK,EAAE,SAAS;AACzB,UAAM,OAAO,EAAE,QAAQ,EAAE,cAAc,MAAM,KAAK,QAAQ;AAC1D,WAAO,KAAK,GAAG,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,KAAK,UAAU,EAAE,OAAO,IAAI,GAAG,IAAI,CAAC;AAAA,EACrF;AACA,SAAO,KAAK,OAAO,YAAY,CAAC,UAAU;AAC1C,aAAW,MAAM,EAAE,MAAO,QAAO,KAAK,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,MAAM,KAAK,QAAQ,GAAG,KAAK,CAAC,CAAC;AACnG,SAAO,KAAK,KAAK;AAEjB,QAAM,WAAqB,CAAC;AAC5B,MAAI,GAAI,UAAS,KAAK,iBAAiB,GAAG,GAAG,MAAM,GAAG,EAAE,MAAM,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,EAAE,OAAO,GAAG,EAAE,CAAC,CAAC,IAAI,KAAK,GAAG,EAAE,OAAO,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE;AACnJ,IAAE,MAAM,QAAQ,CAAC,IAAI,MAAM;AACzB,aAAS,KAAK,WAAW,GAAG,EAAE,OAAO,qBAAqB,SAAS,EAAE;AACrE,aAAS,KAAK,UAAU,GAAG,EAAE,MAAM,GAAG,EAAE,eAAe,GAAG,EAAE,eAAe;AAC3E,aAAS,KAAK,oBAAoB,UAAU,GAAG,EAAE,kBAAkB,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,SAAS,KAAK;AAE5G,eAAW,KAAK,CAAC,GAAG,EAAE,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,EAAG,UAAS,KAAK,qBAAqB,EAAE,EAAE,iBAAiB,GAAG,IAAI,IAAI,GAAG,GAAG,MAAM,GAAG,KAAK;AACzM,aAAS,KAAK,SAAS,GAAG,EAAE,KAAK,SAAS,GAAG,EAAE,GAAG;AAGlD,aAAS,KAAK,cAAc,GAAG,OAAO,IAAI,CAAC,eAAe,cAAc,GAAG,OAAO,IAAI,CAAC,eAAe,KAAK,EAAE;AAAA,EAC/G,CAAC;AACD,aAAW,KAAK,EAAE,SAAS;AACzB,aAAS,KAAK,WAAW,EAAE,EAAE,OAAO,qBAAqB,WAAW,IAAI,kBAAkB;AAE1F,MAAE,MAAM,QAAQ,CAAC,IAAI,MAAM,SAAS,KAAK,UAAU,GAAG,OAAO,IAAI,CAAC,OAAO,GAAG,EAAE,kBAAkB,SAAS,GAAG,QAAQ,GAAG,IAAI,IAAI,GAAG,GAAG,QAAQ,GAAG,OAAO,CAAC;AACxJ,aAAS,KAAK,OAAO,KAAK,EAAE;AAAA,EAC9B;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,EAAE,SAAS,SAAS,MAAM,QAAQ,EAAE,QAAQ,OAAO,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,EAAE,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,SAAS,GAAI,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC,EAAG;AAAA,EAChO;AACF;AAMA,SAAS,cAAc,MAAc,MAAc,OAAc,KAAgC;AAC/F,QAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,IAAI;AACzC,MAAI,SAAS,GAAG,QAAQ;AACxB,QAAM,QAAmE,CAAC;AAC1E,MAAI,SAAS;AACb,MAAI,UAAU;AACd,MAAI,OAAO;AACX,MAAI;AAGJ,QAAM,MAAM;AACZ,aAAW,QAAQ,MAAM,WAAW,MAAM,IAAI,GAAG;AAC/C,QAAI;AACJ,QAAK,IAAI,KAAK,MAAM,mBAAmB,EAAI,UAAS,EAAE,CAAC;AAAA,aAC9C,SAAS,UAAW,WAAU;AAAA,aAC7B,IAAI,KAAK,MAAM,IAAI,OAAO,aAAa,GAAG,GAAG,CAAC,GAAI;AAAE,cAAQ,EAAE,CAAC;AAAG,eAAS,OAAO,EAAE,CAAC,CAAC;AAAA,IAAE,WACxF,IAAI,KAAK,MAAM,IAAI,OAAO,YAAY,GAAG,sDAAsD,CAAC,EAAI,OAAM,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,GAAI,EAAE,CAAC,MAAM,SAAY,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAI,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;AAAA,aAChM,IAAI,KAAK,MAAM,iBAAiB,EAAI,QAAO,EAAE,CAAC;AAAA,aAC9C,IAAI,KAAK,MAAM,wCAAwC,EAAI,WAAU,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;AAAA,QAC7F,OAAM,IAAI,MAAM,YAAY,IAAI,yBAAyB,IAAI,EAAE;AAAA,EACtE;AAGA,QAAM,SAAS,KAAK,IAAI,GAAG,GAAG,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,MAAM,CAAC;AAC9G,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,YAAY,IAAI,sDAAiD;AAC9F,MAAI,CAAC,MAAM,OAAQ,OAAM,IAAI,MAAM,YAAY,IAAI,kDAA6C;AAEhG,QAAM,OAAO,CAAC,MAAM,CAAC,mBAAc,EAAE,IAAI,CAAC,KAAK,GAAI,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,GAAI,OAAO,CAAC,aAAa,OAAO,OAAO,MAAM;AACxI,QAAM,SAAS,CAAC,YAAY,CAAC,UAAU;AACvC,QAAM,QAAQ,CAAC,GAAG,MAAM,OAAO,KAAK,GAAG,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,KAAK,QAAQ,EAAE,UAAU,OAAO,GAAG,EAAE,KAAK,IAAI,IAAI,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;AACpK,MAAI,SAAS;AAGX,UAAM,OAAO,MAAM,KAAK,SAAS,KAAK,EAAE,GAAG,KAAK,GAAG,GAAG;AACtD,UAAM,OAAO,MAAM,KAAK,WAAW,QAAQ,KAAK,GAAG,OAAO,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,MAAO,EAAE,WAAW,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,SAAS,aAAa,MAAM,KAAK,EAAE,KAAK,CAAE;AAChM,WAAO,KAAK,GAAG,UAAU,GAAG,CAAC,SAAS,QAAQ,GAAG,QAAQ,GAAG,GAAG,CAAC;AAAA,EAClE;AACA,SAAO,KAAK,KAAK;AAEjB,QAAM,KAAK,UAAU,SAAS,GAAG,KAAK,IAAI;AAC1C,MAAI,GAAI,MAAK,KAAK,GAAG,GAAG,IAAI;AAC5B,QAAM,WAAqB,CAAC;AAC5B,MAAI,GAAI,UAAS,KAAK,iBAAiB,GAAG,GAAG,MAAM,KAAK,EAAE;AAC1D,QAAM,QAAQ,CAAC,GAAG,MAAM;AACtB,aAAS,KAAK,WAAW,CAAC,IAAI,CAAC,OAAO,qBAAqB,UAAU,CAAC,IAAI,CAAC,IAAI,GAAI,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,IAAI,SAAS,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,GAAI,oBAAoB,UAAU,OAAO,UAAU;AACvL,UAAM,MAAM,SAAS,UAAU,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,WAAW,EAAE,KAAK;AAC7F,aAAS,KAAK,YAAY,GAAG,MAAM,KAAK,IAAI;AAC5C,aAAS,KAAK,WAAW,CAAC,aAAa,uCAAuC,CAAC,YAAY,CAAC,IAAI;AAChG,aAAS,KAAK,gBAAgB;AAC9B,aAAS,KAAK,WAAW,CAAC,WAAW,GAAG,IAAI,qCAAqC,CAAC,YAAY,CAAC,IAAI;AACnG,aAAS,KAAK,cAAc,CAAC,YAAY,KAAK,MAAM,aAAa,OAAO,QAAQ,oCAAoC,CAAC,MAAM,WAAW;AACtI,aAAS,KAAK,WAAW,SAAS,OAAO,KAAK,EAAE;AAAA,EAClD,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,EAAE,SAAS,WAAW,MAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,GAAG,SAAS,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,GAAI,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC,EAAG;AAAA,EACjL;AACF;AAOA,SAAS,YAAY,MAAc,MAAc,OAAc,KAAgC;AAC7F,QAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,IAAI;AACzC,MAAI,SAAS;AACb,MAAI,UAAU;AACd,QAAM,QAAmD,CAAC;AAC1D,aAAW,QAAQ,MAAM,SAAS,MAAM,IAAI,GAAG;AAC7C,QAAI;AACJ,QAAK,IAAI,KAAK,MAAM,mBAAmB,EAAI,UAAS,EAAE,CAAC;AAAA,aAC9C,SAAS,UAAW,WAAU;AAAA,aAC7B,IAAI,KAAK,MAAM,8CAA8C,EAAI,OAAM,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;AAAA,QAClH,OAAM,IAAI,MAAM,UAAU,IAAI,yBAAyB,IAAI,EAAE;AAAA,EACpE;AACA,MAAI,CAAC,MAAM,OAAQ,OAAM,IAAI,MAAM,UAAU,IAAI,yCAAoC;AAErF,QAAM,OAAO,CAAC,MAAM,CAAC,iBAAY,EAAE,IAAI,CAAC,KAAK,GAAI,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,GAAI,OAAO,CAAC,YAAY,OAAO,OAAO,MAAM;AACrI,QAAM,SAAS,CAAC,YAAY,CAAC,UAAU;AACvC,QAAM,QAAQ,CAAC,GAAG,MAAM,OAAO,KAAK,GAAG,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,KAAK,QAAQ,EAAE,KAAK,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;AACzH,SAAO,KAAK,KAAK;AAGjB,QAAM,KAAK,UAAU,SAAS,GAAG,KAAK,IAAI;AAC1C,MAAI,GAAI,MAAK,KAAK,GAAG,GAAG,IAAI;AAC5B,QAAM,WAAqB,CAAC;AAC5B,MAAI,GAAI,UAAS,KAAK,iBAAiB,GAAG,GAAG,MAAM,KAAK,EAAE;AAC1D,QAAM,QAAQ,CAAC,IAAI,MAAM;AAEvB,aAAS,KAAK,WAAW,CAAC,IAAI,CAAC,OAAO,qBAAqB,UAAU,CAAC,IAAI,IAAI,CAAC,IAAI,GAAI,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,IAAI,SAAS,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,GAAI,oBAAoB,UAAU,CAAC,WAAW,CAAC,MAAM,SAAS,CAAC,UAAU,CAAC,YAAY,gCAAgC,CAAC,YAAY,CAAC,IAAI;AAChR,QAAI,MAAM,MAAM,SAAS,EAAG,UAAS,KAAK,SAAS,OAAO,QAAQ,gCAAgC,CAAC,IAAI;AACvG,aAAS,KAAK,OAAO;AACrB,QAAI,IAAI,EAAG,UAAS,KAAK,UAAU,CAAC,UAAU,CAAC,MAAM,qCAAqC,CAAC,YAAY,CAAC,MAAM,OAAO;AACrH,aAAS,KAAK,KAAK;AAGnB,aAAS,KAAK,eAAe,CAAC,WAAW,CAAC,eAAe,KAAK,EAAE;AAAA,EAClE,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,EAAE,SAAS,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,GAAG,SAAS,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,GAAI,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC,EAAG;AAAA,EAC/K;AACF;AAMA,IAAM,SAAS;AAEf,SAAS,MAAM,MAAc,MAAc,KAA0J;AACnM,MAAI,IAAI,QAAQ,UAAa,IAAI,QAAQ,OAAW,OAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,yEAAoE;AACxJ,MAAI,OAAO,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,WAAW,IAAI,GAAG,wBAAwB,IAAI,GAAG,GAAG;AAC5H,QAAM,OAAO,IAAI,QAAQ;AACzB,MAAI,OAAO,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,yBAAyB;AAChF,QAAM,QAAQ,IAAI,SAAS,IAAI;AAC/B,MAAI,OAAO,KAAK,IAAI,OAAO,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI,OAAO,IAAI,GAAG,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,aAAa,KAAK,gBAAgB,IAAI,GAAG,KAAK,IAAI,GAAG,EAAE;AAC/J,QAAM,SAAS,KAAK,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,MAAM,CAAC;AACxG,SAAO,EAAE,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,MAAM,OAAO,QAAQ,GAAG,MAAM,OAAO;AAC5E;AAUA,SAAS,SAAS,GAAW,GAAW,GAAW,GAAwH;AACzK,QAAM,MAAM,EAAE,SAAS,UAAU,CAAC,UAAU,EAAE,IAAI,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,GAAG,CAAC,UAAU,EAAE,IAAI;AAC/F,SAAO;AAAA,IACL,MAAM,CAAC,OAAO,CAAC,UAAU,OAAO,CAAC,YAAY,OAAO,CAAC,YAAY,OAAO,CAAC,eAAe,OAAO,CAAC,UAAU;AAAA,IAC1G,IAAI;AAAA,MACF,MAAM,CAAC;AAAA,MACP,KAAK,CAAC,cAAc,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG;AAAA,MAC3C,QAAQ,CAAC,SAAS,CAAC;AAAA,MAAM,OAAO,CAAC,MAAM,CAAC;AAAA,MAAM,gCAAgC,CAAC,aAAa,CAAC;AAAA,MAAM;AAAA,MACnG;AAAA,MAAK;AAAA,IAAE;AAAA,IACT,QAAQ,CAAC,IAAI,GAAG,UAAU;AAAA,MACxB,WAAW,EAAE;AAAA,MAAO,qBAAqB,KAAK;AAAA;AAAA;AAAA,MAG9C;AAAA,MAAoB,OAAO,CAAC,UAAU,CAAC;AAAA,MAAK,OAAO,CAAC,UAAU,CAAC;AAAA,MAAI,OAAO,CAAC;AAAA,MAAsB,OAAO,CAAC;AAAA,MAAe;AAAA,MACxH;AAAA,MAAqB,OAAO,CAAC;AAAA,MAAY,OAAO,CAAC;AAAA,MAAgB;AAAA,MACjE;AAAA,MAAoB,UAAU,CAAC;AAAA,MAAkB,SAAS,CAAC;AAAA,MAAe;AAAA,MAAgB,SAAS,CAAC,UAAU,CAAC;AAAA,MAAK;AAAA,MAAS;AAAA;AAAA,MAE7H,eAAe,IAAI,IAAI,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE;AAAA,MAAe;AAAA,MAAK;AAAA,IAAE;AAAA,IACtF,MAAM;AAAA,MACJ,QAAQ,CAAC,yBAAyB,CAAC;AAAA,MAAU,OAAO,CAAC,UAAU,CAAC;AAAA,MAAS,OAAO,CAAC;AAAA,MAAuB;AAAA;AAAA,MAExG,QAAQ,CAAC,gBAAgB,CAAC,2BAA2B,CAAC;AAAA,MAAiB,OAAO,CAAC;AAAA,MAAe;AAAA,IAAK;AAAA,EACvG;AACF;AAGA,SAAS,QAAQ,GAAW,GAAW,GAAU,IAAQ,OAAwB;AAC/E,QAAM,OAAO,MAAM,KAAK,SAAS,KAAK,EAAE,GAAG,KAAK,GAAG,GAAG;AACtD,QAAM,OAAO,MAAM,KAAK,WAAW,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,MAAO,EAAE,WAAW,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,MAAM,KAAK,EAAE,KAAK,CAAE;AAC3J,SAAO,UAAU,GAAG,CAAC,SAAS,GAAG,GAAG,GAAG,GAAG,GAAG;AAC/C;AAEA,IAAM,KAAK;AAEX,SAAS,cAAc,MAAc,MAAc,OAAc,KAAgC;AAC/F,QAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,IAAI;AAChC,QAAM,MAAqE,CAAC;AAC5E,MAAI,SAAS;AACb,MAAI,OAAuB,MAAsB;AACjD,aAAW,QAAQ,MAAM,WAAW,MAAM,IAAI,GAAG;AAC/C,QAAI;AACJ,QAAK,IAAI,KAAK,MAAM,mBAAmB,EAAI,UAAS,EAAE,CAAC;AAAA,aAC7C,IAAI,KAAK,MAAM,IAAI,OAAO,4BAA4B,MAAM,GAAG,CAAC,EAAI,KAAI,EAAE,CAAC,CAAU,IAAI,EAAE,CAAC;AAAA,aAC5F,IAAI,KAAK,MAAM,IAAI,OAAO,kCAAkC,EAAE,GAAG,CAAC,GAAI;AAC9E,YAAM,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;AAC9B,UAAI,EAAE,CAAC,MAAM,QAAS,SAAQ;AAAA,eAAa,EAAE,CAAC,MAAM,OAAQ,QAAO;AAAA,UAAS,WAAU;AAAA,IACxF,MAAO,OAAM,IAAI,MAAM,YAAY,IAAI,yBAAyB,IAAI,EAAE;AAAA,EACxE;AACA,QAAM,IAAI,MAAM,WAAW,MAAM,GAAG;AACpC,MAAI,CAAC,SAAS,CAAC,KAAM,OAAM,IAAI,MAAM,YAAY,IAAI,yEAAoE;AAEzH,QAAM,IAAI,GAAG,CAAC;AACd,QAAM,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC;AAC9B,QAAM,OAAO,CAAC,MAAM,CAAC,mBAAc,EAAE,IAAI,CAAC,KAAK,GAAI,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,GAAI,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,GAAG,GAAG,IAAI;AAClI,QAAM,SAAS;AAAA,IAAC,YAAY,CAAC;AAAA,IAC3B,GAAG,UAAU,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,MAAM,KAAK,MAAM,CAAC;AAAA,IAC5F,GAAG,UAAU,GAAG,CAAC,QAAQ,KAAK,GAAG,KAAK,GAAG,MAAM,KAAK,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,CAAC;AAAA,IACpF,GAAI,UAAU,QAAQ,GAAG,GAAG,GAAG,SAAS,KAAK,IAAI,CAAC;AAAA,IAClD;AAAA,EAAK;AACP,QAAM,WAAW,CAAC,GAAG,GAAG,IAAI,GAAG,GAAG,OAAO,GAAG,CAAC,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,iBAAiB,GAAG,GAAG,MAAM,KAAK,EAAE;AAChK,SAAO;AAAA,IACL;AAAA,IAAM;AAAA,IAAQ;AAAA,IACd,MAAM,EAAE,SAAS,WAAW,MAAM,QAAQ,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,QAAQ,GAAI,UAAU,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAE,GAAG,SAAS,IAAI,SAAS,KAAK;AAAA,EACtK;AACF;AAIA,SAAS,aAAa,MAAc,MAAc,OAAc,KAAgC;AAC9F,QAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,IAAI;AAChC,QAAM,MAAqE,CAAC;AAC5E,MAAI,SAAS;AACb,MAAI;AACJ,MAAI,OAAuB,MAAsB;AACjD,aAAW,QAAQ,MAAM,UAAU,MAAM,IAAI,GAAG;AAC9C,QAAI;AACJ,QAAK,IAAI,KAAK,MAAM,mBAAmB,EAAI,UAAS,EAAE,CAAC;AAAA,aAC7C,IAAI,KAAK,MAAM,IAAI,OAAO,4BAA4B,MAAM,GAAG,CAAC,EAAI,KAAI,EAAE,CAAC,CAAU,IAAI,EAAE,CAAC;AAAA,aAC5F,IAAI,KAAK,MAAM,IAAI,OAAO,YAAY,EAAE,aAAa,EAAE,GAAG,CAAC,EAAI,QAAO,EAAE,IAAI,OAAO,EAAE,CAAC,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC,CAAC,EAAE;AAAA,aAC/I,IAAI,KAAK,MAAM,IAAI,OAAO,kCAAkC,EAAE,GAAG,CAAC,GAAI;AAC9E,YAAM,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE;AAC9B,UAAI,EAAE,CAAC,MAAM,QAAS,SAAQ;AAAA,eAAa,EAAE,CAAC,MAAM,OAAQ,QAAO;AAAA,UAAS,WAAU;AAAA,IACxF,MAAO,OAAM,IAAI,MAAM,WAAW,IAAI,yBAAyB,IAAI,EAAE;AAAA,EACvE;AACA,QAAM,IAAI,MAAM,UAAU,MAAM,GAAG;AACnC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,WAAW,IAAI,kFAA6E;AACvH,QAAM,aAAa,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK;AAC3D,MAAI,CAAC,cAAc,EAAE,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,IAAK,OAAM,IAAI,MAAM,WAAW,IAAI,yGAAoG;AACrM,MAAI,CAAC,CAAC,UAAU,CAAC,CAAC,KAAM,OAAM,IAAI,MAAM,WAAW,IAAI,yEAAoE;AAE3H,QAAM,IAAI,GAAG,CAAC;AACd,QAAM,KAAK,SAAS,OAAO,SAAS,GAAG,GAAG,GAAG,CAAC,IAAI;AAClD,QAAM,CAAC,IAAI,EAAE,IAAI,aAAa,CAAC,KAAK,IAAI,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,EAAE;AACpE,QAAM,MAAM,KAAK,IAAI,KAAK,EAAE;AAC5B,QAAM,OAAO,CAAC,MAAM,CAAC,kBAAa,EAAE,IAAI,CAAC,KAAK,GAAI,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,GAAI,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,OAAO,CAAC,YAAY,OAAO,CAAC,YAAY,GAAI,KAAK,GAAG,OAAO,CAAC,CAAE;AAGrL,QAAM,OAAO,aAAa,EAAE,GAAG,MAAM,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM,GAAG;AACxE,QAAM,WAAW,aAAa,EAAE,GAAG,MAAM,IAAI,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM,GAAG;AAC5E,QAAM,SAAS;AAAA,IAAC,YAAY,CAAC;AAAA,IAC3B,GAAG,UAAU,GAAG,CAAC,QAAQ,QAAQ,KAAK,KAAK,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK,KAAK,KAAK,MAAM,CAAC,GAAG,MAAM,KAAK,UAAU,IAAI,QAAQ,GAAG,IAAI;AAAA,IACnI,cAAc,CAAC,cAAc,KAAK,EAAE,IAAI,KAAK,EAAE;AAAA,IAAsB;AAAA,IAAuB,GAAG,MAAM,KAAK,QAAQ,IAAI,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,WAAW,CAAC,EAAE;AAAA,IAAG;AAAA,IAAW;AAAA,IAC/K,GAAI,SAAS,OAAO,CAAC,GAAG,UAAU,GAAG,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,MAAM,KAAK,MAAM,CAAC,GAAG,GAAG,UAAU,GAAG,CAAC,QAAQ,KAAK,GAAG,KAAK,GAAG,MAAM,KAAK,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,IAAI,CAAC;AAAA,IAC9M,GAAI,UAAU,QAAQ,GAAG,GAAG,GAAG,SAAS,KAAK,IAAI,CAAC;AAAA,IAClD;AAAA,EAAK;AAEP,QAAM,MAAM,GAAG,EAAE,GAAG,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,EAAE,IAAI,OAAO,EAAE,IAAI;AAC3G,QAAM,OAAO,aAAa,YAAY;AACtC,QAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG;AACtD,QAAM,WAAW;AAAA,IACf,MAAM,CAAC;AAAA;AAAA,IACP,KAAK,CAAC,YAAY,EAAE,SAAS,UAAU,GAAG,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,GAAG;AAAA,IAC1F;AAAA,IAAK;AAAA,IACL,WAAW,CAAC;AAAA,IACZ;AAAA,IAAoB,OAAO,CAAC;AAAA,IAAY,OAAO,CAAC,UAAU,CAAC;AAAA,IAAI,OAAO,CAAC,SAAS,IAAI;AAAA,IAAK;AAAA,IACzF;AAAA,IAAoB,UAAU,CAAC;AAAA,IAAe,SAAS,CAAC,SAAS,IAAI;AAAA,IAAK;AAAA,IAAS;AAAA,IACnF;AAAA,IAAqB,OAAO,CAAC;AAAA,IAAY,UAAU,CAAC,OAAO,CAAC;AAAA,IAAU,kCAAkC,CAAC,aAAa,CAAC;AAAA,IAAM;AAAA,IAAS;AAAA,IACtI;AAAA,IAAK;AAAA;AAAA,IAEL,WAAW,CAAC;AAAA,IACZ,SAAS,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,KAAK;AAAA,IAAI,SAAS,KAAK,EAAE,OAAO,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,KAAK;AAAA,IAClH,cAAc,CAAC;AAAA,IAAwB,cAAc,CAAC;AAAA,IACtD;AAAA,IAAK;AAAA,IACL,GAAI,KAAK,CAAC,GAAG,GAAG,IAAI,GAAG,GAAG,OAAO,GAAG,CAAC,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,OAAO,GAAG,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,iBAAiB,GAAG,GAAG,MAAM,KAAK,EAAE,IAAI,CAAC;AAAA,EAC/J;AACA,SAAO;AAAA,IACL;AAAA,IAAM;AAAA,IAAQ;AAAA,IACd,MAAM,EAAE,SAAS,UAAU,MAAM,QAAQ,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,UAAU,GAAI,SAAS,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC,GAAI,GAAI,UAAU,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAE,GAAG,SAAS,IAAI,SAAS,KAAK;AAAA,EAC3N;AACF;AAGA,IAAM,YAAY,CAAC,OAAc,SAAuB;AACtD,QAAM,EAAE,GAAG,EAAE,IAAI,MAAM,KAAK,IAAI;AAChC,SAAO,GAAG,CAAC,IAAI,CAAC;AAClB;AAUO,SAAS,SAAS,OAAuB,CAAC,GAAc;AAC7D,QAAM,QAAQ,KAAK,SAAS;AAC5B,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS,uLAA6K,UAAU,OAAO,QAAQ,CAAC,uBAAuB,UAAU,OAAO,MAAM,CAAC;AAAA,MAC/P,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,YAAY,MAAM,MAAM,OAAO,GAAG;AAAA,IACvE;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS,6PAA8O,UAAU,OAAO,MAAM,CAAC;AAAA,MAC/Q,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,cAAc,MAAM,MAAM,OAAO,GAAG;AAAA,IACzE;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS,kKAAmJ,UAAU,OAAO,MAAM,CAAC;AAAA,MACpL,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,YAAY,MAAM,MAAM,OAAO,GAAG;AAAA,IACvE;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS,6PAA8O,UAAU,OAAO,MAAM,CAAC;AAAA,MAC/Q,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,cAAc,MAAM,MAAM,OAAO,GAAG;AAAA,IACzE;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ,CAAC,MAAM,MAAM,MAAM,QAAQ,aAAa,MAAM,MAAM,OAAO,GAAG;AAAA,IACxE;AAAA,EACF;AACF;AAOO,SAAS,UAAU,OAA0E,CAAC,GAAW;AAC9G,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,MAAM,KAAK,YAAY,EAAE,OAAO,KAAK,QAAQ,IAAI;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,IAAI,KAAK,IAAI,IAAI,MAAM;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,KAAK,EAAE,OAAO,EAAE;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,KAAK,UAAU,OAAO,IAAI,CAAC,EAAE;AAAA,IAC7D;AAAA,IACA,+BAA+B,KAAK,MAAM,IAAI,SAAS,MAAM,KAAK,QAAQ,EAAE,IAAI,GAAG,CAAC,iCAAiC,KAAK,MAAM,IAAI,SAAS,MAAM,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC;AAAA,IACxK;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC3cO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,YAAqB,SAAiB,OAAiB;AACrD;AAAA,MACE,wBAAwB,OAAO,yEACd,MAAM,SAAS,MAAM,KAAK,IAAI,IAAI,mBAAmB,uDACjB,OAAO;AAAA,IAC9D;AALmB;AAMnB,SAAK,OAAO;AAAA,EACd;AAAA,EAPqB;AAQvB;AAGO,IAAM,WAAsB,SAAS;AAG5C,IAAM,aAAa;AAenB,IAAM,WAAW;AAUjB,SAAS,QAAQ,MAAwB;AACvC,QAAM,MAAgB,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,GAAG;AAC/D,MAAI,SAAS;AACb,WAAS,YAAY;AACrB,WAAS,IAAI,SAAS,KAAK,IAAI,GAAG,GAAG,IAAI,SAAS,KAAK,IAAI,GAAG;AAC5D,UAAM,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;AAC1C,UAAM,MAAM,SAAS,MAAM,SAAS;AACpC,QAAI,MAAM,EAAG;AACb,UAAM,MAAM,KAAK,MAAM,YAAY,GAAG,GAAG;AAGzC,UAAM,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,QAAQ,UAAU,EAAE,EAAE,QAAQ,gBAAgB,EAAE,IAAI,IAAI,KAAK;AACnG,UAAM,SAAS,EAAE,CAAC,EAAE,SAAS,OAAO,IAAK,EAAE,CAAC,IAAI,IAAI,QAAQ,IAAI,OAAQ,IAAI;AAC5E,WAAO,KAAK,IAAI;AAChB,QAAI,QAAQ,KAAK,MAAM,QAAQ,EAAE,KAAK;AACtC,aAAS,MAAM;AACf,aAAS,YAAY;AAAA,EACvB;AACA,MAAI,QAAQ,KAAK,MAAM,MAAM;AAC7B,SAAO;AACT;AAGA,SAAS,SAAS,MAAc,WAA2B;AACzD,MAAI,QAAQ;AACZ,WAAS,IAAI,YAAY,GAAG,IAAI,KAAK,QAAQ,KAAK;AAChD,QAAI,KAAK,CAAC,MAAM,IAAK;AAAA,aACZ,KAAK,CAAC,MAAM,OAAO,EAAE,UAAU,EAAG,QAAO;AAAA,EACpD;AACA,SAAO;AACT;AAEA,IAAM,WAAW;AAGV,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,MAAc;AACxB,UAAM,8BAA8B,IAAI,uIAAkI;AAC1K,SAAK,OAAO;AAAA,EACd;AACF;AAMA,SAAS,WAAW,KAAaC,WAA8B;AAC7D,QAAM,KAAK,IAAI,OAAO,WAAW,QAAQ,IAAI;AAC7C,QAAM,QAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,KAAK,GAAG,GAAG,GAAG,IAAI,GAAG,KAAK,GAAG,GAAG;AAC9C,UAAM,UAAUA,UAAS,KAAK,CAAC,MAAM,EAAE,YAAY,EAAG,CAAC,CAAC;AACxD,QAAI,CAAC,SAAS;AAGZ,UAAI,EAAE,CAAC,MAAM,SAAS,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,EAAG,OAAM,IAAI,kBAAkB,EAAE,CAAC,GAAGA,UAAS,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AACjH;AAAA,IACF;AACA,UAAM,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS;AACxC,UAAM,QAAQ,SAAS,KAAK,OAAO;AACnC,QAAI,QAAQ,EAAG;AACf,UAAM,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,QAAQ,SAAS,MAAM,IAAI,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;AAClI,OAAG,YAAY,QAAQ;AAAA,EACzB;AACA,SAAO;AACT;AAmBA,SAAS,SAAS,QAAoC,OAAiB,MAAgB,KAA2B;AAChH,QAAM,MAAgB,CAAC,QAAQ,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,YAAY,IAAI,GAAG,IAAI,IAAI,cAAc,EAAE;AACrG,QAAM,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,KAAK,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO;AAChG,MAAI,KAAK,OAAQ,KAAI,KAAK,iBAAiB;AAC3C,aAAW,KAAK,OAAQ,KAAI,KAAK,GAAG,EAAE,UAAU,IAAI;AAEpD,MAAI,KAAK,IAAI,SAAS;AACtB,MAAI,MAAM,OAAQ,KAAI,KAAK,mEAAmE,GAAG,KAAK;AACtG,aAAW,KAAK,OAAQ,KAAI,KAAK,GAAG,EAAE,UAAU,MAAM;AACtD,MAAI,KAAK,OAAQ,KAAI,KAAK,gEAAgE,GAAG,IAAI;AACjG,MAAI,KAAK,KAAK,EAAE;AAEhB,aAAW,KAAK,OAAQ,KAAI,KAAK,GAAG,EAAE,UAAU,QAAQ;AAIxD,MAAI,KAAK,QAAQ;AACf,QAAI,KAAK,iBAAiB,wBAAwB,UAAU,KAAK,KAAK,KAAK,CAAC,OAAO,KAAK,MAAM,MAAM,qBAAqB,0BAA0B,SAAS,OAAO,GAAG;AAAA,EACxK;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;AAGA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,SAAS,UAAU,UAAU,SAAS,SAAS,SAAS,QAAQ,UAAU,UAAU,QAAQ,SAAS,UAAU,MAAM,IAAI,CAAC;AAqBnJ,SAAS,QAAQ,KAAa,OAAuB,CAAC,GAAkB;AAC7E,QAAM,MAAM,KAAK,YAAY;AAC7B,QAAMA,YAAW,KAAK,YAAY;AAElC,QAAM,QAAQ,WAAW,KAAKA,SAAQ;AACtC,MAAI,CAAC,MAAM,QAAQ;AAGjB,UAAM,QAAQ,QAAQ,GAAG;AACzB,QAAI,MAAM,KAAK,UAAU,MAAM,MAAM,QAAQ;AAC3C,YAAM,IAAI,MAAM,qIAAsH;AAAA,IACxI;AACA,UAAM,MAAM,CAAC,MAAM,KAAK,KAAK,GAAG,GAAI,MAAM,IAAI,SAAS,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC,CAAE,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAChH,WAAO,EAAE,SAAS,MAAM,IAAI,SAAS,MAAM,KAAK,MAAM,MAAM,IAAI,SAAS,QAAQ,MAAM,UAAU,MAAM,IAAI,SAAS,GAAG,MAAM,CAAC,EAAE;AAAA,EAClI;AAEA,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,MAAM,EAAE,IAAI;AACxB,QAAI,KAAK,IAAI,GAAG,EAAG,OAAM,IAAI,oBAAoB,EAAE,IAAI;AACvD,SAAK,IAAI,GAAG;AAAA,EACd;AAKA,QAAM,UAAoB,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,GAAG;AACnE,QAAM,SAAS,CAAC,SAAiB;AAC/B,UAAM,OAAO,QAAQ,IAAI;AACzB,YAAQ,IAAI,KAAK,GAAG,KAAK,GAAG;AAC5B,YAAQ,KAAK,KAAK,GAAG,KAAK,IAAI;AAC9B,YAAQ,MAAM,KAAK,GAAG,KAAK,KAAK;AAChC,YAAQ,QAAQ,KAAK;AAAA,EACvB;AACA,MAAI,SAAS;AACb,aAAW,KAAK,OAAO;AAAE,WAAO,IAAI,MAAM,QAAQ,EAAE,KAAK,CAAC;AAAG,aAAS,EAAE;AAAA,EAAI;AAC5E,SAAO,IAAI,MAAM,MAAM,CAAC;AAExB,QAAM,SAAS,MAAM,IAAI,CAAC,GAAG,UAAU;AACrC,UAAM,SAAS,GAAG,MAAM,EAAE,IAAI,CAAC;AAC/B,WAAO,EAAE,WAAW,EAAE,QAAQ,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,OAAO,QAAQ,SAAS,GAAG,MAAM,OAAO,CAAC,EAAE;AAAA,EACzG,CAAC;AAKD,QAAM,SAAS,CAAC,QAAQ,KAAK,KAAK,GAAG,GAAI,QAAQ,IAAI,SAAS,CAAC,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAE,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AACzH,QAAM,UAAU,SAAS,QAAQ,QAAQ,OAAO,QAAQ,MAAM,GAAG;AACjE,SAAO;AAAA,IACL,SAAS,aAAa,SAAS,GAAG,MAAM;AAAA;AAAA,EAAO,OAAO,KAAK,SAAS,GAAG;AAAA,IACvE,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,KAAK,GAAG;AAAA,IAClD,UAAU;AAAA,IACV,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,UAAU,IAAI;AAAA,EAC1C;AACF;","names":["size","gestures"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flatkit/sugarflat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Declarative sugar for FlatInk: a compact block expands into inspectable .flatink. An authoring tool, never a runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Zwyk Studio",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@flatkit/compiler": "0.
|
|
37
|
+
"@flatkit/compiler": "0.43.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@flatkit/player": "0.
|
|
44
|
-
"@flatkit/types": "0.
|
|
43
|
+
"@flatkit/player": "0.43.0",
|
|
44
|
+
"@flatkit/types": "0.43.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsup",
|