From 76b5116798cd8a965b5be5f3fde3fae92d3e3297 Mon Sep 17 00:00:00 2001 From: Youzini-afk <13153778771cx@gmail.com> Date: Mon, 6 Apr 2026 16:10:03 +0800 Subject: [PATCH] fix(panel): memory list layout and formatted metrics Made-with: Cursor --- panel.js | 86 +++++++++++++++++++++++++++++++++++++++---------------- style.css | 72 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 124 insertions(+), 34 deletions(-) diff --git a/panel.js b/panel.js index 0d7fa0e..7d2c467 100644 --- a/panel.js +++ b/panel.js @@ -1069,7 +1069,7 @@ function _refreshMemoryBrowser() { const fragment = document.createDocumentFragment(); nodes.slice(0, 100).forEach((node) => { const name = getNodeDisplayName(node); - const snippet = _getNodeSnippet(node); + const snippetText = _getNodeSnippet(node); const li = document.createElement("li"); li.className = "bme-memory-item"; li.dataset.nodeId = String(node.id || ""); @@ -1077,40 +1077,59 @@ function _refreshMemoryBrowser() { const badge = document.createElement("span"); badge.className = `bme-type-badge ${_safeCssToken(node.type)}`; badge.textContent = _typeLabel(node.type); - li.appendChild(badge); const scopeBadge = document.createElement("span"); scopeBadge.className = "bme-type-badge"; scopeBadge.textContent = buildScopeBadgeText(node.scope); - li.appendChild(scopeBadge); - const content = document.createElement("div"); - const title = document.createElement("div"); - title.className = "bme-memory-name"; - title.textContent = name; + const badgesWrap = document.createElement("div"); + badgesWrap.className = "bme-memory-badges"; + badgesWrap.append(badge, scopeBadge); + const body = document.createElement("div"); - body.className = "bme-memory-content"; - body.textContent = snippet; + body.className = "bme-memory-body"; + + const titleEl = document.createElement("div"); + titleEl.className = "bme-memory-name"; + titleEl.textContent = name; + + const snippetEl = document.createElement("div"); + snippetEl.className = "bme-memory-content"; + snippetEl.textContent = snippetText; + const meta = document.createElement("div"); meta.className = "bme-memory-meta"; - ["imp", "acc", "seq"].forEach((key, index) => { - const span = document.createElement("span"); - span.textContent = - index === 0 - ? `imp: ${node.importance || 5}` - : index === 1 - ? `acc: ${node.accessCount || 0}` - : `seq: ${node.seqRange?.[1] ?? node.seq ?? 0}`; - meta.appendChild(span); - }); + + const impSpan = document.createElement("span"); + impSpan.className = "bme-memory-metric"; + impSpan.textContent = `重要度 ${_formatMemoryMetricNumber(node.importance, { + fallback: 5, + maxFrac: 2, + })}`; + + const accSpan = document.createElement("span"); + accSpan.className = "bme-memory-metric"; + accSpan.textContent = `访问 ${_formatMemoryInt(node.accessCount, 0)}`; + + const seqSpan = document.createElement("span"); + seqSpan.className = "bme-memory-metric"; + seqSpan.textContent = `序列 ${_formatMemoryInt( + node.seqRange?.[1] ?? node.seq, + 0, + )}`; + + meta.append(impSpan, accSpan, seqSpan); + const regionMeta = _buildScopeMetaText(node); if (regionMeta) { const scopeSpan = document.createElement("span"); + scopeSpan.className = "bme-memory-regionline"; scopeSpan.textContent = regionMeta; meta.appendChild(scopeSpan); } - content.append(title, body, meta); - li.appendChild(content); + + body.append(titleEl, snippetEl, meta); + li.append(badgesWrap, body); fragment.appendChild(li); }); listEl.replaceChildren(fragment); @@ -5548,12 +5567,31 @@ function _buildScopeMetaText(node) { parts.push( `${scope.ownerType === "user" ? "用户 POV" : "角色 POV"}: ${scope.ownerName || scope.ownerId || "未命名"}`, ); - } else { - parts.push("客观层"); } const regionLine = buildRegionLine(scope); if (regionLine) parts.push(regionLine); - return parts.join(" | "); + return parts.join(" · "); +} + +/** 记忆列表等指标:避免浮点误差打出 9.499999999999998 */ +function _formatMemoryMetricNumber(value, { fallback = 0, maxFrac = 2 } = {}) { + const x = + value === undefined || value === null || value === "" + ? Number(fallback) + : Number(value); + if (!Number.isFinite(x)) return "—"; + const rounded = Number.parseFloat(x.toFixed(maxFrac)); + if (Object.is(rounded, -0)) return "0"; + return String(rounded); +} + +function _formatMemoryInt(value, fallback = 0) { + const x = + value === undefined || value === null || value === "" + ? Number(fallback) + : Number(value); + if (!Number.isFinite(x)) return "—"; + return String(Math.trunc(x)); } function _typeLabel(type) { diff --git a/style.css b/style.css index 175b343..c7a47a8 100644 --- a/style.css +++ b/style.css @@ -710,6 +710,7 @@ /* --- Memory Browser Tab --- */ .bme-search-bar { display: flex; + flex-wrap: wrap; gap: 6px; margin-bottom: 10px; } @@ -743,6 +744,8 @@ font-size: 11px; color: var(--bme-on-surface); outline: none; + min-width: 7rem; + flex: 1 1 8rem; } .bme-memory-list { @@ -751,13 +754,40 @@ margin: 0; } +.bme-memory-badges { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + flex-shrink: 0; + max-width: min(11rem, 42%); +} + +.bme-memory-badges .bme-type-badge { + margin-top: 0; + max-width: 100%; + white-space: normal; + word-break: break-word; + line-height: 1.25; + text-align: left; +} + +.bme-memory-body { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + align-items: stretch; + gap: 4px; +} + .bme-memory-item { display: flex; align-items: flex-start; - gap: 8px; - padding: 8px; - border-radius: 4px; - margin-bottom: 3px; + gap: 10px; + padding: 10px 8px; + border-radius: 6px; + margin-bottom: 4px; cursor: pointer; transition: background 0.1s; border: 1px solid transparent; @@ -777,28 +807,50 @@ font-size: 12px; font-weight: 600; color: var(--bme-on-surface); + line-height: 1.35; + word-break: break-word; + overflow-wrap: anywhere; } .bme-memory-content { font-size: 11px; color: var(--bme-on-surface-dim); - margin-top: 2px; - line-height: 1.3; + line-height: 1.45; display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; + -webkit-line-clamp: 3; + line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; + word-break: break-word; + overflow-wrap: anywhere; } .bme-memory-meta { display: flex; - gap: 8px; - margin-top: 4px; + flex-wrap: wrap; + align-items: baseline; + gap: 6px 10px; + margin-top: 2px; font-size: 10px; color: var(--bme-on-surface-dim); } +.bme-memory-metric { + flex: 0 0 auto; + white-space: nowrap; + font-variant-numeric: tabular-nums; +} + +.bme-memory-regionline { + flex: 1 1 100%; + min-width: 0; + white-space: normal; + line-height: 1.35; + opacity: 0.92; + word-break: break-word; + overflow-wrap: anywhere; +} + /* --- Injection Preview Tab --- */ .bme-injection-preview { font-family: "Cascadia Code", "Fira Code", monospace;