clay-server 2.8.2 → 2.9.1

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.
Files changed (38) hide show
  1. package/README.md +2 -0
  2. package/bin/cli.js +122 -2
  3. package/lib/config.js +20 -1
  4. package/lib/daemon.js +40 -0
  5. package/lib/pages.js +671 -27
  6. package/lib/project.js +280 -42
  7. package/lib/public/app.js +321 -84
  8. package/lib/public/css/admin.css +576 -0
  9. package/lib/public/css/base.css +3 -0
  10. package/lib/public/css/filebrowser.css +8 -1
  11. package/lib/public/css/home-hub.css +1 -1
  12. package/lib/public/css/icon-strip.css +1 -0
  13. package/lib/public/css/input.css +4 -0
  14. package/lib/public/css/menus.css +16 -51
  15. package/lib/public/css/messages.css +1 -0
  16. package/lib/public/css/mobile-nav.css +2 -2
  17. package/lib/public/css/overlays.css +177 -20
  18. package/lib/public/css/scheduler.css +1 -0
  19. package/lib/public/css/server-settings.css +37 -34
  20. package/lib/public/css/sidebar.css +49 -0
  21. package/lib/public/css/title-bar.css +45 -8
  22. package/lib/public/index.html +96 -25
  23. package/lib/public/manifest.json +2 -2
  24. package/lib/public/modules/admin.js +631 -0
  25. package/lib/public/modules/markdown.js +9 -5
  26. package/lib/public/modules/notifications.js +15 -103
  27. package/lib/public/modules/profile.js +21 -0
  28. package/lib/public/modules/project-settings.js +4 -1
  29. package/lib/public/modules/scheduler.js +55 -48
  30. package/lib/public/modules/server-settings.js +26 -4
  31. package/lib/public/modules/sidebar.js +111 -5
  32. package/lib/public/style.css +1 -0
  33. package/lib/push.js +6 -0
  34. package/lib/server.js +1075 -27
  35. package/lib/sessions.js +127 -41
  36. package/lib/smtp.js +221 -0
  37. package/lib/users.js +459 -0
  38. package/package.json +2 -1
@@ -0,0 +1,576 @@
1
+ /* --- Admin Panel (multi-user management, rendered inside server settings) --- */
2
+
3
+ @keyframes admin-in {
4
+ from { opacity: 0; transform: scale(0.96) translateY(8px); }
5
+ to { opacity: 1; transform: scale(1) translateY(0); }
6
+ }
7
+
8
+ .admin-loading,
9
+ .admin-empty,
10
+ .admin-error {
11
+ text-align: center;
12
+ color: var(--text-muted);
13
+ font-size: 14px;
14
+ padding: 32px 0;
15
+ }
16
+
17
+ .admin-error {
18
+ color: #e5534b;
19
+ }
20
+
21
+ /* Section header */
22
+ .admin-section-header {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ margin-bottom: 12px;
27
+ }
28
+
29
+ .admin-section-header h3 {
30
+ font-size: 14px;
31
+ font-weight: 600;
32
+ color: var(--text);
33
+ margin: 0;
34
+ }
35
+
36
+ .admin-action-btn {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 6px;
40
+ background: var(--accent);
41
+ color: #fff;
42
+ border: none;
43
+ padding: 6px 14px;
44
+ border-radius: 8px;
45
+ font-size: 13px;
46
+ font-weight: 600;
47
+ cursor: pointer;
48
+ font-family: inherit;
49
+ transition: opacity 0.15s;
50
+ }
51
+
52
+ .admin-action-btn:hover {
53
+ opacity: 0.9;
54
+ }
55
+
56
+ /* User list */
57
+ .admin-user-list {
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: 2px;
61
+ }
62
+
63
+ .admin-user-item {
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: space-between;
67
+ padding: 10px 12px;
68
+ border-radius: 10px;
69
+ transition: background 0.1s;
70
+ }
71
+
72
+ .admin-user-item:hover {
73
+ background: var(--accent-8);
74
+ }
75
+
76
+ .admin-user-info {
77
+ flex: 1;
78
+ min-width: 0;
79
+ }
80
+
81
+ .admin-user-name {
82
+ font-size: 14px;
83
+ color: var(--text);
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 8px;
87
+ }
88
+
89
+ .admin-user-meta {
90
+ font-size: 12px;
91
+ color: var(--text-muted);
92
+ margin-top: 2px;
93
+ }
94
+
95
+ .admin-badge {
96
+ font-size: 10px;
97
+ font-weight: 700;
98
+ text-transform: uppercase;
99
+ letter-spacing: 0.5px;
100
+ background: var(--accent-15);
101
+ color: var(--accent);
102
+ padding: 2px 6px;
103
+ border-radius: 4px;
104
+ }
105
+
106
+ .admin-you-badge {
107
+ font-size: 10px;
108
+ font-weight: 600;
109
+ color: var(--text-muted);
110
+ background: var(--border);
111
+ padding: 2px 6px;
112
+ border-radius: 4px;
113
+ }
114
+
115
+ .admin-remove-btn {
116
+ background: none;
117
+ border: none;
118
+ color: var(--text-dimmer);
119
+ cursor: pointer;
120
+ padding: 6px;
121
+ border-radius: 6px;
122
+ display: flex;
123
+ align-items: center;
124
+ transition: color 0.15s, background 0.15s;
125
+ }
126
+
127
+ .admin-remove-btn:hover {
128
+ color: #e5534b;
129
+ background: rgba(229, 83, 75, 0.1);
130
+ }
131
+
132
+ /* Invite list */
133
+ .admin-invite-list {
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: 2px;
137
+ }
138
+
139
+ .admin-invite-item {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ padding: 10px 12px;
144
+ border-radius: 10px;
145
+ }
146
+
147
+ .admin-invite-item:hover {
148
+ background: var(--accent-8);
149
+ }
150
+
151
+ .admin-invite-info {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 12px;
155
+ }
156
+
157
+ .admin-invite-code {
158
+ font-size: 13px;
159
+ background: var(--bg-alt);
160
+ padding: 4px 8px;
161
+ border-radius: 6px;
162
+ color: var(--text);
163
+ }
164
+
165
+ .admin-invite-expiry {
166
+ font-size: 12px;
167
+ color: var(--text-muted);
168
+ }
169
+
170
+ .admin-copy-link-btn {
171
+ background: none;
172
+ border: none;
173
+ color: var(--text-dimmer);
174
+ cursor: pointer;
175
+ padding: 6px;
176
+ border-radius: 6px;
177
+ display: flex;
178
+ align-items: center;
179
+ }
180
+
181
+ .admin-copy-link-btn:hover {
182
+ color: var(--accent);
183
+ background: var(--accent-8);
184
+ }
185
+
186
+ .admin-invite-actions {
187
+ display: flex;
188
+ align-items: center;
189
+ gap: 2px;
190
+ }
191
+
192
+ .admin-revoke-btn {
193
+ background: none;
194
+ border: none;
195
+ color: var(--text-dimmer);
196
+ cursor: pointer;
197
+ padding: 6px;
198
+ border-radius: 6px;
199
+ display: flex;
200
+ align-items: center;
201
+ }
202
+
203
+ .admin-revoke-btn:hover {
204
+ color: #ef4444;
205
+ background: rgba(239, 68, 68, 0.1);
206
+ }
207
+
208
+ /* Project list */
209
+ .admin-project-list {
210
+ display: flex;
211
+ flex-direction: column;
212
+ gap: 2px;
213
+ }
214
+
215
+ .admin-project-item {
216
+ display: flex;
217
+ align-items: center;
218
+ justify-content: space-between;
219
+ padding: 10px 12px;
220
+ border-radius: 10px;
221
+ }
222
+
223
+ .admin-project-item:hover {
224
+ background: var(--accent-8);
225
+ }
226
+
227
+ .admin-project-info {
228
+ flex: 1;
229
+ min-width: 0;
230
+ }
231
+
232
+ .admin-project-name {
233
+ font-size: 14px;
234
+ font-weight: 500;
235
+ color: var(--text);
236
+ }
237
+
238
+ .admin-project-slug {
239
+ font-size: 12px;
240
+ color: var(--text-muted);
241
+ margin-top: 1px;
242
+ }
243
+
244
+ .admin-project-controls {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 8px;
248
+ }
249
+
250
+ .admin-vis-select {
251
+ background: var(--bg-alt);
252
+ border: 1px solid var(--border);
253
+ color: var(--text);
254
+ font-size: 12px;
255
+ padding: 4px 8px;
256
+ border-radius: 6px;
257
+ cursor: pointer;
258
+ font-family: inherit;
259
+ }
260
+
261
+ .admin-vis-select:focus {
262
+ border-color: var(--accent);
263
+ outline: none;
264
+ }
265
+
266
+ .admin-manage-users-btn {
267
+ background: none;
268
+ border: none;
269
+ color: var(--text-dimmer);
270
+ cursor: pointer;
271
+ padding: 6px;
272
+ border-radius: 6px;
273
+ display: flex;
274
+ align-items: center;
275
+ }
276
+
277
+ .admin-manage-users-btn:hover {
278
+ color: var(--accent);
279
+ background: var(--accent-8);
280
+ }
281
+
282
+ /* --- Project Users Modal --- */
283
+ .admin-modal-overlay {
284
+ position: fixed;
285
+ top: 0;
286
+ left: 0;
287
+ width: 100%;
288
+ height: 100%;
289
+ background: rgba(0, 0, 0, 0.5);
290
+ z-index: 1100;
291
+ display: flex;
292
+ align-items: center;
293
+ justify-content: center;
294
+ }
295
+
296
+ .admin-modal {
297
+ background: var(--bg);
298
+ border: 1px solid var(--border);
299
+ border-radius: 14px;
300
+ width: 380px;
301
+ max-width: 90vw;
302
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
303
+ animation: admin-in 0.15s ease-out;
304
+ }
305
+
306
+ .admin-modal-header {
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: space-between;
310
+ padding: 16px 16px 0;
311
+ }
312
+
313
+ .admin-modal-header h3 {
314
+ font-size: 15px;
315
+ font-weight: 600;
316
+ color: var(--text);
317
+ margin: 0;
318
+ }
319
+
320
+ .admin-modal-close {
321
+ background: none;
322
+ border: none;
323
+ color: var(--text-muted);
324
+ cursor: pointer;
325
+ padding: 4px;
326
+ border-radius: 6px;
327
+ display: flex;
328
+ }
329
+
330
+ .admin-modal-close:hover {
331
+ color: var(--text);
332
+ }
333
+
334
+ .admin-modal-body {
335
+ padding: 16px;
336
+ }
337
+
338
+ .admin-modal-desc {
339
+ font-size: 13px;
340
+ color: var(--text-muted);
341
+ margin: 0 0 12px;
342
+ }
343
+
344
+ .admin-user-check {
345
+ display: flex;
346
+ align-items: center;
347
+ gap: 10px;
348
+ padding: 8px 4px;
349
+ font-size: 14px;
350
+ color: var(--text);
351
+ cursor: pointer;
352
+ }
353
+
354
+ .admin-user-check input[type="checkbox"] {
355
+ accent-color: var(--accent);
356
+ width: 16px;
357
+ height: 16px;
358
+ }
359
+
360
+ .admin-user-check small {
361
+ color: var(--text-muted);
362
+ font-size: 12px;
363
+ }
364
+
365
+ .admin-modal-footer {
366
+ display: flex;
367
+ justify-content: flex-end;
368
+ gap: 8px;
369
+ padding: 0 16px 16px;
370
+ }
371
+
372
+ .admin-modal-save {
373
+ background: var(--accent);
374
+ color: #fff;
375
+ border: none;
376
+ padding: 8px 18px;
377
+ border-radius: 8px;
378
+ font-size: 13px;
379
+ font-weight: 600;
380
+ cursor: pointer;
381
+ font-family: inherit;
382
+ }
383
+
384
+ .admin-modal-save:hover {
385
+ opacity: 0.9;
386
+ }
387
+
388
+ .admin-modal-confirm-danger {
389
+ background: #ef4444;
390
+ }
391
+
392
+ .admin-modal-cancel {
393
+ background: none;
394
+ border: 1px solid var(--border);
395
+ color: var(--text);
396
+ padding: 8px 18px;
397
+ border-radius: 8px;
398
+ font-size: 13px;
399
+ cursor: pointer;
400
+ font-family: inherit;
401
+ }
402
+
403
+ .admin-modal-cancel:hover {
404
+ border-color: var(--text-muted);
405
+ }
406
+
407
+ /* --- Admin header buttons row --- */
408
+ .admin-header-btns {
409
+ display: flex;
410
+ gap: 8px;
411
+ align-items: center;
412
+ }
413
+
414
+ .admin-action-btn-secondary {
415
+ background: var(--bg-alt);
416
+ color: var(--text);
417
+ border: 1px solid var(--border);
418
+ }
419
+
420
+ .admin-action-btn-secondary:hover {
421
+ border-color: var(--text-muted);
422
+ opacity: 1;
423
+ }
424
+
425
+ .admin-action-btn-danger {
426
+ background: none;
427
+ color: var(--text-muted);
428
+ border: 1px solid var(--border);
429
+ }
430
+
431
+ .admin-action-btn-danger:hover {
432
+ color: #e5534b;
433
+ border-color: #e5534b;
434
+ opacity: 1;
435
+ }
436
+
437
+ /* --- Invite email label --- */
438
+ .admin-invite-email {
439
+ font-size: 12px;
440
+ color: var(--accent);
441
+ }
442
+
443
+ /* --- SMTP Configuration Form --- */
444
+ .admin-smtp-form {
445
+ padding: 4px 0;
446
+ }
447
+
448
+ .admin-smtp-status {
449
+ display: flex;
450
+ align-items: center;
451
+ gap: 8px;
452
+ padding: 12px 16px;
453
+ border-radius: 8px;
454
+ font-size: 13px;
455
+ font-weight: 500;
456
+ margin-bottom: 16px;
457
+ }
458
+
459
+ .admin-smtp-status svg {
460
+ width: 16px;
461
+ height: 16px;
462
+ flex-shrink: 0;
463
+ }
464
+
465
+ .admin-smtp-status-ok {
466
+ background: rgba(46, 160, 67, 0.1);
467
+ color: #2ea043;
468
+ }
469
+
470
+ .admin-smtp-status-off {
471
+ background: var(--accent-8);
472
+ color: var(--text-muted);
473
+ }
474
+
475
+ .admin-smtp-fields {
476
+ display: flex;
477
+ flex-direction: column;
478
+ gap: 12px;
479
+ padding: 0 16px;
480
+ margin-bottom: 16px;
481
+ }
482
+
483
+ .admin-smtp-row label {
484
+ display: block;
485
+ font-size: 12px;
486
+ font-weight: 600;
487
+ color: var(--text-muted);
488
+ margin-bottom: 4px;
489
+ text-transform: uppercase;
490
+ letter-spacing: 0.3px;
491
+ }
492
+
493
+ .admin-smtp-row-half {
494
+ display: grid;
495
+ grid-template-columns: 1fr 1fr;
496
+ gap: 12px;
497
+ }
498
+
499
+ .admin-smtp-input {
500
+ width: 100%;
501
+ background: var(--bg);
502
+ border: 1px solid var(--border);
503
+ color: var(--text);
504
+ font-size: 13px;
505
+ padding: 8px 10px;
506
+ border-radius: 6px;
507
+ font-family: inherit;
508
+ outline: none;
509
+ box-sizing: border-box;
510
+ }
511
+
512
+ .admin-smtp-input:focus {
513
+ border-color: var(--accent);
514
+ }
515
+
516
+ .admin-smtp-toggle {
517
+ display: flex;
518
+ align-items: center;
519
+ gap: 8px;
520
+ font-size: 13px;
521
+ color: var(--text);
522
+ cursor: pointer;
523
+ padding-top: 4px;
524
+ }
525
+
526
+ .admin-smtp-toggle input[type="checkbox"] {
527
+ accent-color: var(--accent);
528
+ width: 16px;
529
+ height: 16px;
530
+ }
531
+
532
+ .admin-smtp-actions {
533
+ display: flex;
534
+ gap: 8px;
535
+ padding: 0 16px;
536
+ margin-bottom: 12px;
537
+ }
538
+
539
+ .admin-smtp-error {
540
+ font-size: 13px;
541
+ color: #e5534b;
542
+ padding: 0 16px;
543
+ min-height: 0;
544
+ overflow: hidden;
545
+ transition: all 0.15s;
546
+ }
547
+
548
+ .admin-smtp-error-visible {
549
+ margin-top: 8px;
550
+ min-height: 18px;
551
+ }
552
+
553
+ .admin-smtp-hint {
554
+ font-size: 12px;
555
+ color: var(--text-dimmer);
556
+ padding: 8px 16px 12px;
557
+ line-height: 1.5;
558
+ }
559
+
560
+ .admin-smtp-hint a {
561
+ color: var(--accent);
562
+ }
563
+
564
+ /* --- Session visibility indicator --- */
565
+ .session-private-icon {
566
+ display: inline-flex;
567
+ align-items: center;
568
+ margin-right: 4px;
569
+ color: var(--text-dimmer);
570
+ vertical-align: middle;
571
+ }
572
+
573
+ .session-private-icon svg {
574
+ width: 12px;
575
+ height: 12px;
576
+ }
@@ -158,6 +158,8 @@ img.emoji {
158
158
  --content-width: 760px;
159
159
  }
160
160
 
161
+ /* PWA standalone: home indicator safe area still applies on notched devices */
162
+
161
163
  ::selection {
162
164
  background: rgba(80, 250, 123, 0.25);
163
165
  }
@@ -170,6 +172,7 @@ html, body {
170
172
  font-size: 15px;
171
173
  line-height: 1.6;
172
174
  overflow: hidden;
175
+ overscroll-behavior: none;
173
176
  -webkit-font-smoothing: antialiased;
174
177
  -moz-osx-font-smoothing: grayscale;
175
178
  }
@@ -677,6 +677,7 @@
677
677
  color: var(--text);
678
678
  white-space: pre-wrap;
679
679
  word-break: break-word;
680
+ tab-size: 2;
680
681
  }
681
682
 
682
683
  .file-viewer-body pre code {
@@ -700,6 +701,7 @@
700
701
  line-height: 1.55;
701
702
  white-space: pre;
702
703
  word-break: normal;
704
+ tab-size: 2;
703
705
  }
704
706
 
705
707
  .file-viewer-gutter {
@@ -890,7 +892,12 @@
890
892
  @media (max-width: 1023px) {
891
893
  #terminal-container {
892
894
  position: fixed;
893
- inset: 0;
895
+ top: 0;
896
+ left: 0;
897
+ right: 0;
898
+ bottom: 0;
899
+ padding-top: var(--safe-top);
900
+ padding-bottom: var(--safe-bottom);
894
901
  background: var(--bg);
895
902
  z-index: 300;
896
903
  display: flex;
@@ -431,7 +431,7 @@
431
431
  /* --- Mobile --- */
432
432
  @media (max-width: 768px) {
433
433
  #home-hub {
434
- padding: 32px 16px 32px;
434
+ padding: 32px 16px calc(80px + var(--safe-bottom, 0px));
435
435
  }
436
436
  .hub-greeting h1 {
437
437
  font-size: 22px;
@@ -605,6 +605,7 @@
605
605
  height: 22px;
606
606
  }
607
607
 
608
+ /* --- Per-project presence avatars --- */
608
609
  /* --- Mobile: hide icon strip --- */
609
610
  @media (max-width: 768px) {
610
611
  #icon-strip {
@@ -559,6 +559,10 @@
559
559
  ========================================================================== */
560
560
 
561
561
  @media (max-width: 768px) {
562
+ #layout-body {
563
+ background: var(--bg);
564
+ }
565
+
562
566
  #main-area {
563
567
  overflow: visible;
564
568
  border-radius: 0;
@@ -142,46 +142,6 @@
142
142
  flex-shrink: 0;
143
143
  }
144
144
 
145
- /* --- Debug menu --- */
146
- #debug-menu-wrap {
147
- position: relative;
148
- }
149
-
150
- #debug-menu-wrap.hidden { display: none; }
151
-
152
- #debug-btn {
153
- display: flex;
154
- align-items: center;
155
- justify-content: center;
156
- background: none;
157
- border: 1px solid transparent;
158
- border-radius: 8px;
159
- color: var(--error);
160
- cursor: pointer;
161
- padding: 4px;
162
- opacity: 0.6;
163
- transition: opacity 0.15s, background 0.15s, border-color 0.15s;
164
- }
165
-
166
- #debug-btn .lucide { width: 15px; height: 15px; }
167
- #debug-btn:hover { opacity: 1; background: var(--error-8); border-color: var(--error-25); }
168
- #debug-btn.active { opacity: 1; background: var(--error-12); border-color: var(--error-25); }
169
-
170
- #debug-menu {
171
- position: absolute;
172
- top: calc(100% + 6px);
173
- right: 0;
174
- background: var(--bg-alt);
175
- border: 1px solid var(--border);
176
- border-radius: 10px;
177
- padding: 8px 0;
178
- min-width: 200px;
179
- box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.4);
180
- z-index: 200;
181
- }
182
-
183
- #debug-menu.hidden { display: none; }
184
-
185
145
  /* --- Terminal toggle button (mobile only) --- */
186
146
  #footer-status,
187
147
  #terminal-toggle-btn {
@@ -447,23 +407,28 @@
447
407
  .notif-action.copied { color: var(--success); }
448
408
 
449
409
  #client-count {
450
- background: var(--accent);
451
- color: #fff;
452
- font-size: 9px;
453
- font-weight: 700;
454
- min-width: 16px;
455
- height: 16px;
456
- border-radius: 8px;
457
410
  display: inline-flex;
458
411
  align-items: center;
459
- justify-content: center;
460
- padding: 0 4px;
461
- cursor: pointer;
462
- line-height: 1;
412
+ cursor: default;
463
413
  }
464
414
 
465
415
  #client-count.hidden { display: none; }
466
416
 
417
+ .client-avatar {
418
+ width: 22px;
419
+ height: 22px;
420
+ border-radius: 50%;
421
+ border: 2px solid var(--bg);
422
+ background: var(--bg-alt);
423
+ object-fit: cover;
424
+ cursor: default;
425
+ transition: filter 0.15s;
426
+ }
427
+
428
+ .client-avatar:hover {
429
+ filter: brightness(1.3);
430
+ }
431
+
467
432
  .tooltip {
468
433
  position: fixed;
469
434
  background: var(--code-bg);