claude-relay 2.3.0 → 2.4.0

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 (54) hide show
  1. package/README.md +21 -5
  2. package/bin/cli.js +214 -9
  3. package/lib/cli-sessions.js +270 -0
  4. package/lib/config.js +3 -2
  5. package/lib/daemon.js +45 -1
  6. package/lib/pages.js +8 -1
  7. package/lib/project.js +121 -12
  8. package/lib/public/app.js +411 -87
  9. package/lib/public/css/base.css +41 -7
  10. package/lib/public/css/diff.css +6 -6
  11. package/lib/public/css/filebrowser.css +62 -52
  12. package/lib/public/css/highlight.css +144 -0
  13. package/lib/public/css/input.css +11 -9
  14. package/lib/public/css/menus.css +82 -23
  15. package/lib/public/css/messages.css +183 -35
  16. package/lib/public/css/overlays.css +166 -50
  17. package/lib/public/css/rewind.css +17 -17
  18. package/lib/public/css/sidebar.css +210 -137
  19. package/lib/public/index.html +75 -42
  20. package/lib/public/modules/filebrowser.js +2 -1
  21. package/lib/public/modules/markdown.js +10 -10
  22. package/lib/public/modules/notifications.js +38 -1
  23. package/lib/public/modules/sidebar.js +109 -31
  24. package/lib/public/modules/terminal.js +84 -23
  25. package/lib/public/modules/theme.js +622 -0
  26. package/lib/public/modules/tools.js +247 -4
  27. package/lib/public/modules/utils.js +21 -5
  28. package/lib/public/style.css +1 -0
  29. package/lib/sdk-bridge.js +95 -0
  30. package/lib/server.js +45 -3
  31. package/lib/sessions.js +16 -3
  32. package/lib/themes/ayu-light.json +9 -0
  33. package/lib/themes/catppuccin-latte.json +9 -0
  34. package/lib/themes/catppuccin-mocha.json +9 -0
  35. package/lib/themes/claude-light.json +9 -0
  36. package/lib/themes/claude.json +9 -0
  37. package/lib/themes/dracula.json +9 -0
  38. package/lib/themes/everforest-light.json +9 -0
  39. package/lib/themes/everforest.json +9 -0
  40. package/lib/themes/github-light.json +9 -0
  41. package/lib/themes/gruvbox-dark.json +9 -0
  42. package/lib/themes/gruvbox-light.json +9 -0
  43. package/lib/themes/monokai.json +9 -0
  44. package/lib/themes/nord-light.json +9 -0
  45. package/lib/themes/nord.json +9 -0
  46. package/lib/themes/one-dark.json +9 -0
  47. package/lib/themes/one-light.json +9 -0
  48. package/lib/themes/rose-pine-dawn.json +9 -0
  49. package/lib/themes/rose-pine.json +9 -0
  50. package/lib/themes/solarized-dark.json +9 -0
  51. package/lib/themes/solarized-light.json +9 -0
  52. package/lib/themes/tokyo-night-light.json +9 -0
  53. package/lib/themes/tokyo-night.json +9 -0
  54. package/package.json +2 -1
@@ -18,8 +18,8 @@
18
18
  justify-content: center;
19
19
  gap: 8px;
20
20
  padding: 8px 16px;
21
- background: rgba(218, 119, 86, 0.08);
22
- border-bottom: 1px solid rgba(218, 119, 86, 0.15);
21
+ background: var(--accent-8);
22
+ border-bottom: 1px solid var(--accent-15);
23
23
  font-size: 12px;
24
24
  color: var(--text-secondary);
25
25
  }
@@ -73,8 +73,8 @@
73
73
  justify-content: center;
74
74
  gap: 8px;
75
75
  padding: 8px 16px;
76
- background: rgba(87, 171, 90, 0.08);
77
- border-bottom: 1px solid rgba(87, 171, 90, 0.15);
76
+ background: var(--success-8);
77
+ border-bottom: 1px solid var(--success-15);
78
78
  font-size: 12px;
79
79
  color: var(--text-secondary);
80
80
  position: relative;
@@ -92,8 +92,8 @@
92
92
  .update-banner-text strong { color: var(--text); font-weight: 600; }
93
93
 
94
94
  #update-how {
95
- background: rgba(87, 171, 90, 0.15);
96
- border: 1px solid rgba(87, 171, 90, 0.25);
95
+ background: var(--success-15);
96
+ border: 1px solid var(--success-25);
97
97
  border-radius: 6px;
98
98
  color: var(--success);
99
99
  cursor: pointer;
@@ -105,7 +105,7 @@
105
105
  transition: background 0.15s;
106
106
  }
107
107
 
108
- #update-how:hover { background: rgba(87, 171, 90, 0.25); }
108
+ #update-how:hover { background: var(--success-25); }
109
109
 
110
110
  #update-popover {
111
111
  display: none;
@@ -118,7 +118,7 @@
118
118
  border-radius: 10px;
119
119
  padding: 12px 16px;
120
120
  z-index: 200;
121
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
121
+ box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.4);
122
122
  white-space: nowrap;
123
123
  }
124
124
 
@@ -140,7 +140,7 @@
140
140
  font-family: "SF Mono", Menlo, Monaco, monospace;
141
141
  font-size: 13px;
142
142
  color: var(--text);
143
- background: rgba(255, 255, 255, 0.06);
143
+ background: rgba(var(--overlay-rgb), 0.06);
144
144
  padding: 6px 12px;
145
145
  border-radius: 6px;
146
146
  user-select: all;
@@ -186,11 +186,11 @@
186
186
  justify-content: center;
187
187
  gap: 8px;
188
188
  padding: 7px 16px;
189
- background: rgba(229, 83, 75, 0.12);
190
- border-bottom: 1px solid rgba(229, 83, 75, 0.25);
189
+ background: var(--error-12);
190
+ border-bottom: 1px solid var(--error-25);
191
191
  font-size: 12px;
192
192
  font-weight: 500;
193
- color: #E5534B;
193
+ color: var(--error);
194
194
  }
195
195
 
196
196
  #skip-perms-banner.hidden { display: none; }
@@ -211,7 +211,7 @@
211
211
  .confirm-backdrop {
212
212
  position: absolute;
213
213
  inset: 0;
214
- background: rgba(0, 0, 0, 0.5);
214
+ background: rgba(var(--shadow-rgb), 0.5);
215
215
  backdrop-filter: blur(2px);
216
216
  -webkit-backdrop-filter: blur(2px);
217
217
  }
@@ -224,7 +224,7 @@
224
224
  padding: 20px 24px;
225
225
  max-width: 320px;
226
226
  width: 90%;
227
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
227
+ box-shadow: 0 8px 32px rgba(var(--shadow-rgb), 0.5);
228
228
  }
229
229
 
230
230
  .confirm-text {
@@ -341,13 +341,13 @@
341
341
  align-items: center;
342
342
  gap: 8px;
343
343
  padding: 8px 12px;
344
- background: rgba(255, 255, 255, 0.03);
344
+ background: rgba(var(--overlay-rgb), 0.03);
345
345
  cursor: pointer;
346
346
  user-select: none;
347
347
  transition: background 0.15s;
348
348
  }
349
349
 
350
- .rewind-file-header:hover { background: rgba(255, 255, 255, 0.06); }
350
+ .rewind-file-header:hover { background: rgba(var(--overlay-rgb), 0.06); }
351
351
 
352
352
  .rewind-file-chevron {
353
353
  display: inline-flex;
@@ -377,8 +377,8 @@
377
377
  flex-shrink: 0;
378
378
  }
379
379
 
380
- .rewind-file-stats .stat-add { color: #3FB950; }
381
- .rewind-file-stats .stat-del { color: #F85149; }
380
+ .rewind-file-stats .stat-add { color: var(--success); }
381
+ .rewind-file-stats .stat-del { color: var(--error); }
382
382
 
383
383
  .rewind-file-diff {
384
384
  display: none;
@@ -407,7 +407,7 @@
407
407
  font-style: italic;
408
408
  }
409
409
 
410
- /* --- Resume modal --- */
410
+ /* --- Resume / Session Picker modal --- */
411
411
  #resume-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; }
412
412
  #resume-modal.hidden { display: none; }
413
413
 
@@ -418,44 +418,68 @@
418
418
  margin-bottom: 12px;
419
419
  }
420
420
 
421
- .resume-modal-body {
422
- font-size: 13px;
423
- color: var(--text-secondary);
424
- line-height: 1.6;
425
- margin-bottom: 16px;
421
+ .resume-picker-dialog { min-width: 380px; max-width: 480px; }
422
+ .resume-picker-body { margin-bottom: 12px; }
423
+
424
+ .resume-picker-loading {
425
+ display: flex; align-items: center; gap: 10px;
426
+ padding: 24px 0; justify-content: center;
427
+ font-size: 13px; color: var(--text-muted);
426
428
  }
429
+ .resume-picker-loading.hidden { display: none; }
430
+ .resume-picker-empty.hidden { display: none; }
431
+ .resume-picker-list.hidden { display: none; }
427
432
 
428
- .resume-modal-body p { margin: 0 0 10px; }
433
+ .resume-picker-spinner {
434
+ width: 16px; height: 16px;
435
+ border: 2px solid var(--border);
436
+ border-top-color: var(--text-secondary);
437
+ border-radius: 50%;
438
+ animation: spin 0.6s linear infinite;
439
+ }
429
440
 
430
- .resume-modal-hint {
431
- font-size: 12px;
432
- color: var(--text-muted);
433
- margin-bottom: 12px;
441
+ @keyframes spin { to { transform: rotate(360deg); } }
442
+
443
+ .resume-picker-empty {
444
+ padding: 24px 0; text-align: center;
445
+ font-size: 13px; color: var(--text-muted);
434
446
  }
435
447
 
436
- .resume-modal-hint code {
437
- background: rgba(255, 255, 255, 0.07);
438
- padding: 2px 6px;
439
- border-radius: 4px;
440
- font-family: "SF Mono", Menlo, Monaco, monospace;
441
- font-size: 0.92em;
448
+ .resume-picker-list {
449
+ max-height: 400px; overflow-y: auto;
450
+ display: flex; flex-direction: column; gap: 2px;
442
451
  }
443
452
 
444
- #resume-session-input {
445
- width: 100%;
446
- background: var(--input-bg);
447
- border: 1px solid var(--border);
448
- border-radius: 8px;
449
- color: var(--text);
450
- font-size: 13px;
451
- font-family: "SF Mono", Menlo, Monaco, monospace;
452
- padding: 10px 12px;
453
- outline: none;
454
- transition: border-color 0.2s;
453
+ .cli-session-item {
454
+ display: flex; flex-direction: column; gap: 4px;
455
+ padding: 10px 12px; border-radius: 8px;
456
+ cursor: pointer; transition: background 0.15s;
457
+ border: 1px solid transparent;
455
458
  }
456
459
 
457
- #resume-session-input:focus { border-color: var(--text-dimmer); }
458
- #resume-session-input::placeholder { color: var(--text-muted); font-family: "Inter", system-ui, sans-serif; }
460
+ .cli-session-item:hover {
461
+ background: rgba(var(--overlay-rgb), 0.05);
462
+ border-color: var(--border-subtle);
463
+ }
464
+
465
+ .cli-session-title {
466
+ font-size: 13px; color: var(--text);
467
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
468
+ line-height: 1.4;
469
+ }
470
+
471
+ .cli-session-meta {
472
+ display: flex; align-items: center; gap: 8px;
473
+ font-size: 11px; color: var(--text-muted);
474
+ flex-wrap: wrap;
475
+ }
476
+
477
+ .cli-session-meta .badge {
478
+ background: rgba(var(--overlay-rgb), 0.07);
479
+ padding: 1px 6px; border-radius: 4px;
480
+ font-family: "SF Mono", Menlo, Monaco, monospace;
481
+ font-size: 10px;
482
+ }
459
483
 
460
484
  /* --- Notification help modal --- */
461
485
  #notif-help-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; }
@@ -488,7 +512,7 @@
488
512
  display: flex;
489
513
  align-items: center;
490
514
  gap: 8px;
491
- background: rgba(255, 255, 255, 0.04);
515
+ background: rgba(var(--overlay-rgb), 0.04);
492
516
  border: 1px solid var(--border);
493
517
  border-radius: 8px;
494
518
  padding: 8px 10px;
@@ -520,6 +544,98 @@
520
544
  .notif-help-url .popover-copy:hover { color: var(--text); border-color: var(--text-dimmer); }
521
545
  .notif-help-url .popover-copy.copied { color: var(--success); border-color: var(--success); }
522
546
 
547
+ /* --- Add project modal --- */
548
+ #add-project-modal { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; }
549
+ #add-project-modal.hidden { display: none; }
550
+
551
+ .add-project-dialog { max-width: 420px; }
552
+
553
+ .add-project-title {
554
+ font-size: 15px;
555
+ font-weight: 600;
556
+ color: var(--text);
557
+ margin-bottom: 14px;
558
+ }
559
+
560
+ .add-project-body { margin-bottom: 16px; }
561
+
562
+ .add-project-input-wrap { position: relative; }
563
+
564
+ #add-project-input {
565
+ width: 100%;
566
+ background: var(--input-bg);
567
+ border: 1px solid var(--border);
568
+ border-radius: 8px;
569
+ color: var(--text);
570
+ font-size: 13px;
571
+ font-family: "SF Mono", Menlo, Monaco, monospace;
572
+ padding: 10px 12px;
573
+ outline: none;
574
+ transition: border-color 0.2s;
575
+ }
576
+
577
+ #add-project-input:focus { border-color: var(--text-dimmer); }
578
+ #add-project-input::placeholder { color: var(--text-muted); font-family: "Inter", system-ui, sans-serif; }
579
+
580
+ #add-project-suggestions {
581
+ position: absolute;
582
+ top: calc(100% + 4px);
583
+ left: 0;
584
+ right: 0;
585
+ background: var(--bg-alt);
586
+ border: 1px solid var(--border);
587
+ border-radius: 8px;
588
+ max-height: 200px;
589
+ overflow-y: auto;
590
+ z-index: 10;
591
+ box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.4);
592
+ }
593
+
594
+ #add-project-suggestions.hidden { display: none; }
595
+
596
+ .add-project-suggestion-item {
597
+ display: flex;
598
+ align-items: center;
599
+ gap: 8px;
600
+ padding: 8px 12px;
601
+ font-size: 13px;
602
+ color: var(--text);
603
+ cursor: pointer;
604
+ transition: background 0.1s;
605
+ }
606
+
607
+ .add-project-suggestion-item:first-child { border-radius: 8px 8px 0 0; }
608
+ .add-project-suggestion-item:last-child { border-radius: 0 0 8px 8px; }
609
+ .add-project-suggestion-item:only-child { border-radius: 8px; }
610
+ .add-project-suggestion-item:hover,
611
+ .add-project-suggestion-item.active { background: var(--sidebar-hover); }
612
+
613
+ .add-project-suggestion-item .lucide { width: 14px; height: 14px; color: var(--text-muted); flex-shrink: 0; }
614
+
615
+ .add-project-suggestion-name {
616
+ flex: 1;
617
+ overflow: hidden;
618
+ text-overflow: ellipsis;
619
+ white-space: nowrap;
620
+ }
621
+
622
+ .add-project-suggestion-path {
623
+ font-size: 11px;
624
+ color: var(--text-muted);
625
+ overflow: hidden;
626
+ text-overflow: ellipsis;
627
+ white-space: nowrap;
628
+ max-width: 50%;
629
+ }
630
+
631
+ #add-project-error {
632
+ font-size: 12px;
633
+ color: var(--error);
634
+ margin-top: 8px;
635
+ }
636
+
637
+ #add-project-error.hidden { display: none; }
638
+
523
639
  /* --- Connect overlay --- */
524
640
  #connect-overlay {
525
641
  position: absolute;
@@ -570,7 +686,7 @@
570
686
  90deg,
571
687
  var(--accent) 0%,
572
688
  var(--accent) 35%,
573
- #f0c0a0 50%,
689
+ var(--accent-hover) 50%,
574
690
  var(--accent) 65%,
575
691
  var(--accent) 100%
576
692
  );
@@ -37,7 +37,7 @@
37
37
  }
38
38
 
39
39
  #app.rewind-mode .msg-user[data-uuid] .bubble:hover {
40
- box-shadow: 0 0 0 2px var(--accent-hover), 0 0 12px rgba(218, 119, 86, 0.2);
40
+ box-shadow: 0 0 0 2px var(--accent-hover), 0 0 12px var(--accent-20);
41
41
  }
42
42
 
43
43
  #app.rewind-mode .msg-user:not([data-uuid]) { opacity: 0.35; }
@@ -58,9 +58,9 @@
58
58
  align-items: center;
59
59
  gap: 8px;
60
60
  background: var(--bg-alt);
61
- border: 1px solid rgba(218, 119, 86, 0.25);
61
+ border: 1px solid var(--accent-25);
62
62
  border-radius: 20px;
63
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
63
+ box-shadow: 0 4px 16px rgba(var(--shadow-rgb), 0.3);
64
64
  z-index: 10;
65
65
  white-space: nowrap;
66
66
  }
@@ -79,7 +79,7 @@
79
79
  height: 22px;
80
80
  border-radius: 50%;
81
81
  border: none;
82
- background: rgba(218, 119, 86, 0.15);
82
+ background: var(--accent-15);
83
83
  color: var(--accent);
84
84
  cursor: pointer;
85
85
  padding: 0;
@@ -88,7 +88,7 @@
88
88
  }
89
89
 
90
90
  .rewind-exit-btn .lucide { width: 12px; height: 12px; }
91
- .rewind-exit-btn:hover { background: rgba(218, 119, 86, 0.3); }
91
+ .rewind-exit-btn:hover { background: var(--accent-30); }
92
92
 
93
93
  /* --- Rewind timeline --- */
94
94
  .rewind-timeline {
@@ -112,7 +112,7 @@
112
112
  position: absolute;
113
113
  left: -3px;
114
114
  width: 8px;
115
- background: rgba(218, 119, 86, 0.2);
115
+ background: var(--accent-20);
116
116
  border-radius: 4px;
117
117
  pointer-events: none;
118
118
  transition: top 0.1s ease, height 0.1s ease;
@@ -156,7 +156,7 @@
156
156
 
157
157
  .rewind-timeline-marker:hover {
158
158
  border-color: var(--accent);
159
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
159
+ box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.3);
160
160
  }
161
161
 
162
162
  .rewind-timeline-marker:hover .lucide,
@@ -225,7 +225,7 @@
225
225
  overflow: hidden;
226
226
  text-overflow: ellipsis;
227
227
  white-space: nowrap;
228
- background: linear-gradient(90deg, var(--accent) 0%, #F0C8A0 50%, var(--accent) 100%);
228
+ background: linear-gradient(90deg, var(--accent) 0%, var(--accent-hover) 50%, var(--accent) 100%);
229
229
  background-size: 200% auto;
230
230
  -webkit-background-clip: text;
231
231
  -webkit-text-fill-color: transparent;
@@ -280,7 +280,7 @@
280
280
  }
281
281
 
282
282
  .ask-user-option:hover {
283
- background: rgba(255, 255, 255, 0.06);
283
+ background: rgba(var(--overlay-rgb), 0.06);
284
284
  border-color: var(--accent);
285
285
  }
286
286
 
@@ -353,7 +353,7 @@
353
353
  }
354
354
 
355
355
  .ask-user-skip:hover {
356
- background: rgba(255, 255, 255, 0.04);
356
+ background: rgba(var(--overlay-rgb), 0.04);
357
357
  color: var(--text-secondary);
358
358
  border-color: var(--text-dimmer);
359
359
  }
@@ -382,7 +382,7 @@
382
382
  align-items: center;
383
383
  gap: 8px;
384
384
  padding: 10px 14px;
385
- background: rgba(255, 255, 255, 0.03);
385
+ background: rgba(var(--overlay-rgb), 0.03);
386
386
  border: 1px solid var(--border);
387
387
  border-bottom: none;
388
388
  border-radius: 12px 12px 0 0;
@@ -398,7 +398,7 @@
398
398
  border: 1px solid var(--border);
399
399
  border-top: none;
400
400
  border-bottom: none;
401
- background: rgba(255, 255, 255, 0.015);
401
+ background: rgba(var(--overlay-rgb), 0.015);
402
402
  }
403
403
 
404
404
  .permission-summary {
@@ -464,7 +464,7 @@
464
464
  border: 1px solid var(--border);
465
465
  border-top: none;
466
466
  border-radius: 0 0 12px 12px;
467
- background: rgba(255, 255, 255, 0.015);
467
+ background: rgba(var(--overlay-rgb), 0.015);
468
468
  }
469
469
 
470
470
  .permission-btn {
@@ -485,7 +485,7 @@
485
485
  .permission-allow-session { background: var(--bg-alt); color: var(--text-secondary); border-color: var(--border); }
486
486
  .permission-allow-session:hover { background: var(--input-bg); opacity: 1; }
487
487
  .permission-deny { background: transparent; border-color: var(--border); color: var(--text-muted); }
488
- .permission-deny:hover { background: rgba(229, 83, 75, 0.08); border-color: var(--error); color: var(--error); opacity: 1; }
488
+ .permission-deny:hover { background: var(--error-8); border-color: var(--error); color: var(--error); opacity: 1; }
489
489
 
490
490
  /* Resolved states */
491
491
  .permission-container.resolved .permission-btn { display: none; }
@@ -494,10 +494,10 @@
494
494
  .permission-container.resolved-allowed .permission-decision-label { color: var(--text-muted); }
495
495
 
496
496
  .permission-container.resolved-denied .permission-header {
497
- border-color: rgba(229, 83, 75, 0.15);
497
+ border-color: var(--error-15);
498
498
  }
499
- .permission-container.resolved-denied .permission-body { border-color: rgba(229, 83, 75, 0.15); }
500
- .permission-container.resolved-denied .permission-actions { border-color: rgba(229, 83, 75, 0.15); }
499
+ .permission-container.resolved-denied .permission-body { border-color: var(--error-15); }
500
+ .permission-container.resolved-denied .permission-actions { border-color: var(--error-15); }
501
501
  .permission-container.resolved-denied .permission-decision-label { color: var(--error); }
502
502
 
503
503
  .permission-container.resolved-cancelled .permission-decision-label { color: var(--text-dimmer); }