clay-server 2.31.0 → 2.32.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser-mcp-server.js +32 -44
- package/lib/codex-defaults.js +18 -0
- package/lib/debate-mcp-server.js +14 -31
- package/lib/mcp-local.js +31 -1
- package/lib/project-connection.js +9 -6
- package/lib/project-debate.js +8 -0
- package/lib/project-filesystem.js +47 -1
- package/lib/project-http.js +75 -8
- package/lib/project-mate-interaction.js +102 -16
- package/lib/project-mcp.js +4 -0
- package/lib/project-notifications.js +9 -0
- package/lib/project-sessions.js +94 -51
- package/lib/project-user-message.js +12 -7
- package/lib/project.js +234 -99
- package/lib/public/app.js +135 -454
- package/lib/public/codex-avatar.png +0 -0
- package/lib/public/css/debate.css +3 -2
- package/lib/public/css/filebrowser.css +91 -1
- package/lib/public/css/icon-strip.css +21 -5
- package/lib/public/css/input.css +338 -104
- package/lib/public/css/mates.css +43 -0
- package/lib/public/css/mention.css +48 -4
- package/lib/public/css/menus.css +1 -1
- package/lib/public/css/messages.css +2 -0
- package/lib/public/css/notifications-center.css +26 -0
- package/lib/public/css/tooltip.css +47 -0
- package/lib/public/index.html +78 -26
- package/lib/public/modules/app-connection.js +138 -37
- package/lib/public/modules/app-cursors.js +18 -17
- package/lib/public/modules/app-debate-ui.js +9 -9
- package/lib/public/modules/app-dm.js +175 -131
- package/lib/public/modules/app-favicon.js +28 -26
- package/lib/public/modules/app-header.js +79 -68
- package/lib/public/modules/app-home-hub.js +55 -47
- package/lib/public/modules/app-loop-ui.js +34 -18
- package/lib/public/modules/app-loop-wizard.js +6 -6
- package/lib/public/modules/app-messages.js +199 -153
- package/lib/public/modules/app-misc.js +23 -12
- package/lib/public/modules/app-notifications.js +119 -9
- package/lib/public/modules/app-panels.js +203 -49
- package/lib/public/modules/app-projects.js +161 -150
- package/lib/public/modules/app-rate-limit.js +5 -4
- package/lib/public/modules/app-rendering.js +149 -101
- package/lib/public/modules/app-skills-install.js +4 -4
- package/lib/public/modules/context-sources.js +102 -66
- package/lib/public/modules/dom-refs.js +21 -0
- package/lib/public/modules/filebrowser.js +173 -2
- package/lib/public/modules/input.js +122 -0
- package/lib/public/modules/markdown.js +5 -1
- package/lib/public/modules/mate-sidebar.js +38 -0
- package/lib/public/modules/mention.js +24 -6
- package/lib/public/modules/scheduler.js +1 -1
- package/lib/public/modules/sidebar-mates.js +79 -35
- package/lib/public/modules/sidebar-mobile.js +34 -30
- package/lib/public/modules/sidebar-projects.js +60 -57
- package/lib/public/modules/sidebar-sessions.js +75 -69
- package/lib/public/modules/sidebar.js +12 -20
- package/lib/public/modules/skills.js +8 -9
- package/lib/public/modules/sticky-notes.js +1 -2
- package/lib/public/modules/store.js +9 -2
- package/lib/public/modules/stt.js +4 -1
- package/lib/public/modules/terminal.js +12 -0
- package/lib/public/modules/tools.js +18 -13
- package/lib/public/modules/tooltip.js +32 -5
- package/lib/sdk-bridge.js +562 -1114
- package/lib/sdk-message-processor.js +150 -135
- package/lib/sdk-worker.js +4 -0
- package/lib/server-dm.js +1 -0
- package/lib/server.js +86 -1
- package/lib/sessions.js +81 -37
- package/lib/ws-schema.js +2 -0
- package/lib/yoke/adapters/claude-worker.js +559 -0
- package/lib/yoke/adapters/claude.js +1483 -0
- package/lib/yoke/adapters/codex.js +1121 -0
- package/lib/yoke/adapters/gemini.js +709 -0
- package/lib/yoke/codex-app-server.js +307 -0
- package/lib/yoke/index.js +199 -0
- package/lib/yoke/instructions.js +62 -0
- package/lib/yoke/interface.js +98 -0
- package/lib/yoke/mcp-bridge-server.js +294 -0
- package/lib/yoke/package.json +7 -0
- package/package.json +3 -1
|
@@ -17,6 +17,8 @@ export function initContextSources(_ctx) {
|
|
|
17
17
|
|
|
18
18
|
var addBtn = document.getElementById("context-sources-add");
|
|
19
19
|
var picker = document.getElementById("context-sources-picker");
|
|
20
|
+
// Suppress tooltip when the picker is open
|
|
21
|
+
if (addBtn) addBtn.setAttribute("data-tip-suppress-when-open", "#context-sources-picker");
|
|
20
22
|
|
|
21
23
|
addBtn.addEventListener("click", function(e) {
|
|
22
24
|
e.stopPropagation();
|
|
@@ -36,8 +38,22 @@ export function initContextSources(_ctx) {
|
|
|
36
38
|
|
|
37
39
|
function closePicker() {
|
|
38
40
|
var picker = document.getElementById("context-sources-picker");
|
|
39
|
-
picker.classList.add("hidden");
|
|
41
|
+
if (picker) picker.classList.add("hidden");
|
|
40
42
|
document.removeEventListener("click", closePicker, true);
|
|
43
|
+
// Also close mobile bottom sheet if open
|
|
44
|
+
var moreSheet = document.getElementById("input-more-sheet");
|
|
45
|
+
if (moreSheet && moreSheet.classList.contains("open")) {
|
|
46
|
+
moreSheet.classList.remove("open");
|
|
47
|
+
setTimeout(function () { moreSheet.classList.add("hidden"); }, 250);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Re-render all open picker surfaces (desktop popover and mobile bottom sheet)
|
|
52
|
+
function renderAllOpen() {
|
|
53
|
+
var picker = document.getElementById("context-sources-picker");
|
|
54
|
+
if (picker && !picker.classList.contains("hidden")) renderPicker();
|
|
55
|
+
var moreSheet = document.getElementById("input-more-sheet");
|
|
56
|
+
if (moreSheet && moreSheet.classList.contains("open")) renderPicker("-mobile");
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
// Restore state from server
|
|
@@ -80,11 +96,7 @@ export function updateTerminalList(terminals) {
|
|
|
80
96
|
if (changed) saveToServer();
|
|
81
97
|
renderChips();
|
|
82
98
|
|
|
83
|
-
|
|
84
|
-
var picker = document.getElementById("context-sources-picker");
|
|
85
|
-
if (!picker.classList.contains("hidden")) {
|
|
86
|
-
renderPicker();
|
|
87
|
-
}
|
|
99
|
+
renderAllOpen();
|
|
88
100
|
}
|
|
89
101
|
|
|
90
102
|
// Called when Chrome extension sends tab list via postMessage
|
|
@@ -110,11 +122,7 @@ export function updateBrowserTabList(tabs) {
|
|
|
110
122
|
if (changed) saveToServer();
|
|
111
123
|
renderChips();
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
var picker = document.getElementById("context-sources-picker");
|
|
115
|
-
if (!picker.classList.contains("hidden")) {
|
|
116
|
-
renderPicker();
|
|
117
|
-
}
|
|
125
|
+
renderAllOpen();
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
// Called when email_accounts_list arrives from server
|
|
@@ -141,10 +149,7 @@ export function updateEmailAccountList(msg) {
|
|
|
141
149
|
if (changed) saveToServer();
|
|
142
150
|
renderChips();
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
if (!picker.classList.contains("hidden")) {
|
|
146
|
-
renderPicker();
|
|
147
|
-
}
|
|
152
|
+
renderAllOpen();
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
// Called when email_unread_update arrives from server
|
|
@@ -152,10 +157,7 @@ export function updateEmailUnreadCounts(msg) {
|
|
|
152
157
|
emailUnreadCounts = msg.unread || {};
|
|
153
158
|
renderChips();
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
if (!picker.classList.contains("hidden")) {
|
|
157
|
-
renderPicker();
|
|
158
|
-
}
|
|
160
|
+
renderAllOpen();
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
function toggleSource(sourceId) {
|
|
@@ -166,7 +168,7 @@ function toggleSource(sourceId) {
|
|
|
166
168
|
}
|
|
167
169
|
saveToServer();
|
|
168
170
|
renderChips();
|
|
169
|
-
|
|
171
|
+
renderAllOpen();
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
function removeSource(sourceId) {
|
|
@@ -174,63 +176,95 @@ function removeSource(sourceId) {
|
|
|
174
176
|
saveToServer();
|
|
175
177
|
renderChips();
|
|
176
178
|
|
|
177
|
-
|
|
178
|
-
if (!picker.classList.contains("hidden")) {
|
|
179
|
-
renderPicker();
|
|
180
|
-
}
|
|
179
|
+
renderAllOpen();
|
|
181
180
|
}
|
|
182
181
|
|
|
183
|
-
function
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
function buildActiveSourceRow(iconHtml, text) {
|
|
183
|
+
return '<div class="ctx-tip-row">' + iconHtml + '<span>' + escapeHtml(text) + '</span></div>';
|
|
184
|
+
}
|
|
186
185
|
|
|
186
|
+
function getActiveSourceRowsHTML() {
|
|
187
|
+
var rows = [];
|
|
187
188
|
for (var id of activeSourceIds) {
|
|
188
|
-
var
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
189
|
+
var parts = id.split(":");
|
|
190
|
+
var type = parts[0];
|
|
191
|
+
var key = parts.slice(1).join(":");
|
|
192
|
+
if (type === "term") {
|
|
193
|
+
for (var i = 0; i < terminalList.length; i++) {
|
|
194
|
+
if (String(terminalList[i].id) === key) {
|
|
195
|
+
rows.push(buildActiveSourceRow(
|
|
196
|
+
'<i data-lucide="square-terminal"></i>',
|
|
197
|
+
terminalList[i].title || ("Terminal " + key)
|
|
198
|
+
));
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else if (type === "tab") {
|
|
203
|
+
var tabId = parseInt(key, 10);
|
|
204
|
+
for (var j = 0; j < browserTabList.length; j++) {
|
|
205
|
+
if (browserTabList[j].id === tabId) {
|
|
206
|
+
var t = browserTabList[j];
|
|
207
|
+
var title = t.title || t.url || "Tab";
|
|
208
|
+
if (title.length > 50) title = title.slice(0, 47) + "...";
|
|
209
|
+
var faviconHtml = t.favIconUrl
|
|
210
|
+
? '<img src="' + escapeHtml(t.favIconUrl) + '" class="ctx-tip-favicon" onerror="this.style.display=\'none\'">'
|
|
211
|
+
: '<i data-lucide="globe"></i>';
|
|
212
|
+
rows.push(buildActiveSourceRow(faviconHtml, title));
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} else if (type === "email") {
|
|
217
|
+
for (var k = 0; k < emailAccountList.length; k++) {
|
|
218
|
+
if (emailAccountList[k].id === key) {
|
|
219
|
+
rows.push(buildActiveSourceRow(
|
|
220
|
+
'<i data-lucide="mail"></i>',
|
|
221
|
+
emailAccountList[k].email
|
|
222
|
+
));
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
215
227
|
}
|
|
228
|
+
return rows;
|
|
229
|
+
}
|
|
216
230
|
|
|
217
|
-
|
|
231
|
+
function renderChips() {
|
|
232
|
+
// Update add button — show badge count when sources are active
|
|
218
233
|
var addBtn = document.getElementById("context-sources-add");
|
|
219
|
-
var labelSpan = addBtn.querySelector("
|
|
234
|
+
var labelSpan = addBtn.querySelector(".ctx-label");
|
|
235
|
+
var existingBadge = addBtn.querySelector(".ctx-badge");
|
|
220
236
|
if (activeSourceIds.size > 0) {
|
|
221
|
-
labelSpan.
|
|
222
|
-
|
|
237
|
+
if (labelSpan) labelSpan.style.display = "none";
|
|
238
|
+
if (!existingBadge) {
|
|
239
|
+
existingBadge = document.createElement("span");
|
|
240
|
+
existingBadge.className = "ctx-badge";
|
|
241
|
+
addBtn.appendChild(existingBadge);
|
|
242
|
+
}
|
|
243
|
+
existingBadge.textContent = activeSourceIds.size;
|
|
244
|
+
var rows = getActiveSourceRowsHTML();
|
|
245
|
+
if (rows.length > 0) {
|
|
246
|
+
var html = '<div class="ctx-tip-header">Active context sources</div>' + rows.join("");
|
|
247
|
+
addBtn.setAttribute("data-tip-html", html);
|
|
248
|
+
addBtn.removeAttribute("data-tip");
|
|
249
|
+
} else {
|
|
250
|
+
addBtn.setAttribute("data-tip", "Add context sources");
|
|
251
|
+
addBtn.removeAttribute("data-tip-html");
|
|
252
|
+
}
|
|
253
|
+
addBtn.removeAttribute("title");
|
|
223
254
|
} else {
|
|
224
|
-
labelSpan.
|
|
225
|
-
|
|
255
|
+
if (labelSpan) { labelSpan.style.display = ""; }
|
|
256
|
+
if (existingBadge) existingBadge.remove();
|
|
257
|
+
addBtn.setAttribute("data-tip", "Add context sources");
|
|
258
|
+
addBtn.removeAttribute("data-tip-html");
|
|
259
|
+
addBtn.removeAttribute("title");
|
|
226
260
|
}
|
|
227
|
-
|
|
228
|
-
if (typeof lucide !== "undefined") lucide.createIcons();
|
|
229
261
|
}
|
|
230
262
|
|
|
231
|
-
function renderPicker() {
|
|
263
|
+
export function renderPicker(suffix) {
|
|
264
|
+
suffix = suffix || "";
|
|
232
265
|
// --- Terminals section ---
|
|
233
|
-
var termSection = document.getElementById("context-picker-terminals");
|
|
266
|
+
var termSection = document.getElementById("context-picker-terminals" + suffix);
|
|
267
|
+
if (!termSection) return;
|
|
234
268
|
termSection.innerHTML = "";
|
|
235
269
|
|
|
236
270
|
var termLabel = document.createElement("div");
|
|
@@ -268,7 +302,8 @@ function renderPicker() {
|
|
|
268
302
|
}
|
|
269
303
|
|
|
270
304
|
// --- Browser Tabs section ---
|
|
271
|
-
var tabSection = document.getElementById("context-picker-tabs");
|
|
305
|
+
var tabSection = document.getElementById("context-picker-tabs" + suffix);
|
|
306
|
+
if (!tabSection) return;
|
|
272
307
|
tabSection.innerHTML = "";
|
|
273
308
|
|
|
274
309
|
var tabLabel = document.createElement("div");
|
|
@@ -328,7 +363,8 @@ function renderPicker() {
|
|
|
328
363
|
}
|
|
329
364
|
|
|
330
365
|
// --- Email Accounts section ---
|
|
331
|
-
var emailSection = document.getElementById("context-picker-email");
|
|
366
|
+
var emailSection = document.getElementById("context-picker-email" + suffix);
|
|
367
|
+
if (!emailSection) return;
|
|
332
368
|
emailSection.innerHTML = "";
|
|
333
369
|
|
|
334
370
|
var emailLabel = document.createElement("div");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// dom-refs.js - Shared DOM element references
|
|
2
|
+
// Lazy-cached getElementById lookups for elements used across multiple modules.
|
|
3
|
+
// Same pattern as ws-ref.js: infrastructure singleton, not state.
|
|
4
|
+
|
|
5
|
+
var _cache = {};
|
|
6
|
+
|
|
7
|
+
function ref(id) {
|
|
8
|
+
if (!_cache[id]) _cache[id] = document.getElementById(id);
|
|
9
|
+
return _cache[id];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getMessagesEl() { return ref("messages"); }
|
|
13
|
+
export function getInputEl() { return ref("input"); }
|
|
14
|
+
export function getSendBtn() { return ref("send-btn"); }
|
|
15
|
+
export function getSessionListEl() { return ref("session-list"); }
|
|
16
|
+
|
|
17
|
+
export function getStatusDot() {
|
|
18
|
+
return document.querySelector("#icon-strip-projects .icon-strip-item.active .icon-strip-status") ||
|
|
19
|
+
document.querySelector("#icon-strip-projects .icon-strip-wt-item.active .icon-strip-status") ||
|
|
20
|
+
document.querySelector("#icon-strip-users .icon-strip-mate.active .icon-strip-status");
|
|
21
|
+
}
|
|
@@ -179,6 +179,35 @@ export function initFileBrowser(_ctx) {
|
|
|
179
179
|
closeFileViewer();
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
|
+
|
|
183
|
+
// --- File search ---
|
|
184
|
+
var fbSearchInput = document.getElementById("fb-search-input");
|
|
185
|
+
var searchDebounce = null;
|
|
186
|
+
|
|
187
|
+
if (fbSearchInput) {
|
|
188
|
+
fbSearchInput.addEventListener("input", function () {
|
|
189
|
+
var q = fbSearchInput.value.trim();
|
|
190
|
+
if (searchDebounce) clearTimeout(searchDebounce);
|
|
191
|
+
if (!q) {
|
|
192
|
+
renderTree();
|
|
193
|
+
restoreExpanded({});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
searchDebounce = setTimeout(function () {
|
|
197
|
+
if (ctx.ws && ctx.connected) {
|
|
198
|
+
ctx.ws.send(JSON.stringify({ type: "fs_search", query: q }));
|
|
199
|
+
}
|
|
200
|
+
}, 200);
|
|
201
|
+
});
|
|
202
|
+
fbSearchInput.addEventListener("keydown", function (e) {
|
|
203
|
+
if (e.key === "Escape" && fbSearchInput.value) {
|
|
204
|
+
e.stopPropagation();
|
|
205
|
+
fbSearchInput.value = "";
|
|
206
|
+
renderTree();
|
|
207
|
+
restoreExpanded({});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
182
211
|
}
|
|
183
212
|
|
|
184
213
|
// --- File watch helpers ---
|
|
@@ -232,10 +261,8 @@ export function resetFileBrowser() {
|
|
|
232
261
|
// Hide the file browser panel, show sessions panel
|
|
233
262
|
var filesPanel = document.getElementById("sidebar-panel-files");
|
|
234
263
|
var sessionsPanel = document.getElementById("sidebar-panel-sessions");
|
|
235
|
-
var filesHeaderContent = document.getElementById("files-header-content");
|
|
236
264
|
var sessionsHeaderContent = document.getElementById("sessions-header-content");
|
|
237
265
|
if (filesPanel) filesPanel.classList.add("hidden");
|
|
238
|
-
if (filesHeaderContent) filesHeaderContent.classList.add("hidden");
|
|
239
266
|
if (sessionsPanel) sessionsPanel.classList.remove("hidden");
|
|
240
267
|
if (sessionsHeaderContent) sessionsHeaderContent.classList.remove("hidden");
|
|
241
268
|
}
|
|
@@ -341,6 +368,150 @@ export function refreshIfOpen(filePath) {
|
|
|
341
368
|
|
|
342
369
|
// --- WS handlers ---
|
|
343
370
|
|
|
371
|
+
export function handleFsSearch(msg) {
|
|
372
|
+
var entries = msg.entries || [];
|
|
373
|
+
var query = (msg.query || "").trim().toLowerCase();
|
|
374
|
+
if (!query) return;
|
|
375
|
+
|
|
376
|
+
ctx.fileTreeEl.innerHTML = "";
|
|
377
|
+
|
|
378
|
+
if (entries.length === 0) {
|
|
379
|
+
ctx.fileTreeEl.innerHTML = '<div class="fb-search-empty">No files found</div>';
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Build a tree structure from flat search results
|
|
384
|
+
var tree = {};
|
|
385
|
+
for (var i = 0; i < entries.length; i++) {
|
|
386
|
+
var entry = entries[i];
|
|
387
|
+
var parts = entry.path.split("/");
|
|
388
|
+
var node = tree;
|
|
389
|
+
for (var j = 0; j < parts.length; j++) {
|
|
390
|
+
if (!node[parts[j]]) node[parts[j]] = {};
|
|
391
|
+
if (j === parts.length - 1) {
|
|
392
|
+
node[parts[j]]._entry = entry;
|
|
393
|
+
} else {
|
|
394
|
+
node = node[parts[j]];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
renderFilteredTree(ctx.fileTreeEl, tree, 0, query);
|
|
400
|
+
refreshIcons();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function renderFilteredTree(container, tree, depth, query) {
|
|
404
|
+
var keys = Object.keys(tree);
|
|
405
|
+
var dirs = [];
|
|
406
|
+
var files = [];
|
|
407
|
+
for (var i = 0; i < keys.length; i++) {
|
|
408
|
+
if (keys[i] === "_entry") continue;
|
|
409
|
+
var node = tree[keys[i]];
|
|
410
|
+
var entry = node._entry;
|
|
411
|
+
if (entry && entry.type === "file") {
|
|
412
|
+
files.push(keys[i]);
|
|
413
|
+
} else {
|
|
414
|
+
dirs.push(keys[i]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
dirs.sort(function (a, b) {
|
|
418
|
+
var aH = a.charAt(0) === ".";
|
|
419
|
+
var bH = b.charAt(0) === ".";
|
|
420
|
+
if (aH !== bH) return aH ? 1 : -1;
|
|
421
|
+
return a.localeCompare(b);
|
|
422
|
+
});
|
|
423
|
+
files.sort(function (a, b) {
|
|
424
|
+
var aH = a.charAt(0) === ".";
|
|
425
|
+
var bH = b.charAt(0) === ".";
|
|
426
|
+
if (aH !== bH) return aH ? 1 : -1;
|
|
427
|
+
return a.localeCompare(b);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
var allKeys = dirs.concat(files);
|
|
431
|
+
for (var k = 0; k < allKeys.length; k++) {
|
|
432
|
+
var name = allKeys[k];
|
|
433
|
+
var node = tree[name];
|
|
434
|
+
var entry = node._entry;
|
|
435
|
+
var isDir = !entry || entry.type === "dir";
|
|
436
|
+
|
|
437
|
+
var row = document.createElement("div");
|
|
438
|
+
row.className = "file-tree-item" + (isDir ? " expanded" : "");
|
|
439
|
+
row.style.paddingLeft = (8 + depth * 16) + "px";
|
|
440
|
+
if (entry) {
|
|
441
|
+
row.draggable = true;
|
|
442
|
+
row.dataset.path = entry.path;
|
|
443
|
+
row.addEventListener("dragstart", function (e) {
|
|
444
|
+
var cwd = ctx.cwd || "";
|
|
445
|
+
var rel = this.dataset.path;
|
|
446
|
+
var abs = cwd ? cwd.replace(/\/$/, "") + "/" + rel : rel;
|
|
447
|
+
e.dataTransfer.setData("text/plain", abs);
|
|
448
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
var nameHtml = highlightMatch(name, query);
|
|
453
|
+
|
|
454
|
+
if (isDir) {
|
|
455
|
+
row.innerHTML =
|
|
456
|
+
'<span class="file-tree-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
457
|
+
'<span class="file-tree-icon file-tree-folder-icon"></span>' +
|
|
458
|
+
'<span class="file-tree-name">' + nameHtml + '</span>';
|
|
459
|
+
|
|
460
|
+
(function (iconEl, n) {
|
|
461
|
+
getFolderIconSvg(n, true, function (svg) { iconEl.innerHTML = svg; });
|
|
462
|
+
})(row.querySelector(".file-tree-folder-icon"), name);
|
|
463
|
+
|
|
464
|
+
var childContainer = document.createElement("div");
|
|
465
|
+
childContainer.className = "file-tree-children";
|
|
466
|
+
|
|
467
|
+
// Toggle expand/collapse on click
|
|
468
|
+
(function (rowEl, childEl, folderName) {
|
|
469
|
+
rowEl.addEventListener("click", function (e) {
|
|
470
|
+
e.stopPropagation();
|
|
471
|
+
var isExpanded = rowEl.classList.contains("expanded");
|
|
472
|
+
rowEl.classList.toggle("expanded");
|
|
473
|
+
childEl.classList.toggle("hidden", isExpanded);
|
|
474
|
+
var folderIconEl = rowEl.querySelector(".file-tree-folder-icon");
|
|
475
|
+
if (folderIconEl) {
|
|
476
|
+
getFolderIconSvg(folderName, !isExpanded, function (svg) { folderIconEl.innerHTML = svg; });
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
})(row, childContainer, name);
|
|
480
|
+
|
|
481
|
+
container.appendChild(row);
|
|
482
|
+
container.appendChild(childContainer);
|
|
483
|
+
renderFilteredTree(childContainer, node, depth + 1, query);
|
|
484
|
+
} else {
|
|
485
|
+
row.innerHTML =
|
|
486
|
+
'<span class="file-tree-spacer"></span>' +
|
|
487
|
+
'<span class="file-tree-icon">' + getFileIconSvg(name) + '</span>' +
|
|
488
|
+
'<span class="file-tree-name">' + nameHtml + '</span>';
|
|
489
|
+
|
|
490
|
+
(function (filePath, rowEl) {
|
|
491
|
+
rowEl.addEventListener("click", function (e) {
|
|
492
|
+
e.stopPropagation();
|
|
493
|
+
var prev = ctx.fileTreeEl.querySelector(".file-tree-item.active");
|
|
494
|
+
if (prev) prev.classList.remove("active");
|
|
495
|
+
rowEl.classList.add("active");
|
|
496
|
+
requestFileContent(filePath);
|
|
497
|
+
if (window.innerWidth <= 768) closeSidebar();
|
|
498
|
+
});
|
|
499
|
+
})(entry.path, row);
|
|
500
|
+
|
|
501
|
+
container.appendChild(row);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function highlightMatch(text, query) {
|
|
507
|
+
var lower = text.toLowerCase();
|
|
508
|
+
var idx = lower.indexOf(query);
|
|
509
|
+
if (idx === -1) return escapeHtml(text);
|
|
510
|
+
return escapeHtml(text.substring(0, idx)) +
|
|
511
|
+
'<mark>' + escapeHtml(text.substring(idx, idx + query.length)) + '</mark>' +
|
|
512
|
+
escapeHtml(text.substring(idx + query.length));
|
|
513
|
+
}
|
|
514
|
+
|
|
344
515
|
export function handleFsList(msg) {
|
|
345
516
|
var dirPath = msg.path || ".";
|
|
346
517
|
treeData[dirPath] = { loaded: true, children: msg.entries || [] };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
2
2
|
import { setRewindMode, isRewindMode } from './rewind.js';
|
|
3
|
+
import { renderPicker as renderContextPicker } from './context-sources.js';
|
|
3
4
|
import { checkForMention, showMentionMenu, hideMentionMenu, isMentionMenuVisible, mentionMenuKeydown, setMentionAtIdx, parseMentionFromInput, clearMentionState, stickyReapplyMention, sendMention, renderMentionUser, removeMentionChip } from './mention.js';
|
|
5
|
+
import { store } from './store.js';
|
|
6
|
+
import { mateAvatarUrl } from './avatar.js';
|
|
4
7
|
|
|
5
8
|
var ctx;
|
|
6
9
|
|
|
@@ -217,8 +220,15 @@ export function sendMessage() {
|
|
|
217
220
|
if (pastes.length > 0) {
|
|
218
221
|
payload.pastes = pastes;
|
|
219
222
|
}
|
|
223
|
+
// Include selected vendor for session binding (server uses on first message)
|
|
224
|
+
var _selVendor = store.get("currentVendor") || null;
|
|
225
|
+
if (_selVendor) payload.vendor = _selVendor;
|
|
220
226
|
ctx.ws.send(JSON.stringify(payload));
|
|
221
227
|
|
|
228
|
+
// Hide vendor toggle after first message (vendor is locked to this session)
|
|
229
|
+
var _vtw2 = document.getElementById("vendor-toggle-wrap");
|
|
230
|
+
if (_vtw2) { _vtw2.classList.remove("hidden"); _vtw2.classList.add("locked"); }
|
|
231
|
+
|
|
222
232
|
// Show pre-thinking dots before server responds
|
|
223
233
|
if (ctx.isMateDm && ctx.isMateDm()) {
|
|
224
234
|
ctx.showMatePreThinking();
|
|
@@ -648,6 +658,41 @@ export function initInput(_ctx) {
|
|
|
648
658
|
});
|
|
649
659
|
}
|
|
650
660
|
|
|
661
|
+
// Mobile "+" button -> unified bottom sheet with attach/image + context sources
|
|
662
|
+
var moreBtn = document.getElementById("input-more-btn");
|
|
663
|
+
var moreSheet = document.getElementById("input-more-sheet");
|
|
664
|
+
function openMoreSheet() {
|
|
665
|
+
if (!moreSheet) return;
|
|
666
|
+
// Render context sources into mobile sheet containers
|
|
667
|
+
try { renderContextPicker("-mobile"); } catch (e) {}
|
|
668
|
+
moreSheet.classList.remove("hidden");
|
|
669
|
+
requestAnimationFrame(function () { moreSheet.classList.add("open"); });
|
|
670
|
+
}
|
|
671
|
+
function closeMoreSheet() {
|
|
672
|
+
if (!moreSheet) return;
|
|
673
|
+
moreSheet.classList.remove("open");
|
|
674
|
+
setTimeout(function () { moreSheet.classList.add("hidden"); }, 250);
|
|
675
|
+
}
|
|
676
|
+
if (moreBtn && moreSheet) {
|
|
677
|
+
moreBtn.addEventListener("click", function (e) {
|
|
678
|
+
e.stopPropagation();
|
|
679
|
+
openMoreSheet();
|
|
680
|
+
});
|
|
681
|
+
var backdrop = moreSheet.querySelector(".input-more-backdrop");
|
|
682
|
+
if (backdrop) backdrop.addEventListener("click", closeMoreSheet);
|
|
683
|
+
|
|
684
|
+
var moreAttach = document.getElementById("input-more-attach");
|
|
685
|
+
if (moreAttach) moreAttach.addEventListener("click", function () {
|
|
686
|
+
closeMoreSheet();
|
|
687
|
+
createFileInput(null, null, true);
|
|
688
|
+
});
|
|
689
|
+
var moreImage = document.getElementById("input-more-image");
|
|
690
|
+
if (moreImage) moreImage.addEventListener("click", function () {
|
|
691
|
+
closeMoreSheet();
|
|
692
|
+
createFileInput("image/*", null, true);
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
651
696
|
// Schedule button — inline expand with minute input
|
|
652
697
|
var scheduleBtn = document.getElementById("schedule-btn");
|
|
653
698
|
var scheduleInlineInput = null;
|
|
@@ -770,6 +815,69 @@ export function initInput(_ctx) {
|
|
|
770
815
|
// Trigger the mention detection
|
|
771
816
|
inputEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
772
817
|
});
|
|
818
|
+
|
|
819
|
+
// Mate avatar overlay on @ button — rotate random mate faces
|
|
820
|
+
var _lastOverlayIdx = -1;
|
|
821
|
+
var _overlayInterval = null;
|
|
822
|
+
|
|
823
|
+
function getAvailableMates() {
|
|
824
|
+
var mates = store.get('cachedMatesList') || [];
|
|
825
|
+
return mates.filter(function (m) { return m.status !== 'interviewing'; });
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function pickNextMate(available) {
|
|
829
|
+
if (available.length === 0) return null;
|
|
830
|
+
if (available.length === 1) return available[0];
|
|
831
|
+
var idx = Math.floor(Math.random() * available.length);
|
|
832
|
+
if (idx === _lastOverlayIdx) idx = (idx + 1) % available.length;
|
|
833
|
+
_lastOverlayIdx = idx;
|
|
834
|
+
return available[idx];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function setOverlayAvatar(mate) {
|
|
838
|
+
var img = askMateBtn.querySelector('.ask-mate-avatar');
|
|
839
|
+
if (!mate) {
|
|
840
|
+
if (img) img.remove();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
var url = mateAvatarUrl(mate, 20);
|
|
844
|
+
if (!img) {
|
|
845
|
+
img = document.createElement('img');
|
|
846
|
+
img.className = 'ask-mate-avatar fade-in';
|
|
847
|
+
img.width = 20;
|
|
848
|
+
img.height = 20;
|
|
849
|
+
img.alt = '';
|
|
850
|
+
img.src = url;
|
|
851
|
+
askMateBtn.appendChild(img);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
// Fade out, swap, fade in
|
|
855
|
+
img.classList.remove('fade-in');
|
|
856
|
+
img.classList.add('fade-out');
|
|
857
|
+
setTimeout(function () {
|
|
858
|
+
img.src = url;
|
|
859
|
+
img.classList.remove('fade-out');
|
|
860
|
+
img.classList.add('fade-in');
|
|
861
|
+
}, 300);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function rotateMateOverlay() {
|
|
865
|
+
var available = getAvailableMates();
|
|
866
|
+
setOverlayAvatar(pickNextMate(available));
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function startOverlayRotation() {
|
|
870
|
+
if (_overlayInterval) clearInterval(_overlayInterval);
|
|
871
|
+
rotateMateOverlay();
|
|
872
|
+
_overlayInterval = setInterval(rotateMateOverlay, 10000);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Update overlay when mate list changes
|
|
876
|
+
store.subscribe(function (state, prev) {
|
|
877
|
+
if (state.cachedMatesList !== prev.cachedMatesList) {
|
|
878
|
+
startOverlayRotation();
|
|
879
|
+
}
|
|
880
|
+
});
|
|
773
881
|
}
|
|
774
882
|
|
|
775
883
|
// Paste handler
|
|
@@ -930,6 +1038,12 @@ export function initInput(_ctx) {
|
|
|
930
1038
|
return;
|
|
931
1039
|
}
|
|
932
1040
|
e.preventDefault();
|
|
1041
|
+
// If input is empty but ghost suggestion is showing, adopt it
|
|
1042
|
+
var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
|
|
1043
|
+
if (!ctx.inputEl.value.trim() && ghost) {
|
|
1044
|
+
ctx.inputEl.value = ghost;
|
|
1045
|
+
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
1046
|
+
}
|
|
933
1047
|
sendMessage();
|
|
934
1048
|
}
|
|
935
1049
|
});
|
|
@@ -941,6 +1055,14 @@ export function initInput(_ctx) {
|
|
|
941
1055
|
|
|
942
1056
|
// Send/Stop button — if sendable content exists, always send; otherwise stop
|
|
943
1057
|
ctx.sendBtn.addEventListener("click", function () {
|
|
1058
|
+
// Adopt ghost suggestion if input is empty
|
|
1059
|
+
var ghost = ctx.getGhostSuggestion ? ctx.getGhostSuggestion() : "";
|
|
1060
|
+
if (!hasSendableContent() && ghost) {
|
|
1061
|
+
ctx.inputEl.value = ghost;
|
|
1062
|
+
if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
|
|
1063
|
+
sendMessage();
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
944
1066
|
if (hasSendableContent()) {
|
|
945
1067
|
sendMessage();
|
|
946
1068
|
return;
|
|
@@ -23,7 +23,11 @@ export function updateMermaidTheme(vars) {
|
|
|
23
23
|
var mermaidIdCounter = 0;
|
|
24
24
|
|
|
25
25
|
export function renderMarkdown(text) {
|
|
26
|
-
|
|
26
|
+
// Normalize smart quotes so bold/italic delimiters flanking them parse correctly
|
|
27
|
+
var normalized = text
|
|
28
|
+
.replace(/[\u201C\u201D]/g, '"')
|
|
29
|
+
.replace(/[\u2018\u2019]/g, "'");
|
|
30
|
+
return DOMPurify.sanitize(marked.parse(normalized));
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/**
|