clay-server 2.26.0-beta.1 → 2.26.0-beta.11
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 +243 -95
- 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 +296 -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 +328 -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
|
@@ -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
|
}
|
|
@@ -10,6 +10,7 @@ import { closeArchive } from './sticky-notes.js';
|
|
|
10
10
|
import { closeScheduler } from './scheduler.js';
|
|
11
11
|
import { openSearch as openSessionSearch } from './session-search.js';
|
|
12
12
|
import { openCommandPalette } from './command-palette.js';
|
|
13
|
+
import { getMateSessions } from './mate-sidebar.js';
|
|
13
14
|
|
|
14
15
|
var ctx;
|
|
15
16
|
|
|
@@ -351,6 +352,7 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
351
352
|
|
|
352
353
|
var loopName = (children[0].loop && children[0].loop.name) || "Ralph Loop";
|
|
353
354
|
var isRalph = children[0].loop && children[0].loop.source === "ralph";
|
|
355
|
+
var isDebate = children[0].loop && children[0].loop.source === "debate";
|
|
354
356
|
var isCrafting = false;
|
|
355
357
|
for (var j = 0; j < children.length; j++) {
|
|
356
358
|
if (children[j].loop && children[j].loop.role === "crafting") isCrafting = true;
|
|
@@ -363,7 +365,10 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
363
365
|
|
|
364
366
|
// Group header row
|
|
365
367
|
var el = document.createElement("div");
|
|
366
|
-
|
|
368
|
+
var groupClass = "session-loop-group" + (hasActive ? " active" : "") + (expanded ? " expanded" : "");
|
|
369
|
+
if (isDebate) groupClass += " debate";
|
|
370
|
+
else if (!isRalph) groupClass += " scheduled";
|
|
371
|
+
el.className = groupClass;
|
|
367
372
|
el.dataset.loopId = loopId;
|
|
368
373
|
|
|
369
374
|
var chevron = document.createElement("button");
|
|
@@ -388,14 +393,16 @@ function renderLoopGroup(loopId, children, groupKey) {
|
|
|
388
393
|
if (anyProcessing) {
|
|
389
394
|
textHtml += '<span class="session-processing"></span>';
|
|
390
395
|
}
|
|
391
|
-
var groupIcon = isRalph ? "repeat" : "calendar-clock";
|
|
392
|
-
|
|
396
|
+
var groupIcon = isDebate ? "mic" : (isRalph ? "repeat" : "calendar-clock");
|
|
397
|
+
var iconClass = isDebate ? " debate" : (isRalph ? "" : " scheduled");
|
|
398
|
+
textHtml += '<span class="session-loop-icon' + iconClass + '">' + iconHtml(groupIcon) + '</span>';
|
|
393
399
|
textHtml += '<span class="session-loop-name">' + escapeHtml(loopName) + '</span>';
|
|
394
400
|
if (isCrafting && children.length === 1) {
|
|
395
401
|
textHtml += '<span class="session-loop-badge crafting">Crafting</span>';
|
|
396
402
|
} else {
|
|
397
403
|
var countLabel = runCount === 1 ? children.length : runCount + (runCount === 1 ? " run" : " runs");
|
|
398
|
-
|
|
404
|
+
var countClass = isDebate ? " debate" : (isRalph ? "" : " scheduled");
|
|
405
|
+
textHtml += '<span class="session-loop-count' + countClass + '">' + countLabel + '</span>';
|
|
399
406
|
}
|
|
400
407
|
textSpan.innerHTML = textHtml;
|
|
401
408
|
el.appendChild(textSpan);
|
|
@@ -538,6 +545,9 @@ function renderSessionItem(s) {
|
|
|
538
545
|
if (s.isProcessing) {
|
|
539
546
|
textHtml += '<span class="session-processing"></span>';
|
|
540
547
|
}
|
|
548
|
+
if (s.loop && s.loop.source === "debate") {
|
|
549
|
+
textHtml += '<span class="session-debate-icon" title="Debate">' + iconHtml("mic") + '</span>';
|
|
550
|
+
}
|
|
541
551
|
if (ctx.multiUser && s.sessionVisibility === "private") {
|
|
542
552
|
textHtml += '<span class="session-private-icon" title="Private session">' + iconHtml("lock") + '</span>';
|
|
543
553
|
}
|
|
@@ -598,8 +608,8 @@ export function renderSessionList(sessions) {
|
|
|
598
608
|
var normalSessions = [];
|
|
599
609
|
for (var i = 0; i < cachedSessions.length; i++) {
|
|
600
610
|
var s = cachedSessions[i];
|
|
601
|
-
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph") {
|
|
602
|
-
// Task crafting sessions live in the scheduler calendar, not the main list
|
|
611
|
+
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph" && s.loop.source !== "debate") {
|
|
612
|
+
// Task crafting sessions live in the scheduler calendar, not the main list (except debate)
|
|
603
613
|
continue;
|
|
604
614
|
} else if (s.loop && s.loop.loopId) {
|
|
605
615
|
var startedAt = s.loop.startedAt || 0;
|
|
@@ -923,7 +933,8 @@ function renderSheetSessions(listEl) {
|
|
|
923
933
|
(function (p) {
|
|
924
934
|
var chip = document.createElement("button");
|
|
925
935
|
chip.className = "mobile-chat-chip";
|
|
926
|
-
|
|
936
|
+
var isDmActive = document.body.classList.contains("mate-dm-active");
|
|
937
|
+
if (p.slug === cachedCurrentSlug && !isDmActive) chip.classList.add("active");
|
|
927
938
|
chip.dataset.type = "project";
|
|
928
939
|
chip.dataset.slug = p.slug;
|
|
929
940
|
|
|
@@ -975,6 +986,7 @@ function renderSheetSessions(listEl) {
|
|
|
975
986
|
var mp = mate.profile || {};
|
|
976
987
|
var chip = document.createElement("button");
|
|
977
988
|
chip.className = "mobile-chat-chip";
|
|
989
|
+
if (currentDmUserId === mate.id) chip.classList.add("active");
|
|
978
990
|
chip.dataset.type = "mate";
|
|
979
991
|
chip.dataset.mateId = mate.id;
|
|
980
992
|
|
|
@@ -1029,10 +1041,9 @@ function renderSheetSessions(listEl) {
|
|
|
1029
1041
|
if (type === "project") {
|
|
1030
1042
|
renderMobileSessionsInto(sessionListEl);
|
|
1031
1043
|
} else if (type === "mate") {
|
|
1032
|
-
// Mate DM:
|
|
1044
|
+
// Mate DM: open the DM and show mate actions
|
|
1033
1045
|
if (ctx.openDm) ctx.openDm(mateId);
|
|
1034
|
-
|
|
1035
|
-
return;
|
|
1046
|
+
renderMateMobileActions(sessionListEl);
|
|
1036
1047
|
}
|
|
1037
1048
|
|
|
1038
1049
|
refreshIcons();
|
|
@@ -1051,15 +1062,23 @@ function renderSheetSessions(listEl) {
|
|
|
1051
1062
|
var type = chip.dataset.type;
|
|
1052
1063
|
if (type === "project") {
|
|
1053
1064
|
var slug = chip.dataset.slug;
|
|
1054
|
-
|
|
1055
|
-
|
|
1065
|
+
var isDmNow = !!currentDmUserId;
|
|
1066
|
+
if (slug !== cachedCurrentSlug || isDmNow) {
|
|
1067
|
+
// Switch project (or exit DM back to same project)
|
|
1056
1068
|
sessionListEl.innerHTML = "";
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1069
|
+
if (slug !== cachedCurrentSlug) {
|
|
1070
|
+
var loading = document.createElement("div");
|
|
1071
|
+
loading.className = "mobile-chat-context-note";
|
|
1072
|
+
loading.textContent = "Loading sessions...";
|
|
1073
|
+
sessionListEl.appendChild(loading);
|
|
1074
|
+
}
|
|
1061
1075
|
if (ctx.switchProject) ctx.switchProject(slug);
|
|
1062
|
-
|
|
1076
|
+
if (!isDmNow || slug !== cachedCurrentSlug) {
|
|
1077
|
+
// renderSessionList will be called by WS, which calls refreshMobileChatSheet
|
|
1078
|
+
} else {
|
|
1079
|
+
// Exited DM, same project - render sessions now
|
|
1080
|
+
renderSessionsForContext("project", slug, null);
|
|
1081
|
+
}
|
|
1063
1082
|
} else {
|
|
1064
1083
|
renderSessionsForContext("project", slug, null);
|
|
1065
1084
|
}
|
|
@@ -1073,8 +1092,12 @@ function renderSheetSessions(listEl) {
|
|
|
1073
1092
|
// Track that chat sheet is open
|
|
1074
1093
|
mobileChatSheetOpen = true;
|
|
1075
1094
|
|
|
1076
|
-
// --- Initial render:
|
|
1077
|
-
|
|
1095
|
+
// --- Initial render: show mate actions if DM active, otherwise project sessions ---
|
|
1096
|
+
if (currentDmUserId) {
|
|
1097
|
+
renderSessionsForContext("mate", null, currentDmUserId);
|
|
1098
|
+
} else {
|
|
1099
|
+
renderSessionsForContext("project", cachedCurrentSlug, null);
|
|
1100
|
+
}
|
|
1078
1101
|
}
|
|
1079
1102
|
|
|
1080
1103
|
// Helper: create a mobile session item element
|
|
@@ -1350,6 +1373,54 @@ function createMobileLoopGroup(loopId, children, groupKey) {
|
|
|
1350
1373
|
return wrapper;
|
|
1351
1374
|
}
|
|
1352
1375
|
|
|
1376
|
+
function renderMateMobileActions(container) {
|
|
1377
|
+
var newSessionBtn = document.createElement("button");
|
|
1378
|
+
newSessionBtn.className = "mobile-session-new";
|
|
1379
|
+
newSessionBtn.innerHTML = '<i data-lucide="plus" style="width:16px;height:16px"></i> New session';
|
|
1380
|
+
newSessionBtn.addEventListener("click", function () {
|
|
1381
|
+
if (ctx.ws && ctx.connected) {
|
|
1382
|
+
ctx.ws.send(JSON.stringify({ type: "new_session" }));
|
|
1383
|
+
}
|
|
1384
|
+
closeMobileSheet();
|
|
1385
|
+
});
|
|
1386
|
+
container.appendChild(newSessionBtn);
|
|
1387
|
+
|
|
1388
|
+
var debateBtn = document.createElement("button");
|
|
1389
|
+
debateBtn.className = "mobile-session-new";
|
|
1390
|
+
debateBtn.innerHTML = '<i data-lucide="mic" style="width:16px;height:16px"></i> New debate';
|
|
1391
|
+
debateBtn.addEventListener("click", function () {
|
|
1392
|
+
closeMobileSheet();
|
|
1393
|
+
var targetBtn = document.getElementById("mate-debate-btn");
|
|
1394
|
+
if (targetBtn) setTimeout(function () { targetBtn.click(); }, 250);
|
|
1395
|
+
});
|
|
1396
|
+
container.appendChild(debateBtn);
|
|
1397
|
+
|
|
1398
|
+
// Render mate session list
|
|
1399
|
+
var mateSessions = getMateSessions();
|
|
1400
|
+
if (mateSessions.length > 0) {
|
|
1401
|
+
var sorted = mateSessions.slice().sort(function (a, b) {
|
|
1402
|
+
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
var currentGroup = "";
|
|
1406
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
1407
|
+
var s = sorted[i];
|
|
1408
|
+
var group = getDateGroup(s.lastActivity || 0);
|
|
1409
|
+
if (group !== currentGroup) {
|
|
1410
|
+
currentGroup = group;
|
|
1411
|
+
var header = document.createElement("div");
|
|
1412
|
+
header.className = "mobile-sheet-group";
|
|
1413
|
+
header.textContent = group;
|
|
1414
|
+
container.appendChild(header);
|
|
1415
|
+
}
|
|
1416
|
+
var mateItem = createMobileSessionItem(s);
|
|
1417
|
+
container.appendChild(mateItem);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
refreshIcons();
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1353
1424
|
// Helper: render sorted sessions into a container with date groups (with loop session grouping)
|
|
1354
1425
|
function renderMobileSessionsInto(container) {
|
|
1355
1426
|
var newBtn = document.createElement("button");
|
|
@@ -1368,7 +1439,7 @@ function renderMobileSessionsInto(container) {
|
|
|
1368
1439
|
var normalSessions = [];
|
|
1369
1440
|
for (var i = 0; i < cachedSessions.length; i++) {
|
|
1370
1441
|
var s = cachedSessions[i];
|
|
1371
|
-
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph") {
|
|
1442
|
+
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph" && s.loop.source !== "debate") {
|
|
1372
1443
|
continue;
|
|
1373
1444
|
} else if (s.loop && s.loop.loopId) {
|
|
1374
1445
|
var startedAt = s.loop.startedAt || 0;
|
|
@@ -1424,7 +1495,7 @@ function renderMobileSessionsInto(container) {
|
|
|
1424
1495
|
}
|
|
1425
1496
|
|
|
1426
1497
|
// Refresh mobile chat sheet when session data updates (called from renderSessionList)
|
|
1427
|
-
function refreshMobileChatSheet() {
|
|
1498
|
+
export function refreshMobileChatSheet() {
|
|
1428
1499
|
if (!mobileChatSheetOpen) return;
|
|
1429
1500
|
var sheet = document.getElementById("mobile-sheet");
|
|
1430
1501
|
if (!sheet || sheet.classList.contains("hidden")) {
|
|
@@ -1441,7 +1512,10 @@ function refreshMobileChatSheet() {
|
|
|
1441
1512
|
chip.classList.remove("active");
|
|
1442
1513
|
|
|
1443
1514
|
// Update active state
|
|
1444
|
-
|
|
1515
|
+
var isDmActive = !!currentDmUserId;
|
|
1516
|
+
if (chip.dataset.type === "project" && chip.dataset.slug === cachedCurrentSlug && !isDmActive) {
|
|
1517
|
+
chip.classList.add("active");
|
|
1518
|
+
} else if (chip.dataset.type === "mate" && chip.dataset.mateId === currentDmUserId) {
|
|
1445
1519
|
chip.classList.add("active");
|
|
1446
1520
|
}
|
|
1447
1521
|
|
|
@@ -1461,9 +1535,13 @@ function refreshMobileChatSheet() {
|
|
|
1461
1535
|
}
|
|
1462
1536
|
}
|
|
1463
1537
|
|
|
1464
|
-
// Re-render sessions for current
|
|
1538
|
+
// Re-render sessions for current context
|
|
1465
1539
|
sessionListEl.innerHTML = "";
|
|
1466
|
-
|
|
1540
|
+
if (currentDmUserId) {
|
|
1541
|
+
renderMateMobileActions(sessionListEl);
|
|
1542
|
+
} else {
|
|
1543
|
+
renderMobileSessionsInto(sessionListEl);
|
|
1544
|
+
}
|
|
1467
1545
|
|
|
1468
1546
|
refreshIcons();
|
|
1469
1547
|
}
|
|
@@ -1619,8 +1697,7 @@ function renderSheetTools(listEl) {
|
|
|
1619
1697
|
{ icon: "book-open", label: "Knowledge", action: "mate-knowledge" },
|
|
1620
1698
|
{ icon: "sticky-note", label: "Sticky Notes", action: "mate-sticky" },
|
|
1621
1699
|
{ icon: "puzzle", label: "Skills", action: "mate-skills" },
|
|
1622
|
-
{ icon: "calendar-clock", label: "Scheduled Tasks", action: "mate-scheduler" }
|
|
1623
|
-
{ icon: "mic", label: "Debate", action: "mate-debate" }
|
|
1700
|
+
{ icon: "calendar-clock", label: "Scheduled Tasks", action: "mate-scheduler" }
|
|
1624
1701
|
] : [
|
|
1625
1702
|
{ icon: "folder-tree", label: "Files", action: "files" },
|
|
1626
1703
|
{ icon: "square-terminal", label: "Terminal", action: "terminal" },
|
|
@@ -3946,8 +4023,8 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
|
|
|
3946
4023
|
// All other users
|
|
3947
4024
|
var allOthers = cachedAllUsers.filter(function (u) { return u.id !== myUserId; });
|
|
3948
4025
|
|
|
3949
|
-
// Hide section if no other users
|
|
3950
|
-
if (allOthers.length === 0) {
|
|
4026
|
+
// Hide section if no other users and no mates
|
|
4027
|
+
if (allOthers.length === 0 && cachedMates.length === 0) {
|
|
3951
4028
|
container.innerHTML = "";
|
|
3952
4029
|
container.classList.add("hidden");
|
|
3953
4030
|
return;
|
|
@@ -4062,6 +4139,8 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
|
|
|
4062
4139
|
avatar.className = "icon-strip-user-avatar" + (mate.primary ? " icon-strip-primary-mate" : "");
|
|
4063
4140
|
avatar.src = mateAvatarUrl(mate, 34);
|
|
4064
4141
|
avatar.alt = mp.displayName || mate.name || "Mate";
|
|
4142
|
+
var mateColor = (mp.avatarColor) || mate.avatarColor || "#7c3aed";
|
|
4143
|
+
avatar.style.background = mateColor + "30";
|
|
4065
4144
|
el.appendChild(avatar);
|
|
4066
4145
|
|
|
4067
4146
|
// Processing status dot (IO blink)
|
|
@@ -653,6 +653,14 @@ export function handleTermOutput(msg) {
|
|
|
653
653
|
}
|
|
654
654
|
}
|
|
655
655
|
|
|
656
|
+
export function handleTermResized(msg) {
|
|
657
|
+
if (!msg.id) return;
|
|
658
|
+
var tab = tabs.get(msg.id);
|
|
659
|
+
if (tab && tab.xterm && msg.cols > 0 && msg.rows > 0) {
|
|
660
|
+
tab.xterm.resize(msg.cols, msg.rows);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
656
664
|
export function handleTermExited(msg) {
|
|
657
665
|
if (!msg.id) return;
|
|
658
666
|
var tab = tabs.get(msg.id);
|
|
@@ -155,7 +155,8 @@ function computeVars(theme) {
|
|
|
155
155
|
"--code-bg": isLight ? darken(b.base00, 0.03) : darken(b.base00, 0.15),
|
|
156
156
|
"--border": b.base02,
|
|
157
157
|
"--border-subtle": mixColors(b.base00, b.base02, 0.6),
|
|
158
|
-
"--input-bg": mixColors(b.base01, b.base02, 0.5),
|
|
158
|
+
"--input-bg": isLight ? darken(b.base00, 0.04) : mixColors(b.base01, b.base02, 0.5),
|
|
159
|
+
"--ask-mate-bg": isLight ? mixColors("#ffffff", darken(b.base00, 0.04), 0.6) : mixColors(b.base00, mixColors(b.base01, b.base02, 0.5), 0.6),
|
|
159
160
|
"--user-bubble": isLight ? darken(b.base01, 0.03) : mixColors(b.base01, b.base02, 0.3),
|
|
160
161
|
"--error": b.base08,
|
|
161
162
|
"--success": b.base0B,
|