clay-server 2.14.0 → 2.15.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.
@@ -3,7 +3,6 @@ import { hideNotes } from './sticky-notes.js';
3
3
  import { renderMarkdown, highlightCodeBlocks } from './markdown.js';
4
4
 
5
5
  var getMateWs = null;
6
- var containerEl = null;
7
6
  var filesEl = null;
8
7
  var sidebarBtn = null;
9
8
  var countBadge = null;
@@ -16,7 +15,15 @@ var knowledgePanel = null;
16
15
  var knowledgeBackBtn = null;
17
16
  var knowledgeAddSidebarBtn = null;
18
17
 
18
+ // Viewer elements
19
+ var viewerEl = null;
20
+ var viewerNameEl = null;
21
+ var viewerContentEl = null;
22
+ var viewerEditBtn = null;
23
+ var viewerCloseBtn = null;
24
+
19
25
  // Editor elements
26
+ var editorEl = null;
20
27
  var activeNameEl = null;
21
28
  var editorNameEl = null;
22
29
  var editorContentEl = null;
@@ -28,12 +35,18 @@ var editorHighlightPre = null;
28
35
  var previewTimer = null;
29
36
  var editorExtEl = null;
30
37
  var nameGroupEl = null;
38
+
39
+ // State
31
40
  var editingFile = null;
41
+ var editingCommon = false;
42
+ var editingOwnMateId = null;
43
+ var viewingContent = "";
32
44
  var dirty = false;
45
+ var mode = "none"; // "none" | "viewer" | "editor"
46
+ var pendingEditMode = false;
33
47
 
34
48
  export function initMateKnowledge(mateWsGetter) {
35
49
  getMateWs = mateWsGetter;
36
- containerEl = document.getElementById("mate-knowledge-container");
37
50
  filesEl = document.getElementById("mate-knowledge-files");
38
51
  sidebarBtn = document.getElementById("mate-knowledge-btn");
39
52
  countBadge = document.getElementById("mate-knowledge-count");
@@ -44,7 +57,15 @@ export function initMateKnowledge(mateWsGetter) {
44
57
  knowledgeBackBtn = document.getElementById("mate-knowledge-back-btn");
45
58
  knowledgeAddSidebarBtn = document.getElementById("mate-knowledge-add-sidebar-btn");
46
59
 
60
+ // Viewer
61
+ viewerEl = document.getElementById("mate-knowledge-viewer");
62
+ viewerNameEl = document.getElementById("mate-knowledge-viewer-name");
63
+ viewerContentEl = document.getElementById("mate-knowledge-viewer-content");
64
+ viewerEditBtn = document.getElementById("mate-knowledge-viewer-edit-btn");
65
+ viewerCloseBtn = document.getElementById("mate-knowledge-viewer-close-btn");
66
+
47
67
  // Editor
68
+ editorEl = document.getElementById("mate-knowledge-editor");
48
69
  activeNameEl = document.getElementById("mate-knowledge-active-name");
49
70
  editorNameEl = document.getElementById("mate-knowledge-editor-name");
50
71
  editorContentEl = document.getElementById("mate-knowledge-editor-content");
@@ -66,19 +87,33 @@ export function initMateKnowledge(mateWsGetter) {
66
87
  knowledgeBackBtn.addEventListener("click", hideKnowledge);
67
88
  }
68
89
 
90
+ // Viewer buttons
91
+ if (viewerEditBtn) {
92
+ viewerEditBtn.addEventListener("click", function () {
93
+ switchToEditor();
94
+ });
95
+ }
96
+ if (viewerCloseBtn) {
97
+ viewerCloseBtn.addEventListener("click", function () {
98
+ closePanel();
99
+ });
100
+ }
101
+
102
+ // Editor close
69
103
  var closeBtn = document.getElementById("mate-knowledge-close-btn");
70
104
  if (closeBtn) {
71
105
  closeBtn.addEventListener("click", function () {
72
- // Close editor, keep sidebar file list open
73
- if (containerEl) containerEl.classList.add("hidden");
74
- editingFile = null;
75
- renderFileList();
106
+ closePanel();
76
107
  });
77
108
  }
78
109
 
79
110
  if (knowledgeAddSidebarBtn) {
80
111
  knowledgeAddSidebarBtn.addEventListener("click", function () {
81
- selectFile(null, "");
112
+ // New file goes straight to editor
113
+ editingFile = null;
114
+ editingCommon = false;
115
+ viewingContent = "";
116
+ openEditor(null, "");
82
117
  });
83
118
  }
84
119
 
@@ -91,7 +126,7 @@ export function initMateKnowledge(mateWsGetter) {
91
126
  if (ws && ws.readyState === 1) {
92
127
  ws.send(JSON.stringify({ type: "knowledge_delete", name: editingFile }));
93
128
  }
94
- selectFile(null, "");
129
+ closePanel();
95
130
  }
96
131
  });
97
132
  }
@@ -128,6 +163,91 @@ export function initMateKnowledge(mateWsGetter) {
128
163
  }
129
164
  }
130
165
 
166
+ // --- Mode switching ---
167
+
168
+ function closePanel() {
169
+ if (viewerEl) viewerEl.classList.add("hidden");
170
+ if (editorEl) editorEl.classList.add("hidden");
171
+ editingFile = null;
172
+ mode = "none";
173
+ renderFileList();
174
+ }
175
+
176
+ function showViewer(fileName, content) {
177
+ mode = "viewer";
178
+ if (viewerEl) viewerEl.classList.remove("hidden");
179
+ if (editorEl) editorEl.classList.add("hidden");
180
+
181
+ if (viewerNameEl) viewerNameEl.textContent = fileName.replace(/\.(md|jsonl)$/, "");
182
+
183
+ // Hide edit button for common files from other mates
184
+ if (viewerEditBtn) {
185
+ viewerEditBtn.style.display = editingCommon ? "none" : "";
186
+ }
187
+
188
+ if (viewerContentEl) {
189
+ if (fileName.endsWith(".jsonl")) {
190
+ viewerContentEl.innerHTML = buildJsonlTable(content);
191
+ } else {
192
+ viewerContentEl.innerHTML = renderMarkdown(content || "");
193
+ highlightCodeBlocks(viewerContentEl);
194
+ }
195
+ }
196
+
197
+ renderFileList();
198
+ }
199
+
200
+ function switchToEditor() {
201
+ if (!editingFile || editingCommon) return;
202
+ openEditor(editingFile, viewingContent);
203
+ }
204
+
205
+ function openEditor(fileName, content) {
206
+ mode = "editor";
207
+ dirty = false;
208
+ if (viewerEl) viewerEl.classList.add("hidden");
209
+ if (editorEl) editorEl.classList.remove("hidden");
210
+
211
+ // Restore editor pane (may have been hidden by JSONL viewer)
212
+ if (editorContentEl) editorContentEl.style.display = "";
213
+ if (editorHighlightPre) editorHighlightPre.style.display = "";
214
+ if (editorSaveBtn) editorSaveBtn.style.display = "";
215
+
216
+ // Update active name / name input group
217
+ if (fileName) {
218
+ if (activeNameEl) { activeNameEl.textContent = fileName.replace(/\.md$/, ""); activeNameEl.classList.remove("hidden"); }
219
+ if (nameGroupEl) nameGroupEl.classList.add("hidden");
220
+ } else {
221
+ if (activeNameEl) activeNameEl.classList.add("hidden");
222
+ if (nameGroupEl) nameGroupEl.classList.remove("hidden");
223
+ if (editorNameEl) editorNameEl.value = "";
224
+ }
225
+
226
+ if (editorContentEl) {
227
+ editorContentEl.value = content || "";
228
+ editorContentEl.placeholder = fileName ? "" : "Start writing...";
229
+ editorContentEl.readOnly = false;
230
+ }
231
+ if (editorDeleteBtn) {
232
+ editorDeleteBtn.style.display = fileName ? "" : "none";
233
+ }
234
+ if (editorSaveBtn) {
235
+ editorSaveBtn.disabled = true;
236
+ }
237
+
238
+ updateHighlight();
239
+ updatePreview();
240
+ renderFileList();
241
+
242
+ if (!fileName && editorNameEl) {
243
+ editorNameEl.focus();
244
+ } else if (editorContentEl) {
245
+ editorContentEl.focus();
246
+ }
247
+ }
248
+
249
+ // --- Public API ---
250
+
131
251
  export function showKnowledge() {
132
252
  visible = true;
133
253
  hideNotes();
@@ -149,9 +269,8 @@ export function hideKnowledge() {
149
269
  if (knowledgePanel) knowledgePanel.classList.add("hidden");
150
270
  if (sidebarBtn) sidebarBtn.classList.remove("active");
151
271
 
152
- // Hide editor/preview and reset state
153
- if (containerEl) containerEl.classList.add("hidden");
154
- editingFile = null;
272
+ // Hide everything and reset state
273
+ closePanel();
155
274
  cachedFiles = [];
156
275
 
157
276
  // Reset badge
@@ -188,10 +307,37 @@ export function renderKnowledgeList(files) {
188
307
  renderFileList();
189
308
  }
190
309
 
310
+ export function handleKnowledgeContent(msg) {
311
+ editingCommon = !!msg.common;
312
+ editingOwnMateId = msg.ownMateId || null;
313
+ editingFile = msg.name || null;
314
+ viewingContent = msg.content || "";
315
+
316
+ // If triggered from three-dots Edit, go straight to editor
317
+ if (pendingEditMode && !editingCommon) {
318
+ pendingEditMode = false;
319
+ openEditor(msg.name, viewingContent);
320
+ return;
321
+ }
322
+ pendingEditMode = false;
323
+
324
+ // Otherwise open in viewer mode first
325
+ showViewer(msg.name, viewingContent);
326
+ }
327
+
328
+ // --- File list rendering ---
329
+
191
330
  function renderFileList() {
192
331
  if (!filesEl) return;
193
332
  filesEl.innerHTML = "";
194
333
 
334
+ var commonFiles = [];
335
+ var myFiles = [];
336
+ for (var i = 0; i < cachedFiles.length; i++) {
337
+ if (cachedFiles[i].common) commonFiles.push(cachedFiles[i]);
338
+ else myFiles.push(cachedFiles[i]);
339
+ }
340
+
195
341
  if (cachedFiles.length === 0) {
196
342
  var empty = document.createElement("div");
197
343
  empty.className = "mate-knowledge-empty";
@@ -199,16 +345,35 @@ function renderFileList() {
199
345
  filesEl.appendChild(empty);
200
346
  }
201
347
 
202
- for (var i = 0; i < cachedFiles.length; i++) {
203
- filesEl.appendChild(renderFileItem(cachedFiles[i]));
348
+ if (commonFiles.length > 0) {
349
+ var header = document.createElement("div");
350
+ header.className = "mate-knowledge-section-header";
351
+ header.textContent = "Common Knowledge";
352
+ filesEl.appendChild(header);
353
+ for (var c = 0; c < commonFiles.length; c++) {
354
+ filesEl.appendChild(renderFileItem(commonFiles[c]));
355
+ }
204
356
  }
357
+
358
+ if (myFiles.length > 0) {
359
+ if (commonFiles.length > 0) {
360
+ var myHeader = document.createElement("div");
361
+ myHeader.className = "mate-knowledge-section-header";
362
+ myHeader.textContent = "My Knowledge";
363
+ filesEl.appendChild(myHeader);
364
+ }
365
+ for (var m = 0; m < myFiles.length; m++) {
366
+ filesEl.appendChild(renderFileItem(myFiles[m]));
367
+ }
368
+ }
369
+
205
370
  refreshIcons();
206
371
  }
207
372
 
208
373
  function renderFileItem(file) {
209
374
  var item = document.createElement("div");
210
375
  item.className = "mate-knowledge-file-item";
211
- if (editingFile === file.name) item.classList.add("active");
376
+ if (editingFile === file.name && editingCommon === !!file.common) item.classList.add("active");
212
377
 
213
378
  var icon = document.createElement("span");
214
379
  icon.className = "mate-knowledge-file-icon";
@@ -218,7 +383,11 @@ function renderFileItem(file) {
218
383
  var name = document.createElement("span");
219
384
  name.className = "mate-knowledge-file-name";
220
385
  var isJsonl = file.name.endsWith(".jsonl");
221
- name.textContent = file.name.replace(/\.(md|jsonl)$/, "");
386
+ var displayName = file.name.replace(/\.(md|jsonl)$/, "");
387
+ if (file.common && file.ownerName) {
388
+ displayName += " (" + file.ownerName + ")";
389
+ }
390
+ name.textContent = displayName;
222
391
  item.appendChild(name);
223
392
 
224
393
  if (isJsonl) {
@@ -228,131 +397,92 @@ function renderFileItem(file) {
228
397
  item.appendChild(badge);
229
398
  }
230
399
 
231
- item.addEventListener("click", (function (fname) {
400
+ if (file.common) {
401
+ var commonBadge = document.createElement("span");
402
+ commonBadge.className = "mate-knowledge-file-badge common";
403
+ commonBadge.textContent = "common";
404
+ item.appendChild(commonBadge);
405
+ } else if (file.promoted) {
406
+ var promotedBadge = document.createElement("span");
407
+ promotedBadge.className = "mate-knowledge-file-badge promoted";
408
+ promotedBadge.textContent = "common";
409
+ item.appendChild(promotedBadge);
410
+ }
411
+
412
+ // Three-dots menu (only for own files, not common files from other mates)
413
+ if (!file.common) {
414
+ var dotsBtn = document.createElement("button");
415
+ dotsBtn.className = "mate-knowledge-dots-btn";
416
+ dotsBtn.innerHTML = iconHtml("ellipsis");
417
+ dotsBtn.addEventListener("click", (function (f, btn) {
418
+ return function (e) {
419
+ e.stopPropagation();
420
+ showFileMenu(f, btn);
421
+ };
422
+ })(file, dotsBtn));
423
+ item.appendChild(dotsBtn);
424
+ }
425
+
426
+ item.addEventListener("click", (function (f) {
232
427
  return function () {
233
428
  var ws = getMateWs ? getMateWs() : null;
234
429
  if (ws && ws.readyState === 1) {
235
- ws.send(JSON.stringify({ type: "knowledge_read", name: fname }));
430
+ var msg = { type: "knowledge_read", name: f.name };
431
+ if (f.common) {
432
+ msg.common = true;
433
+ msg.ownMateId = f.ownMateId;
434
+ }
435
+ ws.send(JSON.stringify(msg));
236
436
  }
237
437
  };
238
- })(file.name));
438
+ })(file));
239
439
 
240
440
  return item;
241
441
  }
242
442
 
243
- export function handleKnowledgeContent(msg) {
244
- if (msg.name && msg.name.endsWith(".jsonl")) {
245
- selectJsonlFile(msg.name, msg.content || "");
246
- } else {
247
- selectFile(msg.name, msg.content || "");
248
- }
249
- }
250
-
251
- function selectJsonlFile(fileName, content) {
252
- editingFile = fileName;
253
- dirty = false;
254
-
255
- if (containerEl) containerEl.classList.remove("hidden");
256
-
257
- // Show name, hide name input group
258
- if (activeNameEl) { activeNameEl.textContent = fileName.replace(/\.jsonl$/, ""); activeNameEl.classList.remove("hidden"); }
259
- if (nameGroupEl) nameGroupEl.classList.add("hidden");
443
+ // --- JSONL table builder ---
260
444
 
261
- // Hide editor pane and controls
262
- if (editorContentEl) editorContentEl.value = "";
263
- if (editorSaveBtn) editorSaveBtn.style.display = "none";
264
- if (editorDeleteBtn) editorDeleteBtn.style.display = "";
265
- if (editorHighlightPre) editorHighlightPre.style.display = "none";
266
- if (editorContentEl) editorContentEl.style.display = "none";
267
-
268
- // Build table from JSONL
269
- if (editorPreviewEl) {
270
- var lines = content.trim().split("\n").filter(function (l) { return l.trim(); });
271
- if (lines.length === 0) {
272
- editorPreviewEl.innerHTML = "<p style=\"opacity:0.5\">No data entries yet</p>";
273
- } else {
274
- var rows = [];
275
- var allKeys = [];
276
- var keySet = {};
277
- for (var i = 0; i < lines.length; i++) {
278
- try {
279
- var obj = JSON.parse(lines[i]);
280
- rows.push(obj);
281
- var keys = Object.keys(obj);
282
- for (var k = 0; k < keys.length; k++) {
283
- if (!keySet[keys[k]]) { keySet[keys[k]] = true; allKeys.push(keys[k]); }
284
- }
285
- } catch (e) { /* skip malformed lines */ }
286
- }
287
- var html = "<table class=\"mate-knowledge-jsonl-table\"><thead><tr>";
288
- for (var c = 0; c < allKeys.length; c++) {
289
- html += "<th>" + escapeHtml(allKeys[c]) + "</th>";
290
- }
291
- html += "</tr></thead><tbody>";
292
- for (var r = 0; r < rows.length; r++) {
293
- html += "<tr>";
294
- for (var c = 0; c < allKeys.length; c++) {
295
- var val = rows[r][allKeys[c]];
296
- var cell = val === undefined ? "" : typeof val === "object" ? JSON.stringify(val) : String(val);
297
- html += "<td>" + escapeHtml(cell) + "</td>";
298
- }
299
- html += "</tr>";
445
+ function buildJsonlTable(content) {
446
+ var lines = content.trim().split("\n").filter(function (l) { return l.trim(); });
447
+ if (lines.length === 0) {
448
+ return "<p style=\"opacity:0.5\">No data entries yet</p>";
449
+ }
450
+ var rows = [];
451
+ var allKeys = [];
452
+ var keySet = {};
453
+ for (var i = 0; i < lines.length; i++) {
454
+ try {
455
+ var obj = JSON.parse(lines[i]);
456
+ rows.push(obj);
457
+ var keys = Object.keys(obj);
458
+ for (var k = 0; k < keys.length; k++) {
459
+ if (!keySet[keys[k]]) { keySet[keys[k]] = true; allKeys.push(keys[k]); }
300
460
  }
301
- html += "</tbody></table>";
302
- editorPreviewEl.innerHTML = html;
461
+ } catch (e) { /* skip malformed lines */ }
462
+ }
463
+ var html = "<table class=\"mate-knowledge-jsonl-table\"><thead><tr>";
464
+ for (var c = 0; c < allKeys.length; c++) {
465
+ html += "<th>" + escapeHtml(allKeys[c]) + "</th>";
466
+ }
467
+ html += "</tr></thead><tbody>";
468
+ for (var r = 0; r < rows.length; r++) {
469
+ html += "<tr>";
470
+ for (var c2 = 0; c2 < allKeys.length; c2++) {
471
+ var val = rows[r][allKeys[c2]];
472
+ var cell = val === undefined ? "" : typeof val === "object" ? JSON.stringify(val) : String(val);
473
+ html += "<td>" + escapeHtml(cell) + "</td>";
303
474
  }
475
+ html += "</tr>";
304
476
  }
305
-
306
- renderFileList();
477
+ html += "</tbody></table>";
478
+ return html;
307
479
  }
308
480
 
309
481
  function escapeHtml(str) {
310
482
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
311
483
  }
312
484
 
313
- function selectFile(fileName, content) {
314
- editingFile = fileName || null;
315
- dirty = false;
316
-
317
- // Restore editor pane (may have been hidden by JSONL viewer)
318
- if (editorContentEl) editorContentEl.style.display = "";
319
- if (editorHighlightPre) editorHighlightPre.style.display = "";
320
- if (editorSaveBtn) editorSaveBtn.style.display = "";
321
-
322
- // Show editor when a file is selected or new file created
323
- if (containerEl) containerEl.classList.remove("hidden");
324
-
325
- // Update active name / name input group
326
- if (fileName) {
327
- if (activeNameEl) { activeNameEl.textContent = fileName.replace(/\.md$/, ""); activeNameEl.classList.remove("hidden"); }
328
- if (nameGroupEl) nameGroupEl.classList.add("hidden");
329
- } else {
330
- if (activeNameEl) activeNameEl.classList.add("hidden");
331
- if (nameGroupEl) nameGroupEl.classList.remove("hidden");
332
- if (editorNameEl) editorNameEl.value = "";
333
- }
334
-
335
- if (editorContentEl) {
336
- editorContentEl.value = content || "";
337
- editorContentEl.placeholder = fileName ? "" : "Start writing...";
338
- }
339
- if (editorDeleteBtn) {
340
- editorDeleteBtn.style.display = fileName ? "" : "none";
341
- }
342
- if (editorSaveBtn) {
343
- editorSaveBtn.disabled = true;
344
- }
345
-
346
- updateHighlight();
347
- updatePreview();
348
- renderFileList();
349
-
350
- if (!fileName && editorNameEl) {
351
- editorNameEl.focus();
352
- } else if (editorContentEl) {
353
- editorContentEl.focus();
354
- }
355
- }
485
+ // --- Editor helpers ---
356
486
 
357
487
  function updateHighlight() {
358
488
  if (!editorHighlightEl || !editorContentEl) return;
@@ -397,6 +527,7 @@ function saveKnowledge() {
397
527
  ws.send(JSON.stringify({ type: "knowledge_save", name: name, content: content }));
398
528
  }
399
529
  editingFile = name;
530
+ viewingContent = content;
400
531
  dirty = false;
401
532
  if (editorSaveBtn) editorSaveBtn.disabled = true;
402
533
  if (activeNameEl) { activeNameEl.textContent = name.replace(/\.md$/, ""); activeNameEl.classList.remove("hidden"); }
@@ -535,3 +666,126 @@ function applyFormat(textarea, action) {
535
666
 
536
667
  hidePopover();
537
668
  }
669
+
670
+ // --- File context menu (three-dots popover) ---
671
+
672
+ var fileMenuEl = null;
673
+ var fileMenuHideTimer = null;
674
+
675
+ function showFileMenu(file, anchorBtn) {
676
+ hideFileMenu();
677
+
678
+ fileMenuEl = document.createElement("div");
679
+ fileMenuEl.className = "mate-knowledge-file-menu";
680
+
681
+ // Edit
682
+ var editItem = document.createElement("button");
683
+ editItem.className = "mate-knowledge-file-menu-item";
684
+ editItem.innerHTML = iconHtml("pencil") + "<span>Edit</span>";
685
+ editItem.addEventListener("click", function (e) {
686
+ e.stopPropagation();
687
+ hideFileMenu();
688
+ // Request file content, then open editor directly
689
+ editingCommon = false;
690
+ editingFile = file.name;
691
+ var ws = getMateWs ? getMateWs() : null;
692
+ if (ws && ws.readyState === 1) {
693
+ ws.send(JSON.stringify({ type: "knowledge_read", name: file.name }));
694
+ // Override handleKnowledgeContent to go straight to editor
695
+ pendingEditMode = true;
696
+ }
697
+ });
698
+ fileMenuEl.appendChild(editItem);
699
+
700
+ // Promote / Depromote
701
+ if (!file.promoted) {
702
+ var promoteItem = document.createElement("button");
703
+ promoteItem.className = "mate-knowledge-file-menu-item";
704
+ promoteItem.innerHTML = iconHtml("share-2") + "<span>Share to all mates</span>";
705
+ promoteItem.addEventListener("click", function (e) {
706
+ e.stopPropagation();
707
+ var ws = getMateWs ? getMateWs() : null;
708
+ if (ws && ws.readyState === 1) {
709
+ ws.send(JSON.stringify({ type: "knowledge_promote", name: file.name }));
710
+ }
711
+ hideFileMenu();
712
+ });
713
+ fileMenuEl.appendChild(promoteItem);
714
+ } else {
715
+ var depromoteItem = document.createElement("button");
716
+ depromoteItem.className = "mate-knowledge-file-menu-item";
717
+ depromoteItem.innerHTML = iconHtml("x-circle") + "<span>Unshare</span>";
718
+ depromoteItem.addEventListener("click", function (e) {
719
+ e.stopPropagation();
720
+ var ws = getMateWs ? getMateWs() : null;
721
+ if (ws && ws.readyState === 1) {
722
+ ws.send(JSON.stringify({ type: "knowledge_depromote", name: file.name }));
723
+ }
724
+ hideFileMenu();
725
+ });
726
+ fileMenuEl.appendChild(depromoteItem);
727
+ }
728
+
729
+ // Delete (disabled if promoted, must unshare first)
730
+ var deleteItem = document.createElement("button");
731
+ deleteItem.className = "mate-knowledge-file-menu-item menu-item-danger";
732
+ if (file.promoted) {
733
+ deleteItem.innerHTML = iconHtml("trash-2") + "<span>Unshare before deleting</span>";
734
+ deleteItem.disabled = true;
735
+ deleteItem.style.opacity = "0.4";
736
+ deleteItem.style.cursor = "default";
737
+ } else {
738
+ deleteItem.innerHTML = iconHtml("trash-2") + "<span>Delete</span>";
739
+ deleteItem.addEventListener("click", function (e) {
740
+ e.stopPropagation();
741
+ var ws = getMateWs ? getMateWs() : null;
742
+ if (ws && ws.readyState === 1) {
743
+ ws.send(JSON.stringify({ type: "knowledge_delete", name: file.name }));
744
+ }
745
+ hideFileMenu();
746
+ if (editingFile === file.name) closePanel();
747
+ });
748
+ }
749
+ fileMenuEl.appendChild(deleteItem);
750
+
751
+ // Position below the anchor button
752
+ document.body.appendChild(fileMenuEl);
753
+ refreshIcons(fileMenuEl);
754
+
755
+ var btnRect = anchorBtn.getBoundingClientRect();
756
+ var menuWidth = fileMenuEl.offsetWidth || 160;
757
+ var left = btnRect.right - menuWidth;
758
+ var top = btnRect.bottom + 4;
759
+
760
+ // Keep within viewport
761
+ if (left < 8) left = 8;
762
+ if (top + fileMenuEl.offsetHeight > window.innerHeight - 8) {
763
+ top = btnRect.top - fileMenuEl.offsetHeight - 4;
764
+ }
765
+
766
+ fileMenuEl.style.left = left + "px";
767
+ fileMenuEl.style.top = top + "px";
768
+
769
+ // Close on outside click (delayed to avoid immediate close)
770
+ setTimeout(function () {
771
+ document.addEventListener("click", onFileMenuOutsideClick, true);
772
+ }, 0);
773
+ }
774
+
775
+ function hideFileMenu() {
776
+ if (fileMenuEl) {
777
+ fileMenuEl.remove();
778
+ fileMenuEl = null;
779
+ }
780
+ document.removeEventListener("click", onFileMenuOutsideClick, true);
781
+ if (fileMenuHideTimer) {
782
+ clearTimeout(fileMenuHideTimer);
783
+ fileMenuHideTimer = null;
784
+ }
785
+ }
786
+
787
+ function onFileMenuOutsideClick(e) {
788
+ if (fileMenuEl && !fileMenuEl.contains(e.target)) {
789
+ hideFileMenu();
790
+ }
791
+ }
@@ -2,6 +2,7 @@ import { escapeHtml } from './utils.js';
2
2
  import { iconHtml, refreshIcons } from './icons.js';
3
3
  import { hideKnowledge } from './mate-knowledge.js';
4
4
  import { isSchedulerOpen, closeScheduler } from './scheduler.js';
5
+ import { hideNotes } from './sticky-notes.js';
5
6
  import { openSearch as openSessionSearch } from './session-search.js';
6
7
 
7
8
  var getMateWs = null;
@@ -397,11 +398,7 @@ function renderMateSessionItem(s) {
397
398
  // Close any open panels
398
399
  hideKnowledge();
399
400
  if (isSchedulerOpen()) closeScheduler();
400
- var stickyBtn = document.getElementById("sticky-notes-sidebar-btn");
401
- var stickyPanel = document.getElementById("sticky-notes-panel");
402
- if (stickyPanel && !stickyPanel.classList.contains("hidden")) {
403
- if (stickyBtn) stickyBtn.click();
404
- }
401
+ hideNotes();
405
402
  var pendingQuery = searchQuery || "";
406
403
  var ws = getMateWs ? getMateWs() : null;
407
404
  if (ws && ws.readyState === 1) {
@@ -415,7 +412,6 @@ function renderMateSessionItem(s) {
415
412
  el.classList.add("active");
416
413
  // Open in-session search with the sidebar search query
417
414
  if (pendingQuery) {
418
- closeSearch();
419
415
  setTimeout(function () { openSessionSearch(pendingQuery); }, 400);
420
416
  }
421
417
  };