chore: 收口 review 执行清单与 runtime 验证

- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录

- 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目

- 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑

- 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
This commit is contained in:
lix-2026
2026-05-14 05:52:08 +08:00
parent b4a452a8b7
commit 96e03645f7
69 changed files with 4780 additions and 979 deletions
+125 -2
View File
@@ -2814,13 +2814,17 @@ fn build_tree_shell_html(
}
if (sourceKind === "convex_workspace") {
for (const item of deletableItems) {
const documentId = getFileTreeRowDocumentId(item);
await sendCommand({
action: "delete",
workspaceId,
documentId: getFileTreeRowDocumentId(item),
documentId,
});
applyRemovedDocumentLocally(documentId);
}
if (!deletableItems.every((item) => !fileTreeRowById.has(item.rowId))) {
scheduleRefresh();
}
scheduleRefresh();
return true;
}
postToHost("tree.filetree.delete", {
@@ -2986,6 +2990,113 @@ fn build_tree_shell_html(
}, 80);
};
const addTreeItemLocally = (item) => {
if (!item?.nodeId || itemById.has(item.nodeId)) return false;
normalizedItems.push(item);
itemById.set(item.nodeId, item);
fileTreeRowById.set(item.rowId, item);
const parentId = item.parentNodeId && itemById.has(item.parentNodeId) ? item.parentNodeId : null;
if (parentId) {
const bucket = childrenByParentId.get(parentId) || [];
bucket.push(item);
bucket.sort(compareItems);
childrenByParentId.set(parentId, bucket);
const parentItem = itemById.get(parentId);
if (parentItem) parentItem.childCount = Math.max(parentItem.childCount || 0, bucket.length);
expanded.add(parentId);
} else {
roots.push(item);
roots.sort(compareItems);
}
return true;
};
const applyCreatedDocumentLocally = (result, parentId, title) => {
const documentId =
typeof result?.documentId === "string" && result.documentId.trim()
? result.documentId.trim()
: typeof result?.id === "string" && result.id.trim()
? result.id.trim()
: "";
if (!documentId) return false;
const createdAt = normalizeText(result?.updatedAt || result?.execution?.updated_at);
const parentNodeId = parentId && itemById.has(parentId) ? parentId : null;
const item = {
rowId: `doc:${documentId}`,
rowKind: "document",
nodeId: documentId,
parentNodeId,
title: normalizeText(result?.title, title || "无标题"),
depth: parentNodeId ? normalizeNumber(itemById.get(parentNodeId)?.depth, 0) + 1 : 0,
childCount: 0,
position: normalizeNumber(result?.sortOrder ?? result?.execution?.sort_order ?? Date.now()),
expandedByDefault: true,
iconHint: "page",
capabilities: ["open", "rename", "delete", "move", "create"],
resourceMeta: {
resourceKind: "document",
documentId,
assetId: "",
assetKind: "",
objectIdentity: { objectKind: "page", documentId, blockId: null, assetId: null },
blockAssetRelation: null,
},
updatedAt: createdAt,
};
if (!addTreeItemLocally(item)) return false;
currentFocusedDocumentId = documentId;
focusedNodeId = documentId;
currentActiveDocumentId = documentId;
renderTree();
return true;
};
const removeTreeItemEverywhere = (item) => {
if (!item) return;
const itemIndex = normalizedItems.indexOf(item);
if (itemIndex >= 0) normalizedItems.splice(itemIndex, 1);
itemById.delete(item.nodeId);
fileTreeRowById.delete(item.rowId);
const rootIndex = roots.indexOf(item);
if (rootIndex >= 0) roots.splice(rootIndex, 1);
const bucket = item.parentNodeId ? childrenByParentId.get(item.parentNodeId) : null;
if (bucket) {
const bucketIndex = bucket.indexOf(item);
if (bucketIndex >= 0) bucket.splice(bucketIndex, 1);
if (bucket.length === 0) childrenByParentId.delete(item.parentNodeId);
}
};
const applyRemovedDocumentLocally = (documentId) => {
const normalizedDocumentId = normalizeText(documentId);
if (!normalizedDocumentId) return false;
const removedNodeIds = new Set([normalizedDocumentId]);
let changed = false;
let expandedDuringScan = true;
while (expandedDuringScan) {
expandedDuringScan = false;
normalizedItems.forEach((item) => {
if (item.parentNodeId && removedNodeIds.has(item.parentNodeId) && !removedNodeIds.has(item.nodeId)) {
removedNodeIds.add(item.nodeId);
expandedDuringScan = true;
}
});
}
normalizedItems.slice().forEach((item) => {
const itemDocumentId = getFileTreeRowDocumentId(item) || item.resourceMeta?.documentId || item.nodeId;
if (removedNodeIds.has(item.nodeId) || itemDocumentId === normalizedDocumentId) {
removeTreeItemEverywhere(item);
changed = true;
}
});
if (!changed) return false;
if (currentActiveDocumentId === normalizedDocumentId) currentActiveDocumentId = roots[0]?.nodeId || "";
if (currentFocusedDocumentId === normalizedDocumentId) currentFocusedDocumentId = currentActiveDocumentId;
focusedNodeId = resolveFocusedNodeIdFromHostState();
renderTree();
return true;
};
if (sourceKind === "local_folder" && rootUri) {
let localWatchRevision = initialLocalWatchRevision;
let localWatchRefreshTimer = 0;
@@ -4335,6 +4446,14 @@ fn build_tree_shell_html(
payload: { documentId },
});
}
if (sourceKind === "convex_workspace" && applyCreatedDocumentLocally(result, parentId, title)) {
if (mode === "filetree") {
beginInlineRename("filetree", `doc:${documentId}`);
} else {
beginInlineRename("page", documentId);
}
return;
}
scheduleRefresh({
renameRowId: localCreatedRowIdFromCommandResult(result, "markdown"),
});
@@ -6688,6 +6807,10 @@ mod tests {
assert!(html.contains("data-testid=\"tree-node-toggle\""));
assert!(html.contains("hydrateInitialPageTree"));
assert!(html.contains("const usedRustInitialPageRenderer = hydrateInitialPageTree();"));
assert!(html.contains("applyCreatedDocumentLocally"));
assert!(html.contains("applyRemovedDocumentLocally"));
assert!(html.contains("sourceKind === \"convex_workspace\" && applyCreatedDocumentLocally"));
assert!(html.contains("if (!deletableItems.every((item) => !fileTreeRowById.has(item.rowId)))"));
assert!(html.contains("application/x-mnote-page-tree-node"));
assert!(html.contains("页面已拖放到"));
assert!(html.contains("setAttribute(\"role\", \"treeitem\")"));