clay-server 2.26.0-beta.1 → 2.26.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/bin/cli.js +5 -9
- package/lib/browser-mcp-server.js +496 -0
- package/lib/daemon.js +1 -1
- package/lib/os-users.js +23 -0
- package/lib/project-debate.js +206 -88
- package/lib/project-mate-interaction.js +766 -0
- package/lib/project-memory.js +677 -0
- package/lib/project.js +546 -1361
- package/lib/public/app.js +817 -175
- package/lib/public/css/debate.css +224 -2
- package/lib/public/css/icon-strip.css +10 -10
- package/lib/public/css/input.css +263 -83
- package/lib/public/css/mates.css +56 -57
- package/lib/public/css/mention.css +7 -4
- package/lib/public/css/menus.css +7 -0
- package/lib/public/css/messages.css +17 -0
- package/lib/public/css/mobile-nav.css +3 -1
- package/lib/public/css/overlays.css +181 -0
- package/lib/public/css/rewind.css +79 -0
- package/lib/public/css/server-settings.css +1 -0
- package/lib/public/css/sidebar.css +10 -0
- package/lib/public/css/title-bar.css +189 -3
- package/lib/public/index.html +53 -16
- package/lib/public/modules/context-sources.js +313 -0
- package/lib/public/modules/debate.js +184 -97
- package/lib/public/modules/input.js +18 -1
- package/lib/public/modules/mate-knowledge.js +11 -11
- package/lib/public/modules/mate-memory.js +5 -5
- package/lib/public/modules/mate-sidebar.js +13 -9
- package/lib/public/modules/mention.js +40 -2
- package/lib/public/modules/notifications.js +109 -1
- package/lib/public/modules/rewind.js +36 -0
- package/lib/public/modules/sidebar.js +107 -28
- package/lib/public/modules/terminal.js +8 -0
- package/lib/public/modules/theme.js +2 -1
- package/lib/public/modules/tools.js +69 -24
- package/lib/sdk-bridge.js +81 -7
- package/lib/sdk-worker.js +13 -1
- package/lib/server.js +42 -0
- package/lib/sessions.js +39 -7
- package/lib/terminal-manager.js +36 -6
- package/package.json +2 -2
|
@@ -71,6 +71,21 @@ export var builtinCommands = [
|
|
|
71
71
|
|
|
72
72
|
// --- Send ---
|
|
73
73
|
export function sendMessage() {
|
|
74
|
+
// Debate ended mode intercept: route to resume handler
|
|
75
|
+
if (ctx.isDebateEndedMode && ctx.isDebateEndedMode() && ctx.handleDebateEndedSend) {
|
|
76
|
+
ctx.handleDebateEndedSend();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Debate conclude mode intercept: route to conclude handler
|
|
80
|
+
if (ctx.isDebateConcludeMode && ctx.isDebateConcludeMode() && ctx.handleDebateConcludeSend) {
|
|
81
|
+
ctx.handleDebateConcludeSend();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Debate floor mode intercept: route to floor response handler
|
|
85
|
+
if (ctx.isDebateFloorMode && ctx.isDebateFloorMode() && ctx.handleDebateFloorSend) {
|
|
86
|
+
ctx.handleDebateFloorSend();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
74
89
|
// DM mode intercept: if in DM mode, route to DM handler instead
|
|
75
90
|
if (ctx.isDmMode && ctx.isDmMode() && ctx.handleDmSend) {
|
|
76
91
|
ctx.handleDmSend();
|
|
@@ -193,9 +208,11 @@ export function sendMessage() {
|
|
|
193
208
|
}
|
|
194
209
|
ctx.ws.send(JSON.stringify(payload));
|
|
195
210
|
|
|
196
|
-
//
|
|
211
|
+
// Show pre-thinking dots before server responds
|
|
197
212
|
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
198
213
|
ctx.showMatePreThinking();
|
|
214
|
+
} else if (ctx.showClaudePreThinking) {
|
|
215
|
+
ctx.showClaudePreThinking();
|
|
199
216
|
}
|
|
200
217
|
|
|
201
218
|
ctx.inputEl.value = "";
|
|
@@ -3,7 +3,7 @@ import { hideNotes } from './sticky-notes.js';
|
|
|
3
3
|
import { hideMemory } from './mate-memory.js';
|
|
4
4
|
import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var getWs = null;
|
|
7
7
|
var filesEl = null;
|
|
8
8
|
var sidebarBtn = null;
|
|
9
9
|
var countBadge = null;
|
|
@@ -46,8 +46,8 @@ var dirty = false;
|
|
|
46
46
|
var mode = "none"; // "none" | "viewer" | "editor"
|
|
47
47
|
var pendingEditMode = false;
|
|
48
48
|
|
|
49
|
-
export function initMateKnowledge(
|
|
50
|
-
|
|
49
|
+
export function initMateKnowledge(wsGetter) {
|
|
50
|
+
getWs = wsGetter;
|
|
51
51
|
filesEl = document.getElementById("mate-knowledge-files");
|
|
52
52
|
sidebarBtn = document.getElementById("mate-knowledge-btn");
|
|
53
53
|
countBadge = document.getElementById("mate-knowledge-count");
|
|
@@ -123,7 +123,7 @@ export function initMateKnowledge(mateWsGetter) {
|
|
|
123
123
|
if (editorDeleteBtn) {
|
|
124
124
|
editorDeleteBtn.addEventListener("click", function () {
|
|
125
125
|
if (editingFile) {
|
|
126
|
-
var ws =
|
|
126
|
+
var ws = getWs ? getWs() : null;
|
|
127
127
|
if (ws && ws.readyState === 1) {
|
|
128
128
|
ws.send(JSON.stringify({ type: "knowledge_delete", name: editingFile }));
|
|
129
129
|
}
|
|
@@ -317,7 +317,7 @@ export function isKnowledgeVisible() {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
export function requestKnowledgeList() {
|
|
320
|
-
var ws =
|
|
320
|
+
var ws = getWs ? getWs() : null;
|
|
321
321
|
if (ws && ws.readyState === 1) {
|
|
322
322
|
ws.send(JSON.stringify({ type: "knowledge_list" }));
|
|
323
323
|
}
|
|
@@ -457,7 +457,7 @@ function renderFileItem(file) {
|
|
|
457
457
|
|
|
458
458
|
item.addEventListener("click", (function (f) {
|
|
459
459
|
return function () {
|
|
460
|
-
var ws =
|
|
460
|
+
var ws = getWs ? getWs() : null;
|
|
461
461
|
if (ws && ws.readyState === 1) {
|
|
462
462
|
var msg = { type: "knowledge_read", name: f.name };
|
|
463
463
|
if (f.common) {
|
|
@@ -554,7 +554,7 @@ function saveKnowledge() {
|
|
|
554
554
|
setTimeout(function () { editorNameEl.style.outline = ""; }, 1500);
|
|
555
555
|
return;
|
|
556
556
|
}
|
|
557
|
-
var ws =
|
|
557
|
+
var ws = getWs ? getWs() : null;
|
|
558
558
|
if (ws && ws.readyState === 1) {
|
|
559
559
|
ws.send(JSON.stringify({ type: "knowledge_save", name: name, content: content }));
|
|
560
560
|
}
|
|
@@ -720,7 +720,7 @@ function showFileMenu(file, anchorBtn) {
|
|
|
720
720
|
// Request file content, then open editor directly
|
|
721
721
|
editingCommon = false;
|
|
722
722
|
editingFile = file.name;
|
|
723
|
-
var ws =
|
|
723
|
+
var ws = getWs ? getWs() : null;
|
|
724
724
|
if (ws && ws.readyState === 1) {
|
|
725
725
|
ws.send(JSON.stringify({ type: "knowledge_read", name: file.name }));
|
|
726
726
|
// Override handleKnowledgeContent to go straight to editor
|
|
@@ -736,7 +736,7 @@ function showFileMenu(file, anchorBtn) {
|
|
|
736
736
|
promoteItem.innerHTML = iconHtml("share-2") + "<span>Share to all mates</span>";
|
|
737
737
|
promoteItem.addEventListener("click", function (e) {
|
|
738
738
|
e.stopPropagation();
|
|
739
|
-
var ws =
|
|
739
|
+
var ws = getWs ? getWs() : null;
|
|
740
740
|
if (ws && ws.readyState === 1) {
|
|
741
741
|
ws.send(JSON.stringify({ type: "knowledge_promote", name: file.name }));
|
|
742
742
|
}
|
|
@@ -749,7 +749,7 @@ function showFileMenu(file, anchorBtn) {
|
|
|
749
749
|
depromoteItem.innerHTML = iconHtml("x-circle") + "<span>Unshare</span>";
|
|
750
750
|
depromoteItem.addEventListener("click", function (e) {
|
|
751
751
|
e.stopPropagation();
|
|
752
|
-
var ws =
|
|
752
|
+
var ws = getWs ? getWs() : null;
|
|
753
753
|
if (ws && ws.readyState === 1) {
|
|
754
754
|
ws.send(JSON.stringify({ type: "knowledge_depromote", name: file.name }));
|
|
755
755
|
}
|
|
@@ -770,7 +770,7 @@ function showFileMenu(file, anchorBtn) {
|
|
|
770
770
|
deleteItem.innerHTML = iconHtml("trash-2") + "<span>Delete</span>";
|
|
771
771
|
deleteItem.addEventListener("click", function (e) {
|
|
772
772
|
e.stopPropagation();
|
|
773
|
-
var ws =
|
|
773
|
+
var ws = getWs ? getWs() : null;
|
|
774
774
|
if (ws && ws.readyState === 1) {
|
|
775
775
|
ws.send(JSON.stringify({ type: "knowledge_delete", name: file.name }));
|
|
776
776
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
2
2
|
import { escapeHtml } from './utils.js';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var getWs = null;
|
|
5
5
|
var visible = false;
|
|
6
6
|
var cachedEntries = [];
|
|
7
7
|
var cachedSummary = "";
|
|
@@ -24,8 +24,8 @@ var confirmOverlay = null;
|
|
|
24
24
|
|
|
25
25
|
var _onShow = null;
|
|
26
26
|
|
|
27
|
-
export function initMateMemory(
|
|
28
|
-
|
|
27
|
+
export function initMateMemory(wsGetter, opts) {
|
|
28
|
+
getWs = wsGetter;
|
|
29
29
|
if (opts && opts.onShow) _onShow = opts.onShow;
|
|
30
30
|
|
|
31
31
|
sidebarBtn = document.getElementById("mate-memory-btn");
|
|
@@ -99,7 +99,7 @@ export function isMemoryVisible() {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function requestMemoryList() {
|
|
102
|
-
var ws =
|
|
102
|
+
var ws = getWs ? getWs() : null;
|
|
103
103
|
if (ws && ws.readyState === 1) {
|
|
104
104
|
ws.send(JSON.stringify({ type: "memory_list" }));
|
|
105
105
|
}
|
|
@@ -325,7 +325,7 @@ function confirmDelete(index) {
|
|
|
325
325
|
deleteBtn.className = "mate-memory-confirm-delete";
|
|
326
326
|
deleteBtn.textContent = "Delete";
|
|
327
327
|
deleteBtn.addEventListener("click", function () {
|
|
328
|
-
var ws =
|
|
328
|
+
var ws = getWs ? getWs() : null;
|
|
329
329
|
if (ws && ws.readyState === 1) {
|
|
330
330
|
ws.send(JSON.stringify({ type: "memory_delete", index: index }));
|
|
331
331
|
}
|
|
@@ -6,7 +6,7 @@ import { isSchedulerOpen, closeScheduler } from './scheduler.js';
|
|
|
6
6
|
import { hideNotes } from './sticky-notes.js';
|
|
7
7
|
import { openSearch as openSessionSearch } from './session-search.js';
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var getWs = null;
|
|
10
10
|
var currentMateId = null;
|
|
11
11
|
var currentMate = null;
|
|
12
12
|
var cachedSessions = [];
|
|
@@ -27,8 +27,8 @@ var searchQuery = "";
|
|
|
27
27
|
var searchMatchIds = null;
|
|
28
28
|
var searchDebounce = null;
|
|
29
29
|
|
|
30
|
-
export function initMateSidebar(
|
|
31
|
-
|
|
30
|
+
export function initMateSidebar(wsGetter) {
|
|
31
|
+
getWs = wsGetter;
|
|
32
32
|
columnEl = document.getElementById("mate-sidebar-column");
|
|
33
33
|
listEl = document.getElementById("mate-session-list");
|
|
34
34
|
avatarEl = document.getElementById("mate-sidebar-avatar");
|
|
@@ -37,7 +37,7 @@ export function initMateSidebar(mateWsGetter) {
|
|
|
37
37
|
|
|
38
38
|
if (newSessionBtn) {
|
|
39
39
|
newSessionBtn.addEventListener("click", function () {
|
|
40
|
-
var ws =
|
|
40
|
+
var ws = getWs ? getWs() : null;
|
|
41
41
|
if (ws && ws.readyState === 1) {
|
|
42
42
|
ws.send(JSON.stringify({ type: "new_session" }));
|
|
43
43
|
}
|
|
@@ -73,7 +73,7 @@ export function initMateSidebar(mateWsGetter) {
|
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
searchDebounce = setTimeout(function () {
|
|
76
|
-
var ws =
|
|
76
|
+
var ws = getWs ? getWs() : null;
|
|
77
77
|
if (ws && ws.readyState === 1) {
|
|
78
78
|
ws.send(JSON.stringify({ type: "search_sessions", query: q }));
|
|
79
79
|
}
|
|
@@ -316,6 +316,10 @@ function spawnSparks(cx, cy) {
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
export function getMateSessions() {
|
|
320
|
+
return cachedSessions;
|
|
321
|
+
}
|
|
322
|
+
|
|
319
323
|
export function hideMateSidebar() {
|
|
320
324
|
currentMateId = null;
|
|
321
325
|
currentMate = null;
|
|
@@ -498,7 +502,7 @@ function renderDebateGroup(children, groupKey) {
|
|
|
498
502
|
hideKnowledge();
|
|
499
503
|
if (isSchedulerOpen()) closeScheduler();
|
|
500
504
|
hideNotes();
|
|
501
|
-
var ws =
|
|
505
|
+
var ws = getWs ? getWs() : null;
|
|
502
506
|
if (ws && ws.readyState === 1) {
|
|
503
507
|
ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
504
508
|
}
|
|
@@ -534,7 +538,7 @@ function renderDebateGroup(children, groupKey) {
|
|
|
534
538
|
hideKnowledge();
|
|
535
539
|
if (isSchedulerOpen()) closeScheduler();
|
|
536
540
|
hideNotes();
|
|
537
|
-
var ws =
|
|
541
|
+
var ws = getWs ? getWs() : null;
|
|
538
542
|
if (ws && ws.readyState === 1) {
|
|
539
543
|
ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
540
544
|
}
|
|
@@ -580,7 +584,7 @@ function renderMateSessionItem(s) {
|
|
|
580
584
|
if (isSchedulerOpen()) closeScheduler();
|
|
581
585
|
hideNotes();
|
|
582
586
|
var pendingQuery = searchQuery || "";
|
|
583
|
-
var ws =
|
|
587
|
+
var ws = getWs ? getWs() : null;
|
|
584
588
|
if (ws && ws.readyState === 1) {
|
|
585
589
|
ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
586
590
|
}
|
|
@@ -642,7 +646,7 @@ function showMateSessionCtxMenu(anchorEl, sessionId, title) {
|
|
|
642
646
|
overlay.querySelector(".mate-confirm-cancel").addEventListener("click", function () { overlay.remove(); });
|
|
643
647
|
overlay.querySelector(".mate-confirm-delete").addEventListener("click", function () {
|
|
644
648
|
overlay.remove();
|
|
645
|
-
var ws =
|
|
649
|
+
var ws = getWs ? getWs() : null;
|
|
646
650
|
if (ws && ws.readyState === 1) {
|
|
647
651
|
ws.send(JSON.stringify({ type: "delete_session", id: sessionId }));
|
|
648
652
|
}
|
|
@@ -176,13 +176,42 @@ function selectMentionItem(idx) {
|
|
|
176
176
|
hideMentionMenu();
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
function ensureChipContrast(hex) {
|
|
180
|
+
if (!hex || hex.charAt(0) !== "#") return hex;
|
|
181
|
+
var r = parseInt(hex.substring(1, 3), 16);
|
|
182
|
+
var g = parseInt(hex.substring(3, 5), 16);
|
|
183
|
+
var b = parseInt(hex.substring(5, 7), 16);
|
|
184
|
+
// Relative luminance (sRGB)
|
|
185
|
+
var lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
186
|
+
var isDark = document.documentElement.classList.contains("dark") ||
|
|
187
|
+
document.body.classList.contains("dark-mode");
|
|
188
|
+
if (isDark) {
|
|
189
|
+
// Dark mode: lighten if too dark
|
|
190
|
+
return lum < 0.4 ? color_mix_lighten(r, g, b, 0.35) : hex;
|
|
191
|
+
}
|
|
192
|
+
// Light mode: darken if too bright
|
|
193
|
+
return lum > 0.55 ? color_mix_darken(r, g, b, 0.4) : hex;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function color_mix_darken(r, g, b, amount) {
|
|
197
|
+
var f = 1 - amount;
|
|
198
|
+
return "#" + [Math.round(r * f), Math.round(g * f), Math.round(b * f)]
|
|
199
|
+
.map(function (v) { return v.toString(16).padStart(2, "0"); }).join("");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function color_mix_lighten(r, g, b, amount) {
|
|
203
|
+
return "#" + [Math.round(r + (255 - r) * amount), Math.round(g + (255 - g) * amount), Math.round(b + (255 - b) * amount)]
|
|
204
|
+
.map(function (v) { return v.toString(16).padStart(2, "0"); }).join("");
|
|
205
|
+
}
|
|
206
|
+
|
|
179
207
|
function showInputMentionChip(name, color, avatarSrc) {
|
|
180
208
|
removeInputMentionChip();
|
|
209
|
+
var textColor = ensureChipContrast(color);
|
|
181
210
|
var chip = document.createElement("div");
|
|
182
211
|
chip.id = "input-mention-chip";
|
|
183
212
|
chip.innerHTML =
|
|
184
213
|
'<img class="input-mention-chip-avatar" src="' + escapeHtml(avatarSrc) + '" width="18" height="18" />' +
|
|
185
|
-
'<span class="input-mention-chip-name" style="color:' + escapeHtml(
|
|
214
|
+
'<span class="input-mention-chip-name" style="color:' + escapeHtml(textColor) + '">@' + escapeHtml(name) + '</span>' +
|
|
186
215
|
'<button class="input-mention-chip-remove" type="button" aria-label="Remove mention">×</button>';
|
|
187
216
|
chip.style.setProperty("--chip-color", color);
|
|
188
217
|
|
|
@@ -277,7 +306,16 @@ export function sendMention(mateId, text, pastes, images) {
|
|
|
277
306
|
|
|
278
307
|
// Recreate the mention block if it was lost (e.g. session switch)
|
|
279
308
|
function ensureMentionBlock() {
|
|
280
|
-
if (currentMentionEl && currentMentionEl.parentNode)
|
|
309
|
+
if (currentMentionEl && currentMentionEl.parentNode) {
|
|
310
|
+
// If other elements (e.g. permission requests) were added after the mention
|
|
311
|
+
// block, move it to the bottom to maintain chronological order.
|
|
312
|
+
var parent = currentMentionEl.parentNode;
|
|
313
|
+
if (parent.lastElementChild !== currentMentionEl) {
|
|
314
|
+
parent.appendChild(currentMentionEl);
|
|
315
|
+
if (ctx.scrollToBottom) ctx.scrollToBottom();
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
281
319
|
if (!activeMentionMeta) return;
|
|
282
320
|
// Recreate from saved meta
|
|
283
321
|
handleMentionStart(activeMentionMeta);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyToClipboard } from './utils.js';
|
|
1
|
+
import { copyToClipboard, showToast } from './utils.js';
|
|
2
2
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
3
|
|
|
4
4
|
var ctx;
|
|
@@ -155,6 +155,114 @@ export function initNotifications(_ctx) {
|
|
|
155
155
|
});
|
|
156
156
|
})();
|
|
157
157
|
|
|
158
|
+
// --- Extension pill popover ---
|
|
159
|
+
(function () {
|
|
160
|
+
var extPillWrap = $("ext-pill-wrap");
|
|
161
|
+
var extPillBtn = $("ext-pill");
|
|
162
|
+
var extPopover = $("ext-popover");
|
|
163
|
+
var extDownloadBtn = $("ext-download-btn");
|
|
164
|
+
var extDownloadStatus = $("ext-download-status");
|
|
165
|
+
var extCopyUrl = $("ext-copy-url");
|
|
166
|
+
if (!extPillWrap || !extPillBtn || !extPopover) return;
|
|
167
|
+
|
|
168
|
+
// Detect extension connection via postMessage from content.js
|
|
169
|
+
var extConnected = false;
|
|
170
|
+
var connectedBanner = $("ext-connected-banner");
|
|
171
|
+
var extDesc = $("ext-popover-desc");
|
|
172
|
+
var extDivider = $("ext-popover-divider");
|
|
173
|
+
var extGuideTitle = $("ext-popover-guide-title");
|
|
174
|
+
var extSteps = extPopover.querySelector(".ext-popover-steps");
|
|
175
|
+
|
|
176
|
+
function setExtConnected() {
|
|
177
|
+
if (extConnected) return;
|
|
178
|
+
extConnected = true;
|
|
179
|
+
extPillBtn.classList.add("ext-connected");
|
|
180
|
+
extPillBtn.innerHTML = '<i data-lucide="check"></i> Extension';
|
|
181
|
+
refreshIcons(extPillBtn);
|
|
182
|
+
if (connectedBanner) connectedBanner.classList.remove("hidden");
|
|
183
|
+
if (extDownloadBtn) extDownloadBtn.style.display = "none";
|
|
184
|
+
if (extDownloadStatus) extDownloadStatus.classList.add("hidden");
|
|
185
|
+
if (extDesc) extDesc.style.display = "none";
|
|
186
|
+
if (extDivider) extDivider.style.display = "none";
|
|
187
|
+
if (extGuideTitle) extGuideTitle.style.display = "none";
|
|
188
|
+
if (extSteps) extSteps.style.display = "none";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
window.addEventListener("message", function (event) {
|
|
192
|
+
if (event.source !== window) return;
|
|
193
|
+
if (!event.data || event.data.source !== "clay-chrome-extension") return;
|
|
194
|
+
setExtConnected();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Toggle popover
|
|
198
|
+
extPillBtn.addEventListener("click", function (e) {
|
|
199
|
+
e.stopPropagation();
|
|
200
|
+
extPopover.classList.toggle("visible");
|
|
201
|
+
refreshIcons(extPopover);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
document.addEventListener("click", function (e) {
|
|
205
|
+
if (!extPopover.contains(e.target) && e.target !== extPillBtn && !extPillBtn.contains(e.target)) {
|
|
206
|
+
extPopover.classList.remove("visible");
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Download button
|
|
211
|
+
if (extDownloadBtn) {
|
|
212
|
+
extDownloadBtn.addEventListener("click", function (e) {
|
|
213
|
+
e.stopPropagation();
|
|
214
|
+
extDownloadBtn.disabled = true;
|
|
215
|
+
extDownloadBtn.innerHTML = iconHtml("loader") + " Downloading...";
|
|
216
|
+
refreshIcons(extDownloadBtn);
|
|
217
|
+
var loaderIcon = extDownloadBtn.querySelector(".lucide");
|
|
218
|
+
if (loaderIcon) loaderIcon.style.animation = "spin 1s linear infinite";
|
|
219
|
+
if (extDownloadStatus) {
|
|
220
|
+
extDownloadStatus.classList.remove("hidden");
|
|
221
|
+
extDownloadStatus.textContent = "Fetching from GitHub...";
|
|
222
|
+
extDownloadStatus.style.color = "";
|
|
223
|
+
}
|
|
224
|
+
fetch("/api/extension/download").then(function (resp) {
|
|
225
|
+
if (!resp.ok) throw new Error("Download failed (" + resp.status + ")");
|
|
226
|
+
return resp.blob();
|
|
227
|
+
}).then(function (blob) {
|
|
228
|
+
var url = URL.createObjectURL(blob);
|
|
229
|
+
var a = document.createElement("a");
|
|
230
|
+
a.href = url;
|
|
231
|
+
a.download = "clay-chrome-extension.zip";
|
|
232
|
+
document.body.appendChild(a);
|
|
233
|
+
a.click();
|
|
234
|
+
document.body.removeChild(a);
|
|
235
|
+
URL.revokeObjectURL(url);
|
|
236
|
+
if (extDownloadStatus) {
|
|
237
|
+
extDownloadStatus.textContent = "Download complete!";
|
|
238
|
+
extDownloadStatus.style.color = "var(--accent)";
|
|
239
|
+
}
|
|
240
|
+
showToast("Extension downloaded");
|
|
241
|
+
}).catch(function (err) {
|
|
242
|
+
if (extDownloadStatus) {
|
|
243
|
+
extDownloadStatus.textContent = "Failed: " + err.message;
|
|
244
|
+
extDownloadStatus.style.color = "var(--danger, #e53935)";
|
|
245
|
+
}
|
|
246
|
+
showToast("Download failed");
|
|
247
|
+
}).finally(function () {
|
|
248
|
+
extDownloadBtn.disabled = false;
|
|
249
|
+
extDownloadBtn.innerHTML = iconHtml("download") + " Download Extension (.zip)";
|
|
250
|
+
refreshIcons(extDownloadBtn);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Copy chrome://extensions URL
|
|
256
|
+
if (extCopyUrl) {
|
|
257
|
+
extCopyUrl.addEventListener("click", function (e) {
|
|
258
|
+
e.stopPropagation();
|
|
259
|
+
copyToClipboard("chrome://extensions").then(function () {
|
|
260
|
+
showToast("Copied chrome://extensions");
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
})();
|
|
265
|
+
|
|
158
266
|
// --- Settings: Check for updates ---
|
|
159
267
|
(function () {
|
|
160
268
|
var settingsUpdateCheck = $("settings-update-check");
|
|
@@ -4,6 +4,8 @@ import { iconHtml, refreshIcons } from './icons.js';
|
|
|
4
4
|
var ctx;
|
|
5
5
|
var rewindMode = false;
|
|
6
6
|
var pendingRewindUuid = null;
|
|
7
|
+
var rewindPreviewInFlight = false;
|
|
8
|
+
var rewindExecuting = false;
|
|
7
9
|
var rewindBannerEl = null;
|
|
8
10
|
var rewindScrollHandler = null;
|
|
9
11
|
var rewindModal, rewindSummary, rewindFilesList, rewindConfirmBtn, rewindCancelBtn, rewindModeOptions;
|
|
@@ -50,6 +52,17 @@ export function clearPendingRewindUuid() {
|
|
|
50
52
|
pendingRewindUuid = null;
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
export function onRewindComplete() {
|
|
56
|
+
rewindExecuting = false;
|
|
57
|
+
rewindPreviewInFlight = false;
|
|
58
|
+
pendingRewindUuid = null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function onRewindError() {
|
|
62
|
+
rewindExecuting = false;
|
|
63
|
+
rewindPreviewInFlight = false;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
function initiateRewind(uuid) {
|
|
54
67
|
if (ctx.processing) {
|
|
55
68
|
ctx.addSystemMessage("Cannot rewind while processing. Stop the current operation first.", true);
|
|
@@ -59,7 +72,16 @@ function initiateRewind(uuid) {
|
|
|
59
72
|
ctx.addSystemMessage("No rewind point found for this turn.", true);
|
|
60
73
|
return;
|
|
61
74
|
}
|
|
75
|
+
if (rewindPreviewInFlight) {
|
|
76
|
+
// Debounce: ignore clicks while a preview is already in flight
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (rewindExecuting) {
|
|
80
|
+
ctx.addSystemMessage("Rewind already in progress.", true);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
62
83
|
pendingRewindUuid = uuid;
|
|
84
|
+
rewindPreviewInFlight = true;
|
|
63
85
|
if (ctx.ws && ctx.connected) {
|
|
64
86
|
ctx.ws.send(JSON.stringify({ type: "rewind_preview", uuid: uuid }));
|
|
65
87
|
}
|
|
@@ -98,6 +120,17 @@ function updateSummaryForMode() {
|
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
export function showRewindModal(data) {
|
|
123
|
+
rewindPreviewInFlight = false;
|
|
124
|
+
|
|
125
|
+
// Ignore stale preview results that don't match current pending UUID
|
|
126
|
+
if (data.uuid && pendingRewindUuid && data.uuid !== pendingRewindUuid) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// Ignore if a rewind is already executing
|
|
130
|
+
if (rewindExecuting) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
101
134
|
var p = data.preview || data;
|
|
102
135
|
var filePaths = p.filesChanged || p.filePaths || p.files || [];
|
|
103
136
|
var fileCount = filePaths.length;
|
|
@@ -171,6 +204,7 @@ export function showRewindModal(data) {
|
|
|
171
204
|
export function hideRewindModal() {
|
|
172
205
|
rewindModal.classList.add("hidden");
|
|
173
206
|
pendingRewindUuid = null;
|
|
207
|
+
rewindPreviewInFlight = false;
|
|
174
208
|
}
|
|
175
209
|
|
|
176
210
|
export function renderDiffPre(text) {
|
|
@@ -333,7 +367,9 @@ export function initRewind(_ctx) {
|
|
|
333
367
|
});
|
|
334
368
|
|
|
335
369
|
rewindConfirmBtn.addEventListener("click", function() {
|
|
370
|
+
if (rewindExecuting) return;
|
|
336
371
|
if (pendingRewindUuid && ctx.ws && ctx.connected) {
|
|
372
|
+
rewindExecuting = true;
|
|
337
373
|
var mode = getSelectedMode();
|
|
338
374
|
ctx.ws.send(JSON.stringify({ type: "rewind_execute", uuid: pendingRewindUuid, mode: mode }));
|
|
339
375
|
}
|