clay-server 2.27.0-beta.8 → 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-debate.js +19 -12
- 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 -8521
- 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,1269 @@
|
|
|
1
|
+
// sidebar-mobile.js - Mobile sheet overlays, tab bar, and mobile-specific rendering
|
|
2
|
+
// Extracted from sidebar.js (PR-38)
|
|
3
|
+
|
|
4
|
+
import { mateAvatarUrl } from './avatar.js';
|
|
5
|
+
import { escapeHtml } from './utils.js';
|
|
6
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
7
|
+
import { parseEmojis } from './markdown.js';
|
|
8
|
+
import { getCurrentTheme, getChatLayout, setChatLayout } from './theme.js';
|
|
9
|
+
import { openCommandPalette } from './command-palette.js';
|
|
10
|
+
import { getMateSessions } from './mate-sidebar.js';
|
|
11
|
+
import { openProjectSettings } from './project-settings.js';
|
|
12
|
+
import {
|
|
13
|
+
getCachedSessions,
|
|
14
|
+
getDateGroup
|
|
15
|
+
} from './sidebar-sessions.js';
|
|
16
|
+
import {
|
|
17
|
+
getCachedProjectList,
|
|
18
|
+
getCachedCurrentSlug,
|
|
19
|
+
getProjectAbbrev
|
|
20
|
+
} from './sidebar-projects.js';
|
|
21
|
+
import {
|
|
22
|
+
getCurrentDmUserId,
|
|
23
|
+
getCachedMates,
|
|
24
|
+
getCachedDmFavorites,
|
|
25
|
+
getCachedDmUnread,
|
|
26
|
+
getCachedDmRemovedUsers
|
|
27
|
+
} from './sidebar-mates.js';
|
|
28
|
+
|
|
29
|
+
var _ctx = null;
|
|
30
|
+
|
|
31
|
+
// --- Mobile state ---
|
|
32
|
+
var mobileChatSheetOpen = false;
|
|
33
|
+
var mobileSheetMateData = null;
|
|
34
|
+
var expandedMobileLoopGroups = new Set();
|
|
35
|
+
var expandedMobileLoopRuns = new Set();
|
|
36
|
+
|
|
37
|
+
export function setMobileSheetMateData(data) {
|
|
38
|
+
mobileSheetMateData = data;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function openMobileSheet(type) {
|
|
42
|
+
var sheet = document.getElementById("mobile-sheet");
|
|
43
|
+
if (!sheet) return;
|
|
44
|
+
|
|
45
|
+
var titleEl = sheet.querySelector(".mobile-sheet-title");
|
|
46
|
+
var listEl = sheet.querySelector(".mobile-sheet-list");
|
|
47
|
+
if (!titleEl || !listEl) return;
|
|
48
|
+
|
|
49
|
+
// Return file tree to sidebar before clearing (prevents destroying it)
|
|
50
|
+
if (sheet.classList.contains("sheet-files")) {
|
|
51
|
+
var prevFileTree = document.getElementById("file-tree");
|
|
52
|
+
var prevPanel = document.getElementById("sidebar-panel-files");
|
|
53
|
+
if (prevFileTree && prevPanel) prevPanel.appendChild(prevFileTree);
|
|
54
|
+
}
|
|
55
|
+
// Return knowledge files to mate sidebar before clearing
|
|
56
|
+
if (sheet.classList.contains("sheet-knowledge")) {
|
|
57
|
+
var prevKnowledge = document.getElementById("mate-knowledge-files");
|
|
58
|
+
var prevKnowledgePanel = document.getElementById("mate-sidebar-knowledge");
|
|
59
|
+
if (prevKnowledge && prevKnowledgePanel) prevKnowledgePanel.appendChild(prevKnowledge);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
listEl.innerHTML = "";
|
|
63
|
+
sheet.classList.remove("sheet-files", "sheet-knowledge");
|
|
64
|
+
|
|
65
|
+
if (type === "projects") {
|
|
66
|
+
titleEl.textContent = "Projects";
|
|
67
|
+
renderSheetProjects(listEl);
|
|
68
|
+
} else if (type === "sessions") {
|
|
69
|
+
titleEl.textContent = "Chat";
|
|
70
|
+
renderSheetSessions(listEl);
|
|
71
|
+
} else if (type === "files") {
|
|
72
|
+
titleEl.textContent = "Files";
|
|
73
|
+
sheet.classList.add("sheet-files");
|
|
74
|
+
var fileTree = document.getElementById("file-tree");
|
|
75
|
+
if (fileTree) {
|
|
76
|
+
listEl.appendChild(fileTree);
|
|
77
|
+
fileTree.classList.remove("hidden");
|
|
78
|
+
}
|
|
79
|
+
if (_ctx.onFilesTabOpen) _ctx.onFilesTabOpen();
|
|
80
|
+
} else if (type === "mate-knowledge") {
|
|
81
|
+
titleEl.textContent = "Knowledge";
|
|
82
|
+
sheet.classList.add("sheet-knowledge");
|
|
83
|
+
var knowledgeFiles = document.getElementById("mate-knowledge-files");
|
|
84
|
+
if (knowledgeFiles) {
|
|
85
|
+
listEl.appendChild(knowledgeFiles);
|
|
86
|
+
knowledgeFiles.classList.remove("hidden");
|
|
87
|
+
}
|
|
88
|
+
// Request knowledge list if not loaded
|
|
89
|
+
if (_ctx.requestKnowledgeList) _ctx.requestKnowledgeList();
|
|
90
|
+
} else if (type === "mate-profile") {
|
|
91
|
+
titleEl.textContent = "";
|
|
92
|
+
renderSheetMateProfile(listEl);
|
|
93
|
+
} else if (type === "search") {
|
|
94
|
+
titleEl.textContent = "Search";
|
|
95
|
+
renderSheetSearch(listEl);
|
|
96
|
+
} else if (type === "tools") {
|
|
97
|
+
titleEl.textContent = "Tools";
|
|
98
|
+
renderSheetTools(listEl);
|
|
99
|
+
} else if (type === "settings") {
|
|
100
|
+
titleEl.textContent = "Settings";
|
|
101
|
+
renderSheetSettings(listEl);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
sheet.classList.remove("hidden", "closing");
|
|
105
|
+
refreshIcons();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function closeMobileSheet() {
|
|
109
|
+
var sheet = document.getElementById("mobile-sheet");
|
|
110
|
+
if (!sheet || sheet.classList.contains("hidden")) return;
|
|
111
|
+
|
|
112
|
+
mobileChatSheetOpen = false;
|
|
113
|
+
|
|
114
|
+
// Return file tree to sidebar if it was moved
|
|
115
|
+
if (sheet.classList.contains("sheet-files")) {
|
|
116
|
+
var fileTree = document.getElementById("file-tree");
|
|
117
|
+
var sidebarFilesPanel = document.getElementById("sidebar-panel-files");
|
|
118
|
+
if (fileTree && sidebarFilesPanel) {
|
|
119
|
+
sidebarFilesPanel.appendChild(fileTree);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Return knowledge files to mate sidebar if moved
|
|
123
|
+
if (sheet.classList.contains("sheet-knowledge")) {
|
|
124
|
+
var knowledgeFiles = document.getElementById("mate-knowledge-files");
|
|
125
|
+
var knowledgePanel = document.getElementById("mate-sidebar-knowledge");
|
|
126
|
+
if (knowledgeFiles && knowledgePanel) {
|
|
127
|
+
knowledgePanel.appendChild(knowledgeFiles);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
sheet.classList.add("closing");
|
|
132
|
+
setTimeout(function () {
|
|
133
|
+
sheet.classList.add("hidden");
|
|
134
|
+
sheet.classList.remove("closing", "sheet-files");
|
|
135
|
+
}, 230);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function renderSheetProjects(listEl) {
|
|
139
|
+
for (var i = 0; i < getCachedProjectList().length; i++) {
|
|
140
|
+
(function (p) {
|
|
141
|
+
var el = document.createElement("button");
|
|
142
|
+
el.className = "mobile-project-item" + (p.slug === getCachedCurrentSlug() ? " active" : "");
|
|
143
|
+
|
|
144
|
+
var abbrev = document.createElement("span");
|
|
145
|
+
abbrev.className = "mobile-project-abbrev";
|
|
146
|
+
if (p.icon) {
|
|
147
|
+
abbrev.textContent = p.icon;
|
|
148
|
+
parseEmojis(abbrev);
|
|
149
|
+
} else {
|
|
150
|
+
abbrev.textContent = getProjectAbbrev(p.name);
|
|
151
|
+
}
|
|
152
|
+
el.appendChild(abbrev);
|
|
153
|
+
|
|
154
|
+
var name = document.createElement("span");
|
|
155
|
+
name.className = "mobile-project-name";
|
|
156
|
+
name.textContent = p.name;
|
|
157
|
+
el.appendChild(name);
|
|
158
|
+
|
|
159
|
+
if (p.isProcessing) {
|
|
160
|
+
var dot = document.createElement("span");
|
|
161
|
+
dot.className = "mobile-project-processing";
|
|
162
|
+
el.appendChild(dot);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (p.unread > 0 && p.slug !== getCachedCurrentSlug()) {
|
|
166
|
+
var mBadge = document.createElement("span");
|
|
167
|
+
mBadge.className = "mobile-project-unread";
|
|
168
|
+
mBadge.textContent = p.unread > 99 ? "99+" : String(p.unread);
|
|
169
|
+
el.appendChild(mBadge);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
el.addEventListener("click", function () {
|
|
173
|
+
if (_ctx.switchProject) _ctx.switchProject(p.slug);
|
|
174
|
+
closeMobileSheet();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
listEl.appendChild(el);
|
|
178
|
+
})(getCachedProjectList()[i]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function renderSheetSessions(listEl) {
|
|
183
|
+
// --- Context filter bar (horizontal scroll) ---
|
|
184
|
+
var filterBar = document.createElement("div");
|
|
185
|
+
filterBar.className = "mobile-chat-filter-bar";
|
|
186
|
+
|
|
187
|
+
// Current project chip (always first, pre-selected)
|
|
188
|
+
var currentProject = null;
|
|
189
|
+
for (var pi = 0; pi < getCachedProjectList().length; pi++) {
|
|
190
|
+
if (getCachedProjectList()[pi].slug === getCachedCurrentSlug()) {
|
|
191
|
+
currentProject = getCachedProjectList()[pi];
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Build chips: projects first, then mates
|
|
197
|
+
var chips = [];
|
|
198
|
+
|
|
199
|
+
for (var ci = 0; ci < getCachedProjectList().length; ci++) {
|
|
200
|
+
(function (p) {
|
|
201
|
+
var chip = document.createElement("button");
|
|
202
|
+
chip.className = "mobile-chat-chip";
|
|
203
|
+
var isDmActive = document.body.classList.contains("mate-dm-active");
|
|
204
|
+
if (p.slug === getCachedCurrentSlug() && !isDmActive) chip.classList.add("active");
|
|
205
|
+
chip.dataset.type = "project";
|
|
206
|
+
chip.dataset.slug = p.slug;
|
|
207
|
+
|
|
208
|
+
var abbrev = document.createElement("span");
|
|
209
|
+
abbrev.className = "mobile-chat-chip-icon";
|
|
210
|
+
if (p.icon) {
|
|
211
|
+
abbrev.textContent = p.icon;
|
|
212
|
+
parseEmojis(abbrev);
|
|
213
|
+
} else {
|
|
214
|
+
abbrev.textContent = getProjectAbbrev(p.name);
|
|
215
|
+
}
|
|
216
|
+
chip.appendChild(abbrev);
|
|
217
|
+
|
|
218
|
+
var label = document.createElement("span");
|
|
219
|
+
label.textContent = p.name;
|
|
220
|
+
chip.appendChild(label);
|
|
221
|
+
|
|
222
|
+
// Processing dot: same class as icon strip
|
|
223
|
+
var statusDot = document.createElement("span");
|
|
224
|
+
statusDot.className = "icon-strip-status";
|
|
225
|
+
if (p.isProcessing) statusDot.classList.add("processing");
|
|
226
|
+
chip.appendChild(statusDot);
|
|
227
|
+
|
|
228
|
+
if (p.unread > 0 && p.slug !== getCachedCurrentSlug()) {
|
|
229
|
+
var badge = document.createElement("span");
|
|
230
|
+
badge.className = "mobile-chat-chip-badge";
|
|
231
|
+
badge.textContent = p.unread > 99 ? "99+" : String(p.unread);
|
|
232
|
+
chip.appendChild(badge);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
chips.push(chip);
|
|
236
|
+
})(getCachedProjectList()[ci]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
var favoriteChipMates = getCachedMates().filter(function (m) {
|
|
240
|
+
if (getCachedDmRemovedUsers()[m.id]) return false;
|
|
241
|
+
if (getCachedDmFavorites().indexOf(m.id) !== -1) return true;
|
|
242
|
+
if (getCachedDmUnread()[m.id] && getCachedDmUnread()[m.id] > 0) return true;
|
|
243
|
+
return false;
|
|
244
|
+
});
|
|
245
|
+
var sortedChipMates = favoriteChipMates.sort(function (a, b) {
|
|
246
|
+
var aBuiltin = a.builtinKey ? 1 : 0;
|
|
247
|
+
var bBuiltin = b.builtinKey ? 1 : 0;
|
|
248
|
+
if (aBuiltin !== bBuiltin) return bBuiltin - aBuiltin;
|
|
249
|
+
return (a.createdAt || 0) - (b.createdAt || 0);
|
|
250
|
+
});
|
|
251
|
+
for (var mi = 0; mi < sortedChipMates.length; mi++) {
|
|
252
|
+
(function (mate) {
|
|
253
|
+
var mp = mate.profile || {};
|
|
254
|
+
var chip = document.createElement("button");
|
|
255
|
+
chip.className = "mobile-chat-chip";
|
|
256
|
+
if (getCurrentDmUserId() === mate.id) chip.classList.add("active");
|
|
257
|
+
chip.dataset.type = "mate";
|
|
258
|
+
chip.dataset.mateId = mate.id;
|
|
259
|
+
|
|
260
|
+
var avatarEl = document.createElement("img");
|
|
261
|
+
avatarEl.className = "mobile-chat-chip-avatar";
|
|
262
|
+
avatarEl.src = mateAvatarUrl(mate, 20);
|
|
263
|
+
avatarEl.alt = mp.displayName || mate.name || "";
|
|
264
|
+
chip.appendChild(avatarEl);
|
|
265
|
+
|
|
266
|
+
var label = document.createElement("span");
|
|
267
|
+
label.textContent = mp.displayName || mate.name || "Mate";
|
|
268
|
+
chip.appendChild(label);
|
|
269
|
+
|
|
270
|
+
// Processing dot: same class as icon strip, same data source
|
|
271
|
+
var mateSlug = "mate-" + mate.id;
|
|
272
|
+
var mateProj = null;
|
|
273
|
+
var allProjects = (_ctx && _ctx.projectList) || [];
|
|
274
|
+
for (var pi = 0; pi < allProjects.length; pi++) {
|
|
275
|
+
if (allProjects[pi].slug === mateSlug) { mateProj = allProjects[pi]; break; }
|
|
276
|
+
}
|
|
277
|
+
var statusDot = document.createElement("span");
|
|
278
|
+
statusDot.className = "icon-strip-status";
|
|
279
|
+
if (mateProj && mateProj.isProcessing) statusDot.classList.add("processing");
|
|
280
|
+
chip.appendChild(statusDot);
|
|
281
|
+
|
|
282
|
+
var unreadCount = getCachedDmUnread()[mate.id] || 0;
|
|
283
|
+
if (unreadCount > 0) {
|
|
284
|
+
var badge = document.createElement("span");
|
|
285
|
+
badge.className = "mobile-chat-chip-badge";
|
|
286
|
+
badge.textContent = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
287
|
+
chip.appendChild(badge);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
chips.push(chip);
|
|
291
|
+
})(sortedChipMates[mi]);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
for (var i = 0; i < chips.length; i++) {
|
|
295
|
+
filterBar.appendChild(chips[i]);
|
|
296
|
+
}
|
|
297
|
+
listEl.appendChild(filterBar);
|
|
298
|
+
|
|
299
|
+
// --- Session list container ---
|
|
300
|
+
var sessionListEl = document.createElement("div");
|
|
301
|
+
sessionListEl.className = "mobile-chat-session-list";
|
|
302
|
+
listEl.appendChild(sessionListEl);
|
|
303
|
+
|
|
304
|
+
// --- Render sessions for a context ---
|
|
305
|
+
function renderSessionsForContext(type, slug, mateId) {
|
|
306
|
+
sessionListEl.innerHTML = "";
|
|
307
|
+
|
|
308
|
+
if (type === "project") {
|
|
309
|
+
renderMobileSessionsInto(sessionListEl);
|
|
310
|
+
} else if (type === "mate") {
|
|
311
|
+
// Mate DM: open the DM and show mate actions
|
|
312
|
+
if (_ctx.openDm) _ctx.openDm(mateId);
|
|
313
|
+
renderMateMobileActions(sessionListEl);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
refreshIcons();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// --- Chip click handlers ---
|
|
320
|
+
for (var j = 0; j < chips.length; j++) {
|
|
321
|
+
(function (chip) {
|
|
322
|
+
chip.addEventListener("click", function () {
|
|
323
|
+
// Deactivate all chips
|
|
324
|
+
for (var k = 0; k < chips.length; k++) {
|
|
325
|
+
chips[k].classList.remove("active");
|
|
326
|
+
}
|
|
327
|
+
chip.classList.add("active");
|
|
328
|
+
|
|
329
|
+
var type = chip.dataset.type;
|
|
330
|
+
if (type === "project") {
|
|
331
|
+
var slug = chip.dataset.slug;
|
|
332
|
+
var isDmNow = !!getCurrentDmUserId();
|
|
333
|
+
if (slug !== getCachedCurrentSlug() || isDmNow) {
|
|
334
|
+
// Switch project (or exit DM back to same project)
|
|
335
|
+
sessionListEl.innerHTML = "";
|
|
336
|
+
if (slug !== getCachedCurrentSlug()) {
|
|
337
|
+
var loading = document.createElement("div");
|
|
338
|
+
loading.className = "mobile-chat-context-note";
|
|
339
|
+
loading.textContent = "Loading sessions...";
|
|
340
|
+
sessionListEl.appendChild(loading);
|
|
341
|
+
}
|
|
342
|
+
if (_ctx.switchProject) _ctx.switchProject(slug);
|
|
343
|
+
if (!isDmNow || slug !== getCachedCurrentSlug()) {
|
|
344
|
+
// renderSessionList will be called by WS, which calls refreshMobileChatSheet
|
|
345
|
+
} else {
|
|
346
|
+
// Exited DM, same project - render sessions now
|
|
347
|
+
renderSessionsForContext("project", slug, null);
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
renderSessionsForContext("project", slug, null);
|
|
351
|
+
}
|
|
352
|
+
} else if (type === "mate") {
|
|
353
|
+
renderSessionsForContext("mate", null, chip.dataset.mateId);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
})(chips[j]);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Track that chat sheet is open
|
|
360
|
+
mobileChatSheetOpen = true;
|
|
361
|
+
|
|
362
|
+
// --- Initial render: show mate actions if DM active, otherwise project sessions ---
|
|
363
|
+
if (getCurrentDmUserId()) {
|
|
364
|
+
renderSessionsForContext("mate", null, getCurrentDmUserId());
|
|
365
|
+
} else {
|
|
366
|
+
renderSessionsForContext("project", getCachedCurrentSlug(), null);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Helper: create a mobile session item element
|
|
371
|
+
function createMobileSessionItem(s) {
|
|
372
|
+
var el = document.createElement("button");
|
|
373
|
+
el.className = "mobile-session-item" + (s.active ? " active" : "");
|
|
374
|
+
|
|
375
|
+
// Processing dot (left side, before title)
|
|
376
|
+
if (s.isProcessing) {
|
|
377
|
+
var dot = document.createElement("span");
|
|
378
|
+
dot.className = "mobile-session-processing";
|
|
379
|
+
el.appendChild(dot);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
var titleSpan = document.createElement("span");
|
|
383
|
+
titleSpan.className = "mobile-session-title";
|
|
384
|
+
titleSpan.textContent = s.title || "New Session";
|
|
385
|
+
el.appendChild(titleSpan);
|
|
386
|
+
|
|
387
|
+
// Unread badge (right side)
|
|
388
|
+
if (s.unread > 0 && !s.active) {
|
|
389
|
+
var badge = document.createElement("span");
|
|
390
|
+
badge.className = "mobile-session-unread";
|
|
391
|
+
badge.textContent = s.unread > 99 ? "99+" : String(s.unread);
|
|
392
|
+
el.appendChild(badge);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
(function (id) {
|
|
396
|
+
el.addEventListener("click", function () {
|
|
397
|
+
if (_ctx.ws && _ctx.connected) {
|
|
398
|
+
_ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
399
|
+
}
|
|
400
|
+
if (_ctx.dismissOverlayPanels) _ctx.dismissOverlayPanels();
|
|
401
|
+
closeMobileSheet();
|
|
402
|
+
});
|
|
403
|
+
})(s.id);
|
|
404
|
+
|
|
405
|
+
return el;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Helper: create a mobile loop child element (individual session inside a group)
|
|
409
|
+
function createMobileLoopChild(s) {
|
|
410
|
+
var el = document.createElement("button");
|
|
411
|
+
el.className = "mobile-loop-child" + (s.active ? " active" : "");
|
|
412
|
+
|
|
413
|
+
if (s.isProcessing) {
|
|
414
|
+
var dot = document.createElement("span");
|
|
415
|
+
dot.className = "mobile-session-processing";
|
|
416
|
+
el.appendChild(dot);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
var textSpan = document.createElement("span");
|
|
420
|
+
textSpan.className = "mobile-session-title";
|
|
421
|
+
if (s.loop) {
|
|
422
|
+
var isRalphChild = s.loop.source === "ralph";
|
|
423
|
+
var roleName = s.loop.role === "crafting" ? "Crafting" : s.loop.role === "judge" ? "Judge" : (isRalphChild ? "Coder" : "Run");
|
|
424
|
+
var iterSuffix = s.loop.role === "crafting" ? "" : " #" + s.loop.iteration;
|
|
425
|
+
var roleCls = s.loop.role === "crafting" ? " crafting" : (!isRalphChild ? " scheduled" : "");
|
|
426
|
+
var badge = document.createElement("span");
|
|
427
|
+
badge.className = "mobile-loop-role-badge" + roleCls;
|
|
428
|
+
badge.textContent = roleName + iterSuffix;
|
|
429
|
+
textSpan.appendChild(badge);
|
|
430
|
+
}
|
|
431
|
+
el.appendChild(textSpan);
|
|
432
|
+
|
|
433
|
+
(function (id) {
|
|
434
|
+
el.addEventListener("click", function () {
|
|
435
|
+
if (_ctx.ws && _ctx.connected) {
|
|
436
|
+
_ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
437
|
+
}
|
|
438
|
+
if (_ctx.dismissOverlayPanels) _ctx.dismissOverlayPanels();
|
|
439
|
+
closeMobileSheet();
|
|
440
|
+
});
|
|
441
|
+
})(s.id);
|
|
442
|
+
|
|
443
|
+
return el;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Helper: create a mobile loop run sub-group (collapsible time group)
|
|
447
|
+
function createMobileLoopRun(parentGk, startedAtKey, sessions, isRalph) {
|
|
448
|
+
var runGk = parentGk + ":" + startedAtKey;
|
|
449
|
+
var expanded = expandedMobileLoopRuns.has(runGk);
|
|
450
|
+
var startedAt = Number(startedAtKey);
|
|
451
|
+
var timeLabel = startedAt ? new Date(startedAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "Unknown";
|
|
452
|
+
|
|
453
|
+
var hasActive = false;
|
|
454
|
+
var anyProcessing = false;
|
|
455
|
+
var latestSession = sessions[0];
|
|
456
|
+
for (var i = 0; i < sessions.length; i++) {
|
|
457
|
+
if (sessions[i].active) hasActive = true;
|
|
458
|
+
if (sessions[i].isProcessing) anyProcessing = true;
|
|
459
|
+
if ((sessions[i].lastActivity || 0) > (latestSession.lastActivity || 0)) {
|
|
460
|
+
latestSession = sessions[i];
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
var wrapper = document.createElement("div");
|
|
465
|
+
wrapper.className = "mobile-loop-run-wrapper";
|
|
466
|
+
|
|
467
|
+
var header = document.createElement("button");
|
|
468
|
+
header.className = "mobile-loop-run" + (hasActive ? " active" : "") + (expanded ? " expanded" : "") + (isRalph ? "" : " scheduled");
|
|
469
|
+
|
|
470
|
+
var chevron = document.createElement("span");
|
|
471
|
+
chevron.className = "mobile-loop-chevron";
|
|
472
|
+
chevron.innerHTML = iconHtml("chevron-right");
|
|
473
|
+
header.appendChild(chevron);
|
|
474
|
+
|
|
475
|
+
var label = document.createElement("span");
|
|
476
|
+
label.className = "mobile-loop-run-time";
|
|
477
|
+
var labelHtml = "";
|
|
478
|
+
if (anyProcessing) {
|
|
479
|
+
labelHtml += '<span class="mobile-session-processing"></span> ';
|
|
480
|
+
}
|
|
481
|
+
labelHtml += escapeHtml(timeLabel);
|
|
482
|
+
label.innerHTML = labelHtml;
|
|
483
|
+
header.appendChild(label);
|
|
484
|
+
|
|
485
|
+
var countBadge = document.createElement("span");
|
|
486
|
+
countBadge.className = "mobile-loop-count" + (isRalph ? "" : " scheduled");
|
|
487
|
+
countBadge.textContent = String(sessions.length);
|
|
488
|
+
header.appendChild(countBadge);
|
|
489
|
+
|
|
490
|
+
header.addEventListener("click", (function (rk) {
|
|
491
|
+
return function (e) {
|
|
492
|
+
e.stopPropagation();
|
|
493
|
+
if (expandedMobileLoopRuns.has(rk)) {
|
|
494
|
+
expandedMobileLoopRuns.delete(rk);
|
|
495
|
+
} else {
|
|
496
|
+
expandedMobileLoopRuns.add(rk);
|
|
497
|
+
}
|
|
498
|
+
refreshMobileChatSheet();
|
|
499
|
+
};
|
|
500
|
+
})(runGk));
|
|
501
|
+
|
|
502
|
+
wrapper.appendChild(header);
|
|
503
|
+
|
|
504
|
+
if (expanded) {
|
|
505
|
+
var childContainer = document.createElement("div");
|
|
506
|
+
childContainer.className = "mobile-loop-children";
|
|
507
|
+
for (var k = 0; k < sessions.length; k++) {
|
|
508
|
+
childContainer.appendChild(createMobileLoopChild(sessions[k]));
|
|
509
|
+
}
|
|
510
|
+
wrapper.appendChild(childContainer);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return wrapper;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Helper: create a mobile loop group element (collapsible group header)
|
|
517
|
+
function createMobileLoopGroup(loopId, children, groupKey) {
|
|
518
|
+
var gk = groupKey || loopId;
|
|
519
|
+
|
|
520
|
+
// Sub-group children by startedAt (each run)
|
|
521
|
+
var runMap = {};
|
|
522
|
+
for (var i = 0; i < children.length; i++) {
|
|
523
|
+
var runKey = String(children[i].loop && children[i].loop.startedAt || 0);
|
|
524
|
+
if (!runMap[runKey]) runMap[runKey] = [];
|
|
525
|
+
runMap[runKey].push(children[i]);
|
|
526
|
+
}
|
|
527
|
+
var runKeys = Object.keys(runMap);
|
|
528
|
+
|
|
529
|
+
// Sort each run's children by iteration then role
|
|
530
|
+
for (var ri = 0; ri < runKeys.length; ri++) {
|
|
531
|
+
runMap[runKeys[ri]].sort(function (a, b) {
|
|
532
|
+
var ai = (a.loop && a.loop.iteration) || 0;
|
|
533
|
+
var bi = (b.loop && b.loop.iteration) || 0;
|
|
534
|
+
if (ai !== bi) return ai - bi;
|
|
535
|
+
var ar = (a.loop && a.loop.role === "judge") ? 1 : 0;
|
|
536
|
+
var br = (b.loop && b.loop.role === "judge") ? 1 : 0;
|
|
537
|
+
return ar - br;
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Sort runs by startedAt descending (newest first)
|
|
542
|
+
runKeys.sort(function (a, b) { return Number(b) - Number(a); });
|
|
543
|
+
|
|
544
|
+
var expanded = expandedMobileLoopGroups.has(gk);
|
|
545
|
+
var hasActive = false;
|
|
546
|
+
var anyProcessing = false;
|
|
547
|
+
var latestSession = children[0];
|
|
548
|
+
for (var ci = 0; ci < children.length; ci++) {
|
|
549
|
+
if (children[ci].active) hasActive = true;
|
|
550
|
+
if (children[ci].isProcessing) anyProcessing = true;
|
|
551
|
+
if ((children[ci].lastActivity || 0) > (latestSession.lastActivity || 0)) {
|
|
552
|
+
latestSession = children[ci];
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
var loopName = (children[0].loop && children[0].loop.name) || "Loop";
|
|
557
|
+
var isRalph = children[0].loop && children[0].loop.source === "ralph";
|
|
558
|
+
var isCrafting = false;
|
|
559
|
+
for (var j = 0; j < children.length; j++) {
|
|
560
|
+
if (children[j].loop && children[j].loop.role === "crafting") isCrafting = true;
|
|
561
|
+
}
|
|
562
|
+
var runCount = runKeys.length;
|
|
563
|
+
|
|
564
|
+
var wrapper = document.createElement("div");
|
|
565
|
+
wrapper.className = "mobile-loop-wrapper";
|
|
566
|
+
|
|
567
|
+
// Group header row
|
|
568
|
+
var header = document.createElement("button");
|
|
569
|
+
header.className = "mobile-loop-group" + (hasActive ? " active" : "") + (expanded ? " expanded" : "") + (isRalph ? "" : " scheduled");
|
|
570
|
+
|
|
571
|
+
var chevron = document.createElement("span");
|
|
572
|
+
chevron.className = "mobile-loop-chevron";
|
|
573
|
+
chevron.innerHTML = iconHtml("chevron-right");
|
|
574
|
+
header.appendChild(chevron);
|
|
575
|
+
|
|
576
|
+
var iconSpan = document.createElement("span");
|
|
577
|
+
var groupIcon = isRalph ? "repeat" : "calendar-clock";
|
|
578
|
+
iconSpan.className = "mobile-loop-icon" + (isRalph ? "" : " scheduled");
|
|
579
|
+
iconSpan.innerHTML = iconHtml(groupIcon);
|
|
580
|
+
header.appendChild(iconSpan);
|
|
581
|
+
|
|
582
|
+
if (anyProcessing) {
|
|
583
|
+
var dot = document.createElement("span");
|
|
584
|
+
dot.className = "mobile-session-processing";
|
|
585
|
+
header.appendChild(dot);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
var nameSpan = document.createElement("span");
|
|
589
|
+
nameSpan.className = "mobile-loop-name";
|
|
590
|
+
nameSpan.textContent = loopName;
|
|
591
|
+
header.appendChild(nameSpan);
|
|
592
|
+
|
|
593
|
+
if (isCrafting && children.length === 1) {
|
|
594
|
+
var craftBadge = document.createElement("span");
|
|
595
|
+
craftBadge.className = "mobile-loop-badge crafting";
|
|
596
|
+
craftBadge.textContent = "Crafting";
|
|
597
|
+
header.appendChild(craftBadge);
|
|
598
|
+
} else {
|
|
599
|
+
var countBadge = document.createElement("span");
|
|
600
|
+
countBadge.className = "mobile-loop-count" + (isRalph ? "" : " scheduled");
|
|
601
|
+
var countLabel = runCount === 1 ? String(children.length) : runCount + (runCount === 1 ? " run" : " runs");
|
|
602
|
+
countBadge.textContent = countLabel;
|
|
603
|
+
header.appendChild(countBadge);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Chevron toggles expansion
|
|
607
|
+
header.addEventListener("click", (function (lid) {
|
|
608
|
+
return function (e) {
|
|
609
|
+
e.stopPropagation();
|
|
610
|
+
if (expandedMobileLoopGroups.has(lid)) {
|
|
611
|
+
expandedMobileLoopGroups.delete(lid);
|
|
612
|
+
} else {
|
|
613
|
+
expandedMobileLoopGroups.add(lid);
|
|
614
|
+
}
|
|
615
|
+
refreshMobileChatSheet();
|
|
616
|
+
};
|
|
617
|
+
})(gk));
|
|
618
|
+
|
|
619
|
+
wrapper.appendChild(header);
|
|
620
|
+
|
|
621
|
+
// Expanded: show runs
|
|
622
|
+
if (expanded) {
|
|
623
|
+
var childContainer = document.createElement("div");
|
|
624
|
+
childContainer.className = "mobile-loop-children";
|
|
625
|
+
|
|
626
|
+
if (runCount === 1) {
|
|
627
|
+
var singleRun = runMap[runKeys[0]];
|
|
628
|
+
for (var sk = 0; sk < singleRun.length; sk++) {
|
|
629
|
+
childContainer.appendChild(createMobileLoopChild(singleRun[sk]));
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
for (var rk = 0; rk < runKeys.length; rk++) {
|
|
633
|
+
childContainer.appendChild(createMobileLoopRun(gk, runKeys[rk], runMap[runKeys[rk]], isRalph));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
wrapper.appendChild(childContainer);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return wrapper;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function renderMateMobileActions(container) {
|
|
644
|
+
var newSessionBtn = document.createElement("button");
|
|
645
|
+
newSessionBtn.className = "mobile-session-new";
|
|
646
|
+
newSessionBtn.innerHTML = '<i data-lucide="plus" style="width:16px;height:16px"></i> New session';
|
|
647
|
+
newSessionBtn.addEventListener("click", function () {
|
|
648
|
+
if (_ctx.ws && _ctx.connected) {
|
|
649
|
+
_ctx.ws.send(JSON.stringify({ type: "new_session" }));
|
|
650
|
+
}
|
|
651
|
+
closeMobileSheet();
|
|
652
|
+
});
|
|
653
|
+
container.appendChild(newSessionBtn);
|
|
654
|
+
|
|
655
|
+
var debateBtn = document.createElement("button");
|
|
656
|
+
debateBtn.className = "mobile-session-new";
|
|
657
|
+
debateBtn.innerHTML = '<i data-lucide="mic" style="width:16px;height:16px"></i> New debate';
|
|
658
|
+
debateBtn.addEventListener("click", function () {
|
|
659
|
+
closeMobileSheet();
|
|
660
|
+
var targetBtn = document.getElementById("mate-debate-btn");
|
|
661
|
+
if (targetBtn) setTimeout(function () { targetBtn.click(); }, 250);
|
|
662
|
+
});
|
|
663
|
+
container.appendChild(debateBtn);
|
|
664
|
+
|
|
665
|
+
// Render mate session list
|
|
666
|
+
var mateSessions = getMateSessions();
|
|
667
|
+
if (mateSessions.length > 0) {
|
|
668
|
+
var sorted = mateSessions.slice().sort(function (a, b) {
|
|
669
|
+
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
var currentGroup = "";
|
|
673
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
674
|
+
var s = sorted[i];
|
|
675
|
+
var group = getDateGroup(s.lastActivity || 0);
|
|
676
|
+
if (group !== currentGroup) {
|
|
677
|
+
currentGroup = group;
|
|
678
|
+
var header = document.createElement("div");
|
|
679
|
+
header.className = "mobile-sheet-group";
|
|
680
|
+
header.textContent = group;
|
|
681
|
+
container.appendChild(header);
|
|
682
|
+
}
|
|
683
|
+
var mateItem = createMobileSessionItem(s);
|
|
684
|
+
container.appendChild(mateItem);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
refreshIcons();
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Helper: render sorted sessions into a container with date groups (with loop session grouping)
|
|
692
|
+
function renderMobileSessionsInto(container) {
|
|
693
|
+
var newBtn = document.createElement("button");
|
|
694
|
+
newBtn.className = "mobile-session-new";
|
|
695
|
+
newBtn.innerHTML = '<i data-lucide="plus" style="width:16px;height:16px"></i> New session';
|
|
696
|
+
newBtn.addEventListener("click", function () {
|
|
697
|
+
if (_ctx.ws && _ctx.connected) {
|
|
698
|
+
_ctx.ws.send(JSON.stringify({ type: "new_session" }));
|
|
699
|
+
}
|
|
700
|
+
closeMobileSheet();
|
|
701
|
+
});
|
|
702
|
+
container.appendChild(newBtn);
|
|
703
|
+
|
|
704
|
+
var importBtn = document.createElement("button");
|
|
705
|
+
importBtn.className = "mobile-session-new";
|
|
706
|
+
importBtn.innerHTML = '<i data-lucide="import" style="width:16px;height:16px"></i> Import session';
|
|
707
|
+
importBtn.addEventListener("click", function () {
|
|
708
|
+
closeMobileSheet();
|
|
709
|
+
var targetBtn = document.getElementById("resume-session-btn");
|
|
710
|
+
if (targetBtn) setTimeout(function () { targetBtn.click(); }, 250);
|
|
711
|
+
});
|
|
712
|
+
container.appendChild(importBtn);
|
|
713
|
+
|
|
714
|
+
// Partition: loop sessions vs normal sessions (same logic as desktop renderSessionList)
|
|
715
|
+
var sessions = getCachedSessions();
|
|
716
|
+
var loopGroups = {};
|
|
717
|
+
var normalSessions = [];
|
|
718
|
+
for (var i = 0; i < sessions.length; i++) {
|
|
719
|
+
var s = sessions[i];
|
|
720
|
+
if (s.loop && s.loop.loopId && s.loop.role === "crafting" && s.loop.source !== "ralph" && s.loop.source !== "debate") {
|
|
721
|
+
continue;
|
|
722
|
+
} else if (s.loop && s.loop.loopId) {
|
|
723
|
+
var startedAt = s.loop.startedAt || 0;
|
|
724
|
+
var dateStr = startedAt ? new Date(startedAt).toISOString().slice(0, 10) : "unknown";
|
|
725
|
+
var groupKey = s.loop.loopId + ":" + dateStr;
|
|
726
|
+
if (!loopGroups[groupKey]) loopGroups[groupKey] = [];
|
|
727
|
+
loopGroups[groupKey].push(s);
|
|
728
|
+
} else {
|
|
729
|
+
normalSessions.push(s);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Build virtual items
|
|
734
|
+
var items = [];
|
|
735
|
+
for (var j = 0; j < normalSessions.length; j++) {
|
|
736
|
+
items.push({ type: "session", data: normalSessions[j], lastActivity: normalSessions[j].lastActivity || 0 });
|
|
737
|
+
}
|
|
738
|
+
var groupKeys = Object.keys(loopGroups);
|
|
739
|
+
for (var k = 0; k < groupKeys.length; k++) {
|
|
740
|
+
var gk = groupKeys[k];
|
|
741
|
+
var children = loopGroups[gk];
|
|
742
|
+
var realLoopId = children[0].loop.loopId;
|
|
743
|
+
var maxActivity = 0;
|
|
744
|
+
for (var m = 0; m < children.length; m++) {
|
|
745
|
+
var act = children[m].lastActivity || 0;
|
|
746
|
+
if (act > maxActivity) maxActivity = act;
|
|
747
|
+
}
|
|
748
|
+
items.push({ type: "loop", loopId: realLoopId, groupKey: gk, children: children, lastActivity: maxActivity });
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Sort by lastActivity descending
|
|
752
|
+
items.sort(function (a, b) {
|
|
753
|
+
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
var currentGroup = "";
|
|
757
|
+
for (var n = 0; n < items.length; n++) {
|
|
758
|
+
var item = items[n];
|
|
759
|
+
var group = getDateGroup(item.lastActivity || 0);
|
|
760
|
+
if (group !== currentGroup) {
|
|
761
|
+
currentGroup = group;
|
|
762
|
+
var header = document.createElement("div");
|
|
763
|
+
header.className = "mobile-sheet-group";
|
|
764
|
+
header.textContent = group;
|
|
765
|
+
container.appendChild(header);
|
|
766
|
+
}
|
|
767
|
+
if (item.type === "loop") {
|
|
768
|
+
container.appendChild(createMobileLoopGroup(item.loopId, item.children, item.groupKey));
|
|
769
|
+
} else {
|
|
770
|
+
container.appendChild(createMobileSessionItem(item.data));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Refresh mobile chat sheet when session data updates (called from renderSessionList)
|
|
776
|
+
export function refreshMobileChatSheet() {
|
|
777
|
+
if (!mobileChatSheetOpen) return;
|
|
778
|
+
var sheet = document.getElementById("mobile-sheet");
|
|
779
|
+
if (!sheet || sheet.classList.contains("hidden")) {
|
|
780
|
+
mobileChatSheetOpen = false;
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
var sessionListEl = sheet.querySelector(".mobile-chat-session-list");
|
|
784
|
+
if (!sessionListEl) return;
|
|
785
|
+
|
|
786
|
+
// Update chips: active state and processing dots
|
|
787
|
+
var chips = sheet.querySelectorAll(".mobile-chat-chip");
|
|
788
|
+
for (var i = 0; i < chips.length; i++) {
|
|
789
|
+
var chip = chips[i];
|
|
790
|
+
chip.classList.remove("active");
|
|
791
|
+
|
|
792
|
+
// Update active state
|
|
793
|
+
var isDmActive = !!getCurrentDmUserId();
|
|
794
|
+
if (chip.dataset.type === "project" && chip.dataset.slug === getCachedCurrentSlug() && !isDmActive) {
|
|
795
|
+
chip.classList.add("active");
|
|
796
|
+
} else if (chip.dataset.type === "mate" && chip.dataset.mateId === getCurrentDmUserId()) {
|
|
797
|
+
chip.classList.add("active");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// Update processing dot: same class as icon strip
|
|
801
|
+
var statusDot = chip.querySelector(".icon-strip-status");
|
|
802
|
+
if (statusDot) {
|
|
803
|
+
var isProcessing = false;
|
|
804
|
+
var allProjects = (_ctx && _ctx.projectList) || [];
|
|
805
|
+
var lookupSlug = chip.dataset.type === "mate" ? ("mate-" + chip.dataset.mateId) : chip.dataset.slug;
|
|
806
|
+
for (var pi = 0; pi < allProjects.length; pi++) {
|
|
807
|
+
if (allProjects[pi].slug === lookupSlug && allProjects[pi].isProcessing) {
|
|
808
|
+
isProcessing = true;
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
statusDot.classList.toggle("processing", isProcessing);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Re-render sessions for current context
|
|
817
|
+
sessionListEl.innerHTML = "";
|
|
818
|
+
if (getCurrentDmUserId()) {
|
|
819
|
+
renderMateMobileActions(sessionListEl);
|
|
820
|
+
} else {
|
|
821
|
+
renderMobileSessionsInto(sessionListEl);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
refreshIcons();
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function renderSheetMateProfile(listEl) {
|
|
828
|
+
if (!mobileSheetMateData) return;
|
|
829
|
+
var data = mobileSheetMateData;
|
|
830
|
+
|
|
831
|
+
// Profile header
|
|
832
|
+
var header = document.createElement("div");
|
|
833
|
+
header.className = "mate-profile-header";
|
|
834
|
+
|
|
835
|
+
var avatar = document.createElement("img");
|
|
836
|
+
avatar.className = "mate-profile-avatar";
|
|
837
|
+
avatar.src = data.avatarUrl || "";
|
|
838
|
+
avatar.alt = data.displayName || "";
|
|
839
|
+
header.appendChild(avatar);
|
|
840
|
+
|
|
841
|
+
var info = document.createElement("div");
|
|
842
|
+
info.className = "mate-profile-info";
|
|
843
|
+
var nameEl = document.createElement("div");
|
|
844
|
+
nameEl.className = "mate-profile-name";
|
|
845
|
+
nameEl.textContent = data.displayName || "";
|
|
846
|
+
info.appendChild(nameEl);
|
|
847
|
+
if (data.description) {
|
|
848
|
+
var descEl = document.createElement("div");
|
|
849
|
+
descEl.className = "mate-profile-desc";
|
|
850
|
+
descEl.textContent = data.description;
|
|
851
|
+
info.appendChild(descEl);
|
|
852
|
+
}
|
|
853
|
+
header.appendChild(info);
|
|
854
|
+
listEl.appendChild(header);
|
|
855
|
+
|
|
856
|
+
// Action buttons
|
|
857
|
+
var actions = [
|
|
858
|
+
{ icon: "book-open", label: "Knowledge", btnId: "mate-knowledge-btn", countId: "mate-knowledge-count" },
|
|
859
|
+
{ icon: "sticky-note", label: "Sticky Notes", btnId: "sticky-notes-toggle-btn", countId: "sticky-notes-sidebar-count" },
|
|
860
|
+
{ icon: "puzzle", label: "Skills", btnId: "mate-skills-btn" },
|
|
861
|
+
{ icon: "calendar", label: "Scheduled Tasks", btnId: "mate-scheduler-btn" }
|
|
862
|
+
];
|
|
863
|
+
|
|
864
|
+
for (var i = 0; i < actions.length; i++) {
|
|
865
|
+
(function (action) {
|
|
866
|
+
var btn = document.createElement("button");
|
|
867
|
+
btn.className = "mate-profile-action";
|
|
868
|
+
var countHtml = "";
|
|
869
|
+
if (action.countId) {
|
|
870
|
+
var countEl = document.getElementById(action.countId);
|
|
871
|
+
if (countEl && !countEl.classList.contains("hidden") && countEl.textContent) {
|
|
872
|
+
countHtml = '<span class="mate-profile-action-count">' + escapeHtml(countEl.textContent) + '</span>';
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
btn.innerHTML = '<i data-lucide="' + action.icon + '"></i><span>' + action.label + '</span>' + countHtml;
|
|
876
|
+
btn.addEventListener("click", function () {
|
|
877
|
+
closeMobileSheet();
|
|
878
|
+
var targetBtn = document.getElementById(action.btnId);
|
|
879
|
+
if (targetBtn) {
|
|
880
|
+
setTimeout(function () { targetBtn.click(); }, 250);
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
listEl.appendChild(btn);
|
|
884
|
+
})(actions[i]);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function renderSheetSearch(listEl) {
|
|
889
|
+
// Search input at top
|
|
890
|
+
var wrap = document.createElement("div");
|
|
891
|
+
wrap.className = "mobile-search-input-wrap";
|
|
892
|
+
var input = document.createElement("input");
|
|
893
|
+
input.className = "mobile-search-input";
|
|
894
|
+
input.type = "text";
|
|
895
|
+
input.placeholder = "Search sessions, messages...";
|
|
896
|
+
input.autocomplete = "off";
|
|
897
|
+
input.spellcheck = false;
|
|
898
|
+
wrap.appendChild(input);
|
|
899
|
+
listEl.appendChild(wrap);
|
|
900
|
+
|
|
901
|
+
// Results container
|
|
902
|
+
var resultsEl = document.createElement("div");
|
|
903
|
+
resultsEl.style.padding = "0 8px";
|
|
904
|
+
listEl.appendChild(resultsEl);
|
|
905
|
+
|
|
906
|
+
// Auto-focus
|
|
907
|
+
setTimeout(function () { input.focus(); }, 300);
|
|
908
|
+
|
|
909
|
+
// Show all sessions initially
|
|
910
|
+
renderSearchResults(resultsEl, "");
|
|
911
|
+
|
|
912
|
+
input.addEventListener("input", function () {
|
|
913
|
+
var q = input.value.trim().toLowerCase();
|
|
914
|
+
renderSearchResults(resultsEl, q);
|
|
915
|
+
});
|
|
916
|
+
input.addEventListener("keydown", function (e) { e.stopPropagation(); });
|
|
917
|
+
input.addEventListener("keyup", function (e) { e.stopPropagation(); });
|
|
918
|
+
input.addEventListener("keypress", function (e) { e.stopPropagation(); });
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function renderSearchResults(container, query) {
|
|
922
|
+
container.innerHTML = "";
|
|
923
|
+
var sorted = getCachedSessions().slice().sort(function (a, b) {
|
|
924
|
+
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
var found = 0;
|
|
928
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
929
|
+
var s = sorted[i];
|
|
930
|
+
var title = s.title || "New Session";
|
|
931
|
+
if (query && title.toLowerCase().indexOf(query) === -1) continue;
|
|
932
|
+
found++;
|
|
933
|
+
|
|
934
|
+
var el = document.createElement("button");
|
|
935
|
+
el.className = "mobile-session-item";
|
|
936
|
+
if (s.active) el.classList.add("active");
|
|
937
|
+
|
|
938
|
+
var titleSpan = document.createElement("span");
|
|
939
|
+
titleSpan.className = "mobile-session-title";
|
|
940
|
+
titleSpan.textContent = title;
|
|
941
|
+
el.appendChild(titleSpan);
|
|
942
|
+
|
|
943
|
+
if (s.isProcessing) {
|
|
944
|
+
var dot = document.createElement("span");
|
|
945
|
+
dot.className = "mobile-session-processing";
|
|
946
|
+
el.appendChild(dot);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
(function (id) {
|
|
950
|
+
el.addEventListener("click", function () {
|
|
951
|
+
if (_ctx.ws && _ctx.connected) {
|
|
952
|
+
_ctx.ws.send(JSON.stringify({ type: "switch_session", id: id }));
|
|
953
|
+
}
|
|
954
|
+
if (_ctx.dismissOverlayPanels) _ctx.dismissOverlayPanels();
|
|
955
|
+
closeMobileSheet();
|
|
956
|
+
});
|
|
957
|
+
})(s.id);
|
|
958
|
+
|
|
959
|
+
container.appendChild(el);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (found === 0 && query) {
|
|
963
|
+
var empty = document.createElement("div");
|
|
964
|
+
empty.className = "mobile-alert-empty";
|
|
965
|
+
empty.textContent = 'No results for "' + query + '"';
|
|
966
|
+
container.appendChild(empty);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function renderSheetTools(listEl) {
|
|
971
|
+
var isMateDm = document.body.classList.contains("mate-dm-active");
|
|
972
|
+
|
|
973
|
+
var items = isMateDm ? [
|
|
974
|
+
{ icon: "brain", label: "Memory", action: "mate-memory" },
|
|
975
|
+
{ icon: "book-open", label: "Knowledge", action: "mate-knowledge" },
|
|
976
|
+
{ icon: "sticky-note", label: "Sticky Notes", action: "mate-sticky" },
|
|
977
|
+
{ icon: "puzzle", label: "Skills", action: "mate-skills" },
|
|
978
|
+
{ icon: "calendar-clock", label: "Scheduled Tasks", action: "mate-scheduler" }
|
|
979
|
+
] : [
|
|
980
|
+
{ icon: "folder-tree", label: "Files", action: "files" },
|
|
981
|
+
{ icon: "square-terminal", label: "Terminal", action: "terminal" },
|
|
982
|
+
{ icon: "calendar-clock", label: "Scheduled Tasks", action: "scheduler" }
|
|
983
|
+
];
|
|
984
|
+
|
|
985
|
+
for (var i = 0; i < items.length; i++) {
|
|
986
|
+
(function (item) {
|
|
987
|
+
var btn = document.createElement("button");
|
|
988
|
+
btn.className = "mobile-more-item";
|
|
989
|
+
btn.innerHTML = '<i data-lucide="' + item.icon + '"></i><span class="mobile-more-item-label">' + item.label + '</span>';
|
|
990
|
+
btn.addEventListener("click", function () {
|
|
991
|
+
closeMobileSheet();
|
|
992
|
+
var targetId = null;
|
|
993
|
+
if (item.action === "files") {
|
|
994
|
+
setTimeout(function () { openMobileSheet("files"); }, 250);
|
|
995
|
+
} else if (item.action === "terminal") {
|
|
996
|
+
if (_ctx.openTerminal) _ctx.openTerminal();
|
|
997
|
+
} else if (item.action === "scheduler") {
|
|
998
|
+
targetId = "scheduler-btn";
|
|
999
|
+
} else if (item.action === "mate-knowledge") {
|
|
1000
|
+
setTimeout(function () { openMobileSheet("mate-knowledge"); }, 250);
|
|
1001
|
+
return;
|
|
1002
|
+
} else if (item.action === "mate-sticky") {
|
|
1003
|
+
targetId = "mate-sticky-notes-btn";
|
|
1004
|
+
} else if (item.action === "mate-skills") {
|
|
1005
|
+
targetId = "mate-skills-btn";
|
|
1006
|
+
} else if (item.action === "mate-memory") {
|
|
1007
|
+
targetId = "mate-memory-btn";
|
|
1008
|
+
} else if (item.action === "mate-scheduler") {
|
|
1009
|
+
targetId = "mate-scheduler-btn";
|
|
1010
|
+
} else if (item.action === "mate-debate") {
|
|
1011
|
+
targetId = "mate-debate-btn";
|
|
1012
|
+
}
|
|
1013
|
+
if (targetId) {
|
|
1014
|
+
var targetBtn = document.getElementById(targetId);
|
|
1015
|
+
if (targetBtn) setTimeout(function () { targetBtn.click(); }, 250);
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
listEl.appendChild(btn);
|
|
1019
|
+
})(items[i]);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
function renderSheetSettings(listEl) {
|
|
1024
|
+
var items = [
|
|
1025
|
+
{ icon: "folder-cog", label: "Project Settings", action: "project-settings" },
|
|
1026
|
+
{ icon: "settings", label: "Server Settings", action: "server-settings" }
|
|
1027
|
+
];
|
|
1028
|
+
|
|
1029
|
+
for (var i = 0; i < items.length; i++) {
|
|
1030
|
+
(function (item) {
|
|
1031
|
+
var btn = document.createElement("button");
|
|
1032
|
+
btn.className = "mobile-more-item";
|
|
1033
|
+
btn.innerHTML = '<i data-lucide="' + item.icon + '"></i><span class="mobile-more-item-label">' + item.label + '</span>';
|
|
1034
|
+
btn.addEventListener("click", function () {
|
|
1035
|
+
closeMobileSheet();
|
|
1036
|
+
if (item.action === "project-settings") {
|
|
1037
|
+
setTimeout(function () {
|
|
1038
|
+
// Find current project data
|
|
1039
|
+
var proj = null;
|
|
1040
|
+
for (var pi = 0; pi < getCachedProjectList().length; pi++) {
|
|
1041
|
+
if (getCachedProjectList()[pi].slug === getCachedCurrentSlug()) {
|
|
1042
|
+
proj = getCachedProjectList()[pi];
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
// For mate projects, use mate display name instead of slug
|
|
1047
|
+
if (proj && proj.isMate && getCachedMates().length > 0) {
|
|
1048
|
+
var mateId = getCachedCurrentSlug().replace("mate-", "");
|
|
1049
|
+
var _mates = getCachedMates();
|
|
1050
|
+
for (var mi = 0; mi < _mates.length; mi++) {
|
|
1051
|
+
var mp = _mates[mi].profile || {};
|
|
1052
|
+
if (_mates[mi].id === mateId) {
|
|
1053
|
+
proj = Object.assign({}, proj, { name: mp.displayName || _mates[mi].name || proj.name });
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
openProjectSettings(getCachedCurrentSlug(), proj);
|
|
1059
|
+
}, 250);
|
|
1060
|
+
} else if (item.action === "server-settings") {
|
|
1061
|
+
var settingsBtn = document.getElementById("server-settings-btn");
|
|
1062
|
+
if (settingsBtn) setTimeout(function () { settingsBtn.click(); }, 250);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
listEl.appendChild(btn);
|
|
1066
|
+
})(items[i]);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Dark/Light switch button
|
|
1070
|
+
var isDark = getCurrentTheme().variant === "dark";
|
|
1071
|
+
var themeBtn = document.createElement("button");
|
|
1072
|
+
themeBtn.className = "mobile-more-item";
|
|
1073
|
+
themeBtn.innerHTML = '<i data-lucide="' + (isDark ? "sun" : "moon") + '"></i><span class="mobile-more-item-label">Switch to ' + (isDark ? "Light" : "Dark") + '</span>';
|
|
1074
|
+
|
|
1075
|
+
themeBtn.addEventListener("click", function () {
|
|
1076
|
+
var themeToggle = document.getElementById("theme-toggle-check");
|
|
1077
|
+
if (themeToggle) themeToggle.click();
|
|
1078
|
+
// Update button text after a tick (theme applies async)
|
|
1079
|
+
setTimeout(function () {
|
|
1080
|
+
var nowDark = getCurrentTheme().variant === "dark";
|
|
1081
|
+
themeBtn.innerHTML = '<i data-lucide="' + (nowDark ? "sun" : "moon") + '"></i><span class="mobile-more-item-label">Switch to ' + (nowDark ? "Light" : "Dark") + '</span>';
|
|
1082
|
+
refreshIcons();
|
|
1083
|
+
}, 50);
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
listEl.appendChild(themeBtn);
|
|
1087
|
+
|
|
1088
|
+
// Chat Layout switch button
|
|
1089
|
+
var currentLayout = getChatLayout();
|
|
1090
|
+
var isBubble = currentLayout === "bubble";
|
|
1091
|
+
var layoutBtn = document.createElement("button");
|
|
1092
|
+
layoutBtn.className = "mobile-more-item";
|
|
1093
|
+
layoutBtn.innerHTML = '<i data-lucide="' + (isBubble ? "monitor" : "message-circle") + '"></i>'
|
|
1094
|
+
+ '<span class="mobile-more-item-label">Switch to ' + (isBubble ? "Channel" : "Bubble") + '</span>';
|
|
1095
|
+
|
|
1096
|
+
layoutBtn.addEventListener("click", function () {
|
|
1097
|
+
var next = getChatLayout() === "bubble" ? "channel" : "bubble";
|
|
1098
|
+
setChatLayout(next);
|
|
1099
|
+
fetch('/api/user/chat-layout', {
|
|
1100
|
+
method: 'PUT',
|
|
1101
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1102
|
+
body: JSON.stringify({ layout: next })
|
|
1103
|
+
});
|
|
1104
|
+
closeMobileSheet();
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
listEl.appendChild(layoutBtn);
|
|
1108
|
+
|
|
1109
|
+
// "Open as app" -- only show if not already in PWA standalone mode
|
|
1110
|
+
if (!document.documentElement.classList.contains("pwa-standalone")) {
|
|
1111
|
+
var pwaBtn = document.createElement("button");
|
|
1112
|
+
pwaBtn.className = "mobile-more-item";
|
|
1113
|
+
pwaBtn.innerHTML = '<i data-lucide="smartphone"></i><span class="mobile-more-item-label">Open as app</span>';
|
|
1114
|
+
pwaBtn.addEventListener("click", function () {
|
|
1115
|
+
closeMobileSheet();
|
|
1116
|
+
// Trigger the existing PWA install modal
|
|
1117
|
+
var installPill = document.getElementById("pwa-install-pill");
|
|
1118
|
+
if (installPill) {
|
|
1119
|
+
setTimeout(function () { installPill.click(); }, 250);
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
listEl.appendChild(pwaBtn);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export function initSidebarMobile(ctx) {
|
|
1127
|
+
_ctx = ctx;
|
|
1128
|
+
|
|
1129
|
+
// Put refreshMobileChatSheet on ctx for external callers
|
|
1130
|
+
ctx.refreshMobileChatSheet = refreshMobileChatSheet;
|
|
1131
|
+
|
|
1132
|
+
// --- Mobile sheet close handlers ---
|
|
1133
|
+
var mobileSheet = document.getElementById("mobile-sheet");
|
|
1134
|
+
if (mobileSheet) {
|
|
1135
|
+
var sheetBackdrop = mobileSheet.querySelector(".mobile-sheet-backdrop");
|
|
1136
|
+
var sheetCloseBtn = mobileSheet.querySelector(".mobile-sheet-close");
|
|
1137
|
+
if (sheetBackdrop) sheetBackdrop.addEventListener("click", closeMobileSheet);
|
|
1138
|
+
if (sheetCloseBtn) sheetCloseBtn.addEventListener("click", closeMobileSheet);
|
|
1139
|
+
|
|
1140
|
+
// --- Drag to dismiss sheet ---
|
|
1141
|
+
var sheetHandle = mobileSheet.querySelector(".mobile-sheet-handle");
|
|
1142
|
+
var sheetContent = mobileSheet.querySelector(".mobile-sheet-content");
|
|
1143
|
+
if (sheetHandle && sheetContent) {
|
|
1144
|
+
var dragStartY = 0;
|
|
1145
|
+
var dragging = false;
|
|
1146
|
+
|
|
1147
|
+
sheetHandle.addEventListener("touchstart", function (e) {
|
|
1148
|
+
dragStartY = e.touches[0].clientY;
|
|
1149
|
+
dragging = true;
|
|
1150
|
+
sheetContent.style.transition = "none";
|
|
1151
|
+
}, { passive: true });
|
|
1152
|
+
|
|
1153
|
+
mobileSheet.addEventListener("touchmove", function (e) {
|
|
1154
|
+
if (!dragging) return;
|
|
1155
|
+
var deltaY = e.touches[0].clientY - dragStartY;
|
|
1156
|
+
if (deltaY < 0) deltaY = 0;
|
|
1157
|
+
sheetContent.style.transform = "translateY(" + deltaY + "px)";
|
|
1158
|
+
if (sheetBackdrop) {
|
|
1159
|
+
var opacity = Math.max(0, 1 - deltaY / (sheetContent.offsetHeight * 0.5));
|
|
1160
|
+
sheetBackdrop.style.opacity = opacity;
|
|
1161
|
+
}
|
|
1162
|
+
}, { passive: true });
|
|
1163
|
+
|
|
1164
|
+
mobileSheet.addEventListener("touchend", function () {
|
|
1165
|
+
if (!dragging) return;
|
|
1166
|
+
dragging = false;
|
|
1167
|
+
var currentY = parseFloat(sheetContent.style.transform.replace(/[^0-9.-]/g, "")) || 0;
|
|
1168
|
+
var threshold = sheetContent.offsetHeight * 0.3;
|
|
1169
|
+
|
|
1170
|
+
if (currentY > threshold) {
|
|
1171
|
+
sheetContent.style.transition = "transform 0.22s ease-in";
|
|
1172
|
+
sheetContent.style.transform = "translateY(100%)";
|
|
1173
|
+
if (sheetBackdrop) {
|
|
1174
|
+
sheetBackdrop.style.transition = "opacity 0.22s ease-in";
|
|
1175
|
+
sheetBackdrop.style.opacity = "0";
|
|
1176
|
+
}
|
|
1177
|
+
setTimeout(function () {
|
|
1178
|
+
sheetContent.style.transition = "";
|
|
1179
|
+
sheetContent.style.transform = "";
|
|
1180
|
+
if (sheetBackdrop) {
|
|
1181
|
+
sheetBackdrop.style.transition = "";
|
|
1182
|
+
sheetBackdrop.style.opacity = "";
|
|
1183
|
+
}
|
|
1184
|
+
// Close without animation since we already animated
|
|
1185
|
+
var sheet = document.getElementById("mobile-sheet");
|
|
1186
|
+
if (sheet) {
|
|
1187
|
+
if (sheet.classList.contains("sheet-files")) {
|
|
1188
|
+
var fileTree = document.getElementById("file-tree");
|
|
1189
|
+
var sidebarFilesPanel = document.getElementById("sidebar-panel-files");
|
|
1190
|
+
if (fileTree && sidebarFilesPanel) {
|
|
1191
|
+
sidebarFilesPanel.appendChild(fileTree);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
sheet.classList.add("hidden");
|
|
1195
|
+
sheet.classList.remove("closing", "sheet-files");
|
|
1196
|
+
}
|
|
1197
|
+
}, 230);
|
|
1198
|
+
} else {
|
|
1199
|
+
sheetContent.style.transition = "transform 0.2s ease-out";
|
|
1200
|
+
sheetContent.style.transform = "translateY(0)";
|
|
1201
|
+
if (sheetBackdrop) {
|
|
1202
|
+
sheetBackdrop.style.transition = "opacity 0.2s ease-out";
|
|
1203
|
+
sheetBackdrop.style.opacity = "";
|
|
1204
|
+
}
|
|
1205
|
+
setTimeout(function () {
|
|
1206
|
+
sheetContent.style.transition = "";
|
|
1207
|
+
sheetContent.style.transform = "";
|
|
1208
|
+
if (sheetBackdrop) {
|
|
1209
|
+
sheetBackdrop.style.transition = "";
|
|
1210
|
+
sheetBackdrop.style.opacity = "";
|
|
1211
|
+
}
|
|
1212
|
+
}, 200);
|
|
1213
|
+
}
|
|
1214
|
+
}, { passive: true });
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// --- Mobile tab bar ---
|
|
1219
|
+
var mobileTabBar = document.getElementById("mobile-tab-bar");
|
|
1220
|
+
var mobileTabs = mobileTabBar ? mobileTabBar.querySelectorAll(".mobile-tab") : [];
|
|
1221
|
+
var mobileHomeBtn = document.getElementById("mobile-home-btn");
|
|
1222
|
+
|
|
1223
|
+
function setMobileTabActive(tabName) {
|
|
1224
|
+
for (var i = 0; i < mobileTabs.length; i++) {
|
|
1225
|
+
if (mobileTabs[i].dataset.tab === tabName) {
|
|
1226
|
+
mobileTabs[i].classList.add("active");
|
|
1227
|
+
} else {
|
|
1228
|
+
mobileTabs[i].classList.remove("active");
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
if (mobileHomeBtn) {
|
|
1232
|
+
if (tabName === "home") {
|
|
1233
|
+
mobileHomeBtn.classList.add("active");
|
|
1234
|
+
} else {
|
|
1235
|
+
mobileHomeBtn.classList.remove("active");
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
for (var t = 0; t < mobileTabs.length; t++) {
|
|
1241
|
+
(function (tab) {
|
|
1242
|
+
tab.addEventListener("click", function () {
|
|
1243
|
+
var name = tab.dataset.tab;
|
|
1244
|
+
|
|
1245
|
+
if (name === "chat") {
|
|
1246
|
+
openMobileSheet("sessions");
|
|
1247
|
+
setMobileTabActive("chat");
|
|
1248
|
+
} else if (name === "search") {
|
|
1249
|
+
openCommandPalette();
|
|
1250
|
+
setMobileTabActive("search");
|
|
1251
|
+
} else if (name === "tools") {
|
|
1252
|
+
openMobileSheet("tools");
|
|
1253
|
+
setMobileTabActive("tools");
|
|
1254
|
+
} else if (name === "settings") {
|
|
1255
|
+
openMobileSheet("settings");
|
|
1256
|
+
setMobileTabActive("settings");
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
})(mobileTabs[t]);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
if (mobileHomeBtn) {
|
|
1263
|
+
mobileHomeBtn.addEventListener("click", function () {
|
|
1264
|
+
if (_ctx.closeSidebar) _ctx.closeSidebar();
|
|
1265
|
+
setMobileTabActive("home");
|
|
1266
|
+
if (_ctx.showHomeHub) _ctx.showHomeHub();
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
}
|