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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDgjCCAwigAwIBAgISBmtBLGUclrEfhwU9evzgyDCQMAoGCCqGSM49BAMDMDIx
|
|
3
|
+
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
|
|
4
|
+
ODAeFw0yNjAzMjMwOTQ0MDJaFw0yNjA2MjEwOTQ0MDFaMBoxGDAWBgNVBAMMDyou
|
|
5
|
+
ZC5jbGF5LnN0dWRpbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEhAD1htqRg8
|
|
6
|
+
n7evflBZ1X7UaCeqBGcvG/MNtlAKd1VVfVGFuanyUjksV9++R1EuKLhEPM3loL/3
|
|
7
|
+
Gz8+XEewGw6jggIUMIICEDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
|
|
8
|
+
BQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU8Anzwuqwtujb+h3o/CnbJu5Z
|
|
9
|
+
+W8wHwYDVR0jBBgwFoAUjw0TovYuftFQbDMYOF1ZjiNykcowMgYIKwYBBQUHAQEE
|
|
10
|
+
JjAkMCIGCCsGAQUFBzAChhZodHRwOi8vZTguaS5sZW5jci5vcmcvMBoGA1UdEQQT
|
|
11
|
+
MBGCDyouZC5jbGF5LnN0dWRpbzATBgNVHSAEDDAKMAgGBmeBDAECATAtBgNVHR8E
|
|
12
|
+
JjAkMCKgIKAehhxodHRwOi8vZTguYy5sZW5jci5vcmcvMTcuY3JsMIIBBQYKKwYB
|
|
13
|
+
BAHWeQIEAgSB9gSB8wDxAHcAFoMtq/CpJQ8P8DqlRf/Iv8gj0IdL9gQpJ/jnHzMT
|
|
14
|
+
9foAAAGdGkoIlgAABAMASDBGAiEAhJFJwEIag1Bzt0WtYgMzLdJn/k+Is2RukdDo
|
|
15
|
+
G5sXpyMCIQCQOX9nOoaVIXxF1KXiavbAY5QIyJRuvK7Fn6WeL58YQQB2AMs49xWJ
|
|
16
|
+
fIShRF9bwd37yW7ymlnNRwppBYWwyxTDFFjnAAABnRpKCJgAAAQDAEcwRQIhAI98
|
|
17
|
+
cmflulGQJMfD10jbstVwodGpzl5licg6FTxcYachAiBZV1cZnPfasTzcteXyjCuz
|
|
18
|
+
c1wayYAtch+0soAWvBKZRzAKBggqhkjOPQQDAwNoADBlAjAwJO4ti4AJJTtMxYsr
|
|
19
|
+
Jf5052oDrD2POtoiPksruQVVacsq0T/9VYVX+X2vElCrxFwCMQDcSToBRWGpv/G3
|
|
20
|
+
JBpbEAB1qhk1Z9lYPQKH6gRvtp35XJWY0PucGRWgQUrXuZcxGlA=
|
|
21
|
+
-----END CERTIFICATE-----
|
|
22
|
+
-----BEGIN CERTIFICATE-----
|
|
23
|
+
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
|
|
24
|
+
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
|
|
25
|
+
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
|
|
26
|
+
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
|
|
27
|
+
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
|
|
28
|
+
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
|
|
29
|
+
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
|
|
30
|
+
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
|
|
31
|
+
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
|
|
32
|
+
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
|
|
33
|
+
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
|
|
34
|
+
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
|
|
35
|
+
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
|
|
36
|
+
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
|
|
37
|
+
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
|
|
38
|
+
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
|
|
39
|
+
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
|
|
40
|
+
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
|
|
41
|
+
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
|
|
42
|
+
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
|
|
43
|
+
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
|
|
44
|
+
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
|
|
45
|
+
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
|
|
46
|
+
u1igv3OefnWjSQ==
|
|
47
|
+
-----END CERTIFICATE-----
|
package/lib/daemon.js
CHANGED
|
@@ -60,10 +60,26 @@ if (config.osUsers) {
|
|
|
60
60
|
// --- TLS ---
|
|
61
61
|
var tlsOptions = null;
|
|
62
62
|
if (config.tls) {
|
|
63
|
+
// 1. Check builtin cert (shipped with package)
|
|
64
|
+
var builtinKeyPath = path.join(__dirname, "certs", "privkey.pem");
|
|
65
|
+
var builtinCertPath = path.join(__dirname, "certs", "fullchain.pem");
|
|
66
|
+
|
|
67
|
+
// 2. User cert (mkcert, etc.)
|
|
63
68
|
var os = require("os");
|
|
64
69
|
var certDir = path.join(process.env.CLAY_HOME || process.env.CLAUDE_RELAY_HOME || path.join(os.homedir(), ".clay"), "certs");
|
|
65
|
-
var
|
|
66
|
-
var
|
|
70
|
+
var userKeyPath = path.join(certDir, "key.pem");
|
|
71
|
+
var userCertPath = path.join(certDir, "cert.pem");
|
|
72
|
+
|
|
73
|
+
var keyPath, certPath;
|
|
74
|
+
if (config.builtinCert !== false && fs.existsSync(builtinKeyPath) && fs.existsSync(builtinCertPath)) {
|
|
75
|
+
keyPath = builtinKeyPath;
|
|
76
|
+
certPath = builtinCertPath;
|
|
77
|
+
config.builtinCert = true;
|
|
78
|
+
} else {
|
|
79
|
+
keyPath = userKeyPath;
|
|
80
|
+
certPath = userCertPath;
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
try {
|
|
68
84
|
tlsOptions = {
|
|
69
85
|
key: fs.readFileSync(keyPath),
|
|
@@ -78,7 +94,7 @@ var caRoot = null;
|
|
|
78
94
|
try {
|
|
79
95
|
var { execSync } = require("child_process");
|
|
80
96
|
caRoot = path.join(
|
|
81
|
-
execSync("mkcert -CAROOT", { encoding: "utf8" }).trim(),
|
|
97
|
+
execSync("mkcert -CAROOT", { encoding: "utf8", stdio: "pipe" }).trim(),
|
|
82
98
|
"rootCA.pem"
|
|
83
99
|
);
|
|
84
100
|
if (!fs.existsSync(caRoot)) caRoot = null;
|
|
@@ -119,6 +135,7 @@ var listenHost = config.host || "0.0.0.0";
|
|
|
119
135
|
var relay = createServer({
|
|
120
136
|
tlsOptions: tlsOptions,
|
|
121
137
|
caPath: caRoot,
|
|
138
|
+
builtinCert: config.builtinCert || false,
|
|
122
139
|
pinHash: config.pinHash || null,
|
|
123
140
|
port: config.port,
|
|
124
141
|
debug: config.debug || false,
|
package/lib/pages.js
CHANGED
|
@@ -48,56 +48,58 @@ function setupPageHtml(httpsUrl, httpUrl, hasCert, lanMode) {
|
|
|
48
48
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
49
49
|
<title>Setup - Clay</title>
|
|
50
50
|
<style>
|
|
51
|
+
:root{--s-bg:#282a36;--s-text:#f8f8f2;--s-accent:#ffb86c;--s-muted:#6272a4;--s-border:#44475a;--s-dimmer:#6272a4;--s-success:#50fa7b;--s-accent-15:rgba(255,184,108,0.15);--s-success-10:rgba(80,250,123,0.1);--s-success-15:rgba(80,250,123,0.15);--s-accent-06:rgba(255,184,108,0.06);--s-muted-06:rgba(98,114,164,0.06);--s-muted-15:rgba(98,114,164,0.15)}
|
|
52
|
+
@media(prefers-color-scheme:light){:root{--s-bg:#FAFAFA;--s-text:#5C6166;--s-accent:#FA8D3E;--s-muted:#A0A6AC;--s-border:#D2D4D8;--s-dimmer:#8A9199;--s-success:#6CBF49;--s-accent-15:rgba(250,141,62,0.15);--s-success-10:rgba(108,191,73,0.1);--s-success-15:rgba(108,191,73,0.15);--s-accent-06:rgba(250,141,62,0.06);--s-muted-06:rgba(160,166,172,0.06);--s-muted-15:rgba(160,166,172,0.15)}}
|
|
51
53
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
52
|
-
body{background
|
|
54
|
+
body{background:var(--s-bg);color:var(--s-text);font-family:system-ui,-apple-system,sans-serif;min-height:100dvh;display:flex;justify-content:center;padding:env(safe-area-inset-top,0) 20px 40px}
|
|
53
55
|
.c{max-width:480px;width:100%;padding-top:40px}
|
|
54
|
-
h1{color
|
|
55
|
-
.subtitle{text-align:center;color
|
|
56
|
+
h1{color:var(--s-accent);font-size:22px;margin:0 0 4px;text-align:center}
|
|
57
|
+
.subtitle{text-align:center;color:var(--s-muted);font-size:13px;margin-bottom:28px}
|
|
56
58
|
|
|
57
59
|
/* Steps indicator */
|
|
58
60
|
.steps-bar{display:flex;gap:6px;margin-bottom:32px}
|
|
59
|
-
.steps-bar .pip{flex:1;height:3px;border-radius:2px;background
|
|
60
|
-
.steps-bar .pip.done{background
|
|
61
|
-
.steps-bar .pip.active{background
|
|
61
|
+
.steps-bar .pip{flex:1;height:3px;border-radius:2px;background:var(--s-border);transition:background 0.3s}
|
|
62
|
+
.steps-bar .pip.done{background:var(--s-success)}
|
|
63
|
+
.steps-bar .pip.active{background:var(--s-accent)}
|
|
62
64
|
|
|
63
65
|
/* Step card */
|
|
64
66
|
.step-card{display:none;animation:fadeIn 0.25s ease}
|
|
65
67
|
.step-card.active{display:block}
|
|
66
68
|
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
|
|
67
69
|
|
|
68
|
-
.step-label{font-size:11px;text-transform:uppercase;letter-spacing:1px;color
|
|
70
|
+
.step-label{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:var(--s-accent);font-weight:600;margin-bottom:8px}
|
|
69
71
|
.step-title{font-size:18px;font-weight:600;margin-bottom:6px}
|
|
70
|
-
.step-desc{font-size:14px;line-height:1.6;color
|
|
72
|
+
.step-desc{font-size:14px;line-height:1.6;color:var(--s-muted);margin-bottom:20px}
|
|
71
73
|
|
|
72
74
|
.instruction{display:flex;gap:12px;margin-bottom:16px}
|
|
73
|
-
.inst-num{width:24px;height:24px;border-radius:50%;background:
|
|
75
|
+
.inst-num{width:24px;height:24px;border-radius:50%;background:var(--s-accent-15);color:var(--s-accent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;flex-shrink:0;margin-top:1px}
|
|
74
76
|
.inst-text{font-size:14px;line-height:1.6}
|
|
75
|
-
.inst-text .note{font-size:12px;color
|
|
77
|
+
.inst-text .note{font-size:12px;color:var(--s-dimmer);margin-top:4px}
|
|
76
78
|
|
|
77
|
-
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;background
|
|
79
|
+
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;background:var(--s-accent);color:#fff;text-decoration:none;padding:12px 24px;border-radius:12px;font-weight:600;font-size:14px;margin:4px 0;border:none;cursor:pointer;font-family:inherit;transition:opacity 0.15s}
|
|
78
80
|
.btn:hover{opacity:0.9}
|
|
79
|
-
.btn.outline{background:transparent;border:1.5px solid
|
|
80
|
-
.btn.outline:hover{border-color
|
|
81
|
-
.btn.success{background
|
|
81
|
+
.btn.outline{background:transparent;border:1.5px solid var(--s-border);color:var(--s-text)}
|
|
82
|
+
.btn.outline:hover{border-color:var(--s-dimmer)}
|
|
83
|
+
.btn.success{background:var(--s-success)}
|
|
82
84
|
.btn:disabled{opacity:0.4;cursor:default}
|
|
83
85
|
|
|
84
86
|
.btn-row{display:flex;gap:8px;margin-top:20px}
|
|
85
87
|
.btn-row .btn{flex:1}
|
|
86
88
|
|
|
87
89
|
.check-status{display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:10px;font-size:13px;margin:16px 0}
|
|
88
|
-
.check-status.ok{background:
|
|
89
|
-
.check-status.warn{background:
|
|
90
|
-
.check-status.pending{background:
|
|
90
|
+
.check-status.ok{background:var(--s-success-10);color:var(--s-success);border:1px solid var(--s-success-15)}
|
|
91
|
+
.check-status.warn{background:var(--s-accent-06);border:1px solid var(--s-accent-15);color:var(--s-accent)}
|
|
92
|
+
.check-status.pending{background:var(--s-muted-06);border:1px solid var(--s-muted-15);color:var(--s-muted)}
|
|
91
93
|
|
|
92
94
|
.platform-ios,.platform-android,.platform-desktop{display:none}
|
|
93
95
|
|
|
94
96
|
.done-card{text-align:center;padding:40px 0}
|
|
95
97
|
.done-icon{font-size:48px;margin-bottom:16px}
|
|
96
98
|
.done-title{font-size:20px;font-weight:600;margin-bottom:8px}
|
|
97
|
-
.done-desc{font-size:14px;color
|
|
99
|
+
.done-desc{font-size:14px;color:var(--s-muted);margin-bottom:24px}
|
|
98
100
|
|
|
99
|
-
.skip-link{display:block;text-align:center;color
|
|
100
|
-
.skip-link:hover{color
|
|
101
|
+
.skip-link{display:block;text-align:center;color:var(--s-dimmer);font-size:13px;text-decoration:none;margin-top:12px;cursor:pointer;border:none;background:none;font-family:inherit}
|
|
102
|
+
.skip-link:hover{color:var(--s-muted)}
|
|
101
103
|
</style></head><body>
|
|
102
104
|
<div class="c">
|
|
103
105
|
<h1>Clay</h1>
|
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;
|
|
@@ -1646,11 +1647,11 @@ function createProjectContext(opts) {
|
|
|
1646
1647
|
var switchTarget = sm.sessions.get(msg.id);
|
|
1647
1648
|
if (!usersModule.canAccessSession(ws._clayUser.id, switchTarget, { visibility: "public" })) return;
|
|
1648
1649
|
ws._clayActiveSession = msg.id;
|
|
1649
|
-
sm.switchSession(msg.id, ws);
|
|
1650
|
+
sm.switchSession(msg.id, ws, hydrateImageRefs);
|
|
1650
1651
|
broadcastPresence();
|
|
1651
1652
|
} else {
|
|
1652
1653
|
ws._clayActiveSession = msg.id;
|
|
1653
|
-
sm.switchSession(msg.id, ws);
|
|
1654
|
+
sm.switchSession(msg.id, ws, hydrateImageRefs);
|
|
1654
1655
|
}
|
|
1655
1656
|
}
|
|
1656
1657
|
return;
|
|
@@ -2005,7 +2006,7 @@ function createProjectContext(opts) {
|
|
|
2005
2006
|
onProcessingChanged();
|
|
2006
2007
|
|
|
2007
2008
|
sm.saveSessionFile(session);
|
|
2008
|
-
sm.switchSession(session.localId, ws);
|
|
2009
|
+
sm.switchSession(session.localId, ws, hydrateImageRefs);
|
|
2009
2010
|
sm.sendAndRecord(session, { type: "rewind_complete", mode: mode });
|
|
2010
2011
|
sm.broadcastSessionList();
|
|
2011
2012
|
} catch (err) {
|
|
@@ -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;
|
|
@@ -3162,7 +3164,7 @@ function createProjectContext(opts) {
|
|
|
3162
3164
|
judgeCraftSession.ralphCraftingMode = true;
|
|
3163
3165
|
judgeCraftSession.loop = { active: true, iteration: 0, role: "crafting", loopId: newLoopId, name: craftName, source: recordSource, startedAt: loopState.startedAt };
|
|
3164
3166
|
sm.saveSessionFile(judgeCraftSession);
|
|
3165
|
-
sm.switchSession(judgeCraftSession.localId);
|
|
3167
|
+
sm.switchSession(judgeCraftSession.localId, null, hydrateImageRefs);
|
|
3166
3168
|
loopState.craftingSessionId = judgeCraftSession.localId;
|
|
3167
3169
|
|
|
3168
3170
|
loopRegistry.updateRecord(newLoopId, { craftingSessionId: judgeCraftSession.localId });
|
|
@@ -3206,7 +3208,7 @@ function createProjectContext(opts) {
|
|
|
3206
3208
|
craftingSession.ralphCraftingMode = true;
|
|
3207
3209
|
craftingSession.loop = { active: true, iteration: 0, role: "crafting", loopId: newLoopId, name: craftName, source: recordSource, startedAt: loopState.startedAt };
|
|
3208
3210
|
sm.saveSessionFile(craftingSession);
|
|
3209
|
-
sm.switchSession(craftingSession.localId);
|
|
3211
|
+
sm.switchSession(craftingSession.localId, null, hydrateImageRefs);
|
|
3210
3212
|
loopState.craftingSessionId = craftingSession.localId;
|
|
3211
3213
|
|
|
3212
3214
|
// Store crafting session ID in the registry record
|
|
@@ -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';
|
|
@@ -7,7 +8,7 @@ import { initMateKnowledge, requestKnowledgeList, renderKnowledgeList, handleKno
|
|
|
7
8
|
import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid, addRewindButton } from './modules/rewind.js';
|
|
8
9
|
import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
|
|
9
10
|
import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands, sendMessage } from './modules/input.js';
|
|
10
|
-
import { initQrCode } from './modules/qrcode.js';
|
|
11
|
+
import { initQrCode, triggerShare } from './modules/qrcode.js';
|
|
11
12
|
import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer, resetFileBrowser } from './modules/filebrowser.js';
|
|
12
13
|
import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermExited, handleTermClosed, sendTerminalCommand } from './modules/terminal.js';
|
|
13
14
|
import { initStickyNotes, handleNotesList, handleNoteCreated, handleNoteUpdated, handleNoteDeleted, openArchive, closeArchive, isArchiveOpen, hideNotes, showNotes, isNotesVisible } from './modules/sticky-notes.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");
|
|
@@ -5169,6 +5168,8 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
5169
5168
|
|
|
5170
5169
|
// --- QR code ---
|
|
5171
5170
|
initQrCode();
|
|
5171
|
+
var sharePill = document.getElementById("share-pill");
|
|
5172
|
+
if (sharePill) sharePill.addEventListener("click", triggerShare);
|
|
5172
5173
|
|
|
5173
5174
|
// --- File browser ---
|
|
5174
5175
|
initFileBrowser({
|
|
@@ -5192,6 +5193,21 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
5192
5193
|
// --- Playbook Engine ---
|
|
5193
5194
|
initPlaybook();
|
|
5194
5195
|
|
|
5196
|
+
// Auto-open playbook from URL param (e.g. ?playbook=push-notifications)
|
|
5197
|
+
(function () {
|
|
5198
|
+
var params = new URLSearchParams(window.location.search);
|
|
5199
|
+
var pbId = params.get("playbook");
|
|
5200
|
+
if (pbId) {
|
|
5201
|
+
// Small delay to ensure DOM and playbook registry are ready
|
|
5202
|
+
setTimeout(function () { openPlaybook(pbId); }, 300);
|
|
5203
|
+
// Clean up URL
|
|
5204
|
+
params.delete("playbook");
|
|
5205
|
+
var clean = params.toString();
|
|
5206
|
+
var newUrl = window.location.pathname + (clean ? "?" + clean : "") + window.location.hash;
|
|
5207
|
+
window.history.replaceState(null, "", newUrl);
|
|
5208
|
+
}
|
|
5209
|
+
})();
|
|
5210
|
+
|
|
5195
5211
|
// --- In-session search (Cmd+F / Ctrl+F) ---
|
|
5196
5212
|
initSessionSearch({
|
|
5197
5213
|
messagesEl: messagesEl,
|
|
@@ -6751,7 +6767,13 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
6751
6767
|
modal.querySelector(".pwa-modal-backdrop").addEventListener("click", closeModal);
|
|
6752
6768
|
|
|
6753
6769
|
confirmBtn.addEventListener("click", function () {
|
|
6754
|
-
//
|
|
6770
|
+
// Builtin cert (*.d.clay.studio): open PWA setup guide
|
|
6771
|
+
if (location.hostname.endsWith(".d.clay.studio")) {
|
|
6772
|
+
closeModal();
|
|
6773
|
+
location.href = "/pwa";
|
|
6774
|
+
return;
|
|
6775
|
+
}
|
|
6776
|
+
// mkcert / other: redirect to onboarding setup page
|
|
6755
6777
|
var port = parseInt(location.port, 10);
|
|
6756
6778
|
var setupUrl;
|
|
6757
6779
|
if (!port) {
|
|
@@ -6834,7 +6856,7 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
6834
6856
|
return userColorMap[userId];
|
|
6835
6857
|
}
|
|
6836
6858
|
|
|
6837
|
-
function createCursorElement(userId, displayName, color, avatarStyle, avatarSeed) {
|
|
6859
|
+
function createCursorElement(userId, displayName, color, avatarStyle, avatarSeed, avatarCustom) {
|
|
6838
6860
|
var wrapper = document.createElement("div");
|
|
6839
6861
|
wrapper.className = "remote-cursor";
|
|
6840
6862
|
wrapper.dataset.userId = userId;
|
|
@@ -6870,9 +6892,7 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
6870
6892
|
// Avatar
|
|
6871
6893
|
var avatarImg = document.createElement("img");
|
|
6872
6894
|
avatarImg.className = "remote-cursor-avatar";
|
|
6873
|
-
|
|
6874
|
-
var seed = avatarSeed || userId;
|
|
6875
|
-
avatarImg.src = "https://api.dicebear.com/9.x/" + style + "/svg?seed=" + encodeURIComponent(seed) + "&size=16";
|
|
6895
|
+
avatarImg.src = avatarCustom ? avatarCustom : avatarUrl(avatarStyle || "thumbs", avatarSeed || userId, 16);
|
|
6876
6896
|
avatarImg.style.cssText = "width:14px;height:14px;border-radius:50%;background:#fff;flex-shrink:0;";
|
|
6877
6897
|
tag.appendChild(avatarImg);
|
|
6878
6898
|
|
|
@@ -7044,7 +7064,7 @@ import { initLongPress } from './modules/longpress.js';
|
|
|
7044
7064
|
var entry = remoteCursors[userId];
|
|
7045
7065
|
if (!entry) {
|
|
7046
7066
|
var color = getCursorColor(userId);
|
|
7047
|
-
var el = createCursorElement(userId, msg.displayName, color, msg.avatarStyle, msg.avatarSeed);
|
|
7067
|
+
var el = createCursorElement(userId, msg.displayName, color, msg.avatarStyle, msg.avatarSeed, msg.avatarCustom);
|
|
7048
7068
|
messagesEl.appendChild(el);
|
|
7049
7069
|
var indicator = createOffscreenIndicator(userId, msg.displayName, color);
|
|
7050
7070
|
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;
|
package/lib/public/css/menus.css
CHANGED
|
@@ -244,6 +244,25 @@
|
|
|
244
244
|
margin-top: 2px;
|
|
245
245
|
font-weight: 400;
|
|
246
246
|
}
|
|
247
|
+
.qr-share-btn {
|
|
248
|
+
display: inline-flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 6px;
|
|
251
|
+
margin-top: 12px;
|
|
252
|
+
padding: 8px 16px;
|
|
253
|
+
border: 1px solid #ddd;
|
|
254
|
+
border-radius: 8px;
|
|
255
|
+
background: #fff;
|
|
256
|
+
color: #333;
|
|
257
|
+
font-family: inherit;
|
|
258
|
+
font-size: 13px;
|
|
259
|
+
font-weight: 500;
|
|
260
|
+
cursor: pointer;
|
|
261
|
+
transition: background 0.15s;
|
|
262
|
+
}
|
|
263
|
+
.qr-share-btn:hover { background: #f5f5f5; }
|
|
264
|
+
.qr-share-btn .lucide { width: 14px; height: 14px; }
|
|
265
|
+
.qr-share-btn.hidden { display: none; }
|
|
247
266
|
|
|
248
267
|
/* --- Notification menu --- */
|
|
249
268
|
#notif-menu-wrap {
|
|
@@ -44,35 +44,61 @@ button.top-bar-pill.pill-success:hover { background: color-mix(in srgb, var(--su
|
|
|
44
44
|
button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
45
45
|
|
|
46
46
|
/* PWA install button — left side of top bar, icon only */
|
|
47
|
-
.top-bar-
|
|
47
|
+
.top-bar-left-pills {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: 6px;
|
|
48
51
|
position: absolute;
|
|
49
52
|
left: 10px;
|
|
50
|
-
top:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
top: 0;
|
|
54
|
+
bottom: 0;
|
|
55
|
+
}
|
|
56
|
+
.top-bar-install-btn {
|
|
57
|
+
display: inline-flex;
|
|
53
58
|
align-items: center;
|
|
54
|
-
|
|
55
|
-
background:
|
|
56
|
-
border: none;
|
|
57
|
-
border-radius: 6px;
|
|
59
|
+
gap: 4px;
|
|
60
|
+
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
58
61
|
color: var(--accent);
|
|
62
|
+
border: none;
|
|
63
|
+
border-radius: 10px;
|
|
64
|
+
padding: 2px 10px;
|
|
65
|
+
font-family: inherit;
|
|
66
|
+
font-size: 11px;
|
|
67
|
+
font-weight: 600;
|
|
59
68
|
cursor: pointer;
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
line-height: 1;
|
|
71
|
+
transition: background 0.15s;
|
|
62
72
|
}
|
|
63
|
-
.top-bar-install-btn .lucide { width:
|
|
64
|
-
.top-bar-install-btn:hover { background: color-mix(in srgb, var(--accent)
|
|
73
|
+
.top-bar-install-btn .lucide { width: 12px; height: 12px; }
|
|
74
|
+
.top-bar-install-btn:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
65
75
|
.top-bar-install-btn.hidden { display: none; }
|
|
66
76
|
.pwa-standalone .top-bar-install-btn { display: none !important; }
|
|
67
77
|
|
|
78
|
+
/* Share button — desktop only, same style as install pill */
|
|
79
|
+
.top-bar-share-btn {
|
|
80
|
+
display: inline-flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 4px;
|
|
83
|
+
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
84
|
+
color: var(--accent);
|
|
85
|
+
border: none;
|
|
86
|
+
border-radius: 10px;
|
|
87
|
+
padding: 2px 10px;
|
|
88
|
+
font-family: inherit;
|
|
89
|
+
font-size: 11px;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
white-space: nowrap;
|
|
93
|
+
line-height: 1;
|
|
94
|
+
transition: background 0.15s;
|
|
95
|
+
}
|
|
96
|
+
.top-bar-share-btn .lucide { width: 12px; height: 12px; }
|
|
97
|
+
.top-bar-share-btn:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
68
98
|
@media (max-width: 768px) {
|
|
69
|
-
.top-bar-
|
|
70
|
-
top: auto;
|
|
71
|
-
bottom: 0;
|
|
72
|
-
height: 32px;
|
|
73
|
-
transform: none;
|
|
74
|
-
}
|
|
99
|
+
.top-bar-share-btn { display: none; }
|
|
75
100
|
}
|
|
101
|
+
.pwa-standalone .top-bar-share-btn { display: none !important; }
|
|
76
102
|
|
|
77
103
|
/* PWA install modal */
|
|
78
104
|
.pwa-modal {
|