clay-server 2.31.0 → 2.32.0-beta.2
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 +32 -44
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +4 -2
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mcp.js +4 -0
- package/lib/project-sessions.js +88 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +204 -90
- package/lib/public/app.js +123 -448
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +181 -100
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +19 -0
- package/lib/public/index.html +46 -24
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +170 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +195 -152
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +97 -3
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +159 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +12 -41
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +86 -0
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +66 -34
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/tools.js +14 -9
- package/lib/sdk-bridge.js +511 -1113
- package/lib/sdk-message-processor.js +123 -134
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +47 -36
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1418 -0
- package/lib/yoke/adapters/codex.js +968 -0
- package/lib/yoke/adapters/gemini.js +668 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +92 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
|
@@ -179,6 +179,35 @@ export function initFileBrowser(_ctx) {
|
|
|
179
179
|
closeFileViewer();
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
|
+
|
|
183
|
+
// --- File search ---
|
|
184
|
+
var fbSearchInput = document.getElementById("fb-search-input");
|
|
185
|
+
var searchDebounce = null;
|
|
186
|
+
|
|
187
|
+
if (fbSearchInput) {
|
|
188
|
+
fbSearchInput.addEventListener("input", function () {
|
|
189
|
+
var q = fbSearchInput.value.trim();
|
|
190
|
+
if (searchDebounce) clearTimeout(searchDebounce);
|
|
191
|
+
if (!q) {
|
|
192
|
+
renderTree();
|
|
193
|
+
restoreExpanded({});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
searchDebounce = setTimeout(function () {
|
|
197
|
+
if (ctx.ws && ctx.connected) {
|
|
198
|
+
ctx.ws.send(JSON.stringify({ type: "fs_search", query: q }));
|
|
199
|
+
}
|
|
200
|
+
}, 200);
|
|
201
|
+
});
|
|
202
|
+
fbSearchInput.addEventListener("keydown", function (e) {
|
|
203
|
+
if (e.key === "Escape" && fbSearchInput.value) {
|
|
204
|
+
e.stopPropagation();
|
|
205
|
+
fbSearchInput.value = "";
|
|
206
|
+
renderTree();
|
|
207
|
+
restoreExpanded({});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
182
211
|
}
|
|
183
212
|
|
|
184
213
|
// --- File watch helpers ---
|
|
@@ -232,10 +261,8 @@ export function resetFileBrowser() {
|
|
|
232
261
|
// Hide the file browser panel, show sessions panel
|
|
233
262
|
var filesPanel = document.getElementById("sidebar-panel-files");
|
|
234
263
|
var sessionsPanel = document.getElementById("sidebar-panel-sessions");
|
|
235
|
-
var filesHeaderContent = document.getElementById("files-header-content");
|
|
236
264
|
var sessionsHeaderContent = document.getElementById("sessions-header-content");
|
|
237
265
|
if (filesPanel) filesPanel.classList.add("hidden");
|
|
238
|
-
if (filesHeaderContent) filesHeaderContent.classList.add("hidden");
|
|
239
266
|
if (sessionsPanel) sessionsPanel.classList.remove("hidden");
|
|
240
267
|
if (sessionsHeaderContent) sessionsHeaderContent.classList.remove("hidden");
|
|
241
268
|
}
|
|
@@ -341,6 +368,150 @@ export function refreshIfOpen(filePath) {
|
|
|
341
368
|
|
|
342
369
|
// --- WS handlers ---
|
|
343
370
|
|
|
371
|
+
export function handleFsSearch(msg) {
|
|
372
|
+
var entries = msg.entries || [];
|
|
373
|
+
var query = (msg.query || "").trim().toLowerCase();
|
|
374
|
+
if (!query) return;
|
|
375
|
+
|
|
376
|
+
ctx.fileTreeEl.innerHTML = "";
|
|
377
|
+
|
|
378
|
+
if (entries.length === 0) {
|
|
379
|
+
ctx.fileTreeEl.innerHTML = '<div class="fb-search-empty">No files found</div>';
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Build a tree structure from flat search results
|
|
384
|
+
var tree = {};
|
|
385
|
+
for (var i = 0; i < entries.length; i++) {
|
|
386
|
+
var entry = entries[i];
|
|
387
|
+
var parts = entry.path.split("/");
|
|
388
|
+
var node = tree;
|
|
389
|
+
for (var j = 0; j < parts.length; j++) {
|
|
390
|
+
if (!node[parts[j]]) node[parts[j]] = {};
|
|
391
|
+
if (j === parts.length - 1) {
|
|
392
|
+
node[parts[j]]._entry = entry;
|
|
393
|
+
} else {
|
|
394
|
+
node = node[parts[j]];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
renderFilteredTree(ctx.fileTreeEl, tree, 0, query);
|
|
400
|
+
refreshIcons();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function renderFilteredTree(container, tree, depth, query) {
|
|
404
|
+
var keys = Object.keys(tree);
|
|
405
|
+
var dirs = [];
|
|
406
|
+
var files = [];
|
|
407
|
+
for (var i = 0; i < keys.length; i++) {
|
|
408
|
+
if (keys[i] === "_entry") continue;
|
|
409
|
+
var node = tree[keys[i]];
|
|
410
|
+
var entry = node._entry;
|
|
411
|
+
if (entry && entry.type === "file") {
|
|
412
|
+
files.push(keys[i]);
|
|
413
|
+
} else {
|
|
414
|
+
dirs.push(keys[i]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
dirs.sort(function (a, b) {
|
|
418
|
+
var aH = a.charAt(0) === ".";
|
|
419
|
+
var bH = b.charAt(0) === ".";
|
|
420
|
+
if (aH !== bH) return aH ? 1 : -1;
|
|
421
|
+
return a.localeCompare(b);
|
|
422
|
+
});
|
|
423
|
+
files.sort(function (a, b) {
|
|
424
|
+
var aH = a.charAt(0) === ".";
|
|
425
|
+
var bH = b.charAt(0) === ".";
|
|
426
|
+
if (aH !== bH) return aH ? 1 : -1;
|
|
427
|
+
return a.localeCompare(b);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
var allKeys = dirs.concat(files);
|
|
431
|
+
for (var k = 0; k < allKeys.length; k++) {
|
|
432
|
+
var name = allKeys[k];
|
|
433
|
+
var node = tree[name];
|
|
434
|
+
var entry = node._entry;
|
|
435
|
+
var isDir = !entry || entry.type === "dir";
|
|
436
|
+
|
|
437
|
+
var row = document.createElement("div");
|
|
438
|
+
row.className = "file-tree-item" + (isDir ? " expanded" : "");
|
|
439
|
+
row.style.paddingLeft = (8 + depth * 16) + "px";
|
|
440
|
+
if (entry) {
|
|
441
|
+
row.draggable = true;
|
|
442
|
+
row.dataset.path = entry.path;
|
|
443
|
+
row.addEventListener("dragstart", function (e) {
|
|
444
|
+
var cwd = ctx.cwd || "";
|
|
445
|
+
var rel = this.dataset.path;
|
|
446
|
+
var abs = cwd ? cwd.replace(/\/$/, "") + "/" + rel : rel;
|
|
447
|
+
e.dataTransfer.setData("text/plain", abs);
|
|
448
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
var nameHtml = highlightMatch(name, query);
|
|
453
|
+
|
|
454
|
+
if (isDir) {
|
|
455
|
+
row.innerHTML =
|
|
456
|
+
'<span class="file-tree-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
457
|
+
'<span class="file-tree-icon file-tree-folder-icon"></span>' +
|
|
458
|
+
'<span class="file-tree-name">' + nameHtml + '</span>';
|
|
459
|
+
|
|
460
|
+
(function (iconEl, n) {
|
|
461
|
+
getFolderIconSvg(n, true, function (svg) { iconEl.innerHTML = svg; });
|
|
462
|
+
})(row.querySelector(".file-tree-folder-icon"), name);
|
|
463
|
+
|
|
464
|
+
var childContainer = document.createElement("div");
|
|
465
|
+
childContainer.className = "file-tree-children";
|
|
466
|
+
|
|
467
|
+
// Toggle expand/collapse on click
|
|
468
|
+
(function (rowEl, childEl, folderName) {
|
|
469
|
+
rowEl.addEventListener("click", function (e) {
|
|
470
|
+
e.stopPropagation();
|
|
471
|
+
var isExpanded = rowEl.classList.contains("expanded");
|
|
472
|
+
rowEl.classList.toggle("expanded");
|
|
473
|
+
childEl.classList.toggle("hidden", isExpanded);
|
|
474
|
+
var folderIconEl = rowEl.querySelector(".file-tree-folder-icon");
|
|
475
|
+
if (folderIconEl) {
|
|
476
|
+
getFolderIconSvg(folderName, !isExpanded, function (svg) { folderIconEl.innerHTML = svg; });
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
})(row, childContainer, name);
|
|
480
|
+
|
|
481
|
+
container.appendChild(row);
|
|
482
|
+
container.appendChild(childContainer);
|
|
483
|
+
renderFilteredTree(childContainer, node, depth + 1, query);
|
|
484
|
+
} else {
|
|
485
|
+
row.innerHTML =
|
|
486
|
+
'<span class="file-tree-spacer"></span>' +
|
|
487
|
+
'<span class="file-tree-icon">' + getFileIconSvg(name) + '</span>' +
|
|
488
|
+
'<span class="file-tree-name">' + nameHtml + '</span>';
|
|
489
|
+
|
|
490
|
+
(function (filePath, rowEl) {
|
|
491
|
+
rowEl.addEventListener("click", function (e) {
|
|
492
|
+
e.stopPropagation();
|
|
493
|
+
var prev = ctx.fileTreeEl.querySelector(".file-tree-item.active");
|
|
494
|
+
if (prev) prev.classList.remove("active");
|
|
495
|
+
rowEl.classList.add("active");
|
|
496
|
+
requestFileContent(filePath);
|
|
497
|
+
if (window.innerWidth <= 768) closeSidebar();
|
|
498
|
+
});
|
|
499
|
+
})(entry.path, row);
|
|
500
|
+
|
|
501
|
+
container.appendChild(row);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function highlightMatch(text, query) {
|
|
507
|
+
var lower = text.toLowerCase();
|
|
508
|
+
var idx = lower.indexOf(query);
|
|
509
|
+
if (idx === -1) return escapeHtml(text);
|
|
510
|
+
return escapeHtml(text.substring(0, idx)) +
|
|
511
|
+
'<mark>' + escapeHtml(text.substring(idx, idx + query.length)) + '</mark>' +
|
|
512
|
+
escapeHtml(text.substring(idx + query.length));
|
|
513
|
+
}
|
|
514
|
+
|
|
344
515
|
export function handleFsList(msg) {
|
|
345
516
|
var dirPath = msg.path || ".";
|
|
346
517
|
treeData[dirPath] = { loaded: true, children: msg.entries || [] };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
2
2
|
import { setRewindMode, isRewindMode } from './rewind.js';
|
|
3
3
|
import { checkForMention, showMentionMenu, hideMentionMenu, isMentionMenuVisible, mentionMenuKeydown, setMentionAtIdx, parseMentionFromInput, clearMentionState, stickyReapplyMention, sendMention, renderMentionUser, removeMentionChip } from './mention.js';
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
import { mateAvatarUrl } from './avatar.js';
|
|
4
6
|
|
|
5
7
|
var ctx;
|
|
6
8
|
|
|
@@ -217,8 +219,15 @@ export function sendMessage() {
|
|
|
217
219
|
if (pastes.length > 0) {
|
|
218
220
|
payload.pastes = pastes;
|
|
219
221
|
}
|
|
222
|
+
// Include selected vendor for session binding (server uses on first message)
|
|
223
|
+
var _selVendor = store.get("currentVendor") || null;
|
|
224
|
+
if (_selVendor) payload.vendor = _selVendor;
|
|
220
225
|
ctx.ws.send(JSON.stringify(payload));
|
|
221
226
|
|
|
227
|
+
// Hide vendor toggle after first message (vendor is locked to this session)
|
|
228
|
+
var _vtw2 = document.getElementById("vendor-toggle-wrap");
|
|
229
|
+
if (_vtw2) { _vtw2.classList.remove("hidden"); _vtw2.classList.add("locked"); }
|
|
230
|
+
|
|
222
231
|
// Show pre-thinking dots before server responds
|
|
223
232
|
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
224
233
|
ctx.showMatePreThinking();
|
|
@@ -770,6 +779,69 @@ export function initInput(_ctx) {
|
|
|
770
779
|
// Trigger the mention detection
|
|
771
780
|
inputEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
772
781
|
});
|
|
782
|
+
|
|
783
|
+
// Mate avatar overlay on @ button — rotate random mate faces
|
|
784
|
+
var _lastOverlayIdx = -1;
|
|
785
|
+
var _overlayInterval = null;
|
|
786
|
+
|
|
787
|
+
function getAvailableMates() {
|
|
788
|
+
var mates = store.get('cachedMatesList') || [];
|
|
789
|
+
return mates.filter(function (m) { return m.status !== 'interviewing'; });
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function pickNextMate(available) {
|
|
793
|
+
if (available.length === 0) return null;
|
|
794
|
+
if (available.length === 1) return available[0];
|
|
795
|
+
var idx = Math.floor(Math.random() * available.length);
|
|
796
|
+
if (idx === _lastOverlayIdx) idx = (idx + 1) % available.length;
|
|
797
|
+
_lastOverlayIdx = idx;
|
|
798
|
+
return available[idx];
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function setOverlayAvatar(mate) {
|
|
802
|
+
var img = askMateBtn.querySelector('.ask-mate-avatar');
|
|
803
|
+
if (!mate) {
|
|
804
|
+
if (img) img.remove();
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
var url = mateAvatarUrl(mate, 20);
|
|
808
|
+
if (!img) {
|
|
809
|
+
img = document.createElement('img');
|
|
810
|
+
img.className = 'ask-mate-avatar fade-in';
|
|
811
|
+
img.width = 20;
|
|
812
|
+
img.height = 20;
|
|
813
|
+
img.alt = '';
|
|
814
|
+
img.src = url;
|
|
815
|
+
askMateBtn.appendChild(img);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
// Fade out, swap, fade in
|
|
819
|
+
img.classList.remove('fade-in');
|
|
820
|
+
img.classList.add('fade-out');
|
|
821
|
+
setTimeout(function () {
|
|
822
|
+
img.src = url;
|
|
823
|
+
img.classList.remove('fade-out');
|
|
824
|
+
img.classList.add('fade-in');
|
|
825
|
+
}, 300);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function rotateMateOverlay() {
|
|
829
|
+
var available = getAvailableMates();
|
|
830
|
+
setOverlayAvatar(pickNextMate(available));
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function startOverlayRotation() {
|
|
834
|
+
if (_overlayInterval) clearInterval(_overlayInterval);
|
|
835
|
+
rotateMateOverlay();
|
|
836
|
+
_overlayInterval = setInterval(rotateMateOverlay, 10000);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// Update overlay when mate list changes
|
|
840
|
+
store.subscribe(function (state, prev) {
|
|
841
|
+
if (state.cachedMatesList !== prev.cachedMatesList) {
|
|
842
|
+
startOverlayRotation();
|
|
843
|
+
}
|
|
844
|
+
});
|
|
773
845
|
}
|
|
774
846
|
|
|
775
847
|
// Paste handler
|
|
@@ -930,6 +1002,12 @@ export function initInput(_ctx) {
|
|
|
930
1002
|
return;
|
|
931
1003
|
}
|
|
932
1004
|
e.preventDefault();
|
|
1005
|
+
// If input is empty but ghost suggestion is showing, adopt it
|
|
1006
|
+
var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
|
|
1007
|
+
if (!ctx.inputEl.value.trim() && ghost) {
|
|
1008
|
+
ctx.inputEl.value = ghost;
|
|
1009
|
+
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
1010
|
+
}
|
|
933
1011
|
sendMessage();
|
|
934
1012
|
}
|
|
935
1013
|
});
|
|
@@ -941,6 +1019,14 @@ export function initInput(_ctx) {
|
|
|
941
1019
|
|
|
942
1020
|
// Send/Stop button — if sendable content exists, always send; otherwise stop
|
|
943
1021
|
ctx.sendBtn.addEventListener("click", function () {
|
|
1022
|
+
// Adopt ghost suggestion if input is empty
|
|
1023
|
+
var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
|
|
1024
|
+
if (!hasSendableContent() && ghost) {
|
|
1025
|
+
ctx.inputEl.value = ghost;
|
|
1026
|
+
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
1027
|
+
sendMessage();
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
944
1030
|
if (hasSendableContent()) {
|
|
945
1031
|
sendMessage();
|
|
946
1032
|
return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { avatarUrl, mateAvatarUrl } from './avatar.js';
|
|
2
2
|
import { escapeHtml } from './utils.js';
|
|
3
3
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
4
|
+
import { store } from './store.js';
|
|
4
5
|
import { hideKnowledge } from './mate-knowledge.js';
|
|
5
6
|
import { isSchedulerOpen, closeScheduler } from './scheduler.js';
|
|
6
7
|
import { hideNotes } from './sticky-notes.js';
|
|
@@ -150,6 +151,43 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
150
151
|
if (avatarEl) avatarEl.src = mateAvUrl;
|
|
151
152
|
if (nameEl) nameEl.textContent = displayName;
|
|
152
153
|
|
|
154
|
+
// Vendor toggle in header
|
|
155
|
+
var mateVendorWrap = document.getElementById("mate-vendor-toggle");
|
|
156
|
+
if (mateVendorWrap) {
|
|
157
|
+
var available = store.get('availableVendors') || [];
|
|
158
|
+
var mateVendor = mateData.vendor || "claude";
|
|
159
|
+
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png" };
|
|
160
|
+
var vendorNames = { claude: "Claude Code", codex: "Codex" };
|
|
161
|
+
var vendorKeys = ["claude", "codex"];
|
|
162
|
+
mateVendorWrap.innerHTML = "";
|
|
163
|
+
for (var vi = 0; vi < vendorKeys.length; vi++) {
|
|
164
|
+
var vk = vendorKeys[vi];
|
|
165
|
+
var vBtn = document.createElement("button");
|
|
166
|
+
vBtn.className = "mate-vendor-btn" + (vk === mateVendor ? " active" : "");
|
|
167
|
+
if (available.indexOf(vk) === -1) vBtn.classList.add("disabled");
|
|
168
|
+
vBtn.dataset.vendor = vk;
|
|
169
|
+
vBtn.innerHTML = '<img src="' + vendorIcons[vk] + '" class="mate-vendor-icon" alt="' + vendorNames[vk] + '"><span class="mate-vendor-label">' + vendorNames[vk] + '</span>';
|
|
170
|
+
vBtn.addEventListener("click", function() {
|
|
171
|
+
var v = this.dataset.vendor;
|
|
172
|
+
var avail = store.get('availableVendors') || [];
|
|
173
|
+
if (avail.indexOf(v) === -1) return;
|
|
174
|
+
if (v === (currentMate && currentMate.vendor || "claude")) return;
|
|
175
|
+
if (!currentMateId) return;
|
|
176
|
+
var ws = getWs();
|
|
177
|
+
if (ws) ws.send(JSON.stringify({ type: "mate_update", mateId: currentMateId, updates: { vendor: v } }));
|
|
178
|
+
// Update UI immediately
|
|
179
|
+
mateVendorWrap.querySelectorAll(".mate-vendor-btn").forEach(function(b) {
|
|
180
|
+
b.classList.toggle("active", b.dataset.vendor === v);
|
|
181
|
+
});
|
|
182
|
+
if (currentMate) currentMate.vendor = v;
|
|
183
|
+
// If current session is new (no history), sync vendor to input toggle
|
|
184
|
+
var _hTotal = store.get('historyTotal') || 0;
|
|
185
|
+
if (_hTotal === 0) store.set({ currentVendor: v });
|
|
186
|
+
});
|
|
187
|
+
mateVendorWrap.appendChild(vBtn);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
153
191
|
// Also populate collapsed header info
|
|
154
192
|
var collapsedAvatar = document.getElementById("mate-collapsed-avatar");
|
|
155
193
|
var collapsedName = document.getElementById("mate-collapsed-name");
|
|
@@ -2,6 +2,7 @@ import { mateAvatarUrl, userAvatarUrl } from './avatar.js';
|
|
|
2
2
|
import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
|
|
3
3
|
import { escapeHtml, copyToClipboard } from './utils.js';
|
|
4
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
|
+
import { store } from './store.js';
|
|
5
6
|
|
|
6
7
|
var ctx;
|
|
7
8
|
|
|
@@ -77,13 +78,17 @@ export function showMentionMenu(query) {
|
|
|
77
78
|
var menuEl = document.getElementById("mention-menu");
|
|
78
79
|
if (!menuEl) return;
|
|
79
80
|
|
|
80
|
-
menuEl.innerHTML =
|
|
81
|
+
menuEl.innerHTML = '<div class="mention-hint">Mention a Mate to get advice on your current session<button class="mention-close-btn" aria-label="Close">×</button></div>' +
|
|
82
|
+
mentionFiltered.map(function (m, i) {
|
|
81
83
|
var name = (m.profile && m.profile.displayName) || m.name || "Mate";
|
|
82
84
|
var color = (m.profile && m.profile.avatarColor) || "#6c5ce7";
|
|
83
85
|
var bio = m.bio || (m.profile && m.profile.bio) || "";
|
|
84
86
|
var avatarSrc = mateAvatarUrl(m, 24);
|
|
87
|
+
var mVendor = m.vendor || "claude";
|
|
88
|
+
var vendorIcons = { claude: "/claude-code-avatar.png", codex: "/codex-avatar.png" };
|
|
89
|
+
var vendorBadge = vendorIcons[mVendor] ? '<img class="mention-item-vendor-badge" src="' + vendorIcons[mVendor] + '" alt="' + mVendor + '">' : '';
|
|
85
90
|
return '<div class="mention-item' + (i === 0 ? ' active' : '') + '" data-idx="' + i + '">' +
|
|
86
|
-
'<img class="mention-item-avatar" src="' + escapeHtml(avatarSrc) + '" width="24" height="24" />' +
|
|
91
|
+
'<div class="mention-item-avatar-wrap"><img class="mention-item-avatar" src="' + escapeHtml(avatarSrc) + '" width="24" height="24" />' + vendorBadge + '</div>' +
|
|
87
92
|
'<div class="mention-item-info">' +
|
|
88
93
|
'<span class="mention-item-name">' + escapeHtml(name) +
|
|
89
94
|
(m.primary ? ' <span class="mention-item-badge">SYSTEM</span>' : '') +
|
|
@@ -95,6 +100,18 @@ export function showMentionMenu(query) {
|
|
|
95
100
|
}).join("");
|
|
96
101
|
menuEl.classList.add("visible");
|
|
97
102
|
|
|
103
|
+
var closeBtn = menuEl.querySelector(".mention-close-btn");
|
|
104
|
+
if (closeBtn) closeBtn.addEventListener("click", function (e) {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
hideMentionMenu();
|
|
108
|
+
clearMentionState();
|
|
109
|
+
if (ctx && ctx.inputEl) {
|
|
110
|
+
ctx.inputEl.value = ctx.inputEl.value.replace(/@\S*$/, "");
|
|
111
|
+
ctx.inputEl.focus();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
98
115
|
menuEl.querySelectorAll(".mention-item").forEach(function (el) {
|
|
99
116
|
el.addEventListener("click", function (e) {
|
|
100
117
|
e.preventDefault();
|
|
@@ -215,10 +232,11 @@ function showInputMentionChip(name, color, avatarSrc) {
|
|
|
215
232
|
'<button class="input-mention-chip-remove" type="button" aria-label="Remove mention">×</button>';
|
|
216
233
|
chip.style.setProperty("--chip-color", color);
|
|
217
234
|
|
|
218
|
-
// Insert before the textarea inside input-row
|
|
235
|
+
// Insert before the textarea wrapper inside input-row
|
|
219
236
|
var inputRow = document.getElementById("input-row");
|
|
220
|
-
|
|
221
|
-
|
|
237
|
+
var textareaWrap = document.getElementById("input-textarea-wrap");
|
|
238
|
+
if (inputRow && textareaWrap) {
|
|
239
|
+
inputRow.insertBefore(chip, textareaWrap);
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
chip.querySelector(".input-mention-chip-remove").addEventListener("click", function (e) {
|
|
@@ -713,7 +731,7 @@ export function renderMentionUser(entry) {
|
|
|
713
731
|
'<span class="msg-action-time">' + ts2 + '</span>' +
|
|
714
732
|
'<button class="msg-action-btn msg-action-copy" type="button" title="Copy">' + iconHtml("copy") + '</button>' +
|
|
715
733
|
'<button class="msg-action-btn msg-action-fork" type="button" title="Fork">' + iconHtml("git-branch") + '</button>' +
|
|
716
|
-
'<button class="msg-action-btn msg-action-rewind msg-user-rewind-btn" type="button" title="Rewind">' + iconHtml("rotate-ccw") + '</button>' +
|
|
734
|
+
(((store.get('vendorCapabilities') || {}).rewind !== false) ? '<button class="msg-action-btn msg-action-rewind msg-user-rewind-btn" type="button" title="Rewind">' + iconHtml("rotate-ccw") + '</button>' : '') +
|
|
717
735
|
'<button class="msg-action-btn msg-action-hidden msg-action-edit" type="button" title="Edit">' + iconHtml("pencil") + '</button>';
|
|
718
736
|
div.appendChild(actions);
|
|
719
737
|
|
|
@@ -790,7 +790,7 @@ function renderModelTab(bodyEl, rec) {
|
|
|
790
790
|
}
|
|
791
791
|
|
|
792
792
|
var opts = {
|
|
793
|
-
models: store.
|
|
793
|
+
models: store.get('currentModels') || [],
|
|
794
794
|
currentModel: settings.model || "",
|
|
795
795
|
currentMode: settings.permissionMode || "default",
|
|
796
796
|
currentEffort: settings.effort || "medium",
|