clay-server 2.17.0-beta.6 → 2.17.0-beta.8

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/lib/project.js CHANGED
@@ -246,6 +246,7 @@ function createProjectContext(opts) {
246
246
  username: u.username,
247
247
  avatarStyle: p.avatarStyle || "thumbs",
248
248
  avatarSeed: p.avatarSeed || u.username,
249
+ avatarCustom: p.avatarCustom || "",
249
250
  });
250
251
  }
251
252
  msg.users = userList;
@@ -2076,6 +2077,7 @@ function createProjectContext(opts) {
2076
2077
  displayName: p.name || u.displayName || u.username,
2077
2078
  avatarStyle: p.avatarStyle || "thumbs",
2078
2079
  avatarSeed: p.avatarSeed || u.username,
2080
+ avatarCustom: p.avatarCustom || "",
2079
2081
  };
2080
2082
  if (msg.type === "cursor_move") {
2081
2083
  cursorMsg.turn = msg.turn;
@@ -3504,6 +3506,7 @@ function createProjectContext(opts) {
3504
3506
  username: u.username,
3505
3507
  avatarStyle: p.avatarStyle || "thumbs",
3506
3508
  avatarSeed: p.avatarSeed || u.username,
3509
+ avatarCustom: p.avatarCustom || "",
3507
3510
  });
3508
3511
  }
3509
3512
  send({ type: "session_presence", presence: presence });
@@ -4124,6 +4127,7 @@ function createProjectContext(opts) {
4124
4127
  username: u.username,
4125
4128
  avatarStyle: p.avatarStyle || "thumbs",
4126
4129
  avatarSeed: p.avatarSeed || u.username,
4130
+ avatarCustom: p.avatarCustom || "",
4127
4131
  });
4128
4132
  }
4129
4133
  status.onlineUsers = onlineUsers;
package/lib/public/app.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { avatarUrl, userAvatarUrl, mateAvatarUrl } from './modules/avatar.js';
1
2
  import { showToast, copyToClipboard, escapeHtml } from './modules/utils.js';
2
3
  import { refreshIcons, iconHtml, randomThinkingVerb } from './modules/icons.js';
3
4
  import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidModal, parseEmojis } from './modules/markdown.js';
@@ -651,20 +652,20 @@ import { initLongPress } from './modules/longpress.js';
651
652
  document.body.classList.add("mate-dm-active");
652
653
  // Build mate avatar URL for DM bubble injection
653
654
  var mp = targetUser.profile || {};
654
- var mateAvatarUrl = "https://api.dicebear.com/9.x/" + (mp.avatarStyle || targetUser.avatarStyle || "bottts") + "/svg?seed=" + encodeURIComponent(mp.avatarSeed || targetUser.avatarSeed || targetUser.id) + "&size=36";
655
+ var mateAvUrlDm = mateAvatarUrl(targetUser, 36);
655
656
  var myUser = cachedAllUsers.find(function (u) { return u.id === myUserId; });
656
657
  if (!myUser) {
657
658
  try { var cached = JSON.parse(localStorage.getItem("clay_my_user") || "null"); if (cached) myUser = cached; } catch(e) {}
658
659
  }
659
- var myAvatarUrl = "https://api.dicebear.com/9.x/" + ((myUser && myUser.avatarStyle) || "thumbs") + "/svg?seed=" + encodeURIComponent((myUser && (myUser.avatarSeed || myUser.username)) || myUserId) + "&size=36";
660
+ var myAvatarUrl = userAvatarUrl(myUser || { id: myUserId }, 36);
660
661
  var myDisplayName = (myUser && myUser.displayName) || "";
661
- document.body.dataset.mateAvatarUrl = mateAvatarUrl;
662
+ document.body.dataset.mateAvatarUrl = mateAvUrlDm;
662
663
  document.body.dataset.mateName = mp.displayName || targetUser.displayName || targetUser.name || "";
663
664
  document.body.dataset.myAvatarUrl = myAvatarUrl;
664
665
  document.body.dataset.myDisplayName = myDisplayName;
665
666
  // Cache my info for restore after hard refresh
666
667
  if (myUser) {
667
- try { localStorage.setItem("clay_my_user", JSON.stringify({ displayName: myUser.displayName, avatarStyle: myUser.avatarStyle, avatarSeed: myUser.avatarSeed, username: myUser.username })); } catch(e) {}
668
+ try { localStorage.setItem("clay_my_user", JSON.stringify({ displayName: myUser.displayName, avatarStyle: myUser.avatarStyle, avatarSeed: myUser.avatarSeed, avatarCustom: myUser.avatarCustom, username: myUser.username })); } catch(e) {}
668
669
  }
669
670
  var titleBarContent = document.querySelector(".title-bar-content");
670
671
  if (titleBarContent) {
@@ -677,7 +678,7 @@ import { initLongPress } from './modules/longpress.js';
677
678
  var mateMobileAvatar = document.getElementById("mate-mobile-avatar");
678
679
  var mateMobileName = document.getElementById("mate-mobile-name");
679
680
  var mateMobileStatus = document.getElementById("mate-mobile-status");
680
- if (mateMobileAvatar) mateMobileAvatar.src = mateAvatarUrl;
681
+ if (mateMobileAvatar) mateMobileAvatar.src = mateAvUrlDm;
681
682
  if (mateMobileName) mateMobileName.textContent = (mp.displayName || targetUser.displayName || targetUser.name || "");
682
683
  if (mateMobileStatus) mateMobileStatus.textContent = "online";
683
684
  mateMobileTitle.classList.remove("hidden");
@@ -686,7 +687,7 @@ import { initLongPress } from './modules/longpress.js';
686
687
  id: targetUser.id,
687
688
  displayName: mp.displayName || targetUser.displayName || targetUser.name || "",
688
689
  description: mp.description || targetUser.description || "",
689
- avatarUrl: mateAvatarUrl,
690
+ avatarUrl: mateAvUrlDm,
690
691
  color: mateColor
691
692
  });
692
693
  }
@@ -723,7 +724,7 @@ import { initLongPress } from './modules/longpress.js';
723
724
  } else {
724
725
  if (dmHeaderBar) dmHeaderBar.style.display = "";
725
726
  if (dmAvatar) {
726
- dmAvatar.src = "https://api.dicebear.com/9.x/" + (targetUser.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(targetUser.avatarSeed || targetUser.username) + "&size=28";
727
+ dmAvatar.src = userAvatarUrl(targetUser, 28);
727
728
  }
728
729
  if (dmName) dmName.textContent = targetUser.displayName;
729
730
  if (dmHeaderBar && targetUser.avatarColor) {
@@ -1165,11 +1166,9 @@ import { initLongPress } from './modules/longpress.js';
1165
1166
  avatar.className = "dm-msg-avatar";
1166
1167
  if (isMe) {
1167
1168
  var myUser = cachedAllUsers.find(function (u) { return u.id === myUserId; });
1168
- var myStyle = myUser ? myUser.avatarStyle : "thumbs";
1169
- var mySeed = myUser ? (myUser.avatarSeed || myUser.username) : myUserId;
1170
- avatar.src = "https://api.dicebear.com/9.x/" + (myStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(mySeed) + "&size=36";
1169
+ avatar.src = userAvatarUrl(myUser || { id: myUserId }, 36);
1171
1170
  } else if (dmTargetUser) {
1172
- avatar.src = "https://api.dicebear.com/9.x/" + (dmTargetUser.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(dmTargetUser.avatarSeed || dmTargetUser.username) + "&size=36";
1171
+ avatar.src = userAvatarUrl(dmTargetUser, 36);
1173
1172
  }
1174
1173
  div.appendChild(avatar);
1175
1174
 
@@ -1224,7 +1223,7 @@ import { initLongPress } from './modules/longpress.js';
1224
1223
 
1225
1224
  var avatar = document.createElement("img");
1226
1225
  avatar.className = "dm-msg-avatar";
1227
- avatar.src = "https://api.dicebear.com/9.x/" + (dmTargetUser.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(dmTargetUser.avatarSeed || dmTargetUser.username) + "&size=36";
1226
+ avatar.src = userAvatarUrl(dmTargetUser, 36);
1228
1227
  div.appendChild(avatar);
1229
1228
 
1230
1229
  var dots = document.createElement("div");
@@ -1272,10 +1271,10 @@ import { initLongPress } from './modules/longpress.js';
1272
1271
  avatarWrap.className = "home-hub-mate-avatar-wrap";
1273
1272
 
1274
1273
  var mp = mate.profile || {};
1275
- var avatarUrl = "https://api.dicebear.com/9.x/" + (mp.avatarStyle || mate.avatarStyle || "bottts") + "/svg?seed=" + encodeURIComponent(mp.avatarSeed || mate.avatarSeed || mate.id) + "&size=48";
1274
+ var mateAvUrl = mateAvatarUrl(mate, 48);
1276
1275
  var avatar = document.createElement("img");
1277
1276
  avatar.className = "home-hub-mate-avatar";
1278
- avatar.src = avatarUrl;
1277
+ avatar.src = mateAvUrl;
1279
1278
  avatar.alt = mp.displayName || mate.displayName || mate.name || "";
1280
1279
  avatarWrap.appendChild(avatar);
1281
1280
 
@@ -1407,7 +1406,7 @@ import { initLongPress } from './modules/longpress.js';
1407
1406
  var refreshedMyUser = cachedAllUsers.find(function (u) { return u.id === myUserId; });
1408
1407
  if (refreshedMyUser) {
1409
1408
  document.body.dataset.myDisplayName = refreshedMyUser.displayName || "";
1410
- document.body.dataset.myAvatarUrl = "https://api.dicebear.com/9.x/" + (refreshedMyUser.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(refreshedMyUser.avatarSeed || refreshedMyUser.username) + "&size=36";
1409
+ document.body.dataset.myAvatarUrl = userAvatarUrl(refreshedMyUser, 36);
1411
1410
  }
1412
1411
  }
1413
1412
  // Render my avatar (always present, hidden behind user-island)
@@ -1417,7 +1416,7 @@ import { initLongPress } from './modules/longpress.js';
1417
1416
  if (myUser) {
1418
1417
  var meAvatar = document.createElement("img");
1419
1418
  meAvatar.className = "icon-strip-me-avatar";
1420
- meAvatar.src = "https://api.dicebear.com/9.x/" + (myUser.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(myUser.avatarSeed || myUser.username) + "&size=34";
1419
+ meAvatar.src = userAvatarUrl(myUser, 34);
1421
1420
  meEl.appendChild(meAvatar);
1422
1421
  }
1423
1422
  }
@@ -1433,7 +1432,7 @@ import { initLongPress } from './modules/longpress.js';
1433
1432
  var cu = serverUsers[cui];
1434
1433
  var cuImg = document.createElement("img");
1435
1434
  cuImg.className = "client-avatar";
1436
- cuImg.src = "https://api.dicebear.com/9.x/" + (cu.avatarStyle || "thumbs") + "/svg?seed=" + encodeURIComponent(cu.avatarSeed || cu.username) + "&size=24";
1435
+ cuImg.src = userAvatarUrl(cu, 24);
1437
1436
  cuImg.alt = cu.displayName;
1438
1437
  cuImg.dataset.tip = cu.displayName + " (@" + cu.username + ")";
1439
1438
  if (cui > 0) cuImg.style.marginLeft = "-6px";
@@ -4618,7 +4617,7 @@ import { initLongPress } from './modules/longpress.js';
4618
4617
  updateMateSidebarProfile(msg.mate);
4619
4618
  }
4620
4619
  // Update DM header if currently chatting with this mate
4621
- if (dmMode && currentDmTarget === msg.mate.id) {
4620
+ if (dmMode && dmTargetUser && dmTargetUser.id === msg.mate.id) {
4622
4621
  var updatedName = (msg.mate.profile && msg.mate.profile.displayName) || msg.mate.name;
4623
4622
  if (updatedName) {
4624
4623
  var dmHeaderName = document.getElementById("dm-header-name");
@@ -6855,7 +6854,7 @@ import { initLongPress } from './modules/longpress.js';
6855
6854
  return userColorMap[userId];
6856
6855
  }
6857
6856
 
6858
- function createCursorElement(userId, displayName, color, avatarStyle, avatarSeed) {
6857
+ function createCursorElement(userId, displayName, color, avatarStyle, avatarSeed, avatarCustom) {
6859
6858
  var wrapper = document.createElement("div");
6860
6859
  wrapper.className = "remote-cursor";
6861
6860
  wrapper.dataset.userId = userId;
@@ -6891,9 +6890,7 @@ import { initLongPress } from './modules/longpress.js';
6891
6890
  // Avatar
6892
6891
  var avatarImg = document.createElement("img");
6893
6892
  avatarImg.className = "remote-cursor-avatar";
6894
- var style = avatarStyle || "thumbs";
6895
- var seed = avatarSeed || userId;
6896
- avatarImg.src = "https://api.dicebear.com/9.x/" + style + "/svg?seed=" + encodeURIComponent(seed) + "&size=16";
6893
+ avatarImg.src = avatarCustom ? avatarCustom : avatarUrl(avatarStyle || "thumbs", avatarSeed || userId, 16);
6897
6894
  avatarImg.style.cssText = "width:14px;height:14px;border-radius:50%;background:#fff;flex-shrink:0;";
6898
6895
  tag.appendChild(avatarImg);
6899
6896
 
@@ -7065,7 +7062,7 @@ import { initLongPress } from './modules/longpress.js';
7065
7062
  var entry = remoteCursors[userId];
7066
7063
  if (!entry) {
7067
7064
  var color = getCursorColor(userId);
7068
- var el = createCursorElement(userId, msg.displayName, color, msg.avatarStyle, msg.avatarSeed);
7065
+ var el = createCursorElement(userId, msg.displayName, color, msg.avatarStyle, msg.avatarSeed, msg.avatarCustom);
7069
7066
  messagesEl.appendChild(el);
7070
7067
  var indicator = createOffscreenIndicator(userId, msg.displayName, color);
7071
7068
  messagesEl.appendChild(indicator);
@@ -320,7 +320,7 @@
320
320
  right: 0;
321
321
  transform: none;
322
322
  width: 100%;
323
- max-height: 85vh;
323
+ max-height: 85dvh;
324
324
  border-radius: 16px 16px 0 0;
325
325
  border: none;
326
326
  border-top: 1px solid var(--border-subtle);
@@ -1,3 +1,15 @@
1
+ /* === Avatar anti-aliasing === */
2
+ .mate-sidebar-avatar,
3
+ .mate-collapsed-avatar,
4
+ .dm-bubble-avatar,
5
+ .dm-bubble-avatar-me,
6
+ .home-hub-mate-avatar {
7
+ image-rendering: -webkit-optimize-contrast;
8
+ image-rendering: smooth;
9
+ -webkit-backface-visibility: hidden;
10
+ backface-visibility: hidden;
11
+ }
12
+
1
13
  /* === Mate Chat Title Bar === */
2
14
 
3
15
  .title-bar-content.mate-dm-active {
@@ -151,20 +163,7 @@
151
163
  font-size: 48px;
152
164
  line-height: 1;
153
165
  }
154
- .mate-intro-experimental {
155
- display: inline-block;
156
- font-size: 10px;
157
- font-weight: 700;
158
- letter-spacing: 0.05em;
159
- text-transform: uppercase;
160
- color: #f59e0b;
161
- background: rgba(245, 158, 11, 0.12);
162
- border: 1px solid rgba(245, 158, 11, 0.25);
163
- border-radius: 10px;
164
- padding: 2px 7px;
165
- vertical-align: middle;
166
- margin-right: 4px;
167
- }
166
+
168
167
  .mate-intro-title {
169
168
  font-size: 22px;
170
169
  font-weight: 700;
@@ -241,6 +241,134 @@
241
241
  box-shadow: 0 0 0 1px var(--accent);
242
242
  }
243
243
 
244
+ /* Avatar upload button */
245
+ .profile-avatar-upload .profile-avatar-upload-icon {
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ width: 100%;
250
+ height: 100%;
251
+ color: var(--text-muted);
252
+ }
253
+ .profile-avatar-upload .profile-avatar-upload-icon svg {
254
+ width: 24px;
255
+ height: 24px;
256
+ }
257
+ .profile-avatar-upload:hover .profile-avatar-upload-icon {
258
+ color: var(--text);
259
+ }
260
+ .profile-avatar-custom-preview {
261
+ width: 100%;
262
+ height: 100%;
263
+ object-fit: cover;
264
+ border-radius: 6px;
265
+ }
266
+
267
+ /* Avatar positioner overlay */
268
+ .avatar-positioner-overlay {
269
+ position: fixed;
270
+ inset: 0;
271
+ z-index: 10000;
272
+ background: rgba(0,0,0,0.6);
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ }
277
+ .avatar-positioner-container {
278
+ background: var(--bg-secondary, #1e1e2e);
279
+ border-radius: 16px;
280
+ padding: 20px;
281
+ display: flex;
282
+ flex-direction: column;
283
+ align-items: center;
284
+ gap: 16px;
285
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
286
+ min-width: 260px;
287
+ position: relative;
288
+ }
289
+ .avatar-positioner-close {
290
+ position: absolute;
291
+ top: 8px;
292
+ right: 8px;
293
+ width: 28px;
294
+ height: 28px;
295
+ border: none;
296
+ background: transparent;
297
+ color: var(--text-muted);
298
+ font-size: 20px;
299
+ cursor: pointer;
300
+ border-radius: 50%;
301
+ display: flex;
302
+ align-items: center;
303
+ justify-content: center;
304
+ line-height: 1;
305
+ }
306
+ .avatar-positioner-close:hover {
307
+ background: var(--bg-hover, rgba(255,255,255,0.1));
308
+ color: var(--text);
309
+ }
310
+ .avatar-positioner-title {
311
+ font-size: 14px;
312
+ font-weight: 600;
313
+ color: var(--text);
314
+ }
315
+ .avatar-positioner-viewport {
316
+ border-radius: 50%;
317
+ overflow: hidden;
318
+ position: relative;
319
+ cursor: grab;
320
+ border: 2px solid var(--border);
321
+ background: var(--bg);
322
+ touch-action: none;
323
+ }
324
+ .avatar-positioner-viewport:active {
325
+ cursor: grabbing;
326
+ }
327
+ .avatar-positioner-img {
328
+ position: absolute;
329
+ top: 0;
330
+ left: 0;
331
+ pointer-events: none;
332
+ will-change: transform;
333
+ }
334
+ .avatar-positioner-slider-wrap {
335
+ width: 100%;
336
+ padding: 0 8px;
337
+ }
338
+ .avatar-positioner-slider {
339
+ width: 100%;
340
+ accent-color: var(--accent);
341
+ }
342
+ .avatar-positioner-buttons {
343
+ display: flex;
344
+ gap: 8px;
345
+ width: 100%;
346
+ }
347
+ .avatar-positioner-btn {
348
+ flex: 1;
349
+ padding: 8px 0;
350
+ border-radius: 8px;
351
+ border: none;
352
+ cursor: pointer;
353
+ font-size: 13px;
354
+ font-weight: 600;
355
+ }
356
+ .avatar-positioner-btn-cancel {
357
+ background: var(--bg);
358
+ color: var(--text-muted);
359
+ border: 1px solid var(--border);
360
+ }
361
+ .avatar-positioner-btn-cancel:hover {
362
+ background: var(--bg-hover, var(--bg));
363
+ }
364
+ .avatar-positioner-btn-done {
365
+ background: var(--accent);
366
+ color: #fff;
367
+ }
368
+ .avatar-positioner-btn-done:hover {
369
+ filter: brightness(1.1);
370
+ }
371
+
244
372
  /* Color swatch grid */
245
373
  .profile-color-grid {
246
374
  display: flex;
@@ -1416,7 +1416,7 @@
1416
1416
  <div class="mate-intro-step"><span class="mate-intro-step-num">2</span> Have a short interview where your Mate gets to know you</div>
1417
1417
  <div class="mate-intro-step"><span class="mate-intro-step-num">3</span> Start talking</div>
1418
1418
  </div>
1419
- <p class="mate-intro-privacy">Mates run on Claude Code. No separate API keys or external services needed. Your conversations stay on your Clay server and are never stored elsewhere. <span class="mate-intro-experimental">Experimental</span></p>
1419
+ <p class="mate-intro-privacy">Mates run on Claude Code. No separate API keys or external services needed. Your conversations stay on your Clay server and are never stored elsewhere.</p>
1420
1420
  </div>
1421
1421
  </div>
1422
1422
  <!-- Step 1: Relationship -->
@@ -0,0 +1,36 @@
1
+ // Centralized avatar URL builder and style definitions
2
+ // All DiceBear avatar URLs should be constructed through this module.
3
+
4
+ export var AVATAR_STYLES = [
5
+ { id: 'thumbs', name: 'Thumbs' },
6
+ { id: 'bottts', name: 'Bots' },
7
+ { id: 'pixel-art', name: 'Pixel' },
8
+ { id: 'adventurer', name: 'Adventurer' },
9
+ { id: 'micah', name: 'Micah' },
10
+ { id: 'fun-emoji', name: 'Emoji' },
11
+ { id: 'icons', name: 'Icons' },
12
+ ];
13
+
14
+ // Build a DiceBear avatar URL from style, seed, and optional size.
15
+ export function avatarUrl(style, seed, size) {
16
+ var s = encodeURIComponent(seed || 'anonymous');
17
+ return 'https://api.dicebear.com/9.x/' + (style || 'thumbs') + '/svg?seed=' + s + '&size=' + (size || 64);
18
+ }
19
+
20
+ // Build avatar URL for a user object, preferring custom avatar if set.
21
+ export function userAvatarUrl(user, size) {
22
+ if (user && user.avatarCustom) return user.avatarCustom;
23
+ var style = (user && user.avatarStyle) || 'thumbs';
24
+ var seed = (user && (user.avatarSeed || user.username || user.id)) || 'anonymous';
25
+ return avatarUrl(style, seed, size);
26
+ }
27
+
28
+ // Build avatar URL for a mate object, preferring custom avatar if set.
29
+ export function mateAvatarUrl(mate, size) {
30
+ if (!mate) return avatarUrl('bottts', 'mate', size);
31
+ var p = mate.profile || mate;
32
+ if (p.avatarCustom || mate.avatarCustom) return p.avatarCustom || mate.avatarCustom;
33
+ var style = p.avatarStyle || mate.avatarStyle || 'bottts';
34
+ var seed = p.avatarSeed || mate.avatarSeed || mate.id || 'mate';
35
+ return avatarUrl(style, seed, size);
36
+ }
@@ -1,3 +1,4 @@
1
+ import { avatarUrl, userAvatarUrl, mateAvatarUrl } from './avatar.js';
1
2
  import { escapeHtml } from './utils.js';
2
3
  import { refreshIcons } from './icons.js';
3
4
  import { openSearch as openSessionSearch } from './session-search.js';
@@ -274,9 +275,7 @@ function renderHome(filter) {
274
275
  items.push({ type: "user", data: user });
275
276
  var userName = escapeHtml(user.displayName || user.username);
276
277
  var userSub = user.username ? "@" + escapeHtml(user.username) : "";
277
- var uAvatarStyle = user.avatarStyle || "thumbs";
278
- var uAvatarSeed = user.avatarSeed || user.username || user.id;
279
- var uAvatarUrl = "https://api.dicebear.com/9.x/" + uAvatarStyle + "/svg?seed=" + encodeURIComponent(uAvatarSeed) + "&size=28";
278
+ var uAvatarUrl = userAvatarUrl(user, 28);
280
279
  var uAvatarHtml = '<img src="' + uAvatarUrl + '" width="28" height="28" style="border-radius:50%;" alt="">';
281
280
  html += renderItem(flatIndex, uAvatarHtml, userName, userSub, null);
282
281
  flatIndex++;
@@ -298,10 +297,8 @@ function renderHome(filter) {
298
297
  var mp = mate.profile || {};
299
298
  items.push({ type: "mate", data: mate });
300
299
  var mateName = escapeHtml(mp.displayName || mate.name || "Mate");
301
- var avatarStyle = mp.avatarStyle || "bottts";
302
- var avatarSeed = mp.avatarSeed || mate.id;
303
- var avatarUrl = "https://api.dicebear.com/9.x/" + avatarStyle + "/svg?seed=" + encodeURIComponent(avatarSeed) + "&size=28";
304
- var avatarHtml = '<img src="' + avatarUrl + '" width="28" height="28" style="border-radius:50%;" alt="">';
300
+ var mateAvUrl = mateAvatarUrl(mate, 28);
301
+ var avatarHtml = '<img src="' + mateAvUrl + '" width="28" height="28" style="border-radius:50%;" alt="">';
305
302
  html += renderItem(flatIndex, avatarHtml, mateName, null, null);
306
303
  flatIndex++;
307
304
  }
@@ -1,3 +1,4 @@
1
+ import { avatarUrl, mateAvatarUrl } from './avatar.js';
1
2
  import { escapeHtml } from './utils.js';
2
3
  import { iconHtml, refreshIcons } from './icons.js';
3
4
  import { hideKnowledge } from './mate-knowledge.js';
@@ -134,19 +135,17 @@ export function showMateSidebar(mateId, mateData) {
134
135
  // Populate header
135
136
  var profile = mateData.profile || mateData || {};
136
137
  var displayName = profile.displayName || mateData.displayName || mateData.name || "Mate";
137
- var avatarStyle = profile.avatarStyle || "bottts";
138
- var avatarSeed = profile.avatarSeed || mateId;
139
138
 
140
139
  var mateColor = profile.avatarColor || mateData.avatarColor || "#7c3aed";
141
140
 
142
- var avatarUrl = "https://api.dicebear.com/9.x/" + encodeURIComponent(avatarStyle) + "/svg?seed=" + encodeURIComponent(avatarSeed) + "&size=32";
143
- if (avatarEl) avatarEl.src = avatarUrl;
141
+ var mateAvUrl = mateAvatarUrl(mateData, 32);
142
+ if (avatarEl) avatarEl.src = mateAvUrl;
144
143
  if (nameEl) nameEl.textContent = displayName;
145
144
 
146
145
  // Also populate collapsed header info
147
146
  var collapsedAvatar = document.getElementById("mate-collapsed-avatar");
148
147
  var collapsedName = document.getElementById("mate-collapsed-name");
149
- if (collapsedAvatar) collapsedAvatar.src = avatarUrl;
148
+ if (collapsedAvatar) collapsedAvatar.src = mateAvUrl;
150
149
  if (collapsedName) collapsedName.textContent = displayName;
151
150
 
152
151
  // Apply mate color to sidebar
@@ -215,12 +214,10 @@ export function updateMateSidebarProfile(mateData) {
215
214
  if (!columnEl || !mateData) return;
216
215
  var profile = mateData.profile || mateData || {};
217
216
  var displayName = profile.displayName || mateData.displayName || mateData.name || "Mate";
218
- var avatarStyle = profile.avatarStyle || "bottts";
219
- var avatarSeed = profile.avatarSeed || (mateData.id || "mate");
220
217
  var mateColor = profile.avatarColor || mateData.avatarColor || "#7c3aed";
221
218
 
222
219
  if (avatarEl) {
223
- avatarEl.src = "https://api.dicebear.com/9.x/" + encodeURIComponent(avatarStyle) + "/svg?seed=" + encodeURIComponent(avatarSeed) + "&size=32";
220
+ avatarEl.src = mateAvatarUrl(mateData, 32);
224
221
  }
225
222
  // Check if name changed for engrave effect
226
223
  var oldName = nameEl ? nameEl.textContent : "";