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.
- package/README.md +51 -91
- package/bin/cli.js +49 -14
- package/lib/builtin-mates.js +360 -0
- package/lib/cli-sessions.js +3 -3
- package/lib/config.js +30 -4
- package/lib/daemon.js +28 -5
- package/lib/mates.js +169 -7
- package/lib/notes.js +20 -0
- package/lib/os-users.js +71 -2
- package/lib/project.js +903 -228
- package/lib/public/app.js +249 -69
- package/lib/public/css/icon-strip.css +55 -2
- package/lib/public/css/input.css +50 -30
- 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 +65 -14
- package/lib/public/mates/ally.png +0 -0
- package/lib/public/mates/sage.jpg +0 -0
- package/lib/public/mates/scout.png +0 -0
- package/lib/public/modules/command-palette.js +44 -4
- package/lib/public/modules/filebrowser.js +2 -0
- package/lib/public/modules/input.js +158 -16
- 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 +507 -21
- 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 +122 -61
- package/lib/server.js +209 -13
- package/lib/sessions.js +4 -4
- package/lib/users.js +89 -0
- 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:
|
|
107
|
-
sendMention(mention.mateId,
|
|
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
|
-
//
|
|
127
|
-
if (
|
|
128
|
-
var resetsAt =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|