@doan-labs/peek 1.0.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.
package/dist/index.js CHANGED
@@ -114,6 +114,34 @@ var COLORS = {
114
114
  rose: { body: "#EFCAD7", deep: "#DFA4BA" },
115
115
  aqua: { body: "#BDE1E5", deep: "#8FC7D1" }
116
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
+ };
117
145
  var PARTS = {
118
146
  eyes: ["oval", "bead", "ring"],
119
147
  brows: ["arch", "bar", "wedge", "dash"],
@@ -121,8 +149,25 @@ var PARTS = {
121
149
  cheeks: ["oval", "dots", "lines"],
122
150
  trait: ["square", "fin", "ring", "dot", "peak"]
123
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
+ };
124
158
  var VERSIONS = {
125
- 1: { face: 4, color: 7, eyes: 3, brows: 4, mouth: 4, cheeks: 3, trait: 5 }
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
+ }
126
171
  };
127
172
  var LATEST = 1;
128
173
  var GROUPS = {
@@ -330,6 +375,240 @@ var EXPRESSIONS = {
330
375
  })
331
376
  };
332
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
+
333
612
  // src/identity.ts
334
613
  var STYLE = "peek";
335
614
  var tidy = (s) => s.normalize("NFC").trim().replace(/\s+/g, " ").toLowerCase();
@@ -352,7 +631,8 @@ function mulberry32(a) {
352
631
  var LISTS = {
353
632
  face: Object.keys(FACES),
354
633
  color: Object.keys(COLORS),
355
- ...PARTS
634
+ ...PARTS,
635
+ ...ACCESSORIES
356
636
  };
357
637
  var AXES = Object.keys(LISTS);
358
638
  function seed(name, axis, version = LATEST) {
@@ -396,17 +676,12 @@ function identify(name, { version = LATEST } = {}) {
396
676
  mouth: pick("mouth"),
397
677
  cheeks: pick("cheeks"),
398
678
  trait: pick("trait"),
679
+ eyewear: pick("eyewear"),
680
+ headwear: pick("headwear"),
681
+ neckwear: pick("neckwear"),
399
682
  persona
400
683
  };
401
684
  }
402
- // src/peek.tsx
403
- import {
404
- createElement,
405
- useEffect,
406
- useId,
407
- useRef,
408
- useState
409
- } from "react";
410
685
 
411
686
  // src/draw.ts
412
687
  var VB = [-30, -30, 400, 400];
@@ -419,8 +694,11 @@ var smooth = (a, b, v) => {
419
694
  };
420
695
  var f2 = (v) => String(Math.round(v * 100) / 100);
421
696
  var f3 = (v) => String(Math.round(v * 1000) / 1000);
422
- var strokeFor = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.4;
423
- var RAD = Math.PI / 180;
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";
424
702
  function frameOf(face) {
425
703
  const baseY = FLOOR + face.sink - face.bottom;
426
704
  const eyeY = face.eyes[0][1];
@@ -449,18 +727,18 @@ function seat(crown, d, u) {
449
727
  const at = ([x, y], b2) => ({
450
728
  x: x + R * Math.sin(b2),
451
729
  y: y - R * Math.cos(b2),
452
- a: b2 / RAD
730
+ a: b2 / RAD2
453
731
  });
454
732
  const P = crown.poly;
455
733
  if (P.length === 1)
456
734
  return at(P[0], R ? u / R : 0);
457
- const n = P.length;
735
+ const n2 = P.length;
458
736
  const dir = u >= 0 ? 1 : -1;
459
737
  let left = Math.abs(u);
460
738
  let i = 0;
461
739
  let b = 0;
462
- for (let step = 0;step < n; step++) {
463
- const j = (i + dir + n) % n;
740
+ for (let step = 0;step < n2; step++) {
741
+ const j = (i + dir + n2) % n2;
464
742
  const [x0, y0] = P[i];
465
743
  const [x1, y1] = P[j];
466
744
  const ex = x1 - x0;
@@ -480,7 +758,7 @@ function seat(crown, d, u) {
480
758
  return {
481
759
  x: x0 + ex / L * left + R * nx,
482
760
  y: y0 + ey / L * left + R * ny,
483
- a: be / RAD
761
+ a: be / RAD2
484
762
  };
485
763
  left -= L;
486
764
  i = j;
@@ -507,7 +785,7 @@ function depth(crown, x, y) {
507
785
  }
508
786
  return (out <= 0 ? out : near) - crown.r;
509
787
  }
510
- var RING_U = 158 * 35 * RAD;
788
+ var RING_U = 158 * 35 * RAD2;
511
789
  function ringWalk(sh) {
512
790
  const ex = sh.eyes[1][0] + sh.brow.hw + 7;
513
791
  const top = sh.brow.y - 30;
@@ -541,14 +819,11 @@ function prune(node) {
541
819
  }
542
820
  function draw(who, pose, o) {
543
821
  const sh = FACES[who.face];
544
- const { body: fill, deep } = COLORS[who.color];
545
822
  const P = who.persona;
546
823
  const mul = P.mul;
547
824
  const add = P.add;
548
825
  const g = (c) => pose[c] * (mul[c] ?? 1) + (add[c] ?? 0);
549
- const spread = P.spread;
550
826
  const id = o.id;
551
- const st = strokeFor(o.size);
552
827
  const { baseY, R } = frameOf(sh);
553
828
  const defs = [];
554
829
  const VBr = { x: VB[0], y: VB[1], width: VB[2], height: VB[3] };
@@ -642,7 +917,165 @@ function draw(who, pose, o) {
642
917
  const B = sh.bottom;
643
918
  const ty = baseY - alt * R;
644
919
  const sc = (v) => Math.round(v * 1e5) / 1e5;
645
- const bodyNode = "d" in sh.body ? n("path", "body", {
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", {
646
1079
  d: sh.body.d,
647
1080
  fill,
648
1081
  stroke: sh.body.stroke ? fill : undefined,
@@ -730,25 +1163,36 @@ function draw(who, pose, o) {
730
1163
  const lrx = rx * 1.55;
731
1164
  const lry = ry * 1.15;
732
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));
733
1186
  const parts = [
734
1187
  n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
735
- n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, [
736
- n("rect", `${kk}.lid`, {
737
- x: f2(-rx * 2.2),
738
- y: f2(-ry * 3),
739
- width: f2(rx * 4.4),
740
- height: f2(Math.max(0, edge + ry * 3)),
741
- transform: `rotate(${f2(a)})`,
742
- fill
743
- }),
744
- n("ellipse", `${kk}.lower`, {
745
- cx: 0,
746
- cy: f2(top + lry),
747
- rx: f2(lrx),
748
- ry: f2(lry),
749
- fill
750
- })
751
- ])
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)
752
1196
  ];
753
1197
  if (who.eyes === "ring")
754
1198
  parts.push(n("g", `${kk}.edges`, {
@@ -782,9 +1226,7 @@ function draw(who, pose, o) {
782
1226
  "stroke-width": f2(sw * 0.85),
783
1227
  opacity: co > 0.01 ? f2(co) : 0
784
1228
  }));
785
- return n("g", kk, {
786
- transform: bead ? `translate(${f2(ex + side * spread + gx * 6)} ${f2(ey + gy * 6)})` : `translate(${f2(ex + side * spread)} ${ey})`
787
- }, parts);
1229
+ return n("g", kk, { transform: `translate(${f2(etx)} ${f2(ety)})` }, parts);
788
1230
  });
789
1231
  const browY = g("browY") - (eyeS - 1) * sh.ry * 0.9;
790
1232
  const btilt = g("browTilt");
@@ -795,7 +1237,7 @@ function draw(who, pose, o) {
795
1237
  const side = i === 0 ? -1 : 1;
796
1238
  const hw = sh.brow.hw;
797
1239
  const c = -sh.brow.arch * arch;
798
- const [w, 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];
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];
799
1241
  const ti = bw * 1.7;
800
1242
  const to = bw * 0.45;
801
1243
  const [tl, tr] = side < 0 ? [to, ti] : [ti, to];
@@ -804,11 +1246,11 @@ function draw(who, pose, o) {
804
1246
  const half = (t) => wedge ? hl + (hr - hl) * t : bw * k2 / 2;
805
1247
  const bx = ex + side * spread;
806
1248
  const by = sh.brow.y + browY + (i === 1 ? -basym : basym * 0.3);
807
- const cos = Math.cos(side * btilt * RAD);
808
- const sin = Math.sin(side * btilt * RAD);
1249
+ const cos = Math.cos(side * btilt * RAD2);
1250
+ const sin = Math.sin(side * btilt * RAD2);
809
1251
  const inside = (dy) => {
810
1252
  for (let t = 0;t <= 1; t += 0.25) {
811
- const lx = w * (2 * t - 1);
1253
+ const lx = w2 * (2 * t - 1);
812
1254
  const ly = 2 * t * (1 - t) * ca;
813
1255
  const px = bx + lx * cos - ly * sin;
814
1256
  const py = by + dy + lx * sin + ly * cos;
@@ -821,7 +1263,7 @@ function draw(who, pose, o) {
821
1263
  let hi = 0;
822
1264
  if (!inside(0)) {
823
1265
  hi = 40;
824
- for (let k3 = 0;k3 < 12; k3++) {
1266
+ for (let k = 0;k < 12; k++) {
825
1267
  const mid2 = (lo + hi) / 2;
826
1268
  if (inside(mid2))
827
1269
  hi = mid2;
@@ -843,7 +1285,7 @@ function draw(who, pose, o) {
843
1285
  });
844
1286
  }
845
1287
  return n("path", key, {
846
- d: `M${f2(-w)} 0Q0 ${f2(ca)} ${f2(w)} 0`,
1288
+ d: `M${f2(-w2)} 0Q0 ${f2(ca)} ${f2(w2)} 0`,
847
1289
  transform,
848
1290
  fill: "none",
849
1291
  stroke: INK.ink,
@@ -924,7 +1366,7 @@ function draw(who, pose, o) {
924
1366
  fill: deep
925
1367
  });
926
1368
  } else if (who.trait === "fin") {
927
- const s = seat(sh.crown, -6, -164 * 22 * RAD);
1369
+ const s = seat(sh.crown, -6, -164 * 22 * RAD2);
928
1370
  const r = s.a + (hair >= 0 ? hair * 20 : hair * 55) - lag * 0.8;
929
1371
  traitT = `translate(${f2(s.x)} ${f2(s.y + lag * 0.25)}) rotate(${f2(r)})`;
930
1372
  shape = n("path", "trait.shape", {
@@ -933,8 +1375,8 @@ function draw(who, pose, o) {
933
1375
  });
934
1376
  } else if (who.trait === "ring") {
935
1377
  const b = 35 - hair * 14 - (hair < 0 ? hair * -21 : 0) + lag * 0.9;
936
- const w = ringWalk(sh);
937
- const s = seat(sh.crown, 8, Math.max(w.min, w.rest + 158 * (b - 35) * RAD));
1378
+ const w2 = ringWalk(sh);
1379
+ const s = seat(sh.crown, 8, Math.max(w2.min, w2.rest + 158 * (b - 35) * RAD2));
938
1380
  traitT = `translate(${f2(s.x)} ${f2(s.y)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
939
1381
  shape = n("circle", "trait.shape", {
940
1382
  cx: 0,
@@ -946,7 +1388,7 @@ function draw(who, pose, o) {
946
1388
  });
947
1389
  } else if (who.trait === "dot") {
948
1390
  const s = clamp(-hair, 0, 1.2);
949
- const th = Math.min(1, s / 0.35) * 61.05 * RAD;
1391
+ const th = Math.min(1, s / 0.35) * 61.05 * RAD2;
950
1392
  const travel = Math.max(0, s - 0.35) / 0.65 * 92;
951
1393
  const p = seat(sh.crown, 12.5, 26.1 * th + travel);
952
1394
  let cy = p.y;
@@ -968,61 +1410,228 @@ function draw(who, pose, o) {
968
1410
  });
969
1411
  }
970
1412
  const trait = n("g", "trait", { transform: traitT }, [shape]);
971
- const off = (dx, dy) => riso ? `translate(${f2(dx * k * upp)} ${f2(dy * k * upp)})` : undefined;
972
- const av = n("g", "av", {
973
- transform: `translate(${f2(x)} ${f2(ty)}) rotate(${f2(rot)} 170 ${B}) translate(170 ${B}) scale(${sc(sx)} ${sc(sy)}) translate(-170 ${-B})`,
974
- filter: dim > 0.01 ? `url(#${id}-dim)` : undefined
975
- }, [
976
- n("g", "plateA", {
977
- transform: off(-0.55, 0.45),
978
- filter: riso ? `url(#${id}-pA)` : undefined
979
- }, [bodyNode, ...cheeks, trait]),
980
- n("g", "plateB", {
981
- transform: off(0.6, -0.5),
982
- filter: riso ? `url(#${id}-pB)` : undefined
983
- }, [...eyes, ...brows, mouth])
984
- ]);
985
- const scene = [];
986
- if (o.frame !== "none") {
987
- const bg = o.frame === "ink" ? INK.ink : o.frame === "bone" ? INK.bone : INK.paper;
988
- scene.push(n("rect", "bg", { ...VBr, fill: bg }));
989
- const th = clamp(g("thing"), 0, 1);
990
- const dots = [];
991
- for (let j = 0;j < 3; j++)
992
- for (let i = 0;i < 3; i++) {
993
- const q = j * 3 + i;
994
- const kk = smooth(q / 12, q / 12 + 0.35, th);
995
- dots.push(n("circle", `lattice.${q}`, {
996
- cx: 312 + i * 18,
997
- cy: 2 + j * 18,
998
- r: f2(1.2 + 2.2 * kk),
999
- opacity: f3(kk * 0.55)
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
1000
1477
  }));
1001
1478
  }
1002
- scene.push(n("g", "lattice", { fill: o.frame === "ink" ? INK.bone : INK.ink }, dots));
1479
+ }
1480
+ wardrobe.push(n("g", "eyewear", {}, frames));
1003
1481
  }
1004
- scene.push(av);
1005
- if (grainy)
1006
- scene.push(n("rect", "grain", {
1007
- ...VBr,
1008
- filter: `url(#${id}-grain)`,
1009
- opacity: o.frame === "ink" ? 0.3 : 0.16,
1010
- "pointer-events": "none"
1011
- }));
1012
- const root = n("svg", "root", {
1013
- xmlns: "http://www.w3.org/2000/svg",
1014
- viewBox: VB.join(" "),
1015
- width: o.size,
1016
- height: o.size,
1017
- role: o.title === false ? undefined : "img",
1018
- "aria-label": o.title === false ? undefined : o.title,
1019
- "aria-hidden": o.title === false ? "true" : undefined
1020
- }, [
1021
- n("defs", "defs", {}, defs),
1022
- n("g", "frame", { "clip-path": `url(#${id}-f)` }, scene)
1023
- ]);
1024
- return o.live ? root : prune(root) ?? root;
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
+ ];
1025
1580
  }
1581
+ // src/svg.ts
1582
+ var wear = (o, base, s) => ACCESSORIES[s].includes(o[s]) ? o[s] : base[s];
1583
+ function settle(name, o, live = false) {
1584
+ const base = identify(name, { version: o.version });
1585
+ const who = {
1586
+ ...base,
1587
+ face: o.face ?? base.face,
1588
+ color: o.color ?? base.color,
1589
+ eyes: o.eyes ?? base.eyes,
1590
+ brows: o.brows ?? base.brows,
1591
+ mouth: o.mouth ?? base.mouth,
1592
+ cheeks: o.cheeks ?? base.cheeks,
1593
+ trait: o.trait ?? base.trait,
1594
+ eyewear: wear(o, base, "eyewear"),
1595
+ headwear: wear(o, base, "headwear"),
1596
+ neckwear: wear(o, base, "neckwear")
1597
+ };
1598
+ const pose = restPose(who, o.expression ?? "normal", o.gaze);
1599
+ const variant = VARIANTS.includes(o.variant) ? o.variant : "peek";
1600
+ const look = {
1601
+ size: o.size ?? 64,
1602
+ frame: o.frame ?? "ink",
1603
+ square: o.square ?? true,
1604
+ riso: o.riso ?? false,
1605
+ live,
1606
+ ...variant !== "peek" && { variant }
1607
+ };
1608
+ const { eyewear, headwear, neckwear, ...legacy } = who;
1609
+ const wardrobe = eyewear !== "none" || headwear !== "none" || neckwear !== "none";
1610
+ const id = o.id ?? `peek-${fnv1a(JSON.stringify([wardrobe ? who : legacy, pose, look])).toString(36)}`;
1611
+ return { who, pose, opts: { ...look, id, title: o.title ?? name } };
1612
+ }
1613
+ var ILLEGAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
1614
+ var esc = (s) => s.replace(ILLEGAL, "�").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
1615
+ function serialize(node) {
1616
+ let a = "";
1617
+ for (const k in node.attrs)
1618
+ a += ` ${k}="${esc(node.attrs[k])}"`;
1619
+ if (!node.children.length)
1620
+ return `<${node.tag}${a}/>`;
1621
+ return `<${node.tag}${a}>${node.children.map(serialize).join("")}</${node.tag}>`;
1622
+ }
1623
+ function toSvg(name, props = {}) {
1624
+ const { who, pose, opts } = settle(name, props);
1625
+ return serialize(draw(who, pose, opts));
1626
+ }
1627
+ // src/peek.tsx
1628
+ import {
1629
+ createElement,
1630
+ useEffect,
1631
+ useId,
1632
+ useRef,
1633
+ useState
1634
+ } from "react";
1026
1635
 
1027
1636
  // src/animate.ts
1028
1637
  var GROUP_OF = {};
@@ -1600,45 +2209,6 @@ class Live {
1600
2209
  }
1601
2210
  }
1602
2211
 
1603
- // src/svg.ts
1604
- function settle(name, o, live = false) {
1605
- const base = identify(name, { version: o.version });
1606
- const who = {
1607
- ...base,
1608
- face: o.face ?? base.face,
1609
- color: o.color ?? base.color,
1610
- eyes: o.eyes ?? base.eyes,
1611
- brows: o.brows ?? base.brows,
1612
- mouth: o.mouth ?? base.mouth,
1613
- cheeks: o.cheeks ?? base.cheeks,
1614
- trait: o.trait ?? base.trait
1615
- };
1616
- const pose = restPose(who, o.expression ?? "normal", o.gaze);
1617
- const look = {
1618
- size: o.size ?? 64,
1619
- frame: o.frame ?? "ink",
1620
- square: o.square ?? true,
1621
- riso: o.riso ?? false,
1622
- live
1623
- };
1624
- const id = o.id ?? `peek-${fnv1a(JSON.stringify([who, pose, look])).toString(36)}`;
1625
- return { who, pose, opts: { ...look, id, title: o.title ?? name } };
1626
- }
1627
- var ILLEGAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
1628
- var esc = (s) => s.replace(ILLEGAL, "�").replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
1629
- function serialize(node) {
1630
- let a = "";
1631
- for (const k in node.attrs)
1632
- a += ` ${k}="${esc(node.attrs[k])}"`;
1633
- if (!node.children.length)
1634
- return `<${node.tag}${a}/>`;
1635
- return `<${node.tag}${a}>${node.children.map(serialize).join("")}</${node.tag}>`;
1636
- }
1637
- function toSvg(name, props = {}) {
1638
- const { who, pose, opts } = settle(name, props);
1639
- return serialize(draw(who, pose, opts));
1640
- }
1641
-
1642
2212
  // src/peek.tsx
1643
2213
  var camel = (k) => k.startsWith("aria-") ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
1644
2214
  function toReact(node, root) {
@@ -1684,17 +2254,24 @@ function Peek({
1684
2254
  return toReact(tree, { ref: svg, className, style });
1685
2255
  }
1686
2256
  export {
2257
+ ACCESSORIES,
1687
2258
  AXES,
2259
+ BLOC,
1688
2260
  COLORS,
1689
2261
  EXPRESSIONS,
1690
2262
  FACES,
1691
2263
  GROUPS,
2264
+ HAZE,
1692
2265
  LATEST,
1693
2266
  LISTS,
1694
2267
  PARTS,
1695
2268
  Peek,
2269
+ VARIANTS,
1696
2270
  VERSIONS,
2271
+ WHEEL,
2272
+ draw,
1697
2273
  identify,
2274
+ settle,
1698
2275
  tidy,
1699
2276
  toSvg
1700
2277
  };