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.
- package/README.md +25 -40
- package/lib/mates.js +159 -0
- package/lib/project.js +93 -8
- package/lib/public/app.js +5 -11
- package/lib/public/css/input.css +9 -1
- package/lib/public/css/mates.css +167 -13
- package/lib/public/css/session-search.css +9 -0
- package/lib/public/css/sidebar.css +0 -17
- package/lib/public/index.html +15 -1
- package/lib/public/modules/command-palette.js +47 -100
- package/lib/public/modules/mate-knowledge.js +375 -121
- package/lib/public/modules/mate-sidebar.js +2 -6
- package/lib/public/modules/scheduler.js +35 -16
- package/lib/public/modules/sidebar.js +0 -196
- package/lib/scheduler.js +5 -0
- package/lib/sessions.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
153
|
-
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
438
|
+
})(file));
|
|
239
439
|
|
|
240
440
|
return item;
|
|
241
441
|
}
|
|
242
442
|
|
|
243
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
var
|
|
275
|
-
|
|
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
|
-
|
|
302
|
-
|
|
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
|
-
|
|
477
|
+
html += "</tbody></table>";
|
|
478
|
+
return html;
|
|
307
479
|
}
|
|
308
480
|
|
|
309
481
|
function escapeHtml(str) {
|
|
310
482
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
311
483
|
}
|
|
312
484
|
|
|
313
|
-
|
|
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
|
-
|
|
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
|
};
|