clay-server 2.26.0-beta.9 → 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/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 +108 -39
- package/lib/public/app.js +317 -85
- 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 +33 -0
- package/lib/public/css/mates.css +17 -38
- 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 +15 -0
- 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/sidebar.js +105 -26
- package/lib/public/modules/terminal.js +62 -6
- package/lib/public/modules/tools.js +2 -2
- package/lib/sdk-bridge.js +123 -22
- 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
|
}
|
|
@@ -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");
|
|
@@ -205,7 +205,7 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
205
205
|
|
|
206
206
|
var avi = document.createElement("img");
|
|
207
207
|
avi.className = "dm-bubble-avatar";
|
|
208
|
-
avi.src =
|
|
208
|
+
avi.src = mateAvatar;
|
|
209
209
|
container.appendChild(avi);
|
|
210
210
|
|
|
211
211
|
mateContentWrap = document.createElement("div");
|
|
@@ -214,7 +214,7 @@ export function renderAskUserQuestion(toolId, input) {
|
|
|
214
214
|
var headerEl = document.createElement("div");
|
|
215
215
|
headerEl.className = "dm-bubble-header";
|
|
216
216
|
headerEl.innerHTML =
|
|
217
|
-
'<span class="dm-bubble-name">' + escapeHtml(
|
|
217
|
+
'<span class="dm-bubble-name">' + escapeHtml(mateName) + '</span>' +
|
|
218
218
|
'<span class="dm-bubble-time">' + String(new Date().getHours()).padStart(2, "0") + ":" + String(new Date().getMinutes()).padStart(2, "0") + '</span>';
|
|
219
219
|
mateContentWrap.appendChild(headerEl);
|
|
220
220
|
}
|