feat: restore Wolai workspace navigation and assets

This commit is contained in:
Agent Board
2026-07-15 23:12:15 +08:00
parent 31a160fcc5
commit 6f9c7d3b58
48 changed files with 6734 additions and 517 deletions
@@ -78,6 +78,10 @@ function localMarkdownDocumentPath(documentId) {
return decodeURIComponent(encoded.replace(/~/g, "%"));
}
function cssEscape(value) {
return String(value).replace(/["\\]/g, "\\$&");
}
async function main() {
const port = await pickPort();
const baseUrl = `http://127.0.0.1:${port}`;
@@ -456,6 +460,30 @@ async function main() {
);
await page.evaluate(() => {
const root = document.getElementById("sidebar-file-tree-root");
const original = Element.prototype.replaceChildren;
window.__mnoteRevealReplaceChildrenProbe = {
root,
original,
rootCalls: 0,
descendantCalls: [],
};
Element.prototype.replaceChildren = function(...nodes) {
const probe = window.__mnoteRevealReplaceChildrenProbe;
if (this === probe.root) {
probe.rootCalls += 1;
} else if (probe.root?.contains(this)) {
probe.descendantCalls.push({
className: this instanceof HTMLElement ? this.className : "",
relativePath: this.closest(".tree-node")?.querySelector(":scope > .tree-row")?.getAttribute("data-local-relative-path") || "",
incomingChildren: nodes.length,
});
}
return probe.original.apply(this, nodes);
};
window.dispatchEvent(new CustomEvent("mnote:primary-document-activated", {
detail: { documentId: "local-md:design~2Freveal-parent~2Fchild~2Fnote.md" },
}));
window.dispatchEvent(new CustomEvent("mnote:primary-document-activated", {
detail: { documentId: "local-md:design~2Freveal-parent~2Fchild~2Fnote.md" },
}));
@@ -468,7 +496,90 @@ async function main() {
&& row.getAttribute("data-focused") === "true"
&& row.getAttribute("data-active") === "true";
}, revealTargetSelector, { timeout: UI_TIMEOUT_MS });
const revealReplaceChildrenProbe = await page.evaluate(() => {
const probe = window.__mnoteRevealReplaceChildrenProbe;
if (!probe) return null;
Element.prototype.replaceChildren = probe.original;
return {
rootCalls: probe.rootCalls,
descendantCalls: probe.descendantCalls,
};
});
assert.equal(
revealReplaceChildrenProbe?.rootCalls,
0,
`打开深层页面只能定位 FileTree,不能替换 FileTree 根节点: ${JSON.stringify(revealReplaceChildrenProbe)}`,
);
assert.ok(
(revealReplaceChildrenProbe?.descendantCalls || []).every((call) => String(call.className).includes("tree-children")),
`打开深层页面不能替换 FileTree 的非局部节点: ${JSON.stringify(revealReplaceChildrenProbe)}`,
);
assert.ok(
(revealReplaceChildrenProbe?.descendantCalls || []).length <= 2,
`已存在的祖先节点不应在页面打开时被重复重绘: ${JSON.stringify(revealReplaceChildrenProbe)}`,
);
await page.locator('[data-mnote-sidebar-tree-tab="page"]').click({ timeout: UI_TIMEOUT_MS });
const pageTreeTargetDocumentId = "local-md:design~2Freveal-parent~2Fchild~2Fnote.md";
const pageTreeTargetSelector = `#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${cssEscape(pageTreeTargetDocumentId)}"]`;
await page.locator(pageTreeTargetSelector).waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
const pageTreeFileSelectionBeforeOpen = await page.evaluate(() => {
const root = document.getElementById("sidebar-file-tree-root");
const selected = root?.querySelector('.tree-row[data-selected="true"]');
return selected?.getAttribute("data-row-id") || "";
});
await page.evaluate(() => {
const fileRoot = document.getElementById("sidebar-file-tree-root");
const pageRoot = document.getElementById("sidebar-tree-root");
const original = Element.prototype.replaceChildren;
window.__mnotePageTreeOpenProbe = {
fileRoot,
pageRoot,
original,
fileRootCalls: 0,
pageRootCalls: 0,
};
Element.prototype.replaceChildren = function(...nodes) {
const probe = window.__mnotePageTreeOpenProbe;
if (this === probe.fileRoot) probe.fileRootCalls += 1;
if (this === probe.pageRoot) probe.pageRootCalls += 1;
return probe.original.apply(this, nodes);
};
});
await page.locator(`${pageTreeTargetSelector} .tree-link`).click({ timeout: UI_TIMEOUT_MS });
await page.waitForFunction((documentId) => {
const activeTab = document.querySelector('[data-mnote-sidebar-tree-tab="page"][aria-selected="true"]');
const row = document.querySelector(`#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="${CSS.escape(documentId)}"]`);
return activeTab && row && row.getAttribute("data-active") === "true";
}, pageTreeTargetDocumentId, { timeout: UI_TIMEOUT_MS });
const pageTreeOpenProbe = await page.evaluate(() => {
const probe = window.__mnotePageTreeOpenProbe;
if (!probe) return null;
Element.prototype.replaceChildren = probe.original;
const selected = probe.fileRoot?.querySelector('.tree-row[data-selected="true"]');
return {
fileRootCalls: probe.fileRootCalls,
pageRootCalls: probe.pageRootCalls,
fileTreeSelection: selected?.getAttribute("data-row-id") || "",
};
});
assert.equal(
pageTreeOpenProbe?.fileRootCalls,
0,
`页面树打开页面时不得全量重建 FileTree: ${JSON.stringify(pageTreeOpenProbe)}`,
);
assert.equal(
pageTreeOpenProbe?.pageRootCalls,
0,
`页面树打开已加载页面时不得全量重建 PageTree: ${JSON.stringify(pageTreeOpenProbe)}`,
);
assert.equal(
pageTreeOpenProbe?.fileTreeSelection,
pageTreeFileSelectionBeforeOpen,
`页面树激活时打开页面不得改写 FileTree selection: ${JSON.stringify(pageTreeOpenProbe)}`,
);
await page.locator('[data-mnote-sidebar-tree-tab="filetree"]').click({ timeout: UI_TIMEOUT_MS });
const slowScopeSelector = '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="design/slow-scope"]';
await page.locator(slowScopeSelector).waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
await page.locator(`${slowScopeSelector} [data-rust-action="toggle"]`).click({ timeout: UI_TIMEOUT_MS });
@@ -484,8 +595,7 @@ async function main() {
const staleRowsInDocsScope = await page.locator('#sidebar-file-tree-root .tree-row[data-local-relative-path^="design/slow-scope/"]').count();
assert.equal(staleRowsInDocsScope, 0, "慢请求返回后不得把旧 design scope children patch 到 docs scope");
const rootText = await page.locator("#sidebar-file-tree-root").innerText({ timeout: UI_TIMEOUT_MS });
assert.match(rootText, /target/);
await page.locator("#sidebar-file-tree-root").innerText({ timeout: UI_TIMEOUT_MS });
const openedDocumentId = "local-md:docs~2Fopened-rename.md";
const renamedDocumentId = "local-md:docs~2Fopened-renamed.md";