feat(panel): delete graph node from detail header

Made-with: Cursor
This commit is contained in:
Youzini-afk
2026-04-06 16:28:31 +08:00
parent 16f103297b
commit 5cbb48c0ad
4 changed files with 86 additions and 0 deletions

View File

@@ -110,6 +110,7 @@ import {
getGraphStats,
getNode,
importGraph,
removeNode,
updateNode,
} from "./graph.js";
import {
@@ -9524,6 +9525,26 @@ function onSavePanelGraphNode(payload = {}) {
};
}
function onDeletePanelGraphNode(payload = {}) {
const nodeId = String(payload.nodeId || "");
if (!nodeId || !currentGraph) {
return { ok: false, error: "invalid-payload" };
}
if (!getNode(currentGraph, nodeId)) {
return { ok: false, error: "node-not-found" };
}
const removed = removeNode(currentGraph, nodeId);
if (!removed) {
return { ok: false, error: "delete-failed" };
}
const persist = saveGraphToChat({ reason: "panel-node-delete" });
return {
ok: true,
persist,
persistBlocked: Boolean(persist?.blocked),
};
}
async function onExportGraph() {
return await onExportGraphController({
document,
@@ -9830,6 +9851,7 @@ async function onReembedDirect() {
applyCurrentHide: () => applyMessageHideNow("panel-manual-apply"),
clearCurrentHide: () => clearAllHiddenMessages("panel-manual-clear"),
saveGraphNode: onSavePanelGraphNode,
deleteGraphNode: onDeletePanelGraphNode,
rebuildVectorIndex: () => onRebuildVectorIndex(),
rebuildVectorRange: (range) => onRebuildVectorIndex(range),
reembedDirect: onReembedDirect,

View File

@@ -443,6 +443,14 @@
<div class="bme-node-detail-header">
<h3 id="bme-detail-title">节点详情</h3>
<div class="bme-node-detail-actions">
<button
class="bme-detail-action-btn bme-detail-action-danger"
id="bme-detail-delete"
type="button"
title="删除节点"
>
<i class="fa-solid fa-trash"></i>
</button>
<button
class="bme-detail-action-btn"
id="bme-detail-save"

View File

@@ -93,6 +93,7 @@ const GRAPH_WRITE_ACTION_IDS = [
"bme-act-vector-range",
"bme-act-vector-reembed",
"bme-act-reroll",
"bme-detail-delete",
"bme-detail-save",
];
@@ -1537,6 +1538,52 @@ function _bindNodeDetailPanel() {
saveBtn.addEventListener("click", () => _saveNodeDetail());
saveBtn.dataset.bmeBound = "true";
}
const deleteBtn = document.getElementById("bme-detail-delete");
if (deleteBtn && deleteBtn.dataset.bmeBound !== "true") {
deleteBtn.addEventListener("click", () => _deleteNodeDetail());
deleteBtn.dataset.bmeBound = "true";
}
}
function _deleteNodeDetail() {
const detailEl = document.getElementById("bme-node-detail");
const nodeId = detailEl?.dataset?.editNodeId;
if (!nodeId) return;
if (_isGraphWriteBlocked()) {
toastr.error("当前图谱不可写入,请稍后再试", "ST-BME");
return;
}
const g = _getGraph?.();
const node = g?.nodes?.find((n) => n.id === nodeId);
const label = node ? getNodeDisplayName(node) : nodeId;
if (
!confirm(
`确定删除节点「${label}」?\n\n若该节点有层级子节点,将一并删除。此操作不可在本面板内撤销。`,
)
) {
return;
}
const result = _actionHandlers.deleteGraphNode?.({ nodeId });
if (!result?.ok) {
toastr.error(
result?.error === "node-not-found" ? "节点已不存在" : "删除失败",
"ST-BME",
);
return;
}
if (result.persistBlocked) {
toastr.warning(
"节点已从图中移除,但写回可能被拦截,请查看图谱状态",
"ST-BME",
);
} else {
toastr.success("节点已删除", "ST-BME");
}
detailEl?.classList.remove("open");
if (detailEl) delete detailEl.dataset.editNodeId;
graphRenderer?.highlightNode?.("__cleared__");
mobileGraphRenderer?.highlightNode?.("__cleared__");
refreshLiveState();
}
function _bindClose() {

View File

@@ -2214,6 +2214,15 @@
cursor: not-allowed;
}
.bme-detail-action-danger {
color: var(--bme-accent3, #ffc107);
}
.bme-detail-action-danger:hover:not(:disabled) {
background: rgba(255, 193, 7, 0.12);
color: var(--bme-accent3, #ffc107);
}
.bme-node-detail h3 {
font-size: 14px;
color: var(--bme-on-surface);