clay-server 3.8.0-beta.1 → 3.8.0-beta.3
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/builtin-mates.js +14 -14
- package/lib/mates.js +2 -6
- package/lib/project-mate-interaction.js +7 -7
- package/lib/project-session-pair.js +84 -4
- package/lib/project-sessions.js +1 -1
- package/lib/project-user-mention.js +4 -4
- package/lib/project-worker-proposal.js +1 -1
- package/lib/project.js +4 -3
- package/lib/public/css/avatar-imprints.css +46 -0
- package/lib/public/css/icon-strip.css +3 -3
- package/lib/public/css/pane.css +1 -7
- package/lib/public/css/profile.css +123 -95
- package/lib/public/css/session-actions.css +40 -9
- package/lib/public/css/user-settings.css +1 -1
- package/lib/public/index.html +2 -2
- package/lib/public/modules/app-cursors.js +2 -2
- package/lib/public/modules/app-dm.js +1 -8
- package/lib/public/modules/app-messages.js +2 -2
- package/lib/public/modules/app-projects.js +7 -6
- package/lib/public/modules/app-rate-limit.js +0 -1
- package/lib/public/modules/app-rendering.js +0 -3
- package/lib/public/modules/avatar-imprint.js +134 -0
- package/lib/public/modules/avatar.js +25 -19
- package/lib/public/modules/debate.js +10 -5
- package/lib/public/modules/mate-sidebar.js +2 -2
- package/lib/public/modules/mention.js +26 -16
- package/lib/public/modules/profile.js +73 -190
- package/lib/public/modules/session-actions.js +5 -3
- package/lib/public/modules/sidebar-mates.js +5 -4
- package/lib/public/modules/sidebar-sessions.js +1 -1
- package/lib/public/modules/split-pair-ui.js +2 -8
- package/lib/public/style.css +1 -0
- package/lib/server-auth.js +2 -2
- package/lib/server-dm.js +8 -8
- package/lib/server-mates.js +3 -3
- package/lib/server-settings.js +2 -2
- package/lib/server.js +5 -4
- package/lib/session-pair-mcp-server.js +16 -1
- package/lib/users.js +4 -4
- package/package.json +1 -1
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
to { opacity: 1; transform: translateY(0); }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/*
|
|
21
|
+
/* Identity header */
|
|
22
22
|
.profile-banner {
|
|
23
23
|
height: 52px;
|
|
24
24
|
position: relative;
|
|
25
25
|
transition: background 0.2s ease;
|
|
26
26
|
border-radius: 14px 14px 0 0;
|
|
27
27
|
z-index: 1;
|
|
28
|
+
background: var(--bg);
|
|
29
|
+
border-top: 3px solid var(--profile-accent, #5857fc);
|
|
30
|
+
border-bottom: 1px solid var(--border);
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
.profile-close-btn {
|
|
@@ -34,8 +37,8 @@
|
|
|
34
37
|
width: 24px;
|
|
35
38
|
height: 24px;
|
|
36
39
|
border: none;
|
|
37
|
-
background:
|
|
38
|
-
color:
|
|
40
|
+
background: var(--bg-alt);
|
|
41
|
+
color: var(--text-muted);
|
|
39
42
|
border-radius: 50%;
|
|
40
43
|
font-size: 16px;
|
|
41
44
|
line-height: 1;
|
|
@@ -47,7 +50,8 @@
|
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
.profile-close-btn:hover {
|
|
50
|
-
background:
|
|
53
|
+
background: var(--bg-hover, var(--bg-alt));
|
|
54
|
+
color: var(--text);
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
/* Avatar row (overlaps banner) */
|
|
@@ -65,7 +69,7 @@
|
|
|
65
69
|
.profile-popover-avatar {
|
|
66
70
|
width: 60px;
|
|
67
71
|
height: 60px;
|
|
68
|
-
border-radius:
|
|
72
|
+
border-radius: 17px;
|
|
69
73
|
display: flex;
|
|
70
74
|
align-items: center;
|
|
71
75
|
justify-content: center;
|
|
@@ -79,30 +83,7 @@
|
|
|
79
83
|
width: 100%;
|
|
80
84
|
height: 100%;
|
|
81
85
|
object-fit: cover;
|
|
82
|
-
border-radius:
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/* Shuffle button next to Avatar label */
|
|
86
|
-
.profile-shuffle-btn {
|
|
87
|
-
background: none;
|
|
88
|
-
border: none;
|
|
89
|
-
padding: 0;
|
|
90
|
-
margin-left: 4px;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
color: var(--text-muted);
|
|
93
|
-
vertical-align: middle;
|
|
94
|
-
transition: color 0.15s;
|
|
95
|
-
display: inline-flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.profile-shuffle-btn .lucide {
|
|
100
|
-
width: 12px;
|
|
101
|
-
height: 12px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.profile-shuffle-btn:hover {
|
|
105
|
-
color: var(--text);
|
|
86
|
+
border-radius: 14px;
|
|
106
87
|
}
|
|
107
88
|
|
|
108
89
|
.profile-name-display {
|
|
@@ -202,66 +183,140 @@
|
|
|
202
183
|
border-color: var(--accent);
|
|
203
184
|
}
|
|
204
185
|
|
|
205
|
-
/*
|
|
206
|
-
.profile-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
186
|
+
/* Clay Imprint identity control */
|
|
187
|
+
.profile-imprint-panel {
|
|
188
|
+
border: 1px solid var(--border);
|
|
189
|
+
border-radius: 12px;
|
|
190
|
+
background: var(--bg);
|
|
191
|
+
padding: 9px;
|
|
210
192
|
}
|
|
211
193
|
|
|
212
|
-
.profile-
|
|
213
|
-
width: 100%;
|
|
214
|
-
aspect-ratio: 1;
|
|
215
|
-
border-radius: 10px;
|
|
216
|
-
border: 2px solid var(--border);
|
|
217
|
-
background: var(--bg);
|
|
218
|
-
cursor: pointer;
|
|
219
|
-
padding: 3px;
|
|
220
|
-
overflow: hidden;
|
|
194
|
+
.profile-imprint-preview {
|
|
221
195
|
display: flex;
|
|
222
196
|
align-items: center;
|
|
223
|
-
|
|
224
|
-
|
|
197
|
+
gap: 10px;
|
|
198
|
+
min-height: 54px;
|
|
225
199
|
}
|
|
226
200
|
|
|
227
|
-
.profile-
|
|
228
|
-
width:
|
|
229
|
-
height:
|
|
230
|
-
|
|
231
|
-
|
|
201
|
+
.profile-imprint-preview img {
|
|
202
|
+
width: 50px;
|
|
203
|
+
height: 50px;
|
|
204
|
+
border-radius: 13px;
|
|
205
|
+
flex: 0 0 auto;
|
|
232
206
|
}
|
|
233
207
|
|
|
234
|
-
.profile-
|
|
235
|
-
|
|
236
|
-
|
|
208
|
+
.profile-imprint-preview span {
|
|
209
|
+
display: flex;
|
|
210
|
+
flex-direction: column;
|
|
211
|
+
gap: 2px;
|
|
212
|
+
min-width: 0;
|
|
237
213
|
}
|
|
238
214
|
|
|
239
|
-
.profile-
|
|
240
|
-
|
|
241
|
-
|
|
215
|
+
.profile-imprint-preview strong {
|
|
216
|
+
color: var(--text);
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
font-weight: 650;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.profile-imprint-preview small {
|
|
222
|
+
color: var(--text-dimmer);
|
|
223
|
+
font-size: 10px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.profile-imprint-actions {
|
|
227
|
+
display: grid;
|
|
228
|
+
grid-template-columns: 1fr 1fr;
|
|
229
|
+
gap: 5px;
|
|
230
|
+
margin-top: 8px;
|
|
242
231
|
}
|
|
243
232
|
|
|
244
|
-
|
|
245
|
-
|
|
233
|
+
.profile-imprint-actions button {
|
|
234
|
+
min-width: 0;
|
|
235
|
+
height: 28px;
|
|
236
|
+
padding: 0 8px;
|
|
237
|
+
border: 1px solid var(--border);
|
|
238
|
+
border-radius: 7px;
|
|
239
|
+
background: var(--bg-alt);
|
|
240
|
+
color: var(--text-muted);
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
font-size: 10px;
|
|
243
|
+
font-weight: 600;
|
|
244
|
+
white-space: nowrap;
|
|
246
245
|
display: flex;
|
|
247
246
|
align-items: center;
|
|
248
247
|
justify-content: center;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
color: var(--text-muted);
|
|
248
|
+
gap: 4px;
|
|
249
|
+
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
|
252
250
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
|
|
252
|
+
.profile-imprint-actions button:hover {
|
|
253
|
+
color: var(--text);
|
|
254
|
+
border-color: var(--text-muted);
|
|
256
255
|
}
|
|
257
|
-
|
|
256
|
+
|
|
257
|
+
.profile-imprint-actions button.is-active {
|
|
258
258
|
color: var(--text);
|
|
259
|
+
border-color: var(--accent);
|
|
260
|
+
box-shadow: inset 0 0 0 1px var(--accent);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.profile-imprint-actions button .lucide {
|
|
264
|
+
width: 11px;
|
|
265
|
+
height: 11px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.profile-avatar-upload {
|
|
269
|
+
grid-column: 1 / -1;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.profile-mate-mark-actions .profile-avatar-upload {
|
|
273
|
+
grid-column: auto;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.profile-avatar-upload.is-active::before {
|
|
277
|
+
content: "";
|
|
278
|
+
width: 5px;
|
|
279
|
+
height: 5px;
|
|
280
|
+
border-radius: 50%;
|
|
281
|
+
background: var(--accent);
|
|
259
282
|
}
|
|
283
|
+
|
|
260
284
|
.profile-avatar-custom-preview {
|
|
261
285
|
width: 100%;
|
|
262
286
|
height: 100%;
|
|
263
287
|
object-fit: cover;
|
|
264
|
-
border-radius:
|
|
288
|
+
border-radius: 12px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.profile-use-imprint.is-active::before {
|
|
292
|
+
content: "";
|
|
293
|
+
width: 5px;
|
|
294
|
+
height: 5px;
|
|
295
|
+
border-radius: 50%;
|
|
296
|
+
background: var(--accent);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.profile-imprint-actions button:focus-visible {
|
|
300
|
+
outline: 2px solid var(--accent);
|
|
301
|
+
outline-offset: 2px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.profile-imprint-actions button:active {
|
|
305
|
+
transform: translateY(1px);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.profile-imprint-actions button:disabled {
|
|
309
|
+
opacity: 0.45;
|
|
310
|
+
cursor: default;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.profile-imprint-actions button:disabled:hover {
|
|
314
|
+
border-color: var(--border);
|
|
315
|
+
color: var(--text-muted);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.profile-imprint-actions button.is-active:hover {
|
|
319
|
+
color: var(--text);
|
|
265
320
|
}
|
|
266
321
|
|
|
267
322
|
/* Avatar positioner overlay */
|
|
@@ -297,7 +352,7 @@
|
|
|
297
352
|
color: var(--text-muted);
|
|
298
353
|
font-size: 20px;
|
|
299
354
|
cursor: pointer;
|
|
300
|
-
border-radius:
|
|
355
|
+
border-radius: 28px;
|
|
301
356
|
display: flex;
|
|
302
357
|
align-items: center;
|
|
303
358
|
justify-content: center;
|
|
@@ -313,7 +368,7 @@
|
|
|
313
368
|
color: var(--text);
|
|
314
369
|
}
|
|
315
370
|
.avatar-positioner-viewport {
|
|
316
|
-
border-radius:
|
|
371
|
+
border-radius: 26%;
|
|
317
372
|
overflow: hidden;
|
|
318
373
|
position: relative;
|
|
319
374
|
cursor: grab;
|
|
@@ -368,30 +423,3 @@
|
|
|
368
423
|
.avatar-positioner-btn-done:hover {
|
|
369
424
|
filter: brightness(1.1);
|
|
370
425
|
}
|
|
371
|
-
|
|
372
|
-
/* Color swatch grid */
|
|
373
|
-
.profile-color-grid {
|
|
374
|
-
display: flex;
|
|
375
|
-
flex-wrap: wrap;
|
|
376
|
-
gap: 6px;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.profile-color-swatch {
|
|
380
|
-
width: 22px;
|
|
381
|
-
height: 22px;
|
|
382
|
-
border-radius: 50%;
|
|
383
|
-
border: 2px solid transparent;
|
|
384
|
-
cursor: pointer;
|
|
385
|
-
transition: transform 0.15s, border-color 0.15s;
|
|
386
|
-
padding: 0;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.profile-color-swatch:hover {
|
|
390
|
-
transform: scale(1.15);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.profile-color-swatch.profile-color-active {
|
|
394
|
-
border-color: #fff;
|
|
395
|
-
box-shadow: 0 0 0 2px var(--accent);
|
|
396
|
-
}
|
|
397
|
-
|
|
@@ -2,23 +2,29 @@
|
|
|
2
2
|
display: inline-flex;
|
|
3
3
|
align-items: center;
|
|
4
4
|
justify-content: center;
|
|
5
|
-
|
|
5
|
+
gap: 6px;
|
|
6
|
+
width: auto;
|
|
6
7
|
height: 28px;
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 0 9px;
|
|
10
|
+
border: 1px solid var(--border-subtle);
|
|
9
11
|
border-radius: 8px;
|
|
10
|
-
background:
|
|
11
|
-
color: var(--text-
|
|
12
|
+
background: color-mix(in srgb, var(--brand-indigo) 5%, var(--bg));
|
|
13
|
+
color: var(--text-secondary);
|
|
12
14
|
cursor: pointer;
|
|
13
15
|
flex-shrink: 0;
|
|
16
|
+
font: inherit;
|
|
17
|
+
font-size: 11px;
|
|
18
|
+
font-weight: 650;
|
|
19
|
+
white-space: nowrap;
|
|
14
20
|
transition: color 0.15s, background 0.15s, border-color 0.15s;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
#header-session-actions-btn:hover,
|
|
18
24
|
#header-session-actions-btn[aria-expanded="true"] {
|
|
19
25
|
color: var(--text);
|
|
20
|
-
background:
|
|
21
|
-
border-color: var(--border);
|
|
26
|
+
background: color-mix(in srgb, var(--brand-indigo) 9%, var(--bg));
|
|
27
|
+
border-color: color-mix(in srgb, var(--brand-indigo) 32%, var(--border));
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
#header-session-actions-btn:focus { outline: none; }
|
|
@@ -28,12 +34,26 @@
|
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
#header-session-actions-btn.hidden { display: none; }
|
|
31
|
-
#header-session-actions-btn .lucide {
|
|
37
|
+
#header-session-actions-btn .lucide:first-child {
|
|
38
|
+
width: 14px;
|
|
39
|
+
height: 14px;
|
|
40
|
+
color: color-mix(in srgb, var(--brand-indigo) 78%, var(--text));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#header-session-actions-btn .session-actions-trigger-chevron {
|
|
44
|
+
width: 13px;
|
|
45
|
+
height: 13px;
|
|
46
|
+
transition: transform 0.16s ease;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#header-session-actions-btn[aria-expanded="true"] .session-actions-trigger-chevron {
|
|
50
|
+
transform: rotate(180deg);
|
|
51
|
+
}
|
|
32
52
|
|
|
33
53
|
.session-actions-menu {
|
|
34
54
|
position: fixed;
|
|
35
55
|
z-index: 10020;
|
|
36
|
-
width:
|
|
56
|
+
width: min(300px, calc(100vw - 16px));
|
|
37
57
|
padding: 6px;
|
|
38
58
|
border: 1px solid var(--border);
|
|
39
59
|
border-radius: 12px;
|
|
@@ -63,6 +83,17 @@
|
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
.session-actions-row:hover { background: var(--bg-alt); }
|
|
86
|
+
.session-actions-row.is-primary {
|
|
87
|
+
background: color-mix(in srgb, var(--brand-indigo) 6%, transparent);
|
|
88
|
+
}
|
|
89
|
+
.session-actions-row.is-primary:hover {
|
|
90
|
+
background: color-mix(in srgb, var(--brand-indigo) 10%, transparent);
|
|
91
|
+
}
|
|
92
|
+
.session-actions-row.is-primary .session-actions-row-icon {
|
|
93
|
+
border-color: transparent;
|
|
94
|
+
background: var(--brand-indigo);
|
|
95
|
+
color: var(--brand-paper);
|
|
96
|
+
}
|
|
66
97
|
.session-actions-row:disabled { cursor: default; opacity: 0.42; }
|
|
67
98
|
.session-actions-row:disabled:hover { background: transparent; }
|
|
68
99
|
|
package/lib/public/index.html
CHANGED
|
@@ -388,8 +388,8 @@
|
|
|
388
388
|
<div id="ralph-sticky" class="hidden"></div>
|
|
389
389
|
<div id="debate-sticky" class="hidden"></div>
|
|
390
390
|
<div class="status">
|
|
391
|
-
<button id="header-session-actions-btn" type="button" aria-label="Session actions"
|
|
392
|
-
<i data-lucide="
|
|
391
|
+
<button id="header-session-actions-btn" type="button" aria-label="Session actions" aria-haspopup="menu" aria-expanded="false">
|
|
392
|
+
<i data-lucide="bot"></i><span>Add AI worker</span><i class="session-actions-trigger-chevron" data-lucide="chevron-down"></i>
|
|
393
393
|
</button>
|
|
394
394
|
<button type="button" id="header-tui-font-btn" class="header-tui-font-btn hidden" title="Terminal font" aria-label="Terminal font">
|
|
395
395
|
<i data-lucide="type"></i>
|
|
@@ -72,8 +72,8 @@ function createCursorElement(userId, displayName, color, avatarStyle, avatarSeed
|
|
|
72
72
|
// Avatar
|
|
73
73
|
var avatarImg = document.createElement("img");
|
|
74
74
|
avatarImg.className = "remote-cursor-avatar";
|
|
75
|
-
avatarImg.src = avatarCustom ? avatarCustom : avatarUrl(avatarStyle || "
|
|
76
|
-
avatarImg.style.cssText = "width:14px;height:14px;border-radius:
|
|
75
|
+
avatarImg.src = avatarCustom ? avatarCustom : avatarUrl(avatarStyle || "imprint", avatarSeed || userId, 16);
|
|
76
|
+
avatarImg.style.cssText = "width:14px;height:14px;border-radius:4px;background:#fff;flex-shrink:0;";
|
|
77
77
|
tag.appendChild(avatarImg);
|
|
78
78
|
|
|
79
79
|
// Name label
|
|
@@ -215,7 +215,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
215
215
|
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
216
216
|
if (termBtn) termBtn.style.display = "none";
|
|
217
217
|
// Apply mate color to chat title bar and panels
|
|
218
|
-
var mateColor = (targetUser.profile && targetUser.profile.avatarColor) || targetUser.avatarColor || "#
|
|
218
|
+
var mateColor = (targetUser.profile && targetUser.profile.avatarColor) || targetUser.avatarColor || "#5857fc";
|
|
219
219
|
document.body.style.setProperty("--mate-color", mateColor);
|
|
220
220
|
document.body.style.setProperty("--mate-color-tint", mateColor + "0a");
|
|
221
221
|
document.body.classList.add("mate-dm-active");
|
|
@@ -223,19 +223,12 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
223
223
|
var mp = targetUser.profile || {};
|
|
224
224
|
var mateAvUrlDm = mateAvatarUrl(targetUser, 36);
|
|
225
225
|
var myUser = store.get('cachedAllUsers').find(function (u) { return u.id === store.get('myUserId'); });
|
|
226
|
-
if (!myUser) {
|
|
227
|
-
try { var cached = JSON.parse(localStorage.getItem("clay_my_user") || "null"); if (cached) myUser = cached; } catch(e) {}
|
|
228
|
-
}
|
|
229
226
|
var myAvatarUrl = userAvatarUrl(myUser || { id: store.get('myUserId') }, 36);
|
|
230
227
|
var myDisplayName = (myUser && myUser.displayName) || "";
|
|
231
228
|
document.body.dataset.mateAvatarUrl = mateAvUrlDm;
|
|
232
229
|
document.body.dataset.mateName = mp.displayName || targetUser.displayName || targetUser.name || "";
|
|
233
230
|
document.body.dataset.myAvatarUrl = myAvatarUrl;
|
|
234
231
|
document.body.dataset.myDisplayName = myDisplayName;
|
|
235
|
-
// Cache my info for restore after hard refresh
|
|
236
|
-
if (myUser) {
|
|
237
|
-
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) {}
|
|
238
|
-
}
|
|
239
232
|
var titleBarContent = document.querySelector(".title-bar-content");
|
|
240
233
|
if (titleBarContent) {
|
|
241
234
|
titleBarContent.style.background = mateColor;
|
|
@@ -95,7 +95,7 @@ export function processMessage(msg) {
|
|
|
95
95
|
if (headerTitleEl) headerTitleEl.textContent = _mdn;
|
|
96
96
|
var _tbpn = document.getElementById("title-bar-project-name");
|
|
97
97
|
if (_tbpn) _tbpn.textContent = _mdn;
|
|
98
|
-
var _mc2 = (store.get('dmTargetUser').profile && store.get('dmTargetUser').profile.avatarColor) || store.get('dmTargetUser').avatarColor || "#
|
|
98
|
+
var _mc2 = (store.get('dmTargetUser').profile && store.get('dmTargetUser').profile.avatarColor) || store.get('dmTargetUser').avatarColor || "#5857fc";
|
|
99
99
|
var _tbc2 = document.querySelector(".title-bar-content");
|
|
100
100
|
if (_tbc2) { _tbc2.style.background = _mc2; _tbc2.classList.add("mate-dm-active"); }
|
|
101
101
|
document.body.classList.add("mate-dm-active");
|
|
@@ -299,7 +299,7 @@ export function processMessage(msg) {
|
|
|
299
299
|
var tbProjectName = document.getElementById("title-bar-project-name");
|
|
300
300
|
if (tbProjectName) tbProjectName.textContent = _mateDN;
|
|
301
301
|
// Re-apply mate title bar styling (may be lost during project switch)
|
|
302
|
-
var _mc = (store.get('dmTargetUser').profile && store.get('dmTargetUser').profile.avatarColor) || store.get('dmTargetUser').avatarColor || "#
|
|
302
|
+
var _mc = (store.get('dmTargetUser').profile && store.get('dmTargetUser').profile.avatarColor) || store.get('dmTargetUser').avatarColor || "#5857fc";
|
|
303
303
|
var _tbc = document.querySelector(".title-bar-content");
|
|
304
304
|
if (_tbc) { _tbc.style.background = _mc; _tbc.classList.add("mate-dm-active"); }
|
|
305
305
|
document.body.classList.add("mate-dm-active");
|
|
@@ -257,12 +257,13 @@ export function updateProjectList(msg) {
|
|
|
257
257
|
if (msg.dmConversations) store.set({ cachedDmConversations: msg.dmConversations });
|
|
258
258
|
// renderUserStrip is handled by the store subscriber
|
|
259
259
|
var st2 = store.snap();
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
var refreshedMyUser = st2.cachedAllUsers.find(function (u) { return u.id === st2.myUserId; });
|
|
261
|
+
if (refreshedMyUser) {
|
|
262
|
+
document.body.dataset.myDisplayName = refreshedMyUser.displayName || refreshedMyUser.username || "";
|
|
263
|
+
document.body.dataset.myAvatarUrl = userAvatarUrl(refreshedMyUser, 36);
|
|
264
|
+
var myMessageAvatars = document.querySelectorAll('.dm-bubble-avatar-me');
|
|
265
|
+
for (var avatarIndex = 0; avatarIndex < myMessageAvatars.length; avatarIndex++) {
|
|
266
|
+
myMessageAvatars[avatarIndex].src = document.body.dataset.myAvatarUrl;
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
269
|
// Render my avatar (always present, hidden behind user-island)
|
|
@@ -311,7 +311,6 @@ export function addScheduledMessageBubble(text, resetsAt) {
|
|
|
311
311
|
if (isChannel) {
|
|
312
312
|
// Channel mode: avatar + header with scheduled badge + message
|
|
313
313
|
var _me = store.get('cachedAllUsers').find(function (u) { return u.id === store.get('myUserId'); });
|
|
314
|
-
if (!_me) { try { _me = JSON.parse(localStorage.getItem("clay_my_user") || "null"); } catch(e) {} }
|
|
315
314
|
var _myName = document.body.dataset.myDisplayName || (_me && (_me.displayName || _me.username)) || "Me";
|
|
316
315
|
|
|
317
316
|
var avi = document.createElement("img");
|
|
@@ -552,9 +552,6 @@ export function addUserMessage(text, images, pastes, fromUserId, fromUserName, d
|
|
|
552
552
|
_displayName = fromUserName || (_targetUser && (_targetUser.displayName || _targetUser.username)) || "User";
|
|
553
553
|
} else {
|
|
554
554
|
_targetUser = cachedAllUsers.find(function (u) { return u.id === myUserId; });
|
|
555
|
-
if (!_targetUser) {
|
|
556
|
-
try { _targetUser = JSON.parse(localStorage.getItem("clay_my_user") || "null"); } catch(e) {}
|
|
557
|
-
}
|
|
558
555
|
_displayName = document.body.dataset.myDisplayName || "";
|
|
559
556
|
if (!_displayName) {
|
|
560
557
|
_displayName = (_targetUser && (_targetUser.displayName || _targetUser.username)) || "Me";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Clay Imprints: deterministic SVG identities derived from a stable seed.
|
|
2
|
+
|
|
3
|
+
var PAPER = "#f1efe8";
|
|
4
|
+
var INK = "#1b1b1a";
|
|
5
|
+
var LIGHT_BASE = "#e7e5de";
|
|
6
|
+
var LIGHT_FIELD = "#333330";
|
|
7
|
+
var REGISTRATION_INDIGO = "#5857fc";
|
|
8
|
+
var BRAND_GREEN = "#07e5a3";
|
|
9
|
+
|
|
10
|
+
function hashString(value) {
|
|
11
|
+
var text = String(value || "anonymous");
|
|
12
|
+
var hash = 2166136261;
|
|
13
|
+
for (var index = 0; index < text.length; index++) {
|
|
14
|
+
hash ^= text.charCodeAt(index);
|
|
15
|
+
hash = Math.imul(hash, 16777619);
|
|
16
|
+
}
|
|
17
|
+
return hash >>> 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeRandom(seed) {
|
|
21
|
+
var state = seed >>> 0;
|
|
22
|
+
return function nextRandom() {
|
|
23
|
+
state += 0x6d2b79f5;
|
|
24
|
+
var value = state;
|
|
25
|
+
value = Math.imul(value ^ (value >>> 15), value | 1);
|
|
26
|
+
value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
|
|
27
|
+
return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function between(random, min, max) {
|
|
32
|
+
return min + random() * (max - min);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function number(value) {
|
|
36
|
+
return Math.round(value * 100) / 100;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function xmlText(value) {
|
|
40
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function legacyIdentity(style, seed) {
|
|
44
|
+
var sourceStyle = String(style || "imprint").toLowerCase();
|
|
45
|
+
var sourceSeed = String(seed || "anonymous");
|
|
46
|
+
if (sourceStyle === "imprint") return sourceSeed;
|
|
47
|
+
return "legacy:" + sourceStyle + ":" + sourceSeed;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function curvePath(y, bow, endOffset) {
|
|
51
|
+
return "M -28 " + number(y) +
|
|
52
|
+
" C 17 " + number(y + bow) +
|
|
53
|
+
", 71 " + number(y - bow) +
|
|
54
|
+
", 124 " + number(y + endOffset);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function cutPath(random) {
|
|
58
|
+
var start = between(random, 37, 52);
|
|
59
|
+
var first = between(random, 14, 84);
|
|
60
|
+
var second = between(random, 12, 86);
|
|
61
|
+
var end = between(random, 40, 64);
|
|
62
|
+
return "M -22 " + number(start) + " C 21 " + number(first) + ", 73 " + number(second) + ", 118 " + number(end);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function imprintSvg(options) {
|
|
66
|
+
var settings = options || {};
|
|
67
|
+
var size = Math.max(12, Math.min(256, Number(settings.size) || 64));
|
|
68
|
+
var identity = legacyIdentity(settings.style, settings.seed);
|
|
69
|
+
var hash = hashString(identity);
|
|
70
|
+
var random = makeRandom(hash);
|
|
71
|
+
var darkBase = random() > 0.52;
|
|
72
|
+
var base = darkBase ? INK : LIGHT_BASE;
|
|
73
|
+
var field = darkBase ? PAPER : LIGHT_FIELD;
|
|
74
|
+
var accent = REGISTRATION_INDIGO;
|
|
75
|
+
var rotation = Math.floor(random() * 4) * 90;
|
|
76
|
+
var bandWidth = between(random, 37, 56);
|
|
77
|
+
var secondaryWidth = between(random, 14, 24);
|
|
78
|
+
var cutWidth = between(random, 15, 25);
|
|
79
|
+
var firstY = between(random, 24, 42);
|
|
80
|
+
var secondY = between(random, 58, 78);
|
|
81
|
+
var firstBow = between(random, -34, 34);
|
|
82
|
+
var secondBow = between(random, -32, 32);
|
|
83
|
+
var firstPath = curvePath(firstY, firstBow, 6);
|
|
84
|
+
var secondPath = curvePath(secondY, -secondBow, -5);
|
|
85
|
+
var contours = "";
|
|
86
|
+
|
|
87
|
+
if (size >= 40) {
|
|
88
|
+
for (var contour = -1; contour <= 1; contour++) {
|
|
89
|
+
var offset = contour * 5.2;
|
|
90
|
+
contours += '<path d="' + curvePath(firstY + offset, firstBow, 0) + '" fill="none" stroke="' +
|
|
91
|
+
(darkBase ? PAPER : INK) + '" stroke-opacity="0.2" stroke-width="0.8"/>';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
var clipId = "imprint-" + hash.toString(16);
|
|
96
|
+
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
|
|
97
|
+
'" viewBox="0 0 96 96"><defs><clipPath id="' + clipId +
|
|
98
|
+
'"><rect width="96" height="96" rx="24"/></clipPath></defs><g clip-path="url(#' + clipId +
|
|
99
|
+
')"><rect width="96" height="96" fill="' + base + '"/><g transform="rotate(' + rotation +
|
|
100
|
+
' 48 48)"><path d="' + firstPath + '" fill="none" stroke="' + field + '" stroke-width="' +
|
|
101
|
+
number(bandWidth) + '" stroke-linecap="square"/><path d="' + secondPath + '" fill="none" stroke="' +
|
|
102
|
+
accent + '" stroke-width="' + number(secondaryWidth) + '" stroke-linecap="square"/><path d="' +
|
|
103
|
+
cutPath(random) + '" fill="none" stroke="' + base + '" stroke-width="' + number(cutWidth) +
|
|
104
|
+
'" stroke-linecap="square"/>' + contours + '</g></g><rect x="0.5" y="0.5" width="95" height="95" rx="23.5" fill="none" stroke="' +
|
|
105
|
+
(darkBase ? "#000000" : INK) + '" stroke-opacity="0.16"/></svg>';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function imprintDataUrl(options) {
|
|
109
|
+
return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(imprintSvg(options));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function mateMarkSvg(options) {
|
|
113
|
+
var settings = options || {};
|
|
114
|
+
var size = Math.max(12, Math.min(256, Number(settings.size) || 64));
|
|
115
|
+
var identity = String(settings.seed || "M").trim();
|
|
116
|
+
var initial = Array.from(identity || "M")[0].toUpperCase();
|
|
117
|
+
var palette = [
|
|
118
|
+
{ background: REGISTRATION_INDIGO, foreground: PAPER },
|
|
119
|
+
{ background: BRAND_GREEN, foreground: INK },
|
|
120
|
+
{ background: PAPER, foreground: REGISTRATION_INDIGO },
|
|
121
|
+
][initial.codePointAt(0) % 3];
|
|
122
|
+
var clipId = "mate-mark";
|
|
123
|
+
|
|
124
|
+
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
|
|
125
|
+
'" viewBox="0 0 96 96"><defs><clipPath id="' + clipId +
|
|
126
|
+
'"><rect width="96" height="96" rx="24"/></clipPath></defs><g clip-path="url(#' + clipId +
|
|
127
|
+
')"><rect width="96" height="96" fill="' + palette.background + '"/><text x="48" y="50" fill="' + palette.foreground +
|
|
128
|
+
'" font-family="Arial, Helvetica, sans-serif" font-size="43" font-weight="600" text-anchor="middle" dominant-baseline="middle">' +
|
|
129
|
+
xmlText(initial) + '</text></g><rect x="0.5" y="0.5" width="95" height="95" rx="23.5" fill="none" stroke="#000000" stroke-opacity="0.18"/></svg>';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function mateMarkDataUrl(options) {
|
|
133
|
+
return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(mateMarkSvg(options));
|
|
134
|
+
}
|