clay-server 2.17.0-beta.7 → 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 +4 -0
- package/lib/public/app.js +20 -23
- package/lib/public/css/mates.css +13 -14
- package/lib/public/css/profile.css +128 -0
- package/lib/public/index.html +1 -1
- package/lib/public/modules/avatar.js +36 -0
- package/lib/public/modules/command-palette.js +4 -7
- package/lib/public/modules/mate-sidebar.js +5 -8
- package/lib/public/modules/profile.js +350 -23
- package/lib/public/modules/sidebar.js +10 -9
- package/lib/server.js +211 -3
- package/package.json +1 -1
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
|
|
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 =
|
|
660
|
+
var myAvatarUrl = userAvatarUrl(myUser || { id: myUserId }, 36);
|
|
660
661
|
var myDisplayName = (myUser && myUser.displayName) || "";
|
|
661
|
-
document.body.dataset.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 =
|
|
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:
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
1274
|
+
var mateAvUrl = mateAvatarUrl(mate, 48);
|
|
1276
1275
|
var avatar = document.createElement("img");
|
|
1277
1276
|
avatar.className = "home-hub-mate-avatar";
|
|
1278
|
-
avatar.src =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 &&
|
|
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
|
-
|
|
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);
|
package/lib/public/css/mates.css
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/lib/public/index.html
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
302
|
-
var
|
|
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
|
|
143
|
-
if (avatarEl) avatarEl.src =
|
|
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 =
|
|
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 =
|
|
220
|
+
avatarEl.src = mateAvatarUrl(mateData, 32);
|
|
224
221
|
}
|
|
225
222
|
// Check if name changed for engrave effect
|
|
226
223
|
var oldName = nameEl ? nameEl.textContent : "";
|