clay-server 2.31.0 → 2.32.0-beta.10
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/codex-defaults.js +18 -0
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +9 -6
- package/lib/project-debate.js +8 -0
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mate-interaction.js +102 -16
- package/lib/project-mcp.js +4 -0
- package/lib/project-notifications.js +9 -0
- package/lib/project-sessions.js +94 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +234 -99
- package/lib/public/app.js +135 -454
- 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 +338 -104
- 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 +26 -0
- package/lib/public/css/tooltip.css +47 -0
- package/lib/public/index.html +78 -26
- 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 +175 -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 +199 -153
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +119 -9
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +161 -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 +102 -66
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +122 -0
- package/lib/public/modules/markdown.js +5 -1
- 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 +79 -35
- 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/terminal.js +12 -0
- package/lib/public/modules/tools.js +18 -13
- package/lib/public/modules/tooltip.js +32 -5
- package/lib/sdk-bridge.js +562 -1114
- package/lib/sdk-message-processor.js +150 -135
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +81 -37
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1483 -0
- package/lib/yoke/adapters/codex.js +1121 -0
- package/lib/yoke/adapters/gemini.js +709 -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 +98 -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,50 @@ 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();
|
|
213
|
+
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
214
|
+
if (termBtn) termBtn.style.display = "none";
|
|
184
215
|
// Apply mate color to chat title bar and panels
|
|
185
216
|
var mateColor = (targetUser.profile && targetUser.profile.avatarColor) || targetUser.avatarColor || "#7c3aed";
|
|
186
217
|
document.body.style.setProperty("--mate-color", mateColor);
|
|
@@ -188,12 +219,12 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
188
219
|
document.body.classList.add("mate-dm-active");
|
|
189
220
|
// Build mate avatar URL for DM bubble injection
|
|
190
221
|
var mp = targetUser.profile || {};
|
|
191
|
-
var mateAvUrlDm =
|
|
192
|
-
var myUser =
|
|
222
|
+
var mateAvUrlDm = mateAvatarUrl(targetUser, 36);
|
|
223
|
+
var myUser = store.get('cachedAllUsers').find(function (u) { return u.id === store.get('myUserId'); });
|
|
193
224
|
if (!myUser) {
|
|
194
225
|
try { var cached = JSON.parse(localStorage.getItem("clay_my_user") || "null"); if (cached) myUser = cached; } catch(e) {}
|
|
195
226
|
}
|
|
196
|
-
var myAvatarUrl =
|
|
227
|
+
var myAvatarUrl = userAvatarUrl(myUser || { id: store.get('myUserId') }, 36);
|
|
197
228
|
var myDisplayName = (myUser && myUser.displayName) || "";
|
|
198
229
|
document.body.dataset.mateAvatarUrl = mateAvUrlDm;
|
|
199
230
|
document.body.dataset.mateName = mp.displayName || targetUser.displayName || targetUser.name || "";
|
|
@@ -219,7 +250,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
219
250
|
if (mateMobileStatus) mateMobileStatus.textContent = "online";
|
|
220
251
|
mateMobileTitle.classList.remove("hidden");
|
|
221
252
|
// Store mate data for profile sheet
|
|
222
|
-
|
|
253
|
+
setMobileSheetMateData({
|
|
223
254
|
id: targetUser.id,
|
|
224
255
|
displayName: mp.displayName || targetUser.displayName || targetUser.name || "",
|
|
225
256
|
description: mp.description || targetUser.description || "",
|
|
@@ -238,20 +269,22 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
238
269
|
if (userIsland && !isMate) userIsland.classList.add("dm-hidden");
|
|
239
270
|
|
|
240
271
|
// Render DM messages
|
|
241
|
-
|
|
242
|
-
|
|
272
|
+
store.set({ dmMessageCache: messages ? messages.slice() : [] });
|
|
273
|
+
var messagesEl = getMessagesEl();
|
|
274
|
+
messagesEl.innerHTML = "";
|
|
243
275
|
if (messages && messages.length > 0) {
|
|
244
276
|
for (var i = 0; i < messages.length; i++) {
|
|
245
277
|
appendDmMessage(messages[i]);
|
|
246
278
|
}
|
|
247
279
|
}
|
|
248
|
-
|
|
280
|
+
scrollToBottom();
|
|
249
281
|
|
|
250
282
|
// Focus input
|
|
251
|
-
|
|
283
|
+
var inputEl = getInputEl();
|
|
284
|
+
if (inputEl) {
|
|
252
285
|
var targetName = targetUser ? ((targetUser.profile && targetUser.profile.displayName) || targetUser.displayName || targetUser.name || "") : "";
|
|
253
|
-
|
|
254
|
-
|
|
286
|
+
inputEl.placeholder = "Message " + targetName;
|
|
287
|
+
inputEl.focus();
|
|
255
288
|
}
|
|
256
289
|
|
|
257
290
|
// Populate DM header bar with user avatar, name, and personal color
|
|
@@ -265,7 +298,7 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
265
298
|
} else {
|
|
266
299
|
if (dmHeaderBar) dmHeaderBar.style.display = "";
|
|
267
300
|
if (dmAvatar) {
|
|
268
|
-
dmAvatar.src =
|
|
301
|
+
dmAvatar.src = userAvatarUrl(targetUser, 28);
|
|
269
302
|
}
|
|
270
303
|
if (dmName) dmName.textContent = targetUser.displayName;
|
|
271
304
|
if (dmHeaderBar && targetUser.avatarColor) {
|
|
@@ -279,26 +312,22 @@ export function enterDmMode(key, targetUser, messages) {
|
|
|
279
312
|
}
|
|
280
313
|
|
|
281
314
|
export function exitDmMode(skipProjectSwitch) {
|
|
282
|
-
if (!
|
|
283
|
-
var wasMate =
|
|
284
|
-
|
|
285
|
-
_ctx.dmKey = null;
|
|
286
|
-
_ctx.dmTargetUser = null;
|
|
315
|
+
if (!store.get('dmMode')) return;
|
|
316
|
+
var wasMate = store.get('dmTargetUser') && store.get('dmTargetUser').isMate;
|
|
317
|
+
store.set({ dmMode: false, dmKey: null, dmTargetUser: null });
|
|
287
318
|
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");
|
|
319
|
+
setCurrentDmUser(null);
|
|
320
|
+
|
|
321
|
+
// dm-mode CSS classes are handled by the store subscriber.
|
|
296
322
|
// Re-sync resize handle positions after DM width changes (defer to let layout settle)
|
|
297
|
-
setTimeout(function () {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (
|
|
323
|
+
setTimeout(function () { syncResizeHandles(); }, 100);
|
|
324
|
+
hideMateSidebar();
|
|
325
|
+
hideKnowledge();
|
|
326
|
+
hideMemory();
|
|
327
|
+
if (isSchedulerOpen()) closeScheduler();
|
|
328
|
+
// Restore terminal button
|
|
329
|
+
var termBtn = document.getElementById("terminal-toggle-btn");
|
|
330
|
+
if (termBtn) termBtn.style.display = "";
|
|
302
331
|
// Reset DM header
|
|
303
332
|
var dmHeaderBar = document.getElementById("dm-header-bar");
|
|
304
333
|
if (dmHeaderBar) {
|
|
@@ -315,7 +344,8 @@ export function exitDmMode(skipProjectSwitch) {
|
|
|
315
344
|
delete document.body.dataset.mateName;
|
|
316
345
|
delete document.body.dataset.myAvatarUrl;
|
|
317
346
|
// Remove injected DM bubble avatars
|
|
318
|
-
var
|
|
347
|
+
var messagesEl = getMessagesEl();
|
|
348
|
+
var bubbleAvatars = messagesEl.querySelectorAll(".dm-bubble-avatar");
|
|
319
349
|
for (var ba = 0; ba < bubbleAvatars.length; ba++) bubbleAvatars[ba].remove();
|
|
320
350
|
var titleBarContent = document.querySelector(".title-bar-content");
|
|
321
351
|
if (titleBarContent) {
|
|
@@ -330,38 +360,40 @@ export function exitDmMode(skipProjectSwitch) {
|
|
|
330
360
|
var userIsland = document.getElementById("user-island");
|
|
331
361
|
if (userIsland) userIsland.classList.remove("dm-hidden");
|
|
332
362
|
|
|
333
|
-
|
|
363
|
+
var inputEl = getInputEl();
|
|
364
|
+
if (inputEl) inputEl.placeholder = "";
|
|
334
365
|
|
|
335
366
|
// Switch back to main project (same as project switching)
|
|
336
367
|
if (wasMate && !skipProjectSwitch) {
|
|
337
368
|
disconnectMateProject();
|
|
338
369
|
} else if (wasMate && skipProjectSwitch) {
|
|
339
370
|
// Just clean up mate state, caller will handle project switch
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
_ctx.savedMainSlug = null;
|
|
343
|
-
_ctx.showDebateSticky("hide", null);
|
|
371
|
+
store.set({ returningFromMateDm: true, mateProjectSlug: null, savedMainSlug: null });
|
|
372
|
+
showDebateSticky("hide", null);
|
|
344
373
|
var debateFloat = document.getElementById("debate-info-float");
|
|
345
374
|
if (debateFloat) { debateFloat.classList.add("hidden"); debateFloat.innerHTML = ""; }
|
|
346
375
|
} else {
|
|
347
376
|
// Human DM: just re-request state from main project
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
377
|
+
var ws = getWs();
|
|
378
|
+
if (ws && ws.readyState === 1) {
|
|
379
|
+
ws.send(JSON.stringify({ type: "switch_session", id: store.get('activeSessionId') }));
|
|
380
|
+
ws.send(JSON.stringify({ type: "note_list_request" }));
|
|
351
381
|
}
|
|
352
382
|
}
|
|
353
|
-
|
|
383
|
+
renderProjectList();
|
|
354
384
|
}
|
|
355
385
|
|
|
356
386
|
export function handleMateCreatedInApp(mate, msg) {
|
|
357
387
|
if (!mate) return;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (msg && msg.
|
|
361
|
-
|
|
388
|
+
var newMates = store.get('cachedMatesList').concat([mate]);
|
|
389
|
+
var updates = { cachedMatesList: newMates };
|
|
390
|
+
if (msg && msg.availableBuiltins) updates.cachedAvailableBuiltins = msg.availableBuiltins;
|
|
391
|
+
if (msg && msg.dmFavorites) updates.cachedDmFavorites = msg.dmFavorites;
|
|
392
|
+
store.set(updates);
|
|
393
|
+
// renderUserStrip is handled by the store subscriber
|
|
362
394
|
// Built-in mates handle their own onboarding via CLAUDE.md, skip auto-interview
|
|
363
395
|
if (!mate.builtinKey) {
|
|
364
|
-
|
|
396
|
+
store.set({ pendingMateInterview: mate });
|
|
365
397
|
}
|
|
366
398
|
openDm(mate.id);
|
|
367
399
|
}
|
|
@@ -402,18 +434,20 @@ export function renderAvailableBuiltins(builtins) {
|
|
|
402
434
|
addBtn.title = "Re-add " + b.displayName;
|
|
403
435
|
addBtn.addEventListener("click", function (e) {
|
|
404
436
|
e.stopPropagation();
|
|
405
|
-
|
|
406
|
-
|
|
437
|
+
var ws = getWs();
|
|
438
|
+
if (ws && ws.readyState === 1) {
|
|
439
|
+
ws.send(JSON.stringify({ type: "mate_readd_builtin", builtinKey: b.key }));
|
|
407
440
|
}
|
|
408
|
-
|
|
441
|
+
closeDmUserPicker();
|
|
409
442
|
});
|
|
410
443
|
item.appendChild(addBtn);
|
|
411
444
|
|
|
412
445
|
item.addEventListener("click", function () {
|
|
413
|
-
|
|
414
|
-
|
|
446
|
+
var ws = getWs();
|
|
447
|
+
if (ws && ws.readyState === 1) {
|
|
448
|
+
ws.send(JSON.stringify({ type: "mate_readd_builtin", builtinKey: b.key }));
|
|
415
449
|
}
|
|
416
|
-
|
|
450
|
+
closeDmUserPicker();
|
|
417
451
|
});
|
|
418
452
|
|
|
419
453
|
matesList.appendChild(item);
|
|
@@ -424,7 +458,7 @@ export function renderAvailableBuiltins(builtins) {
|
|
|
424
458
|
export function buildMateInterviewPrompt(mate) {
|
|
425
459
|
var sd = mate.seedData || {};
|
|
426
460
|
var parts = [];
|
|
427
|
-
var spokenLang =
|
|
461
|
+
var spokenLang = getProfileLang() || "en-US";
|
|
428
462
|
parts.push("Spoken Language: " + spokenLang);
|
|
429
463
|
if (sd.relationship) parts.push("Relationship: " + sd.relationship);
|
|
430
464
|
if (sd.activity && sd.activity.length > 0) parts.push("Activities: " + sd.activity.join(", "));
|
|
@@ -449,8 +483,8 @@ export function buildMateInterviewPrompt(mate) {
|
|
|
449
483
|
}
|
|
450
484
|
|
|
451
485
|
export function updateMateIconStatus(msg) {
|
|
452
|
-
if (!
|
|
453
|
-
var slug =
|
|
486
|
+
if (!store.get('mateProjectSlug')) return;
|
|
487
|
+
var slug = store.get('mateProjectSlug');
|
|
454
488
|
if (msg.type === "content" || msg.type === "tool" || msg.type === "tool_use" || msg.type === "thinking") {
|
|
455
489
|
var ioDot = document.querySelector('.icon-strip-mate[data-mate-slug="' + slug + '"] .icon-strip-status');
|
|
456
490
|
if (ioDot) {
|
|
@@ -474,42 +508,47 @@ export function updateMateIconStatus(msg) {
|
|
|
474
508
|
}
|
|
475
509
|
|
|
476
510
|
export function connectMateProject(slug) {
|
|
477
|
-
|
|
511
|
+
var s = store.snap();
|
|
512
|
+
store.set({ mateProjectSlug: slug });
|
|
478
513
|
// Only save the main slug on the FIRST mate switch (preserve original main project)
|
|
479
|
-
if (!
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
_ctx.connect();
|
|
514
|
+
if (!s.savedMainSlug) store.set({ savedMainSlug: s.currentSlug });
|
|
515
|
+
store.set({ currentSlug: slug, wsPath: "/p/" + slug + "/ws" });
|
|
516
|
+
resetClientState();
|
|
517
|
+
connect();
|
|
484
518
|
}
|
|
485
519
|
|
|
486
520
|
export function disconnectMateProject() {
|
|
487
|
-
|
|
521
|
+
store.set({ mateProjectSlug: null });
|
|
488
522
|
// Hide debate sticky when leaving mate DM
|
|
489
|
-
|
|
523
|
+
showDebateSticky("hide", null);
|
|
490
524
|
// Hide debate info float
|
|
491
525
|
var debateFloat = document.getElementById("debate-info-float");
|
|
492
526
|
if (debateFloat) { debateFloat.classList.add("hidden"); debateFloat.innerHTML = ""; }
|
|
493
527
|
// Switch back to main project
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
528
|
+
var savedMainSlug = store.get('savedMainSlug');
|
|
529
|
+
if (savedMainSlug) {
|
|
530
|
+
store.set({
|
|
531
|
+
returningFromMateDm: true,
|
|
532
|
+
currentSlug: savedMainSlug,
|
|
533
|
+
basePath: "/p/" + savedMainSlug + "/",
|
|
534
|
+
wsPath: "/p/" + savedMainSlug + "/ws",
|
|
535
|
+
savedMainSlug: null
|
|
536
|
+
});
|
|
537
|
+
resetClientState();
|
|
538
|
+
connect();
|
|
502
539
|
}
|
|
503
540
|
}
|
|
504
541
|
|
|
505
542
|
export function appendDmMessage(msg) {
|
|
506
|
-
|
|
507
|
-
|
|
543
|
+
var s = store.snap();
|
|
544
|
+
if (s.dmMode) s.dmMessageCache.push(msg);
|
|
545
|
+
var isMe = msg.from === s.myUserId;
|
|
508
546
|
var d = new Date(msg.ts);
|
|
509
547
|
var timeStr = d.getHours().toString().padStart(2, "0") + ":" + d.getMinutes().toString().padStart(2, "0");
|
|
510
548
|
|
|
549
|
+
var messagesEl = getMessagesEl();
|
|
511
550
|
// Check if we can compact (same sender as previous, within 5 min)
|
|
512
|
-
var prev =
|
|
551
|
+
var prev = messagesEl.lastElementChild;
|
|
513
552
|
var compact = false;
|
|
514
553
|
if (prev && prev.dataset.from === msg.from) {
|
|
515
554
|
var prevTs = parseInt(prev.dataset.ts || "0", 10);
|
|
@@ -537,10 +576,10 @@ export function appendDmMessage(msg) {
|
|
|
537
576
|
var avatar = document.createElement("img");
|
|
538
577
|
avatar.className = "dm-msg-avatar";
|
|
539
578
|
if (isMe) {
|
|
540
|
-
var myUser =
|
|
541
|
-
avatar.src =
|
|
542
|
-
} else if (
|
|
543
|
-
avatar.src =
|
|
579
|
+
var myUser = s.cachedAllUsers.find(function (u) { return u.id === s.myUserId; });
|
|
580
|
+
avatar.src = userAvatarUrl(myUser || { id: s.myUserId }, 36);
|
|
581
|
+
} else if (s.dmTargetUser) {
|
|
582
|
+
avatar.src = userAvatarUrl(s.dmTargetUser, 36);
|
|
544
583
|
}
|
|
545
584
|
div.appendChild(avatar);
|
|
546
585
|
|
|
@@ -553,10 +592,10 @@ export function appendDmMessage(msg) {
|
|
|
553
592
|
var name = document.createElement("span");
|
|
554
593
|
name.className = "dm-msg-name";
|
|
555
594
|
if (isMe) {
|
|
556
|
-
var mu =
|
|
595
|
+
var mu = s.cachedAllUsers.find(function (u) { return u.id === s.myUserId; });
|
|
557
596
|
name.textContent = mu ? mu.displayName : "Me";
|
|
558
597
|
} else {
|
|
559
|
-
name.textContent =
|
|
598
|
+
name.textContent = s.dmTargetUser ? s.dmTargetUser.displayName : "User";
|
|
560
599
|
}
|
|
561
600
|
header.appendChild(name);
|
|
562
601
|
|
|
@@ -575,7 +614,7 @@ export function appendDmMessage(msg) {
|
|
|
575
614
|
div.appendChild(content);
|
|
576
615
|
}
|
|
577
616
|
|
|
578
|
-
|
|
617
|
+
messagesEl.appendChild(div);
|
|
579
618
|
}
|
|
580
619
|
|
|
581
620
|
export function showDmTypingIndicator(typing) {
|
|
@@ -585,7 +624,8 @@ export function showDmTypingIndicator(typing) {
|
|
|
585
624
|
return;
|
|
586
625
|
}
|
|
587
626
|
if (existing) return; // already showing
|
|
588
|
-
|
|
627
|
+
var dmTargetUser = store.get('dmTargetUser');
|
|
628
|
+
if (!dmTargetUser) return;
|
|
589
629
|
|
|
590
630
|
var div = document.createElement("div");
|
|
591
631
|
div.id = "dm-typing-indicator";
|
|
@@ -593,7 +633,7 @@ export function showDmTypingIndicator(typing) {
|
|
|
593
633
|
|
|
594
634
|
var avatar = document.createElement("img");
|
|
595
635
|
avatar.className = "dm-msg-avatar";
|
|
596
|
-
avatar.src =
|
|
636
|
+
avatar.src = userAvatarUrl(dmTargetUser, 36);
|
|
597
637
|
div.appendChild(avatar);
|
|
598
638
|
|
|
599
639
|
var dots = document.createElement("div");
|
|
@@ -601,8 +641,9 @@ export function showDmTypingIndicator(typing) {
|
|
|
601
641
|
dots.innerHTML = "<span></span><span></span><span></span>";
|
|
602
642
|
div.appendChild(dots);
|
|
603
643
|
|
|
604
|
-
|
|
605
|
-
|
|
644
|
+
var messagesEl = getMessagesEl();
|
|
645
|
+
messagesEl.appendChild(div);
|
|
646
|
+
scrollToBottom();
|
|
606
647
|
|
|
607
648
|
// Auto-hide after 5s in case stop signal is missed
|
|
608
649
|
clearTimeout(dmTypingTimer);
|
|
@@ -612,11 +653,14 @@ export function showDmTypingIndicator(typing) {
|
|
|
612
653
|
}
|
|
613
654
|
|
|
614
655
|
export function handleDmSend() {
|
|
615
|
-
|
|
616
|
-
var
|
|
656
|
+
var s = store.snap();
|
|
657
|
+
var inputEl = getInputEl();
|
|
658
|
+
if (!s.dmMode || !s.dmKey || !inputEl) return false;
|
|
659
|
+
var text = inputEl.value.trim();
|
|
617
660
|
if (!text) return false;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
661
|
+
var ws = getWs();
|
|
662
|
+
ws.send(JSON.stringify({ type: "dm_send", dmKey: s.dmKey, text: text }));
|
|
663
|
+
inputEl.value = "";
|
|
664
|
+
autoResize();
|
|
621
665
|
return true;
|
|
622
666
|
}
|