clay-server 2.19.0 → 2.20.0-beta.1
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 +51 -91
- package/bin/cli.js +49 -14
- package/lib/cli-sessions.js +3 -3
- package/lib/config.js +30 -4
- package/lib/daemon.js +28 -5
- package/lib/mates.js +61 -7
- package/lib/notes.js +20 -0
- package/lib/os-users.js +71 -2
- package/lib/project.js +850 -184
- package/lib/public/app.js +160 -16
- package/lib/public/css/mates.css +316 -2
- package/lib/public/css/mention.css +23 -0
- package/lib/public/css/mobile-nav.css +198 -0
- package/lib/public/css/overlays.css +23 -0
- package/lib/public/css/rewind.css +32 -0
- package/lib/public/css/title-bar.css +3 -0
- package/lib/public/css/user-settings.css +2 -2
- package/lib/public/index.html +64 -14
- package/lib/public/modules/command-palette.js +44 -4
- package/lib/public/modules/filebrowser.js +2 -0
- package/lib/public/modules/input.js +11 -3
- package/lib/public/modules/mate-knowledge.js +2 -0
- package/lib/public/modules/mate-memory.js +353 -0
- package/lib/public/modules/mention.js +77 -2
- package/lib/public/modules/notifications.js +0 -8
- package/lib/public/modules/server-settings.js +11 -4
- package/lib/public/modules/sidebar.js +284 -6
- package/lib/public/modules/theme.js +10 -12
- package/lib/public/modules/tools.js +84 -12
- package/lib/public/modules/user-settings.js +45 -12
- package/lib/sdk-bridge.js +114 -35
- package/lib/server.js +84 -6
- package/lib/sessions.js +4 -4
- package/lib/users.js +26 -0
- package/package.json +1 -1
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
2
|
+
import { escapeHtml } from './utils.js';
|
|
3
|
+
|
|
4
|
+
var getMateWs = null;
|
|
5
|
+
var visible = false;
|
|
6
|
+
var cachedEntries = [];
|
|
7
|
+
var cachedSummary = "";
|
|
8
|
+
|
|
9
|
+
// DOM elements
|
|
10
|
+
var sidebarBtn = null;
|
|
11
|
+
var countBadge = null;
|
|
12
|
+
var viewerEl = null;
|
|
13
|
+
var closeBtn = null;
|
|
14
|
+
var summaryContentEl = null;
|
|
15
|
+
var digestListEl = null;
|
|
16
|
+
var digestDetailEl = null;
|
|
17
|
+
var tabSummary = null;
|
|
18
|
+
var tabDigests = null;
|
|
19
|
+
var tabBodySummary = null;
|
|
20
|
+
var tabBodyDigests = null;
|
|
21
|
+
|
|
22
|
+
// Confirm overlay
|
|
23
|
+
var confirmOverlay = null;
|
|
24
|
+
|
|
25
|
+
var _onShow = null;
|
|
26
|
+
|
|
27
|
+
export function initMateMemory(mateWsGetter, opts) {
|
|
28
|
+
getMateWs = mateWsGetter;
|
|
29
|
+
if (opts && opts.onShow) _onShow = opts.onShow;
|
|
30
|
+
|
|
31
|
+
sidebarBtn = document.getElementById("mate-memory-btn");
|
|
32
|
+
countBadge = document.getElementById("mate-memory-count");
|
|
33
|
+
viewerEl = document.getElementById("mate-memory-viewer");
|
|
34
|
+
closeBtn = document.getElementById("mate-memory-viewer-close-btn");
|
|
35
|
+
summaryContentEl = document.getElementById("mate-memory-summary-content");
|
|
36
|
+
digestListEl = document.getElementById("mate-memory-digest-list");
|
|
37
|
+
digestDetailEl = document.getElementById("mate-memory-digest-detail");
|
|
38
|
+
tabBodySummary = document.getElementById("mate-memory-tab-summary");
|
|
39
|
+
tabBodyDigests = document.getElementById("mate-memory-tab-digests");
|
|
40
|
+
|
|
41
|
+
// Tab buttons
|
|
42
|
+
var tabs = viewerEl ? viewerEl.querySelectorAll(".mate-memory-tab") : [];
|
|
43
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
44
|
+
if (tabs[i].dataset.tab === "summary") tabSummary = tabs[i];
|
|
45
|
+
if (tabs[i].dataset.tab === "digests") tabDigests = tabs[i];
|
|
46
|
+
(function (tab) {
|
|
47
|
+
tab.addEventListener("click", function () {
|
|
48
|
+
switchTab(tab.dataset.tab);
|
|
49
|
+
});
|
|
50
|
+
})(tabs[i]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (sidebarBtn) {
|
|
54
|
+
sidebarBtn.addEventListener("click", function () {
|
|
55
|
+
if (visible) { hideMemory(); } else { showMemory(); }
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (closeBtn) {
|
|
60
|
+
closeBtn.addEventListener("click", hideMemory);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function switchTab(tabName) {
|
|
65
|
+
if (tabSummary) tabSummary.classList.toggle("active", tabName === "summary");
|
|
66
|
+
if (tabDigests) tabDigests.classList.toggle("active", tabName === "digests");
|
|
67
|
+
if (tabBodySummary) tabBodySummary.classList.toggle("hidden", tabName !== "summary");
|
|
68
|
+
if (tabBodyDigests) tabBodyDigests.classList.toggle("hidden", tabName !== "digests");
|
|
69
|
+
|
|
70
|
+
// When switching to digests, hide detail and show list
|
|
71
|
+
if (tabName === "digests") {
|
|
72
|
+
if (digestDetailEl) digestDetailEl.classList.add("hidden");
|
|
73
|
+
if (digestListEl) digestListEl.classList.remove("hidden");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function showMemory() {
|
|
78
|
+
if (_onShow) _onShow();
|
|
79
|
+
visible = true;
|
|
80
|
+
if (sidebarBtn) sidebarBtn.classList.add("active");
|
|
81
|
+
if (viewerEl) viewerEl.classList.remove("hidden");
|
|
82
|
+
|
|
83
|
+
// Default to summary tab
|
|
84
|
+
switchTab("summary");
|
|
85
|
+
requestMemoryList();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function hideMemory() {
|
|
89
|
+
visible = false;
|
|
90
|
+
if (sidebarBtn) sidebarBtn.classList.remove("active");
|
|
91
|
+
if (viewerEl) viewerEl.classList.add("hidden");
|
|
92
|
+
dismissConfirm();
|
|
93
|
+
cachedEntries = [];
|
|
94
|
+
cachedSummary = "";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isMemoryVisible() {
|
|
98
|
+
return visible;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function requestMemoryList() {
|
|
102
|
+
var ws = getMateWs ? getMateWs() : null;
|
|
103
|
+
if (ws && ws.readyState === 1) {
|
|
104
|
+
ws.send(JSON.stringify({ type: "memory_list" }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function renderMemoryList(entries, summary) {
|
|
109
|
+
cachedEntries = entries || [];
|
|
110
|
+
cachedSummary = summary || "";
|
|
111
|
+
|
|
112
|
+
// Update badge
|
|
113
|
+
if (countBadge) {
|
|
114
|
+
if (cachedEntries.length > 0) {
|
|
115
|
+
countBadge.textContent = cachedEntries.length;
|
|
116
|
+
countBadge.classList.remove("hidden");
|
|
117
|
+
} else {
|
|
118
|
+
countBadge.textContent = "";
|
|
119
|
+
countBadge.classList.add("hidden");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Render summary tab
|
|
124
|
+
renderSummary();
|
|
125
|
+
|
|
126
|
+
// Render digest list
|
|
127
|
+
renderDigestList();
|
|
128
|
+
|
|
129
|
+
refreshIcons();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function renderSummary() {
|
|
133
|
+
if (!summaryContentEl) return;
|
|
134
|
+
|
|
135
|
+
if (!cachedSummary) {
|
|
136
|
+
summaryContentEl.innerHTML =
|
|
137
|
+
'<div class="mate-memory-empty">No memory summary yet. Memories will be created automatically as you chat.</div>';
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Render markdown summary (simple conversion)
|
|
142
|
+
var html = escapeHtml(cachedSummary);
|
|
143
|
+
// Convert markdown headers
|
|
144
|
+
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
|
145
|
+
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
|
146
|
+
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
|
147
|
+
// Convert bullet points
|
|
148
|
+
html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
|
|
149
|
+
html = html.replace(/(<li>.*<\/li>\n?)+/g, function (m) { return '<ul>' + m + '</ul>'; });
|
|
150
|
+
// Convert bold
|
|
151
|
+
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
152
|
+
// Line breaks
|
|
153
|
+
html = html.replace(/\n\n/g, '</p><p>');
|
|
154
|
+
html = '<p>' + html + '</p>';
|
|
155
|
+
html = html.replace(/<p>\s*<(h[123]|ul)/g, '<$1');
|
|
156
|
+
html = html.replace(/<\/(h[123]|ul)>\s*<\/p>/g, '</$1>');
|
|
157
|
+
|
|
158
|
+
summaryContentEl.innerHTML = html;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function renderDigestList() {
|
|
162
|
+
if (!digestListEl) return;
|
|
163
|
+
digestListEl.innerHTML = "";
|
|
164
|
+
|
|
165
|
+
if (cachedEntries.length === 0) {
|
|
166
|
+
var empty = document.createElement("div");
|
|
167
|
+
empty.className = "mate-memory-empty";
|
|
168
|
+
empty.textContent = "No session logs yet";
|
|
169
|
+
digestListEl.appendChild(empty);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (var i = 0; i < cachedEntries.length; i++) {
|
|
174
|
+
digestListEl.appendChild(renderDigestItem(cachedEntries[i], i));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function renderDigestItem(entry, listIndex) {
|
|
179
|
+
var item = document.createElement("div");
|
|
180
|
+
item.className = "mate-memory-item";
|
|
181
|
+
|
|
182
|
+
// Top row: date + type badge + delete
|
|
183
|
+
var topRow = document.createElement("div");
|
|
184
|
+
topRow.className = "mate-memory-item-top";
|
|
185
|
+
|
|
186
|
+
var dateEl = document.createElement("span");
|
|
187
|
+
dateEl.className = "mate-memory-date";
|
|
188
|
+
dateEl.textContent = entry.date || "?";
|
|
189
|
+
topRow.appendChild(dateEl);
|
|
190
|
+
|
|
191
|
+
if (entry.type) {
|
|
192
|
+
var badge = document.createElement("span");
|
|
193
|
+
badge.className = "mate-memory-type-badge";
|
|
194
|
+
badge.textContent = entry.type;
|
|
195
|
+
topRow.appendChild(badge);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (entry.tags && entry.tags.length > 0) {
|
|
199
|
+
for (var t = 0; t < entry.tags.length && t < 3; t++) {
|
|
200
|
+
var tag = document.createElement("span");
|
|
201
|
+
tag.className = "mate-memory-tag";
|
|
202
|
+
tag.textContent = entry.tags[t];
|
|
203
|
+
topRow.appendChild(tag);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var delBtn = document.createElement("button");
|
|
208
|
+
delBtn.className = "mate-memory-delete-btn";
|
|
209
|
+
delBtn.title = "Delete";
|
|
210
|
+
delBtn.innerHTML = iconHtml("trash-2");
|
|
211
|
+
delBtn.addEventListener("click", function (e) {
|
|
212
|
+
e.stopPropagation();
|
|
213
|
+
confirmDelete(entry.index);
|
|
214
|
+
});
|
|
215
|
+
topRow.appendChild(delBtn);
|
|
216
|
+
|
|
217
|
+
item.appendChild(topRow);
|
|
218
|
+
|
|
219
|
+
// Topic
|
|
220
|
+
var topicEl = document.createElement("div");
|
|
221
|
+
topicEl.className = "mate-memory-topic";
|
|
222
|
+
topicEl.textContent = entry.topic || "(no topic)";
|
|
223
|
+
item.appendChild(topicEl);
|
|
224
|
+
|
|
225
|
+
// Position preview
|
|
226
|
+
if (entry.my_position) {
|
|
227
|
+
var posEl = document.createElement("div");
|
|
228
|
+
posEl.className = "mate-memory-position";
|
|
229
|
+
var preview = entry.my_position;
|
|
230
|
+
if (preview.length > 120) preview = preview.substring(0, 120) + "...";
|
|
231
|
+
posEl.textContent = preview;
|
|
232
|
+
item.appendChild(posEl);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
item.addEventListener("click", function () {
|
|
236
|
+
openDigestDetail(entry, listIndex);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return item;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function openDigestDetail(entry, listIndex) {
|
|
243
|
+
if (!digestDetailEl || !digestListEl) return;
|
|
244
|
+
|
|
245
|
+
// Hide list, show detail
|
|
246
|
+
digestListEl.classList.add("hidden");
|
|
247
|
+
digestDetailEl.classList.remove("hidden");
|
|
248
|
+
|
|
249
|
+
// Build detail HTML
|
|
250
|
+
var html = '';
|
|
251
|
+
html += '<div class="mate-memory-detail-header">';
|
|
252
|
+
html += '<button class="mate-memory-detail-back">' + iconHtml("arrow-left") + ' Back</button>';
|
|
253
|
+
html += '<div class="mate-knowledge-toolbar-spacer"></div>';
|
|
254
|
+
html += '<button class="mate-memory-detail-delete mate-memory-danger-btn" title="Delete">' + iconHtml("trash-2") + '</button>';
|
|
255
|
+
html += '</div>';
|
|
256
|
+
html += '<div class="mate-memory-detail-body">';
|
|
257
|
+
html += renderField("Date", entry.date);
|
|
258
|
+
if (entry.type) html += renderField("Type", entry.type);
|
|
259
|
+
html += renderField("Topic", entry.topic);
|
|
260
|
+
html += renderField("My Position", entry.my_position);
|
|
261
|
+
if (entry.user_intent) html += renderField("User Intent", entry.user_intent);
|
|
262
|
+
if (entry.other_perspectives) html += renderField("Other Perspectives", entry.other_perspectives);
|
|
263
|
+
html += renderField("Decisions", entry.decisions);
|
|
264
|
+
html += renderField("Open Items", entry.open_items);
|
|
265
|
+
if (entry.user_sentiment) html += renderField("User Sentiment", entry.user_sentiment);
|
|
266
|
+
if (entry.my_role) html += renderField("My Role", entry.my_role);
|
|
267
|
+
if (entry.outcome) html += renderField("Outcome", entry.outcome);
|
|
268
|
+
if (entry.confidence) html += renderField("Confidence", entry.confidence);
|
|
269
|
+
if (entry.tags && entry.tags.length > 0) html += renderField("Tags", entry.tags.join(", "));
|
|
270
|
+
html += '</div>';
|
|
271
|
+
|
|
272
|
+
digestDetailEl.innerHTML = html;
|
|
273
|
+
refreshIcons();
|
|
274
|
+
|
|
275
|
+
// Back button
|
|
276
|
+
var backBtn = digestDetailEl.querySelector(".mate-memory-detail-back");
|
|
277
|
+
if (backBtn) {
|
|
278
|
+
backBtn.addEventListener("click", function () {
|
|
279
|
+
digestDetailEl.classList.add("hidden");
|
|
280
|
+
digestListEl.classList.remove("hidden");
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Delete button
|
|
285
|
+
var delBtn = digestDetailEl.querySelector(".mate-memory-detail-delete");
|
|
286
|
+
if (delBtn) {
|
|
287
|
+
delBtn.addEventListener("click", function () {
|
|
288
|
+
confirmDelete(entry.index);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function renderField(label, value) {
|
|
294
|
+
if (!value || value === "null") return "";
|
|
295
|
+
return '<div class="mate-memory-detail">' +
|
|
296
|
+
'<div class="mate-memory-detail-label">' + escapeHtml(label) + '</div>' +
|
|
297
|
+
'<div class="mate-memory-detail-value">' + escapeHtml(String(value)) + '</div>' +
|
|
298
|
+
'</div>';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function confirmDelete(index) {
|
|
302
|
+
dismissConfirm();
|
|
303
|
+
|
|
304
|
+
confirmOverlay = document.createElement("div");
|
|
305
|
+
confirmOverlay.className = "mate-memory-confirm-overlay";
|
|
306
|
+
|
|
307
|
+
var dialog = document.createElement("div");
|
|
308
|
+
dialog.className = "mate-memory-confirm-dialog";
|
|
309
|
+
|
|
310
|
+
var msg = document.createElement("div");
|
|
311
|
+
msg.className = "mate-memory-confirm-msg";
|
|
312
|
+
msg.textContent = "Delete this memory?";
|
|
313
|
+
dialog.appendChild(msg);
|
|
314
|
+
|
|
315
|
+
var actions = document.createElement("div");
|
|
316
|
+
actions.className = "mate-memory-confirm-actions";
|
|
317
|
+
|
|
318
|
+
var cancelBtn = document.createElement("button");
|
|
319
|
+
cancelBtn.className = "mate-memory-confirm-cancel";
|
|
320
|
+
cancelBtn.textContent = "Cancel";
|
|
321
|
+
cancelBtn.addEventListener("click", dismissConfirm);
|
|
322
|
+
actions.appendChild(cancelBtn);
|
|
323
|
+
|
|
324
|
+
var deleteBtn = document.createElement("button");
|
|
325
|
+
deleteBtn.className = "mate-memory-confirm-delete";
|
|
326
|
+
deleteBtn.textContent = "Delete";
|
|
327
|
+
deleteBtn.addEventListener("click", function () {
|
|
328
|
+
var ws = getMateWs ? getMateWs() : null;
|
|
329
|
+
if (ws && ws.readyState === 1) {
|
|
330
|
+
ws.send(JSON.stringify({ type: "memory_delete", index: index }));
|
|
331
|
+
}
|
|
332
|
+
dismissConfirm();
|
|
333
|
+
});
|
|
334
|
+
actions.appendChild(deleteBtn);
|
|
335
|
+
|
|
336
|
+
dialog.appendChild(actions);
|
|
337
|
+
confirmOverlay.appendChild(dialog);
|
|
338
|
+
confirmOverlay.addEventListener("click", function (e) {
|
|
339
|
+
if (e.target === confirmOverlay) dismissConfirm();
|
|
340
|
+
});
|
|
341
|
+
document.body.appendChild(confirmOverlay);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function dismissConfirm() {
|
|
345
|
+
if (confirmOverlay && confirmOverlay.parentNode) {
|
|
346
|
+
confirmOverlay.parentNode.removeChild(confirmOverlay);
|
|
347
|
+
}
|
|
348
|
+
confirmOverlay = null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function handleMemoryDeleted() {
|
|
352
|
+
// List update follows via memory_list
|
|
353
|
+
}
|
|
@@ -233,10 +233,11 @@ export function clearMentionState() {
|
|
|
233
233
|
removeInputMentionChip();
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
export function sendMention(mateId, text, pastes) {
|
|
236
|
+
export function sendMention(mateId, text, pastes, images) {
|
|
237
237
|
if (!ctx.ws || !ctx.connected) return;
|
|
238
238
|
var payload = { type: "mention", mateId: mateId, text: text };
|
|
239
239
|
if (pastes && pastes.length > 0) payload.pastes = pastes;
|
|
240
|
+
if (images && images.length > 0) payload.images = images;
|
|
240
241
|
ctx.ws.send(JSON.stringify(payload));
|
|
241
242
|
}
|
|
242
243
|
|
|
@@ -298,6 +299,17 @@ export function handleMentionStart(msg) {
|
|
|
298
299
|
badge.className = "mention-badge";
|
|
299
300
|
badge.textContent = "@MENTION";
|
|
300
301
|
header.appendChild(badge);
|
|
302
|
+
|
|
303
|
+
var stopBtn = document.createElement("button");
|
|
304
|
+
stopBtn.className = "mention-stop-btn";
|
|
305
|
+
stopBtn.title = "Stop";
|
|
306
|
+
stopBtn.innerHTML = iconHtml("square");
|
|
307
|
+
stopBtn.addEventListener("click", function () {
|
|
308
|
+
if (ctx.ws && ctx.connected) {
|
|
309
|
+
ctx.ws.send(JSON.stringify({ type: "mention_stop", mateId: msg.mateId }));
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
header.appendChild(stopBtn);
|
|
301
313
|
contentWrap.appendChild(header);
|
|
302
314
|
|
|
303
315
|
// Activity indicator
|
|
@@ -336,6 +348,17 @@ export function handleMentionStart(msg) {
|
|
|
336
348
|
nameSpan.textContent = msg.mateName || "Mate";
|
|
337
349
|
header.appendChild(nameSpan);
|
|
338
350
|
|
|
351
|
+
var stopBtn = document.createElement("button");
|
|
352
|
+
stopBtn.className = "mention-stop-btn";
|
|
353
|
+
stopBtn.title = "Stop";
|
|
354
|
+
stopBtn.innerHTML = iconHtml("square");
|
|
355
|
+
stopBtn.addEventListener("click", function () {
|
|
356
|
+
if (ctx.ws && ctx.connected) {
|
|
357
|
+
ctx.ws.send(JSON.stringify({ type: "mention_stop", mateId: msg.mateId }));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
header.appendChild(stopBtn);
|
|
361
|
+
|
|
339
362
|
currentMentionEl.appendChild(header);
|
|
340
363
|
|
|
341
364
|
// Activity indicator
|
|
@@ -457,10 +480,12 @@ function flushMentionStream() {
|
|
|
457
480
|
|
|
458
481
|
export function handleMentionDone(msg) {
|
|
459
482
|
flushMentionStream();
|
|
460
|
-
// Hide activity bar
|
|
461
483
|
if (currentMentionEl) {
|
|
462
484
|
var bar = currentMentionEl.querySelector(".mention-activity-bar");
|
|
463
485
|
if (bar) bar.style.display = "none";
|
|
486
|
+
// Remove stop button
|
|
487
|
+
var stopBtn = currentMentionEl.querySelector(".mention-stop-btn");
|
|
488
|
+
if (stopBtn) stopBtn.remove();
|
|
464
489
|
// Add copy handler so user can "click to grab this"
|
|
465
490
|
if (ctx.addCopyHandler && mentionFullText) {
|
|
466
491
|
ctx.addCopyHandler(currentMentionEl, mentionFullText);
|
|
@@ -477,6 +502,8 @@ export function handleMentionError(msg) {
|
|
|
477
502
|
if (currentMentionEl) {
|
|
478
503
|
var bar = currentMentionEl.querySelector(".mention-activity-bar");
|
|
479
504
|
if (bar) bar.style.display = "none";
|
|
505
|
+
var stopBtn = currentMentionEl.querySelector(".mention-stop-btn");
|
|
506
|
+
if (stopBtn) stopBtn.remove();
|
|
480
507
|
var contentEl = currentMentionEl.querySelector(".mention-content");
|
|
481
508
|
if (contentEl) {
|
|
482
509
|
contentEl.innerHTML = '<div class="mention-error">Error: ' + escapeHtml(msg.error || "Unknown error") + '</div>';
|
|
@@ -511,6 +538,54 @@ export function renderMentionUser(entry) {
|
|
|
511
538
|
bubble.className = "bubble";
|
|
512
539
|
bubble.dir = "auto";
|
|
513
540
|
|
|
541
|
+
// Render images (same pattern as addUserMessage in app.js)
|
|
542
|
+
if (entry.images && entry.images.length > 0) {
|
|
543
|
+
var imgRow = document.createElement("div");
|
|
544
|
+
imgRow.className = "bubble-images";
|
|
545
|
+
for (var ii = 0; ii < entry.images.length; ii++) {
|
|
546
|
+
var img = document.createElement("img");
|
|
547
|
+
if (entry.images[ii].url) {
|
|
548
|
+
img.src = entry.images[ii].url;
|
|
549
|
+
} else if (entry.images[ii].data) {
|
|
550
|
+
img.src = "data:" + entry.images[ii].mediaType + ";base64," + entry.images[ii].data;
|
|
551
|
+
}
|
|
552
|
+
img.loading = "lazy";
|
|
553
|
+
img.className = "bubble-img";
|
|
554
|
+
img.addEventListener("click", function () {
|
|
555
|
+
if (ctx.showImageModal) ctx.showImageModal(this.src);
|
|
556
|
+
});
|
|
557
|
+
img.addEventListener("error", function () {
|
|
558
|
+
var placeholder = document.createElement("div");
|
|
559
|
+
placeholder.className = "bubble-img-expired";
|
|
560
|
+
placeholder.textContent = "Image deleted";
|
|
561
|
+
this.parentNode.replaceChild(placeholder, this);
|
|
562
|
+
});
|
|
563
|
+
imgRow.appendChild(img);
|
|
564
|
+
}
|
|
565
|
+
bubble.appendChild(imgRow);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Render pastes
|
|
569
|
+
if (entry.pastes && entry.pastes.length > 0) {
|
|
570
|
+
var pasteRow = document.createElement("div");
|
|
571
|
+
pasteRow.className = "bubble-pastes";
|
|
572
|
+
for (var pi = 0; pi < entry.pastes.length; pi++) {
|
|
573
|
+
(function (pasteText) {
|
|
574
|
+
var chip = document.createElement("div");
|
|
575
|
+
chip.className = "bubble-paste";
|
|
576
|
+
var preview = pasteText.substring(0, 60).replace(/\n/g, " ");
|
|
577
|
+
if (pasteText.length > 60) preview += "...";
|
|
578
|
+
chip.innerHTML = '<span class="bubble-paste-preview">' + escapeHtml(preview) + '</span><span class="bubble-paste-label">PASTED</span>';
|
|
579
|
+
chip.addEventListener("click", function (e) {
|
|
580
|
+
e.stopPropagation();
|
|
581
|
+
if (ctx.showPasteModal) ctx.showPasteModal(pasteText);
|
|
582
|
+
});
|
|
583
|
+
pasteRow.appendChild(chip);
|
|
584
|
+
})(entry.pastes[pi]);
|
|
585
|
+
}
|
|
586
|
+
bubble.appendChild(pasteRow);
|
|
587
|
+
}
|
|
588
|
+
|
|
514
589
|
var textEl = document.createElement("span");
|
|
515
590
|
textEl.innerHTML = '<span class="mention-chip">@' + escapeHtml(entry.mateName || "Mate") + '</span> ' + escapeHtml(entry.text || "");
|
|
516
591
|
bubble.appendChild(textEl);
|
|
@@ -184,14 +184,6 @@ export function initNotifications(_ctx) {
|
|
|
184
184
|
ctx.ws.send(JSON.stringify({ type: "check_update" }));
|
|
185
185
|
}
|
|
186
186
|
setUpdateBtn("Checking…", true, true);
|
|
187
|
-
setTimeout(function () {
|
|
188
|
-
if (settingsUpdateCheck.disabled) {
|
|
189
|
-
setUpdateBtn("Up to date", false, true);
|
|
190
|
-
setTimeout(function () {
|
|
191
|
-
setUpdateBtn("Check for updates", false, false);
|
|
192
|
-
}, 1500);
|
|
193
|
-
}
|
|
194
|
-
}, 2000);
|
|
195
187
|
});
|
|
196
188
|
}
|
|
197
189
|
|
|
@@ -144,6 +144,8 @@ export function initServerSettings(appCtx) {
|
|
|
144
144
|
if (e.key === "Escape") { e.preventDefault(); hidePinForm(); }
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
// Auto-continue moved to User Settings > Behavior
|
|
148
|
+
|
|
147
149
|
// Keep awake toggle
|
|
148
150
|
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
149
151
|
if (keepAwakeToggle) {
|
|
@@ -318,10 +320,8 @@ function populateSettings() {
|
|
|
318
320
|
// Nav header defaults to hostname (updated by updateDaemonConfig)
|
|
319
321
|
if (nameEl && !nameEl.textContent) nameEl.textContent = "Server";
|
|
320
322
|
|
|
321
|
-
|
|
322
|
-
if (versionEl &&
|
|
323
|
-
versionEl.textContent = footerVersion.textContent || "-";
|
|
324
|
-
}
|
|
323
|
+
// Version is set from WebSocket "info" message in app.js
|
|
324
|
+
if (versionEl && !versionEl.textContent) versionEl.textContent = "-";
|
|
325
325
|
|
|
326
326
|
if (slugEl) slugEl.textContent = ctx.currentSlug || "(default)";
|
|
327
327
|
if (wsPathEl) wsPathEl.textContent = ctx.wsPath || "/ws";
|
|
@@ -475,6 +475,9 @@ export function updateDaemonConfig(config) {
|
|
|
475
475
|
// PIN status
|
|
476
476
|
updatePinStatus(!!config.pinEnabled);
|
|
477
477
|
|
|
478
|
+
// Auto-continue on rate limit
|
|
479
|
+
// Auto-continue is now per-user (User Settings > Behavior)
|
|
480
|
+
|
|
478
481
|
// Keep awake
|
|
479
482
|
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
480
483
|
if (keepAwakeToggle) keepAwakeToggle.checked = !!config.keepAwake;
|
|
@@ -529,6 +532,10 @@ export function handleKeepAwakeChanged(msg) {
|
|
|
529
532
|
if (keepAwakeToggle) keepAwakeToggle.checked = !!msg.keepAwake;
|
|
530
533
|
}
|
|
531
534
|
|
|
535
|
+
export function handleAutoContinueChanged(msg) {
|
|
536
|
+
// Auto-continue is now per-user; server broadcast no longer updates UI
|
|
537
|
+
}
|
|
538
|
+
|
|
532
539
|
export function handleRestartResult(msg) {
|
|
533
540
|
var restartBtn = document.getElementById("settings-restart-btn");
|
|
534
541
|
var errorEl = document.getElementById("settings-restart-error");
|