clay-server 2.31.0 → 2.32.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/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 +91 -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
|
@@ -1,15 +1,53 @@
|
|
|
1
1
|
// app-dm.js - DM mode, mate project switching, mate onboarding
|
|
2
2
|
// Extracted from app.js (PR-24)
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { store } from './store.js';
|
|
5
|
+
import { getWs } from './ws-ref.js';
|
|
6
|
+
import { getMessagesEl, getInputEl } from './dom-refs.js';
|
|
7
|
+
import { userAvatarUrl, mateAvatarUrl } from './avatar.js';
|
|
8
|
+
import { connect } from './app-connection.js';
|
|
9
|
+
import { resetClientState, renderProjectList, getCachedProjects } from './app-projects.js';
|
|
10
|
+
import { scrollToBottom } from './app-rendering.js';
|
|
11
|
+
import { autoResize } from './input.js';
|
|
12
|
+
import { showDebateSticky } from './app-debate-ui.js';
|
|
13
|
+
import { updateDmBadge, setCurrentDmUser, closeDmUserPicker } from './sidebar-mates.js';
|
|
14
|
+
import { hideHomeHub } from './app-home-hub.js';
|
|
15
|
+
import { hideNotes } from './sticky-notes.js';
|
|
16
|
+
import { showMateSidebar, hideMateSidebar } from './mate-sidebar.js';
|
|
17
|
+
import { hideKnowledge } from './mate-knowledge.js';
|
|
18
|
+
import { hideMemory } from './mate-memory.js';
|
|
19
|
+
import { closeFileViewer } from './filebrowser.js';
|
|
20
|
+
import { closeTerminal } from './terminal.js';
|
|
21
|
+
import { openMobileSheet, setMobileSheetMateData } from './sidebar-mobile.js';
|
|
22
|
+
import { getProfileLang } from './profile.js';
|
|
23
|
+
import { isSchedulerOpen, closeScheduler } from './scheduler.js';
|
|
24
|
+
import { requireClayMateInterview } from './app-skills-install.js';
|
|
25
|
+
import { syncResizeHandles } from './sidebar.js';
|
|
5
26
|
|
|
6
27
|
var MATE_ONBOARDING_KEY = "clay-mate-onboarding-shown";
|
|
7
28
|
var CLAUDE_CODE_AVATAR = "/claude-code-avatar.png";
|
|
8
29
|
var bgMateIoTimers = {};
|
|
9
30
|
var dmTypingTimer = null;
|
|
10
31
|
|
|
11
|
-
export function initDm(
|
|
12
|
-
|
|
32
|
+
export function initDm() {
|
|
33
|
+
// --- Reactive UI sync for dmMode ---
|
|
34
|
+
store.subscribe(function (state, prev) {
|
|
35
|
+
if (state.dmMode !== prev.dmMode) {
|
|
36
|
+
var isMate = state.dmTargetUser && state.dmTargetUser.isMate;
|
|
37
|
+
var mainCol = document.getElementById("main-column");
|
|
38
|
+
var sidebarCol = document.getElementById("sidebar-column");
|
|
39
|
+
var resizeHandle = document.getElementById("sidebar-resize-handle");
|
|
40
|
+
if (state.dmMode) {
|
|
41
|
+
if (!isMate && mainCol) mainCol.classList.add("dm-mode");
|
|
42
|
+
if (sidebarCol) sidebarCol.classList.add("dm-mode");
|
|
43
|
+
if (resizeHandle) resizeHandle.classList.add("dm-mode");
|
|
44
|
+
} else {
|
|
45
|
+
if (mainCol) mainCol.classList.remove("dm-mode");
|
|
46
|
+
if (sidebarCol) sidebarCol.classList.remove("dm-mode");
|
|
47
|
+
if (resizeHandle) resizeHandle.classList.remove("dm-mode");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
13
51
|
|
|
14
52
|
// --- Mobile mate title bar click handlers ---
|
|
15
53
|
var mobileBack = document.getElementById("mate-mobile-back");
|
|
@@ -24,30 +62,32 @@ export function initDm(ctx) {
|
|
|
24
62
|
if (mobileMore) {
|
|
25
63
|
mobileMore.addEventListener("click", function (e) {
|
|
26
64
|
e.stopPropagation();
|
|
27
|
-
|
|
65
|
+
openMobileSheet("mate-profile");
|
|
28
66
|
});
|
|
29
67
|
}
|
|
30
68
|
if (mobileTitle) {
|
|
31
69
|
mobileTitle.addEventListener("click", function () {
|
|
32
|
-
|
|
70
|
+
openMobileSheet("mate-profile");
|
|
33
71
|
});
|
|
34
72
|
}
|
|
35
73
|
}
|
|
36
74
|
|
|
37
75
|
export function openDm(targetUserId) {
|
|
38
|
-
|
|
76
|
+
var ws = getWs();
|
|
77
|
+
if (!ws || ws.readyState !== 1) return;
|
|
39
78
|
// Persist DM state for refresh recovery
|
|
40
79
|
try { localStorage.setItem("clay-active-dm", targetUserId); } catch (e) {}
|
|
41
80
|
// Check mate skill updates before opening mate DM
|
|
42
81
|
if (typeof targetUserId === "string" && targetUserId.indexOf("mate_") === 0) {
|
|
43
82
|
showMateOnboarding(function () {
|
|
44
|
-
|
|
45
|
-
|
|
83
|
+
requireClayMateInterview(function () {
|
|
84
|
+
var ws2 = getWs();
|
|
85
|
+
if (ws2) ws2.send(JSON.stringify({ type: "dm_open", targetUserId: targetUserId }));
|
|
46
86
|
});
|
|
47
87
|
});
|
|
48
88
|
return;
|
|
49
89
|
}
|
|
50
|
-
|
|
90
|
+
ws.send(JSON.stringify({ type: "dm_open", targetUserId: targetUserId }));
|
|
51
91
|
}
|
|
52
92
|
|
|
53
93
|
function showMateOnboarding(callback) {
|
|
@@ -96,11 +136,12 @@ function showMateOnboarding(callback) {
|
|
|
96
136
|
|
|
97
137
|
export function enterDmMode(key, targetUser, messages) {
|
|
98
138
|
console.log("[DEBUG enterDmMode] key=" + key, "isMate=" + (targetUser && targetUser.isMate), "messages=" + (messages ? messages.length : 0));
|
|
139
|
+
var s = store.snap();
|
|
99
140
|
// Clean up previous DM/mate state before entering new one
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
141
|
+
if (s.dmMode) {
|
|
142
|
+
hideMateSidebar();
|
|
143
|
+
hideKnowledge();
|
|
144
|
+
hideMemory();
|
|
104
145
|
// Reset dm-header-bar
|
|
105
146
|
var prevHeader = document.getElementById("dm-header-bar");
|
|
106
147
|
if (prevHeader) {
|
|
@@ -109,13 +150,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
109
150
|
var prevTag = prevHeader.querySelector(".dm-header-mate-tag");
|
|
110
151
|
if (prevTag) prevTag.remove();
|
|
111
152
|
}
|
|
112
|
-
//
|
|
113
|
-
var prevMain = document.getElementById("main-column");
|
|
114
|
-
if (prevMain) prevMain.classList.remove("dm-mode");
|
|
115
|
-
var prevSidebar = document.getElementById("sidebar-column");
|
|
116
|
-
if (prevSidebar) prevSidebar.classList.remove("dm-mode");
|
|
117
|
-
var prevResize = document.getElementById("sidebar-resize-handle");
|
|
118
|
-
if (prevResize) prevResize.classList.remove("dm-mode");
|
|
153
|
+
// dm-mode CSS classes stay managed by the store subscriber.
|
|
119
154
|
// Reset chat title bar
|
|
120
155
|
var prevTitleBar = document.querySelector(".title-bar-content");
|
|
121
156
|
if (prevTitleBar) {
|
|
@@ -124,9 +159,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
124
159
|
}
|
|
125
160
|
}
|
|
126
161
|
|
|
127
|
-
|
|
128
|
-
_ctx.dmKey = key;
|
|
129
|
-
_ctx.dmTargetUser = targetUser;
|
|
162
|
+
store.set({ dmMode: true, dmKey: key, dmTargetUser: targetUser });
|
|
130
163
|
|
|
131
164
|
// Notify server of active mate DM (server-side presence tracking)
|
|
132
165
|
// IMPORTANT: set_mate_dm must go to the MAIN project, not a mate project WS.
|
|
@@ -135,52 +168,48 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
135
168
|
// The server will also receive it via the mate project's onDmMessage handler,
|
|
136
169
|
// but the presence should only be stored on the main project slug.
|
|
137
170
|
if (targetUser && targetUser.isMate) {
|
|
171
|
+
var ws = getWs();
|
|
138
172
|
// Send to the current WS only if it's the main project (not another mate)
|
|
139
|
-
if (!
|
|
140
|
-
try {
|
|
173
|
+
if (!store.get('mateProjectSlug') && ws && ws.readyState === 1) {
|
|
174
|
+
try { ws.send(JSON.stringify({ type: "set_mate_dm", mateId: targetUser.id })); } catch(e) {}
|
|
141
175
|
}
|
|
142
176
|
}
|
|
143
177
|
|
|
144
178
|
// Clear unread for this user
|
|
145
179
|
if (targetUser) {
|
|
146
|
-
|
|
147
|
-
|
|
180
|
+
store.get('dmUnread')[targetUser.id] = 0;
|
|
181
|
+
updateDmBadge(targetUser.id, 0);
|
|
148
182
|
}
|
|
149
183
|
|
|
150
184
|
// Update icon strip active state
|
|
151
|
-
|
|
185
|
+
setCurrentDmUser(targetUser ? targetUser.id : null);
|
|
152
186
|
var activeProj = document.querySelector("#icon-strip-projects .icon-strip-item.active");
|
|
153
187
|
if (activeProj) activeProj.classList.remove("active");
|
|
154
188
|
var homeIcon = document.querySelector(".icon-strip-home");
|
|
155
189
|
if (homeIcon) homeIcon.classList.remove("active");
|
|
156
190
|
// Re-render user strip to show active state
|
|
157
|
-
|
|
158
|
-
|
|
191
|
+
var cp = getCachedProjects();
|
|
192
|
+
if (cp && cp.length > 0) {
|
|
193
|
+
renderProjectList();
|
|
159
194
|
}
|
|
160
195
|
|
|
161
196
|
// Hide home hub if visible
|
|
162
|
-
|
|
197
|
+
hideHomeHub();
|
|
163
198
|
|
|
164
199
|
// Hide sticky notes if visible
|
|
165
|
-
|
|
200
|
+
hideNotes();
|
|
166
201
|
|
|
167
202
|
var isMate = targetUser && targetUser.isMate;
|
|
168
203
|
|
|
169
|
-
//
|
|
170
|
-
var mainCol = document.getElementById("main-column");
|
|
171
|
-
if (mainCol && !isMate) mainCol.classList.add("dm-mode");
|
|
172
|
-
var sidebarCol = document.getElementById("sidebar-column");
|
|
173
|
-
if (sidebarCol) sidebarCol.classList.add("dm-mode");
|
|
174
|
-
var resizeHandle = document.getElementById("sidebar-resize-handle");
|
|
175
|
-
if (resizeHandle) resizeHandle.classList.add("dm-mode");
|
|
204
|
+
// dm-mode CSS classes are handled by the store subscriber above.
|
|
176
205
|
// Sync resize handles after DM sidebar appears
|
|
177
|
-
setTimeout(function () {
|
|
206
|
+
setTimeout(function () { syncResizeHandles(); }, 50);
|
|
178
207
|
if (isMate && targetUser.projectSlug) {
|
|
179
208
|
// Mate DM: switch to mate's project (same as project switching)
|
|
180
|
-
|
|
209
|
+
showMateSidebar(targetUser.id, targetUser);
|
|
181
210
|
// Close file viewer and terminal panel BEFORE switching WS (needs old WS still open)
|
|
182
|
-
try {
|
|
183
|
-
|
|
211
|
+
try { closeFileViewer(); } catch(e) {}
|
|
212
|
+
closeTerminal();
|
|
184
213
|
// Apply mate color to chat title bar and panels
|
|
185
214
|
var mateColor = (targetUser.profile && targetUser.profile.avatarColor) || targetUser.avatarColor || "#7c3aed";
|
|
186
215
|
document.body.style.setProperty("--mate-color", mateColor);
|
|
@@ -188,12 +217,12 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
188
217
|
document.body.classList.add("mate-dm-active");
|
|
189
218
|
// Build mate avatar URL for DM bubble injection
|
|
190
219
|
var mp = targetUser.profile || {};
|
|
191
|
-
var mateAvUrlDm =
|
|
192
|
-
var myUser =
|
|
220
|
+
var mateAvUrlDm = mateAvatarUrl(targetUser, 36);
|
|
221
|
+
var myUser = store.get('cachedAllUsers').find(function (u) { return u.id === store.get('myUserId'); });
|
|
193
222
|
if (!myUser) {
|
|
194
223
|
try { var cached = JSON.parse(localStorage.getItem("clay_my_user") || "null"); if (cached) myUser = cached; } catch(e) {}
|
|
195
224
|
}
|
|
196
|
-
var myAvatarUrl =
|
|
225
|
+
var myAvatarUrl = userAvatarUrl(myUser || { id: store.get('myUserId') }, 36);
|
|
197
226
|
var myDisplayName = (myUser && myUser.displayName) || "";
|
|
198
227
|
document.body.dataset.mateAvatarUrl = mateAvUrlDm;
|
|
199
228
|
document.body.dataset.mateName = mp.displayName || targetUser.displayName || targetUser.name || "";
|
|
@@ -219,7 +248,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
219
248
|
if (mateMobileStatus) mateMobileStatus.textContent = "online";
|
|
220
249
|
mateMobileTitle.classList.remove("hidden");
|
|
221
250
|
// Store mate data for profile sheet
|
|
222
|
-
|
|
251
|
+
setMobileSheetMateData({
|
|
223
252
|
id: targetUser.id,
|
|
224
253
|
displayName: mp.displayName || targetUser.displayName || targetUser.name || "",
|
|
225
254
|
description: mp.description || targetUser.description || "",
|
|
@@ -238,20 +267,22 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
238
267
|
if (userIsland && !isMate) userIsland.classList.add("dm-hidden");
|
|
239
268
|
|
|
240
269
|
// Render DM messages
|
|
241
|
-
|
|
242
|
-
|
|
270
|
+
store.set({ dmMessageCache: messages ? messages.slice() : [] });
|
|
271
|
+
var messagesEl = getMessagesEl();
|
|
272
|
+
messagesEl.innerHTML = "";
|
|
243
273
|
if (messages && messages.length > 0) {
|
|
244
274
|
for (var i = 0; i < messages.length; i++) {
|
|
245
275
|
appendDmMessage(messages[i]);
|
|
246
276
|
}
|
|
247
277
|
}
|
|
248
|
-
|
|
278
|
+
scrollToBottom();
|
|
249
279
|
|
|
250
280
|
// Focus input
|
|
251
|
-
|
|
281
|
+
var inputEl = getInputEl();
|
|
282
|
+
if (inputEl) {
|
|
252
283
|
var targetName = targetUser ? ((targetUser.profile && targetUser.profile.displayName) || targetUser.displayName || targetUser.name || "") : "";
|
|
253
|
-
|
|
254
|
-
|
|
284
|
+
inputEl.placeholder = "Message " + targetName;
|
|
285
|
+
inputEl.focus();
|
|
255
286
|
}
|
|
256
287
|
|
|
257
288
|
// Populate DM header bar with user avatar, name, and personal color
|
|
@@ -265,7 +296,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
265
296
|
} else {
|
|
266
297
|
if (dmHeaderBar) dmHeaderBar.style.display = "";
|
|
267
298
|
if (dmAvatar) {
|
|
268
|
-
dmAvatar.src =
|
|
299
|
+
dmAvatar.src = userAvatarUrl(targetUser, 28);
|
|
269
300
|
}
|
|
270
301
|
if (dmName) dmName.textContent = targetUser.displayName;
|
|
271
302
|
if (dmHeaderBar && targetUser.avatarColor) {
|
|
@@ -279,26 +310,19 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
279
310
|
}
|
|
280
311
|
|
|
281
312
|
export function exitDmMode(skipProjectSwitch) {
|
|
282
|
-
if (!
|
|
283
|
-
var wasMate =
|
|
284
|
-
|
|
285
|
-
_ctx.dmKey = null;
|
|
286
|
-
_ctx.dmTargetUser = null;
|
|
313
|
+
if (!store.get('dmMode')) return;
|
|
314
|
+
var wasMate = store.get('dmTargetUser') && store.get('dmTargetUser').isMate;
|
|
315
|
+
store.set({ dmMode: false, dmKey: null, dmTargetUser: null });
|
|
287
316
|
try { localStorage.removeItem("clay-active-dm"); } catch (e) {}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (mainCol) mainCol.classList.remove("dm-mode");
|
|
292
|
-
var sidebarCol = document.getElementById("sidebar-column");
|
|
293
|
-
if (sidebarCol) sidebarCol.classList.remove("dm-mode");
|
|
294
|
-
var resizeHandle = document.getElementById("sidebar-resize-handle");
|
|
295
|
-
if (resizeHandle) resizeHandle.classList.remove("dm-mode");
|
|
317
|
+
setCurrentDmUser(null);
|
|
318
|
+
|
|
319
|
+
// dm-mode CSS classes are handled by the store subscriber.
|
|
296
320
|
// Re-sync resize handle positions after DM width changes (defer to let layout settle)
|
|
297
|
-
setTimeout(function () {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (
|
|
321
|
+
setTimeout(function () { syncResizeHandles(); }, 100);
|
|
322
|
+
hideMateSidebar();
|
|
323
|
+
hideKnowledge();
|
|
324
|
+
hideMemory();
|
|
325
|
+
if (isSchedulerOpen()) closeScheduler();
|
|
302
326
|
// Reset DM header
|
|
303
327
|
var dmHeaderBar = document.getElementById("dm-header-bar");
|
|
304
328
|
if (dmHeaderBar) {
|
|
@@ -315,7 +339,8 @@ export function exitDmMode(skipProjectSwitch) {
|
|
|
315
339
|
delete document.body.dataset.mateName;
|
|
316
340
|
delete document.body.dataset.myAvatarUrl;
|
|
317
341
|
// Remove injected DM bubble avatars
|
|
318
|
-
var
|
|
342
|
+
var messagesEl = getMessagesEl();
|
|
343
|
+
var bubbleAvatars = messagesEl.querySelectorAll(".dm-bubble-avatar");
|
|
319
344
|
for (var ba = 0; ba < bubbleAvatars.length; ba++) bubbleAvatars[ba].remove();
|
|
320
345
|
var titleBarContent = document.querySelector(".title-bar-content");
|
|
321
346
|
if (titleBarContent) {
|
|
@@ -330,38 +355,40 @@ export function exitDmMode(skipProjectSwitch) {
|
|
|
330
355
|
var userIsland = document.getElementById("user-island");
|
|
331
356
|
if (userIsland) userIsland.classList.remove("dm-hidden");
|
|
332
357
|
|
|
333
|
-
|
|
358
|
+
var inputEl = getInputEl();
|
|
359
|
+
if (inputEl) inputEl.placeholder = "";
|
|
334
360
|
|
|
335
361
|
// Switch back to main project (same as project switching)
|
|
336
362
|
if (wasMate && !skipProjectSwitch) {
|
|
337
363
|
disconnectMateProject();
|
|
338
364
|
} else if (wasMate && skipProjectSwitch) {
|
|
339
365
|
// Just clean up mate state, caller will handle project switch
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
_ctx.savedMainSlug = null;
|
|
343
|
-
_ctx.showDebateSticky("hide", null);
|
|
366
|
+
store.set({ returningFromMateDm: true, mateProjectSlug: null, savedMainSlug: null });
|
|
367
|
+
showDebateSticky("hide", null);
|
|
344
368
|
var debateFloat = document.getElementById("debate-info-float");
|
|
345
369
|
if (debateFloat) { debateFloat.classList.add("hidden"); debateFloat.innerHTML = ""; }
|
|
346
370
|
} else {
|
|
347
371
|
// Human DM: just re-request state from main project
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
372
|
+
var ws = getWs();
|
|
373
|
+
if (ws && ws.readyState === 1) {
|
|
374
|
+
ws.send(JSON.stringify({ type: "switch_session", id: store.get('activeSessionId') }));
|
|
375
|
+
ws.send(JSON.stringify({ type: "note_list_request" }));
|
|
351
376
|
}
|
|
352
377
|
}
|
|
353
|
-
|
|
378
|
+
renderProjectList();
|
|
354
379
|
}
|
|
355
380
|
|
|
356
381
|
export function handleMateCreatedInApp(mate, msg) {
|
|
357
382
|
if (!mate) return;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (msg && msg.
|
|
361
|
-
|
|
383
|
+
var newMates = store.get('cachedMatesList').concat([mate]);
|
|
384
|
+
var updates = { cachedMatesList: newMates };
|
|
385
|
+
if (msg && msg.availableBuiltins) updates.cachedAvailableBuiltins = msg.availableBuiltins;
|
|
386
|
+
if (msg && msg.dmFavorites) updates.cachedDmFavorites = msg.dmFavorites;
|
|
387
|
+
store.set(updates);
|
|
388
|
+
// renderUserStrip is handled by the store subscriber
|
|
362
389
|
// Built-in mates handle their own onboarding via CLAUDE.md, skip auto-interview
|
|
363
390
|
if (!mate.builtinKey) {
|
|
364
|
-
|
|
391
|
+
store.set({ pendingMateInterview: mate });
|
|
365
392
|
}
|
|
366
393
|
openDm(mate.id);
|
|
367
394
|
}
|
|
@@ -402,18 +429,20 @@ export function renderAvailableBuiltins(builtins) {
|
|
|
402
429
|
addBtn.title = "Re-add " + b.displayName;
|
|
403
430
|
addBtn.addEventListener("click", function (e) {
|
|
404
431
|
e.stopPropagation();
|
|
405
|
-
|
|
406
|
-
|
|
432
|
+
var ws = getWs();
|
|
433
|
+
if (ws && ws.readyState === 1) {
|
|
434
|
+
ws.send(JSON.stringify({ type: "mate_readd_builtin", builtinKey: b.key }));
|
|
407
435
|
}
|
|
408
|
-
|
|
436
|
+
closeDmUserPicker();
|
|
409
437
|
});
|
|
410
438
|
item.appendChild(addBtn);
|
|
411
439
|
|
|
412
440
|
item.addEventListener("click", function () {
|
|
413
|
-
|
|
414
|
-
|
|
441
|
+
var ws = getWs();
|
|
442
|
+
if (ws && ws.readyState === 1) {
|
|
443
|
+
ws.send(JSON.stringify({ type: "mate_readd_builtin", builtinKey: b.key }));
|
|
415
444
|
}
|
|
416
|
-
|
|
445
|
+
closeDmUserPicker();
|
|
417
446
|
});
|
|
418
447
|
|
|
419
448
|
matesList.appendChild(item);
|
|
@@ -424,7 +453,7 @@ export function renderAvailableBuiltins(builtins) {
|
|
|
424
453
|
export function buildMateInterviewPrompt(mate) {
|
|
425
454
|
var sd = mate.seedData || {};
|
|
426
455
|
var parts = [];
|
|
427
|
-
var spokenLang =
|
|
456
|
+
var spokenLang = getProfileLang() || "en-US";
|
|
428
457
|
parts.push("Spoken Language: " + spokenLang);
|
|
429
458
|
if (sd.relationship) parts.push("Relationship: " + sd.relationship);
|
|
430
459
|
if (sd.activity && sd.activity.length > 0) parts.push("Activities: " + sd.activity.join(", "));
|
|
@@ -449,8 +478,8 @@ export function buildMateInterviewPrompt(mate) {
|
|
|
449
478
|
}
|
|
450
479
|
|
|
451
480
|
export function updateMateIconStatus(msg) {
|
|
452
|
-
if (!
|
|
453
|
-
var slug =
|
|
481
|
+
if (!store.get('mateProjectSlug')) return;
|
|
482
|
+
var slug = store.get('mateProjectSlug');
|
|
454
483
|
if (msg.type === "content" || msg.type === "tool" || msg.type === "tool_use" || msg.type === "thinking") {
|
|
455
484
|
var ioDot = document.querySelector('.icon-strip-mate[data-mate-slug="' + slug + '"] .icon-strip-status');
|
|
456
485
|
if (ioDot) {
|
|
@@ -474,42 +503,47 @@ export function updateMateIconStatus(msg) {
|
|
|
474
503
|
}
|
|
475
504
|
|
|
476
505
|
export function connectMateProject(slug) {
|
|
477
|
-
|
|
506
|
+
var s = store.snap();
|
|
507
|
+
store.set({ mateProjectSlug: slug });
|
|
478
508
|
// Only save the main slug on the FIRST mate switch (preserve original main project)
|
|
479
|
-
if (!
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
_ctx.connect();
|
|
509
|
+
if (!s.savedMainSlug) store.set({ savedMainSlug: s.currentSlug });
|
|
510
|
+
store.set({ currentSlug: slug, wsPath: "/p/" + slug + "/ws" });
|
|
511
|
+
resetClientState();
|
|
512
|
+
connect();
|
|
484
513
|
}
|
|
485
514
|
|
|
486
515
|
export function disconnectMateProject() {
|
|
487
|
-
|
|
516
|
+
store.set({ mateProjectSlug: null });
|
|
488
517
|
// Hide debate sticky when leaving mate DM
|
|
489
|
-
|
|
518
|
+
showDebateSticky("hide", null);
|
|
490
519
|
// Hide debate info float
|
|
491
520
|
var debateFloat = document.getElementById("debate-info-float");
|
|
492
521
|
if (debateFloat) { debateFloat.classList.add("hidden"); debateFloat.innerHTML = ""; }
|
|
493
522
|
// Switch back to main project
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
523
|
+
var savedMainSlug = store.get('savedMainSlug');
|
|
524
|
+
if (savedMainSlug) {
|
|
525
|
+
store.set({
|
|
526
|
+
returningFromMateDm: true,
|
|
527
|
+
currentSlug: savedMainSlug,
|
|
528
|
+
basePath: "/p/" + savedMainSlug + "/",
|
|
529
|
+
wsPath: "/p/" + savedMainSlug + "/ws",
|
|
530
|
+
savedMainSlug: null
|
|
531
|
+
});
|
|
532
|
+
resetClientState();
|
|
533
|
+
connect();
|
|
502
534
|
}
|
|
503
535
|
}
|
|
504
536
|
|
|
505
537
|
export function appendDmMessage(msg) {
|
|
506
|
-
|
|
507
|
-
|
|
538
|
+
var s = store.snap();
|
|
539
|
+
if (s.dmMode) s.dmMessageCache.push(msg);
|
|
540
|
+
var isMe = msg.from === s.myUserId;
|
|
508
541
|
var d = new Date(msg.ts);
|
|
509
542
|
var timeStr = d.getHours().toString().padStart(2, "0") + ":" + d.getMinutes().toString().padStart(2, "0");
|
|
510
543
|
|
|
544
|
+
var messagesEl = getMessagesEl();
|
|
511
545
|
// Check if we can compact (same sender as previous, within 5 min)
|
|
512
|
-
var prev =
|
|
546
|
+
var prev = messagesEl.lastElementChild;
|
|
513
547
|
var compact = false;
|
|
514
548
|
if (prev && prev.dataset.from === msg.from) {
|
|
515
549
|
var prevTs = parseInt(prev.dataset.ts || "0", 10);
|
|
@@ -537,10 +571,10 @@ export function appendDmMessage(msg) {
|
|
|
537
571
|
var avatar = document.createElement("img");
|
|
538
572
|
avatar.className = "dm-msg-avatar";
|
|
539
573
|
if (isMe) {
|
|
540
|
-
var myUser =
|
|
541
|
-
avatar.src =
|
|
542
|
-
} else if (
|
|
543
|
-
avatar.src =
|
|
574
|
+
var myUser = s.cachedAllUsers.find(function (u) { return u.id === s.myUserId; });
|
|
575
|
+
avatar.src = userAvatarUrl(myUser || { id: s.myUserId }, 36);
|
|
576
|
+
} else if (s.dmTargetUser) {
|
|
577
|
+
avatar.src = userAvatarUrl(s.dmTargetUser, 36);
|
|
544
578
|
}
|
|
545
579
|
div.appendChild(avatar);
|
|
546
580
|
|
|
@@ -553,10 +587,10 @@ export function appendDmMessage(msg) {
|
|
|
553
587
|
var name = document.createElement("span");
|
|
554
588
|
name.className = "dm-msg-name";
|
|
555
589
|
if (isMe) {
|
|
556
|
-
var mu =
|
|
590
|
+
var mu = s.cachedAllUsers.find(function (u) { return u.id === s.myUserId; });
|
|
557
591
|
name.textContent = mu ? mu.displayName : "Me";
|
|
558
592
|
} else {
|
|
559
|
-
name.textContent =
|
|
593
|
+
name.textContent = s.dmTargetUser ? s.dmTargetUser.displayName : "User";
|
|
560
594
|
}
|
|
561
595
|
header.appendChild(name);
|
|
562
596
|
|
|
@@ -575,7 +609,7 @@ export function appendDmMessage(msg) {
|
|
|
575
609
|
div.appendChild(content);
|
|
576
610
|
}
|
|
577
611
|
|
|
578
|
-
|
|
612
|
+
messagesEl.appendChild(div);
|
|
579
613
|
}
|
|
580
614
|
|
|
581
615
|
export function showDmTypingIndicator(typing) {
|
|
@@ -585,7 +619,8 @@ export function showDmTypingIndicator(typing) {
|
|
|
585
619
|
return;
|
|
586
620
|
}
|
|
587
621
|
if (existing) return; // already showing
|
|
588
|
-
|
|
622
|
+
var dmTargetUser = store.get('dmTargetUser');
|
|
623
|
+
if (!dmTargetUser) return;
|
|
589
624
|
|
|
590
625
|
var div = document.createElement("div");
|
|
591
626
|
div.id = "dm-typing-indicator";
|
|
@@ -593,7 +628,7 @@ export function showDmTypingIndicator(typing) {
|
|
|
593
628
|
|
|
594
629
|
var avatar = document.createElement("img");
|
|
595
630
|
avatar.className = "dm-msg-avatar";
|
|
596
|
-
avatar.src =
|
|
631
|
+
avatar.src = userAvatarUrl(dmTargetUser, 36);
|
|
597
632
|
div.appendChild(avatar);
|
|
598
633
|
|
|
599
634
|
var dots = document.createElement("div");
|
|
@@ -601,8 +636,9 @@ export function showDmTypingIndicator(typing) {
|
|
|
601
636
|
dots.innerHTML = "<span></span><span></span><span></span>";
|
|
602
637
|
div.appendChild(dots);
|
|
603
638
|
|
|
604
|
-
|
|
605
|
-
|
|
639
|
+
var messagesEl = getMessagesEl();
|
|
640
|
+
messagesEl.appendChild(div);
|
|
641
|
+
scrollToBottom();
|
|
606
642
|
|
|
607
643
|
// Auto-hide after 5s in case stop signal is missed
|
|
608
644
|
clearTimeout(dmTypingTimer);
|
|
@@ -612,11 +648,14 @@ export function showDmTypingIndicator(typing) {
|
|
|
612
648
|
}
|
|
613
649
|
|
|
614
650
|
export function handleDmSend() {
|
|
615
|
-
|
|
616
|
-
var
|
|
651
|
+
var s = store.snap();
|
|
652
|
+
var inputEl = getInputEl();
|
|
653
|
+
if (!s.dmMode || !s.dmKey || !inputEl) return false;
|
|
654
|
+
var text = inputEl.value.trim();
|
|
617
655
|
if (!text) return false;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
656
|
+
var ws = getWs();
|
|
657
|
+
ws.send(JSON.stringify({ type: "dm_send", dmKey: s.dmKey, text: text }));
|
|
658
|
+
inputEl.value = "";
|
|
659
|
+
autoResize();
|
|
621
660
|
return true;
|
|
622
661
|
}
|