clay-server 2.12.0 → 2.13.0-beta.1
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/daemon.js +14 -0
- package/lib/dm.js +3 -3
- package/lib/mates.js +222 -0
- package/lib/project.js +83 -2
- package/lib/public/app.js +273 -10
- package/lib/public/css/base.css +1 -0
- package/lib/public/css/mates.css +1018 -0
- package/lib/public/index.html +238 -0
- package/lib/public/modules/mate-knowledge.js +222 -0
- package/lib/public/modules/mate-sidebar.js +329 -0
- package/lib/public/modules/mate-wizard.js +265 -0
- package/lib/public/modules/profile.js +207 -0
- package/lib/public/modules/sidebar.js +143 -3
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +4 -0
- package/lib/server.js +110 -3
- package/lib/sessions.js +27 -0
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -2,6 +2,8 @@ import { showToast, copyToClipboard, escapeHtml } from './modules/utils.js';
|
|
|
2
2
|
import { refreshIcons, iconHtml, randomThinkingVerb } from './modules/icons.js';
|
|
3
3
|
import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidModal, parseEmojis } from './modules/markdown.js';
|
|
4
4
|
import { initSidebar, renderSessionList, handleSearchResults, handleSearchContentResults, updateSessionPresence, updatePageTitle, getActiveSearchQuery, buildSearchTimeline, removeSearchTimeline, onHistoryPrepended, populateCliSessionList, renderIconStrip, renderSidebarPresence, initIconStrip, getEmojiCategories, renderUserStrip, setCurrentDmUser, updateDmBadge, updateSessionBadge, updateProjectBadge, closeDmUserPicker, spawnDustParticles } from './modules/sidebar.js';
|
|
5
|
+
import { initMateSidebar, showMateSidebar, hideMateSidebar, renderMateSessionList, updateMateSidebarProfile } from './modules/mate-sidebar.js';
|
|
6
|
+
import { initMateKnowledge, requestKnowledgeList, renderKnowledgeList, handleKnowledgeContent, hideKnowledge } from './modules/mate-knowledge.js';
|
|
5
7
|
import { initRewind, setRewindMode, showRewindModal, clearPendingRewindUuid, addRewindButton } from './modules/rewind.js';
|
|
6
8
|
import { initNotifications, showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './modules/notifications.js';
|
|
7
9
|
import { initInput, clearPendingImages, handleInputSync, autoResize, builtinCommands, sendMessage } from './modules/input.js';
|
|
@@ -18,10 +20,11 @@ import { initScheduler, resetScheduler, handleLoopRegistryUpdated, handleSchedul
|
|
|
18
20
|
import { initAsciiLogo, startLogoAnimation, stopLogoAnimation } from './modules/ascii-logo.js';
|
|
19
21
|
import { initPlaybook, openPlaybook, getPlaybooks, getPlaybookForTip, isCompleted as isPlaybookCompleted } from './modules/playbook.js';
|
|
20
22
|
import { initSTT } from './modules/stt.js';
|
|
21
|
-
import { initProfile } from './modules/profile.js';
|
|
23
|
+
import { initProfile, getProfileLang } from './modules/profile.js';
|
|
22
24
|
import { initAdmin, checkAdminAccess } from './modules/admin.js';
|
|
23
25
|
import { initSessionSearch, toggleSearch, closeSearch, isSearchOpen, handleFindInSessionResults, onHistoryPrepended as onSessionSearchHistoryPrepended } from './modules/session-search.js';
|
|
24
26
|
import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
27
|
+
import { initMateWizard, openMateWizard, closeMateWizard, handleMateCreated } from './modules/mate-wizard.js';
|
|
25
28
|
|
|
26
29
|
// --- Base path for multi-project routing ---
|
|
27
30
|
var slugMatch = location.pathname.match(/^\/p\/([a-z0-9_-]+)/);
|
|
@@ -62,6 +65,11 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
62
65
|
var cachedDmFavorites = [];
|
|
63
66
|
var cachedDmConversations = [];
|
|
64
67
|
var dmRemovedUsers = {}; // { userId: true } - users explicitly removed from favorites
|
|
68
|
+
var cachedMatesList = []; // Cached list of mates for user strip
|
|
69
|
+
|
|
70
|
+
// --- Mate WS (separate connection to mate project) ---
|
|
71
|
+
var mateWs = null;
|
|
72
|
+
var mateProjectSlug = null;
|
|
65
73
|
|
|
66
74
|
// --- Home Hub ---
|
|
67
75
|
var homeHub = $("home-hub");
|
|
@@ -572,13 +580,24 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
572
580
|
// Hide sticky notes if visible
|
|
573
581
|
hideNotes();
|
|
574
582
|
|
|
583
|
+
var isMate = targetUser && targetUser.isMate;
|
|
584
|
+
|
|
575
585
|
// Hide project UI + sidebar, show DM UI
|
|
576
586
|
var mainCol = document.getElementById("main-column");
|
|
577
|
-
if (mainCol) mainCol.classList.add("dm-mode");
|
|
587
|
+
if (mainCol && !isMate) mainCol.classList.add("dm-mode");
|
|
578
588
|
var sidebarCol = document.getElementById("sidebar-column");
|
|
579
589
|
if (sidebarCol) sidebarCol.classList.add("dm-mode");
|
|
580
590
|
var resizeHandle = document.getElementById("sidebar-resize-handle");
|
|
581
591
|
if (resizeHandle) resizeHandle.classList.add("dm-mode");
|
|
592
|
+
if (isMate && targetUser.projectSlug) {
|
|
593
|
+
// Mate DM: connect to mate's own project via separate WS
|
|
594
|
+
// Main column stays visible (regular project chat UI), sidebar swaps to mate sidebar
|
|
595
|
+
showMateSidebar(targetUser.id, targetUser);
|
|
596
|
+
connectMateWs(targetUser.projectSlug);
|
|
597
|
+
// Hide terminal button (not relevant for mate)
|
|
598
|
+
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
599
|
+
if (termBtn) termBtn.style.display = "none";
|
|
600
|
+
}
|
|
582
601
|
|
|
583
602
|
// Hide user-island (my avatar behind it becomes visible)
|
|
584
603
|
var userIsland = document.getElementById("user-island");
|
|
@@ -611,6 +630,15 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
611
630
|
if (dmHeaderBar && targetUser.avatarColor) {
|
|
612
631
|
dmHeaderBar.style.background = targetUser.avatarColor;
|
|
613
632
|
}
|
|
633
|
+
// Add mate tag if this is a mate
|
|
634
|
+
var existingTag = dmHeaderBar ? dmHeaderBar.querySelector(".dm-header-mate-tag") : null;
|
|
635
|
+
if (existingTag) existingTag.remove();
|
|
636
|
+
if (targetUser.isMate && dmHeaderBar) {
|
|
637
|
+
var mateTag = document.createElement("span");
|
|
638
|
+
mateTag.className = "dm-header-mate-tag";
|
|
639
|
+
mateTag.textContent = "MATE";
|
|
640
|
+
dmHeaderBar.appendChild(mateTag);
|
|
641
|
+
}
|
|
614
642
|
}
|
|
615
643
|
}
|
|
616
644
|
|
|
@@ -627,10 +655,24 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
627
655
|
if (sidebarCol) sidebarCol.classList.remove("dm-mode");
|
|
628
656
|
var resizeHandle = document.getElementById("sidebar-resize-handle");
|
|
629
657
|
if (resizeHandle) resizeHandle.classList.remove("dm-mode");
|
|
658
|
+
hideMateSidebar();
|
|
659
|
+
hideKnowledge();
|
|
660
|
+
disconnectMateWs();
|
|
661
|
+
// Restore terminal button
|
|
662
|
+
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
663
|
+
if (termBtn) termBtn.style.display = "";
|
|
664
|
+
// Re-request session list from main project to refresh state
|
|
665
|
+
if (ws && ws.readyState === 1) {
|
|
666
|
+
ws.send(JSON.stringify({ type: "switch_session", id: activeSessionId }));
|
|
667
|
+
}
|
|
630
668
|
|
|
631
669
|
// Reset DM header
|
|
632
670
|
var dmHeaderBar = document.getElementById("dm-header-bar");
|
|
633
|
-
if (dmHeaderBar)
|
|
671
|
+
if (dmHeaderBar) {
|
|
672
|
+
dmHeaderBar.style.background = "";
|
|
673
|
+
var mateTag = dmHeaderBar.querySelector(".dm-header-mate-tag");
|
|
674
|
+
if (mateTag) mateTag.remove();
|
|
675
|
+
}
|
|
634
676
|
|
|
635
677
|
// Restore user-island (covers my avatar again)
|
|
636
678
|
var userIsland = document.getElementById("user-island");
|
|
@@ -641,6 +683,138 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
641
683
|
renderProjectList();
|
|
642
684
|
}
|
|
643
685
|
|
|
686
|
+
function handleMateCreatedInApp(mate) {
|
|
687
|
+
if (!mate) return;
|
|
688
|
+
cachedMatesList.push(mate);
|
|
689
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
690
|
+
// Store pending interview data so we can auto-send after DM opens
|
|
691
|
+
pendingMateInterview = mate;
|
|
692
|
+
openDm(mate.id);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
var pendingMateInterview = null;
|
|
696
|
+
|
|
697
|
+
function buildMateInterviewPrompt(mate) {
|
|
698
|
+
var sd = mate.seedData || {};
|
|
699
|
+
var parts = [];
|
|
700
|
+
var spokenLang = getProfileLang() || "en-US";
|
|
701
|
+
parts.push("Spoken Language: " + spokenLang);
|
|
702
|
+
if (sd.relationship) parts.push("Relationship: " + sd.relationship);
|
|
703
|
+
if (sd.activity && sd.activity.length > 0) parts.push("Activities: " + sd.activity.join(", "));
|
|
704
|
+
if (sd.communicationStyle && sd.communicationStyle.length > 0) {
|
|
705
|
+
var styleLabels = {
|
|
706
|
+
direct_concise: "direct and concise",
|
|
707
|
+
soft_detailed: "soft and detailed",
|
|
708
|
+
witty: "witty",
|
|
709
|
+
encouraging: "encouraging",
|
|
710
|
+
formal: "formal",
|
|
711
|
+
no_nonsense: "no-nonsense",
|
|
712
|
+
};
|
|
713
|
+
var styles = sd.communicationStyle.map(function (s) { return styleLabels[s] || s.replace(/_/g, " "); });
|
|
714
|
+
parts.push("Communication: " + styles.join(", "));
|
|
715
|
+
}
|
|
716
|
+
if (sd.autonomy) parts.push("Autonomy: " + sd.autonomy.replace(/_/g, " "));
|
|
717
|
+
|
|
718
|
+
return "Use the /clay-mate-interview skill to start the interview.\n\n" +
|
|
719
|
+
"Mate ID: " + mate.id + "\n" +
|
|
720
|
+
"Mate Directory: .claude/mates/" + mate.id + "\n\n" +
|
|
721
|
+
"Seed Data:\n" + parts.join("\n");
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// --- Mate WS connection ---
|
|
725
|
+
var savedMainWs = null; // stash main project ws while in mate DM
|
|
726
|
+
|
|
727
|
+
function connectMateWs(slug) {
|
|
728
|
+
disconnectMateWs();
|
|
729
|
+
mateProjectSlug = slug;
|
|
730
|
+
var protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
731
|
+
mateWs = new WebSocket(protocol + "//" + location.host + "/p/" + slug + "/ws");
|
|
732
|
+
|
|
733
|
+
mateWs.onopen = function () {
|
|
734
|
+
// Swap main ws to mateWs so all UI (input, model selector, etc.) routes through mate project
|
|
735
|
+
savedMainWs = ws;
|
|
736
|
+
ws = mateWs;
|
|
737
|
+
connected = true;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
mateWs.onmessage = function (ev) {
|
|
741
|
+
var msg;
|
|
742
|
+
try { msg = JSON.parse(ev.data); } catch (e) { return; }
|
|
743
|
+
// Intercept session_list for mate sidebar
|
|
744
|
+
if (msg.type === "init" && msg.sessions) {
|
|
745
|
+
renderMateSessionList(msg.sessions);
|
|
746
|
+
requestKnowledgeList();
|
|
747
|
+
}
|
|
748
|
+
if (msg.type === "session_list") {
|
|
749
|
+
renderMateSessionList(msg.sessions || []);
|
|
750
|
+
}
|
|
751
|
+
// Intercept knowledge messages
|
|
752
|
+
if (msg.type === "knowledge_list") {
|
|
753
|
+
renderKnowledgeList(msg.files);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (msg.type === "knowledge_content") {
|
|
757
|
+
handleKnowledgeContent(msg);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (msg.type === "knowledge_saved" || msg.type === "knowledge_deleted") {
|
|
761
|
+
return; // list update follows separately
|
|
762
|
+
}
|
|
763
|
+
// On done: scan DOM for [[MATE_READY: name]], update name, strip marker
|
|
764
|
+
if (msg.type === "done" && dmTargetUser && dmTargetUser.isMate) {
|
|
765
|
+
// Let processMessage render first, then scan DOM
|
|
766
|
+
setTimeout(function () {
|
|
767
|
+
var fullText = messagesEl ? messagesEl.textContent : "";
|
|
768
|
+
var readyMatch = fullText.match(/\[\[MATE_READY:\s*(.+?)\]\]/);
|
|
769
|
+
if (readyMatch) {
|
|
770
|
+
var newName = readyMatch[1].trim();
|
|
771
|
+
dmTargetUser.displayName = newName;
|
|
772
|
+
updateMateSidebarProfile({ profile: { displayName: newName, avatarColor: dmTargetUser.avatarColor, avatarStyle: dmTargetUser.avatarStyle, avatarSeed: dmTargetUser.avatarSeed } });
|
|
773
|
+
if (savedMainWs && savedMainWs.readyState === 1) {
|
|
774
|
+
savedMainWs.send(JSON.stringify({
|
|
775
|
+
type: "mate_update",
|
|
776
|
+
mateId: dmTargetUser.id,
|
|
777
|
+
updates: { name: newName, status: "ready", profile: { displayName: newName } },
|
|
778
|
+
}));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// Strip all MATE_READY markers from visible elements
|
|
782
|
+
var walker = document.createTreeWalker(messagesEl, NodeFilter.SHOW_TEXT, null, false);
|
|
783
|
+
var node;
|
|
784
|
+
while (node = walker.nextNode()) {
|
|
785
|
+
if (node.nodeValue.indexOf("[[MATE_READY:") !== -1) {
|
|
786
|
+
node.nodeValue = node.nodeValue.replace(/\[\[MATE_READY:\s*.+?\]\]/g, "").trim();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}, 100);
|
|
790
|
+
}
|
|
791
|
+
// Feed everything into the main message handler (renders text, tools, etc.)
|
|
792
|
+
processMessage(msg);
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
mateWs.onclose = function () {
|
|
796
|
+
if (ws === mateWs) {
|
|
797
|
+
ws = savedMainWs;
|
|
798
|
+
savedMainWs = null;
|
|
799
|
+
}
|
|
800
|
+
mateWs = null;
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function disconnectMateWs() {
|
|
805
|
+
if (mateWs) {
|
|
806
|
+
// Restore main ws before closing
|
|
807
|
+
if (ws === mateWs && savedMainWs) {
|
|
808
|
+
ws = savedMainWs;
|
|
809
|
+
savedMainWs = null;
|
|
810
|
+
}
|
|
811
|
+
mateWs.onclose = null;
|
|
812
|
+
mateWs.close();
|
|
813
|
+
mateWs = null;
|
|
814
|
+
}
|
|
815
|
+
mateProjectSlug = null;
|
|
816
|
+
}
|
|
817
|
+
|
|
644
818
|
function appendDmMessage(msg) {
|
|
645
819
|
var isMe = msg.from === myUserId;
|
|
646
820
|
var d = new Date(msg.ts);
|
|
@@ -858,7 +1032,7 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
858
1032
|
renderTopbarPresence(msg.serverUsers);
|
|
859
1033
|
// Re-render user strip online dots even without allUsers update
|
|
860
1034
|
if (!msg.allUsers && cachedAllUsers.length > 0) {
|
|
861
|
-
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
|
|
1035
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
862
1036
|
}
|
|
863
1037
|
}
|
|
864
1038
|
// Update user strip (DM targets) in icon strip
|
|
@@ -866,7 +1040,7 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
866
1040
|
cachedAllUsers = msg.allUsers;
|
|
867
1041
|
if (msg.dmFavorites) cachedDmFavorites = msg.dmFavorites;
|
|
868
1042
|
if (msg.dmConversations) cachedDmConversations = msg.dmConversations;
|
|
869
|
-
renderUserStrip(msg.allUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
|
|
1043
|
+
renderUserStrip(msg.allUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
870
1044
|
// Render my avatar (always present, hidden behind user-island)
|
|
871
1045
|
var meEl = document.getElementById("icon-strip-me");
|
|
872
1046
|
if (meEl && !meEl.hasChildNodes()) {
|
|
@@ -903,8 +1077,10 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
903
1077
|
}
|
|
904
1078
|
|
|
905
1079
|
function renderProjectList() {
|
|
906
|
-
// Render icon strip projects
|
|
907
|
-
var iconStripProjects = cachedProjects.
|
|
1080
|
+
// Render icon strip projects (exclude mate projects)
|
|
1081
|
+
var iconStripProjects = cachedProjects.filter(function (p) {
|
|
1082
|
+
return !p.isMate;
|
|
1083
|
+
}).map(function (p) {
|
|
908
1084
|
return {
|
|
909
1085
|
slug: p.slug,
|
|
910
1086
|
name: p.title || p.project,
|
|
@@ -1258,6 +1434,7 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
1258
1434
|
get myUserId() { return myUserId; },
|
|
1259
1435
|
get projectOwnerId() { return currentProjectOwnerId; },
|
|
1260
1436
|
openDm: function (userId) { openDm(userId); },
|
|
1437
|
+
openMateWizard: function () { requireClayMateInterview(function () { openMateWizard(); }); },
|
|
1261
1438
|
openAddProjectModal: function () { openAddProjectModal(); },
|
|
1262
1439
|
sendWs: function (msg) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(msg)); },
|
|
1263
1440
|
onDmRemoveUser: function (userId) { dmRemovedUsers[userId] = true; },
|
|
@@ -1265,6 +1442,12 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
1265
1442
|
};
|
|
1266
1443
|
initSidebar(sidebarCtx);
|
|
1267
1444
|
initIconStrip(sidebarCtx);
|
|
1445
|
+
initMateSidebar(function () { return mateWs; });
|
|
1446
|
+
initMateKnowledge(function () { return mateWs; });
|
|
1447
|
+
initMateWizard(
|
|
1448
|
+
function (msg) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(msg)); },
|
|
1449
|
+
function (mate) { handleMateCreatedInApp(mate); }
|
|
1450
|
+
);
|
|
1268
1451
|
|
|
1269
1452
|
// --- Connect overlay (animated ASCII logo) ---
|
|
1270
1453
|
var asciiLogoCanvas = $("ascii-logo-canvas");
|
|
@@ -2914,6 +3097,11 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
2914
3097
|
}));
|
|
2915
3098
|
} catch(e) {}
|
|
2916
3099
|
}
|
|
3100
|
+
|
|
3101
|
+
// Request mates list
|
|
3102
|
+
try {
|
|
3103
|
+
ws.send(JSON.stringify({ type: "mate_list" }));
|
|
3104
|
+
} catch(e) {}
|
|
2917
3105
|
};
|
|
2918
3106
|
|
|
2919
3107
|
ws.onclose = function (e) {
|
|
@@ -3717,7 +3905,26 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
3717
3905
|
|
|
3718
3906
|
// --- DM ---
|
|
3719
3907
|
case "dm_history":
|
|
3908
|
+
// Attach projectSlug to targetUser for mate DMs
|
|
3909
|
+
if (msg.projectSlug && msg.targetUser) {
|
|
3910
|
+
msg.targetUser.projectSlug = msg.projectSlug;
|
|
3911
|
+
}
|
|
3720
3912
|
enterDmMode(msg.dmKey, msg.targetUser, msg.messages);
|
|
3913
|
+
// Auto-send first interview prompt after mate DM opens
|
|
3914
|
+
if (pendingMateInterview && msg.targetUser && msg.targetUser.isMate && msg.projectSlug) {
|
|
3915
|
+
var interviewMate = pendingMateInterview;
|
|
3916
|
+
pendingMateInterview = null;
|
|
3917
|
+
// Wait for mateWs to be swapped in as main ws, then send interview prompt
|
|
3918
|
+
var checkMateReady = setInterval(function () {
|
|
3919
|
+
if (ws && ws === mateWs && ws.readyState === 1) {
|
|
3920
|
+
clearInterval(checkMateReady);
|
|
3921
|
+
var interviewText = buildMateInterviewPrompt(interviewMate);
|
|
3922
|
+
// Send through normal input flow (ws is now mateWs)
|
|
3923
|
+
ws.send(JSON.stringify({ type: "message", text: interviewText }));
|
|
3924
|
+
}
|
|
3925
|
+
}, 100);
|
|
3926
|
+
setTimeout(function () { clearInterval(checkMateReady); }, 5000);
|
|
3927
|
+
}
|
|
3721
3928
|
break;
|
|
3722
3929
|
|
|
3723
3930
|
case "dm_message":
|
|
@@ -3731,7 +3938,7 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
3731
3938
|
if (fromId && fromId !== myUserId) {
|
|
3732
3939
|
dmUnread[fromId] = (dmUnread[fromId] || 0) + 1;
|
|
3733
3940
|
// Re-render strip so non-favorited sender appears
|
|
3734
|
-
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
|
|
3941
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
3735
3942
|
updateDmBadge(fromId, dmUnread[fromId]);
|
|
3736
3943
|
}
|
|
3737
3944
|
}
|
|
@@ -3763,7 +3970,55 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
3763
3970
|
}
|
|
3764
3971
|
}
|
|
3765
3972
|
cachedDmFavorites = msg.dmFavorites || [];
|
|
3766
|
-
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers);
|
|
3973
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
3974
|
+
break;
|
|
3975
|
+
|
|
3976
|
+
case "mate_created":
|
|
3977
|
+
handleMateCreatedInApp(msg.mate);
|
|
3978
|
+
break;
|
|
3979
|
+
|
|
3980
|
+
case "mate_deleted":
|
|
3981
|
+
cachedMatesList = cachedMatesList.filter(function (m) { return m.id !== msg.mateId; });
|
|
3982
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
3983
|
+
// If currently in DM with this mate, exit DM mode
|
|
3984
|
+
if (dmMode && dmTargetUser && dmTargetUser.id === msg.mateId) {
|
|
3985
|
+
exitDmMode();
|
|
3986
|
+
}
|
|
3987
|
+
break;
|
|
3988
|
+
|
|
3989
|
+
case "mate_updated":
|
|
3990
|
+
if (msg.mate) {
|
|
3991
|
+
for (var mi = 0; mi < cachedMatesList.length; mi++) {
|
|
3992
|
+
if (cachedMatesList[mi].id === msg.mate.id) {
|
|
3993
|
+
cachedMatesList[mi] = msg.mate;
|
|
3994
|
+
break;
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
3998
|
+
// Update mate sidebar if currently viewing this mate
|
|
3999
|
+
if (dmMode && dmTargetUser && dmTargetUser.isMate && dmTargetUser.id === msg.mate.id) {
|
|
4000
|
+
updateMateSidebarProfile(msg.mate);
|
|
4001
|
+
}
|
|
4002
|
+
// Update DM header if currently chatting with this mate
|
|
4003
|
+
if (dmMode && currentDmTarget === msg.mate.id) {
|
|
4004
|
+
var updatedName = (msg.mate.profile && msg.mate.profile.displayName) || msg.mate.name;
|
|
4005
|
+
if (updatedName) {
|
|
4006
|
+
var dmHeaderName = document.getElementById("dm-header-name");
|
|
4007
|
+
if (dmHeaderName) dmHeaderName.textContent = updatedName;
|
|
4008
|
+
var dmInput = document.getElementById("dm-input");
|
|
4009
|
+
if (dmInput) dmInput.placeholder = "Message " + updatedName;
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
break;
|
|
4014
|
+
|
|
4015
|
+
case "mate_list":
|
|
4016
|
+
cachedMatesList = msg.mates || [];
|
|
4017
|
+
renderUserStrip(cachedAllUsers, cachedOnlineIds, myUserId, cachedDmFavorites, cachedDmConversations, dmUnread, dmRemovedUsers, cachedMatesList);
|
|
4018
|
+
break;
|
|
4019
|
+
|
|
4020
|
+
case "mate_error":
|
|
4021
|
+
showToast(msg.error || "Mate operation failed", "error");
|
|
3767
4022
|
break;
|
|
3768
4023
|
|
|
3769
4024
|
case "daemon_config":
|
|
@@ -4074,7 +4329,7 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
4074
4329
|
showImageModal: showImageModal,
|
|
4075
4330
|
hideSuggestionChips: hideSuggestionChips,
|
|
4076
4331
|
setSendBtnMode: setSendBtnMode,
|
|
4077
|
-
isDmMode: function () { return dmMode; },
|
|
4332
|
+
isDmMode: function () { return dmMode && !(dmTargetUser && dmTargetUser.isMate); },
|
|
4078
4333
|
getDmKey: function () { return dmKey; },
|
|
4079
4334
|
handleDmSend: function () { handleDmSend(); },
|
|
4080
4335
|
});
|
|
@@ -4685,6 +4940,14 @@ import { initTooltips, registerTooltip } from './modules/tooltip.js';
|
|
|
4685
4940
|
}, cb);
|
|
4686
4941
|
}
|
|
4687
4942
|
|
|
4943
|
+
function requireClayMateInterview(cb) {
|
|
4944
|
+
requireSkills({
|
|
4945
|
+
title: "Skill Installation Required",
|
|
4946
|
+
reason: "The Mate Interview skill is required to create a new Mate.",
|
|
4947
|
+
skills: [{ name: "clay-mate-interview", url: "https://github.com/chadbyte/clay-mate-interview", scope: "global" }]
|
|
4948
|
+
}, cb);
|
|
4949
|
+
}
|
|
4950
|
+
|
|
4688
4951
|
// --- Ralph Wizard ---
|
|
4689
4952
|
|
|
4690
4953
|
function openRalphWizard() {
|