claude-relay 2.4.2 → 2.5.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 (75) hide show
  1. package/bin/cli.js +1 -2350
  2. package/package.json +7 -42
  3. package/LICENSE +0 -21
  4. package/README.md +0 -281
  5. package/lib/cli-sessions.js +0 -270
  6. package/lib/config.js +0 -222
  7. package/lib/daemon.js +0 -423
  8. package/lib/ipc.js +0 -112
  9. package/lib/pages.js +0 -714
  10. package/lib/project.js +0 -1224
  11. package/lib/public/app.js +0 -2157
  12. package/lib/public/apple-touch-icon.png +0 -0
  13. package/lib/public/css/base.css +0 -145
  14. package/lib/public/css/diff.css +0 -128
  15. package/lib/public/css/filebrowser.css +0 -1076
  16. package/lib/public/css/highlight.css +0 -144
  17. package/lib/public/css/input.css +0 -512
  18. package/lib/public/css/menus.css +0 -683
  19. package/lib/public/css/messages.css +0 -1159
  20. package/lib/public/css/overlays.css +0 -731
  21. package/lib/public/css/rewind.css +0 -529
  22. package/lib/public/css/sidebar.css +0 -794
  23. package/lib/public/favicon.svg +0 -26
  24. package/lib/public/icon-192.png +0 -0
  25. package/lib/public/icon-512.png +0 -0
  26. package/lib/public/icon-mono.svg +0 -19
  27. package/lib/public/index.html +0 -460
  28. package/lib/public/manifest.json +0 -27
  29. package/lib/public/modules/diff.js +0 -398
  30. package/lib/public/modules/events.js +0 -21
  31. package/lib/public/modules/filebrowser.js +0 -1375
  32. package/lib/public/modules/fileicons.js +0 -172
  33. package/lib/public/modules/icons.js +0 -54
  34. package/lib/public/modules/input.js +0 -578
  35. package/lib/public/modules/markdown.js +0 -149
  36. package/lib/public/modules/notifications.js +0 -643
  37. package/lib/public/modules/qrcode.js +0 -70
  38. package/lib/public/modules/rewind.js +0 -334
  39. package/lib/public/modules/sidebar.js +0 -628
  40. package/lib/public/modules/state.js +0 -3
  41. package/lib/public/modules/terminal.js +0 -658
  42. package/lib/public/modules/theme.js +0 -622
  43. package/lib/public/modules/tools.js +0 -1410
  44. package/lib/public/modules/utils.js +0 -56
  45. package/lib/public/style.css +0 -10
  46. package/lib/public/sw.js +0 -75
  47. package/lib/push.js +0 -125
  48. package/lib/sdk-bridge.js +0 -771
  49. package/lib/server.js +0 -577
  50. package/lib/sessions.js +0 -402
  51. package/lib/terminal-manager.js +0 -187
  52. package/lib/terminal.js +0 -24
  53. package/lib/themes/ayu-light.json +0 -9
  54. package/lib/themes/catppuccin-latte.json +0 -9
  55. package/lib/themes/catppuccin-mocha.json +0 -9
  56. package/lib/themes/claude-light.json +0 -9
  57. package/lib/themes/claude.json +0 -9
  58. package/lib/themes/dracula.json +0 -9
  59. package/lib/themes/everforest-light.json +0 -9
  60. package/lib/themes/everforest.json +0 -9
  61. package/lib/themes/github-light.json +0 -9
  62. package/lib/themes/gruvbox-dark.json +0 -9
  63. package/lib/themes/gruvbox-light.json +0 -9
  64. package/lib/themes/monokai.json +0 -9
  65. package/lib/themes/nord-light.json +0 -9
  66. package/lib/themes/nord.json +0 -9
  67. package/lib/themes/one-dark.json +0 -9
  68. package/lib/themes/one-light.json +0 -9
  69. package/lib/themes/rose-pine-dawn.json +0 -9
  70. package/lib/themes/rose-pine.json +0 -9
  71. package/lib/themes/solarized-dark.json +0 -9
  72. package/lib/themes/solarized-light.json +0 -9
  73. package/lib/themes/tokyo-night-light.json +0 -9
  74. package/lib/themes/tokyo-night.json +0 -9
  75. package/lib/updater.js +0 -96
@@ -1,70 +0,0 @@
1
- import { copyToClipboard } from './utils.js';
2
-
3
- function getShareUrl() {
4
- var url = window.location.href;
5
- var h = window.location.hostname;
6
- if ((h === "localhost" || h === "127.0.0.1") && window.__lanHost) {
7
- url = url.replace(h + ":" + window.location.port, window.__lanHost);
8
- }
9
- return url;
10
- }
11
-
12
- export function initQrCode() {
13
- var $ = function (id) { return document.getElementById(id); };
14
- var qrBtn = $("qr-btn");
15
- var qrOverlay = $("qr-overlay");
16
- var qrCanvas = $("qr-canvas");
17
- var qrUrl = $("qr-url");
18
-
19
- qrBtn.addEventListener("click", function (e) {
20
- e.stopPropagation();
21
- var url = getShareUrl();
22
-
23
- // Use Web Share API if available
24
- if (navigator.share) {
25
- navigator.share({ title: document.title || "Claude Relay", url: url }).catch(function () {});
26
- return;
27
- }
28
-
29
- // Fallback: show QR overlay
30
- var qr = qrcode(0, "M");
31
- qr.addData(url);
32
- qr.make();
33
- qrCanvas.innerHTML = qr.createSvgTag(5, 0);
34
- qrUrl.innerHTML = url + '<span class="qr-hint">click to copy</span>';
35
-
36
- qrOverlay.classList.remove("hidden");
37
- qrBtn.classList.add("active");
38
- });
39
-
40
- // click URL to copy
41
- qrUrl.addEventListener("click", function () {
42
- var url = getShareUrl();
43
- copyToClipboard(url).then(function () {
44
- qrUrl.innerHTML = "Copied!";
45
- qrUrl.classList.add("copied");
46
- setTimeout(function () {
47
- qrUrl.innerHTML = url + '<span class="qr-hint">click to copy</span>';
48
- qrUrl.classList.remove("copied");
49
- }, 1500);
50
- });
51
- });
52
-
53
- qrOverlay.addEventListener("click", function () {
54
- qrOverlay.classList.add("hidden");
55
- qrBtn.classList.remove("active");
56
- });
57
-
58
- // prevent closing when clicking the inner card
59
- $("qr-overlay-inner").addEventListener("click", function (e) {
60
- e.stopPropagation();
61
- });
62
-
63
- // ESC to close
64
- document.addEventListener("keydown", function (e) {
65
- if (e.key === "Escape" && !qrOverlay.classList.contains("hidden")) {
66
- qrOverlay.classList.add("hidden");
67
- qrBtn.classList.remove("active");
68
- }
69
- });
70
- }
@@ -1,334 +0,0 @@
1
- import { escapeHtml } from './utils.js';
2
- import { iconHtml, refreshIcons } from './icons.js';
3
-
4
- var ctx;
5
- var rewindMode = false;
6
- var pendingRewindUuid = null;
7
- var rewindBannerEl = null;
8
- var rewindScrollHandler = null;
9
- var rewindModal, rewindSummary, rewindFilesList, rewindConfirmBtn, rewindCancelBtn, rewindModeOptions;
10
- var cachedPreview = null;
11
-
12
- export function setRewindMode(on) {
13
- rewindMode = on;
14
- var appEl = ctx.$("app");
15
- if (on) {
16
- appEl.classList.add("rewind-mode");
17
- if (!rewindBannerEl) {
18
- rewindBannerEl = document.createElement("div");
19
- rewindBannerEl.className = "rewind-mode-banner";
20
- rewindBannerEl.innerHTML =
21
- '<i data-lucide="rotate-ccw"></i>' +
22
- '<span class="rewind-banner-text">Select a message to rewind to</span>' +
23
- '<button class="rewind-exit-btn" title="Exit rewind mode"><i data-lucide="x"></i></button>';
24
- rewindBannerEl.querySelector(".rewind-exit-btn").addEventListener("click", function() {
25
- setRewindMode(false);
26
- });
27
- ctx.$("app").appendChild(rewindBannerEl);
28
- refreshIcons();
29
- }
30
- buildRewindTimeline();
31
- } else {
32
- appEl.classList.remove("rewind-mode");
33
- if (rewindBannerEl) {
34
- rewindBannerEl.remove();
35
- rewindBannerEl = null;
36
- }
37
- removeRewindTimeline();
38
- }
39
- }
40
-
41
- export function isRewindMode() {
42
- return rewindMode;
43
- }
44
-
45
- export function getPendingRewindUuid() {
46
- return pendingRewindUuid;
47
- }
48
-
49
- export function clearPendingRewindUuid() {
50
- pendingRewindUuid = null;
51
- }
52
-
53
- function initiateRewind(uuid) {
54
- if (ctx.processing) {
55
- ctx.addSystemMessage("Cannot rewind while processing. Stop the current operation first.", true);
56
- return;
57
- }
58
- if (!uuid) {
59
- ctx.addSystemMessage("No rewind point found for this turn.", true);
60
- return;
61
- }
62
- pendingRewindUuid = uuid;
63
- if (ctx.ws && ctx.connected) {
64
- ctx.ws.send(JSON.stringify({ type: "rewind_preview", uuid: uuid }));
65
- }
66
- }
67
-
68
- function getSelectedMode() {
69
- if (!rewindModeOptions) return "both";
70
- var checked = rewindModeOptions.querySelector('input[name="rewind-mode"]:checked');
71
- return checked ? checked.value : "both";
72
- }
73
-
74
- function updateSummaryForMode() {
75
- if (!cachedPreview) return;
76
- var mode = getSelectedMode();
77
- var fileCount = cachedPreview.fileCount;
78
- var insertions = cachedPreview.insertions;
79
- var deletions = cachedPreview.deletions;
80
-
81
- if (mode === "chat") {
82
- rewindSummary.textContent = "Conversation will be rewound. Files will not be changed.";
83
- rewindFilesList.style.display = "none";
84
- } else if (fileCount > 0) {
85
- var summary = fileCount + " file" + (fileCount !== 1 ? "s" : "") + " will be restored.";
86
- if (insertions || deletions) summary += " (+" + insertions + " / -" + deletions + " lines)";
87
- if (mode === "files") summary += " Conversation will not be changed.";
88
- rewindSummary.textContent = summary;
89
- rewindFilesList.style.display = "";
90
- } else {
91
- if (mode === "files") {
92
- rewindSummary.textContent = "No file changes to restore.";
93
- } else {
94
- rewindSummary.textContent = "No file changes to restore. Conversation will be rewound.";
95
- }
96
- rewindFilesList.style.display = "none";
97
- }
98
- }
99
-
100
- export function showRewindModal(data) {
101
- var p = data.preview || data;
102
- var filePaths = p.filesChanged || p.filePaths || p.files || [];
103
- var fileCount = filePaths.length;
104
- var insertions = p.insertions || 0;
105
- var deletions = p.deletions || 0;
106
-
107
- cachedPreview = { fileCount: fileCount, insertions: insertions, deletions: deletions };
108
-
109
- // Reset radio to default
110
- if (rewindModeOptions) {
111
- var defaultRadio = rewindModeOptions.querySelector('input[value="both"]');
112
- if (defaultRadio) defaultRadio.checked = true;
113
- }
114
-
115
- if (fileCount > 0) {
116
- var summary = fileCount + " file" + (fileCount !== 1 ? "s" : "") + " will be restored.";
117
- if (insertions || deletions) summary += " (+" + insertions + " / -" + deletions + " lines)";
118
- rewindSummary.textContent = summary;
119
- } else {
120
- rewindSummary.textContent = "No file changes to restore. Conversation will be rewound.";
121
- }
122
-
123
- var diffs = data.diffs || {};
124
- rewindFilesList.innerHTML = "";
125
- if (filePaths.length > 0) {
126
- rewindFilesList.style.display = "";
127
- for (var i = 0; i < filePaths.length; i++) {
128
- var fp = filePaths[i];
129
- var section = document.createElement("div");
130
- section.className = "rewind-file-section expanded";
131
-
132
- var header = document.createElement("div");
133
- header.className = "rewind-file-header";
134
- header.innerHTML = '<span class="rewind-file-chevron"><i data-lucide="chevron-right"></i></span>';
135
-
136
- var pathSpan = document.createElement("span");
137
- pathSpan.className = "rewind-file-path";
138
- pathSpan.textContent = fp;
139
- pathSpan.title = fp;
140
- header.appendChild(pathSpan);
141
-
142
- header.addEventListener("click", function(sec) {
143
- return function() { sec.classList.toggle("expanded"); };
144
- }(section));
145
-
146
- section.appendChild(header);
147
-
148
- var diffContainer = document.createElement("div");
149
- diffContainer.className = "rewind-file-diff";
150
- var diffText = diffs[fp];
151
- if (diffText) {
152
- diffContainer.appendChild(renderDiffPre(diffText));
153
- } else {
154
- var noDiff = document.createElement("div");
155
- noDiff.className = "rewind-no-diff";
156
- noDiff.textContent = "No diff available (file may be untracked)";
157
- diffContainer.appendChild(noDiff);
158
- }
159
- section.appendChild(diffContainer);
160
-
161
- rewindFilesList.appendChild(section);
162
- }
163
- refreshIcons();
164
- } else {
165
- rewindFilesList.style.display = "none";
166
- }
167
-
168
- rewindModal.classList.remove("hidden");
169
- }
170
-
171
- export function hideRewindModal() {
172
- rewindModal.classList.add("hidden");
173
- pendingRewindUuid = null;
174
- }
175
-
176
- export function renderDiffPre(text) {
177
- var pre = document.createElement("pre");
178
- pre.className = "diff-content";
179
- var lines = text.split("\n");
180
- for (var i = 0; i < lines.length; i++) {
181
- var line = lines[i];
182
- var span = document.createElement("span");
183
- if (line.startsWith("@@")) {
184
- span.className = "diff-hunk";
185
- } else if (line.startsWith("---") || line.startsWith("+++")) {
186
- span.className = "diff-file-header";
187
- } else if (line.startsWith("+")) {
188
- span.className = "diff-add";
189
- } else if (line.startsWith("-")) {
190
- span.className = "diff-del";
191
- } else {
192
- span.className = "diff-ctx";
193
- }
194
- span.textContent = line;
195
- pre.appendChild(span);
196
- if (i < lines.length - 1) pre.appendChild(document.createTextNode("\n"));
197
- }
198
- return pre;
199
- }
200
-
201
- // --- Rewind timeline ---
202
- function buildRewindTimeline() {
203
- removeRewindTimeline();
204
-
205
- var userMsgs = ctx.messagesEl.querySelectorAll(".msg-user[data-uuid]");
206
- if (userMsgs.length === 0) return;
207
-
208
- var timeline = document.createElement("div");
209
- timeline.className = "rewind-timeline";
210
- timeline.id = "rewind-timeline";
211
-
212
- var track = document.createElement("div");
213
- track.className = "rewind-timeline-track";
214
-
215
- var viewport = document.createElement("div");
216
- viewport.className = "rewind-timeline-viewport";
217
- track.appendChild(viewport);
218
-
219
- for (var i = 0; i < userMsgs.length; i++) {
220
- var msg = userMsgs[i];
221
- var pct = userMsgs.length === 1 ? 50 : 6 + (i / (userMsgs.length - 1)) * 88;
222
-
223
- var bubble = msg.querySelector(".bubble");
224
- var msgText = bubble ? bubble.textContent.trim() : "";
225
- if (msgText.length > 18) msgText = msgText.substring(0, 18) + "\u2026";
226
-
227
- var marker = document.createElement("div");
228
- marker.className = "rewind-timeline-marker";
229
- marker.innerHTML = '<i data-lucide="message-square"></i><span class="marker-text">' + escapeHtml(msgText) + '</span>';
230
- marker.style.top = pct + "%";
231
- marker.dataset.uuid = msg.dataset.uuid;
232
- marker.dataset.offsetTop = msg.offsetTop;
233
-
234
- (function(targetMsg, markerEl) {
235
- markerEl.addEventListener("click", function() {
236
- targetMsg.scrollIntoView({ behavior: "smooth", block: "center" });
237
- });
238
- })(msg, marker);
239
-
240
- track.appendChild(marker);
241
- }
242
-
243
- timeline.appendChild(track);
244
-
245
- // Position timeline to align with messages area
246
- var appEl = ctx.$("app");
247
- var headerEl = ctx.$("header");
248
- var inputAreaEl = ctx.$("input-area");
249
- var appRect = appEl.getBoundingClientRect();
250
- var headerRect = headerEl.getBoundingClientRect();
251
- var inputRect = inputAreaEl.getBoundingClientRect();
252
-
253
- timeline.style.top = (headerRect.bottom - appRect.top + 4) + "px";
254
- timeline.style.bottom = (appRect.bottom - inputRect.top + 4) + "px";
255
-
256
- appEl.appendChild(timeline);
257
- refreshIcons();
258
-
259
- rewindScrollHandler = function() { updateTimelineViewport(track, viewport); };
260
- ctx.messagesEl.addEventListener("scroll", rewindScrollHandler);
261
- updateTimelineViewport(track, viewport);
262
- }
263
-
264
- function updateTimelineViewport(track, viewport) {
265
- if (!track) return;
266
- var scrollH = ctx.messagesEl.scrollHeight;
267
- var viewH = ctx.messagesEl.clientHeight;
268
- if (scrollH <= viewH) {
269
- viewport.style.top = "0";
270
- viewport.style.height = "100%";
271
- } else {
272
- var viewTop = ctx.messagesEl.scrollTop / scrollH;
273
- var viewBot = (ctx.messagesEl.scrollTop + viewH) / scrollH;
274
- viewport.style.top = (viewTop * 100) + "%";
275
- viewport.style.height = ((viewBot - viewTop) * 100) + "%";
276
- }
277
-
278
- var markers = track.querySelectorAll(".rewind-timeline-marker");
279
- var vTop = ctx.messagesEl.scrollTop;
280
- var vBot = vTop + viewH;
281
-
282
- for (var i = 0; i < markers.length; i++) {
283
- var msgTop = parseInt(markers[i].dataset.offsetTop, 10);
284
- if (msgTop >= vTop && msgTop <= vBot) {
285
- markers[i].classList.add("in-view");
286
- } else {
287
- markers[i].classList.remove("in-view");
288
- }
289
- }
290
- }
291
-
292
- function removeRewindTimeline() {
293
- var existing = document.getElementById("rewind-timeline");
294
- if (existing) existing.remove();
295
- if (rewindScrollHandler) {
296
- ctx.messagesEl.removeEventListener("scroll", rewindScrollHandler);
297
- rewindScrollHandler = null;
298
- }
299
- }
300
-
301
- export function initRewind(_ctx) {
302
- ctx = _ctx;
303
-
304
- rewindModal = ctx.$("rewind-modal");
305
- rewindSummary = ctx.$("rewind-summary");
306
- rewindFilesList = ctx.$("rewind-files-list");
307
- rewindConfirmBtn = ctx.$("rewind-confirm");
308
- rewindCancelBtn = ctx.$("rewind-cancel");
309
- rewindModeOptions = ctx.$("rewind-mode-options");
310
-
311
- // Update summary when rewind mode radio changes
312
- if (rewindModeOptions) {
313
- rewindModeOptions.addEventListener("change", updateSummaryForMode);
314
- }
315
-
316
- // Click on user message bubble to rewind
317
- ctx.messagesEl.addEventListener("click", function(e) {
318
- var bubble = e.target.closest(".msg-user[data-uuid] .bubble");
319
- if (!bubble) return;
320
- var msgEl = bubble.closest(".msg-user[data-uuid]");
321
- if (msgEl) initiateRewind(msgEl.dataset.uuid);
322
- });
323
-
324
- rewindConfirmBtn.addEventListener("click", function() {
325
- if (pendingRewindUuid && ctx.ws && ctx.connected) {
326
- var mode = getSelectedMode();
327
- ctx.ws.send(JSON.stringify({ type: "rewind_execute", uuid: pendingRewindUuid, mode: mode }));
328
- }
329
- hideRewindModal();
330
- });
331
-
332
- rewindCancelBtn.addEventListener("click", hideRewindModal);
333
- rewindModal.querySelector(".confirm-backdrop").addEventListener("click", hideRewindModal);
334
- }