@doan-labs/peek 1.1.0 → 2.0.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.
@@ -0,0 +1,2205 @@
1
+ // src/tables.ts
2
+ var INK = {
3
+ ink: "#1A1918",
4
+ bone: "#F3F0E8",
5
+ paper: "#FFFDF8"
6
+ };
7
+ var FACES = {
8
+ diamond: {
9
+ body: { d: "M170 20L320 170L170 320L20 170Z" },
10
+ bottom: 320,
11
+ sink: 30,
12
+ eyes: [
13
+ [132.5, 155],
14
+ [207.5, 155]
15
+ ],
16
+ rx: 20,
17
+ ry: 27.5,
18
+ pr: 10,
19
+ brow: { y: 102.5, hw: 18.75, arch: 15, w: 5.62 },
20
+ cheeks: [
21
+ [117.5, 200],
22
+ [222.5, 200]
23
+ ],
24
+ crx: 11.25,
25
+ cry: 5.62,
26
+ mouth: { x: 170, y: 218.75, w: 15, d: 16.25, sw: 4.38 },
27
+ crown: {
28
+ poly: [
29
+ [170, 20],
30
+ [320, 170],
31
+ [170, 320],
32
+ [20, 170]
33
+ ],
34
+ r: 0
35
+ }
36
+ },
37
+ semicircle: {
38
+ body: { d: "M0 255A170 170 0 0 1 340 255Z" },
39
+ bottom: 255,
40
+ sink: 0,
41
+ eyes: [
42
+ [102.5, 175],
43
+ [237.5, 175]
44
+ ],
45
+ rx: 30,
46
+ ry: 25,
47
+ pr: 11.25,
48
+ brow: { y: 127.5, hw: 25, arch: 15, w: 5.62 },
49
+ cheeks: [
50
+ [55, 212.5],
51
+ [285, 212.5]
52
+ ],
53
+ crx: 11.25,
54
+ cry: 5.62,
55
+ mouth: { x: 170, y: 221.25, w: 16.25, d: 16.25, sw: 4.38 },
56
+ crown: { poly: [[170, 255]], r: 170 }
57
+ },
58
+ circle: {
59
+ body: { circle: [170, 170, 150] },
60
+ bottom: 320,
61
+ sink: 20,
62
+ eyes: [
63
+ [112.8, 162.2],
64
+ [227.2, 162.2]
65
+ ],
66
+ rx: 29.9,
67
+ ry: 29.9,
68
+ pr: 13,
69
+ brow: { y: 110.2, hw: 23.4, arch: 15.6, w: 5.85 },
70
+ cheeks: [
71
+ [84.2, 203.8],
72
+ [255.8, 203.8]
73
+ ],
74
+ crx: 11.7,
75
+ cry: 5.85,
76
+ mouth: { x: 170, y: 223.3, w: 16.9, d: 16.9, sw: 4.55 },
77
+ crown: { poly: [[170, 170]], r: 150 }
78
+ },
79
+ triangle: {
80
+ body: { d: "M170 40.8L312.8 299.2L27.2 299.2Z", stroke: 27.2 },
81
+ bottom: 312.8,
82
+ sink: 0,
83
+ eyes: [
84
+ [132.6, 210.8],
85
+ [207.4, 210.8]
86
+ ],
87
+ rx: 20.4,
88
+ ry: 27.2,
89
+ pr: 10,
90
+ brow: { y: 166.6, hw: 18.7, arch: 15.3, w: 5.62 },
91
+ cheeks: [
92
+ [98.6, 238],
93
+ [241.4, 238]
94
+ ],
95
+ crx: 11.5,
96
+ cry: 5.75,
97
+ mouth: { x: 170, y: 254, w: 17, d: 16.25, sw: 4.38 },
98
+ crown: {
99
+ poly: [
100
+ [170, 40.8],
101
+ [312.8, 299.2],
102
+ [27.2, 299.2]
103
+ ],
104
+ r: 13.6
105
+ }
106
+ }
107
+ };
108
+ var COLORS = {
109
+ lavender: { body: "#D8CDF0", deep: "#BDAEE6" },
110
+ fog: { body: "#C8D6E8", deep: "#A8BCDC" },
111
+ clay: { body: "#F1CDBF", deep: "#E2A893" },
112
+ mint: { body: "#CFE7D6", deep: "#A5CDB1" },
113
+ butter: { body: "#ECE2B9", deep: "#D9C284" },
114
+ rose: { body: "#EFCAD7", deep: "#DFA4BA" },
115
+ aqua: { body: "#BDE1E5", deep: "#8FC7D1" }
116
+ };
117
+ var VARIANTS = ["peek", "haze", "bloc"];
118
+ var HAZE = {
119
+ lavender: { tint: "#C8B1F5", glow: "#F2EDFD" },
120
+ fog: { tint: "#9EC3F2", glow: "#E6F1FF" },
121
+ clay: { tint: "#F4AE93", glow: "#FEECE5" },
122
+ mint: { tint: "#A3DAB5", glow: "#E5F5EA" },
123
+ butter: { tint: "#E1CC7A", glow: "#F4F0E0" },
124
+ rose: { tint: "#F0A9C4", glow: "#FDEAF1" },
125
+ aqua: { tint: "#85D3DC", glow: "#E0F5F7" }
126
+ };
127
+ var WHEEL = [
128
+ "clay",
129
+ "butter",
130
+ "mint",
131
+ "aqua",
132
+ "fog",
133
+ "lavender",
134
+ "rose"
135
+ ];
136
+ var BLOC = {
137
+ lavender: "#B093E5",
138
+ fog: "#7BA8DF",
139
+ clay: "#E38A66",
140
+ mint: "#82CB9B",
141
+ butter: "#E2CA6A",
142
+ rose: "#DF84A8",
143
+ aqua: "#5EC3CD"
144
+ };
145
+ var PARTS = {
146
+ eyes: ["oval", "bead", "ring"],
147
+ brows: ["arch", "bar", "wedge", "dash"],
148
+ mouth: ["poly", "round", "line", "box"],
149
+ cheeks: ["oval", "dots", "lines"],
150
+ trait: ["square", "fin", "ring", "dot", "peak"]
151
+ };
152
+ var ACCESSORY_INK = { knot: "#E5412D" };
153
+ var ACCESSORIES = {
154
+ eyewear: ["none", "glasses", "sunglasses"],
155
+ headwear: ["none", "bow", "cap", "sprout"],
156
+ neckwear: ["none", "tie"]
157
+ };
158
+ var VERSIONS = {
159
+ 1: {
160
+ face: 4,
161
+ color: 7,
162
+ eyes: 3,
163
+ brows: 4,
164
+ mouth: 4,
165
+ cheeks: 3,
166
+ trait: 5,
167
+ eyewear: 1,
168
+ headwear: 1,
169
+ neckwear: 1
170
+ }
171
+ };
172
+ var LATEST = 1;
173
+ var GROUPS = {
174
+ eyes: ["lid", "lower", "lidTilt", "eyeS", "lidAsym"],
175
+ pupil: ["pupil", "gx", "gy", "shine"],
176
+ brows: ["browY", "browTilt", "browArch", "browW", "browAsym"],
177
+ mouth: ["mw", "mt", "mb", "mx", "my", "mk"],
178
+ body: ["alt", "rot", "x", "sx", "sy"],
179
+ extra: ["blush", "hair", "dim", "thing"]
180
+ };
181
+ var BASE = {
182
+ alt: 0,
183
+ rot: 0,
184
+ x: 0,
185
+ sx: 1,
186
+ sy: 1,
187
+ lid: 0.1,
188
+ lower: 0,
189
+ lidTilt: 0,
190
+ eyeS: 1,
191
+ lidAsym: 0,
192
+ pupil: 1,
193
+ gx: 0,
194
+ gy: 0,
195
+ shine: 0.7,
196
+ browY: 0,
197
+ browTilt: 0,
198
+ browArch: 0.4,
199
+ browW: 1,
200
+ browAsym: 0,
201
+ mw: 0.72,
202
+ mt: 0,
203
+ mb: 0.5,
204
+ mx: 0,
205
+ my: 0,
206
+ mk: 0,
207
+ blush: 0,
208
+ hair: 0,
209
+ dim: 0,
210
+ thing: 0
211
+ };
212
+ var S = (o) => ({ ...BASE, ...o });
213
+ var EXPRESSIONS = {
214
+ normal: S({}),
215
+ happy: S({
216
+ lid: 0,
217
+ lower: 0.42,
218
+ shine: 1,
219
+ gy: -0.05,
220
+ browY: -5,
221
+ browArch: 1,
222
+ mw: 1,
223
+ mb: 1,
224
+ blush: 1,
225
+ hair: 0.45,
226
+ alt: 0.05
227
+ }),
228
+ sad: S({
229
+ lid: 0.42,
230
+ lidTilt: 16,
231
+ pupil: 0.95,
232
+ gy: 0.8,
233
+ shine: 0,
234
+ browY: 3,
235
+ browTilt: 14,
236
+ browArch: 0,
237
+ mw: 0.8,
238
+ mt: -0.55,
239
+ mb: -0.55,
240
+ my: 6,
241
+ alt: -0.16,
242
+ sy: 0.985,
243
+ hair: -0.75
244
+ }),
245
+ angry: S({
246
+ lid: 0.34,
247
+ lidTilt: -22,
248
+ lower: 0.12,
249
+ pupil: 0.82,
250
+ gy: 0.1,
251
+ shine: 0,
252
+ browY: 5,
253
+ browTilt: -21,
254
+ browArch: 0,
255
+ browW: 1.3,
256
+ mw: 0.8,
257
+ mt: -0.08,
258
+ mb: -0.08,
259
+ my: 3,
260
+ sx: 1.02,
261
+ sy: 0.98,
262
+ hair: 0.25
263
+ }),
264
+ sleepy: S({
265
+ lid: 1,
266
+ lidTilt: 4,
267
+ pupil: 0.9,
268
+ gy: 0.5,
269
+ shine: 0,
270
+ browY: 6,
271
+ browTilt: 3,
272
+ browArch: 0.15,
273
+ browW: 0.9,
274
+ mw: 0.35,
275
+ mt: -0.3,
276
+ mb: 0.3,
277
+ my: 2,
278
+ alt: -1.12,
279
+ rot: -3,
280
+ hair: -1
281
+ }),
282
+ curious: S({
283
+ lid: 0,
284
+ eyeS: 1.06,
285
+ pupil: 1.05,
286
+ shine: 1,
287
+ browY: -3,
288
+ browAsym: 9,
289
+ browArch: 0.8,
290
+ mw: 0.38,
291
+ mt: -0.36,
292
+ mb: 0.36,
293
+ rot: 6,
294
+ alt: -0.32,
295
+ hair: 0.3,
296
+ thing: 1
297
+ }),
298
+ surprised: S({
299
+ lid: 0,
300
+ eyeS: 1.2,
301
+ pupil: 0.66,
302
+ shine: 0.8,
303
+ browY: -13,
304
+ browArch: 1,
305
+ mw: 0.55,
306
+ mt: -0.62,
307
+ mb: 0.62,
308
+ alt: 0.36,
309
+ hair: 1
310
+ }),
311
+ excited: S({
312
+ lid: 0,
313
+ lower: 0.5,
314
+ eyeS: 1.08,
315
+ pupil: 1.08,
316
+ shine: 1,
317
+ browY: -9,
318
+ browArch: 1,
319
+ mw: 1.15,
320
+ mb: 1.3,
321
+ blush: 1,
322
+ hair: 1,
323
+ alt: 0.12
324
+ }),
325
+ confused: S({
326
+ lid: 0.22,
327
+ lidAsym: 0.28,
328
+ lidTilt: -4,
329
+ pupil: 0.92,
330
+ gx: -0.35,
331
+ gy: -0.45,
332
+ shine: 0.3,
333
+ browY: -2,
334
+ browAsym: 11,
335
+ browTilt: -5,
336
+ browArch: 0.3,
337
+ mw: 0.6,
338
+ mt: -0.1,
339
+ mb: 0.12,
340
+ mx: 5,
341
+ mk: 13,
342
+ rot: -7,
343
+ alt: -0.05,
344
+ hair: 0.1
345
+ }),
346
+ bored: S({
347
+ lid: 0.5,
348
+ pupil: 0.9,
349
+ gx: -0.55,
350
+ gy: 0.25,
351
+ shine: 0,
352
+ browY: 3,
353
+ browArch: 0.1,
354
+ mw: 0.55,
355
+ mt: 0,
356
+ mb: 0.04,
357
+ mx: -5,
358
+ alt: -0.34,
359
+ hair: -0.45,
360
+ dim: 1
361
+ }),
362
+ attentive: S({
363
+ lid: 0,
364
+ eyeS: 1.1,
365
+ pupil: 0.8,
366
+ gy: -0.1,
367
+ shine: 1,
368
+ browY: -7,
369
+ browArch: 0.7,
370
+ mw: 0.55,
371
+ mb: 0.4,
372
+ alt: 0.15,
373
+ sy: 1.03,
374
+ hair: 0.65
375
+ })
376
+ };
377
+
378
+ // src/bloc.ts
379
+ var RAD = Math.PI / 180;
380
+ var TRI = [
381
+ [170, 12.8],
382
+ [320, 312.8],
383
+ [20, 312.8]
384
+ ];
385
+ var BODY = {
386
+ diamond: FACES.diamond,
387
+ semicircle: FACES.semicircle,
388
+ circle: FACES.circle,
389
+ triangle: {
390
+ body: { d: `M${TRI.map((p) => p.join(" ")).join("L")}Z` },
391
+ crown: { poly: TRI, r: 0 }
392
+ }
393
+ };
394
+ var strokeFor = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.6;
395
+ var boostFor = (px) => px < 30 ? 1.25 : px < 56 ? 1.12 : 1;
396
+ var turn = (x, y, deg) => {
397
+ const c = Math.cos(deg * RAD);
398
+ const s = Math.sin(deg * RAD);
399
+ return [x * c - y * s, x * s + y * c];
400
+ };
401
+ var poly = (pts) => `M${pts.map(([x, y]) => `${f2(x)} ${f2(y)}`).join("L")}Z`;
402
+ function blocParts({ who, pose, o, defs, g }) {
403
+ const sh = FACES[who.face];
404
+ const { body, crown } = BODY[who.face];
405
+ const id = o.id;
406
+ const P = who.persona;
407
+ const spread = P.spread;
408
+ const st = strokeFor(o.size);
409
+ const up = boostFor(o.size);
410
+ const strong = BLOC[who.color];
411
+ const light = o.frame === "bone" || o.frame === "paper" ? COLORS[who.color].deep : COLORS[who.color].body;
412
+ const lineW = sh.brow.w * st;
413
+ defs.push(n("clipPath", "half", { id: `${id}-r` }, [
414
+ n("rect", "half.shape", { x: 170, y: -30, width: 200, height: 400 })
415
+ ]));
416
+ const block = (key, fill, clip) => ("d" in body) ? n("path", key, { d: body.d, fill, "clip-path": clip }) : n("circle", key, {
417
+ cx: body.circle[0],
418
+ cy: body.circle[1],
419
+ r: body.circle[2],
420
+ fill,
421
+ "clip-path": clip
422
+ });
423
+ const eyeS = g("eyeS");
424
+ const r0 = Math.sqrt(sh.rx * sh.ry) * up;
425
+ const lid0 = g("lid");
426
+ const lidAsym = g("lidAsym");
427
+ const lower = clamp(g("lower"), 0, 1);
428
+ const pupil = g("pupil");
429
+ const gx = clamp(g("gx"), -1.1, 1.1);
430
+ const gy = clamp(g("gy"), -1.1, 1.1);
431
+ const tilt = g("lidTilt");
432
+ const bead = who.eyes === "bead";
433
+ const eyes = sh.eyes.map(([ex, ey], i) => {
434
+ const side = i === 0 ? -1 : 1;
435
+ const kk = `eye${i}`;
436
+ let lid = clamp(lid0 + (i === 0 ? lidAsym : -lidAsym * 0.3), 0, 1);
437
+ lid = lid + (1 - lid) * pose.blink;
438
+ const cut = clamp((lid - 0.1) / 0.9, 0, 1);
439
+ const r = bead ? r0 * 0.7 * eyeS * (0.4 + 0.6 * pupil) : r0 * eyeS;
440
+ const [tx, ty] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
441
+ const a = side * tilt;
442
+ const edge = -r - 1 + cut * (2 * r + 2);
443
+ const floor = r + 1 - lower * r * 2.2;
444
+ const W = r + 2;
445
+ const lidAt = (x) => x * Math.tan(a * RAD) + edge / Math.cos(a * RAD);
446
+ const [t0, t1] = [lidAt(-W), lidAt(W)];
447
+ defs.push(n("clipPath", `${kk}.clip`, { id: `${id}-e${i}` }, [
448
+ n("path", `${kk}.clip.shape`, {
449
+ d: poly([
450
+ [-W, t0],
451
+ [W, t1],
452
+ [W, Math.max(floor, t1)],
453
+ [-W, Math.max(floor, t0)]
454
+ ])
455
+ })
456
+ ]));
457
+ const pr = sh.pr * pupil * up;
458
+ const px = gx * Math.max(0, r - pr) * 0.85;
459
+ const py = gy * Math.max(0, r - pr) * 0.75;
460
+ const rim = Math.min(r * 0.27 * Math.min(st, 1.6), r * 0.4);
461
+ const inner = bead ? [n("circle", `${kk}.bead`, { cx: 0, cy: 0, r: f2(r), fill: INK.ink })] : [
462
+ n("circle", `${kk}.white`, {
463
+ cx: 0,
464
+ cy: 0,
465
+ r: f2(r),
466
+ fill: INK.paper
467
+ }),
468
+ n("circle", `${kk}.pupil`, {
469
+ cx: f2(px),
470
+ cy: f2(py),
471
+ r: f2(pr),
472
+ fill: INK.ink
473
+ })
474
+ ];
475
+ if (who.eyes === "ring")
476
+ inner.push(n("circle", `${kk}.rim`, {
477
+ cx: 0,
478
+ cy: 0,
479
+ r: f2(r - rim / 2),
480
+ fill: "none",
481
+ stroke: INK.ink,
482
+ "stroke-width": f2(rim)
483
+ }));
484
+ const co = smooth(0.86, 1, lid);
485
+ const yy = Math.min(edge, r) - lineW / 2;
486
+ return n("g", kk, { transform: `translate(${f2(tx)} ${f2(ty)})` }, [
487
+ n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
488
+ n("path", `${kk}.closed`, {
489
+ d: `M${f2(-r * 0.95)} ${f2(yy)}H${f2(r * 0.95)}`,
490
+ transform: `rotate(${f2(a * 0.6)})`,
491
+ stroke: INK.ink,
492
+ "stroke-width": f2(lineW * 1.4),
493
+ opacity: co > 0.01 ? f2(co) : 0
494
+ })
495
+ ]);
496
+ });
497
+ const bt = pose.browTilt;
498
+ const basym = g("browAsym");
499
+ const th = lineW * 2 * g("browW");
500
+ const hw = sh.eyes[1][0] - 170 + spread + Math.sqrt(sh.rx * sh.ry) * P.mul.eyeS * up;
501
+ const gap = smooth(0, 8, Math.abs(bt)) * th * 0.9;
502
+ const slant = P.add.browTilt * 0.3 - Math.atan(1.3 * basym / (2 * hw)) / RAD;
503
+ const y0 = sh.brow.y + g("browY") - (eyeS - 1) * sh.ry * 0.9 - 0.35 * basym;
504
+ const halves = (dy, len2 = 1) => [-1, 1].map((s2) => {
505
+ const [ox, oy] = turn(s2 * gap / 2, 0, slant);
506
+ const x0 = 170 + ox;
507
+ const yy = y0 + dy + oy;
508
+ const ang = slant + s2 * bt;
509
+ const [ux, uy] = turn(s2 * (hw * len2 - gap / 2), 0, ang);
510
+ const [nx, ny] = turn(0, th / 2, ang);
511
+ return [
512
+ [x0 - nx, yy - ny],
513
+ [x0 + ux - nx, yy + uy - ny],
514
+ [x0 + ux + nx, yy + uy + ny],
515
+ [x0 + nx, yy + ny]
516
+ ];
517
+ });
518
+ const inside = (dy, len2 = 1) => halves(dy, len2).every((q) => q.every(([x, y]) => depth(crown, x, y) + 2 < 0));
519
+ let lo = 0;
520
+ let hi = 0;
521
+ if (!inside(0)) {
522
+ hi = 60;
523
+ for (let k = 0;k < 12; k++) {
524
+ const mid2 = (lo + hi) / 2;
525
+ if (inside(mid2))
526
+ hi = mid2;
527
+ else
528
+ lo = mid2;
529
+ }
530
+ }
531
+ let len = 1;
532
+ if (!inside(hi)) {
533
+ let short = 0;
534
+ for (let k = 0;k < 12; k++) {
535
+ const mid2 = (short + len) / 2;
536
+ if (inside(hi, mid2))
537
+ short = mid2;
538
+ else
539
+ len = mid2;
540
+ }
541
+ len = short;
542
+ }
543
+ const bars = halves(hi, len).map((q, i) => n("path", `bar${i}`, { d: poly(q), fill: INK.ink }));
544
+ const m = sh.mouth;
545
+ const big = (who.face === "diamond" || who.face === "triangle" ? 1.25 : 1) * up;
546
+ const mw = m.w * Math.max(0.05, g("mw")) * big;
547
+ const mt = g("mt") * m.d * big;
548
+ const mb = g("mb") * m.d * big;
549
+ const mid = (mt + mb) / 2;
550
+ const kb = 4 / 3;
551
+ let d;
552
+ if (who.mouth === "poly") {
553
+ const s2 = Math.max(mw * 0.9, (mb - mt) / 2);
554
+ d = poly([
555
+ [-s2, mid],
556
+ [0, mid - s2],
557
+ [s2, mid],
558
+ [0, mid + s2]
559
+ ]);
560
+ } else if (who.mouth === "box") {
561
+ const h = Math.max(mb - mt, lineW * 1.6) / 2;
562
+ const w = mw * 0.85;
563
+ d = poly([
564
+ [-w, mid - h],
565
+ [w, mid - h],
566
+ [w, mid + h],
567
+ [-w, mid + h]
568
+ ]);
569
+ } else if (who.mouth === "line") {
570
+ const h = lineW * 1.4 / 2;
571
+ const w = mw * 1.5;
572
+ const y = mid * 0.5;
573
+ d = poly([
574
+ [-w, y - h],
575
+ [w, y - h],
576
+ [w, y + h],
577
+ [-w, y + h]
578
+ ]);
579
+ } else {
580
+ d = `M${f2(-mw)} 0C${f2(-mw)} ${f2(mb * kb)} ${f2(mw)} ${f2(mb * kb)} ${f2(mw)} 0C${f2(mw)} ${f2(mt * kb)} ${f2(-mw)} ${f2(mt * kb)} ${f2(-mw)} 0Z`;
581
+ }
582
+ const mouth = n("path", "mouth", {
583
+ d,
584
+ transform: `translate(${f2(m.x + g("mx"))} ${f2(m.y + g("my"))}) rotate(${f2(g("mk"))})`,
585
+ fill: INK.ink
586
+ });
587
+ const hair = g("hair");
588
+ const s = seat(crown, Math.max(0, hair) * 8, 110 + Math.max(0, -hair) * 40);
589
+ const shape = who.trait === "square" ? n("rect", "trait.shape", { x: -21, y: -21, width: 42, height: 42 }) : who.trait === "dot" ? n("circle", "trait.shape", { cx: 0, cy: 0, r: 22 }) : who.trait === "ring" ? n("circle", "trait.shape", {
590
+ cx: 0,
591
+ cy: 0,
592
+ r: 17.5,
593
+ fill: "none",
594
+ stroke: strong,
595
+ "stroke-width": 13
596
+ }) : who.trait === "fin" ? n("path", "trait.shape", { d: "M-27 27V-27A54 54 0 0 1 27 27Z" }) : n("rect", "trait.shape", {
597
+ x: -16,
598
+ y: -30,
599
+ width: 32,
600
+ height: 60
601
+ });
602
+ const trait = n("g", "trait", {
603
+ transform: `translate(${f2(s.x)} ${f2(s.y + pose.lag)}) scale(${f2(up)})`,
604
+ fill: strong
605
+ }, [shape]);
606
+ return [
607
+ [block("body", strong), block("body.light", light, `url(#${id}-r)`), trait],
608
+ [...eyes, ...bars, mouth]
609
+ ];
610
+ }
611
+
612
+ // src/identity.ts
613
+ var STYLE = "peek";
614
+ var tidy = (s) => s.normalize("NFC").trim().replace(/\s+/g, " ").toLowerCase();
615
+ function fnv1a(str) {
616
+ let h = 2166136261;
617
+ for (const b of new TextEncoder().encode(str)) {
618
+ h ^= b;
619
+ h = Math.imul(h, 16777619) >>> 0;
620
+ }
621
+ return h >>> 0;
622
+ }
623
+ function mulberry32(a) {
624
+ return () => {
625
+ a = a + 1831565813 | 0;
626
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
627
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
628
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
629
+ };
630
+ }
631
+ var LISTS = {
632
+ face: Object.keys(FACES),
633
+ color: Object.keys(COLORS),
634
+ ...PARTS,
635
+ ...ACCESSORIES
636
+ };
637
+ var AXES = Object.keys(LISTS);
638
+ function seed(name, axis, version = LATEST) {
639
+ return fnv1a(`${STYLE}@${version}:${axis}:${tidy(name)}`);
640
+ }
641
+ function identify(name, { version = LATEST } = {}) {
642
+ const lengths = VERSIONS[version];
643
+ if (!lengths)
644
+ throw new Error(`peek@${version} does not exist`);
645
+ const pick = (axis) => LISTS[axis][seed(name, axis, version) % lengths[axis]];
646
+ const rnd = mulberry32(seed(name, "persona", version));
647
+ const r = (a, b) => Math.round((a + rnd() * (b - a)) * 100) / 100;
648
+ const persona = {
649
+ spread: r(-7, 7),
650
+ blink: r(0.75, 1.4),
651
+ add: {
652
+ browY: r(-4, 4),
653
+ browTilt: r(-5, 5),
654
+ browArch: r(-0.2, 0.3),
655
+ rot: r(-3, 3),
656
+ hair: r(-0.2, 0.35),
657
+ gx: r(-0.12, 0.12),
658
+ my: r(-2, 3)
659
+ },
660
+ mul: {
661
+ eyeS: r(0.92, 1.08),
662
+ pupil: r(0.9, 1.12),
663
+ browW: r(0.85, 1.2),
664
+ mw: r(0.85, 1.18)
665
+ }
666
+ };
667
+ const key = tidy(name);
668
+ return {
669
+ key,
670
+ hash: fnv1a(key),
671
+ version,
672
+ face: pick("face"),
673
+ color: pick("color"),
674
+ eyes: pick("eyes"),
675
+ brows: pick("brows"),
676
+ mouth: pick("mouth"),
677
+ cheeks: pick("cheeks"),
678
+ trait: pick("trait"),
679
+ eyewear: pick("eyewear"),
680
+ headwear: pick("headwear"),
681
+ neckwear: pick("neckwear"),
682
+ persona
683
+ };
684
+ }
685
+
686
+ // src/draw.ts
687
+ var VB = [-30, -30, 400, 400];
688
+ var FLOOR = 370;
689
+ var THING = [330, 20];
690
+ var clamp = (v, a, b) => v < a ? a : v > b ? b : v;
691
+ var smooth = (a, b, v) => {
692
+ const t = clamp((v - a) / (b - a), 0, 1);
693
+ return t * t * (3 - 2 * t);
694
+ };
695
+ var f2 = (v) => String(Math.round(v * 100) / 100);
696
+ var f3 = (v) => String(Math.round(v * 1000) / 1000);
697
+ var strokeFor2 = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.4;
698
+ var RAD2 = Math.PI / 180;
699
+ var STEM = "M0 0Q3 -19 0 -36";
700
+ var LEAF0 = "M0 -33C-24 -30 -38 -52 -25 -53C-12 -54 -2 -43 0 -33Z";
701
+ var LEAF1 = "M0 -33C5 -54 28 -61 27 -48C26 -36 11 -31 0 -33Z";
702
+ function frameOf(face) {
703
+ const baseY = FLOOR + face.sink - face.bottom;
704
+ const eyeY = face.eyes[0][1];
705
+ return { baseY, eyeY, R: FLOOR - (eyeY + baseY) };
706
+ }
707
+ function thingGaze(face, x, alt) {
708
+ const { baseY, eyeY, R } = frameOf(face);
709
+ const ey = eyeY + baseY - alt * R;
710
+ return [
711
+ clamp((THING[0] - 170 - x) / 150, -1, 1),
712
+ clamp((THING[1] - ey) / 150, -1, 1)
713
+ ];
714
+ }
715
+ function restPose(who, expression, gaze) {
716
+ const s = EXPRESSIONS[expression];
717
+ const pose = { ...s, blink: 0, lag: 0, expression };
718
+ if (expression === "curious")
719
+ pose.rot = 7;
720
+ const g = gaze ?? (expression === "curious" ? thingGaze(FACES[who.face], 0, s.alt) : null);
721
+ if (g)
722
+ [pose.gx, pose.gy] = g;
723
+ return pose;
724
+ }
725
+ function seat(crown, d, u) {
726
+ const R = Math.max(0, crown.r + d);
727
+ const at = ([x, y], b2) => ({
728
+ x: x + R * Math.sin(b2),
729
+ y: y - R * Math.cos(b2),
730
+ a: b2 / RAD2
731
+ });
732
+ const P = crown.poly;
733
+ if (P.length === 1)
734
+ return at(P[0], R ? u / R : 0);
735
+ const n2 = P.length;
736
+ const dir = u >= 0 ? 1 : -1;
737
+ let left = Math.abs(u);
738
+ let i = 0;
739
+ let b = 0;
740
+ for (let step = 0;step < n2; step++) {
741
+ const j = (i + dir + n2) % n2;
742
+ const [x0, y0] = P[i];
743
+ const [x1, y1] = P[j];
744
+ const ex = x1 - x0;
745
+ const ey = y1 - y0;
746
+ const L = Math.hypot(ex, ey);
747
+ const nx = dir * ey / L;
748
+ const ny = -dir * ex / L;
749
+ const be = Math.atan2(nx, -ny);
750
+ let db = be - b;
751
+ while (dir * db < 0)
752
+ db += dir * 2 * Math.PI;
753
+ const arc = R * Math.abs(db);
754
+ if (left <= arc)
755
+ return at(P[i], R ? b + dir * left / R : b);
756
+ left -= arc;
757
+ if (left <= L)
758
+ return {
759
+ x: x0 + ex / L * left + R * nx,
760
+ y: y0 + ey / L * left + R * ny,
761
+ a: be / RAD2
762
+ };
763
+ left -= L;
764
+ i = j;
765
+ b = be;
766
+ }
767
+ return at(P[i], b);
768
+ }
769
+ function depth(crown, x, y) {
770
+ const P = crown.poly;
771
+ if (P.length === 1)
772
+ return Math.hypot(x - P[0][0], y - P[0][1]) - crown.r;
773
+ let out = -Infinity;
774
+ let near = Infinity;
775
+ for (let i = 0;i < P.length; i++) {
776
+ const [ax, ay] = P[i];
777
+ const [bx, by] = P[(i + 1) % P.length];
778
+ const ex = bx - ax;
779
+ const ey = by - ay;
780
+ const L2 = ex * ex + ey * ey;
781
+ const L = Math.sqrt(L2);
782
+ out = Math.max(out, ((x - ax) * ey - (y - ay) * ex) / L);
783
+ const t = clamp(((x - ax) * ex + (y - ay) * ey) / L2, 0, 1);
784
+ near = Math.min(near, Math.hypot(x - ax - t * ex, y - ay - t * ey));
785
+ }
786
+ return (out <= 0 ? out : near) - crown.r;
787
+ }
788
+ var RING_U = 158 * 35 * RAD2;
789
+ function ringWalk(sh) {
790
+ const ex = sh.eyes[1][0] + sh.brow.hw + 7;
791
+ const top = sh.brow.y - 30;
792
+ const bottom = sh.brow.y + 8;
793
+ let u = RING_U;
794
+ for (;u < 400; u += 2) {
795
+ const s = seat(sh.crown, 8, u);
796
+ const dx = s.x - ex;
797
+ const dy = s.y - clamp(s.y, top, bottom);
798
+ if (s.x > ex ? Math.hypot(dx, dy) > 27 : s.y < top - 27)
799
+ break;
800
+ }
801
+ return u > RING_U ? { rest: u + 20, min: u } : { rest: u, min: -Infinity };
802
+ }
803
+ function n(tag, key, attrs, children = []) {
804
+ const out = {};
805
+ for (const k in attrs) {
806
+ const v = attrs[k];
807
+ if (v !== undefined && v !== false)
808
+ out[k] = String(v);
809
+ }
810
+ return { tag, key, attrs: out, children };
811
+ }
812
+ function prune(node) {
813
+ if (node.attrs.opacity === "0")
814
+ return null;
815
+ const children = node.children.map(prune).filter((c) => c !== null);
816
+ if (node.tag === "g" && children.length === 0)
817
+ return null;
818
+ return { ...node, children };
819
+ }
820
+ function draw(who, pose, o) {
821
+ const sh = FACES[who.face];
822
+ const P = who.persona;
823
+ const mul = P.mul;
824
+ const add = P.add;
825
+ const g = (c) => pose[c] * (mul[c] ?? 1) + (add[c] ?? 0);
826
+ const id = o.id;
827
+ const { baseY, R } = frameOf(sh);
828
+ const defs = [];
829
+ const VBr = { x: VB[0], y: VB[1], width: VB[2], height: VB[3] };
830
+ defs.push(n("clipPath", "frame.clip", { id: `${id}-f` }, [
831
+ o.square ? n("rect", "frame.clip.shape", VBr) : n("circle", "frame.clip.shape", { cx: 170, cy: 170, r: 200 })
832
+ ]));
833
+ const k = o.riso ? smooth(120, 480, o.size) : 0;
834
+ const riso = k > 0.01;
835
+ const upp = VB[2] / Math.max(o.size, 1);
836
+ const mk = k * k;
837
+ const plate = (p, seedA, seedB, sc2) => n("filter", `riso.${p}`, {
838
+ id: `${id}-p${p}`,
839
+ x: "-20%",
840
+ y: "-20%",
841
+ width: "140%",
842
+ height: "140%",
843
+ "color-interpolation-filters": "sRGB"
844
+ }, [
845
+ n("feTurbulence", `riso.${p}.warp`, {
846
+ type: "fractalNoise",
847
+ baseFrequency: "0.032",
848
+ numOctaves: 2,
849
+ seed: seedA,
850
+ result: "warp"
851
+ }),
852
+ n("feDisplacementMap", `riso.${p}.disp`, {
853
+ in: "SourceGraphic",
854
+ in2: "warp",
855
+ scale: f2(sc2 * k),
856
+ xChannelSelector: "R",
857
+ yChannelSelector: "G",
858
+ result: "moved"
859
+ }),
860
+ n("feTurbulence", `riso.${p}.grain`, {
861
+ type: "fractalNoise",
862
+ baseFrequency: "0.85",
863
+ numOctaves: 2,
864
+ seed: seedB,
865
+ result: "grain"
866
+ }),
867
+ n("feColorMatrix", `riso.${p}.mask`, {
868
+ in: "grain",
869
+ type: "matrix",
870
+ values: `0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ${f3(-8 * mk)} 0 0 0 ${f3(1 + 5.85 * mk)}`,
871
+ result: "mask"
872
+ }),
873
+ n("feComposite", `riso.${p}.out`, {
874
+ in: "moved",
875
+ in2: "mask",
876
+ operator: "in"
877
+ })
878
+ ]);
879
+ const grainy = riso && o.frame !== "none";
880
+ if (riso)
881
+ defs.push(plate("A", 4, 11, 3.2), plate("B", 9, 23, 2.2));
882
+ if (grainy) {
883
+ const [r, gg, b] = o.frame === "ink" ? [0.953, 0.941, 0.91] : [0.102, 0.098, 0.094];
884
+ defs.push(n("filter", "grain.filter", {
885
+ id: `${id}-grain`,
886
+ x: 0,
887
+ y: 0,
888
+ width: 1,
889
+ height: 1,
890
+ "color-interpolation-filters": "sRGB"
891
+ }, [
892
+ n("feTurbulence", "grain.noise", {
893
+ type: "fractalNoise",
894
+ baseFrequency: "0.9",
895
+ numOctaves: 2,
896
+ seed: 5
897
+ }),
898
+ n("feColorMatrix", "grain.matrix", {
899
+ type: "matrix",
900
+ values: `0 0 0 0 ${r} 0 0 0 0 ${gg} 0 0 0 0 ${b} 7 0 0 0 -5.1`
901
+ })
902
+ ]));
903
+ }
904
+ const dim = clamp(g("dim"), 0, 1);
905
+ if (o.live || dim > 0.01)
906
+ defs.push(n("filter", "dim.filter", { id: `${id}-dim`, "color-interpolation-filters": "sRGB" }, [
907
+ n("feColorMatrix", "dim.matrix", {
908
+ type: "saturate",
909
+ values: f3(1 - dim * 0.55)
910
+ })
911
+ ]));
912
+ const alt = g("alt");
913
+ const rot = g("rot");
914
+ const x = g("x");
915
+ const sx = g("sx");
916
+ const sy = g("sy");
917
+ const B = sh.bottom;
918
+ const ty = baseY - alt * R;
919
+ const sc = (v) => Math.round(v * 1e5) / 1e5;
920
+ const [front, back] = (o.variant === "bloc" ? blocParts : peekParts)({
921
+ who,
922
+ pose,
923
+ o,
924
+ defs,
925
+ g
926
+ });
927
+ const off = (dx, dy) => riso ? `translate(${f2(dx * k * upp)} ${f2(dy * k * upp)})` : undefined;
928
+ const av = n("g", "av", {
929
+ transform: `translate(${f2(x)} ${f2(ty)}) rotate(${f2(rot)} 170 ${B}) translate(170 ${B}) scale(${sc(sx)} ${sc(sy)}) translate(-170 ${-B})`,
930
+ filter: dim > 0.01 ? `url(#${id}-dim)` : undefined
931
+ }, [
932
+ n("g", "plateA", {
933
+ transform: off(-0.55, 0.45),
934
+ filter: riso ? `url(#${id}-pA)` : undefined
935
+ }, front),
936
+ n("g", "plateB", {
937
+ transform: off(0.6, -0.5),
938
+ filter: riso ? `url(#${id}-pB)` : undefined
939
+ }, back)
940
+ ]);
941
+ const scene = [];
942
+ if (o.frame !== "none") {
943
+ const bg = o.frame === "ink" ? INK.ink : o.frame === "bone" ? INK.bone : INK.paper;
944
+ scene.push(n("rect", "bg", { ...VBr, fill: bg }));
945
+ const th = clamp(g("thing"), 0, 1);
946
+ const dots = [];
947
+ for (let j = 0;j < 3; j++)
948
+ for (let i = 0;i < 3; i++) {
949
+ const q = j * 3 + i;
950
+ const kk = smooth(q / 12, q / 12 + 0.35, th);
951
+ dots.push(n("circle", `lattice.${q}`, {
952
+ cx: 312 + i * 18,
953
+ cy: 2 + j * 18,
954
+ r: f2(1.2 + 2.2 * kk),
955
+ opacity: f3(kk * 0.55)
956
+ }));
957
+ }
958
+ scene.push(n("g", "lattice", { fill: o.frame === "ink" ? INK.bone : INK.ink }, dots));
959
+ }
960
+ scene.push(av);
961
+ if (grainy)
962
+ scene.push(n("rect", "grain", {
963
+ ...VBr,
964
+ filter: `url(#${id}-grain)`,
965
+ opacity: o.frame === "ink" ? 0.3 : 0.16,
966
+ "pointer-events": "none"
967
+ }));
968
+ const root = n("svg", "root", {
969
+ xmlns: "http://www.w3.org/2000/svg",
970
+ viewBox: VB.join(" "),
971
+ width: o.size,
972
+ height: o.size,
973
+ role: o.title === false ? undefined : "img",
974
+ "aria-label": o.title === false ? undefined : o.title,
975
+ "aria-hidden": o.title === false ? "true" : undefined
976
+ }, [
977
+ n("defs", "defs", {}, defs),
978
+ n("g", "frame", { "clip-path": `url(#${id}-f)` }, scene)
979
+ ]);
980
+ return o.live ? root : prune(root) ?? root;
981
+ }
982
+ var HAZE_LEVEL = {
983
+ medium: {
984
+ deep: [0.502, 0.224],
985
+ glow: [0.749, 0.337],
986
+ tint: [0.4, 0.5],
987
+ tintStops: [
988
+ [0, 0.878],
989
+ [0.15, 0.878],
990
+ [0.618, 0.396]
991
+ ]
992
+ },
993
+ light: {
994
+ deep: [0.298, 0.133],
995
+ glow: [0.549, 0.247],
996
+ tint: [0.31, 0.387],
997
+ tintStops: [
998
+ [0, 0.549],
999
+ [0.55, 0.247]
1000
+ ]
1001
+ }
1002
+ };
1003
+ function hazePaint(who, o) {
1004
+ const sh = FACES[who.face];
1005
+ const id = o.id;
1006
+ const L = HAZE_LEVEL[o.size < 32 ? "light" : "medium"];
1007
+ const xs = sh.crown.poly.map((p) => p[0]);
1008
+ const ys = sh.crown.poly.map((p) => p[1]);
1009
+ const x0 = Math.min(...xs) - sh.crown.r;
1010
+ const y0 = Math.min(...ys) - sh.crown.r;
1011
+ const W = Math.max(...xs) + sh.crown.r - x0;
1012
+ const H = sh.bottom - y0;
1013
+ const rnd = mulberry32(fnv1a(`haze:${who.key ?? ""}`));
1014
+ const warm = rnd() < 0.5;
1015
+ const left = rnd() < 0.5;
1016
+ const t = rnd();
1017
+ const tintX = 0.04 + 0.174 * t;
1018
+ const tintY = 0.649 + 0.202 * t;
1019
+ const glowX = 0.38 - 0.12 * rnd();
1020
+ const glowY = 0.12 + 0.099 * rnd();
1021
+ const deepX = 0.251 - 0.151 * rnd();
1022
+ const tintA = 90 - 180 * rnd();
1023
+ const at = (inset, onLeft) => x0 + W * (onLeft ? inset : 1 - inset);
1024
+ const i = WHEEL.indexOf(who.color);
1025
+ const near = WHEEL[(i + (warm ? WHEEL.length - 1 : 1)) % WHEEL.length];
1026
+ const { body, deep } = COLORS[who.color];
1027
+ const blob = (k, color, [cx, cy, a, rx, ry], stops) => n("radialGradient", `haze.${k}`, {
1028
+ id: `${id}-h${k[0]}`,
1029
+ gradientUnits: "userSpaceOnUse",
1030
+ cx: 0,
1031
+ cy: 0,
1032
+ r: 1,
1033
+ gradientTransform: `translate(${f2(cx)} ${f2(cy)}) rotate(${f2(a)}) scale(${f2(rx)} ${f2(ry)})`
1034
+ }, [...stops, [1, 0]].map(([at2, op], j) => n("stop", `haze.${k}.${j}`, {
1035
+ offset: at2,
1036
+ "stop-color": color,
1037
+ "stop-opacity": op
1038
+ })));
1039
+ const fade = ([a, b]) => [
1040
+ [0, a],
1041
+ [0.55, b]
1042
+ ];
1043
+ const blobs = [
1044
+ blob("deep", deep, [at(deepX, !left), y0 + H * 0.95, 0, W * 0.5, W * 0.35], fade(L.deep)),
1045
+ blob("tint", HAZE[near].tint, [at(tintX, left), y0 + H * tintY, tintA, W * L.tint[0], W * L.tint[1]], L.tintStops),
1046
+ blob("glow", HAZE[who.color].glow, [at(glowX, !left), y0 + H * glowY, 0, W * 0.475, W * 0.356], fade(L.glow))
1047
+ ];
1048
+ const shape = (key, paint) => ("d" in sh.body) ? n("path", key, {
1049
+ d: sh.body.d,
1050
+ fill: paint,
1051
+ stroke: sh.body.stroke ? paint : undefined,
1052
+ "stroke-width": sh.body.stroke,
1053
+ "stroke-linejoin": sh.body.stroke ? "round" : undefined
1054
+ }) : n("circle", key, {
1055
+ cx: sh.body.circle[0],
1056
+ cy: sh.body.circle[1],
1057
+ r: sh.body.circle[2],
1058
+ fill: paint
1059
+ });
1060
+ return [
1061
+ ...blobs,
1062
+ n("g", "haze", { id: `${id}-hz` }, [
1063
+ shape("haze.body", body),
1064
+ ...["deep", "tint", "glow"].map((k) => shape(`haze.${k}.fill`, `url(#${id}-h${k[0]})`))
1065
+ ])
1066
+ ];
1067
+ }
1068
+ function peekParts({ who, pose, o, defs, g }) {
1069
+ const sh = FACES[who.face];
1070
+ const { body: fill, deep } = COLORS[who.color];
1071
+ const P = who.persona;
1072
+ const spread = P.spread;
1073
+ const id = o.id;
1074
+ const st = strokeFor2(o.size);
1075
+ const haze = o.variant === "haze";
1076
+ if (haze)
1077
+ defs.push(...hazePaint(who, o));
1078
+ const bodyNode = haze ? n("use", "body", { href: `#${id}-hz` }) : ("d" in sh.body) ? n("path", "body", {
1079
+ d: sh.body.d,
1080
+ fill,
1081
+ stroke: sh.body.stroke ? fill : undefined,
1082
+ "stroke-width": sh.body.stroke,
1083
+ "stroke-linejoin": sh.body.stroke ? "round" : undefined
1084
+ }) : n("circle", "body", {
1085
+ cx: sh.body.circle[0],
1086
+ cy: sh.body.circle[1],
1087
+ r: sh.body.circle[2],
1088
+ fill
1089
+ });
1090
+ const eyeS = g("eyeS");
1091
+ const rx = sh.rx * eyeS;
1092
+ const ry = sh.ry * eyeS;
1093
+ const lid0 = g("lid");
1094
+ const lidAsym = g("lidAsym");
1095
+ const lower = clamp(g("lower"), 0, 1);
1096
+ const blink = pose.blink;
1097
+ const pupil = g("pupil");
1098
+ const gx = clamp(g("gx"), -1.1, 1.1);
1099
+ const gy = clamp(g("gy"), -1.1, 1.1);
1100
+ const tilt = g("lidTilt");
1101
+ const shine = clamp(g("shine"), 0, 1);
1102
+ const sw = sh.brow.w * st;
1103
+ const bead = who.eyes === "bead";
1104
+ const eyes = sh.eyes.map(([ex, ey], i) => {
1105
+ const side = i === 0 ? -1 : 1;
1106
+ const kk = `eye${i}`;
1107
+ let lid = clamp(lid0 + (i === 0 ? lidAsym : -lidAsym * 0.3), 0, 1);
1108
+ lid = lid + (1 - lid) * blink;
1109
+ defs.push(n("clipPath", `${kk}.clip`, { id: `${id}-e${i}` }, [
1110
+ n("ellipse", `${kk}.clip.shape`, {
1111
+ cx: 0,
1112
+ cy: 0,
1113
+ rx: f2(rx),
1114
+ ry: f2(ry)
1115
+ })
1116
+ ]), n("clipPath", `${kk}.lidclip`, { id: `${id}-l${i}` }, [
1117
+ n("ellipse", `${kk}.lidclip.shape`, {
1118
+ cx: 0,
1119
+ cy: 0,
1120
+ rx: f2(rx + 1.6),
1121
+ ry: f2(ry + 1.6)
1122
+ })
1123
+ ]));
1124
+ const br = Math.min(sh.rx, sh.ry) * 0.62;
1125
+ const pr = bead ? br * eyeS * (0.4 + 0.6 * pupil) : sh.pr * pupil;
1126
+ const base = bead ? br : sh.pr;
1127
+ const px = gx * Math.max(0, sh.rx - base) * 0.9;
1128
+ const py = gy * Math.max(0, sh.ry - base) * 0.8;
1129
+ const rimW = Math.min(sw * 0.8, Math.min(sh.rx, sh.ry) * 0.28);
1130
+ const inner = [];
1131
+ if (!bead)
1132
+ inner.push(n("ellipse", `${kk}.white`, {
1133
+ cx: 0,
1134
+ cy: 0,
1135
+ rx: f2(rx),
1136
+ ry: f2(ry),
1137
+ fill: INK.paper
1138
+ }));
1139
+ inner.push(n("circle", `${kk}.pupil`, {
1140
+ cx: f2(px),
1141
+ cy: f2(py),
1142
+ r: f2(pr),
1143
+ fill: INK.ink
1144
+ }), n("circle", `${kk}.shine`, {
1145
+ cx: f2(px + pr * 0.44),
1146
+ cy: f2(py - pr * 0.44),
1147
+ r: f2(pr * (bead ? 0.26 : 0.3)),
1148
+ fill: INK.paper,
1149
+ opacity: f2(shine)
1150
+ }));
1151
+ if (who.eyes === "ring")
1152
+ inner.push(n("ellipse", `${kk}.rim`, {
1153
+ cx: 0,
1154
+ cy: 0,
1155
+ rx: f2(rx - rimW / 2),
1156
+ ry: f2(ry - rimW / 2),
1157
+ fill: "none",
1158
+ stroke: INK.ink,
1159
+ "stroke-width": f2(rimW)
1160
+ }));
1161
+ const edge = -ry - 1.6 + lid * (2 * ry + 3.2);
1162
+ const a = side * tilt;
1163
+ const lrx = rx * 1.55;
1164
+ const lry = ry * 1.15;
1165
+ const top = ry + 1.6 - lower * ry * 1.5;
1166
+ const [etx, ety] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
1167
+ const lids = [
1168
+ n("rect", `${kk}.lid`, {
1169
+ x: f2(-rx * 2.2),
1170
+ y: f2(-ry * 3),
1171
+ width: f2(rx * 4.4),
1172
+ height: f2(Math.max(0, edge + ry * 3)),
1173
+ transform: `rotate(${f2(a)})`,
1174
+ fill
1175
+ }),
1176
+ n("ellipse", `${kk}.lower`, {
1177
+ cx: 0,
1178
+ cy: f2(top + lry),
1179
+ rx: f2(lrx),
1180
+ ry: f2(lry),
1181
+ fill
1182
+ })
1183
+ ];
1184
+ if (haze)
1185
+ defs.push(n("clipPath", `${kk}.lidshape`, { id: `${id}-s${i}` }, lids));
1186
+ const parts = [
1187
+ n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
1188
+ n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, haze ? [
1189
+ n("g", `${kk}.lidpaint`, { "clip-path": `url(#${id}-s${i})` }, [
1190
+ n("use", `${kk}.lidbody`, {
1191
+ href: `#${id}-hz`,
1192
+ transform: `translate(${f2(-etx)} ${f2(-ety)})`
1193
+ })
1194
+ ])
1195
+ ] : lids)
1196
+ ];
1197
+ if (who.eyes === "ring")
1198
+ parts.push(n("g", `${kk}.edges`, {
1199
+ "clip-path": `url(#${id}-e${i})`,
1200
+ fill: "none",
1201
+ stroke: INK.ink,
1202
+ "stroke-width": f2(rimW)
1203
+ }, [
1204
+ n("path", `${kk}.edge`, {
1205
+ d: `M${f2(-rx * 2.2)} ${f2(edge)}H${f2(rx * 2.2)}`,
1206
+ transform: `rotate(${f2(a)})`,
1207
+ opacity: lid > 0.02 ? 1 : 0
1208
+ }),
1209
+ n("ellipse", `${kk}.edge.lower`, {
1210
+ cx: 0,
1211
+ cy: f2(top + lry),
1212
+ rx: f2(lrx),
1213
+ ry: f2(lry),
1214
+ opacity: lower > 0.02 ? 1 : 0
1215
+ })
1216
+ ]));
1217
+ const co = smooth(0.86, 1, lid);
1218
+ const yy = Math.min(edge, ry) - 1;
1219
+ const hw = rx * 0.92;
1220
+ parts.push(n("path", `${kk}.closed`, {
1221
+ d: `M${f2(-hw)} ${f2(yy - 2)}Q0 ${f2(yy + 6)} ${f2(hw)} ${f2(yy - 2)}`,
1222
+ transform: `rotate(${f2(a * 0.6)})`,
1223
+ fill: "none",
1224
+ stroke: INK.ink,
1225
+ "stroke-linecap": "round",
1226
+ "stroke-width": f2(sw * 0.85),
1227
+ opacity: co > 0.01 ? f2(co) : 0
1228
+ }));
1229
+ return n("g", kk, { transform: `translate(${f2(etx)} ${f2(ety)})` }, parts);
1230
+ });
1231
+ const browY = g("browY") - (eyeS - 1) * sh.ry * 0.9;
1232
+ const btilt = g("browTilt");
1233
+ const arch = g("browArch");
1234
+ const basym = g("browAsym");
1235
+ const bw = sw * g("browW");
1236
+ const brows = sh.eyes.map(([ex], i) => {
1237
+ const side = i === 0 ? -1 : 1;
1238
+ const hw = sh.brow.hw;
1239
+ const c = -sh.brow.arch * arch;
1240
+ const [w2, ca, k2] = who.brows === "bar" ? [hw * 0.9, c * 0.3, 1.75] : who.brows === "dash" ? [hw * 0.42, c * 0.25, 1.9] : [hw, c, 1];
1241
+ const ti = bw * 1.7;
1242
+ const to = bw * 0.45;
1243
+ const [tl, tr] = side < 0 ? [to, ti] : [ti, to];
1244
+ const wedge = who.brows === "wedge";
1245
+ const [hl, hr] = wedge ? [tl / 2 + to / 2, tr / 2 + to / 2] : [0, 0];
1246
+ const half = (t) => wedge ? hl + (hr - hl) * t : bw * k2 / 2;
1247
+ const bx = ex + side * spread;
1248
+ const by = sh.brow.y + browY + (i === 1 ? -basym : basym * 0.3);
1249
+ const cos = Math.cos(side * btilt * RAD2);
1250
+ const sin = Math.sin(side * btilt * RAD2);
1251
+ const inside = (dy) => {
1252
+ for (let t = 0;t <= 1; t += 0.25) {
1253
+ const lx = w2 * (2 * t - 1);
1254
+ const ly = 2 * t * (1 - t) * ca;
1255
+ const px = bx + lx * cos - ly * sin;
1256
+ const py = by + dy + lx * sin + ly * cos;
1257
+ if (depth(sh.crown, px, py) + half(t) + 2 > 0)
1258
+ return false;
1259
+ }
1260
+ return true;
1261
+ };
1262
+ let lo = 0;
1263
+ let hi = 0;
1264
+ if (!inside(0)) {
1265
+ hi = 40;
1266
+ for (let k = 0;k < 12; k++) {
1267
+ const mid2 = (lo + hi) / 2;
1268
+ if (inside(mid2))
1269
+ hi = mid2;
1270
+ else
1271
+ lo = mid2;
1272
+ }
1273
+ }
1274
+ const transform = `translate(${f2(bx)} ${f2(by + hi)}) rotate(${f2(side * btilt)})`;
1275
+ const key = `brow${i}`;
1276
+ if (wedge) {
1277
+ const dm = (tl + tr) / 2 * 0.75;
1278
+ return n("path", key, {
1279
+ d: `M${f2(-hw)} ${f2(-tl / 2)}Q0 ${f2(c - dm)} ${f2(hw)} ${f2(-tr / 2)}L${f2(hw)} ${f2(tr / 2)}Q0 ${f2(c + dm)} ${f2(-hw)} ${f2(tl / 2)}Z`,
1280
+ transform,
1281
+ fill: INK.ink,
1282
+ stroke: INK.ink,
1283
+ "stroke-linejoin": "round",
1284
+ "stroke-width": f2(to)
1285
+ });
1286
+ }
1287
+ return n("path", key, {
1288
+ d: `M${f2(-w2)} 0Q0 ${f2(ca)} ${f2(w2)} 0`,
1289
+ transform,
1290
+ fill: "none",
1291
+ stroke: INK.ink,
1292
+ "stroke-linecap": "round",
1293
+ "stroke-linejoin": "round",
1294
+ "stroke-width": f2(bw * k2)
1295
+ });
1296
+ });
1297
+ const m = sh.mouth;
1298
+ const mw = m.w * Math.max(0.05, g("mw"));
1299
+ const mt = g("mt") * m.d;
1300
+ const mb = g("mb") * m.d;
1301
+ const kb = 4 / 3;
1302
+ const w7 = mw * 0.72;
1303
+ const mid = (mt + mb) / 2 * kb;
1304
+ const d = who.mouth === "poly" ? `M${f2(-mw)} 0L0 ${f2(mt)}L${f2(mw)} 0L0 ${f2(mb)}Z` : who.mouth === "box" ? `M${f2(-mw)} 0L${f2(-w7)} ${f2(mb)}L${f2(w7)} ${f2(mb)}L${f2(mw)} 0L${f2(w7)} ${f2(mt)}L${f2(-w7)} ${f2(mt)}Z` : who.mouth === "line" ? `M${f2(-mw)} 0C${f2(-mw * 0.5)} ${f2(mid)} ${f2(mw * 0.5)} ${f2(mid)} ${f2(mw)} 0` : `M${f2(-mw)} 0C${f2(-mw)} ${f2(mb * kb)} ${f2(mw)} ${f2(mb * kb)} ${f2(mw)} 0C${f2(mw)} ${f2(mt * kb)} ${f2(-mw)} ${f2(mt * kb)} ${f2(-mw)} 0Z`;
1305
+ const open = who.mouth === "line";
1306
+ const mouth = n("path", "mouth", {
1307
+ d,
1308
+ transform: `translate(${f2(m.x + g("mx"))} ${f2(m.y + g("my"))}) rotate(${f2(g("mk"))})`,
1309
+ fill: open ? "none" : INK.ink,
1310
+ stroke: INK.ink,
1311
+ "stroke-linecap": "round",
1312
+ "stroke-linejoin": "round",
1313
+ "stroke-width": f2(open ? sw : m.sw * st)
1314
+ });
1315
+ const blush = clamp(g("blush"), 0, 1);
1316
+ const cheeks = sh.cheeks.map(([cx, cy], i) => {
1317
+ const key = `cheek${i}`;
1318
+ const at = {
1319
+ opacity: f2(blush),
1320
+ transform: `translate(${f2(cx + (i === 0 ? -1 : 1) * spread * 0.6)} ${f2(cy - lower * 4)}) scale(${f2(0.6 + 0.4 * blush)}) translate(${-cx} ${-cy})`
1321
+ };
1322
+ if (who.cheeks === "dots") {
1323
+ const r = sh.cry * 0.9;
1324
+ return n("g", key, { ...at, fill: deep }, [
1325
+ n("circle", `${key}.0`, { cx: f2(cx - sh.crx * 1.05), cy, r: f2(r) }),
1326
+ n("circle", `${key}.1`, { cx, cy: f2(cy + sh.cry * 0.5), r: f2(r) }),
1327
+ n("circle", `${key}.2`, { cx: f2(cx + sh.crx * 1.05), cy, r: f2(r) })
1328
+ ]);
1329
+ }
1330
+ if (who.cheeks === "lines") {
1331
+ const h = sh.cry * 1.3;
1332
+ return n("g", key, {
1333
+ ...at,
1334
+ fill: "none",
1335
+ stroke: deep,
1336
+ "stroke-linecap": "round",
1337
+ "stroke-width": f2(sh.cry * 0.78)
1338
+ }, [-1, 0, 1].map((j) => {
1339
+ const x0 = cx + j * sh.crx * 0.8;
1340
+ return n("path", `${key}.${j + 1}`, {
1341
+ d: `M${f2(x0 + h * 0.5)} ${f2(cy - h)}L${f2(x0 - h * 0.5)} ${f2(cy + h)}`
1342
+ });
1343
+ }));
1344
+ }
1345
+ return n("ellipse", key, {
1346
+ cx,
1347
+ cy,
1348
+ rx: sh.crx,
1349
+ ry: sh.cry,
1350
+ fill: deep,
1351
+ ...at
1352
+ });
1353
+ });
1354
+ const hair = g("hair");
1355
+ const lag = pose.lag;
1356
+ let traitT;
1357
+ let shape;
1358
+ if (who.trait === "square") {
1359
+ const s = seat(sh.crown, 22 + hair * 10, 0);
1360
+ traitT = `translate(${f2(s.x)} ${f2(s.y + lag)}) rotate(${f2(s.a + 22 + hair * 23)})`;
1361
+ shape = n("rect", "trait.shape", {
1362
+ x: -11,
1363
+ y: -11,
1364
+ width: 22,
1365
+ height: 22,
1366
+ fill: deep
1367
+ });
1368
+ } else if (who.trait === "fin") {
1369
+ const s = seat(sh.crown, -6, -164 * 22 * RAD2);
1370
+ const r = s.a + (hair >= 0 ? hair * 20 : hair * 55) - lag * 0.8;
1371
+ traitT = `translate(${f2(s.x)} ${f2(s.y + lag * 0.25)}) rotate(${f2(r)})`;
1372
+ shape = n("path", "trait.shape", {
1373
+ d: "M0 0L0 -40A40 40 0 0 1 40 0Z",
1374
+ fill: deep
1375
+ });
1376
+ } else if (who.trait === "ring") {
1377
+ const b = 35 - hair * 14 - (hair < 0 ? hair * -21 : 0) + lag * 0.9;
1378
+ const w2 = ringWalk(sh);
1379
+ const s = seat(sh.crown, 8, Math.max(w2.min, w2.rest + 158 * (b - 35) * RAD2));
1380
+ traitT = `translate(${f2(s.x)} ${f2(s.y)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
1381
+ shape = n("circle", "trait.shape", {
1382
+ cx: 0,
1383
+ cy: 0,
1384
+ r: 12.5,
1385
+ fill: "none",
1386
+ stroke: deep,
1387
+ "stroke-width": f2(7 * (pose.expression === "angry" ? 1.25 : 1))
1388
+ });
1389
+ } else if (who.trait === "dot") {
1390
+ const s = clamp(-hair, 0, 1.2);
1391
+ const th = Math.min(1, s / 0.35) * 61.05 * RAD2;
1392
+ const travel = Math.max(0, s - 0.35) / 0.65 * 92;
1393
+ const p = seat(sh.crown, 12.5, 26.1 * th + travel);
1394
+ let cy = p.y;
1395
+ if (hair > 0)
1396
+ cy -= hair * 18;
1397
+ cy += lag * (s > 0.35 ? 0.3 : 1);
1398
+ traitT = `translate(${f2(p.x)} ${f2(cy)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
1399
+ shape = n("circle", "trait.shape", { cx: 0, cy: 0, r: 12.5, fill: deep });
1400
+ } else {
1401
+ const s = seat(sh.crown, 4 + Math.max(0, hair) * 9, 0);
1402
+ const r = s.a + (hair < 0 ? -hair * 38 : 0) - lag * 0.5;
1403
+ traitT = `translate(${f2(s.x)} ${f2(s.y + lag)}) rotate(${f2(r)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
1404
+ shape = n("path", "trait.shape", {
1405
+ d: "M0 -27L13 -3L-13 -3Z",
1406
+ fill: deep,
1407
+ stroke: deep,
1408
+ "stroke-width": 6,
1409
+ "stroke-linejoin": "round"
1410
+ });
1411
+ }
1412
+ const trait = n("g", "trait", { transform: traitT }, [shape]);
1413
+ const wardrobe = [];
1414
+ const w = (px) => f2(px * Math.min(st, 1.6));
1415
+ const line = {
1416
+ fill: "none",
1417
+ stroke: INK.ink,
1418
+ "stroke-width": w(5.25),
1419
+ "stroke-linecap": "round",
1420
+ "stroke-linejoin": "round"
1421
+ };
1422
+ if (who.eyewear !== "none") {
1423
+ const lx = sh.eyes[0][0] - spread;
1424
+ const rr = sh.eyes[1][0] + spread;
1425
+ const ey = sh.eyes[0][1];
1426
+ const gap = rr - lx;
1427
+ const erx = Math.min(rx + 9, (gap - 15) / 2);
1428
+ const ery = ry + 9;
1429
+ const glasses = who.eyewear === "glasses";
1430
+ const lensX = glasses ? erx : Math.min(rx + 11, (gap - 12) / 2);
1431
+ const top = glasses ? ey - 4 : ey - ry - 2;
1432
+ const frames = [
1433
+ n("path", "eyewear.bridge", {
1434
+ ...line,
1435
+ d: `M${f2(lx + lensX)} ${f2(top)}Q${f2((lx + rr) / 2)} ${f2(top - (glasses ? 13 : 0))} ${f2(rr - lensX)} ${f2(top)}`
1436
+ })
1437
+ ];
1438
+ for (const [i, cx] of [lx, rr].entries()) {
1439
+ const key = `eyewear.lens${i}`;
1440
+ frames.push(n("path", `${key}.arm`, {
1441
+ ...line,
1442
+ d: i === 0 ? `M${f2(cx - lensX - 9)} ${f2(top + 2)}L${f2(cx - lensX)} ${f2(top)}` : `M${f2(cx + lensX)} ${f2(top)}L${f2(cx + lensX + 9)} ${f2(top + 2)}`
1443
+ }));
1444
+ if (glasses) {
1445
+ frames.push(n("ellipse", key, {
1446
+ ...line,
1447
+ cx: f2(cx),
1448
+ cy: ey,
1449
+ rx: f2(erx),
1450
+ ry: f2(ery)
1451
+ }));
1452
+ frames.push(n("path", `${key}.glint`, {
1453
+ ...line,
1454
+ stroke: INK.paper,
1455
+ "stroke-width": w(3),
1456
+ d: `M${f2(cx - erx * 0.65)} ${f2(ey - ery * 0.45)}Q${f2(cx - erx * 0.5)} ${f2(ey - ery * 0.8)} ${f2(cx - erx * 0.2)} ${f2(ey - ery * 0.86)}`
1457
+ }));
1458
+ } else {
1459
+ const h = ry * 2 + 9;
1460
+ frames.push(n("path", key, {
1461
+ d: `M${f2(cx - lensX)} ${f2(top)}H${f2(cx + lensX)}V${f2(top + h * 0.55)}Q${f2(cx + lensX)} ${f2(top + h)} ${f2(cx)} ${f2(top + h)}Q${f2(cx - lensX)} ${f2(top + h)} ${f2(cx - lensX)} ${f2(top + h * 0.55)}Z`,
1462
+ fill: INK.ink,
1463
+ stroke: INK.ink,
1464
+ "stroke-width": w(3),
1465
+ "stroke-linejoin": "round"
1466
+ }));
1467
+ frames.push(n("path", `${key}.glint`, {
1468
+ ...line,
1469
+ stroke: INK.paper,
1470
+ "stroke-width": w(4),
1471
+ d: `M${f2(cx - lensX * 0.58)} ${f2(top + 19)}l8 -8m-1 15 6 -6`
1472
+ }), n("circle", `${key}.pin`, {
1473
+ cx: f2(cx + lensX * 0.55),
1474
+ cy: f2(top + h - 9),
1475
+ r: 1.7,
1476
+ fill: INK.paper
1477
+ }));
1478
+ }
1479
+ }
1480
+ wardrobe.push(n("g", "eyewear", {}, frames));
1481
+ }
1482
+ if (who.headwear !== "none") {
1483
+ const side = who.trait === "ring" || who.trait === "dot" ? -1 : 1;
1484
+ const cap = who.headwear === "cap";
1485
+ const sprout = who.headwear === "sprout";
1486
+ const inset = sprout ? -6 : cap ? -8 : 0;
1487
+ const offset = sprout ? 34 : 40;
1488
+ let lo = 0;
1489
+ let hi = 160;
1490
+ for (let i = 0;i < 16; i++) {
1491
+ const mid2 = (lo + hi) / 2;
1492
+ const p = seat(sh.crown, inset, side * mid2);
1493
+ if (side * (p.x - 170) < offset)
1494
+ lo = mid2;
1495
+ else
1496
+ hi = mid2;
1497
+ }
1498
+ const anchor = seat(sh.crown, inset, side * (lo + hi) / 2);
1499
+ const angle = clamp(anchor.a, -40, 40);
1500
+ const parts = [];
1501
+ if (cap) {
1502
+ parts.push(n("path", "headwear.crown", {
1503
+ ...line,
1504
+ fill: INK.paper,
1505
+ d: "M-33 0C-35 -20 -20 -39 0 -39C20 -39 35 -20 33 0Z"
1506
+ }), n("path", "headwear.seam", {
1507
+ ...line,
1508
+ "stroke-width": w(3.5),
1509
+ d: "M-4 -38Q9 -24 8 -1"
1510
+ }), n("path", "headwear.brim", {
1511
+ ...line,
1512
+ "stroke-width": w(7),
1513
+ d: side > 0 ? "M-35 1Q8 4 52 3" : "M-52 3Q-8 4 35 1"
1514
+ }), n("circle", "headwear.button", {
1515
+ cx: 0,
1516
+ cy: -40,
1517
+ r: 3.5,
1518
+ fill: INK.ink
1519
+ }));
1520
+ } else if (sprout) {
1521
+ parts.push(n("path", "headwear.halo", {
1522
+ ...line,
1523
+ stroke: fill,
1524
+ "stroke-width": w(11),
1525
+ d: `${STEM}${LEAF0}${LEAF1}`
1526
+ }), n("path", "headwear.stem", {
1527
+ ...line,
1528
+ "stroke-width": w(4.5),
1529
+ d: STEM
1530
+ }), n("path", "headwear.leaf0", {
1531
+ ...line,
1532
+ "stroke-width": w(4),
1533
+ fill: COLORS.mint.body,
1534
+ d: LEAF0
1535
+ }), n("path", "headwear.leaf1", {
1536
+ ...line,
1537
+ "stroke-width": w(4),
1538
+ fill: COLORS.mint.deep,
1539
+ d: LEAF1
1540
+ }));
1541
+ } else {
1542
+ parts.push(n("path", "headwear.wings", {
1543
+ d: "M-3 0C-13 -16 -28 -23 -29 -10C-31 2 -26 19 -16 13L-3 4L3 4L16 13C26 19 31 2 29 -10C28 -23 13 -16 3 0Z",
1544
+ fill: INK.ink,
1545
+ stroke: fill,
1546
+ "stroke-width": w(6),
1547
+ "stroke-linejoin": "round",
1548
+ "paint-order": "stroke"
1549
+ }), n("ellipse", "headwear.knot", {
1550
+ cx: 0,
1551
+ cy: 1,
1552
+ rx: 6,
1553
+ ry: 8,
1554
+ fill: ACCESSORY_INK.knot
1555
+ }));
1556
+ }
1557
+ wardrobe.push(n("g", "headwear", {
1558
+ transform: `translate(${f2(anchor.x)} ${f2(anchor.y)}) rotate(${f2(sprout ? angle * 0.5 : angle)})`
1559
+ }, parts));
1560
+ }
1561
+ if (who.neckwear === "tie") {
1562
+ const skew = Math.abs(Math.sin(g("mk") * RAD2)) * mw;
1563
+ const bottom = Math.max(0, mt * kb, mb * kb) + skew + m.sw * st;
1564
+ wardrobe.push(n("g", "neckwear", {
1565
+ transform: `translate(${m.x} ${f2(m.y + g("my") + bottom + 8)})`,
1566
+ fill: INK.ink
1567
+ }, [
1568
+ n("path", "neckwear.knot", {
1569
+ d: "M-9 0Q-11 0 -9 4L-5 11Q0 15 5 11L9 4Q11 0 9 0Z"
1570
+ }),
1571
+ n("path", "neckwear.blade", {
1572
+ d: "M-4 15L-13 63Q-14 66 -11 69L0 79L11 69Q14 66 13 63L4 15Z"
1573
+ })
1574
+ ]));
1575
+ }
1576
+ return [
1577
+ [bodyNode, ...cheeks, trait],
1578
+ [...eyes, ...brows, mouth, ...wardrobe]
1579
+ ];
1580
+ }
1581
+
1582
+ // src/animate.ts
1583
+ var GROUP_OF = {};
1584
+ for (const gr of Object.keys(GROUPS))
1585
+ for (const c of GROUPS[gr])
1586
+ GROUP_OF[c] = gr;
1587
+ var CHANNELS = Object.keys(GROUP_OF);
1588
+ var DEF_DELAY = {
1589
+ eyes: 0,
1590
+ pupil: 50,
1591
+ brows: 110,
1592
+ mouth: 170,
1593
+ extra: 140,
1594
+ body: 230
1595
+ };
1596
+ var DEF_SPR = {
1597
+ eyes: [14, 0.82],
1598
+ pupil: [15, 0.8],
1599
+ brows: [11, 0.72],
1600
+ mouth: [12, 0.74],
1601
+ body: [7, 0.72],
1602
+ extra: [8, 0.5]
1603
+ };
1604
+ var CHOREO = {
1605
+ happy: {
1606
+ delay: {
1607
+ eyes: 0,
1608
+ pupil: 40,
1609
+ brows: 120,
1610
+ mouth: 200,
1611
+ extra: 220,
1612
+ body: 280
1613
+ },
1614
+ spr: { body: [9, 0.42], extra: [9, 0.35] },
1615
+ keys: [
1616
+ [280, { alt: 0.2 }, { body: [11, 0.5] }],
1617
+ [520, { alt: 0.05 }, { body: [8, 0.38] }]
1618
+ ]
1619
+ },
1620
+ sad: {
1621
+ delay: {
1622
+ eyes: 0,
1623
+ pupil: 160,
1624
+ brows: 260,
1625
+ mouth: 380,
1626
+ extra: 420,
1627
+ body: 560
1628
+ },
1629
+ spr: {
1630
+ eyes: [6, 0.95],
1631
+ pupil: [5, 0.95],
1632
+ brows: [6, 0.95],
1633
+ mouth: [6, 0.9],
1634
+ body: [3.2, 0.95],
1635
+ extra: [3, 0.9]
1636
+ }
1637
+ },
1638
+ angry: {
1639
+ delay: { brows: 0, eyes: 40, pupil: 60, mouth: 160, body: 100, extra: 60 },
1640
+ spr: { brows: [18, 0.7], eyes: [16, 0.75], body: [14, 0.5] }
1641
+ },
1642
+ sleepy: {
1643
+ delay: {
1644
+ eyes: 0,
1645
+ pupil: 200,
1646
+ brows: 750,
1647
+ mouth: 950,
1648
+ extra: 900,
1649
+ body: 1150
1650
+ },
1651
+ spr: {
1652
+ eyes: [2.6, 1],
1653
+ pupil: [3, 1],
1654
+ brows: [3.2, 1],
1655
+ mouth: [3, 1],
1656
+ extra: [2.4, 0.9],
1657
+ body: [1.9, 1]
1658
+ },
1659
+ keys: [
1660
+ [0, { lid: 0.62 }],
1661
+ [1300, { lid: 1 }]
1662
+ ]
1663
+ },
1664
+ curious: {
1665
+ delay: { eyes: 0, pupil: 0, brows: 220, mouth: 320, extra: 0, body: 0 },
1666
+ spr: { body: [6, 0.95] },
1667
+ keys: [
1668
+ [0, { alt: -0.8, rot: 0, gx: 0, gy: -0.1, thing: 0 }],
1669
+ [900, { alt: -0.56 }, { body: [2.2, 1] }],
1670
+ [1500, { thing: 1 }, { extra: [5, 0.9] }],
1671
+ [2200, { look: 1 }, { pupil: [6, 0.9] }],
1672
+ [2500, { rot: 7 }, { body: [3, 0.9] }],
1673
+ [2900, { alt: -0.32 }, { body: [2, 1] }]
1674
+ ]
1675
+ },
1676
+ surprised: {
1677
+ delay: { eyes: 0, pupil: 0, brows: 0, mouth: 40, extra: 60, body: 0 },
1678
+ spr: {
1679
+ eyes: [30, 0.62],
1680
+ pupil: [26, 0.7],
1681
+ brows: [24, 0.55],
1682
+ mouth: [22, 0.6],
1683
+ extra: [15, 0.32]
1684
+ },
1685
+ keys: [
1686
+ [0, { alt: (v) => v - 0.1, sy: 0.9, sx: 1.05 }, { body: [30, 1] }],
1687
+ [110, { alt: 0.36, sy: 1.08, sx: 0.97 }, { body: [17, 0.42] }],
1688
+ [380, { sy: 1, sx: 1 }, { body: [10, 0.5] }]
1689
+ ]
1690
+ },
1691
+ excited: {
1692
+ delay: { eyes: 0, pupil: 30, brows: 80, mouth: 140, extra: 120, body: 180 },
1693
+ spr: { body: [9, 0.4], extra: [12, 0.3] }
1694
+ },
1695
+ confused: {
1696
+ delay: {
1697
+ eyes: 0,
1698
+ brows: 60,
1699
+ pupil: 100,
1700
+ mouth: 250,
1701
+ body: 200,
1702
+ extra: 200
1703
+ },
1704
+ spr: { body: [5, 0.7] }
1705
+ },
1706
+ bored: {
1707
+ delay: {
1708
+ eyes: 0,
1709
+ pupil: 300,
1710
+ brows: 500,
1711
+ mouth: 600,
1712
+ extra: 700,
1713
+ body: 800
1714
+ },
1715
+ spr: {
1716
+ eyes: [3, 1],
1717
+ pupil: [2.5, 1],
1718
+ brows: [3, 1],
1719
+ mouth: [3, 1],
1720
+ body: [1.6, 1],
1721
+ extra: [2, 1]
1722
+ }
1723
+ },
1724
+ attentive: {
1725
+ delay: {
1726
+ eyes: 0,
1727
+ pupil: 180,
1728
+ brows: 90,
1729
+ mouth: 240,
1730
+ extra: 200,
1731
+ body: 360
1732
+ },
1733
+ spr: {
1734
+ eyes: [20, 0.8],
1735
+ pupil: [20, 0.85],
1736
+ brows: [16, 0.7],
1737
+ body: [5, 0.78],
1738
+ extra: [11, 0.4]
1739
+ }
1740
+ }
1741
+ };
1742
+ var ATTENTION = {
1743
+ normal: 0.8,
1744
+ happy: 0.6,
1745
+ sad: 0.2,
1746
+ angry: 0.6,
1747
+ sleepy: 0,
1748
+ curious: 1,
1749
+ surprised: 0.5,
1750
+ excited: 0.5,
1751
+ confused: 0.3,
1752
+ bored: 0.1,
1753
+ attentive: 1
1754
+ };
1755
+ var BLINK = {
1756
+ normal: [2.4, 5],
1757
+ happy: [2.2, 4],
1758
+ sad: [3, 6],
1759
+ angry: [3.5, 6],
1760
+ sleepy: null,
1761
+ curious: [3.5, 6],
1762
+ surprised: [3, 5],
1763
+ excited: [1.8, 3.2],
1764
+ confused: [2, 4],
1765
+ bored: [4.5, 7.5],
1766
+ attentive: [4.5, 7.5]
1767
+ };
1768
+ var rand = (a, b) => a + Math.random() * (b - a);
1769
+ var zero = () => {
1770
+ const o = {};
1771
+ for (const c of CHANNELS)
1772
+ o[c] = 0;
1773
+ return o;
1774
+ };
1775
+ var ALL = new Set;
1776
+ var LIVE = new Set;
1777
+ var raf = 0;
1778
+ var last = 0;
1779
+ function tick(now) {
1780
+ const dt = Math.min(0.05, (now - last) / 1000);
1781
+ last = now;
1782
+ if (!document.hidden) {
1783
+ for (const l of LIVE)
1784
+ if (l.visible)
1785
+ l.measure();
1786
+ for (const l of LIVE)
1787
+ if (l.visible)
1788
+ l.step(dt);
1789
+ }
1790
+ raf = LIVE.size ? requestAnimationFrame(tick) : 0;
1791
+ }
1792
+ function run() {
1793
+ if (raf)
1794
+ return;
1795
+ last = performance.now();
1796
+ raf = requestAnimationFrame(tick);
1797
+ }
1798
+ var motionQuery = null;
1799
+ function reducedMotion() {
1800
+ if (!motionQuery) {
1801
+ motionQuery = matchMedia("(prefers-reduced-motion: reduce)");
1802
+ motionQuery.addEventListener("change", (e) => {
1803
+ for (const l of ALL)
1804
+ l.setReduced(e.matches);
1805
+ });
1806
+ }
1807
+ return motionQuery.matches;
1808
+ }
1809
+ var pointer = null;
1810
+ var listening = false;
1811
+ function listen() {
1812
+ if (listening)
1813
+ return;
1814
+ listening = true;
1815
+ const at = (e) => {
1816
+ pointer = [e.clientX, e.clientY];
1817
+ };
1818
+ document.addEventListener("pointermove", at, { passive: true });
1819
+ document.addEventListener("pointerdown", at, { passive: true });
1820
+ document.documentElement.addEventListener("pointerleave", () => {
1821
+ pointer = null;
1822
+ });
1823
+ }
1824
+
1825
+ class Live {
1826
+ svg;
1827
+ who;
1828
+ opts;
1829
+ visible = true;
1830
+ state;
1831
+ reduced;
1832
+ nodes = new Map;
1833
+ prev = new Map;
1834
+ io = null;
1835
+ clock = 0;
1836
+ enteredAt = 0;
1837
+ val;
1838
+ vel = zero();
1839
+ base;
1840
+ spr = {};
1841
+ out = zero();
1842
+ queue = [];
1843
+ bumps = [];
1844
+ blinkAt = rand(1, 3);
1845
+ blinkT = -1;
1846
+ blinkDur = 0.16;
1847
+ blinkV = 0;
1848
+ doubleBlink = false;
1849
+ saccAt = rand(0.6, 1.8);
1850
+ sacc = { gx: 0, gy: 0 };
1851
+ nodAt = 0;
1852
+ lookThing;
1853
+ lag = { y: 0, v: 0 };
1854
+ fixed = null;
1855
+ watch = false;
1856
+ rect = null;
1857
+ constructor(svg, tree, who, opts, expression, gaze) {
1858
+ this.svg = svg;
1859
+ this.who = who;
1860
+ this.opts = opts;
1861
+ this.state = expression;
1862
+ this.bind(tree, svg);
1863
+ this.setGaze(gaze);
1864
+ const start = restPose(who, expression, this.fixed ?? undefined);
1865
+ this.val = { ...start };
1866
+ this.base = { ...EXPRESSIONS[expression] };
1867
+ if (this.fixed)
1868
+ [this.base.gx, this.base.gy] = this.fixed;
1869
+ for (const c of CHANNELS)
1870
+ this.spr[c] = DEF_SPR[GROUP_OF[c]];
1871
+ this.lookThing = expression === "curious";
1872
+ this.lag.y = -start.alt * frameOf(FACES[who.face]).R;
1873
+ this.reduced = reducedMotion();
1874
+ this.paint();
1875
+ this.io = new IntersectionObserver((es) => {
1876
+ for (const e of es)
1877
+ this.visible = e.isIntersecting;
1878
+ });
1879
+ this.io.observe(svg);
1880
+ ALL.add(this);
1881
+ if (this.reduced)
1882
+ return;
1883
+ LIVE.add(this);
1884
+ run();
1885
+ }
1886
+ destroy() {
1887
+ ALL.delete(this);
1888
+ LIVE.delete(this);
1889
+ this.io?.disconnect();
1890
+ }
1891
+ setReduced(reduced) {
1892
+ if (reduced === this.reduced)
1893
+ return;
1894
+ this.reduced = reduced;
1895
+ if (!reduced) {
1896
+ LIVE.add(this);
1897
+ run();
1898
+ return;
1899
+ }
1900
+ LIVE.delete(this);
1901
+ this.snap();
1902
+ }
1903
+ measure() {
1904
+ this.rect = this.watch && pointer ? this.svg.getBoundingClientRect() : null;
1905
+ }
1906
+ bind(node, el) {
1907
+ this.nodes.set(node.key, el);
1908
+ const now = {};
1909
+ for (const a of Array.from(el.attributes))
1910
+ if (a.name !== "class" && a.name !== "style")
1911
+ now[a.name] = a.value;
1912
+ this.prev.set(node.key, now);
1913
+ node.children.forEach((c, i) => {
1914
+ const child = el.children[i];
1915
+ if (child)
1916
+ this.bind(c, child);
1917
+ });
1918
+ }
1919
+ write(node) {
1920
+ const el = this.nodes.get(node.key);
1921
+ const prev = this.prev.get(node.key) ?? {};
1922
+ if (el) {
1923
+ for (const k in node.attrs)
1924
+ if (prev[k] !== node.attrs[k])
1925
+ el.setAttribute(k, node.attrs[k]);
1926
+ for (const k in prev)
1927
+ if (!(k in node.attrs))
1928
+ el.removeAttribute(k);
1929
+ }
1930
+ this.prev.set(node.key, node.attrs);
1931
+ for (const c of node.children)
1932
+ this.write(c);
1933
+ }
1934
+ setGaze(gaze) {
1935
+ this.fixed = Array.isArray(gaze) ? gaze : null;
1936
+ this.watch = gaze === "pointer";
1937
+ if (this.watch)
1938
+ listen();
1939
+ if (this.reduced && this.fixed) {
1940
+ [this.val.gx, this.val.gy] = this.fixed;
1941
+ this.paint();
1942
+ }
1943
+ }
1944
+ setExpression(name) {
1945
+ if (name === this.state)
1946
+ return;
1947
+ const prev = this.state;
1948
+ this.state = name;
1949
+ this.enteredAt = this.clock;
1950
+ this.lookThing = false;
1951
+ this.queue = [];
1952
+ const tgt = EXPRESSIONS[name];
1953
+ const ch = CHOREO[name] ?? {};
1954
+ const delay = { ...DEF_DELAY, ...ch.delay };
1955
+ const spr = { ...DEF_SPR, ...ch.spr };
1956
+ if (this.reduced) {
1957
+ this.snap();
1958
+ return;
1959
+ }
1960
+ for (const gr of Object.keys(GROUPS)) {
1961
+ const set = {};
1962
+ for (const c of GROUPS[gr])
1963
+ set[c] = tgt[c];
1964
+ this.queue.push({
1965
+ at: this.clock + delay[gr] / 1000,
1966
+ set,
1967
+ spr: { [gr]: spr[gr] }
1968
+ });
1969
+ }
1970
+ for (const [t, set, s] of ch.keys ?? [])
1971
+ this.queue.push({ at: this.clock + t / 1000 + 0.0001, set, spr: s });
1972
+ this.queue.sort((a, b) => a.at - b.at);
1973
+ if (name === "surprised") {
1974
+ this.blinkAt = this.clock + 0.75;
1975
+ this.doubleBlink = true;
1976
+ }
1977
+ if (name === "sleepy")
1978
+ this.nodAt = this.clock + rand(6, 8);
1979
+ if (prev === "sleepy")
1980
+ this.bumps = [];
1981
+ }
1982
+ snap() {
1983
+ const rest = restPose(this.who, this.state, this.fixed ?? undefined);
1984
+ for (const c of CHANNELS) {
1985
+ this.base[c] = EXPRESSIONS[this.state][c];
1986
+ this.val[c] = rest[c];
1987
+ this.vel[c] = 0;
1988
+ }
1989
+ this.lookThing = this.state === "curious";
1990
+ this.queue = [];
1991
+ this.bumps = [];
1992
+ this.blinkT = -1;
1993
+ this.blinkV = 0;
1994
+ this.out = zero();
1995
+ this.paint();
1996
+ }
1997
+ bump(ch, amt, a, h, r) {
1998
+ this.bumps.push({ ch, amt, t0: this.clock, a, h, r });
1999
+ }
2000
+ step(dt) {
2001
+ this.clock += dt;
2002
+ const t = this.clock;
2003
+ const age = t - this.enteredAt;
2004
+ const st = this.state;
2005
+ const face = FACES[this.who.face];
2006
+ while (this.queue.length && this.queue[0].at <= t) {
2007
+ const q = this.queue.shift();
2008
+ if (q.spr)
2009
+ for (const gr of Object.keys(q.spr)) {
2010
+ const s = q.spr[gr];
2011
+ if (s)
2012
+ for (const c of GROUPS[gr])
2013
+ this.spr[c] = s;
2014
+ }
2015
+ for (const c of Object.keys(q.set)) {
2016
+ if (c === "look") {
2017
+ this.lookThing = true;
2018
+ continue;
2019
+ }
2020
+ const v = q.set[c];
2021
+ this.base[c] = typeof v === "function" ? v(this.val[c]) : v;
2022
+ }
2023
+ }
2024
+ const tgt = zero();
2025
+ const out = zero();
2026
+ let gx = this.base.gx;
2027
+ let gy = this.base.gy;
2028
+ if (this.lookThing)
2029
+ [gx, gy] = thingGaze(face, this.val.x, this.val.alt);
2030
+ if (this.fixed)
2031
+ [gx, gy] = this.fixed;
2032
+ const still = st === "sleepy" || st === "bored";
2033
+ if (!still && t >= this.saccAt) {
2034
+ this.sacc = { gx: rand(-0.17, 0.17), gy: rand(-0.1, 0.1) };
2035
+ this.saccAt = t + rand(0.8, 2.6);
2036
+ }
2037
+ if (!still) {
2038
+ gx += this.sacc.gx;
2039
+ gy += this.sacc.gy;
2040
+ }
2041
+ const w = ATTENTION[st];
2042
+ const r = this.rect;
2043
+ if (pointer && r && w > 0) {
2044
+ if (r.width > 0) {
2045
+ const fx = VB[0] + (pointer[0] - r.left) / r.width * VB[2];
2046
+ const fy = VB[1] + (pointer[1] - r.top) / r.height * VB[3];
2047
+ const { baseY, eyeY, R: R2 } = frameOf(face);
2048
+ const ex = 170 + this.val.x;
2049
+ const ey = eyeY + baseY - this.val.alt * R2;
2050
+ const px = clamp((fx - ex) / 170, -1, 1);
2051
+ const py = clamp((fy - ey) / 170, -1, 1);
2052
+ gx += (px - gx) * w;
2053
+ gy += (py - gy) * w;
2054
+ }
2055
+ }
2056
+ tgt.gx = gx - this.base.gx;
2057
+ tgt.gy = gy - this.base.gy;
2058
+ const br = st === "sleepy" ? 1.3 : st === "bored" ? 1.2 : st === "attentive" ? 2.6 : 2.1;
2059
+ const ba = st === "sleepy" ? 0.018 : st === "attentive" ? 0.004 : 0.007;
2060
+ out.sy += ba * Math.sin(t * br);
2061
+ out.alt += (st === "sleepy" ? 0.03 : 0.008) * Math.sin(t * br + 1.1);
2062
+ if (st === "happy")
2063
+ out.rot += 1.4 * Math.sin(t * 1.7);
2064
+ if (st === "angry") {
2065
+ out.x += 7 * Math.sin(age * 42) * Math.exp(-age * 6);
2066
+ out.x += 0.35 * Math.sin(t * 31);
2067
+ }
2068
+ if (st === "excited") {
2069
+ const ph = age % 1.5;
2070
+ if (ph < 0.9) {
2071
+ const h2 = Math.max(0, Math.sin(ph / 0.45 * Math.PI));
2072
+ out.alt += h2 * 0.17;
2073
+ out.sy += h2 * 0.045;
2074
+ out.rot += 2 * Math.sin(t * 9) * h2;
2075
+ }
2076
+ }
2077
+ if (st === "confused") {
2078
+ tgt.gx += 0.45 * Math.sin(age * 1.1);
2079
+ out.rot += 2.2 * Math.sin(age * 0.7);
2080
+ }
2081
+ if (st === "bored")
2082
+ tgt.gx += 0.22 * Math.sin(t * 0.33);
2083
+ if (st === "sleepy" && t >= this.nodAt && age > 3) {
2084
+ this.bump("alt", 0.3, 0.14, 0.5, 1.6);
2085
+ this.bump("lid", -0.5, 0.14, 0.45, 1.3);
2086
+ this.bump("hair", 0.6, 0.12, 0.4, 1.2);
2087
+ this.nodAt = t + rand(6.5, 9.5);
2088
+ }
2089
+ if ((st === "sad" || st === "bored") && Math.random() < dt / 7) {
2090
+ this.bump("sy", -0.035, 0.5, 0.25, 0.9);
2091
+ this.bump("alt", -0.05, 0.5, 0.25, 0.9);
2092
+ }
2093
+ const h = dt / 2;
2094
+ for (const c of CHANNELS) {
2095
+ const [om, ze] = this.spr[c];
2096
+ const target = this.base[c] + tgt[c];
2097
+ let x = this.val[c];
2098
+ let v = this.vel[c];
2099
+ for (let i = 0;i < 2; i++) {
2100
+ v += (om * om * (target - x) - 2 * ze * om * v) * h;
2101
+ x += v * h;
2102
+ }
2103
+ this.val[c] = x;
2104
+ this.vel[c] = v;
2105
+ }
2106
+ this.bumps = this.bumps.filter((b) => {
2107
+ const u = t - b.t0;
2108
+ if (u > b.a + b.h + b.r)
2109
+ return false;
2110
+ const e = u < b.a ? smooth(0, 1, u / b.a) : u < b.a + b.h ? 1 : 1 - smooth(0, 1, (u - b.a - b.h) / b.r);
2111
+ out[b.ch] += b.amt * e;
2112
+ return true;
2113
+ });
2114
+ const bl = BLINK[st];
2115
+ let blink = 0;
2116
+ if (bl && this.val.lid < 0.75) {
2117
+ if (this.blinkT < 0 && t >= this.blinkAt) {
2118
+ this.blinkT = 0;
2119
+ this.blinkDur = st === "bored" ? 0.42 : 0.16;
2120
+ }
2121
+ if (this.blinkT >= 0) {
2122
+ this.blinkT += dt;
2123
+ const u = this.blinkT / this.blinkDur;
2124
+ if (u >= 1) {
2125
+ this.blinkT = -1;
2126
+ if (this.doubleBlink) {
2127
+ this.doubleBlink = false;
2128
+ this.blinkAt = t + 0.09;
2129
+ } else {
2130
+ const k = this.who.persona.blink;
2131
+ this.blinkAt = t + rand(bl[0] * k, bl[1] * k);
2132
+ }
2133
+ } else
2134
+ blink = Math.sin(u * Math.PI);
2135
+ }
2136
+ } else if (!bl)
2137
+ this.blinkT = -1;
2138
+ this.blinkV = blink;
2139
+ const R = frameOf(face).R;
2140
+ const avY = -(this.val.alt + out.alt) * R;
2141
+ const lom = 10;
2142
+ const lze = 0.32;
2143
+ this.lag.v += (lom * lom * (avY - this.lag.y) - 2 * lze * lom * this.lag.v) * dt;
2144
+ this.lag.y += this.lag.v * dt;
2145
+ this.out = out;
2146
+ this.paint();
2147
+ }
2148
+ paint() {
2149
+ const R = frameOf(FACES[this.who.face]).R;
2150
+ const pose = { blink: this.blinkV, expression: this.state };
2151
+ for (const c of CHANNELS)
2152
+ pose[c] = this.val[c] + this.out[c];
2153
+ pose.lag = this.reduced ? 0 : clamp((this.lag.y + pose.alt * R) * 0.4, -16, 16);
2154
+ this.write(draw(this.who, pose, this.opts));
2155
+ }
2156
+ }
2157
+ // src/svg.ts
2158
+ var wear = (o, base, s) => ACCESSORIES[s].includes(o[s]) ? o[s] : base[s];
2159
+ function settle(name, o, live = false) {
2160
+ const base = identify(name, { version: o.version });
2161
+ const who = {
2162
+ ...base,
2163
+ face: o.face ?? base.face,
2164
+ color: o.color ?? base.color,
2165
+ eyes: o.eyes ?? base.eyes,
2166
+ brows: o.brows ?? base.brows,
2167
+ mouth: o.mouth ?? base.mouth,
2168
+ cheeks: o.cheeks ?? base.cheeks,
2169
+ trait: o.trait ?? base.trait,
2170
+ eyewear: wear(o, base, "eyewear"),
2171
+ headwear: wear(o, base, "headwear"),
2172
+ neckwear: wear(o, base, "neckwear")
2173
+ };
2174
+ const pose = restPose(who, o.expression ?? "normal", o.gaze);
2175
+ const variant = VARIANTS.includes(o.variant) ? o.variant : "peek";
2176
+ const look = {
2177
+ size: o.size ?? 64,
2178
+ frame: o.frame ?? "ink",
2179
+ square: o.square ?? true,
2180
+ riso: o.riso ?? false,
2181
+ live,
2182
+ ...variant !== "peek" && { variant }
2183
+ };
2184
+ const { eyewear, headwear, neckwear, ...legacy } = who;
2185
+ const wardrobe = eyewear !== "none" || headwear !== "none" || neckwear !== "none";
2186
+ const id = o.id ?? `peek-${fnv1a(JSON.stringify([wardrobe ? who : legacy, pose, look])).toString(36)}`;
2187
+ return { who, pose, opts: { ...look, id, title: o.title ?? name } };
2188
+ }
2189
+ var ILLEGAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
2190
+ var esc = (s) => s.replace(ILLEGAL, "�").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
2191
+ function serialize(node) {
2192
+ let a = "";
2193
+ for (const k in node.attrs)
2194
+ a += ` ${k}="${esc(node.attrs[k])}"`;
2195
+ if (!node.children.length)
2196
+ return `<${node.tag}${a}/>`;
2197
+ return `<${node.tag}${a}>${node.children.map(serialize).join("")}</${node.tag}>`;
2198
+ }
2199
+ export {
2200
+ Live,
2201
+ draw,
2202
+ restPose,
2203
+ serialize,
2204
+ settle
2205
+ };