claude-relay 2.3.1 → 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.
- package/README.md +20 -5
- package/bin/cli.js +206 -8
- package/lib/cli-sessions.js +270 -0
- package/lib/daemon.js +40 -0
- package/lib/project.js +121 -1
- package/lib/public/app.js +385 -76
- package/lib/public/css/base.css +41 -7
- package/lib/public/css/diff.css +6 -6
- package/lib/public/css/filebrowser.css +34 -54
- package/lib/public/css/highlight.css +144 -0
- package/lib/public/css/input.css +9 -9
- package/lib/public/css/menus.css +82 -23
- package/lib/public/css/messages.css +178 -34
- package/lib/public/css/overlays.css +166 -50
- package/lib/public/css/rewind.css +17 -17
- package/lib/public/css/sidebar.css +210 -137
- package/lib/public/index.html +73 -40
- package/lib/public/modules/filebrowser.js +2 -1
- package/lib/public/modules/markdown.js +10 -10
- package/lib/public/modules/notifications.js +38 -1
- package/lib/public/modules/sidebar.js +109 -31
- package/lib/public/modules/terminal.js +11 -23
- package/lib/public/modules/theme.js +622 -0
- package/lib/public/modules/tools.js +245 -4
- package/lib/public/modules/utils.js +21 -5
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +95 -0
- package/lib/server.js +41 -0
- package/lib/sessions.js +16 -3
- package/lib/themes/ayu-light.json +9 -0
- package/lib/themes/catppuccin-latte.json +9 -0
- package/lib/themes/catppuccin-mocha.json +9 -0
- package/lib/themes/claude-light.json +9 -0
- package/lib/themes/claude.json +9 -0
- package/lib/themes/dracula.json +9 -0
- package/lib/themes/everforest-light.json +9 -0
- package/lib/themes/everforest.json +9 -0
- package/lib/themes/github-light.json +9 -0
- package/lib/themes/gruvbox-dark.json +9 -0
- package/lib/themes/gruvbox-light.json +9 -0
- package/lib/themes/monokai.json +9 -0
- package/lib/themes/nord-light.json +9 -0
- package/lib/themes/nord.json +9 -0
- package/lib/themes/one-dark.json +9 -0
- package/lib/themes/one-light.json +9 -0
- package/lib/themes/rose-pine-dawn.json +9 -0
- package/lib/themes/rose-pine.json +9 -0
- package/lib/themes/solarized-dark.json +9 -0
- package/lib/themes/solarized-light.json +9 -0
- package/lib/themes/tokyo-night-light.json +9 -0
- package/lib/themes/tokyo-night.json +9 -0
- package/package.json +2 -1
package/lib/public/app.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { showToast, copyToClipboard, escapeHtml } from './modules/utils.js';
|
|
2
2
|
import { refreshIcons, iconHtml, randomThinkingVerb } from './modules/icons.js';
|
|
3
3
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidModal } from './modules/markdown.js';
|
|
4
|
-
import { initSidebar, renderSessionList, handleSearchResults, updatePageTitle, getActiveSearchQuery, buildSearchTimeline, removeSearchTimeline } from './modules/sidebar.js';
|
|
4
|
+
import { initSidebar, renderSessionList, handleSearchResults, updatePageTitle, getActiveSearchQuery, buildSearchTimeline, removeSearchTimeline, populateCliSessionList } from './modules/sidebar.js';
|
|
5
5
|
import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid } from './modules/rewind.js';
|
|
6
6
|
import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
|
|
7
7
|
import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands } from './modules/input.js';
|
|
8
8
|
import { initQrCode } from './modules/qrcode.js';
|
|
9
9
|
import { initFileBrowser, loadRootDirectory, refreshTree, handleFsList, handleFsRead, handleDirChanged, refreshIfOpen, handleFileChanged, handleFileHistory, handleGitDiff, handleFileAt, getPendingNavigate, closeFileViewer } from './modules/filebrowser.js';
|
|
10
10
|
import { initTerminal, openTerminal, closeTerminal, resetTerminals, handleTermList, handleTermCreated, handleTermOutput, handleTermExited, handleTermClosed } from './modules/terminal.js';
|
|
11
|
-
import {
|
|
11
|
+
import { initTheme, getThemeColor, getComputedVar, onThemeChange } from './modules/theme.js';
|
|
12
|
+
import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionResolved, markPermissionCancelled, renderPlanBanner, renderPlanCard, handleTodoWrite, handleTaskCreate, handleTaskUpdate, startThinking, appendThinking, stopThinking, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, addTurnMeta, enableMainInput, getTools, getPlanContent, setPlanContent, isPlanFilePath, getTodoTools, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, closeToolGroup, removeToolFromGroup } from './modules/tools.js';
|
|
12
13
|
|
|
13
14
|
// --- Base path for multi-project routing ---
|
|
14
15
|
var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
|
|
@@ -21,7 +22,8 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
21
22
|
var inputEl = $("input");
|
|
22
23
|
var sendBtn = $("send-btn");
|
|
23
24
|
var statusDot = $("status-dot");
|
|
24
|
-
var
|
|
25
|
+
var headerTitleEl = $("header-title");
|
|
26
|
+
var headerRenameBtn = $("header-rename-btn");
|
|
25
27
|
var slashMenu = $("slash-menu");
|
|
26
28
|
var sidebar = $("sidebar");
|
|
27
29
|
var sidebarOverlay = $("sidebar-overlay");
|
|
@@ -36,25 +38,21 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
36
38
|
var connectVerbEl = $("connect-verb");
|
|
37
39
|
var connectStatusEl = $("connect-status");
|
|
38
40
|
|
|
39
|
-
// --- Project
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
43
|
-
var projectDropdownList = $("project-dropdown-list");
|
|
44
|
-
var projectSwitcherName = $("project-switcher-name");
|
|
45
|
-
var projectSwitcherCount = $("project-switcher-count");
|
|
41
|
+
// --- Project List ---
|
|
42
|
+
var projectListSection = $("project-list-section");
|
|
43
|
+
var projectListEl = $("project-list");
|
|
44
|
+
var projectListAddBtn = $("project-list-add");
|
|
46
45
|
var projectHint = $("project-hint");
|
|
47
46
|
var projectHintDismiss = $("project-hint-dismiss");
|
|
48
47
|
var cachedProjects = [];
|
|
49
48
|
var cachedProjectCount = 0;
|
|
50
49
|
var currentSlug = slugMatch ? slugMatch[1] : null;
|
|
51
50
|
|
|
52
|
-
function
|
|
51
|
+
function updateProjectList(msg) {
|
|
53
52
|
if (typeof msg.projectCount === "number") cachedProjectCount = msg.projectCount;
|
|
54
53
|
if (msg.projects) cachedProjects = msg.projects;
|
|
55
54
|
var count = cachedProjectCount || 0;
|
|
56
|
-
|
|
57
|
-
projectSwitcherCount.textContent = count ? count + (count === 1 ? " project" : " projects") : "";
|
|
55
|
+
renderProjectList();
|
|
58
56
|
if (count === 1 && projectHint) {
|
|
59
57
|
try {
|
|
60
58
|
if (!localStorage.getItem("claude-relay-project-hint-dismissed")) {
|
|
@@ -66,14 +64,15 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
69
|
-
function
|
|
70
|
-
|
|
67
|
+
function renderProjectList() {
|
|
68
|
+
if (!projectListEl) return;
|
|
69
|
+
projectListEl.innerHTML = "";
|
|
71
70
|
for (var i = 0; i < cachedProjects.length; i++) {
|
|
72
71
|
var p = cachedProjects[i];
|
|
73
72
|
var isCurrent = p.slug === currentSlug;
|
|
74
73
|
var displayName = p.title || p.project;
|
|
75
74
|
var item = document.createElement("a");
|
|
76
|
-
item.className = "project-
|
|
75
|
+
item.className = "project-list-item" + (isCurrent ? " current" : "");
|
|
77
76
|
item.href = "/p/" + p.slug + "/";
|
|
78
77
|
|
|
79
78
|
var indicator = document.createElement("span");
|
|
@@ -85,60 +84,35 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
85
84
|
name.textContent = displayName;
|
|
86
85
|
item.appendChild(name);
|
|
87
86
|
|
|
88
|
-
var
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
var removeBtn = document.createElement("button");
|
|
88
|
+
removeBtn.className = "pd-remove";
|
|
89
|
+
removeBtn.type = "button";
|
|
90
|
+
removeBtn.title = "Remove project";
|
|
91
|
+
removeBtn.innerHTML = '<i data-lucide="trash-2"></i>';
|
|
92
|
+
removeBtn.dataset.slug = p.slug;
|
|
93
|
+
removeBtn.dataset.name = displayName;
|
|
94
|
+
removeBtn.addEventListener("click", function (e) {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
var s = this.dataset.slug;
|
|
98
|
+
var n = this.dataset.name;
|
|
99
|
+
confirmRemoveProject(s, n);
|
|
100
|
+
});
|
|
101
|
+
item.appendChild(removeBtn);
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
var addBtn = document.getElementById("project-dropdown-add");
|
|
98
|
-
if (addBtn) {
|
|
99
|
-
addBtn.onclick = function () {
|
|
100
|
-
closeProjectDropdown();
|
|
101
|
-
var hint = document.getElementById("project-hint");
|
|
102
|
-
if (hint) {
|
|
103
|
-
hint.classList.remove("hidden");
|
|
104
|
-
try { localStorage.removeItem("claude-relay-project-hint-dismissed"); } catch (e) {}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
103
|
+
projectListEl.appendChild(item);
|
|
107
104
|
}
|
|
108
105
|
refreshIcons();
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
projectDropdown.classList.remove("hidden");
|
|
115
|
-
projectSwitcher.classList.add("open");
|
|
116
|
-
} else {
|
|
117
|
-
closeProjectDropdown();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function closeProjectDropdown() {
|
|
122
|
-
projectDropdown.classList.add("hidden");
|
|
123
|
-
projectSwitcher.classList.remove("open");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (projectSwitcherBtn) {
|
|
127
|
-
projectSwitcherBtn.addEventListener("click", function (e) {
|
|
128
|
-
e.stopPropagation();
|
|
129
|
-
toggleProjectDropdown();
|
|
108
|
+
if (projectListAddBtn) {
|
|
109
|
+
projectListAddBtn.addEventListener("click", function () {
|
|
110
|
+
openAddProjectModal();
|
|
130
111
|
});
|
|
131
112
|
}
|
|
132
113
|
|
|
133
|
-
document.addEventListener("click", function (e) {
|
|
134
|
-
if (projectSwitcher && !projectSwitcher.contains(e.target)) {
|
|
135
|
-
closeProjectDropdown();
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
114
|
document.addEventListener("keydown", function (e) {
|
|
140
115
|
if (e.key === "Escape") {
|
|
141
|
-
closeProjectDropdown();
|
|
142
116
|
closeImageModal();
|
|
143
117
|
}
|
|
144
118
|
});
|
|
@@ -217,6 +191,45 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
217
191
|
|
|
218
192
|
// builtinCommands -> modules/input.js
|
|
219
193
|
|
|
194
|
+
// --- Header session rename ---
|
|
195
|
+
if (headerRenameBtn) {
|
|
196
|
+
headerRenameBtn.addEventListener("click", function () {
|
|
197
|
+
if (!activeSessionId) return;
|
|
198
|
+
var currentText = headerTitleEl.textContent;
|
|
199
|
+
var input = document.createElement("input");
|
|
200
|
+
input.type = "text";
|
|
201
|
+
input.className = "header-rename-input";
|
|
202
|
+
input.value = currentText;
|
|
203
|
+
headerTitleEl.style.display = "none";
|
|
204
|
+
headerRenameBtn.style.display = "none";
|
|
205
|
+
headerTitleEl.parentNode.insertBefore(input, headerTitleEl.nextSibling);
|
|
206
|
+
input.focus();
|
|
207
|
+
input.select();
|
|
208
|
+
|
|
209
|
+
function commit() {
|
|
210
|
+
var newTitle = input.value.trim();
|
|
211
|
+
if (newTitle && newTitle !== currentText && ws && ws.readyState === 1) {
|
|
212
|
+
ws.send(JSON.stringify({ type: "rename_session", id: activeSessionId, title: newTitle }));
|
|
213
|
+
headerTitleEl.textContent = newTitle;
|
|
214
|
+
}
|
|
215
|
+
input.remove();
|
|
216
|
+
headerTitleEl.style.display = "";
|
|
217
|
+
headerRenameBtn.style.display = "";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
input.addEventListener("keydown", function (e) {
|
|
221
|
+
if (e.key === "Enter") { e.preventDefault(); commit(); }
|
|
222
|
+
if (e.key === "Escape") {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
input.remove();
|
|
225
|
+
headerTitleEl.style.display = "";
|
|
226
|
+
headerRenameBtn.style.display = "";
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
input.addEventListener("blur", commit);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
220
233
|
// --- Confirm modal ---
|
|
221
234
|
var confirmModal = $("confirm-modal");
|
|
222
235
|
var confirmText = $("confirm-text");
|
|
@@ -267,6 +280,9 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
267
280
|
addSystemMessage: addSystemMessage,
|
|
268
281
|
});
|
|
269
282
|
|
|
283
|
+
// --- Theme (module) ---
|
|
284
|
+
initTheme();
|
|
285
|
+
|
|
270
286
|
// --- Sidebar (module) ---
|
|
271
287
|
initSidebar({
|
|
272
288
|
$: $,
|
|
@@ -282,6 +298,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
282
298
|
hamburgerBtn: hamburgerBtn,
|
|
283
299
|
newSessionBtn: newSessionBtn,
|
|
284
300
|
resumeSessionBtn: resumeSessionBtn,
|
|
301
|
+
headerTitleEl: headerTitleEl,
|
|
285
302
|
showConfirm: showConfirm,
|
|
286
303
|
onFilesTabOpen: function () { loadRootDirectory(); },
|
|
287
304
|
});
|
|
@@ -339,9 +356,9 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
339
356
|
];
|
|
340
357
|
|
|
341
358
|
var CELL = 12;
|
|
342
|
-
var accent = "
|
|
343
|
-
var eye = "
|
|
344
|
-
var antenna = "
|
|
359
|
+
var accent = getThemeColor("base09");
|
|
360
|
+
var eye = getThemeColor("base00");
|
|
361
|
+
var antenna = getThemeColor("base06");
|
|
345
362
|
|
|
346
363
|
for (var r = 0; r < grid.length; r++) {
|
|
347
364
|
for (var c = 0; c < grid[r].length; c++) {
|
|
@@ -359,6 +376,23 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
359
376
|
}
|
|
360
377
|
})();
|
|
361
378
|
|
|
379
|
+
// Update pixel mascot colors when theme changes
|
|
380
|
+
onThemeChange(function () {
|
|
381
|
+
var newAccent = getThemeColor("base09");
|
|
382
|
+
var newEye = getThemeColor("base00");
|
|
383
|
+
var newAntenna = getThemeColor("base06");
|
|
384
|
+
for (var i = 0; i < pixelBlocks.length; i++) {
|
|
385
|
+
var el = pixelBlocks[i];
|
|
386
|
+
var bg = el.style.background;
|
|
387
|
+
if (bg === accent || bg === newAccent) el.style.background = newAccent;
|
|
388
|
+
else if (bg === eye || bg === newEye) el.style.background = newEye;
|
|
389
|
+
else if (bg === antenna || bg === newAntenna) el.style.background = newAntenna;
|
|
390
|
+
}
|
|
391
|
+
accent = newAccent;
|
|
392
|
+
eye = newEye;
|
|
393
|
+
antenna = newAntenna;
|
|
394
|
+
});
|
|
395
|
+
|
|
362
396
|
function pixelScatter() {
|
|
363
397
|
stopSpark();
|
|
364
398
|
for (var i = 0; i < pixelBlocks.length; i++) {
|
|
@@ -398,7 +432,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
398
432
|
antennaBlocks[i].style.background = "#FFF";
|
|
399
433
|
antennaBlocks[i].style.boxShadow = "0 0 6px 2px rgba(255,255,255,0.6)";
|
|
400
434
|
} else {
|
|
401
|
-
antennaBlocks[i].style.background =
|
|
435
|
+
antennaBlocks[i].style.background = antenna;
|
|
402
436
|
antennaBlocks[i].style.boxShadow = "none";
|
|
403
437
|
}
|
|
404
438
|
}
|
|
@@ -413,7 +447,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
413
447
|
sparkTimer = null;
|
|
414
448
|
}
|
|
415
449
|
for (var i = 0; i < antennaBlocks.length; i++) {
|
|
416
|
-
antennaBlocks[i].style.background =
|
|
450
|
+
antennaBlocks[i].style.background = antenna;
|
|
417
451
|
antennaBlocks[i].style.boxShadow = "none";
|
|
418
452
|
}
|
|
419
453
|
}
|
|
@@ -458,7 +492,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
458
492
|
if (xhr.status === 200) faviconSvg = xhr.responseText;
|
|
459
493
|
else return;
|
|
460
494
|
}
|
|
461
|
-
var svg = faviconSvg.replace(/fill="#57AB5A"
|
|
495
|
+
var svg = faviconSvg.replace(/fill="#57AB5A"/g, 'fill="' + bgColor + '"');
|
|
462
496
|
faviconLink.href = "data:image/svg+xml," + encodeURIComponent(svg);
|
|
463
497
|
}
|
|
464
498
|
|
|
@@ -484,9 +518,9 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
484
518
|
ioTimer = setTimeout(function () { statusDot.classList.remove("io"); }, 60);
|
|
485
519
|
|
|
486
520
|
// Blink favicon: dim then restore
|
|
487
|
-
updateFavicon("
|
|
521
|
+
updateFavicon(getComputedVar("--sidebar-bg"));
|
|
488
522
|
clearTimeout(faviconIoTimer);
|
|
489
|
-
faviconIoTimer = setTimeout(function () { updateFavicon("
|
|
523
|
+
faviconIoTimer = setTimeout(function () { updateFavicon(getComputedVar("--success")); }, 60);
|
|
490
524
|
}
|
|
491
525
|
|
|
492
526
|
// --- Urgent favicon blink (permission / ask user) ---
|
|
@@ -495,7 +529,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
495
529
|
function startUrgentBlink() {
|
|
496
530
|
if (urgentBlinkTimer) return;
|
|
497
531
|
savedTitle = document.title;
|
|
498
|
-
var colors = ["
|
|
532
|
+
var colors = [getComputedVar("--accent"), getComputedVar("--success"), getComputedVar("--accent"), getComputedVar("--text"), getComputedVar("--accent"), getComputedVar("--success")];
|
|
499
533
|
var tick = 0;
|
|
500
534
|
urgentBlinkTimer = setInterval(function () {
|
|
501
535
|
updateFavicon(colors[tick % colors.length]);
|
|
@@ -507,7 +541,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
507
541
|
if (!urgentBlinkTimer) return;
|
|
508
542
|
clearInterval(urgentBlinkTimer);
|
|
509
543
|
urgentBlinkTimer = null;
|
|
510
|
-
updateFavicon("
|
|
544
|
+
updateFavicon(getComputedVar("--success"));
|
|
511
545
|
if (savedTitle) document.title = savedTitle;
|
|
512
546
|
savedTitle = null;
|
|
513
547
|
}
|
|
@@ -522,12 +556,12 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
522
556
|
setSendBtnMode("send");
|
|
523
557
|
connectOverlay.classList.add("hidden");
|
|
524
558
|
stopVerbCycle();
|
|
525
|
-
updateFavicon("
|
|
559
|
+
updateFavicon(getComputedVar("--success"));
|
|
526
560
|
} else if (status === "processing") {
|
|
527
561
|
statusDot.classList.add("processing");
|
|
528
562
|
processing = true;
|
|
529
563
|
setSendBtnMode("stop");
|
|
530
|
-
updateFavicon("
|
|
564
|
+
updateFavicon(getComputedVar("--success"));
|
|
531
565
|
} else {
|
|
532
566
|
connected = false;
|
|
533
567
|
sendBtn.disabled = true;
|
|
@@ -535,7 +569,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
535
569
|
connectStatusEl.textContent = "Reconnecting...";
|
|
536
570
|
startVerbCycle();
|
|
537
571
|
startPixelAnim();
|
|
538
|
-
updateFavicon("
|
|
572
|
+
updateFavicon(getComputedVar("--error"));
|
|
539
573
|
}
|
|
540
574
|
}
|
|
541
575
|
|
|
@@ -1104,6 +1138,8 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1104
1138
|
if (currentFullText) {
|
|
1105
1139
|
addCopyHandler(currentMsgEl, currentFullText);
|
|
1106
1140
|
}
|
|
1141
|
+
// Assistant text appeared, so break the current tool group
|
|
1142
|
+
closeToolGroup();
|
|
1107
1143
|
}
|
|
1108
1144
|
currentMsgEl = null;
|
|
1109
1145
|
currentFullText = "";
|
|
@@ -1257,6 +1293,16 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1257
1293
|
break;
|
|
1258
1294
|
|
|
1259
1295
|
case "history_done":
|
|
1296
|
+
// Render + finalize any incomplete turn from the replayed history
|
|
1297
|
+
if (currentMsgEl && currentFullText) {
|
|
1298
|
+
var replayContentEl = currentMsgEl.querySelector(".md-content");
|
|
1299
|
+
if (replayContentEl) {
|
|
1300
|
+
replayContentEl.innerHTML = renderMarkdown(currentFullText);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
markAllToolsDone();
|
|
1304
|
+
finalizeAssistantBlock();
|
|
1305
|
+
scrollToBottom();
|
|
1260
1306
|
var pendingQuery = getActiveSearchQuery();
|
|
1261
1307
|
if (pendingQuery) {
|
|
1262
1308
|
requestAnimationFrame(function() { buildSearchTimeline(pendingQuery); });
|
|
@@ -1271,6 +1317,9 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1271
1317
|
target = messagesEl.querySelector('[data-uuid="' + nav.assistantUuid + '"]');
|
|
1272
1318
|
}
|
|
1273
1319
|
if (target) {
|
|
1320
|
+
// Auto-expand parent tool group if collapsed
|
|
1321
|
+
var parentGroup = target.closest(".tool-group");
|
|
1322
|
+
if (parentGroup) parentGroup.classList.remove("collapsed");
|
|
1274
1323
|
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1275
1324
|
target.classList.add("message-blink");
|
|
1276
1325
|
setTimeout(function() { target.classList.remove("message-blink"); }, 2000);
|
|
@@ -1282,7 +1331,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1282
1331
|
case "info":
|
|
1283
1332
|
projectName = msg.project || msg.cwd;
|
|
1284
1333
|
if (msg.slug) currentSlug = msg.slug;
|
|
1285
|
-
|
|
1334
|
+
headerTitleEl.textContent = projectName;
|
|
1286
1335
|
updatePageTitle();
|
|
1287
1336
|
if (msg.version) {
|
|
1288
1337
|
var vEl = $("footer-version");
|
|
@@ -1297,7 +1346,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1297
1346
|
var spBanner = $("skip-perms-banner");
|
|
1298
1347
|
if (spBanner) spBanner.classList.remove("hidden");
|
|
1299
1348
|
}
|
|
1300
|
-
|
|
1349
|
+
updateProjectList(msg);
|
|
1301
1350
|
break;
|
|
1302
1351
|
|
|
1303
1352
|
case "update_available":
|
|
@@ -1366,6 +1415,10 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1366
1415
|
handleSearchResults(msg);
|
|
1367
1416
|
break;
|
|
1368
1417
|
|
|
1418
|
+
case "cli_session_list":
|
|
1419
|
+
populateCliSessionList(msg.sessions || []);
|
|
1420
|
+
break;
|
|
1421
|
+
|
|
1369
1422
|
case "session_switched":
|
|
1370
1423
|
// Save draft from outgoing session
|
|
1371
1424
|
if (activeSessionId && inputEl.value) {
|
|
@@ -1468,6 +1521,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1468
1521
|
if (askTool) {
|
|
1469
1522
|
if (askTool.el) askTool.el.style.display = "none";
|
|
1470
1523
|
askTool.done = true;
|
|
1524
|
+
removeToolFromGroup(msg.id);
|
|
1471
1525
|
}
|
|
1472
1526
|
renderAskUserQuestion(msg.id, msg.input);
|
|
1473
1527
|
startUrgentBlink();
|
|
@@ -1543,10 +1597,23 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1543
1597
|
scrollToBottom();
|
|
1544
1598
|
break;
|
|
1545
1599
|
|
|
1600
|
+
case "subagent_activity":
|
|
1601
|
+
updateSubagentActivity(msg.parentToolId, msg.text);
|
|
1602
|
+
break;
|
|
1603
|
+
|
|
1604
|
+
case "subagent_tool":
|
|
1605
|
+
addSubagentToolEntry(msg.parentToolId, msg.toolName, msg.toolId, msg.text);
|
|
1606
|
+
break;
|
|
1607
|
+
|
|
1608
|
+
case "subagent_done":
|
|
1609
|
+
markSubagentDone(msg.parentToolId);
|
|
1610
|
+
break;
|
|
1611
|
+
|
|
1546
1612
|
case "result":
|
|
1547
1613
|
setActivity(null);
|
|
1548
1614
|
stopThinking();
|
|
1549
1615
|
markAllToolsDone();
|
|
1616
|
+
closeToolGroup();
|
|
1550
1617
|
finalizeAssistantBlock();
|
|
1551
1618
|
addTurnMeta(msg.cost, msg.duration);
|
|
1552
1619
|
accumulateUsage(msg.cost, msg.usage);
|
|
@@ -1557,6 +1624,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1557
1624
|
setActivity(null);
|
|
1558
1625
|
stopThinking();
|
|
1559
1626
|
markAllToolsDone();
|
|
1627
|
+
closeToolGroup();
|
|
1560
1628
|
finalizeAssistantBlock();
|
|
1561
1629
|
processing = false;
|
|
1562
1630
|
setStatus("connected");
|
|
@@ -1579,7 +1647,7 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1579
1647
|
case "error":
|
|
1580
1648
|
setActivity(null);
|
|
1581
1649
|
addSystemMessage(msg.text, true);
|
|
1582
|
-
updateFavicon("
|
|
1650
|
+
updateFavicon(getComputedVar("--error"));
|
|
1583
1651
|
break;
|
|
1584
1652
|
|
|
1585
1653
|
case "rewind_preview_result":
|
|
@@ -1650,6 +1718,22 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1650
1718
|
case "process_stats":
|
|
1651
1719
|
updateStatusPanel(msg);
|
|
1652
1720
|
break;
|
|
1721
|
+
|
|
1722
|
+
case "browse_dir_result":
|
|
1723
|
+
handleBrowseDirResult(msg);
|
|
1724
|
+
break;
|
|
1725
|
+
|
|
1726
|
+
case "add_project_result":
|
|
1727
|
+
handleAddProjectResult(msg);
|
|
1728
|
+
break;
|
|
1729
|
+
|
|
1730
|
+
case "remove_project_result":
|
|
1731
|
+
handleRemoveProjectResult(msg);
|
|
1732
|
+
break;
|
|
1733
|
+
|
|
1734
|
+
case "projects_updated":
|
|
1735
|
+
updateProjectList(msg);
|
|
1736
|
+
break;
|
|
1653
1737
|
}
|
|
1654
1738
|
}
|
|
1655
1739
|
|
|
@@ -1822,6 +1906,231 @@ import { initTools, resetToolState, saveToolState, restoreToolState, renderAskUs
|
|
|
1822
1906
|
fileViewerEl: $("file-viewer"),
|
|
1823
1907
|
});
|
|
1824
1908
|
|
|
1909
|
+
// --- Remove project ---
|
|
1910
|
+
function confirmRemoveProject(slug, name) {
|
|
1911
|
+
showConfirm("Remove project \"" + name + "\"?", function () {
|
|
1912
|
+
if (ws && ws.readyState === 1) {
|
|
1913
|
+
ws.send(JSON.stringify({ type: "remove_project", slug: slug }));
|
|
1914
|
+
}
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
function handleRemoveProjectResult(msg) {
|
|
1919
|
+
if (msg.ok) {
|
|
1920
|
+
showToast("Project removed", "success");
|
|
1921
|
+
// If we removed the current project, navigate to first available
|
|
1922
|
+
if (msg.slug === currentSlug) {
|
|
1923
|
+
window.location.href = "/";
|
|
1924
|
+
}
|
|
1925
|
+
} else {
|
|
1926
|
+
showToast(msg.error || "Failed to remove project", "error");
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// --- Add project modal ---
|
|
1931
|
+
var addProjectModal = document.getElementById("add-project-modal");
|
|
1932
|
+
var addProjectInput = document.getElementById("add-project-input");
|
|
1933
|
+
var addProjectSuggestions = document.getElementById("add-project-suggestions");
|
|
1934
|
+
var addProjectError = document.getElementById("add-project-error");
|
|
1935
|
+
var addProjectOk = document.getElementById("add-project-ok");
|
|
1936
|
+
var addProjectCancel = document.getElementById("add-project-cancel");
|
|
1937
|
+
var addProjectDebounce = null;
|
|
1938
|
+
var addProjectActiveIdx = -1;
|
|
1939
|
+
|
|
1940
|
+
function openAddProjectModal() {
|
|
1941
|
+
addProjectModal.classList.remove("hidden");
|
|
1942
|
+
addProjectInput.value = "/";
|
|
1943
|
+
addProjectError.classList.add("hidden");
|
|
1944
|
+
addProjectError.textContent = "";
|
|
1945
|
+
addProjectSuggestions.classList.add("hidden");
|
|
1946
|
+
addProjectSuggestions.innerHTML = "";
|
|
1947
|
+
addProjectActiveIdx = -1;
|
|
1948
|
+
addProjectOk.disabled = false;
|
|
1949
|
+
setTimeout(function () {
|
|
1950
|
+
addProjectInput.focus();
|
|
1951
|
+
addProjectInput.setSelectionRange(1, 1);
|
|
1952
|
+
}, 50);
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
function closeAddProjectModal() {
|
|
1956
|
+
addProjectModal.classList.add("hidden");
|
|
1957
|
+
addProjectInput.value = "";
|
|
1958
|
+
addProjectSuggestions.classList.add("hidden");
|
|
1959
|
+
addProjectSuggestions.innerHTML = "";
|
|
1960
|
+
addProjectError.classList.add("hidden");
|
|
1961
|
+
addProjectActiveIdx = -1;
|
|
1962
|
+
if (addProjectDebounce) { clearTimeout(addProjectDebounce); addProjectDebounce = null; }
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
function requestBrowseDir(val) {
|
|
1966
|
+
if (!ws || ws.readyState !== 1) return;
|
|
1967
|
+
ws.send(JSON.stringify({ type: "browse_dir", path: val }));
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
function handleBrowseDirResult(msg) {
|
|
1971
|
+
addProjectSuggestions.innerHTML = "";
|
|
1972
|
+
addProjectActiveIdx = -1;
|
|
1973
|
+
if (msg.error) {
|
|
1974
|
+
addProjectSuggestions.classList.add("hidden");
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
var entries = msg.entries || [];
|
|
1978
|
+
if (entries.length === 0) {
|
|
1979
|
+
addProjectSuggestions.classList.add("hidden");
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
for (var si = 0; si < entries.length; si++) {
|
|
1983
|
+
var entry = entries[si];
|
|
1984
|
+
var item = document.createElement("div");
|
|
1985
|
+
item.className = "add-project-suggestion-item";
|
|
1986
|
+
item.dataset.path = entry.path;
|
|
1987
|
+
item.innerHTML = '<i data-lucide="folder"></i><span class="add-project-suggestion-name">' +
|
|
1988
|
+
escapeHtml(entry.name) + '</span>';
|
|
1989
|
+
item.addEventListener("click", function (e) {
|
|
1990
|
+
var p = this.dataset.path + "/";
|
|
1991
|
+
addProjectInput.value = p;
|
|
1992
|
+
addProjectInput.focus();
|
|
1993
|
+
addProjectError.classList.add("hidden");
|
|
1994
|
+
requestBrowseDir(p);
|
|
1995
|
+
});
|
|
1996
|
+
addProjectSuggestions.appendChild(item);
|
|
1997
|
+
}
|
|
1998
|
+
addProjectSuggestions.classList.remove("hidden");
|
|
1999
|
+
refreshIcons();
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
function handleAddProjectResult(msg) {
|
|
2003
|
+
if (msg.ok) {
|
|
2004
|
+
closeAddProjectModal();
|
|
2005
|
+
if (msg.existing) {
|
|
2006
|
+
showToast("Project already registered", "info");
|
|
2007
|
+
} else {
|
|
2008
|
+
showToast("Project added", "success");
|
|
2009
|
+
// Navigate to the new project
|
|
2010
|
+
if (msg.slug) {
|
|
2011
|
+
window.location.href = "/p/" + msg.slug + "/";
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
} else {
|
|
2015
|
+
addProjectError.textContent = msg.error || "Failed to add project";
|
|
2016
|
+
addProjectError.classList.remove("hidden");
|
|
2017
|
+
addProjectOk.disabled = false;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
function setActiveIdx(idx) {
|
|
2022
|
+
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
2023
|
+
addProjectActiveIdx = idx;
|
|
2024
|
+
for (var ai = 0; ai < items.length; ai++) {
|
|
2025
|
+
if (ai === idx) {
|
|
2026
|
+
items[ai].classList.add("active");
|
|
2027
|
+
items[ai].scrollIntoView({ block: "nearest" });
|
|
2028
|
+
} else {
|
|
2029
|
+
items[ai].classList.remove("active");
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
addProjectInput.addEventListener("focus", function () {
|
|
2035
|
+
var val = addProjectInput.value;
|
|
2036
|
+
if (val && addProjectSuggestions.children.length === 0) {
|
|
2037
|
+
requestBrowseDir(val);
|
|
2038
|
+
} else if (addProjectSuggestions.children.length > 0) {
|
|
2039
|
+
addProjectSuggestions.classList.remove("hidden");
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
|
|
2043
|
+
addProjectModal.querySelector(".confirm-dialog").addEventListener("click", function (e) {
|
|
2044
|
+
if (e.target === addProjectInput || addProjectInput.contains(e.target)) return;
|
|
2045
|
+
if (e.target === addProjectSuggestions || addProjectSuggestions.contains(e.target)) return;
|
|
2046
|
+
addProjectSuggestions.classList.add("hidden");
|
|
2047
|
+
addProjectActiveIdx = -1;
|
|
2048
|
+
});
|
|
2049
|
+
|
|
2050
|
+
addProjectInput.addEventListener("input", function () {
|
|
2051
|
+
var val = addProjectInput.value;
|
|
2052
|
+
addProjectError.classList.add("hidden");
|
|
2053
|
+
if (addProjectDebounce) clearTimeout(addProjectDebounce);
|
|
2054
|
+
addProjectDebounce = setTimeout(function () {
|
|
2055
|
+
requestBrowseDir(val);
|
|
2056
|
+
}, 200);
|
|
2057
|
+
});
|
|
2058
|
+
|
|
2059
|
+
addProjectInput.addEventListener("keydown", function (e) {
|
|
2060
|
+
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
2061
|
+
|
|
2062
|
+
if (e.key === "ArrowDown") {
|
|
2063
|
+
e.preventDefault();
|
|
2064
|
+
if (items.length > 0) {
|
|
2065
|
+
var next = addProjectActiveIdx < items.length - 1 ? addProjectActiveIdx + 1 : 0;
|
|
2066
|
+
setActiveIdx(next);
|
|
2067
|
+
}
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
if (e.key === "ArrowUp") {
|
|
2072
|
+
e.preventDefault();
|
|
2073
|
+
if (items.length > 0) {
|
|
2074
|
+
var prev = addProjectActiveIdx > 0 ? addProjectActiveIdx - 1 : items.length - 1;
|
|
2075
|
+
setActiveIdx(prev);
|
|
2076
|
+
}
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
if (e.key === "Tab") {
|
|
2081
|
+
e.preventDefault();
|
|
2082
|
+
var target = addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length
|
|
2083
|
+
? items[addProjectActiveIdx]
|
|
2084
|
+
: items.length > 0 ? items[0] : null;
|
|
2085
|
+
if (target) {
|
|
2086
|
+
var p = target.dataset.path + "/";
|
|
2087
|
+
addProjectInput.value = p;
|
|
2088
|
+
addProjectError.classList.add("hidden");
|
|
2089
|
+
requestBrowseDir(p);
|
|
2090
|
+
}
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
if (e.key === "Enter") {
|
|
2095
|
+
e.preventDefault();
|
|
2096
|
+
// If a suggestion is highlighted, pick it first
|
|
2097
|
+
if (addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length) {
|
|
2098
|
+
var picked = items[addProjectActiveIdx].dataset.path + "/";
|
|
2099
|
+
addProjectInput.value = picked;
|
|
2100
|
+
addProjectError.classList.add("hidden");
|
|
2101
|
+
requestBrowseDir(picked);
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
// Otherwise submit
|
|
2105
|
+
submitAddProject();
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
if (e.key === "Escape") {
|
|
2110
|
+
e.preventDefault();
|
|
2111
|
+
closeAddProjectModal();
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
|
|
2116
|
+
function submitAddProject() {
|
|
2117
|
+
var val = addProjectInput.value.replace(/\/+$/, "");
|
|
2118
|
+
if (!val) return;
|
|
2119
|
+
addProjectOk.disabled = true;
|
|
2120
|
+
addProjectError.classList.add("hidden");
|
|
2121
|
+
if (ws && ws.readyState === 1) {
|
|
2122
|
+
ws.send(JSON.stringify({ type: "add_project", path: val }));
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
addProjectOk.addEventListener("click", function () { submitAddProject(); });
|
|
2127
|
+
addProjectCancel.addEventListener("click", function () { closeAddProjectModal(); });
|
|
2128
|
+
|
|
2129
|
+
// Close on backdrop click
|
|
2130
|
+
addProjectModal.querySelector(".confirm-backdrop").addEventListener("click", function () {
|
|
2131
|
+
closeAddProjectModal();
|
|
2132
|
+
});
|
|
2133
|
+
|
|
1825
2134
|
// --- Init ---
|
|
1826
2135
|
lucide.createIcons();
|
|
1827
2136
|
startVerbCycle();
|