clay-server 2.27.0-beta.9 → 2.27.0
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/README.md +10 -0
- package/lib/daemon-projects.js +164 -0
- package/lib/daemon.js +13 -126
- package/lib/mates-identity.js +132 -0
- package/lib/mates-knowledge.js +113 -0
- package/lib/mates-prompts.js +398 -0
- package/lib/mates.js +40 -599
- package/lib/project-connection.js +2 -0
- package/lib/project-http.js +4 -2
- package/lib/project-loop.js +110 -48
- package/lib/project-mate-interaction.js +4 -0
- package/lib/project-notifications.js +210 -0
- package/lib/project-sessions.js +5 -2
- package/lib/project-user-message.js +2 -1
- package/lib/project.js +26 -2
- package/lib/public/app.js +1193 -8517
- package/lib/public/css/command-palette.css +14 -0
- package/lib/public/css/loop.css +301 -0
- package/lib/public/css/notifications-center.css +190 -0
- package/lib/public/css/rewind.css +6 -0
- package/lib/public/index.html +89 -35
- package/lib/public/modules/app-connection.js +160 -0
- package/lib/public/modules/app-cursors.js +473 -0
- package/lib/public/modules/app-debate-ui.js +389 -0
- package/lib/public/modules/app-dm.js +627 -0
- package/lib/public/modules/app-favicon.js +212 -0
- package/lib/public/modules/app-header.js +229 -0
- package/lib/public/modules/app-home-hub.js +600 -0
- package/lib/public/modules/app-loop-ui.js +589 -0
- package/lib/public/modules/app-loop-wizard.js +439 -0
- package/lib/public/modules/app-messages.js +1560 -0
- package/lib/public/modules/app-misc.js +299 -0
- package/lib/public/modules/app-notifications.js +372 -0
- package/lib/public/modules/app-panels.js +888 -0
- package/lib/public/modules/app-projects.js +798 -0
- package/lib/public/modules/app-rate-limit.js +451 -0
- package/lib/public/modules/app-rendering.js +597 -0
- package/lib/public/modules/app-skills-install.js +234 -0
- package/lib/public/modules/command-palette.js +27 -4
- package/lib/public/modules/input.js +31 -20
- package/lib/public/modules/scheduler-config.js +1532 -0
- package/lib/public/modules/scheduler-history.js +79 -0
- package/lib/public/modules/scheduler.js +33 -1554
- package/lib/public/modules/session-search.js +13 -1
- package/lib/public/modules/sidebar-mates.js +812 -0
- package/lib/public/modules/sidebar-mobile.js +1269 -0
- package/lib/public/modules/sidebar-projects.js +1449 -0
- package/lib/public/modules/sidebar-sessions.js +986 -0
- package/lib/public/modules/sidebar.js +232 -4591
- package/lib/public/modules/store.js +27 -0
- package/lib/public/modules/ws-ref.js +7 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +96 -717
- package/lib/sdk-message-processor.js +587 -0
- package/lib/sdk-message-queue.js +42 -0
- package/lib/sdk-skill-discovery.js +131 -0
- package/lib/server-admin.js +712 -0
- package/lib/server-auth.js +737 -0
- package/lib/server-dm.js +221 -0
- package/lib/server-mates.js +281 -0
- package/lib/server-palette.js +110 -0
- package/lib/server-settings.js +479 -0
- package/lib/server-skills.js +280 -0
- package/lib/server.js +246 -2755
- package/lib/sessions.js +11 -4
- package/lib/users-auth.js +146 -0
- package/lib/users-permissions.js +118 -0
- package/lib/users-preferences.js +210 -0
- package/lib/users.js +48 -398
- package/lib/ws-schema.js +498 -0
- package/package.json +1 -1
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
// app-projects.js - Project list, switching, add/remove project modals
|
|
2
|
+
// Extracted from app.js (PR-29)
|
|
3
|
+
|
|
4
|
+
import { escapeHtml } from './utils.js';
|
|
5
|
+
import { refreshIcons } from './icons.js';
|
|
6
|
+
import { parseEmojis } from './markdown.js';
|
|
7
|
+
|
|
8
|
+
var _ctx = null;
|
|
9
|
+
|
|
10
|
+
// --- Module-owned state ---
|
|
11
|
+
var cachedProjects = [];
|
|
12
|
+
var cachedProjectCount = 0;
|
|
13
|
+
var cachedRemovedProjects = [];
|
|
14
|
+
var pendingRemoveSlug = null;
|
|
15
|
+
var pendingRemoveName = null;
|
|
16
|
+
|
|
17
|
+
// Add-project modal state
|
|
18
|
+
var addProjectModal = null;
|
|
19
|
+
var addProjectInput = null;
|
|
20
|
+
var addProjectCreateInput = null;
|
|
21
|
+
var addProjectCloneInput = null;
|
|
22
|
+
var addProjectCloneProgress = null;
|
|
23
|
+
var addProjectSuggestions = null;
|
|
24
|
+
var addProjectError = null;
|
|
25
|
+
var addProjectOk = null;
|
|
26
|
+
var addProjectCancel = null;
|
|
27
|
+
var addProjectModeBtns = null;
|
|
28
|
+
var addProjectPanels = null;
|
|
29
|
+
var addProjectRemoved = null;
|
|
30
|
+
var addProjectDebounce = null;
|
|
31
|
+
var addProjectActiveIdx = -1;
|
|
32
|
+
var addProjectMode = "existing";
|
|
33
|
+
|
|
34
|
+
export function initProjects(ctx) {
|
|
35
|
+
_ctx = ctx;
|
|
36
|
+
|
|
37
|
+
// Init add-project modal DOM refs
|
|
38
|
+
addProjectModal = document.getElementById("add-project-modal");
|
|
39
|
+
addProjectInput = document.getElementById("add-project-input");
|
|
40
|
+
addProjectCreateInput = document.getElementById("add-project-create-input");
|
|
41
|
+
addProjectCloneInput = document.getElementById("add-project-clone-input");
|
|
42
|
+
addProjectCloneProgress = document.getElementById("add-project-clone-progress");
|
|
43
|
+
addProjectSuggestions = document.getElementById("add-project-suggestions");
|
|
44
|
+
addProjectError = document.getElementById("add-project-error");
|
|
45
|
+
addProjectOk = document.getElementById("add-project-ok");
|
|
46
|
+
addProjectCancel = document.getElementById("add-project-cancel");
|
|
47
|
+
addProjectModeBtns = addProjectModal.querySelectorAll(".add-project-mode-btn");
|
|
48
|
+
addProjectPanels = addProjectModal.querySelectorAll(".add-project-panel");
|
|
49
|
+
addProjectRemoved = document.getElementById("add-project-removed");
|
|
50
|
+
|
|
51
|
+
// Mode button click listeners
|
|
52
|
+
for (var mbi = 0; mbi < addProjectModeBtns.length; mbi++) {
|
|
53
|
+
addProjectModeBtns[mbi].addEventListener("click", function () {
|
|
54
|
+
if (this.disabled) return;
|
|
55
|
+
switchAddProjectMode(this.dataset.mode);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Existing project input listeners
|
|
60
|
+
addProjectInput.addEventListener("focus", function () {
|
|
61
|
+
var val = addProjectInput.value;
|
|
62
|
+
if (val && addProjectSuggestions.children.length === 0) {
|
|
63
|
+
requestBrowseDir(val);
|
|
64
|
+
} else if (addProjectSuggestions.children.length > 0) {
|
|
65
|
+
addProjectSuggestions.classList.remove("hidden");
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
addProjectModal.querySelector(".confirm-dialog").addEventListener("click", function (e) {
|
|
70
|
+
if (e.target === addProjectInput || addProjectInput.contains(e.target)) return;
|
|
71
|
+
if (e.target === addProjectSuggestions || addProjectSuggestions.contains(e.target)) return;
|
|
72
|
+
addProjectSuggestions.classList.add("hidden");
|
|
73
|
+
addProjectActiveIdx = -1;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
addProjectInput.addEventListener("input", function () {
|
|
77
|
+
var val = addProjectInput.value;
|
|
78
|
+
addProjectError.classList.add("hidden");
|
|
79
|
+
if (addProjectDebounce) clearTimeout(addProjectDebounce);
|
|
80
|
+
addProjectDebounce = setTimeout(function () {
|
|
81
|
+
requestBrowseDir(val);
|
|
82
|
+
}, 200);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
addProjectInput.addEventListener("keydown", function (e) {
|
|
86
|
+
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
87
|
+
|
|
88
|
+
if (e.key === "ArrowDown") {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
if (items.length > 0) {
|
|
91
|
+
var next = addProjectActiveIdx < items.length - 1 ? addProjectActiveIdx + 1 : 0;
|
|
92
|
+
setActiveIdx(next);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (e.key === "ArrowUp") {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
if (items.length > 0) {
|
|
100
|
+
var prev = addProjectActiveIdx > 0 ? addProjectActiveIdx - 1 : items.length - 1;
|
|
101
|
+
setActiveIdx(prev);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (e.key === "Tab") {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
var target = addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length
|
|
109
|
+
? items[addProjectActiveIdx]
|
|
110
|
+
: items.length > 0 ? items[0] : null;
|
|
111
|
+
if (target) {
|
|
112
|
+
var p = target.dataset.path + "/";
|
|
113
|
+
addProjectInput.value = p;
|
|
114
|
+
addProjectError.classList.add("hidden");
|
|
115
|
+
requestBrowseDir(p);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (e.key === "Enter") {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
if (addProjectActiveIdx >= 0 && addProjectActiveIdx < items.length) {
|
|
123
|
+
var picked = items[addProjectActiveIdx].dataset.path + "/";
|
|
124
|
+
addProjectInput.value = picked;
|
|
125
|
+
addProjectError.classList.add("hidden");
|
|
126
|
+
requestBrowseDir(picked);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
submitAddProject();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (e.key === "Escape") {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
closeAddProjectModal();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Enter key on create/clone inputs
|
|
141
|
+
addProjectCreateInput.addEventListener("keydown", function (e) {
|
|
142
|
+
if (e.key === "Enter") { e.preventDefault(); submitAddProject(); }
|
|
143
|
+
if (e.key === "Escape") { e.preventDefault(); closeAddProjectModal(); }
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
addProjectCloneInput.addEventListener("keydown", function (e) {
|
|
147
|
+
if (e.key === "Enter") { e.preventDefault(); submitAddProject(); }
|
|
148
|
+
if (e.key === "Escape") { e.preventDefault(); closeAddProjectModal(); }
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
addProjectOk.addEventListener("click", function () { submitAddProject(); });
|
|
152
|
+
addProjectCancel.addEventListener("click", function () { closeAddProjectModal(); });
|
|
153
|
+
|
|
154
|
+
// Close on backdrop click
|
|
155
|
+
addProjectModal.querySelector(".confirm-backdrop").addEventListener("click", function () {
|
|
156
|
+
closeAddProjectModal();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Project list add button
|
|
160
|
+
var projectListAddBtn = _ctx.$("project-list-add");
|
|
161
|
+
if (projectListAddBtn) {
|
|
162
|
+
projectListAddBtn.addEventListener("click", function () {
|
|
163
|
+
openAddProjectModal();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- State accessors ---
|
|
169
|
+
|
|
170
|
+
export function getCachedProjects() { return cachedProjects; }
|
|
171
|
+
export function setCachedProjects(v) { cachedProjects = v; }
|
|
172
|
+
export function getCachedProjectCount() { return cachedProjectCount; }
|
|
173
|
+
export function setCachedProjectCount(v) { cachedProjectCount = v; }
|
|
174
|
+
export function getCachedRemovedProjects() { return cachedRemovedProjects; }
|
|
175
|
+
export function setCachedRemovedProjects(v) { cachedRemovedProjects = v; }
|
|
176
|
+
|
|
177
|
+
// --- Functions ---
|
|
178
|
+
|
|
179
|
+
export function updateProjectList(msg) {
|
|
180
|
+
if (typeof msg.projectCount === "number") cachedProjectCount = msg.projectCount;
|
|
181
|
+
if (msg.projects) cachedProjects = msg.projects;
|
|
182
|
+
if (msg.removedProjects) cachedRemovedProjects = msg.removedProjects;
|
|
183
|
+
else if (msg.removedProjects === undefined) { /* keep cached */ }
|
|
184
|
+
else cachedRemovedProjects = [];
|
|
185
|
+
var count = cachedProjectCount || 0;
|
|
186
|
+
renderProjectList();
|
|
187
|
+
var projectHint = _ctx.$("project-hint");
|
|
188
|
+
if (count === 1 && projectHint) {
|
|
189
|
+
try {
|
|
190
|
+
if (!localStorage.getItem("clay-project-hint-dismissed")) {
|
|
191
|
+
projectHint.classList.remove("hidden");
|
|
192
|
+
}
|
|
193
|
+
} catch (e) {}
|
|
194
|
+
} else if (projectHint) {
|
|
195
|
+
projectHint.classList.add("hidden");
|
|
196
|
+
}
|
|
197
|
+
// Update topbar with server-wide presence
|
|
198
|
+
if (msg.serverUsers) {
|
|
199
|
+
var newOnlineIds = msg.serverUsers.map(function (u) { return u.id; });
|
|
200
|
+
var prevOnlineIds = _ctx.cachedOnlineIds || [];
|
|
201
|
+
_ctx.setCachedOnlineIds(newOnlineIds);
|
|
202
|
+
renderTopbarPresence(msg.serverUsers);
|
|
203
|
+
// Only re-render user strip if online IDs actually changed
|
|
204
|
+
if (!msg.allUsers && _ctx.cachedAllUsers.length > 0) {
|
|
205
|
+
var onlineChanged = newOnlineIds.length !== prevOnlineIds.length || newOnlineIds.some(function (id, i) { return id !== prevOnlineIds[i]; });
|
|
206
|
+
if (onlineChanged) {
|
|
207
|
+
_ctx.renderUserStrip(_ctx.cachedAllUsers, newOnlineIds, _ctx.myUserId, _ctx.cachedDmFavorites, _ctx.cachedDmConversations, _ctx.dmUnread, _ctx.dmRemovedUsers, _ctx.cachedMatesList);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Update user strip (DM targets) in icon strip
|
|
212
|
+
if (msg.allUsers) {
|
|
213
|
+
_ctx.setCachedAllUsers(msg.allUsers);
|
|
214
|
+
if (msg.dmFavorites) _ctx.setCachedDmFavorites(msg.dmFavorites);
|
|
215
|
+
if (msg.dmConversations) _ctx.setCachedDmConversations(msg.dmConversations);
|
|
216
|
+
_ctx.renderUserStrip(msg.allUsers, _ctx.cachedOnlineIds, _ctx.myUserId, _ctx.cachedDmFavorites, _ctx.cachedDmConversations, _ctx.dmUnread, _ctx.dmRemovedUsers, _ctx.cachedMatesList);
|
|
217
|
+
if (document.body.classList.contains("mate-dm-active") || document.body.classList.contains("wide-view")) {
|
|
218
|
+
var refreshedMyUser = _ctx.cachedAllUsers.find(function (u) { return u.id === _ctx.myUserId; });
|
|
219
|
+
if (refreshedMyUser) {
|
|
220
|
+
document.body.dataset.myDisplayName = refreshedMyUser.displayName || refreshedMyUser.username || "";
|
|
221
|
+
document.body.dataset.myAvatarUrl = _ctx.userAvatarUrl(refreshedMyUser, 36);
|
|
222
|
+
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) {}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Render my avatar (always present, hidden behind user-island)
|
|
226
|
+
var meEl = document.getElementById("icon-strip-me");
|
|
227
|
+
if (meEl && !meEl.hasChildNodes()) {
|
|
228
|
+
var myUser = _ctx.cachedAllUsers.find(function (u) { return u.id === _ctx.myUserId; });
|
|
229
|
+
if (myUser) {
|
|
230
|
+
var meAvatar = document.createElement("img");
|
|
231
|
+
meAvatar.className = "icon-strip-me-avatar";
|
|
232
|
+
meAvatar.src = _ctx.userAvatarUrl(myUser, 34);
|
|
233
|
+
meEl.appendChild(meAvatar);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
var _lastTopbarUserIds = [];
|
|
240
|
+
export function renderTopbarPresence(serverUsers) {
|
|
241
|
+
var countEl = document.getElementById("client-count");
|
|
242
|
+
if (!countEl) return;
|
|
243
|
+
if (serverUsers.length > 1) {
|
|
244
|
+
// Skip re-render if user list unchanged
|
|
245
|
+
var newIds = serverUsers.map(function (u) { return u.id; }).sort();
|
|
246
|
+
if (newIds.length === _lastTopbarUserIds.length && newIds.every(function (id, i) { return id === _lastTopbarUserIds[i]; })) return;
|
|
247
|
+
_lastTopbarUserIds = newIds;
|
|
248
|
+
countEl.innerHTML = "";
|
|
249
|
+
for (var cui = 0; cui < serverUsers.length; cui++) {
|
|
250
|
+
var cu = serverUsers[cui];
|
|
251
|
+
var cuImg = document.createElement("img");
|
|
252
|
+
cuImg.className = "client-avatar";
|
|
253
|
+
cuImg.src = _ctx.userAvatarUrl(cu, 24);
|
|
254
|
+
cuImg.alt = cu.displayName;
|
|
255
|
+
cuImg.dataset.tip = cu.displayName + " (@" + cu.username + ")";
|
|
256
|
+
if (cui > 0) cuImg.style.marginLeft = "-6px";
|
|
257
|
+
countEl.appendChild(cuImg);
|
|
258
|
+
}
|
|
259
|
+
countEl.classList.remove("hidden");
|
|
260
|
+
} else {
|
|
261
|
+
_lastTopbarUserIds = [];
|
|
262
|
+
countEl.classList.add("hidden");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function renderProjectList() {
|
|
267
|
+
var iconStripProjects = cachedProjects.filter(function (p) {
|
|
268
|
+
return !p.isMate;
|
|
269
|
+
}).map(function (p) {
|
|
270
|
+
return {
|
|
271
|
+
slug: p.slug,
|
|
272
|
+
name: p.title || p.project,
|
|
273
|
+
icon: p.icon || null,
|
|
274
|
+
isProcessing: p.isProcessing,
|
|
275
|
+
onlineUsers: p.onlineUsers || [],
|
|
276
|
+
unread: p.unread || 0,
|
|
277
|
+
pendingPermissions: p.pendingPermissions || 0,
|
|
278
|
+
isWorktree: p.isWorktree || false,
|
|
279
|
+
parentSlug: p.parentSlug || null,
|
|
280
|
+
branch: p.branch || null,
|
|
281
|
+
worktreeAccessible: p.worktreeAccessible !== undefined ? p.worktreeAccessible : true,
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
var iconStripActiveSlug = (_ctx.mateProjectSlug && _ctx.savedMainSlug) ? _ctx.savedMainSlug : _ctx.currentSlug;
|
|
285
|
+
_ctx.renderIconStrip(iconStripProjects, iconStripActiveSlug);
|
|
286
|
+
// Update title bar project name and icon if it changed
|
|
287
|
+
if (!_ctx.mateProjectSlug) {
|
|
288
|
+
for (var pi = 0; pi < cachedProjects.length; pi++) {
|
|
289
|
+
if (cachedProjects[pi].slug === _ctx.currentSlug) {
|
|
290
|
+
var updatedName = cachedProjects[pi].title || cachedProjects[pi].project;
|
|
291
|
+
var tbName = document.getElementById("title-bar-project-name");
|
|
292
|
+
if (tbName && updatedName) tbName.textContent = updatedName;
|
|
293
|
+
var tbIcon = document.getElementById("title-bar-project-icon");
|
|
294
|
+
if (tbIcon) {
|
|
295
|
+
var pIcon = cachedProjects[pi].icon || null;
|
|
296
|
+
if (pIcon) {
|
|
297
|
+
tbIcon.textContent = pIcon;
|
|
298
|
+
parseEmojis(tbIcon);
|
|
299
|
+
tbIcon.classList.add("has-icon");
|
|
300
|
+
try { localStorage.setItem("clay-project-icon-" + (_ctx.currentSlug || "default"), pIcon); } catch (e) {}
|
|
301
|
+
} else {
|
|
302
|
+
tbIcon.textContent = "";
|
|
303
|
+
tbIcon.classList.remove("has-icon");
|
|
304
|
+
try { localStorage.removeItem("clay-project-icon-" + (_ctx.currentSlug || "default")); } catch (e) {}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Re-apply current socket status to the active icon's dot
|
|
312
|
+
var dot = _ctx.getStatusDot();
|
|
313
|
+
if (dot) {
|
|
314
|
+
if (_ctx.connected && _ctx.processing) { dot.classList.add("connected"); dot.classList.add("processing"); }
|
|
315
|
+
else if (_ctx.connected) { dot.classList.add("connected"); }
|
|
316
|
+
}
|
|
317
|
+
_ctx.updateCrossProjectBlink();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function resetClientState() {
|
|
321
|
+
_ctx.closeSearch();
|
|
322
|
+
_ctx.messagesEl.innerHTML = "";
|
|
323
|
+
_ctx.setCurrentMsgEl(null);
|
|
324
|
+
_ctx.setCurrentFullText("");
|
|
325
|
+
_ctx.resetToolState();
|
|
326
|
+
_ctx.clearPendingImages();
|
|
327
|
+
_ctx.setActivityEl(null);
|
|
328
|
+
_ctx.setProcessing(false);
|
|
329
|
+
_ctx.setTurnCounter(0);
|
|
330
|
+
_ctx.setMessageUuidMap([]);
|
|
331
|
+
_ctx.setHistoryFrom(0);
|
|
332
|
+
_ctx.setHistoryTotal(0);
|
|
333
|
+
_ctx.setPrependAnchor(null);
|
|
334
|
+
_ctx.setLoadingMore(false);
|
|
335
|
+
_ctx.setIsUserScrolledUp(false);
|
|
336
|
+
_ctx.newMsgBtn.classList.add("hidden");
|
|
337
|
+
_ctx.setRewindMode(false);
|
|
338
|
+
_ctx.setActivity(null);
|
|
339
|
+
_ctx.setStatus("connected");
|
|
340
|
+
if (!_ctx.loopActive) _ctx.enableMainInput();
|
|
341
|
+
_ctx.resetUsage();
|
|
342
|
+
_ctx.resetTurnMetaCost();
|
|
343
|
+
_ctx.resetContext();
|
|
344
|
+
_ctx.resetRateLimitState();
|
|
345
|
+
if (_ctx.getHeaderContextEl()) { _ctx.getHeaderContextEl().remove(); _ctx.setHeaderContextEl(null); }
|
|
346
|
+
_ctx.hideSuggestionChips();
|
|
347
|
+
_ctx.closeSessionInfoPopover();
|
|
348
|
+
_ctx.stopUrgentBlink();
|
|
349
|
+
// Clear debate UI and state from previous session
|
|
350
|
+
_ctx.setDebateStickyState(null);
|
|
351
|
+
_ctx.resetDebateState();
|
|
352
|
+
var debateBadges = document.querySelectorAll(".debate-header-badge");
|
|
353
|
+
for (var dbi = 0; dbi < debateBadges.length; dbi++) debateBadges[dbi].remove();
|
|
354
|
+
_ctx.removeDebateBottomBar();
|
|
355
|
+
var handBar = document.getElementById("debate-hand-raise-bar");
|
|
356
|
+
if (handBar) handBar.remove();
|
|
357
|
+
var debateSticky = document.getElementById("debate-sticky");
|
|
358
|
+
if (debateSticky) { debateSticky.classList.add("hidden"); debateSticky.innerHTML = ""; }
|
|
359
|
+
var debateFloat = document.getElementById("debate-info-float");
|
|
360
|
+
if (debateFloat) { debateFloat.classList.add("hidden"); debateFloat.innerHTML = ""; }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function switchProject(slug) {
|
|
364
|
+
if (!slug) return;
|
|
365
|
+
var wasDm = _ctx.dmMode;
|
|
366
|
+
var wasMate = _ctx.dmMode && _ctx.dmTargetUser && _ctx.dmTargetUser.isMate;
|
|
367
|
+
if (_ctx.dmMode) _ctx.exitDmMode(wasMate);
|
|
368
|
+
if (_ctx.isHomeHubVisible()) {
|
|
369
|
+
_ctx.hideHomeHub();
|
|
370
|
+
if (slug === _ctx.currentSlug) return;
|
|
371
|
+
}
|
|
372
|
+
if (slug === _ctx.currentSlug) {
|
|
373
|
+
if (wasDm && _ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
374
|
+
_ctx.getWs().send(JSON.stringify({ type: "switch_session", id: _ctx.activeSessionId }));
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
_ctx.resetFileBrowser();
|
|
379
|
+
_ctx.closeArchive();
|
|
380
|
+
_ctx.hideMemory();
|
|
381
|
+
if (_ctx.isSchedulerOpen()) _ctx.closeScheduler();
|
|
382
|
+
_ctx.resetScheduler(slug);
|
|
383
|
+
_ctx.setCurrentSlug(slug);
|
|
384
|
+
_ctx.setBasePath("/p/" + slug + "/");
|
|
385
|
+
_ctx.setWsPath("/p/" + slug + "/ws");
|
|
386
|
+
if (document.documentElement.classList.contains("pwa-standalone")) {
|
|
387
|
+
history.replaceState(null, "", "/p/" + slug + "/");
|
|
388
|
+
} else {
|
|
389
|
+
history.pushState(null, "", "/p/" + slug + "/");
|
|
390
|
+
}
|
|
391
|
+
resetClientState();
|
|
392
|
+
_ctx.connect();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function showUpdateAvailable(msg) {
|
|
396
|
+
var $ = _ctx.$;
|
|
397
|
+
var updatePillWrap = $("update-pill-wrap");
|
|
398
|
+
var updateVersion = $("update-version");
|
|
399
|
+
if (updatePillWrap && updateVersion && msg.version) {
|
|
400
|
+
updateVersion.textContent = "v" + msg.version;
|
|
401
|
+
updatePillWrap.classList.remove("hidden");
|
|
402
|
+
var updPill = $("update-pill");
|
|
403
|
+
var updResetBtn = $("update-now");
|
|
404
|
+
if (_ctx.isHeadlessMode) {
|
|
405
|
+
if (updPill) updPill.innerHTML = '<i data-lucide="arrow-up-circle"></i> <span id="update-version">v' + msg.version + '</span> available. Update manually';
|
|
406
|
+
if (updResetBtn) updResetBtn.style.display = "none";
|
|
407
|
+
} else {
|
|
408
|
+
if (updResetBtn) {
|
|
409
|
+
updResetBtn.innerHTML = '<i data-lucide="download"></i> Update now';
|
|
410
|
+
updResetBtn.disabled = false;
|
|
411
|
+
updResetBtn.style.display = "";
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
var updManualCmd = $("update-manual-cmd");
|
|
415
|
+
if (updManualCmd) {
|
|
416
|
+
var updTag = msg.version.indexOf("-beta") !== -1 ? "beta" : "latest";
|
|
417
|
+
updManualCmd.textContent = "npx clay-server@" + updTag;
|
|
418
|
+
}
|
|
419
|
+
refreshIcons();
|
|
420
|
+
}
|
|
421
|
+
var settingsUpdBtn = $("settings-update-check");
|
|
422
|
+
if (settingsUpdBtn && msg.version) {
|
|
423
|
+
settingsUpdBtn.innerHTML = "";
|
|
424
|
+
var ic = document.createElement("i");
|
|
425
|
+
ic.setAttribute("data-lucide", "arrow-up-circle");
|
|
426
|
+
settingsUpdBtn.appendChild(ic);
|
|
427
|
+
settingsUpdBtn.appendChild(document.createTextNode(" Update available (v" + msg.version + ")"));
|
|
428
|
+
settingsUpdBtn.classList.add("settings-btn-update-available");
|
|
429
|
+
settingsUpdBtn.disabled = false;
|
|
430
|
+
refreshIcons();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// --- Remove project ---
|
|
435
|
+
|
|
436
|
+
export function confirmRemoveProject(slug, name) {
|
|
437
|
+
pendingRemoveSlug = slug;
|
|
438
|
+
pendingRemoveName = name;
|
|
439
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
440
|
+
_ctx.getWs().send(JSON.stringify({ type: "remove_project_check", slug: slug }));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function handleRemoveProjectCheckResult(msg) {
|
|
445
|
+
var slug = msg.slug || pendingRemoveSlug;
|
|
446
|
+
var name = msg.name || pendingRemoveName || slug;
|
|
447
|
+
if (!slug) return;
|
|
448
|
+
|
|
449
|
+
if (msg.count > 0) {
|
|
450
|
+
showRemoveProjectTaskDialog(slug, name, msg.count);
|
|
451
|
+
} else {
|
|
452
|
+
var isWt = slug.indexOf("--") !== -1;
|
|
453
|
+
var confirmMsg = isWt
|
|
454
|
+
? 'Delete worktree "' + name + '"? The branch and working directory will be removed from disk.'
|
|
455
|
+
: 'Remove "' + name + '"? You can re-add it later.';
|
|
456
|
+
_ctx.showConfirm(confirmMsg, function () {
|
|
457
|
+
var iconEl = document.querySelector('.icon-strip-item[data-slug="' + slug + '"]');
|
|
458
|
+
if (iconEl) {
|
|
459
|
+
var rect = iconEl.getBoundingClientRect();
|
|
460
|
+
_ctx.spawnDustParticles(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
461
|
+
}
|
|
462
|
+
setTimeout(function () {
|
|
463
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
464
|
+
_ctx.getWs().send(JSON.stringify({ type: "remove_project", slug: slug }));
|
|
465
|
+
}
|
|
466
|
+
}, 1000);
|
|
467
|
+
}, "Remove", true);
|
|
468
|
+
}
|
|
469
|
+
pendingRemoveSlug = null;
|
|
470
|
+
pendingRemoveName = null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function showRemoveProjectTaskDialog(slug, name, taskCount) {
|
|
474
|
+
var otherProjects = cachedProjects.filter(function (p) { return p.slug !== slug; });
|
|
475
|
+
|
|
476
|
+
var modal = document.createElement("div");
|
|
477
|
+
modal.className = "remove-project-task-modal";
|
|
478
|
+
modal.innerHTML =
|
|
479
|
+
'<div class="remove-project-task-backdrop"></div>' +
|
|
480
|
+
'<div class="remove-project-task-dialog">' +
|
|
481
|
+
'<div class="remove-project-task-title">Remove project "' + (name || slug) + '"</div>' +
|
|
482
|
+
'<div class="remove-project-task-text">This project has <strong>' + taskCount + '</strong> task' + (taskCount > 1 ? 's' : '') + '/schedule' + (taskCount > 1 ? 's' : '') + '.</div>' +
|
|
483
|
+
'<div class="remove-project-task-options">' +
|
|
484
|
+
(otherProjects.length > 0
|
|
485
|
+
? '<div class="remove-project-task-label">Move tasks to:</div>' +
|
|
486
|
+
'<select class="remove-project-task-select" id="rpt-move-target">' +
|
|
487
|
+
otherProjects.map(function (p) {
|
|
488
|
+
return '<option value="' + p.slug + '">' + (p.title || p.project || p.slug) + '</option>';
|
|
489
|
+
}).join("") +
|
|
490
|
+
'</select>' +
|
|
491
|
+
'<button class="remove-project-task-btn move" id="rpt-move-btn">Move & Remove</button>'
|
|
492
|
+
: '') +
|
|
493
|
+
'<button class="remove-project-task-btn delete" id="rpt-delete-btn">Delete all & Remove</button>' +
|
|
494
|
+
'<button class="remove-project-task-btn cancel" id="rpt-cancel-btn">Cancel</button>' +
|
|
495
|
+
'</div>' +
|
|
496
|
+
'</div>';
|
|
497
|
+
|
|
498
|
+
document.body.appendChild(modal);
|
|
499
|
+
|
|
500
|
+
var backdrop = modal.querySelector(".remove-project-task-backdrop");
|
|
501
|
+
var moveBtn = modal.querySelector("#rpt-move-btn");
|
|
502
|
+
var deleteBtn = modal.querySelector("#rpt-delete-btn");
|
|
503
|
+
var cancelBtn = modal.querySelector("#rpt-cancel-btn");
|
|
504
|
+
var selectEl = modal.querySelector("#rpt-move-target");
|
|
505
|
+
|
|
506
|
+
function close() { modal.remove(); }
|
|
507
|
+
backdrop.addEventListener("click", close);
|
|
508
|
+
cancelBtn.addEventListener("click", close);
|
|
509
|
+
|
|
510
|
+
if (moveBtn) {
|
|
511
|
+
moveBtn.addEventListener("click", function () {
|
|
512
|
+
var targetSlug = selectEl ? selectEl.value : null;
|
|
513
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1 && targetSlug) {
|
|
514
|
+
_ctx.getWs().send(JSON.stringify({ type: "remove_project", slug: slug, moveTasksTo: targetSlug }));
|
|
515
|
+
}
|
|
516
|
+
close();
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
deleteBtn.addEventListener("click", function () {
|
|
521
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
522
|
+
_ctx.getWs().send(JSON.stringify({ type: "remove_project", slug: slug }));
|
|
523
|
+
}
|
|
524
|
+
close();
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export function handleRemoveProjectResult(msg) {
|
|
529
|
+
if (msg.ok) {
|
|
530
|
+
if (msg.slug === _ctx.currentSlug) {
|
|
531
|
+
var isWorktree = msg.slug.indexOf("--") !== -1;
|
|
532
|
+
var parentSlug = isWorktree ? msg.slug.split("--")[0] : null;
|
|
533
|
+
|
|
534
|
+
_ctx.showToast(isWorktree ? "Worktree removed" : "Project removed", "success");
|
|
535
|
+
|
|
536
|
+
// Suppress disconnect overlay and reconnect by detaching the WS
|
|
537
|
+
var ws = _ctx.getWs();
|
|
538
|
+
if (ws) { ws.onclose = null; ws.onerror = null; ws.close(); _ctx.setWs(null); }
|
|
539
|
+
_ctx.cancelReconnect();
|
|
540
|
+
_ctx.setConnected(false);
|
|
541
|
+
_ctx.connectOverlay.classList.add("hidden");
|
|
542
|
+
if (!isWorktree) {
|
|
543
|
+
var removedProj = null;
|
|
544
|
+
for (var ri = 0; ri < cachedProjects.length; ri++) {
|
|
545
|
+
if (cachedProjects[ri].slug === msg.slug) { removedProj = cachedProjects[ri]; break; }
|
|
546
|
+
}
|
|
547
|
+
if (removedProj) {
|
|
548
|
+
cachedRemovedProjects.push({
|
|
549
|
+
path: removedProj.path || "",
|
|
550
|
+
title: removedProj.title || null,
|
|
551
|
+
icon: removedProj.icon || null,
|
|
552
|
+
removedAt: Date.now(),
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
cachedProjects = cachedProjects.filter(function (p) { return p.slug !== msg.slug; });
|
|
557
|
+
cachedProjectCount = cachedProjects.length;
|
|
558
|
+
_ctx.setCurrentSlug(null);
|
|
559
|
+
renderProjectList();
|
|
560
|
+
resetClientState();
|
|
561
|
+
|
|
562
|
+
if (parentSlug) {
|
|
563
|
+
switchProject(parentSlug);
|
|
564
|
+
} else {
|
|
565
|
+
_ctx.showHomeHub();
|
|
566
|
+
}
|
|
567
|
+
} else {
|
|
568
|
+
_ctx.showToast(msg.slug.indexOf("--") !== -1 ? "Worktree removed" : "Project removed", "success");
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
_ctx.showToast(msg.error || "Failed to remove project", "error");
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// --- Add project modal ---
|
|
576
|
+
|
|
577
|
+
function switchAddProjectMode(mode) {
|
|
578
|
+
addProjectMode = mode;
|
|
579
|
+
for (var mi = 0; mi < addProjectModeBtns.length; mi++) {
|
|
580
|
+
var btn = addProjectModeBtns[mi];
|
|
581
|
+
if (btn.dataset.mode === mode) {
|
|
582
|
+
btn.classList.add("active");
|
|
583
|
+
} else {
|
|
584
|
+
btn.classList.remove("active");
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
for (var pi = 0; pi < addProjectPanels.length; pi++) {
|
|
588
|
+
var panel = addProjectPanels[pi];
|
|
589
|
+
if (panel.dataset.panel === mode) {
|
|
590
|
+
panel.classList.add("active");
|
|
591
|
+
} else {
|
|
592
|
+
panel.classList.remove("active");
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
addProjectError.classList.add("hidden");
|
|
596
|
+
addProjectCloneProgress.classList.add("hidden");
|
|
597
|
+
if (mode === "existing") {
|
|
598
|
+
addProjectOk.textContent = "Add";
|
|
599
|
+
} else if (mode === "create") {
|
|
600
|
+
addProjectOk.textContent = "Create";
|
|
601
|
+
} else if (mode === "clone") {
|
|
602
|
+
addProjectOk.textContent = "Clone";
|
|
603
|
+
}
|
|
604
|
+
setTimeout(function () {
|
|
605
|
+
if (mode === "existing") {
|
|
606
|
+
addProjectInput.focus();
|
|
607
|
+
} else if (mode === "create") {
|
|
608
|
+
addProjectCreateInput.focus();
|
|
609
|
+
} else if (mode === "clone") {
|
|
610
|
+
addProjectCloneInput.focus();
|
|
611
|
+
}
|
|
612
|
+
}, 50);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export function openAddProjectModal() {
|
|
616
|
+
addProjectModal.classList.remove("hidden");
|
|
617
|
+
addProjectInput.value = "/";
|
|
618
|
+
addProjectCreateInput.value = "";
|
|
619
|
+
addProjectCloneInput.value = "";
|
|
620
|
+
addProjectError.classList.add("hidden");
|
|
621
|
+
addProjectError.textContent = "";
|
|
622
|
+
addProjectCloneProgress.classList.add("hidden");
|
|
623
|
+
addProjectSuggestions.classList.add("hidden");
|
|
624
|
+
addProjectSuggestions.innerHTML = "";
|
|
625
|
+
addProjectActiveIdx = -1;
|
|
626
|
+
addProjectOk.disabled = false;
|
|
627
|
+
var existingBtn = addProjectModal.querySelector('.add-project-mode-btn[data-mode="existing"]');
|
|
628
|
+
if (_ctx.isOsUsers) {
|
|
629
|
+
// Default: disable existing directory for multi-user, but allow for admins
|
|
630
|
+
existingBtn.disabled = true;
|
|
631
|
+
switchAddProjectMode("create");
|
|
632
|
+
if (_ctx.checkAdminAccess) {
|
|
633
|
+
_ctx.checkAdminAccess().then(function (isAdmin) {
|
|
634
|
+
if (isAdmin) {
|
|
635
|
+
existingBtn.disabled = false;
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
existingBtn.disabled = false;
|
|
641
|
+
switchAddProjectMode("existing");
|
|
642
|
+
}
|
|
643
|
+
renderRemovedProjectsList();
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function renderRemovedProjectsList() {
|
|
647
|
+
if (!addProjectRemoved) return;
|
|
648
|
+
addProjectRemoved.innerHTML = "";
|
|
649
|
+
if (!cachedRemovedProjects || cachedRemovedProjects.length === 0) {
|
|
650
|
+
addProjectRemoved.classList.add("hidden");
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
addProjectRemoved.classList.remove("hidden");
|
|
654
|
+
for (var ri = 0; ri < cachedRemovedProjects.length; ri++) {
|
|
655
|
+
var rp = cachedRemovedProjects[ri];
|
|
656
|
+
var item = document.createElement("div");
|
|
657
|
+
item.className = "add-project-removed-item";
|
|
658
|
+
item.dataset.path = rp.path;
|
|
659
|
+
item.addEventListener("click", function () {
|
|
660
|
+
var p = this.dataset.path;
|
|
661
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
662
|
+
_ctx.getWs().send(JSON.stringify({ type: "add_project", path: p }));
|
|
663
|
+
}
|
|
664
|
+
closeAddProjectModal();
|
|
665
|
+
});
|
|
666
|
+
var iconEl = document.createElement("span");
|
|
667
|
+
iconEl.className = "add-project-removed-icon";
|
|
668
|
+
iconEl.textContent = rp.icon || "📁";
|
|
669
|
+
item.appendChild(iconEl);
|
|
670
|
+
var info = document.createElement("div");
|
|
671
|
+
info.className = "add-project-removed-info";
|
|
672
|
+
var nameEl = document.createElement("div");
|
|
673
|
+
nameEl.className = "add-project-removed-name";
|
|
674
|
+
nameEl.textContent = rp.title || rp.path.split("/").pop() || rp.path;
|
|
675
|
+
info.appendChild(nameEl);
|
|
676
|
+
var pathEl = document.createElement("div");
|
|
677
|
+
pathEl.className = "add-project-removed-path";
|
|
678
|
+
pathEl.textContent = rp.path;
|
|
679
|
+
info.appendChild(pathEl);
|
|
680
|
+
item.appendChild(info);
|
|
681
|
+
addProjectRemoved.appendChild(item);
|
|
682
|
+
}
|
|
683
|
+
try { parseEmojis(addProjectRemoved); } catch (e) {}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export function closeAddProjectModal() {
|
|
687
|
+
addProjectModal.classList.add("hidden");
|
|
688
|
+
addProjectInput.value = "";
|
|
689
|
+
addProjectCreateInput.value = "";
|
|
690
|
+
addProjectCloneInput.value = "";
|
|
691
|
+
addProjectSuggestions.classList.add("hidden");
|
|
692
|
+
addProjectSuggestions.innerHTML = "";
|
|
693
|
+
addProjectError.classList.add("hidden");
|
|
694
|
+
addProjectCloneProgress.classList.add("hidden");
|
|
695
|
+
addProjectActiveIdx = -1;
|
|
696
|
+
if (addProjectDebounce) { clearTimeout(addProjectDebounce); addProjectDebounce = null; }
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function requestBrowseDir(val) {
|
|
700
|
+
if (!_ctx.getWs() || _ctx.getWs().readyState !== 1) return;
|
|
701
|
+
_ctx.getWs().send(JSON.stringify({ type: "browse_dir", path: val }));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export function handleBrowseDirResult(msg) {
|
|
705
|
+
addProjectSuggestions.innerHTML = "";
|
|
706
|
+
addProjectActiveIdx = -1;
|
|
707
|
+
if (msg.error) {
|
|
708
|
+
addProjectSuggestions.classList.add("hidden");
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
var entries = msg.entries || [];
|
|
712
|
+
if (entries.length === 0) {
|
|
713
|
+
addProjectSuggestions.classList.add("hidden");
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
for (var si = 0; si < entries.length; si++) {
|
|
717
|
+
var entry = entries[si];
|
|
718
|
+
var item = document.createElement("div");
|
|
719
|
+
item.className = "add-project-suggestion-item";
|
|
720
|
+
item.dataset.path = entry.path;
|
|
721
|
+
item.innerHTML = '<i data-lucide="folder"></i><span class="add-project-suggestion-name">' +
|
|
722
|
+
escapeHtml(entry.name) + '</span>';
|
|
723
|
+
item.addEventListener("click", function (e) {
|
|
724
|
+
var p = this.dataset.path + "/";
|
|
725
|
+
addProjectInput.value = p;
|
|
726
|
+
addProjectInput.focus();
|
|
727
|
+
addProjectError.classList.add("hidden");
|
|
728
|
+
requestBrowseDir(p);
|
|
729
|
+
});
|
|
730
|
+
addProjectSuggestions.appendChild(item);
|
|
731
|
+
}
|
|
732
|
+
addProjectSuggestions.classList.remove("hidden");
|
|
733
|
+
refreshIcons();
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
export function handleAddProjectResult(msg) {
|
|
737
|
+
addProjectCloneProgress.classList.add("hidden");
|
|
738
|
+
if (msg.ok) {
|
|
739
|
+
closeAddProjectModal();
|
|
740
|
+
if (msg.existing) {
|
|
741
|
+
_ctx.showToast("Project already registered", "info");
|
|
742
|
+
} else {
|
|
743
|
+
var toastMsg = addProjectMode === "create" ? "Project created" : addProjectMode === "clone" ? "Project cloned" : "Project added";
|
|
744
|
+
_ctx.showToast(toastMsg, "success");
|
|
745
|
+
if (msg.slug) {
|
|
746
|
+
switchProject(msg.slug);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} else {
|
|
750
|
+
addProjectError.textContent = msg.error || "Failed to add project";
|
|
751
|
+
addProjectError.classList.remove("hidden");
|
|
752
|
+
addProjectOk.disabled = false;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export function handleCloneProgress(msg) {
|
|
757
|
+
if (msg.status === "cloning") {
|
|
758
|
+
addProjectCloneProgress.classList.remove("hidden");
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function setActiveIdx(idx) {
|
|
763
|
+
var items = addProjectSuggestions.querySelectorAll(".add-project-suggestion-item");
|
|
764
|
+
addProjectActiveIdx = idx;
|
|
765
|
+
for (var ai = 0; ai < items.length; ai++) {
|
|
766
|
+
if (ai === idx) {
|
|
767
|
+
items[ai].classList.add("active");
|
|
768
|
+
items[ai].scrollIntoView({ block: "nearest" });
|
|
769
|
+
} else {
|
|
770
|
+
items[ai].classList.remove("active");
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function submitAddProject() {
|
|
776
|
+
addProjectError.classList.add("hidden");
|
|
777
|
+
addProjectOk.disabled = true;
|
|
778
|
+
|
|
779
|
+
if (addProjectMode === "existing") {
|
|
780
|
+
var val = addProjectInput.value.replace(/\/+$/, "");
|
|
781
|
+
if (!val) { addProjectOk.disabled = false; return; }
|
|
782
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
783
|
+
_ctx.getWs().send(JSON.stringify({ type: "add_project", path: val }));
|
|
784
|
+
}
|
|
785
|
+
} else if (addProjectMode === "create") {
|
|
786
|
+
var name = addProjectCreateInput.value.trim();
|
|
787
|
+
if (!name) { addProjectOk.disabled = false; return; }
|
|
788
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
789
|
+
_ctx.getWs().send(JSON.stringify({ type: "create_project", name: name }));
|
|
790
|
+
}
|
|
791
|
+
} else if (addProjectMode === "clone") {
|
|
792
|
+
var url = addProjectCloneInput.value.trim();
|
|
793
|
+
if (!url) { addProjectOk.disabled = false; return; }
|
|
794
|
+
if (_ctx.getWs() && _ctx.getWs().readyState === 1) {
|
|
795
|
+
_ctx.getWs().send(JSON.stringify({ type: "clone_project", url: url }));
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|