mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
fix(ui): use proper memory node display data in task browser
This commit is contained in:
140
ui/panel.js
140
ui/panel.js
@@ -1538,13 +1538,47 @@ function _refreshTaskMemoryBrowser() {
|
|||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
const graph = _getGraph?.();
|
const graph = _getGraph?.();
|
||||||
if (!graph) {
|
const loadInfo = _getGraphPersistenceSnapshot();
|
||||||
|
if (!graph || !_canRenderGraphData(loadInfo)) {
|
||||||
el.innerHTML = '<div class="bme-memory-detail-empty">图谱未加载</div>';
|
el.innerHTML = '<div class="bme-memory-detail-empty">图谱未加载</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodes = graph.nodes || [];
|
const currentQuery = String(document.getElementById("bme-task-memory-search")?.value || "")
|
||||||
const sorted = nodes.slice().sort((a, b) => (b.importance || 0) - (a.importance || 0));
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
const currentFilter = document.getElementById("bme-task-memory-filter")?.value || "all";
|
||||||
|
|
||||||
|
let nodes = Array.isArray(graph.nodes)
|
||||||
|
? graph.nodes.filter((node) => !node?.archived)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (currentFilter !== "all") {
|
||||||
|
nodes = nodes.filter((node) => _matchesMemoryFilter(node, currentFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentQuery) {
|
||||||
|
nodes = nodes.filter((node) => {
|
||||||
|
const name = getNodeDisplayName(node).toLowerCase();
|
||||||
|
const snippet = _getNodeSnippet(node).toLowerCase();
|
||||||
|
const fieldsText = JSON.stringify(node?.fields || {}).toLowerCase();
|
||||||
|
return (
|
||||||
|
name.includes(currentQuery) ||
|
||||||
|
snippet.includes(currentQuery) ||
|
||||||
|
fieldsText.includes(currentQuery)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = nodes.slice().sort((a, b) => {
|
||||||
|
const importanceDiff = (b.importance || 5) - (a.importance || 5);
|
||||||
|
if (importanceDiff !== 0) return importanceDiff;
|
||||||
|
return (b.seqRange?.[1] ?? b.seq ?? 0) - (a.seqRange?.[1] ?? a.seq ?? 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!sorted.some((node) => node.id === currentSelectedMemoryNodeId)) {
|
||||||
|
currentSelectedMemoryNodeId = sorted[0]?.id || "";
|
||||||
|
}
|
||||||
|
|
||||||
const typeClass = (type) => {
|
const typeClass = (type) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -1557,27 +1591,25 @@ function _refreshTaskMemoryBrowser() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeLabel = (node) => {
|
|
||||||
if (node.scope === "characterPov") return "角色POV";
|
|
||||||
if (node.scope === "userPov") return "用户POV";
|
|
||||||
return node.type || "memory";
|
|
||||||
};
|
|
||||||
|
|
||||||
const listItems = sorted.map((node) => {
|
const listItems = sorted.map((node) => {
|
||||||
const sel = node.id === currentSelectedMemoryNodeId ? "selected" : "";
|
const sel = node.id === currentSelectedMemoryNodeId ? "selected" : "";
|
||||||
const preview = (node.description || "").slice(0, 80);
|
const preview = _getNodeSnippet(node);
|
||||||
|
const scopeBadge = buildScopeBadgeText(node.scope);
|
||||||
|
const metaText = _buildScopeMetaText(node);
|
||||||
|
const displayName = getNodeDisplayName(node);
|
||||||
return `
|
return `
|
||||||
<div class="bme-memory-node-item ${sel}" data-node-id="${_escHtml(node.id)}">
|
<div class="bme-memory-node-item ${sel}" data-node-id="${_escHtml(node.id)}">
|
||||||
<div class="bme-memory-node-item__header">
|
<div class="bme-memory-node-item__header">
|
||||||
<span class="bme-memory-node-item__type ${typeClass(node.type)}">${_escHtml(typeLabel(node))}</span>
|
<span class="bme-memory-node-item__type ${typeClass(node.type)}">${_escHtml(_typeLabel(node.type))}</span>
|
||||||
<span class="bme-memory-node-item__imp">IMP: ${typeof node.importance === "number" ? node.importance.toFixed(1) : "—"}</span>
|
<span class="bme-memory-node-item__imp">IMP: ${typeof node.importance === "number" ? node.importance.toFixed(1) : "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-memory-node-item__title">${_escHtml(node.label || node.id)}</div>
|
<div class="bme-memory-node-item__title">${_escHtml(displayName)}</div>
|
||||||
<div class="bme-memory-node-item__preview">${_escHtml(preview)}</div>
|
<div class="bme-memory-node-item__preview">${_escHtml(preview)}</div>
|
||||||
<div class="bme-memory-node-item__meta">
|
<div class="bme-memory-node-item__meta">
|
||||||
<span>SEQ: ${node.lastSeq ?? "—"}</span>
|
<span>${_escHtml(scopeBadge)}</span>
|
||||||
${node.region ? `<span>LOC: ${_escHtml(node.region)}</span>` : ""}
|
<span>SEQ: ${_formatMemoryInt(node.seqRange?.[1] ?? node.seq, 0)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
${metaText ? `<div class="bme-memory-node-item__meta">${_escHtml(metaText)}</div>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join("");
|
}).join("");
|
||||||
|
|
||||||
@@ -1587,17 +1619,17 @@ function _refreshTaskMemoryBrowser() {
|
|||||||
<div class="bme-memory-master-detail">
|
<div class="bme-memory-master-detail">
|
||||||
<div class="bme-memory-list-panel">
|
<div class="bme-memory-list-panel">
|
||||||
<div class="bme-memory-list-filters">
|
<div class="bme-memory-list-filters">
|
||||||
<input type="text" class="bme-search-input" id="bme-task-memory-search" placeholder="搜索记忆节点..." />
|
<input type="text" class="bme-search-input" id="bme-task-memory-search" placeholder="搜索记忆节点..." value="${_escHtml(currentQuery)}" />
|
||||||
<select class="bme-filter-select" id="bme-task-memory-filter">
|
<select class="bme-filter-select" id="bme-task-memory-filter">
|
||||||
<option value="all">全部</option>
|
<option value="all"${currentFilter === "all" ? " selected" : ""}>全部</option>
|
||||||
<option value="scope:objective">客观</option>
|
<option value="scope:objective"${currentFilter === "scope:objective" ? " selected" : ""}>客观</option>
|
||||||
<option value="scope:characterPov">角色 POV</option>
|
<option value="scope:characterPov"${currentFilter === "scope:characterPov" ? " selected" : ""}>角色 POV</option>
|
||||||
<option value="scope:userPov">用户 POV</option>
|
<option value="scope:userPov"${currentFilter === "scope:userPov" ? " selected" : ""}>用户 POV</option>
|
||||||
<option value="pov_memory">主观记忆</option>
|
<option value="pov_memory"${currentFilter === "pov_memory" ? " selected" : ""}>主观记忆</option>
|
||||||
<option value="event">事件</option>
|
<option value="event"${currentFilter === "event" ? " selected" : ""}>事件</option>
|
||||||
<option value="location">地点</option>
|
<option value="location"${currentFilter === "location" ? " selected" : ""}>地点</option>
|
||||||
<option value="thread">线索</option>
|
<option value="thread"${currentFilter === "thread" ? " selected" : ""}>线索</option>
|
||||||
<option value="rule">规则</option>
|
<option value="rule"${currentFilter === "rule" ? " selected" : ""}>规则</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-memory-list-scroll" id="bme-task-memory-list">
|
<div class="bme-memory-list-scroll" id="bme-task-memory-list">
|
||||||
@@ -1611,6 +1643,17 @@ function _refreshTaskMemoryBrowser() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
_bindTaskMemoryListClick();
|
_bindTaskMemoryListClick();
|
||||||
|
|
||||||
|
const searchInput = document.getElementById("bme-task-memory-search");
|
||||||
|
const filterSelect = document.getElementById("bme-task-memory-filter");
|
||||||
|
if (searchInput) {
|
||||||
|
let timer = null;
|
||||||
|
searchInput.addEventListener("input", () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => _refreshTaskMemoryBrowser(), 180);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
filterSelect?.addEventListener("change", () => _refreshTaskMemoryBrowser());
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bindTaskMemoryListClick() {
|
function _bindTaskMemoryListClick() {
|
||||||
@@ -1632,32 +1675,55 @@ function _renderMemoryDetailPanel(node, graph) {
|
|||||||
if (!node) return '<div class="bme-memory-detail-empty"><i class="fa-solid fa-arrow-left" style="margin-right:6px"></i>选择左侧节点查看详情</div>';
|
if (!node) return '<div class="bme-memory-detail-empty"><i class="fa-solid fa-arrow-left" style="margin-right:6px"></i>选择左侧节点查看详情</div>';
|
||||||
|
|
||||||
const edges = (graph?.edges || []).filter((e) => e.source === node.id || e.target === node.id);
|
const edges = (graph?.edges || []).filter((e) => e.source === node.id || e.target === node.id);
|
||||||
|
const fields = node?.fields || {};
|
||||||
|
const detailSummary = _getNodeSnippet(node);
|
||||||
|
const scopeMeta = _buildScopeMetaText(node);
|
||||||
|
const scopeBadge = buildScopeBadgeText(node.scope);
|
||||||
|
const displayName = getNodeDisplayName(node);
|
||||||
const badges = [
|
const badges = [
|
||||||
node.type ? `<span class="bme-memory-node-item__type ${node.type === "pov_memory" || node.type === "character" ? "type-character" : node.type === "event" ? "type-event" : node.type === "location" ? "type-location" : "type-default"}">${_escHtml(node.type)}</span>` : "",
|
node.type ? `<span class="bme-memory-node-item__type ${node.type === "pov_memory" || node.type === "character" ? "type-character" : node.type === "event" ? "type-event" : node.type === "location" ? "type-location" : node.type === "rule" ? "type-rule" : node.type === "thread" ? "type-thread" : "type-default"}">${_escHtml(_typeLabel(node.type))}</span>` : "",
|
||||||
node.scope ? `<span class="bme-memory-node-item__type type-default">${_escHtml(node.scope)}</span>` : "",
|
scopeBadge ? `<span class="bme-memory-node-item__type type-default">${_escHtml(scopeBadge)}</span>` : "",
|
||||||
node.archived ? '<span class="bme-memory-node-item__type type-default">ARCHIVED</span>' : "",
|
node.archived ? '<span class="bme-memory-node-item__type type-default">ARCHIVED</span>' : "",
|
||||||
].filter(Boolean).join("");
|
].filter(Boolean).join("");
|
||||||
|
|
||||||
const fields = [
|
const detailFields = [
|
||||||
["ID", node.id],
|
["ID", node.id],
|
||||||
["Owner", node.owner || "—"],
|
["名称", displayName],
|
||||||
["Region", node.region || "—"],
|
["范围", scopeMeta || scopeBadge || "—"],
|
||||||
["Importance", typeof node.importance === "number" ? node.importance.toFixed(2) : "—"],
|
["重要度", _formatMemoryMetricNumber(node.importance, { fallback: 5, maxFrac: 2 })],
|
||||||
["Last Seq", node.lastSeq ?? "—"],
|
["访问次数", _formatMemoryInt(node.accessCount, 0)],
|
||||||
["Created", node.createdAt || "—"],
|
["最后序列", _formatMemoryInt(node.seqRange?.[1] ?? node.seq, 0)],
|
||||||
["Updated", node.updatedAt || "—"],
|
["创建时间", node.createdAt || "—"],
|
||||||
|
["更新时间", node.updatedAt || "—"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const extraFieldRows = Object.entries(fields)
|
||||||
|
.filter(([key]) => !["embedding", "name", "title", "summary"].includes(key))
|
||||||
|
.slice(0, 6)
|
||||||
|
.map(([key, value]) => {
|
||||||
|
let text = "—";
|
||||||
|
if (typeof value === "string") {
|
||||||
|
text = value;
|
||||||
|
} else if (value !== undefined && value !== null) {
|
||||||
|
try {
|
||||||
|
text = JSON.stringify(value);
|
||||||
|
} catch {
|
||||||
|
text = String(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [key, text];
|
||||||
|
});
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="bme-memory-detail__title">${_escHtml(node.label || node.id)}</div>
|
<div class="bme-memory-detail__title">${_escHtml(displayName)}</div>
|
||||||
<div class="bme-memory-detail__badges">${badges}</div>
|
<div class="bme-memory-detail__badges">${badges}</div>
|
||||||
<div class="bme-memory-detail__desc">${_escHtml(node.description || "无描述")}</div>
|
<div class="bme-memory-detail__desc">${_escHtml(detailSummary || "无补充字段")}</div>
|
||||||
<div class="bme-memory-detail__fields">
|
<div class="bme-memory-detail__fields">
|
||||||
${fields.map(([k, v]) => `<div class="bme-memory-detail__field-row"><span class="bme-memory-detail__field-key">${_escHtml(k)}</span><span class="bme-memory-detail__field-val">${_escHtml(String(v))}</span></div>`).join("")}
|
${detailFields.concat(extraFieldRows).map(([k, v]) => `<div class="bme-memory-detail__field-row"><span class="bme-memory-detail__field-key">${_escHtml(k)}</span><span class="bme-memory-detail__field-val">${_escHtml(String(v))}</span></div>`).join("")}
|
||||||
</div>
|
</div>
|
||||||
<div class="bme-memory-detail__stats">
|
<div class="bme-memory-detail__stats">
|
||||||
<span><i class="fa-solid fa-link" style="margin-right:4px;opacity:.5"></i>${edges.length} 条连接</span>
|
<span><i class="fa-solid fa-link" style="margin-right:4px;opacity:.5"></i>${edges.length} 条连接</span>
|
||||||
<span><i class="fa-solid fa-eye" style="margin-right:4px;opacity:.5"></i>recall ${node.recallCount ?? 0}</span>
|
<span><i class="fa-solid fa-eye" style="margin-right:4px;opacity:.5"></i>访问 ${_formatMemoryInt(node.accessCount, 0)}</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user