feat(ui): use mobile memory popup in task monitor

This commit is contained in:
Youzini-afk
2026-04-13 02:08:08 +08:00
parent 32759ed08b
commit d3a547cd7b
3 changed files with 192 additions and 10 deletions

101
style.css
View File

@@ -1438,6 +1438,101 @@
flex-wrap: wrap;
}
/* ==================== Memory Popup (Mobile) ==================== */
.bme-memory-popup-scrim {
position: fixed;
inset: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
cursor: pointer;
}
.bme-memory-popup-scrim[hidden] {
display: none !important;
}
.bme-memory-popup {
display: none;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) scale(0.92);
width: min(460px, calc(100vw - 24px));
max-height: min(82vh, 680px);
background: var(--bme-surface, #131316);
border: 1px solid var(--bme-border);
border-radius: 14px;
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.65);
z-index: 1000;
overflow-y: auto;
padding: 18px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: transform 0.18s ease, opacity 0.18s ease, visibility 0.18s ease;
}
.bme-memory-popup.open {
display: block;
transform: translate(-50%, -50%) scale(1);
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.bme-memory-popup__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 10px;
}
.bme-memory-popup__title {
font-size: 16px;
font-weight: 700;
color: var(--bme-on-surface);
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bme-memory-popup__actions {
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
}
.bme-memory-popup__badges {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.bme-memory-popup__body {
font-size: 12px;
color: var(--bme-on-surface);
}
.bme-memory-popup::-webkit-scrollbar {
width: 4px;
}
.bme-memory-popup::-webkit-scrollbar-track {
background: transparent;
}
.bme-memory-popup::-webkit-scrollbar-thumb {
background: var(--bme-surface-highest);
border-radius: 2px;
}
/* ==================== Injection Preview ==================== */
.bme-injection-token-bar {
@@ -7063,11 +7158,11 @@
width: 100%;
max-width: none;
border-right: none;
border-bottom: 1px solid var(--bme-border);
max-height: 40vh;
border-bottom: none;
max-height: none;
}
.bme-memory-detail-panel {
padding: 14px;
display: none;
}
.bme-batch-stages {
flex-wrap: wrap;

View File

@@ -2986,6 +2986,26 @@
<div class="bme-task-section" data-task-section="trace" id="bme-task-trace"></div>
<div class="bme-task-section" data-task-section="persistence" id="bme-task-persistence"></div>
</div>
<div class="bme-memory-popup-scrim" id="bme-memory-popup-scrim" hidden></div>
<div class="bme-memory-popup" id="bme-memory-popup">
<div class="bme-memory-popup__header">
<h3 class="bme-memory-popup__title" id="bme-memory-popup-title">节点详情</h3>
<div class="bme-memory-popup__actions">
<button class="bme-detail-action-btn" id="bme-memory-popup-save" type="button" title="保存修改">
<i class="fa-solid fa-floppy-disk"></i>
</button>
<button class="bme-detail-action-btn bme-detail-action-danger" id="bme-memory-popup-delete" type="button" title="删除节点">
<i class="fa-solid fa-trash"></i>
</button>
<button class="bme-panel-close" id="bme-memory-popup-close" type="button" title="关闭">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</div>
<div class="bme-memory-popup__badges" id="bme-memory-popup-badges"></div>
<div class="bme-memory-popup__body" id="bme-memory-popup-body"></div>
</div>
</div>
</div>

View File

@@ -932,6 +932,7 @@ export async function initPanel({
_bindTabs();
_bindClose();
_bindNodeDetailPanel();
_bindMemoryPopup();
_bindResizeHandle();
_bindPanelResize();
_bindGraphControls();
@@ -1186,6 +1187,7 @@ export function openPanel() {
export function closePanel() {
if (!overlayEl) return;
overlayEl.classList.remove("active");
_closeMemoryPopup();
_clearScheduledVisibleGraphRefresh();
lastVisibleGraphRefreshToken = "";
}
@@ -1246,6 +1248,7 @@ function _switchTab(tabId) {
}
currentTabId = next;
_closeNodeDetailUi();
_closeMemoryPopup();
panelEl?.querySelectorAll(".bme-tab-btn").forEach((btn) => {
btn.classList.toggle("active", btn.dataset.tab === currentTabId);
});
@@ -1352,6 +1355,7 @@ function _bindTaskNavigation() {
function _switchTaskSection(sectionId) {
currentTaskSectionId = sectionId || "pipeline";
_closeMemoryPopup();
_syncTaskSectionState();
_refreshTaskMonitor();
}
@@ -1758,7 +1762,12 @@ function _bindTaskMemoryListClick() {
currentSelectedMemoryNodeId = item.dataset.nodeId || "";
list.querySelectorAll(".bme-memory-node-item").forEach((n) => n.classList.toggle("selected", n.dataset.nodeId === currentSelectedMemoryNodeId));
const graph = _getGraph?.();
_renderTaskMemoryDetailSelection(graph);
if (_isMobile()) {
const node = (graph?.nodes || []).find((c) => c.id === currentSelectedMemoryNodeId) || null;
if (node) _openMemoryPopup(node, graph);
} else {
_renderTaskMemoryDetailSelection(graph);
}
});
}
@@ -1832,20 +1841,31 @@ function _renderTaskMemoryDetailPanel(detailEl, node, graph) {
}
function _saveTaskMemoryDetail() {
const detailEl = document.getElementById("bme-task-memory-detail");
const bodyEl = detailEl?.querySelector("#bme-task-memory-editor-body");
const popupBody = document.getElementById("bme-memory-popup-body");
const popupOpen = document.getElementById("bme-memory-popup")?.classList.contains("open");
const detailEl = popupOpen ? null : document.getElementById("bme-task-memory-detail");
const bodyEl = popupOpen
? popupBody
: detailEl?.querySelector("#bme-task-memory-editor-body");
const nodeId = currentSelectedMemoryNodeId;
if (!nodeId || !bodyEl) return;
const collected = _collectNodeDetailEditorUpdates(bodyEl, {
idPrefix: "bme-task-detail",
});
const idPrefix = popupOpen ? "bme-popup-detail" : "bme-task-detail";
const collected = _collectNodeDetailEditorUpdates(bodyEl, { idPrefix });
if (!collected.ok) {
toastr.error(collected.errorMessage || "保存失败", "ST-BME");
return;
}
_persistNodeDetailEdits(nodeId, collected.updates);
_persistNodeDetailEdits(nodeId, collected.updates, {
afterSuccess: () => {
if (popupOpen) {
const graph = _getGraph?.();
const refreshedNode = (graph?.nodes || []).find((n) => n.id === nodeId);
if (refreshedNode) _openMemoryPopup(refreshedNode, graph);
}
},
});
}
function _deleteTaskMemoryDetail() {
@@ -1855,10 +1875,57 @@ function _deleteTaskMemoryDetail() {
_deleteGraphNodeById(nodeId, {
afterSuccess: () => {
currentSelectedMemoryNodeId = "";
_closeMemoryPopup();
},
});
}
function _openMemoryPopup(node, graph) {
const popup = document.getElementById("bme-memory-popup");
const scrim = document.getElementById("bme-memory-popup-scrim");
const titleEl = document.getElementById("bme-memory-popup-title");
const badgesEl = document.getElementById("bme-memory-popup-badges");
const bodyEl = document.getElementById("bme-memory-popup-body");
if (!popup || !bodyEl) return;
const displayName = getNodeDisplayName(node);
const scopeBadge = buildScopeBadgeText(node.scope);
const badges = [
node.type ? `<span class="bme-memory-node-item__type ${_getMemoryNodeTypeClass(node.type)}">${_escHtml(_typeLabel(node.type))}</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>' : "",
].filter(Boolean).join("");
if (titleEl) titleEl.textContent = displayName;
if (badgesEl) badgesEl.innerHTML = badges;
bodyEl.replaceChildren(
_buildNodeDetailEditorFragment(node, { idPrefix: "bme-popup-detail" }),
);
scrim?.removeAttribute("hidden");
popup.classList.add("open");
}
function _closeMemoryPopup() {
const popup = document.getElementById("bme-memory-popup");
const scrim = document.getElementById("bme-memory-popup-scrim");
popup?.classList.remove("open");
scrim?.setAttribute("hidden", "");
}
function _bindMemoryPopup() {
const closeBtn = document.getElementById("bme-memory-popup-close");
const scrim = document.getElementById("bme-memory-popup-scrim");
const saveBtn = document.getElementById("bme-memory-popup-save");
const deleteBtn = document.getElementById("bme-memory-popup-delete");
closeBtn?.addEventListener("click", () => _closeMemoryPopup());
scrim?.addEventListener("click", () => _closeMemoryPopup());
saveBtn?.addEventListener("click", () => _saveTaskMemoryDetail());
deleteBtn?.addEventListener("click", () => _deleteTaskMemoryDetail());
}
// ---------- Injection Preview ----------
function _refreshTaskInjectionPreview() {