@usenavii/core 0.2.0 → 0.3.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
@@ -557,6 +557,76 @@ function renderTopper(id, anchor, palette) {
557
557
  }
558
558
  }
559
559
 
560
+ // src/parts/outfit.ts
561
+ function renderOutfit(id, anchor, palette) {
562
+ if (id === "none") return "";
563
+ const cx = anchor.cx;
564
+ const cy = anchor.mouthY + (anchor.groundY - anchor.mouthY) * 0.55;
565
+ const ink = palette.ink;
566
+ const accent = palette.accent;
567
+ switch (id) {
568
+ case "collar":
569
+ return [
570
+ `<path d="M${cx - 9} ${cy} L${cx - 2} ${cy - 4} L${cx - 2} ${cy + 5} Z" fill="${accent}" stroke="${ink}" stroke-width="0.7" />`,
571
+ `<path d="M${cx + 9} ${cy} L${cx + 2} ${cy - 4} L${cx + 2} ${cy + 5} Z" fill="${accent}" stroke="${ink}" stroke-width="0.7" />`,
572
+ // tiny button at center
573
+ `<circle cx="${cx}" cy="${cy + 4}" r="0.9" fill="${ink}" />`
574
+ ].join("");
575
+ case "scarf":
576
+ return [
577
+ // wrap band
578
+ `<path d="M${cx - 14} ${cy - 2} Q${cx} ${cy + 3} ${cx + 14} ${cy - 2} L${cx + 14} ${cy + 3} Q${cx} ${cy + 8} ${cx - 14} ${cy + 3} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.6" />`,
579
+ // tail 1 (left)
580
+ `<path d="M${cx - 6} ${cy + 5} L${cx - 9} ${cy + 12} L${cx - 4} ${cy + 12} L${cx - 2} ${cy + 5} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.5" />`,
581
+ // tail 2 (slightly right)
582
+ `<path d="M${cx + 1} ${cy + 6} L${cx + 4} ${cy + 13} L${cx - 1} ${cy + 13} L${cx - 2} ${cy + 6} Z" fill="${palette.bodyFrom}" stroke="${ink}" stroke-width="0.5" />`
583
+ ].join("");
584
+ case "bowtie":
585
+ return [
586
+ // left wing
587
+ `<path d="M${cx - 1} ${cy} L${cx - 9} ${cy - 4} L${cx - 9} ${cy + 4} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.7" />`,
588
+ // right wing
589
+ `<path d="M${cx + 1} ${cy} L${cx + 9} ${cy - 4} L${cx + 9} ${cy + 4} Z" fill="${palette.bodyTo}" stroke="${ink}" stroke-width="0.7" />`,
590
+ // center knot
591
+ `<rect x="${cx - 1.4}" y="${cy - 2.4}" width="2.8" height="4.8" rx="0.8" fill="${palette.bodyFrom}" stroke="${ink}" stroke-width="0.5" />`
592
+ ].join("");
593
+ case "sunflower": {
594
+ const fx = cx - 8;
595
+ const fy = cy + 2;
596
+ const petals = [];
597
+ for (let i = 0; i < 8; i++) {
598
+ const a = i / 8 * Math.PI * 2;
599
+ const px = fx + Math.cos(a) * 3.2;
600
+ const py = fy + Math.sin(a) * 3.2;
601
+ petals.push(
602
+ `<ellipse cx="${px.toFixed(2)}" cy="${py.toFixed(2)}" rx="2.4" ry="1.3" fill="#FACC15" stroke="${ink}" stroke-width="0.35" transform="rotate(${(a * 180 / Math.PI).toFixed(1)} ${px.toFixed(2)} ${py.toFixed(2)})" />`
603
+ );
604
+ }
605
+ return [
606
+ // stem (tucked behind)
607
+ `<path d="M${fx + 2} ${fy + 2} Q${fx + 4} ${fy + 6} ${fx + 1} ${fy + 10}" stroke="#16A34A" stroke-width="1.1" fill="none" stroke-linecap="round" />`,
608
+ // leaf
609
+ `<path d="M${fx + 3} ${fy + 6} Q${fx + 7} ${fy + 4} ${fx + 6} ${fy + 8} Q${fx + 4} ${fy + 8} ${fx + 3} ${fy + 6} Z" fill="#22C55E" stroke="${ink}" stroke-width="0.35" />`,
610
+ ...petals,
611
+ // center disc
612
+ `<circle cx="${fx}" cy="${fy}" r="2" fill="#92400E" stroke="${ink}" stroke-width="0.4" />`,
613
+ // texture dots on disc
614
+ `<circle cx="${fx - 0.6}" cy="${fy - 0.5}" r="0.4" fill="#451A03" />`,
615
+ `<circle cx="${fx + 0.7}" cy="${fy + 0.3}" r="0.4" fill="#451A03" />`,
616
+ `<circle cx="${fx - 0.4}" cy="${fy + 0.8}" r="0.4" fill="#451A03" />`
617
+ ].join("");
618
+ }
619
+ case "necklace":
620
+ return [
621
+ // chain (curve from collarbone left → drop → right)
622
+ `<path d="M${cx - 10} ${cy} Q${cx} ${cy + 8} ${cx + 10} ${cy}" stroke="${accent}" stroke-width="0.8" fill="none" stroke-linecap="round" />`,
623
+ // pendant
624
+ `<circle cx="${cx}" cy="${cy + 7}" r="1.6" fill="${accent}" stroke="${ink}" stroke-width="0.5" />`,
625
+ `<circle cx="${cx}" cy="${cy + 7}" r="0.7" fill="${palette.blush}" />`
626
+ ].join("");
627
+ }
628
+ }
629
+
560
630
  // src/parts/index.ts
561
631
  var BODY_IDS = [
562
632
  "orb",
@@ -655,6 +725,7 @@ function selectAvatar(seed2, options = {}) {
655
725
  accessory,
656
726
  background,
657
727
  topper,
728
+ outfit: "none",
658
729
  hueShift,
659
730
  bodyScale,
660
731
  eyeGapShift,
@@ -738,10 +809,13 @@ function renderAvatarInner(spec, options = {}) {
738
809
  const antennaWrapped = antennaSvg ? `<g${transformAntenna(spec.antennaTilt ?? 0, anchor)}><g class="antenna">${antennaSvg}</g></g>` : "";
739
810
  const tileBg = resolveTileBg(options.tileBg, spec.palette);
740
811
  const tileCircle = tileBg ? `<circle cx="50" cy="50" r="50" fill="${tileBg}" />` : "";
812
+ const outfitSvg = renderOutfit(spec.outfit ?? "none", anchor, spec.palette);
741
813
  const parts = [
742
814
  tileCircle,
743
815
  renderBackground(spec.background, spec.palette, bgOverride),
744
816
  bodyWrapped,
817
+ // outfit sits on the body but below the face, so face features stay readable
818
+ outfitSvg,
745
819
  renderTopper(spec.topper, anchor, spec.palette),
746
820
  wrap("eyes", renderEyes(spec.eyes, spec.palette, anchor)),
747
821
  renderMouth(spec.mouth, spec.palette, anchor, spec.mouthCurveScale ?? 1),
@@ -857,6 +931,7 @@ function build(spec = {}, options = {}) {
857
931
  accessory: spec.accessory ?? "none",
858
932
  background: spec.background ?? "none",
859
933
  topper: spec.topper ?? "none",
934
+ outfit: spec.outfit ?? "none",
860
935
  hueShift: spec.hueShift ?? 0,
861
936
  bodyScale: spec.bodyScale ?? 1,
862
937
  eyeGapShift: spec.eyeGapShift ?? 0,