// 记忆 modal:只读两栏 master-detail。左栏列 Core(常驻)+ Extended 各条(带 description),
// 右栏渲染选中项原文(markdown)。左侧 rail 底部「记忆」按钮触发。
// **只读** —— 改记忆全走对话(agent 自管,见 core/memory.py 的 _CONTRACT)。
// GUI 当"眼睛"不当"手":看全貌靠直接读 FS(便宜、是地面真相),改靠模型(DESIGN §3.7)。
// 后端:GET /v1/memory(全貌)、GET /v1/memory/extended/{filename}(单篇原文)。
import { $ } from "./dom.js";
import { api } from "./api.js";
import { escapeHtml } from "./format.js";
import { renderMd, highlightIn } from "./markdown.js";
const PLACEHOLDER = '
← 选 Core 或某条专题查看
';
let _cache = null; // 本次打开的 {core, extended} 快照;渲染右栏 Core 复用,免二次请求
function openMemoryModal() {
$("memory-modal").classList.add("show");
$("mem-detail").innerHTML = PLACEHOLDER;
renderList();
}
export function closeMemoryModal() {
$("memory-modal").classList.remove("show");
}
async function renderList() {
const list = $("mem-list");
list.innerHTML = '加载中…
';
let data;
try {
data = await api("GET", "/v1/memory");
} catch (e) {
list.innerHTML = `加载失败: ${escapeHtml(e.message)}
`;
return;
}
_cache = data;
const ext = data.extended || [];
const coreEmpty = !data.core || !data.core.trim();
let html = '常驻 (Core)
';
html += `
core.md${coreEmpty ? ' 空' : ""}
每轮注入,跨任务高频事实
`;
html += `专题 (Extended ${ext.length})
`;
html += ext.length
? ext
.map(
(e) => `
${escapeHtml(e.filename)}
${escapeHtml(e.description || "")}
`
)
.join("")
: '还没有。在对话里让我「记住某专题」即可。
';
list.innerHTML = html;
}
function highlightSel(itemEl) {
$("mem-list").querySelectorAll(".sk-item.active").forEach((el) => el.classList.remove("active"));
if (itemEl) itemEl.classList.add("active");
}
function showCore(itemEl) {
highlightSel(itemEl);
const detail = $("mem-detail");
const core = (_cache && _cache.core) || "";
detail.innerHTML = core.trim()
? 'core.md常驻
' +
`${renderMd(core)}
`
: 'core.md 还是空的。在对话里跟我说你的偏好 / 项目约定,我会记进来。
';
highlightIn(detail);
}
async function showExt(filename, itemEl) {
highlightSel(itemEl);
const detail = $("mem-detail");
detail.innerHTML = '加载中…
';
let data;
try {
data = await api("GET", "/v1/memory/extended/" + encodeURIComponent(filename));
} catch (e) {
detail.innerHTML = `加载失败: ${escapeHtml(e.message)}
`;
return;
}
detail.innerHTML =
`${escapeHtml(filename)}按需
` +
`${renderMd(data.content)}
`;
highlightIn(detail);
}
// ───── 顶层绑定 ─────
$("hd-memory").onclick = openMemoryModal;
$("mem-close").onclick = closeMemoryModal;
$("memory-modal").addEventListener("click", (e) => {
if (e.target.id === "memory-modal") closeMemoryModal(); // 点遮罩关闭
});
$("mem-list").addEventListener("click", (e) => {
const item = e.target.closest(".sk-item");
if (!item) return;
if (item.getAttribute("data-kind") === "core") showCore(item);
else showExt(item.getAttribute("data-file"), item);
});