solfaces 2.2.0 → 2.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.
Files changed (64) hide show
  1. package/README.md +45 -19
  2. package/SKILL.md +2 -3
  3. package/dist/agent/index.cjs +13 -14
  4. package/dist/agent/index.js +3 -4
  5. package/dist/agent/mcp-server.cjs +326 -333
  6. package/dist/{chunk-5DT27HMT.js → chunk-2KW35VRI.js} +3 -3
  7. package/dist/{chunk-5DT27HMT.js.map → chunk-2KW35VRI.js.map} +1 -1
  8. package/dist/{chunk-PVJR3SFG.cjs → chunk-BI3GHRKQ.cjs} +14 -15
  9. package/dist/chunk-BI3GHRKQ.cjs.map +1 -0
  10. package/dist/{chunk-DRUSCLEF.js → chunk-MYUSB4LA.js} +28 -8
  11. package/dist/chunk-MYUSB4LA.js.map +1 -0
  12. package/dist/{chunk-3CE7Q44S.js → chunk-N5GDLCCL.js} +324 -110
  13. package/dist/chunk-N5GDLCCL.js.map +1 -0
  14. package/dist/{chunk-74CSG63X.js → chunk-O2IIBSQH.js} +6 -7
  15. package/dist/chunk-O2IIBSQH.js.map +1 -0
  16. package/dist/{chunk-6QRDULAO.cjs → chunk-PCSRDAWQ.cjs} +28 -7
  17. package/dist/chunk-PCSRDAWQ.cjs.map +1 -0
  18. package/dist/{chunk-WIXGHS77.cjs → chunk-T7HEUW2O.cjs} +6 -6
  19. package/dist/{chunk-WIXGHS77.cjs.map → chunk-T7HEUW2O.cjs.map} +1 -1
  20. package/dist/{chunk-F54WHRCE.cjs → chunk-W2U6ITMR.cjs} +328 -110
  21. package/dist/chunk-W2U6ITMR.cjs.map +1 -0
  22. package/dist/core/index.cjs +43 -44
  23. package/dist/core/index.d.cts +39 -5
  24. package/dist/core/index.d.ts +39 -5
  25. package/dist/core/index.js +2 -3
  26. package/dist/index.cjs +53 -50
  27. package/dist/index.d.cts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +4 -5
  30. package/dist/react/index.cjs +107 -120
  31. package/dist/react/index.cjs.map +1 -1
  32. package/dist/react/index.d.cts +23 -1
  33. package/dist/react/index.d.ts +23 -1
  34. package/dist/react/index.js +95 -108
  35. package/dist/react/index.js.map +1 -1
  36. package/dist/solfaces.cdn.global.js +2 -2
  37. package/dist/solfaces.cdn.global.js.map +1 -1
  38. package/dist/themes/index.cjs +18 -14
  39. package/dist/themes/index.d.cts +30 -4
  40. package/dist/themes/index.d.ts +30 -4
  41. package/dist/themes/index.js +1 -1
  42. package/dist/traits-D4tbtZIr.d.cts +259 -0
  43. package/dist/traits-D4tbtZIr.d.ts +259 -0
  44. package/dist/vanilla/index.cjs +6 -7
  45. package/dist/vanilla/index.cjs.map +1 -1
  46. package/dist/vanilla/index.d.cts +1 -1
  47. package/dist/vanilla/index.d.ts +1 -1
  48. package/dist/vanilla/index.js +2 -3
  49. package/dist/vanilla/index.js.map +1 -1
  50. package/package.json +4 -1
  51. package/python/solfaces.py +103 -119
  52. package/reference/integrations.md +1 -1
  53. package/dist/chunk-3CE7Q44S.js.map +0 -1
  54. package/dist/chunk-6QRDULAO.cjs.map +0 -1
  55. package/dist/chunk-74CSG63X.js.map +0 -1
  56. package/dist/chunk-CKHLRORB.js +0 -239
  57. package/dist/chunk-CKHLRORB.js.map +0 -1
  58. package/dist/chunk-DRUSCLEF.js.map +0 -1
  59. package/dist/chunk-F54WHRCE.cjs.map +0 -1
  60. package/dist/chunk-PVJR3SFG.cjs.map +0 -1
  61. package/dist/chunk-TYTBYDQU.cjs +0 -244
  62. package/dist/chunk-TYTBYDQU.cjs.map +0 -1
  63. package/dist/traits-sfe7rM9C.d.cts +0 -106
  64. package/dist/traits-sfe7rM9C.d.ts +0 -106
@@ -18,7 +18,10 @@ var EYE_COLORS = [
18
18
  "#3868A8",
19
19
  "#38784C",
20
20
  "#808838",
21
- "#586878"
21
+ "#586878",
22
+ "#A06830",
23
+ "#685898",
24
+ "#889898"
22
25
  ];
23
26
  var HAIR_COLORS = [
24
27
  "#1A1A24",
@@ -42,7 +45,9 @@ var BG_COLORS = [
42
45
  "#7f8bbd",
43
46
  "#8869ab",
44
47
  "#b785b3",
45
- "#ab6984"
48
+ "#ab6984",
49
+ "#a07ab5",
50
+ "#74b5a0"
46
51
  ];
47
52
  function djb2(str) {
48
53
  let hash = 5381;
@@ -66,22 +71,20 @@ function generateTraits(walletAddress, overrides) {
66
71
  const traits = {
67
72
  faceShape: Math.floor(rand() * 4),
68
73
  skinColor: Math.floor(rand() * 10),
69
- eyeStyle: Math.floor(rand() * 8),
70
- eyeColor: Math.floor(rand() * 5),
71
- eyebrows: Math.floor(rand() * 5),
72
- nose: Math.floor(rand() * 4),
74
+ eyeStyle: Math.floor(rand() * 9),
75
+ eyeColor: Math.floor(rand() * 8),
76
+ eyebrows: Math.floor(rand() * 8),
77
+ nose: Math.floor(rand() * 8),
73
78
  mouth: Math.floor(rand() * 8),
74
79
  hairStyle: Math.floor(rand() * 10),
75
80
  hairColor: Math.floor(rand() * 10),
76
- accessory: Math.floor(rand() * 10),
77
- bgColor: Math.floor(rand() * 10)
81
+ accessory: Math.floor(rand() * 12),
82
+ bgColor: Math.floor(rand() * 12)
78
83
  };
79
84
  return overrides ? { ...traits, ...overrides } : traits;
80
85
  }
81
86
  function effectiveAccessory(traits) {
82
- const ai = traits.accessory % 10;
83
- const hi = traits.hairStyle % 10;
84
- if ((ai === 4 || ai === 7) && (hi === 5 || hi === 6)) return 0;
87
+ const ai = traits.accessory % 12;
85
88
  return ai;
86
89
  }
87
90
  var FACE_LABELS = ["Squircle"];
@@ -97,22 +100,22 @@ var SKIN_LABELS = [
97
100
  "Brown",
98
101
  "Deep"
99
102
  ];
100
- var EYE_STYLE_LABELS = ["Round", "Minimal", "Almond", "Wide", "Relaxed", "Joyful", "Bright", "Gentle"];
101
- var EYE_COLOR_LABELS = ["Chocolate", "Sky", "Emerald", "Hazel", "Storm"];
102
- var BROW_LABELS = ["Wispy", "Straight", "Natural", "Arched", "Angled"];
103
- var NOSE_LABELS = ["Shadow", "Button", "Soft", "Nostrils"];
103
+ var EYE_STYLE_LABELS = ["Round", "Minimal", "Almond", "Wide", "Relaxed", "Joyful", "Bright", "Gentle", "Side-look"];
104
+ var EYE_COLOR_LABELS = ["Chocolate", "Sky", "Emerald", "Hazel", "Storm", "Amber", "Violet", "Gray"];
105
+ var BROW_LABELS = ["Wispy", "Straight", "Natural", "Arched", "Angled", "Worried", "Bushy", "Thin"];
106
+ var NOSE_LABELS = ["Shadow", "Button", "Soft", "Nostrils", "Pointed", "Wide", "Bridge", "Snub"];
104
107
  var MOUTH_LABELS = ["Smile", "Calm", "Happy", "Oh", "Smirk", "Grin", "Flat", "Pout"];
105
108
  var HAIR_STYLE_LABELS = [
106
109
  "Bald",
107
- "Short",
110
+ "Crew",
108
111
  "Curly",
109
- "Side Sweep",
110
- "Puff",
112
+ "Side Part",
111
113
  "Long",
112
- "Bob",
113
114
  "Buzz",
114
- "Wavy",
115
- "Topknot"
115
+ "Beanie",
116
+ "Cap",
117
+ "Mohawk",
118
+ "Bun"
116
119
  ];
117
120
  var HAIR_COLOR_LABELS = [
118
121
  "Black",
@@ -136,7 +139,9 @@ var ACCESSORY_LABELS = [
136
139
  "Freckles",
137
140
  "Stud Earrings",
138
141
  "Aviators",
139
- "Band-Aid"
142
+ "Band-Aid",
143
+ "Left Eyebrow Slit",
144
+ "Right Eyebrow Slit"
140
145
  ];
141
146
  var BG_COLOR_LABELS = [
142
147
  "Rose",
@@ -148,7 +153,9 @@ var BG_COLOR_LABELS = [
148
153
  "Sky",
149
154
  "Lavender",
150
155
  "Orchid",
151
- "Blush"
156
+ "Blush",
157
+ "Lilac",
158
+ "Seafoam"
152
159
  ];
153
160
  function getTraitLabels(traits) {
154
161
  return {
@@ -211,7 +218,21 @@ function blend(a, b, t = 0.5) {
211
218
  }
212
219
  function luminance(hex) {
213
220
  const [r, g, b] = hexToRgb(hex);
214
- return (r + g + b) / 3;
221
+ return 0.299 * r + 0.587 * g + 0.114 * b;
222
+ }
223
+ function relativeLuminance(hex) {
224
+ const [r, g, b] = hexToRgb(hex).map((c) => {
225
+ const s = c / 255;
226
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
227
+ });
228
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
229
+ }
230
+ function contrastRatio(hex1, hex2) {
231
+ const l1 = relativeLuminance(hex1);
232
+ const l2 = relativeLuminance(hex2);
233
+ const lighter = Math.max(l1, l2);
234
+ const darker = Math.min(l1, l2);
235
+ return (lighter + 0.05) / (darker + 0.05);
215
236
  }
216
237
  function deriveSkinColors(skin) {
217
238
  const sL = luminance(skin);
@@ -245,11 +266,20 @@ function deriveSkinColors(skin) {
245
266
  const lipRaw = blend(skin, lipBase, Math.min(0.82, lipBlend));
246
267
  const [lr, lg, lb] = hexToRgb(lipRaw);
247
268
  const lipD = Math.abs(sr - lr) + Math.abs(sg - lg) + Math.abs(sb - lb);
248
- const lipColor = lipD < 60 ? blend(skin, lipBase, 0.78) : lipRaw;
249
- const browColor = isDark ? lighten(skin, sL < 80 ? 0.35 : 0.25) : darken(skin, 0.55);
250
- const noseFill = isDark ? lighten(skin, 0.2) : darken(skin, 0.2);
269
+ let lipColor = lipD < 60 ? blend(skin, lipBase, 0.78) : lipRaw;
270
+ let attempts = 0;
271
+ while (contrastRatio(lipColor, skin) < 1.8 && attempts < 12) {
272
+ lipColor = darken(lipColor, 0.06);
273
+ attempts++;
274
+ }
275
+ const browColor = isDark ? lighten(skin, sL < 80 ? 0.35 : sL < 100 ? 0.32 : 0.25) : darken(skin, 0.55);
276
+ const noseShift = 0.2 + 0.06 * (1 - Math.abs(sL - 100) / 140);
277
+ const noseFill = isDark ? lighten(skin, noseShift) : darken(skin, noseShift);
251
278
  const earT = Math.max(0, Math.min(1, (sL - 100) / 60));
252
- const earFill = blend(lighten(skin, 0.08), skinMid, earT);
279
+ let earFill = blend(lighten(skin, 0.08), skinMid, earT);
280
+ if (contrastRatio(earFill, skin) < 1.05) {
281
+ earFill = isDark ? lighten(earFill, 0.06) : darken(earFill, 0.06);
282
+ }
253
283
  const earShadow = darken(skin, 0.1 + 0.06 * (1 - Math.min(1, sL / 160)));
254
284
  const lidColor = isDark ? lighten(skin, 0.18) : darken(skin, 0.15);
255
285
  const ewT = Math.max(0, Math.min(1, (sL - 60) / 180));
@@ -273,10 +303,209 @@ function deriveSkinColors(skin) {
273
303
  accessoryColor
274
304
  };
275
305
  }
276
- function buzzOpacity(hairCol, skin) {
277
- const [hr, hg, hb] = hexToRgb(hairCol);
278
- const [sr, sg, sb] = hexToRgb(skin);
279
- return Math.abs(hr - sr) + Math.abs(hg - sg) + Math.abs(hb - sb) < 80 ? 0.7 : 0.5;
306
+
307
+ // src/core/describe.ts
308
+ var SKIN_TONES = {
309
+ 0: "porcelain",
310
+ 1: "ivory",
311
+ 2: "fair",
312
+ 3: "light",
313
+ 4: "sand",
314
+ 5: "golden",
315
+ 6: "warm",
316
+ 7: "caramel",
317
+ 8: "brown",
318
+ 9: "deep"
319
+ };
320
+ var EYE_STYLES = {
321
+ 0: "round, wide-open",
322
+ 1: "small and minimal",
323
+ 2: "almond-shaped",
324
+ 3: "wide and expressive",
325
+ 4: "relaxed, half-lidded",
326
+ 5: "joyful, crescent-shaped",
327
+ 6: "bright and sparkling",
328
+ 7: "gentle and narrow",
329
+ 8: "side-looking, shifted"
330
+ };
331
+ var EYE_COLORS_DESC = {
332
+ 0: "dark brown",
333
+ 1: "blue",
334
+ 2: "green",
335
+ 3: "hazel",
336
+ 4: "gray",
337
+ 5: "amber",
338
+ 6: "violet",
339
+ 7: "steel gray"
340
+ };
341
+ var EYEBROW_STYLES = {
342
+ 0: "wispy",
343
+ 1: "straight",
344
+ 2: "natural",
345
+ 3: "elegantly arched",
346
+ 4: "sharply angled",
347
+ 5: "worried, furrowed",
348
+ 6: "bushy",
349
+ 7: "thin, delicate"
350
+ };
351
+ var NOSE_STYLES = {
352
+ 0: "a subtle shadow nose",
353
+ 1: "a small button nose",
354
+ 2: "a soft curved nose",
355
+ 3: "a button nose with visible nostrils",
356
+ 4: "a pointed nose",
357
+ 5: "a wide, broad nose",
358
+ 6: "a nose with a visible bridge",
359
+ 7: "a small snub nose"
360
+ };
361
+ var MOUTH_STYLES = {
362
+ 0: "a gentle smile",
363
+ 1: "a calm, neutral expression",
364
+ 2: "a happy grin",
365
+ 3: "a surprised O-shaped mouth",
366
+ 4: "a confident smirk",
367
+ 5: "a wide, toothy grin",
368
+ 6: "a flat, straight expression",
369
+ 7: "a soft pout"
370
+ };
371
+ var ACCESSORY_DESC = {
372
+ 0: "",
373
+ 1: "a beauty mark",
374
+ 2: "round glasses",
375
+ 3: "rectangular glasses",
376
+ 4: "a dangling earring",
377
+ 5: "a headband",
378
+ 6: "freckles",
379
+ 7: "stud earrings",
380
+ 8: "aviator sunglasses",
381
+ 9: "a band-aid",
382
+ 10: "a left eyebrow slit",
383
+ 11: "a right eyebrow slit"
384
+ };
385
+ var BG_COLORS_DESC = {
386
+ 0: "rose",
387
+ 1: "olive",
388
+ 2: "sage",
389
+ 3: "fern",
390
+ 4: "mint",
391
+ 5: "ocean",
392
+ 6: "sky",
393
+ 7: "lavender",
394
+ 8: "orchid",
395
+ 9: "blush",
396
+ 10: "lilac",
397
+ 11: "seafoam"
398
+ };
399
+ function describeAppearance(walletAddress, options) {
400
+ const traits = generateTraits(walletAddress);
401
+ const {
402
+ includeBackground = true,
403
+ format = "paragraph",
404
+ perspective = "third",
405
+ name
406
+ } = options ?? {};
407
+ if (format === "structured") return buildStructured(traits, includeBackground);
408
+ if (format === "compact") return buildCompact(traits);
409
+ return buildParagraph(traits, perspective, name, includeBackground);
410
+ }
411
+ function describeTraits(traits, options) {
412
+ const {
413
+ includeBackground = true,
414
+ format = "paragraph",
415
+ perspective = "third",
416
+ name
417
+ } = options ?? {};
418
+ if (format === "structured") return buildStructured(traits, includeBackground);
419
+ if (format === "compact") return buildCompact(traits);
420
+ return buildParagraph(traits, perspective, name, includeBackground);
421
+ }
422
+ function buildParagraph(t, perspective, name, includeBg) {
423
+ const parts = [];
424
+ const ai = effectiveAccessory(t);
425
+ const subject = perspective === "first" ? name ? `I'm ${name}. I have` : "I have" : name ? `${name} has` : "This SolFace has";
426
+ const im = perspective === "first" ? "I'm" : "They're";
427
+ parts.push(`${subject} a squircle face with ${SKIN_TONES[t.skinColor] ?? "warm"} skin`);
428
+ const eyeStyle = EYE_STYLES[t.eyeStyle] ?? "round";
429
+ const eyeColor = EYE_COLORS_DESC[t.eyeColor] ?? "dark";
430
+ parts.push(`${eyeStyle} ${eyeColor} eyes`);
431
+ const brows = EYEBROW_STYLES[t.eyebrows];
432
+ if (brows) parts.push(`${brows} eyebrows`);
433
+ parts.push(perspective === "first" ? "and am bald" : "and is bald");
434
+ let desc = parts[0];
435
+ if (parts.length > 2) {
436
+ desc += ", " + parts.slice(1, -1).join(", ") + ", " + parts[parts.length - 1];
437
+ } else if (parts.length === 2) {
438
+ desc += " and " + parts[1];
439
+ }
440
+ desc += ".";
441
+ const nose = NOSE_STYLES[t.nose];
442
+ if (nose) {
443
+ const noseSubject = perspective === "first" ? "I have" : (name ?? "They") + (name ? " has" : " have");
444
+ desc += ` ${noseSubject} ${nose}.`;
445
+ }
446
+ const acc = ACCESSORY_DESC[ai];
447
+ if (acc) {
448
+ desc += ` ${im} wearing ${acc}.`;
449
+ }
450
+ const mouth = MOUTH_STYLES[t.mouth] ?? "a smile";
451
+ const mouthVerb = perspective === "first" ? "I have" : (name ?? "They") + (name ? " has" : " have");
452
+ desc += ` ${mouthVerb} ${mouth}.`;
453
+ if (includeBg) {
454
+ const bg = BG_COLORS_DESC[t.bgColor] ?? "colorful";
455
+ desc += ` The background is ${bg}.`;
456
+ }
457
+ return desc;
458
+ }
459
+ function buildStructured(t, includeBg) {
460
+ const ai = effectiveAccessory(t);
461
+ const lines = [
462
+ `Face: squircle`,
463
+ `Skin: ${SKIN_TONES[t.skinColor] ?? "warm"}`,
464
+ `Eyes: ${EYE_STYLES[t.eyeStyle] ?? "round"}, ${EYE_COLORS_DESC[t.eyeColor] ?? "dark"}`,
465
+ `Eyebrows: ${EYEBROW_STYLES[t.eyebrows] ?? "wispy"}`
466
+ ];
467
+ const nose = NOSE_STYLES[t.nose];
468
+ if (nose) lines.push(`Nose: ${nose.replace(/^a /, "")}`);
469
+ lines.push(`Mouth: ${MOUTH_STYLES[t.mouth] ?? "smile"}`);
470
+ lines.push("Hair: bald");
471
+ const acc = ACCESSORY_DESC[ai];
472
+ if (acc) lines.push(`Accessory: ${acc}`);
473
+ if (includeBg) {
474
+ lines.push(`Background: ${BG_COLORS_DESC[t.bgColor] ?? "colorful"}`);
475
+ }
476
+ return lines.join("\n");
477
+ }
478
+ function buildCompact(t) {
479
+ const ai = effectiveAccessory(t);
480
+ const parts = [];
481
+ parts.push(`squircle face`);
482
+ parts.push(`${SKIN_TONES[t.skinColor] ?? "warm"} skin`);
483
+ parts.push(`${EYE_COLORS_DESC[t.eyeColor] ?? "dark"} ${EYE_STYLES[t.eyeStyle] ?? "round"} eyes`);
484
+ parts.push("bald");
485
+ const acc = ACCESSORY_DESC[ai];
486
+ if (acc) parts.push(acc);
487
+ parts.push((MOUTH_STYLES[t.mouth] ?? "smiling").replace(/^a /, ""));
488
+ return parts.join(", ");
489
+ }
490
+ function solFaceAltText(walletAddress) {
491
+ return `SolFace avatar: ${describeAppearance(walletAddress, { format: "compact", includeBackground: false })}`;
492
+ }
493
+ function agentAppearancePrompt(walletAddress, agentName) {
494
+ const desc = describeAppearance(walletAddress, {
495
+ perspective: "first",
496
+ name: agentName,
497
+ includeBackground: false,
498
+ format: "paragraph"
499
+ });
500
+ const hash = (djb2Simple(walletAddress) >>> 0).toString(16).padStart(8, "0");
501
+ return `My visual identity is a SolFace avatar (ID: ${hash}) derived from my wallet address. ${desc} This appearance is deterministic \u2014 anyone who looks up my wallet will see the same face.`;
502
+ }
503
+ function djb2Simple(str) {
504
+ let hash = 5381;
505
+ for (let i = 0; i < str.length; i++) {
506
+ hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
507
+ }
508
+ return hash >>> 0;
280
509
  }
281
510
 
282
511
  // src/core/renderer.ts
@@ -292,12 +521,9 @@ function buildDefs(id, skin, skinHi, skinLo, hairCol, bgCol, cheekColor, cheekOp
292
521
  let d = "<defs>";
293
522
  d += `<linearGradient id="${id}sg" x1="0" y1="0" x2="0" y2="1">`;
294
523
  d += `<stop offset="0%" stop-color="${skinHi}"/>`;
524
+ d += `<stop offset="50%" stop-color="${skin}"/>`;
295
525
  d += `<stop offset="100%" stop-color="${skinLo}"/>`;
296
526
  d += `</linearGradient>`;
297
- d += `<linearGradient id="${id}hg" x1="0" y1="0" x2="0" y2="1">`;
298
- d += `<stop offset="0%" stop-color="${lighten(hairCol, 0.15)}"/>`;
299
- d += `<stop offset="100%" stop-color="${darken(hairCol, 0.15)}"/>`;
300
- d += `</linearGradient>`;
301
527
  d += `<linearGradient id="${id}bg" x1="0" y1="0" x2="1" y2="1">`;
302
528
  d += `<stop offset="0%" stop-color="${lighten(bgCol, 0.12)}"/>`;
303
529
  d += `<stop offset="100%" stop-color="${darken(bgCol, 0.12)}"/>`;
@@ -323,18 +549,8 @@ function buildDefs(id, skin, skinHi, skinLo, hairCol, bgCol, cheekColor, cheekOp
323
549
  d += "</defs>";
324
550
  return d;
325
551
  }
326
- function renderHairBack(hi, id, flat) {
327
- const fill = flat ? "currentColor" : `url(#${id}hg)`;
328
- switch (hi) {
329
- case 5:
330
- return `<g fill="${fill}"><rect x="10" y="14" width="44" height="42" rx="6"/></g>`;
331
- case 6:
332
- return `<g fill="${fill}"><rect x="12" y="14" width="40" height="32" rx="8"/></g>`;
333
- case 8:
334
- return `<g fill="${fill}"><rect x="11" y="14" width="42" height="38" rx="8"/></g>`;
335
- default:
336
- return "";
337
- }
552
+ function renderHairBack(_hi, _id, _flat) {
553
+ return "";
338
554
  }
339
555
  function renderEars(earFill, earShadow) {
340
556
  return `<ellipse cx="11" cy="34" rx="4" ry="5" fill="${earFill}"/><ellipse cx="11" cy="34" rx="2.5" ry="3.5" fill="${earShadow}" opacity="0.3"/><ellipse cx="53" cy="34" rx="4" ry="5" fill="${earFill}"/><ellipse cx="53" cy="34" rx="2.5" ry="3.5" fill="${earShadow}" opacity="0.3"/>`;
@@ -348,35 +564,8 @@ function renderFaceOverlays(id, flat) {
348
564
  if (flat) return "";
349
565
  return `<rect x="14" y="16" width="36" height="38" rx="12" ry="12" fill="url(#${id}glow)"/><rect x="14" y="16" width="36" height="38" rx="12" ry="12" fill="url(#${id}chin)"/><ellipse cx="22" cy="42" rx="5" ry="3.5" fill="url(#${id}cL)"/><ellipse cx="42" cy="42" rx="5" ry="3.5" fill="url(#${id}cR)"/><line x1="20" y1="50" x2="44" y2="50" stroke="currentColor" stroke-width="0.3" opacity="0.08" stroke-linecap="round"/>`;
350
566
  }
351
- function renderHairFront(hi, id, hairCol, skin, flat) {
352
- const fill = flat ? hairCol : `url(#${id}hg)`;
353
- switch (hi) {
354
- case 0:
355
- return "";
356
- // Bald
357
- case 1:
358
- return `<path d="M14 28 Q14 14 32 12 Q50 14 50 28 L50 22 Q50 12 32 10 Q14 12 14 22 Z" fill="${fill}"/>`;
359
- case 2:
360
- return `<g fill="${fill}"><circle cx="20" cy="14" r="5"/><circle cx="28" cy="11" r="5.5"/><circle cx="36" cy="11" r="5.5"/><circle cx="44" cy="14" r="5"/><circle cx="16" cy="20" r="4"/><circle cx="48" cy="20" r="4"/></g>`;
361
- case 3:
362
- return `<path d="M14 26 Q14 12 32 10 Q50 12 50 26 L50 20 Q50 10 32 8 Q14 10 14 20 Z" fill="${fill}"/><path d="M14 20 Q8 16 10 8 Q14 10 20 16 Z" fill="${fill}"/>`;
363
- case 4:
364
- return `<ellipse cx="32" cy="10" rx="14" ry="8" fill="${fill}"/>`;
365
- case 5:
366
- return `<path d="M14 28 Q14 12 32 10 Q50 12 50 28 L50 20 Q50 10 32 8 Q14 10 14 20 Z" fill="${fill}"/>`;
367
- case 6:
368
- return `<path d="M14 28 Q14 12 32 10 Q50 12 50 28 L50 20 Q50 10 32 8 Q14 10 14 20 Z" fill="${fill}"/><rect x="10" y="28" width="8" height="14" rx="4" fill="${fill}"/><rect x="46" y="28" width="8" height="14" rx="4" fill="${fill}"/>`;
369
- case 7: {
370
- const bOp = buzzOpacity(hairCol, skin);
371
- return `<rect x="15" y="13" width="34" height="16" rx="10" ry="8" fill="${hairCol}" opacity="${bOp.toFixed(2)}"/>`;
372
- }
373
- case 8:
374
- return `<path d="M14 28 Q14 12 32 10 Q50 12 50 28 L50 20 Q50 10 32 8 Q14 10 14 20 Z" fill="${fill}"/><path d="M12 30 Q10 20 14 16" fill="none" stroke="${fill}" stroke-width="4" stroke-linecap="round"/><path d="M52 30 Q54 20 50 16" fill="none" stroke="${fill}" stroke-width="4" stroke-linecap="round"/>`;
375
- case 9:
376
- return `<path d="M14 28 Q14 14 32 12 Q50 14 50 28 L50 22 Q50 12 32 10 Q14 12 14 22 Z" fill="${fill}"/><ellipse cx="32" cy="6" rx="6" ry="5" fill="${fill}"/>`;
377
- default:
378
- return "";
379
- }
567
+ function renderHairFront(_hi, _id, _hairCol, _skin, _flat) {
568
+ return "";
380
569
  }
381
570
  function renderEyes(ei, eyeCol, eyeWhite, lidColor, full) {
382
571
  const lx = 25, rx = 39, y = 33;
@@ -391,8 +580,10 @@ function renderEyes(ei, eyeCol, eyeWhite, lidColor, full) {
391
580
  if (full) s += `<circle cx="${rx + 1.5}" cy="${y - 1}" r="0.7" fill="white" opacity="0.8"/>`;
392
581
  break;
393
582
  case 1:
394
- s += `<circle cx="${lx}" cy="${y}" r="2" fill="${eyeCol}"/>`;
395
- s += `<circle cx="${rx}" cy="${y}" r="2" fill="${eyeCol}"/>`;
583
+ s += `<circle cx="${lx}" cy="${y}" r="2.2" fill="${eyeCol}"/>`;
584
+ if (full) s += `<circle cx="${lx + 0.5}" cy="${y - 0.5}" r="0.5" fill="white" opacity="0.4"/>`;
585
+ s += `<circle cx="${rx}" cy="${y}" r="2.2" fill="${eyeCol}"/>`;
586
+ if (full) s += `<circle cx="${rx + 0.5}" cy="${y - 0.5}" r="0.5" fill="white" opacity="0.4"/>`;
396
587
  break;
397
588
  case 2:
398
589
  s += `<ellipse cx="${lx}" cy="${y}" rx="4.5" ry="2.8" fill="${eyeWhite}"/>`;
@@ -419,24 +610,16 @@ function renderEyes(ei, eyeCol, eyeWhite, lidColor, full) {
419
610
  if (full) s += `<line x1="${rx - 4.5}" y1="${y - 0.5}" x2="${rx + 4.5}" y2="${y - 0.5}" stroke="${lidColor}" stroke-width="0.8" stroke-linecap="round"/>`;
420
611
  break;
421
612
  case 5:
422
- s += `<path d="M${lx - 4} ${y} Q${lx} ${y + 4} ${lx + 4} ${y}" fill="none" stroke="${eyeCol}" stroke-width="1.8" stroke-linecap="round"/>`;
423
- s += `<path d="M${rx - 4} ${y} Q${rx} ${y + 4} ${rx + 4} ${y}" fill="none" stroke="${eyeCol}" stroke-width="1.8" stroke-linecap="round"/>`;
613
+ s += `<path d="M${lx - 4} ${y} Q${lx} ${y + 4.5} ${lx + 4} ${y}" fill="none" stroke="${eyeCol}" stroke-width="2" stroke-linecap="round"/>`;
614
+ s += `<path d="M${rx - 4} ${y} Q${rx} ${y + 4.5} ${rx + 4} ${y}" fill="none" stroke="${eyeCol}" stroke-width="2" stroke-linecap="round"/>`;
424
615
  break;
425
616
  case 6:
426
617
  s += `<circle cx="${lx}" cy="${y}" r="3.5" fill="${eyeWhite}"/>`;
427
618
  s += `<circle cx="${lx + 0.5}" cy="${y}" r="2" fill="${eyeCol}"/>`;
428
619
  s += `<circle cx="${lx + 1.5}" cy="${y - 1}" r="1" fill="white" opacity="0.9"/>`;
429
- if (full) {
430
- s += `<line x1="${lx + 2.5}" y1="${y - 3.5}" x2="${lx + 4}" y2="${y - 5}" stroke="${eyeCol}" stroke-width="0.8" stroke-linecap="round"/>`;
431
- s += `<line x1="${lx + 3.5}" y1="${y - 2.5}" x2="${lx + 5}" y2="${y - 3.5}" stroke="${eyeCol}" stroke-width="0.8" stroke-linecap="round"/>`;
432
- }
433
620
  s += `<circle cx="${rx}" cy="${y}" r="3.5" fill="${eyeWhite}"/>`;
434
621
  s += `<circle cx="${rx + 0.5}" cy="${y}" r="2" fill="${eyeCol}"/>`;
435
622
  s += `<circle cx="${rx + 1.5}" cy="${y - 1}" r="1" fill="white" opacity="0.9"/>`;
436
- if (full) {
437
- s += `<line x1="${rx + 2.5}" y1="${y - 3.5}" x2="${rx + 4}" y2="${y - 5}" stroke="${eyeCol}" stroke-width="0.8" stroke-linecap="round"/>`;
438
- s += `<line x1="${rx + 3.5}" y1="${y - 2.5}" x2="${rx + 5}" y2="${y - 3.5}" stroke="${eyeCol}" stroke-width="0.8" stroke-linecap="round"/>`;
439
- }
440
623
  break;
441
624
  case 7:
442
625
  s += `<ellipse cx="${lx}" cy="${y}" rx="4.5" ry="1.5" fill="${eyeWhite}"/>`;
@@ -444,6 +627,14 @@ function renderEyes(ei, eyeCol, eyeWhite, lidColor, full) {
444
627
  s += `<ellipse cx="${rx}" cy="${y}" rx="4.5" ry="1.5" fill="${eyeWhite}"/>`;
445
628
  s += `<ellipse cx="${rx + 0.5}" cy="${y}" rx="2.2" ry="1.2" fill="${eyeCol}"/>`;
446
629
  break;
630
+ case 8:
631
+ s += `<circle cx="${lx}" cy="${y}" r="3.5" fill="${eyeWhite}"/>`;
632
+ s += `<circle cx="${lx - 1}" cy="${y}" r="2" fill="${eyeCol}"/>`;
633
+ if (full) s += `<circle cx="${lx - 0.3}" cy="${y - 0.8}" r="0.7" fill="white" opacity="0.8"/>`;
634
+ s += `<circle cx="${rx}" cy="${y}" r="3.5" fill="${eyeWhite}"/>`;
635
+ s += `<circle cx="${rx - 1}" cy="${y}" r="2" fill="${eyeCol}"/>`;
636
+ if (full) s += `<circle cx="${rx - 0.3}" cy="${y - 0.8}" r="0.7" fill="white" opacity="0.8"/>`;
637
+ break;
447
638
  default:
448
639
  s += `<circle cx="${lx}" cy="${y}" r="3.5" fill="${eyeWhite}"/>`;
449
640
  s += `<circle cx="${lx + 0.8}" cy="${y}" r="2" fill="${eyeCol}"/>`;
@@ -456,7 +647,7 @@ function renderEyes(ei, eyeCol, eyeWhite, lidColor, full) {
456
647
  }
457
648
  return s;
458
649
  }
459
- function renderEyebrows(bi, browColor) {
650
+ function renderEyebrows(bi, browColor, full = true) {
460
651
  const lx = 25, rx = 39, y = 27;
461
652
  switch (bi) {
462
653
  case 0:
@@ -469,6 +660,14 @@ function renderEyebrows(bi, browColor) {
469
660
  return `<path d="M${lx - 4} ${y + 1} Q${lx} ${y - 3} ${lx + 4} ${y + 1}" fill="none" stroke="${browColor}" stroke-width="1" stroke-linecap="round"/><path d="M${rx - 4} ${y + 1} Q${rx} ${y - 3} ${rx + 4} ${y + 1}" fill="none" stroke="${browColor}" stroke-width="1" stroke-linecap="round"/>`;
470
661
  case 4:
471
662
  return `<polyline points="${lx - 3},${y + 1} ${lx},${y - 2} ${lx + 3},${y}" fill="none" stroke="${browColor}" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/><polyline points="${rx - 3},${y} ${rx},${y - 2} ${rx + 3},${y + 1}" fill="none" stroke="${browColor}" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>`;
663
+ case 5:
664
+ return `<line x1="${lx - 3}" y1="${y - 1}" x2="${lx + 3}" y2="${y + 1}" stroke="${browColor}" stroke-width="1.1" stroke-linecap="round"/><line x1="${rx - 3}" y1="${y + 1}" x2="${rx + 3}" y2="${y - 1}" stroke="${browColor}" stroke-width="1.1" stroke-linecap="round"/>`;
665
+ case 6: {
666
+ const bw = full ? "2.0" : "1.5";
667
+ return `<path d="M${lx - 4} ${y + 0.5} Q${lx} ${y - 2} ${lx + 4} ${y + 0.5}" fill="none" stroke="${browColor}" stroke-width="${bw}" stroke-linecap="round"/><path d="M${rx - 4} ${y + 0.5} Q${rx} ${y - 2} ${rx + 4} ${y + 0.5}" fill="none" stroke="${browColor}" stroke-width="${bw}" stroke-linecap="round"/>`;
668
+ }
669
+ case 7:
670
+ return `<path d="M${lx - 3.5} ${y} Q${lx} ${y - 1.5} ${lx + 3.5} ${y}" fill="none" stroke="${browColor}" stroke-width="0.5" stroke-linecap="round"/><path d="M${rx - 3.5} ${y} Q${rx} ${y - 1.5} ${rx + 3.5} ${y}" fill="none" stroke="${browColor}" stroke-width="0.5" stroke-linecap="round"/>`;
472
671
  default:
473
672
  return "";
474
673
  }
@@ -484,6 +683,14 @@ function renderNose(ni, noseFill) {
484
683
  return `<path d="M${cx - 2} ${y + 1} Q${cx} ${y - 2} ${cx + 2} ${y + 1}" fill="none" stroke="${noseFill}" stroke-width="1" stroke-linecap="round" opacity="0.5"/>`;
485
684
  case 3:
486
685
  return `<circle cx="${cx - 1.8}" cy="${y}" r="1.2" fill="${noseFill}" opacity="0.4"/><circle cx="${cx + 1.8}" cy="${y}" r="1.2" fill="${noseFill}" opacity="0.4"/>`;
686
+ case 4:
687
+ return `<path d="M${cx} ${y - 2} L${cx - 2} ${y + 1.5} L${cx + 2} ${y + 1.5} Z" fill="${noseFill}" opacity="0.4"/>`;
688
+ case 5:
689
+ return `<ellipse cx="${cx}" cy="${y}" rx="3.5" ry="1.5" fill="${noseFill}" opacity="0.35"/>`;
690
+ case 6:
691
+ return `<line x1="${cx}" y1="${y - 3}" x2="${cx}" y2="${y + 1}" stroke="${noseFill}" stroke-width="1.2" stroke-linecap="round" opacity="0.4"/>`;
692
+ case 7:
693
+ return `<circle cx="${cx}" cy="${y + 0.5}" r="2" fill="${noseFill}" opacity="0.35"/><ellipse cx="${cx}" cy="${y - 0.5}" rx="1" ry="0.5" fill="${noseFill}" opacity="0.15"/>`;
487
694
  default:
488
695
  return `<ellipse cx="${cx}" cy="${y}" rx="2" ry="1.2" fill="${noseFill}" opacity="0.35"/>`;
489
696
  }
@@ -505,20 +712,20 @@ function renderMouth(mi, lipColor, isDark) {
505
712
  case 5:
506
713
  return `<path d="M${cx - 5} ${y} Q${cx} ${y + 6} ${cx + 5} ${y}" fill="${teethCol}" stroke="${lipColor}" stroke-width="1"/><line x1="${cx - 4}" y1="${y + 1.5}" x2="${cx + 4}" y2="${y + 1.5}" stroke="${lipColor}" stroke-width="0.3" opacity="0.3"/>`;
507
714
  case 6:
508
- return `<line x1="${cx - 4}" y1="${y + 1}" x2="${cx + 4}" y2="${y + 1}" stroke="${lipColor}" stroke-width="1.5" stroke-linecap="round"/>`;
715
+ return `<path d="M${cx - 4} ${y + 0.5} Q${cx} ${y + 1.5} ${cx + 4} ${y + 0.5}" fill="none" stroke="${lipColor}" stroke-width="1.4" stroke-linecap="round"/>`;
509
716
  case 7:
510
717
  return `<ellipse cx="${cx}" cy="${y + 1}" rx="3.5" ry="2" fill="${lipColor}" opacity="0.25"/><path d="M${cx - 3} ${y} Q${cx} ${y + 2.5} ${cx + 3} ${y}" fill="none" stroke="${lipColor}" stroke-width="1.2" stroke-linecap="round"/>`;
511
718
  default:
512
719
  return `<path d="M${cx - 4} ${y} Q${cx} ${y + 4} ${cx + 4} ${y}" fill="none" stroke="${lipColor}" stroke-width="1.4" stroke-linecap="round"/>`;
513
720
  }
514
721
  }
515
- function renderAccessory(ai, accessoryColor, glassesColor, earringColor, headbandColor) {
722
+ function renderAccessory(ai, accessoryColor, glassesColor, earringColor, headbandColor, beautyMarkColor = "#3a2a2a", freckleColor = "#a0785a", skinColor = "#E8BA8B") {
516
723
  switch (ai) {
517
724
  case 0:
518
725
  return "";
519
726
  // None
520
727
  case 1:
521
- return `<circle cx="40" cy="44" r="0.8" fill="#3a2a2a"/>`;
728
+ return `<circle cx="40" cy="44" r="0.8" fill="${beautyMarkColor}"/>`;
522
729
  case 2:
523
730
  return `<g fill="none" stroke="${glassesColor}" stroke-width="1"><circle cx="25" cy="33" r="5.5"/><circle cx="39" cy="33" r="5.5"/><line x1="30.5" y1="33" x2="33.5" y2="33"/><line x1="19.5" y1="33" x2="14" y2="31"/><line x1="44.5" y1="33" x2="50" y2="31"/></g>`;
524
731
  case 3:
@@ -528,13 +735,17 @@ function renderAccessory(ai, accessoryColor, glassesColor, earringColor, headban
528
735
  case 5:
529
736
  return `<rect x="13" y="20" width="38" height="3.5" rx="1.5" fill="${headbandColor}" opacity="0.85"/>`;
530
737
  case 6:
531
- return `<g fill="#a0785a" opacity="0.35"><circle cx="21" cy="40" r="0.6"/><circle cx="23" cy="42" r="0.5"/><circle cx="19" cy="41.5" r="0.5"/><circle cx="43" cy="40" r="0.6"/><circle cx="41" cy="42" r="0.5"/><circle cx="45" cy="41.5" r="0.5"/></g>`;
738
+ return `<g fill="${freckleColor}" opacity="0.35"><circle cx="21" cy="40" r="0.6"/><circle cx="23" cy="42" r="0.5"/><circle cx="19" cy="41.5" r="0.5"/><circle cx="43" cy="40" r="0.6"/><circle cx="41" cy="42" r="0.5"/><circle cx="45" cy="41.5" r="0.5"/></g>`;
532
739
  case 7:
533
740
  return `<circle cx="10" cy="37" r="1.2" fill="${earringColor}"/><circle cx="54" cy="37" r="1.2" fill="${earringColor}"/>`;
534
741
  case 8:
535
742
  return `<g fill="none" stroke="${glassesColor}" stroke-width="1.2"><path d="M19 30 Q19 28 25 28 Q31 28 31 33 Q31 38 25 38 Q19 38 19 33 Z" fill="${glassesColor}" fill-opacity="0.15"/><path d="M33 30 Q33 28 39 28 Q45 28 45 33 Q45 38 39 38 Q33 38 33 33 Z" fill="${glassesColor}" fill-opacity="0.15"/><line x1="31" y1="32" x2="33" y2="32"/><line x1="19" y1="31" x2="14" y2="29"/><line x1="45" y1="31" x2="50" y2="29"/></g>`;
536
743
  case 9:
537
- return `<g><rect x="38" y="38" width="8" height="4" rx="1" fill="#f0d0a0" transform="rotate(-15 42 40)"/><line x1="40" y1="39" x2="40" y2="41" stroke="#c0a080" stroke-width="0.4" transform="rotate(-15 42 40)"/><line x1="42" y1="39" x2="42" y2="41" stroke="#c0a080" stroke-width="0.4" transform="rotate(-15 42 40)"/><line x1="44" y1="39" x2="44" y2="41" stroke="#c0a080" stroke-width="0.4" transform="rotate(-15 42 40)"/></g>`;
744
+ return `<g><rect x="38" y="38" width="9" height="4.5" rx="1.2" fill="#f0d0a0" transform="rotate(-15 42 40)"/><rect x="39.5" y="38.5" width="6" height="3.5" rx="0.8" fill="#f5ddb5" transform="rotate(-15 42 40)"/><circle cx="42.5" cy="40.25" r="0.5" fill="#d4b898" transform="rotate(-15 42 40)"/></g>`;
745
+ case 10:
746
+ return `<line x1="23" y1="24.8" x2="23.8" y2="29.2" stroke="${skinColor}" stroke-width="1.3" stroke-linecap="butt"/>`;
747
+ case 11:
748
+ return `<line x1="41" y1="24.8" x2="40.2" y2="29.2" stroke="${skinColor}" stroke-width="1.3" stroke-linecap="butt"/>`;
538
749
  default:
539
750
  return "";
540
751
  }
@@ -564,14 +775,17 @@ function renderSolFaceSVG(walletAddress, options) {
564
775
  const glassesColor = theme?.glassesColor ?? "#4a4a5a";
565
776
  const earringColor = theme?.earringColor ?? blend(skin, "#d4a840", 0.4);
566
777
  const headbandColor = theme?.headbandColor ?? blend(hairCol, "#c04040", 0.5);
778
+ const beautyMarkColor = theme?.beautyMarkColor ?? "#3a2a2a";
779
+ const freckleColor = theme?.freckleColor ?? "#a0785a";
567
780
  const id = "sf" + djb22(walletAddress).toString(36);
568
781
  const cheekEnabled = theme?.cheekEnabled ?? true;
569
782
  const cheekColor = theme?.cheekColor ?? derived.cheekColor;
570
783
  const cheekOpacity = theme?.cheekOpacity ?? derived.cheekOpacity;
571
- const hi = traits.hairStyle % 10;
784
+ traits.hairStyle % 10;
572
785
  const ai = effectiveAccessory(traits);
573
786
  const classAttr = className ? ` class="${className}"` : "";
574
- let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="${size}" height="${size}"${classAttr}>`;
787
+ const altText = solFaceAltText(walletAddress).replace(/"/g, "&quot;");
788
+ let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="${size}" height="${size}"${classAttr} role="img" aria-label="${altText}">`;
575
789
  const glowIntensity = theme?.glowIntensity ?? 0.1;
576
790
  svg += buildDefs(id, skin, derived.skinHi, derived.skinLo, hairCol, bgCol, cheekColor, cheekOpacity, flat, full && cheekEnabled, glowIntensity);
577
791
  const blinkEnabled = !!enableBlink;
@@ -584,29 +798,29 @@ function renderSolFaceSVG(walletAddress, options) {
584
798
  }
585
799
  const bgFill = flat ? bgCol : `url(#${id}bg)`;
586
800
  svg += `<rect x="0" y="0" width="64" height="64" fill="${bgFill}" opacity="${bgOpacity}" rx="${bgRadius}"/>`;
587
- svg += renderHairBack(hi, id, flat);
588
- svg += renderEars(derived.earFill, derived.earShadow);
801
+ if (theme?.hairEnabled !== false) svg += renderHairBack();
802
+ if (theme?.earsEnabled !== false) svg += renderEars(theme?.earColor ?? derived.earFill, derived.earShadow);
589
803
  const skinOpacity = theme?.skinOpacity ?? 1;
590
804
  svg += renderFace(id, skin, flat, skinOpacity);
591
805
  const shadowEnabled = theme?.shadowEnabled ?? true;
592
806
  if (full && cheekEnabled && shadowEnabled) {
593
807
  svg += renderFaceOverlays(id, flat);
594
808
  }
595
- if (ai === 5) {
596
- svg += renderAccessory(5, accColor, glassesColor, earringColor, headbandColor);
809
+ if (theme?.hairEnabled !== false) svg += renderHairFront();
810
+ if (ai === 5 && theme?.accessoriesEnabled !== false) {
811
+ svg += renderAccessory(5, accColor, glassesColor, earringColor, headbandColor, beautyMarkColor, freckleColor, skin);
597
812
  }
598
- svg += renderHairFront(hi, id, hairCol, skin, flat);
599
813
  if (blinkEnabled) {
600
814
  const uid = `sf-${walletAddress.slice(0, 8)}`;
601
815
  svg += `<g class="${uid}-eyes">`;
602
816
  }
603
- svg += renderEyes(traits.eyeStyle % 8, eyeCol, eyeWhite, derived.lidColor, full);
817
+ svg += renderEyes(traits.eyeStyle % 9, eyeCol, eyeWhite, theme?.lidColor ?? derived.lidColor, full);
604
818
  if (blinkEnabled) svg += `</g>`;
605
- svg += renderEyebrows(traits.eyebrows % 5, browColor);
606
- svg += renderNose(traits.nose % 4, noseFill);
819
+ if (theme?.eyebrowsEnabled !== false) svg += renderEyebrows(traits.eyebrows % 8, browColor, full);
820
+ if (theme?.noseEnabled !== false) svg += renderNose(traits.nose % 8, noseFill);
607
821
  svg += renderMouth(traits.mouth % 8, lipColor, derived.isDark);
608
- if (ai !== 0 && ai !== 5) {
609
- svg += renderAccessory(ai, accColor, glassesColor, earringColor, headbandColor);
822
+ if (ai !== 0 && ai !== 5 && theme?.accessoriesEnabled !== false) {
823
+ svg += renderAccessory(ai, accColor, glassesColor, earringColor, headbandColor, beautyMarkColor, freckleColor, skin);
610
824
  }
611
825
  if (theme?.border) {
612
826
  svg += `<rect x="0" y="0" width="64" height="64" fill="none" stroke="${theme.border.color}" stroke-width="${theme.border.width}" rx="${bgRadius}"/>`;
@@ -627,10 +841,13 @@ exports.BG_COLORS = BG_COLORS;
627
841
  exports.EYE_COLORS = EYE_COLORS;
628
842
  exports.HAIR_COLORS = HAIR_COLORS;
629
843
  exports.SKIN_COLORS = SKIN_COLORS;
844
+ exports.agentAppearancePrompt = agentAppearancePrompt;
630
845
  exports.blend = blend;
631
- exports.buzzOpacity = buzzOpacity;
846
+ exports.contrastRatio = contrastRatio;
632
847
  exports.darken = darken;
633
848
  exports.deriveSkinColors = deriveSkinColors;
849
+ exports.describeAppearance = describeAppearance;
850
+ exports.describeTraits = describeTraits;
634
851
  exports.effectiveAccessory = effectiveAccessory;
635
852
  exports.generateTraits = generateTraits;
636
853
  exports.getTraitLabels = getTraitLabels;
@@ -643,6 +860,7 @@ exports.renderSolFaceDataURI = renderSolFaceDataURI;
643
860
  exports.renderSolFaceSVG = renderSolFaceSVG;
644
861
  exports.resolveTheme = resolveTheme;
645
862
  exports.rgbToHex = rgbToHex;
863
+ exports.solFaceAltText = solFaceAltText;
646
864
  exports.traitHash = traitHash;
647
- //# sourceMappingURL=chunk-F54WHRCE.cjs.map
648
- //# sourceMappingURL=chunk-F54WHRCE.cjs.map
865
+ //# sourceMappingURL=chunk-W2U6ITMR.cjs.map
866
+ //# sourceMappingURL=chunk-W2U6ITMR.cjs.map