clay-server 2.19.0 → 2.20.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +51 -91
  2. package/bin/cli.js +49 -14
  3. package/lib/builtin-mates.js +360 -0
  4. package/lib/cli-sessions.js +3 -3
  5. package/lib/config.js +30 -4
  6. package/lib/daemon.js +28 -5
  7. package/lib/mates.js +169 -7
  8. package/lib/notes.js +20 -0
  9. package/lib/os-users.js +71 -2
  10. package/lib/project.js +903 -228
  11. package/lib/public/app.js +249 -69
  12. package/lib/public/css/icon-strip.css +55 -2
  13. package/lib/public/css/input.css +50 -30
  14. package/lib/public/css/mates.css +316 -2
  15. package/lib/public/css/mention.css +23 -0
  16. package/lib/public/css/mobile-nav.css +198 -0
  17. package/lib/public/css/overlays.css +23 -0
  18. package/lib/public/css/rewind.css +32 -0
  19. package/lib/public/css/title-bar.css +3 -0
  20. package/lib/public/css/user-settings.css +2 -2
  21. package/lib/public/index.html +65 -14
  22. package/lib/public/mates/ally.png +0 -0
  23. package/lib/public/mates/sage.jpg +0 -0
  24. package/lib/public/mates/scout.png +0 -0
  25. package/lib/public/modules/command-palette.js +44 -4
  26. package/lib/public/modules/filebrowser.js +2 -0
  27. package/lib/public/modules/input.js +158 -16
  28. package/lib/public/modules/mate-knowledge.js +2 -0
  29. package/lib/public/modules/mate-memory.js +353 -0
  30. package/lib/public/modules/mention.js +77 -2
  31. package/lib/public/modules/notifications.js +0 -8
  32. package/lib/public/modules/server-settings.js +11 -4
  33. package/lib/public/modules/sidebar.js +507 -21
  34. package/lib/public/modules/theme.js +10 -12
  35. package/lib/public/modules/tools.js +84 -12
  36. package/lib/public/modules/user-settings.js +45 -12
  37. package/lib/sdk-bridge.js +122 -61
  38. package/lib/server.js +209 -13
  39. package/lib/sessions.js +4 -4
  40. package/lib/users.js +89 -0
  41. package/package.json +1 -1
@@ -13,6 +13,7 @@ var slashActiveIdx = -1;
13
13
  var slashFiltered = [];
14
14
  var isComposing = false;
15
15
  var isRemoteInput = false;
16
+ var scheduleDelayMs = 0; // 0 = no schedule, >0 = delay in ms
16
17
 
17
18
  export function hasSendableContent() {
18
19
  return !!(
@@ -23,6 +24,36 @@ export function hasSendableContent() {
23
24
  );
24
25
  }
25
26
 
27
+ export function getScheduleDelay() {
28
+ return scheduleDelayMs;
29
+ }
30
+
31
+ export function clearScheduleDelay() {
32
+ scheduleDelayMs = 0;
33
+ var btn = document.getElementById("schedule-btn");
34
+ if (btn) {
35
+ btn.classList.remove("schedule-active", "schedule-expanded");
36
+ var lbl = btn.querySelector(".schedule-delay-label");
37
+ if (lbl) lbl.remove();
38
+ var inp = btn.querySelector(".schedule-inline-input");
39
+ if (inp) inp.remove();
40
+ btn.title = "Schedule message";
41
+ }
42
+ }
43
+
44
+ export function setScheduleBtnDisabled(disabled) {
45
+ var btn = document.getElementById("schedule-btn");
46
+ if (!btn) return;
47
+ btn.disabled = disabled;
48
+ if (disabled) {
49
+ btn.style.opacity = "0.3";
50
+ btn.style.pointerEvents = "none";
51
+ } else {
52
+ btn.style.opacity = "";
53
+ btn.style.pointerEvents = "";
54
+ }
55
+ }
56
+
26
57
  export var builtinCommands = [
27
58
  { name: "clear", desc: "Clear conversation" },
28
59
  { name: "context", desc: "Context window usage" },
@@ -101,10 +132,18 @@ export function sendMessage() {
101
132
  if (mention) {
102
133
  hideMentionMenu();
103
134
  if (ctx.hideSuggestionChips) ctx.hideSuggestionChips();
135
+ var mentionImages = pendingImages.slice();
104
136
  var mentionPastes = pendingPastes.map(function (p) { return p.text; });
137
+ // Prepend file paths to mention text (same pattern as regular messages)
138
+ var mentionFiles = pendingFiles.slice();
139
+ var mentionText = mention.text;
140
+ if (mentionFiles.length > 0) {
141
+ var mFilePaths = mentionFiles.map(function (f) { return "[Uploaded file: " + f.path + "]"; }).join("\n");
142
+ mentionText = mentionText ? mFilePaths + "\n\n" + mentionText : mFilePaths;
143
+ }
105
144
  // Render user message with mention chip (same as history replay)
106
- renderMentionUser({ mateName: mention.mateName, text: mention.text });
107
- sendMention(mention.mateId, mention.text, mentionPastes);
145
+ renderMentionUser({ mateName: mention.mateName, text: mentionText, images: mentionImages.length > 0 ? mentionImages : null, pastes: mentionPastes.length > 0 ? mentionPastes : null });
146
+ sendMention(mention.mateId, mentionText, mentionPastes, mentionImages);
108
147
  clearMentionState();
109
148
  ctx.inputEl.value = "";
110
149
  sendInputSync();
@@ -123,19 +162,17 @@ export function sendMessage() {
123
162
 
124
163
  var pastes = pendingPastes.map(function (p) { return p.text; });
125
164
 
126
- // Schedule mode: queue message for after rate limit resets
127
- if (ctx.isScheduleMode && ctx.isScheduleMode()) {
128
- var resetsAt = ctx.getRateLimitResetsAt ? ctx.getRateLimitResetsAt() : null;
129
- if (resetsAt && resetsAt > Date.now()) {
130
- ctx.ws.send(JSON.stringify({ type: "schedule_message", text: text || "", resetsAt: resetsAt }));
131
- if (ctx.exitScheduleMode) ctx.exitScheduleMode();
132
- ctx.inputEl.value = "";
133
- sendInputSync();
134
- clearPendingImages();
135
- autoResize();
136
- ctx.inputEl.focus();
137
- return;
138
- }
165
+ // Scheduled message: queue message with timer delay
166
+ if (scheduleDelayMs > 0) {
167
+ var resetsAt = Date.now() + scheduleDelayMs;
168
+ ctx.ws.send(JSON.stringify({ type: "schedule_message", text: text || "", resetsAt: resetsAt }));
169
+ clearScheduleDelay();
170
+ ctx.inputEl.value = "";
171
+ sendInputSync();
172
+ clearPendingImages();
173
+ autoResize();
174
+ ctx.inputEl.focus();
175
+ return;
139
176
  }
140
177
 
141
178
  ctx.addUserMessage(text, images.length > 0 ? images : null, pastes.length > 0 ? pastes : null);
@@ -570,11 +607,116 @@ export function initInput(_ctx) {
570
607
  });
571
608
  }
572
609
 
610
+ // Schedule button — inline expand with minute input
611
+ var scheduleBtn = document.getElementById("schedule-btn");
612
+ var scheduleInlineInput = null;
613
+ var scheduleInlineLabel = null;
614
+ var scheduleOutsideHandler = null;
615
+
616
+ function formatDelayLabel(ms) {
617
+ var mins = Math.round(ms / 60000);
618
+ if (mins < 60) return mins + "m";
619
+ var hrs = Math.floor(mins / 60);
620
+ var rem = mins % 60;
621
+ return rem > 0 ? hrs + "h " + rem + "m" : hrs + "h";
622
+ }
623
+
624
+ function collapseScheduleBtn() {
625
+ if (!scheduleBtn) return;
626
+ scheduleBtn.classList.remove("schedule-expanded");
627
+ if (scheduleInlineInput) { scheduleInlineInput.remove(); scheduleInlineInput = null; }
628
+ if (scheduleInlineLabel) { scheduleInlineLabel.remove(); scheduleInlineLabel = null; }
629
+ if (scheduleOutsideHandler) {
630
+ document.removeEventListener("mousedown", scheduleOutsideHandler);
631
+ scheduleOutsideHandler = null;
632
+ }
633
+ }
634
+
635
+ function setScheduleDelay(ms) {
636
+ scheduleDelayMs = ms;
637
+ if (!scheduleBtn) return;
638
+ collapseScheduleBtn();
639
+ if (ms > 0) {
640
+ scheduleBtn.classList.add("schedule-active", "schedule-expanded");
641
+ scheduleBtn.title = "Scheduled: " + formatDelayLabel(ms) + " (click to clear)";
642
+ scheduleInlineLabel = document.createElement("span");
643
+ scheduleInlineLabel.className = "schedule-delay-label";
644
+ scheduleInlineLabel.textContent = formatDelayLabel(ms);
645
+ scheduleBtn.appendChild(scheduleInlineLabel);
646
+ } else {
647
+ scheduleBtn.classList.remove("schedule-active", "schedule-expanded");
648
+ scheduleBtn.title = "Schedule message";
649
+ }
650
+ }
651
+
652
+ function expandScheduleInput() {
653
+ if (!scheduleBtn) return;
654
+ scheduleBtn.classList.add("schedule-expanded");
655
+ scheduleInlineInput = document.createElement("input");
656
+ scheduleInlineInput.type = "number";
657
+ scheduleInlineInput.min = "1";
658
+ scheduleInlineInput.max = "1440";
659
+ scheduleInlineInput.placeholder = "min";
660
+ scheduleInlineInput.className = "schedule-inline-input";
661
+ scheduleBtn.appendChild(scheduleInlineInput);
662
+
663
+ setTimeout(function () { scheduleInlineInput.focus(); }, 0);
664
+
665
+ scheduleInlineInput.addEventListener("click", function (e) { e.stopPropagation(); });
666
+ scheduleInlineInput.addEventListener("keydown", function (e) {
667
+ if (e.key === "Enter") {
668
+ e.preventDefault();
669
+ var val = parseInt(scheduleInlineInput.value, 10);
670
+ if (val >= 1 && val <= 1440) {
671
+ setScheduleDelay(val * 60000);
672
+ } else {
673
+ collapseScheduleBtn();
674
+ }
675
+ } else if (e.key === "Escape") {
676
+ collapseScheduleBtn();
677
+ }
678
+ });
679
+
680
+ // Close on outside click
681
+ setTimeout(function () {
682
+ scheduleOutsideHandler = function (ev) {
683
+ if (!scheduleBtn.contains(ev.target)) {
684
+ if (scheduleInlineInput) {
685
+ var val = parseInt(scheduleInlineInput.value, 10);
686
+ if (val >= 1 && val <= 1440) {
687
+ setScheduleDelay(val * 60000);
688
+ } else {
689
+ collapseScheduleBtn();
690
+ }
691
+ }
692
+ document.removeEventListener("mousedown", scheduleOutsideHandler);
693
+ scheduleOutsideHandler = null;
694
+ }
695
+ };
696
+ document.addEventListener("mousedown", scheduleOutsideHandler);
697
+ }, 0);
698
+ }
699
+
700
+ if (scheduleBtn) {
701
+ scheduleBtn.addEventListener("click", function (e) {
702
+ e.stopPropagation();
703
+ if (scheduleDelayMs > 0) {
704
+ setScheduleDelay(0);
705
+ return;
706
+ }
707
+ if (scheduleInlineInput) {
708
+ collapseScheduleBtn();
709
+ } else {
710
+ expandScheduleInput();
711
+ }
712
+ });
713
+ }
714
+
573
715
  // Paste handler
574
716
  document.addEventListener("paste", function (e) {
575
717
  // Don't intercept paste when typing in modals or other non-chat inputs
576
718
  var target = e.target;
577
- if (target && target.closest && target.closest(".sticky-note, #notes-archive, #ralph-wizard, .confirm-modal, .scheduler-detail")) return;
719
+ if (target && target.closest && target.closest(".sticky-note, #notes-archive, #ralph-wizard, .confirm-modal, .scheduler-detail, #debate-modal, .us-modal")) return;
578
720
 
579
721
  var cd = e.clipboardData;
580
722
  if (!cd) return;
@@ -1,5 +1,6 @@
1
1
  import { iconHtml, refreshIcons } from './icons.js';
2
2
  import { hideNotes } from './sticky-notes.js';
3
+ import { hideMemory } from './mate-memory.js';
3
4
  import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
4
5
 
5
6
  var getMateWs = null;
@@ -281,6 +282,7 @@ function openEditor(fileName, content) {
281
282
  export function showKnowledge() {
282
283
  visible = true;
283
284
  hideNotes();
285
+ hideMemory();
284
286
 
285
287
  // Toggle sidebar panels: hide conversations, show knowledge file list
286
288
  if (conversationsPanel) conversationsPanel.classList.add("hidden");
@@ -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
+ }