clay-server 2.26.0-beta.8 → 2.26.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/lib/browser-mcp-server.js +4 -4
- package/lib/daemon.js +1 -1
- package/lib/debate-mcp-server.js +94 -0
- package/lib/mates.js +12 -24
- package/lib/project-debate.js +304 -166
- package/lib/project-mate-interaction.js +10 -5
- package/lib/project.js +128 -39
- package/lib/public/app.js +452 -127
- package/lib/public/css/debate.css +230 -2
- package/lib/public/css/filebrowser.css +41 -4
- package/lib/public/css/icon-strip.css +10 -10
- package/lib/public/css/input.css +107 -19
- package/lib/public/css/mates.css +56 -57
- package/lib/public/css/mention.css +7 -4
- package/lib/public/css/messages.css +17 -0
- package/lib/public/css/mobile-nav.css +3 -1
- package/lib/public/css/rewind.css +17 -4
- package/lib/public/index.html +23 -15
- package/lib/public/modules/context-sources.js +21 -6
- package/lib/public/modules/debate.js +298 -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/sidebar.js +105 -26
- package/lib/public/modules/terminal.js +62 -6
- package/lib/public/modules/theme.js +2 -1
- package/lib/public/modules/tools.js +47 -23
- package/lib/sdk-bridge.js +134 -21
- package/lib/sessions.js +2 -2
- package/package.json +1 -1
|
@@ -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);
|
|
@@ -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" },
|
|
@@ -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)
|
|
@@ -352,6 +352,16 @@ function createXtermForTab(tab) {
|
|
|
352
352
|
|
|
353
353
|
xterm.open(bodyEl);
|
|
354
354
|
|
|
355
|
+
// WebGL addon: pixel-perfect rendering (eliminates gaps in block characters)
|
|
356
|
+
// Must be loaded after xterm.open() so the rendering context is available.
|
|
357
|
+
if (typeof WebglAddon !== "undefined") {
|
|
358
|
+
try {
|
|
359
|
+
xterm.loadAddon(new WebglAddon.WebglAddon());
|
|
360
|
+
} catch (e) {
|
|
361
|
+
// WebGL not available, fall back to DOM renderer
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
355
365
|
// Route input to server
|
|
356
366
|
xterm.onData(function (data) {
|
|
357
367
|
if (ctx.ws && ctx.connected) {
|
|
@@ -481,14 +491,14 @@ function renderTabBar() {
|
|
|
481
491
|
startRenameTab(t, label);
|
|
482
492
|
});
|
|
483
493
|
|
|
484
|
-
var
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
494
|
+
var moreBtn = document.createElement("button");
|
|
495
|
+
moreBtn.className = "terminal-tab-more";
|
|
496
|
+
moreBtn.innerHTML = '<i data-lucide="ellipsis" style="width:12px;height:12px"></i>';
|
|
497
|
+
moreBtn.addEventListener("click", function (e) {
|
|
488
498
|
e.stopPropagation();
|
|
489
|
-
|
|
499
|
+
showTabContextMenu(e, t, label);
|
|
490
500
|
});
|
|
491
|
-
el.appendChild(
|
|
501
|
+
el.appendChild(moreBtn);
|
|
492
502
|
|
|
493
503
|
el.addEventListener("click", function () {
|
|
494
504
|
if (t.id !== activeTabId) {
|
|
@@ -504,6 +514,52 @@ function renderTabBar() {
|
|
|
504
514
|
refreshIcons();
|
|
505
515
|
}
|
|
506
516
|
|
|
517
|
+
// --- Tab context menu (three-dot) ---
|
|
518
|
+
function showTabContextMenu(e, tab, labelEl) {
|
|
519
|
+
var existing = document.querySelector(".terminal-tab-ctx");
|
|
520
|
+
if (existing) existing.remove();
|
|
521
|
+
|
|
522
|
+
var menu = document.createElement("div");
|
|
523
|
+
menu.className = "terminal-tab-ctx";
|
|
524
|
+
|
|
525
|
+
var renameItem = document.createElement("button");
|
|
526
|
+
renameItem.className = "terminal-tab-ctx-item";
|
|
527
|
+
renameItem.innerHTML = '<i data-lucide="pencil" style="width:13px;height:13px"></i> Rename';
|
|
528
|
+
renameItem.addEventListener("click", function () {
|
|
529
|
+
menu.remove();
|
|
530
|
+
startRenameTab(tab, labelEl);
|
|
531
|
+
});
|
|
532
|
+
menu.appendChild(renameItem);
|
|
533
|
+
|
|
534
|
+
var closeItem = document.createElement("button");
|
|
535
|
+
closeItem.className = "terminal-tab-ctx-item terminal-tab-ctx-danger";
|
|
536
|
+
closeItem.innerHTML = '<i data-lucide="trash-2" style="width:13px;height:13px"></i> Close';
|
|
537
|
+
closeItem.addEventListener("click", function () {
|
|
538
|
+
menu.remove();
|
|
539
|
+
closeTab(tab.id);
|
|
540
|
+
});
|
|
541
|
+
menu.appendChild(closeItem);
|
|
542
|
+
|
|
543
|
+
document.body.appendChild(menu);
|
|
544
|
+
refreshIcons();
|
|
545
|
+
|
|
546
|
+
// Position near the button
|
|
547
|
+
var rect = e.currentTarget.getBoundingClientRect();
|
|
548
|
+
menu.style.top = (rect.bottom + 4) + "px";
|
|
549
|
+
menu.style.left = rect.left + "px";
|
|
550
|
+
|
|
551
|
+
// Dismiss on outside click
|
|
552
|
+
function dismiss(ev) {
|
|
553
|
+
if (!menu.contains(ev.target)) {
|
|
554
|
+
menu.remove();
|
|
555
|
+
document.removeEventListener("mousedown", dismiss, true);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
setTimeout(function () {
|
|
559
|
+
document.addEventListener("mousedown", dismiss, true);
|
|
560
|
+
}, 0);
|
|
561
|
+
}
|
|
562
|
+
|
|
507
563
|
// --- Rename tab inline ---
|
|
508
564
|
function startRenameTab(tab, labelEl) {
|
|
509
565
|
var input = document.createElement("input");
|
|
@@ -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,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { escapeHtml, copyToClipboard } from './utils.js';
|
|
2
|
-
import { iconHtml, refreshIcons
|
|
2
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
3
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks } from './markdown.js';
|
|
4
4
|
import { renderUnifiedDiff, renderSplitDiff, renderPatchDiff } from './diff.js';
|
|
5
5
|
import { openFile } from './filebrowser.js';
|
|
6
6
|
import { mateAvatarUrl } from './avatar.js';
|
|
7
|
+
import { getChatLayout } from './theme.js';
|
|
7
8
|
|
|
8
9
|
var ctx;
|
|
9
10
|
|
|
@@ -496,12 +497,17 @@ export function renderPermissionRequest(requestId, toolName, toolInput, decision
|
|
|
496
497
|
return;
|
|
497
498
|
}
|
|
498
499
|
|
|
499
|
-
// Mate DM:
|
|
500
|
-
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
501
|
-
|
|
500
|
+
// Channel layout or Mate DM: conversational "Can I ...?" style
|
|
501
|
+
if ((ctx.isMateDm && ctx.isMateDm()) || getChatLayout() === "channel") {
|
|
502
|
+
renderConversationalPermission(requestId, toolName, toolInput, mateId);
|
|
502
503
|
return;
|
|
503
504
|
}
|
|
504
505
|
|
|
506
|
+
// Bubble layout: formal "Permission Required" dialog
|
|
507
|
+
renderFormalPermission(requestId, toolName, toolInput, decisionReason);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function renderFormalPermission(requestId, toolName, toolInput, decisionReason) {
|
|
505
511
|
var container = document.createElement("div");
|
|
506
512
|
container.className = "permission-container";
|
|
507
513
|
container.dataset.requestId = requestId;
|
|
@@ -755,18 +761,40 @@ function matePermissionInfo(toolName, toolInput) {
|
|
|
755
761
|
return { verb: verb, target: target };
|
|
756
762
|
}
|
|
757
763
|
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
764
|
+
function resolvePermissionIdentity(mateId) {
|
|
765
|
+
// Mate DM: use DM target mate info
|
|
766
|
+
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
767
|
+
var name = ctx.getMateName();
|
|
768
|
+
var avatar = ctx.getMateAvatarUrl();
|
|
769
|
+
// Override if specific mateId provided (e.g. @mention)
|
|
770
|
+
if (mateId && ctx.getMateById) {
|
|
771
|
+
var mentionMate = ctx.getMateById(mateId);
|
|
772
|
+
if (mentionMate) {
|
|
773
|
+
name = (mentionMate.profile && mentionMate.profile.displayName) || mentionMate.displayName || mentionMate.name || name;
|
|
774
|
+
avatar = mateAvatarUrl(mentionMate, 36);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return { name: name, avatar: avatar };
|
|
778
|
+
}
|
|
779
|
+
// Channel with Mate mention
|
|
763
780
|
if (mateId && ctx.getMateById) {
|
|
764
|
-
var
|
|
765
|
-
if (
|
|
766
|
-
|
|
767
|
-
|
|
781
|
+
var mate = ctx.getMateById(mateId);
|
|
782
|
+
if (mate) {
|
|
783
|
+
return {
|
|
784
|
+
name: (mate.profile && mate.profile.displayName) || mate.displayName || mate.name || "Mate",
|
|
785
|
+
avatar: mateAvatarUrl(mate, 36)
|
|
786
|
+
};
|
|
768
787
|
}
|
|
769
788
|
}
|
|
789
|
+
// Project chat (Claude Code)
|
|
790
|
+
return {
|
|
791
|
+
name: "Claude Code",
|
|
792
|
+
avatar: ctx.getClaudeAvatar ? ctx.getClaudeAvatar() : ""
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function renderConversationalPermission(requestId, toolName, toolInput, mateId) {
|
|
797
|
+
var identity = resolvePermissionIdentity(mateId);
|
|
770
798
|
var info = matePermissionInfo(toolName, toolInput);
|
|
771
799
|
var askMsg = "Can I " + info.verb + (info.target ? " " + info.target : "") + "?";
|
|
772
800
|
|
|
@@ -777,7 +805,7 @@ function renderMatePermission(requestId, toolName, toolInput, mateId) {
|
|
|
777
805
|
// Avatar (left column)
|
|
778
806
|
var avi = document.createElement("img");
|
|
779
807
|
avi.className = "dm-bubble-avatar dm-bubble-avatar-mate";
|
|
780
|
-
avi.src =
|
|
808
|
+
avi.src = identity.avatar;
|
|
781
809
|
avi.alt = "";
|
|
782
810
|
container.appendChild(avi);
|
|
783
811
|
|
|
@@ -789,7 +817,7 @@ function renderMatePermission(requestId, toolName, toolInput, mateId) {
|
|
|
789
817
|
var headerRow = document.createElement("div");
|
|
790
818
|
headerRow.className = "dm-bubble-header";
|
|
791
819
|
headerRow.innerHTML =
|
|
792
|
-
'<span class="dm-bubble-name">' + escapeHtml(
|
|
820
|
+
'<span class="dm-bubble-name">' + escapeHtml(identity.name) + '</span>' +
|
|
793
821
|
'<span class="dm-bubble-time">' + String(new Date().getHours()).padStart(2, "0") + ":" + String(new Date().getMinutes()).padStart(2, "0") + '</span>';
|
|
794
822
|
content.appendChild(headerRow);
|
|
795
823
|
|
|
@@ -1409,12 +1437,11 @@ export function startThinking() {
|
|
|
1409
1437
|
var el = thinkingGroup.el;
|
|
1410
1438
|
el.classList.remove("done");
|
|
1411
1439
|
el.querySelector(".thinking-content").textContent = "";
|
|
1412
|
-
// Mate mode: restore
|
|
1440
|
+
// Mate mode: restore dots activity row, hide thinking header
|
|
1413
1441
|
if (el.classList.contains("mate-thinking")) {
|
|
1414
1442
|
var actRow = el.querySelector(".mate-thinking-activity");
|
|
1415
1443
|
if (actRow) {
|
|
1416
1444
|
actRow.style.display = "";
|
|
1417
|
-
actRow.querySelector(".activity-text").textContent = randomThinkingVerb() + "...";
|
|
1418
1445
|
}
|
|
1419
1446
|
var header = el.querySelector(".thinking-header");
|
|
1420
1447
|
if (header) header.style.display = "none";
|
|
@@ -1423,7 +1450,7 @@ export function startThinking() {
|
|
|
1423
1450
|
refreshIcons();
|
|
1424
1451
|
ctx.scrollToBottom();
|
|
1425
1452
|
if (!el.classList.contains("mate-thinking")) {
|
|
1426
|
-
ctx.setActivity(
|
|
1453
|
+
ctx.setActivity("thinking");
|
|
1427
1454
|
}
|
|
1428
1455
|
return;
|
|
1429
1456
|
}
|
|
@@ -1439,10 +1466,7 @@ export function startThinking() {
|
|
|
1439
1466
|
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
1440
1467
|
'<div class="dm-bubble-content">' +
|
|
1441
1468
|
'<div class="dm-bubble-header"><span class="dm-bubble-name">' + escapeHtml(mateName) + '</span></div>' +
|
|
1442
|
-
'<div class="
|
|
1443
|
-
'<span class="activity-icon">' + iconHtml("sparkles") + '</span>' +
|
|
1444
|
-
'<span class="activity-text">' + randomThinkingVerb() + '...</span>' +
|
|
1445
|
-
'</div>' +
|
|
1469
|
+
'<div class="mate-thinking-dots mate-thinking-activity"><span></span><span></span><span></span></div>' +
|
|
1446
1470
|
'<div class="thinking-header" style="display:none">' +
|
|
1447
1471
|
'<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
1448
1472
|
'<span class="thinking-label">Thinking</span>' +
|
|
@@ -1472,7 +1496,7 @@ export function startThinking() {
|
|
|
1472
1496
|
thinkingGroup = { el: el, count: 0, totalDuration: 0 };
|
|
1473
1497
|
currentThinking = { el: el, fullText: "", startTime: Date.now() };
|
|
1474
1498
|
if (!ctx.isMateDm()) {
|
|
1475
|
-
ctx.setActivity(
|
|
1499
|
+
ctx.setActivity("thinking");
|
|
1476
1500
|
}
|
|
1477
1501
|
}
|
|
1478
1502
|
|