clay-server 2.16.0 → 2.17.0-beta.10
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/README.md +20 -7
- package/bin/cli.js +158 -239
- package/lib/certs/fullchain.pem +47 -0
- package/lib/certs/privkey.pem +5 -0
- package/lib/daemon.js +20 -3
- package/lib/pages.js +22 -20
- package/lib/project.js +9 -5
- package/lib/public/app.js +45 -25
- package/lib/public/css/command-palette.css +1 -1
- package/lib/public/css/mates.css +13 -14
- package/lib/public/css/menus.css +19 -0
- package/lib/public/css/overlays.css +44 -18
- package/lib/public/css/profile.css +128 -0
- package/lib/public/css/title-bar.css +0 -4
- package/lib/public/index.html +9 -5
- 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 +351 -24
- package/lib/public/modules/qrcode.js +23 -3
- package/lib/public/modules/sidebar.js +26 -9
- package/lib/public/sw.js +4 -1
- package/lib/server.js +224 -3
- package/lib/sessions.js +4 -4
- package/package.json +1 -1
- package/lib/themes/clay-light.json +0 -10
- package/lib/themes/clay.json +0 -10
|
@@ -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
|
@@ -29,15 +29,18 @@
|
|
|
29
29
|
<div id="layout">
|
|
30
30
|
<!-- === Top Bar (full width, forms reverse-ㄱ with icon strip) === -->
|
|
31
31
|
<div id="top-bar">
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<
|
|
32
|
+
<div class="top-bar-left-pills">
|
|
33
|
+
<button id="pwa-install-pill" class="top-bar-install-btn hidden" title="Open as app"><i data-lucide="download"></i> Open as app</button>
|
|
34
|
+
<button id="share-pill" class="top-bar-share-btn" title="Share"><i data-lucide="qr-code"></i> Share</button>
|
|
35
|
+
<div id="update-pill-wrap" class="top-bar-update hidden">
|
|
36
|
+
<button id="update-pill" class="top-bar-update-btn"><i data-lucide="arrow-up-circle"></i> <span id="update-version"></span> is available. Update now</button>
|
|
36
37
|
<div id="update-popover" class="top-bar-popover">
|
|
37
38
|
<div class="popover-row"><button id="update-now" class="popover-action popover-action-primary"><i data-lucide="download"></i> Update now</button></div>
|
|
38
39
|
<div class="popover-row"><div class="popover-label">Or run manually:</div><div class="popover-cmd"><code id="update-manual-cmd">npx clay-server@latest</code><button class="popover-copy" title="Copy"><i data-lucide="copy"></i></button></div></div>
|
|
39
40
|
</div>
|
|
40
41
|
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<button id="cmd-palette-btn" class="cmd-palette-searchbar" title="Command palette"><i data-lucide="search"></i><span class="cmd-palette-searchbar-text">Search sessions, projects, and commands</span><kbd class="cmd-palette-searchbar-kbd"></kbd></button>
|
|
41
44
|
<div class="top-bar-actions">
|
|
42
45
|
<!-- Pill badges -->
|
|
43
46
|
<div id="skip-perms-pill" class="top-bar-pill pill-error hidden"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
|
|
@@ -721,6 +724,7 @@
|
|
|
721
724
|
<div id="qr-overlay-inner">
|
|
722
725
|
<div id="qr-canvas"></div>
|
|
723
726
|
<div id="qr-url"></div>
|
|
727
|
+
<button id="qr-share-btn" class="qr-share-btn hidden"><i data-lucide="share-2"></i> Share</button>
|
|
724
728
|
</div>
|
|
725
729
|
</div>
|
|
726
730
|
|
|
@@ -1414,7 +1418,7 @@
|
|
|
1414
1418
|
<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>
|
|
1415
1419
|
<div class="mate-intro-step"><span class="mate-intro-step-num">3</span> Start talking</div>
|
|
1416
1420
|
</div>
|
|
1417
|
-
<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
|
|
1421
|
+
<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>
|
|
1418
1422
|
</div>
|
|
1419
1423
|
</div>
|
|
1420
1424
|
<!-- 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 : "";
|