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
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
// app-projects.js - Project list, switching, add/remove project modals
|
|
2
2
|
// Extracted from app.js (PR-29)
|
|
3
3
|
|
|
4
|
-
import { escapeHtml } from './utils.js';
|
|
4
|
+
import { escapeHtml, showToast } from './utils.js';
|
|
5
5
|
import { refreshIcons } from './icons.js';
|
|
6
6
|
import { parseEmojis } from './markdown.js';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { store } from './store.js';
|
|
8
|
+
import { getWs, setWs } from './ws-ref.js';
|
|
9
|
+
import { getMessagesEl, getStatusDot } from './dom-refs.js';
|
|
10
|
+
import { userAvatarUrl } from './avatar.js';
|
|
11
|
+
import { showConfirm } from './app-misc.js';
|
|
12
|
+
// renderUserStrip is now reactive via store subscriber in sidebar-mates.js
|
|
13
|
+
import { renderIconStrip } from './sidebar-projects.js';
|
|
14
|
+
import { updateCrossProjectBlink, stopUrgentBlink, setActivity } from './app-favicon.js';
|
|
15
|
+
import { spawnDustParticles } from './sidebar.js';
|
|
16
|
+
import { isSearchOpen, closeSearch } from './session-search.js';
|
|
17
|
+
import { exitDmMode } from './app-dm.js';
|
|
18
|
+
import { isHomeHubVisible, hideHomeHub, showHomeHub } from './app-home-hub.js';
|
|
19
|
+
import { resetFileBrowser } from './filebrowser.js';
|
|
20
|
+
import { closeArchive } from './sticky-notes.js';
|
|
21
|
+
import { hideMemory } from './mate-memory.js';
|
|
22
|
+
import { isSchedulerOpen, closeScheduler, resetScheduler } from './scheduler.js';
|
|
23
|
+
import { connect, cancelReconnect, setStatus } from './app-connection.js';
|
|
24
|
+
import { setTurnCounter, setPrependAnchor, setActivityEl, setIsUserScrolledUp, hideSuggestionChips } from './app-rendering.js';
|
|
25
|
+
import { resetToolState, enableMainInput, resetTurnMetaCost } from './tools.js';
|
|
26
|
+
import { clearPendingImages } from './input.js';
|
|
27
|
+
import { setRewindMode } from './rewind.js';
|
|
28
|
+
import { resetUsage, resetContext } from './app-panels.js';
|
|
29
|
+
import { resetRateLimitState } from './app-rate-limit.js';
|
|
30
|
+
import { closeSessionInfoPopover } from './app-header.js';
|
|
31
|
+
import { resetDebateState } from './debate.js';
|
|
32
|
+
import { removeDebateBottomBar } from './app-debate-ui.js';
|
|
9
33
|
|
|
10
34
|
// --- Module-owned state ---
|
|
11
35
|
var cachedProjects = [];
|
|
@@ -33,9 +57,7 @@ var addProjectDebounce = null;
|
|
|
33
57
|
var addProjectActiveIdx = -1;
|
|
34
58
|
var addProjectMode = "existing";
|
|
35
59
|
|
|
36
|
-
export function initProjects(
|
|
37
|
-
_ctx = ctx;
|
|
38
|
-
|
|
60
|
+
export function initProjects() {
|
|
39
61
|
// Init add-project modal DOM refs
|
|
40
62
|
addProjectModal = document.getElementById("add-project-modal");
|
|
41
63
|
addProjectInput = document.getElementById("add-project-input");
|
|
@@ -160,7 +182,7 @@ export function initProjects(ctx) {
|
|
|
160
182
|
});
|
|
161
183
|
|
|
162
184
|
// Project list add button
|
|
163
|
-
var projectListAddBtn =
|
|
185
|
+
var projectListAddBtn = document.getElementById("project-list-add");
|
|
164
186
|
if (projectListAddBtn) {
|
|
165
187
|
projectListAddBtn.addEventListener("click", function () {
|
|
166
188
|
openAddProjectModal();
|
|
@@ -197,12 +219,13 @@ export function updateProjectList(msg) {
|
|
|
197
219
|
else cachedRemovedProjects = [];
|
|
198
220
|
|
|
199
221
|
// Only re-render project strip + title bar if data or active slug changed
|
|
200
|
-
var
|
|
222
|
+
var currentSlug = store.get('currentSlug');
|
|
223
|
+
var slugChanged = currentSlug !== _lastRenderedSlug;
|
|
201
224
|
if (projectsChanged || slugChanged) {
|
|
202
|
-
_lastRenderedSlug =
|
|
225
|
+
_lastRenderedSlug = currentSlug;
|
|
203
226
|
var count = cachedProjectCount || 0;
|
|
204
227
|
renderProjectList();
|
|
205
|
-
var projectHint =
|
|
228
|
+
var projectHint = document.getElementById("project-hint");
|
|
206
229
|
if (count === 1 && projectHint) {
|
|
207
230
|
try {
|
|
208
231
|
if (!localStorage.getItem("clay-project-hint-dismissed")) {
|
|
@@ -217,40 +240,35 @@ export function updateProjectList(msg) {
|
|
|
217
240
|
// Update topbar with server-wide presence (renderTopbarPresence has its own guard)
|
|
218
241
|
if (msg.serverUsers) {
|
|
219
242
|
var newOnlineIds = msg.serverUsers.map(function (u) { return u.id; });
|
|
220
|
-
var prevOnlineIds =
|
|
221
|
-
|
|
243
|
+
var prevOnlineIds = store.get('cachedOnlineIds') || [];
|
|
244
|
+
store.set({ cachedOnlineIds: newOnlineIds });
|
|
222
245
|
renderTopbarPresence(msg.serverUsers);
|
|
223
|
-
//
|
|
224
|
-
if (!msg.allUsers && _ctx.cachedAllUsers.length > 0) {
|
|
225
|
-
var onlineChanged = newOnlineIds.length !== prevOnlineIds.length || newOnlineIds.some(function (id, i) { return id !== prevOnlineIds[i]; });
|
|
226
|
-
if (onlineChanged) {
|
|
227
|
-
_ctx.renderUserStrip(_ctx.cachedAllUsers, newOnlineIds, _ctx.myUserId, _ctx.cachedDmFavorites, _ctx.cachedDmConversations, _ctx.dmUnread, _ctx.dmRemovedUsers, _ctx.cachedMatesList);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
246
|
+
// renderUserStrip is handled by the store subscriber (fingerprint-guarded)
|
|
230
247
|
}
|
|
231
248
|
|
|
232
249
|
// Update user strip (DM targets) - renderUserStrip has its own fingerprint guard
|
|
233
250
|
if (msg.allUsers) {
|
|
234
|
-
|
|
235
|
-
if (msg.dmFavorites)
|
|
236
|
-
if (msg.dmConversations)
|
|
237
|
-
|
|
251
|
+
store.set({ cachedAllUsers: msg.allUsers });
|
|
252
|
+
if (msg.dmFavorites) store.set({ cachedDmFavorites: msg.dmFavorites });
|
|
253
|
+
if (msg.dmConversations) store.set({ cachedDmConversations: msg.dmConversations });
|
|
254
|
+
// renderUserStrip is handled by the store subscriber
|
|
255
|
+
var st2 = store.snap();
|
|
238
256
|
if (document.body.classList.contains("mate-dm-active") || document.body.classList.contains("wide-view")) {
|
|
239
|
-
var refreshedMyUser =
|
|
257
|
+
var refreshedMyUser = st2.cachedAllUsers.find(function (u) { return u.id === st2.myUserId; });
|
|
240
258
|
if (refreshedMyUser) {
|
|
241
259
|
document.body.dataset.myDisplayName = refreshedMyUser.displayName || refreshedMyUser.username || "";
|
|
242
|
-
document.body.dataset.myAvatarUrl =
|
|
260
|
+
document.body.dataset.myAvatarUrl = userAvatarUrl(refreshedMyUser, 36);
|
|
243
261
|
try { localStorage.setItem("clay_my_user", JSON.stringify({ displayName: refreshedMyUser.displayName, username: refreshedMyUser.username, avatarStyle: refreshedMyUser.avatarStyle, avatarSeed: refreshedMyUser.avatarSeed, avatarCustom: refreshedMyUser.avatarCustom })); } catch(e) {}
|
|
244
262
|
}
|
|
245
263
|
}
|
|
246
264
|
// Render my avatar (always present, hidden behind user-island)
|
|
247
265
|
var meEl = document.getElementById("icon-strip-me");
|
|
248
266
|
if (meEl && !meEl.hasChildNodes()) {
|
|
249
|
-
var myUser =
|
|
267
|
+
var myUser = st2.cachedAllUsers.find(function (u) { return u.id === st2.myUserId; });
|
|
250
268
|
if (myUser) {
|
|
251
269
|
var meAvatar = document.createElement("img");
|
|
252
270
|
meAvatar.className = "icon-strip-me-avatar";
|
|
253
|
-
meAvatar.src =
|
|
271
|
+
meAvatar.src = userAvatarUrl(myUser, 34);
|
|
254
272
|
meEl.appendChild(meAvatar);
|
|
255
273
|
}
|
|
256
274
|
}
|
|
@@ -273,7 +291,7 @@ export function renderTopbarPresence(serverUsers) {
|
|
|
273
291
|
var cu = serverUsers[cui];
|
|
274
292
|
var cuImg = document.createElement("img");
|
|
275
293
|
cuImg.className = "client-avatar";
|
|
276
|
-
cuImg.src =
|
|
294
|
+
cuImg.src = userAvatarUrl(cu, 24);
|
|
277
295
|
cuImg.alt = cu.displayName;
|
|
278
296
|
cuImg.dataset.tip = cu.displayName + " (@" + cu.username + ")";
|
|
279
297
|
if (cui > 0) cuImg.style.marginLeft = "-6px";
|
|
@@ -304,12 +322,13 @@ export function renderProjectList() {
|
|
|
304
322
|
worktreeAccessible: p.worktreeAccessible !== undefined ? p.worktreeAccessible : true,
|
|
305
323
|
};
|
|
306
324
|
});
|
|
307
|
-
var
|
|
308
|
-
|
|
325
|
+
var st = store.snap();
|
|
326
|
+
var iconStripActiveSlug = (st.mateProjectSlug && st.savedMainSlug) ? st.savedMainSlug : st.currentSlug;
|
|
327
|
+
renderIconStrip(iconStripProjects, iconStripActiveSlug);
|
|
309
328
|
// Update title bar project name and icon if it changed
|
|
310
|
-
if (!
|
|
329
|
+
if (!st.mateProjectSlug) {
|
|
311
330
|
for (var pi = 0; pi < cachedProjects.length; pi++) {
|
|
312
|
-
if (cachedProjects[pi].slug ===
|
|
331
|
+
if (cachedProjects[pi].slug === st.currentSlug) {
|
|
313
332
|
var updatedName = cachedProjects[pi].title || cachedProjects[pi].project;
|
|
314
333
|
var tbName = document.getElementById("title-bar-project-name");
|
|
315
334
|
if (tbName && updatedName) tbName.textContent = updatedName;
|
|
@@ -320,11 +339,11 @@ export function renderProjectList() {
|
|
|
320
339
|
tbIcon.textContent = pIcon;
|
|
321
340
|
parseEmojis(tbIcon);
|
|
322
341
|
tbIcon.classList.add("has-icon");
|
|
323
|
-
try { localStorage.setItem("clay-project-icon-" + (
|
|
342
|
+
try { localStorage.setItem("clay-project-icon-" + (st.currentSlug || "default"), pIcon); } catch (e) {}
|
|
324
343
|
} else {
|
|
325
344
|
tbIcon.textContent = "";
|
|
326
345
|
tbIcon.classList.remove("has-icon");
|
|
327
|
-
try { localStorage.removeItem("clay-project-icon-" + (
|
|
346
|
+
try { localStorage.removeItem("clay-project-icon-" + (st.currentSlug || "default")); } catch (e) {}
|
|
328
347
|
}
|
|
329
348
|
}
|
|
330
349
|
break;
|
|
@@ -332,49 +351,50 @@ export function renderProjectList() {
|
|
|
332
351
|
}
|
|
333
352
|
}
|
|
334
353
|
// Re-apply current socket status to the active icon's dot
|
|
335
|
-
var dot =
|
|
354
|
+
var dot = getStatusDot();
|
|
336
355
|
if (dot) {
|
|
337
|
-
if (
|
|
338
|
-
else if (
|
|
356
|
+
if (st.connected && st.processing) { dot.classList.add("connected"); dot.classList.add("processing"); }
|
|
357
|
+
else if (st.connected) { dot.classList.add("connected"); }
|
|
339
358
|
}
|
|
340
|
-
|
|
359
|
+
updateCrossProjectBlink();
|
|
341
360
|
}
|
|
342
361
|
|
|
343
362
|
export function resetClientState() {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (!
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
363
|
+
if (isSearchOpen()) closeSearch();
|
|
364
|
+
getMessagesEl().innerHTML = "";
|
|
365
|
+
store.set({ currentMsgEl: null });
|
|
366
|
+
store.set({ currentFullText: "" });
|
|
367
|
+
resetToolState();
|
|
368
|
+
clearPendingImages();
|
|
369
|
+
setActivityEl(null);
|
|
370
|
+
store.set({ processing: false });
|
|
371
|
+
setTurnCounter(0);
|
|
372
|
+
store.set({ messageUuidMap: [] });
|
|
373
|
+
store.set({ historyFrom: 0 });
|
|
374
|
+
store.set({ historyTotal: 0 });
|
|
375
|
+
setPrependAnchor(null);
|
|
376
|
+
store.set({ loadingMore: false });
|
|
377
|
+
setIsUserScrolledUp(false);
|
|
378
|
+
document.getElementById("new-msg-btn").classList.add("hidden");
|
|
379
|
+
setRewindMode(false);
|
|
380
|
+
setActivity(null);
|
|
381
|
+
setStatus("connected");
|
|
382
|
+
if (!store.get('loopActive')) enableMainInput();
|
|
383
|
+
resetUsage();
|
|
384
|
+
resetTurnMetaCost();
|
|
385
|
+
resetContext();
|
|
386
|
+
resetRateLimitState();
|
|
387
|
+
var headerCtx = store.get('headerContextEl');
|
|
388
|
+
if (headerCtx) { headerCtx.remove(); store.set({ headerContextEl: null }); }
|
|
389
|
+
hideSuggestionChips();
|
|
390
|
+
closeSessionInfoPopover();
|
|
391
|
+
stopUrgentBlink();
|
|
372
392
|
// Clear debate UI and state from previous session
|
|
373
|
-
|
|
374
|
-
|
|
393
|
+
store.set({ debateStickyState: null });
|
|
394
|
+
resetDebateState();
|
|
375
395
|
var debateBadges = document.querySelectorAll(".debate-header-badge");
|
|
376
396
|
for (var dbi = 0; dbi < debateBadges.length; dbi++) debateBadges[dbi].remove();
|
|
377
|
-
|
|
397
|
+
removeDebateBottomBar();
|
|
378
398
|
var handBar = document.getElementById("debate-hand-raise-bar");
|
|
379
399
|
if (handBar) handBar.remove();
|
|
380
400
|
var debateSticky = document.getElementById("debate-sticky");
|
|
@@ -385,63 +405,41 @@ export function resetClientState() {
|
|
|
385
405
|
|
|
386
406
|
export function switchProject(slug) {
|
|
387
407
|
if (!slug) return;
|
|
388
|
-
var
|
|
389
|
-
var
|
|
390
|
-
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
408
|
+
var st = store.snap();
|
|
409
|
+
var wasDm = st.dmMode;
|
|
410
|
+
var wasMate = st.dmMode && st.dmTargetUser && st.dmTargetUser.isMate;
|
|
411
|
+
if (st.dmMode) exitDmMode(wasMate);
|
|
412
|
+
if (isHomeHubVisible()) {
|
|
413
|
+
hideHomeHub();
|
|
414
|
+
if (slug === store.get('currentSlug')) return;
|
|
415
|
+
}
|
|
416
|
+
if (slug === store.get('currentSlug')) {
|
|
417
|
+
var ws = getWs();
|
|
418
|
+
if (wasDm && ws && ws.readyState === 1) {
|
|
419
|
+
ws.send(JSON.stringify({ type: "switch_session", id: store.get('activeSessionId') }));
|
|
398
420
|
}
|
|
399
421
|
return;
|
|
400
422
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
423
|
+
resetFileBrowser();
|
|
424
|
+
closeArchive();
|
|
425
|
+
hideMemory();
|
|
426
|
+
if (isSchedulerOpen()) closeScheduler();
|
|
427
|
+
resetScheduler(slug);
|
|
428
|
+
store.set({ currentSlug: slug });
|
|
429
|
+
store.set({ basePath: "/p/" + slug + "/" });
|
|
430
|
+
store.set({ wsPath: "/p/" + slug + "/ws" });
|
|
409
431
|
if (document.documentElement.classList.contains("pwa-standalone")) {
|
|
410
432
|
history.replaceState(null, "", "/p/" + slug + "/");
|
|
411
433
|
} else {
|
|
412
434
|
history.pushState(null, "", "/p/" + slug + "/");
|
|
413
435
|
}
|
|
414
436
|
resetClientState();
|
|
415
|
-
|
|
437
|
+
connect();
|
|
416
438
|
}
|
|
417
439
|
|
|
418
440
|
export function showUpdateAvailable(msg) {
|
|
419
|
-
|
|
420
|
-
var
|
|
421
|
-
var updateVersion = $("update-version");
|
|
422
|
-
if (updatePillWrap && updateVersion && msg.version) {
|
|
423
|
-
updateVersion.textContent = "v" + msg.version;
|
|
424
|
-
updatePillWrap.classList.remove("hidden");
|
|
425
|
-
var updPill = $("update-pill");
|
|
426
|
-
var updResetBtn = $("update-now");
|
|
427
|
-
if (_ctx.isHeadlessMode) {
|
|
428
|
-
if (updPill) updPill.innerHTML = '<i data-lucide="arrow-up-circle"></i> <span id="update-version">v' + msg.version + '</span> available. Update manually';
|
|
429
|
-
if (updResetBtn) updResetBtn.style.display = "none";
|
|
430
|
-
} else {
|
|
431
|
-
if (updResetBtn) {
|
|
432
|
-
updResetBtn.innerHTML = '<i data-lucide="download"></i> Update now';
|
|
433
|
-
updResetBtn.disabled = false;
|
|
434
|
-
updResetBtn.style.display = "";
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
var updManualCmd = $("update-manual-cmd");
|
|
438
|
-
if (updManualCmd) {
|
|
439
|
-
var updTag = msg.version.indexOf("-beta") !== -1 ? "beta" : "latest";
|
|
440
|
-
updManualCmd.textContent = "npx clay-server@" + updTag;
|
|
441
|
-
}
|
|
442
|
-
refreshIcons();
|
|
443
|
-
}
|
|
444
|
-
var settingsUpdBtn = $("settings-update-check");
|
|
441
|
+
// Update the settings panel button only (top bar pill replaced by notification center)
|
|
442
|
+
var settingsUpdBtn = document.getElementById("settings-update-check");
|
|
445
443
|
if (settingsUpdBtn && msg.version) {
|
|
446
444
|
settingsUpdBtn.innerHTML = "";
|
|
447
445
|
var ic = document.createElement("i");
|
|
@@ -459,8 +457,9 @@ export function showUpdateAvailable(msg) {
|
|
|
459
457
|
export function confirmRemoveProject(slug, name) {
|
|
460
458
|
pendingRemoveSlug = slug;
|
|
461
459
|
pendingRemoveName = name;
|
|
462
|
-
|
|
463
|
-
|
|
460
|
+
var ws = getWs();
|
|
461
|
+
if (ws && ws.readyState === 1) {
|
|
462
|
+
ws.send(JSON.stringify({ type: "remove_project_check", slug: slug }));
|
|
464
463
|
}
|
|
465
464
|
}
|
|
466
465
|
|
|
@@ -476,15 +475,16 @@ export function handleRemoveProjectCheckResult(msg) {
|
|
|
476
475
|
var confirmMsg = isWt
|
|
477
476
|
? 'Delete worktree "' + name + '"? The branch and working directory will be removed from disk.'
|
|
478
477
|
: 'Remove "' + name + '"? You can re-add it later.';
|
|
479
|
-
|
|
478
|
+
showConfirm(confirmMsg, function () {
|
|
480
479
|
var iconEl = document.querySelector('.icon-strip-item[data-slug="' + slug + '"]');
|
|
481
480
|
if (iconEl) {
|
|
482
481
|
var rect = iconEl.getBoundingClientRect();
|
|
483
|
-
|
|
482
|
+
spawnDustParticles(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
484
483
|
}
|
|
485
484
|
setTimeout(function () {
|
|
486
|
-
|
|
487
|
-
|
|
485
|
+
var ws = getWs();
|
|
486
|
+
if (ws && ws.readyState === 1) {
|
|
487
|
+
ws.send(JSON.stringify({ type: "remove_project", slug: slug }));
|
|
488
488
|
}
|
|
489
489
|
}, 1000);
|
|
490
490
|
}, "Remove", true);
|
|
@@ -533,16 +533,18 @@ function showRemoveProjectTaskDialog(slug, name, taskCount) {
|
|
|
533
533
|
if (moveBtn) {
|
|
534
534
|
moveBtn.addEventListener("click", function () {
|
|
535
535
|
var targetSlug = selectEl ? selectEl.value : null;
|
|
536
|
-
|
|
537
|
-
|
|
536
|
+
var ws = getWs();
|
|
537
|
+
if (ws && ws.readyState === 1 && targetSlug) {
|
|
538
|
+
ws.send(JSON.stringify({ type: "remove_project", slug: slug, moveTasksTo: targetSlug }));
|
|
538
539
|
}
|
|
539
540
|
close();
|
|
540
541
|
});
|
|
541
542
|
}
|
|
542
543
|
|
|
543
544
|
deleteBtn.addEventListener("click", function () {
|
|
544
|
-
|
|
545
|
-
|
|
545
|
+
var ws = getWs();
|
|
546
|
+
if (ws && ws.readyState === 1) {
|
|
547
|
+
ws.send(JSON.stringify({ type: "remove_project", slug: slug }));
|
|
546
548
|
}
|
|
547
549
|
close();
|
|
548
550
|
});
|
|
@@ -550,18 +552,19 @@ function showRemoveProjectTaskDialog(slug, name, taskCount) {
|
|
|
550
552
|
|
|
551
553
|
export function handleRemoveProjectResult(msg) {
|
|
552
554
|
if (msg.ok) {
|
|
553
|
-
|
|
555
|
+
var currentSlug = store.get('currentSlug');
|
|
556
|
+
if (msg.slug === currentSlug) {
|
|
554
557
|
var isWorktree = msg.slug.indexOf("--") !== -1;
|
|
555
558
|
var parentSlug = isWorktree ? msg.slug.split("--")[0] : null;
|
|
556
559
|
|
|
557
|
-
|
|
560
|
+
showToast(isWorktree ? "Worktree removed" : "Project removed", "success");
|
|
558
561
|
|
|
559
562
|
// Suppress disconnect overlay and reconnect by detaching the WS
|
|
560
|
-
var ws =
|
|
561
|
-
if (ws) { ws.onclose = null; ws.onerror = null; ws.close();
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
563
|
+
var ws = getWs();
|
|
564
|
+
if (ws) { ws.onclose = null; ws.onerror = null; ws.close(); setWs(null); }
|
|
565
|
+
cancelReconnect();
|
|
566
|
+
store.set({ connected: false });
|
|
567
|
+
document.getElementById("connect-overlay").classList.add("hidden");
|
|
565
568
|
if (!isWorktree) {
|
|
566
569
|
var removedProj = null;
|
|
567
570
|
for (var ri = 0; ri < cachedProjects.length; ri++) {
|
|
@@ -578,20 +581,20 @@ export function handleRemoveProjectResult(msg) {
|
|
|
578
581
|
}
|
|
579
582
|
cachedProjects = cachedProjects.filter(function (p) { return p.slug !== msg.slug; });
|
|
580
583
|
cachedProjectCount = cachedProjects.length;
|
|
581
|
-
|
|
584
|
+
store.set({ currentSlug: null });
|
|
582
585
|
renderProjectList();
|
|
583
586
|
resetClientState();
|
|
584
587
|
|
|
585
588
|
if (parentSlug) {
|
|
586
589
|
switchProject(parentSlug);
|
|
587
590
|
} else {
|
|
588
|
-
|
|
591
|
+
showHomeHub();
|
|
589
592
|
}
|
|
590
593
|
} else {
|
|
591
|
-
|
|
594
|
+
showToast(msg.slug.indexOf("--") !== -1 ? "Worktree removed" : "Project removed", "success");
|
|
592
595
|
}
|
|
593
596
|
} else {
|
|
594
|
-
|
|
597
|
+
showToast(msg.error || "Failed to remove project", "error");
|
|
595
598
|
}
|
|
596
599
|
}
|
|
597
600
|
|
|
@@ -647,9 +650,10 @@ export function openAddProjectModal() {
|
|
|
647
650
|
addProjectActiveIdx = -1;
|
|
648
651
|
addProjectOk.disabled = false;
|
|
649
652
|
var existingBtn = addProjectModal.querySelector('.add-project-mode-btn[data-mode="existing"]');
|
|
650
|
-
|
|
653
|
+
var st = store.snap();
|
|
654
|
+
if (st.isOsUsers) {
|
|
651
655
|
existingBtn.disabled = false;
|
|
652
|
-
var myUser =
|
|
656
|
+
var myUser = st.cachedAllUsers.find(function (u) { return u.id === st.myUserId; });
|
|
653
657
|
var isAdmin = myUser && myUser.role === "admin";
|
|
654
658
|
if (!isAdmin && myUser && myUser.linuxUser) {
|
|
655
659
|
// Non-admin: lock prefix to home directory
|
|
@@ -692,14 +696,15 @@ function renderRemovedProjectsList() {
|
|
|
692
696
|
item.dataset.path = rp.path;
|
|
693
697
|
item.addEventListener("click", function () {
|
|
694
698
|
var p = this.dataset.path;
|
|
695
|
-
|
|
696
|
-
|
|
699
|
+
var ws = getWs();
|
|
700
|
+
if (ws && ws.readyState === 1) {
|
|
701
|
+
ws.send(JSON.stringify({ type: "add_project", path: p }));
|
|
697
702
|
}
|
|
698
703
|
closeAddProjectModal();
|
|
699
704
|
});
|
|
700
705
|
var iconEl = document.createElement("span");
|
|
701
706
|
iconEl.className = "add-project-removed-icon";
|
|
702
|
-
iconEl.textContent = rp.icon || "
|
|
707
|
+
iconEl.textContent = rp.icon || "\uD83D\uDCC1";
|
|
703
708
|
item.appendChild(iconEl);
|
|
704
709
|
var info = document.createElement("div");
|
|
705
710
|
info.className = "add-project-removed-info";
|
|
@@ -744,8 +749,9 @@ function stripPrefix(fullPath) {
|
|
|
744
749
|
}
|
|
745
750
|
|
|
746
751
|
function requestBrowseDir(val) {
|
|
747
|
-
|
|
748
|
-
|
|
752
|
+
var ws = getWs();
|
|
753
|
+
if (!ws || ws.readyState !== 1) return;
|
|
754
|
+
ws.send(JSON.stringify({ type: "browse_dir", path: getFullPath(val) }));
|
|
749
755
|
}
|
|
750
756
|
|
|
751
757
|
export function handleBrowseDirResult(msg) {
|
|
@@ -785,10 +791,10 @@ export function handleAddProjectResult(msg) {
|
|
|
785
791
|
if (msg.ok) {
|
|
786
792
|
closeAddProjectModal();
|
|
787
793
|
if (msg.existing) {
|
|
788
|
-
|
|
794
|
+
showToast("Project already registered", "info");
|
|
789
795
|
} else {
|
|
790
796
|
var toastMsg = addProjectMode === "create" ? "Project created" : addProjectMode === "clone" ? "Project cloned" : "Project added";
|
|
791
|
-
|
|
797
|
+
showToast(toastMsg, "success");
|
|
792
798
|
if (msg.slug) {
|
|
793
799
|
switchProject(msg.slug);
|
|
794
800
|
}
|
|
@@ -826,20 +832,23 @@ function submitAddProject() {
|
|
|
826
832
|
if (addProjectMode === "existing") {
|
|
827
833
|
var val = getFullPath(addProjectInput.value).replace(/\/+$/, "");
|
|
828
834
|
if (!val) { addProjectOk.disabled = false; return; }
|
|
829
|
-
|
|
830
|
-
|
|
835
|
+
var ws = getWs();
|
|
836
|
+
if (ws && ws.readyState === 1) {
|
|
837
|
+
ws.send(JSON.stringify({ type: "add_project", path: val }));
|
|
831
838
|
}
|
|
832
839
|
} else if (addProjectMode === "create") {
|
|
833
840
|
var name = addProjectCreateInput.value.trim();
|
|
834
841
|
if (!name) { addProjectOk.disabled = false; return; }
|
|
835
|
-
|
|
836
|
-
|
|
842
|
+
var ws2 = getWs();
|
|
843
|
+
if (ws2 && ws2.readyState === 1) {
|
|
844
|
+
ws2.send(JSON.stringify({ type: "create_project", name: name }));
|
|
837
845
|
}
|
|
838
846
|
} else if (addProjectMode === "clone") {
|
|
839
847
|
var url = addProjectCloneInput.value.trim();
|
|
840
848
|
if (!url) { addProjectOk.disabled = false; return; }
|
|
841
|
-
|
|
842
|
-
|
|
849
|
+
var ws3 = getWs();
|
|
850
|
+
if (ws3 && ws3.readyState === 1) {
|
|
851
|
+
ws3.send(JSON.stringify({ type: "clone_project", url: url }));
|
|
843
852
|
}
|
|
844
853
|
}
|
|
845
854
|
}
|
|
@@ -210,10 +210,11 @@ export function updateRateLimitUsage(msg) {
|
|
|
210
210
|
rateLimitUsageEl = document.createElement("a");
|
|
211
211
|
rateLimitUsageEl.id = "rate-limit-usage-link";
|
|
212
212
|
rateLimitUsageEl.className = "top-bar-pill pill-dim usage-check-link";
|
|
213
|
-
|
|
213
|
+
var vendor = store.get('currentVendor') || "claude";
|
|
214
|
+
rateLimitUsageEl.href = vendor === "codex" ? "https://chatgpt.com/admin/usage" : "https://claude.ai/settings/usage";
|
|
214
215
|
rateLimitUsageEl.target = "_blank";
|
|
215
216
|
rateLimitUsageEl.rel = "noopener";
|
|
216
|
-
rateLimitUsageEl.title = "Check usage on claude.ai";
|
|
217
|
+
rateLimitUsageEl.title = vendor === "codex" ? "Check usage on ChatGPT" : "Check usage on claude.ai";
|
|
217
218
|
var ref = document.getElementById("skip-perms-pill");
|
|
218
219
|
topBarActions.insertBefore(rateLimitUsageEl, ref);
|
|
219
220
|
}
|
|
@@ -254,13 +255,13 @@ export function addScheduledMessageBubble(text, resetsAt) {
|
|
|
254
255
|
|
|
255
256
|
if (isChannel) {
|
|
256
257
|
// Channel mode: avatar + header with scheduled badge + message
|
|
257
|
-
var _me = store.
|
|
258
|
+
var _me = store.get('cachedAllUsers').find(function (u) { return u.id === store.get('myUserId'); });
|
|
258
259
|
if (!_me) { try { _me = JSON.parse(localStorage.getItem("clay_my_user") || "null"); } catch(e) {} }
|
|
259
260
|
var _myName = document.body.dataset.myDisplayName || (_me && (_me.displayName || _me.username)) || "Me";
|
|
260
261
|
|
|
261
262
|
var avi = document.createElement("img");
|
|
262
263
|
avi.className = "dm-bubble-avatar dm-bubble-avatar-me";
|
|
263
|
-
avi.src = document.body.dataset.myAvatarUrl || userAvatarUrl(_me || { id: store.
|
|
264
|
+
avi.src = document.body.dataset.myAvatarUrl || userAvatarUrl(_me || { id: store.get('myUserId') }, 36);
|
|
264
265
|
wrap.appendChild(avi);
|
|
265
266
|
|
|
266
267
|
var content = document.createElement("div");
|