feat: add evidence search and stabilize pdf previews
- add document evidence parsing/search/open routes, Hermes tool wiring, local index settings/status, and the document-evidence skill plus design notes - fix PDF resource tabs by rendering PDFs inline with pdf.js canvases instead of iframe preview pages, release PDF documents on close, and document the fourth-PDF stall bug - keep PDF preview at 2x rendering while removing the previous lazy-load/placeholder direction, and make dev:hot bind loopback defaults externally reachable Verification: - node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js - node scripts/task-dev-hot-plan-test.js - cargo test -p mnote-web --manifest-path rust/Cargo.toml pdf_preview_page_does_not_render_visible_toolbar - cargo test -p mnote-web --manifest-path rust/Cargo.toml document_shell_returns_page_aggregate_snapshot - cargo build -p mnote-web --manifest-path rust/Cargo.toml - browser smoke: sequentially opened the four tea_seed_oil_cosmetic PDFs; fourth PDF rendered 15/15 canvases, iframeCount=0, browser errors=0
This commit is contained in:
@@ -30,6 +30,14 @@ function documentUrl(root, relativePath) {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function navigationUrl(root) {
|
||||
const url = new URL(`${BASE_URL}/`);
|
||||
url.searchParams.set("sourceKind", "local_folder");
|
||||
url.searchParams.set("rootUri", fileUrl(root));
|
||||
url.searchParams.set("treeView", "filetree");
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function writeWorkspaceManifest(root, ownerId) {
|
||||
const metadataDir = path.join(root, ".mnote");
|
||||
fs.mkdirSync(metadataDir, { recursive: true });
|
||||
@@ -66,13 +74,31 @@ async function browserSearch(page, root, query) {
|
||||
}, { rootUri: fileUrl(root), queryText: query });
|
||||
}
|
||||
|
||||
async function browserRefreshLocalIndex(page, root) {
|
||||
return page.evaluate(async ({ rootUri }) => {
|
||||
const response = await fetch("/api/search/local-index/refresh", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json", accept: "application/json" },
|
||||
body: JSON.stringify({
|
||||
workspaceId: "local-ws:user_real:task452",
|
||||
rootUri,
|
||||
}),
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
return {
|
||||
status: response.status,
|
||||
payload,
|
||||
};
|
||||
}, { rootUri: fileUrl(root) });
|
||||
}
|
||||
|
||||
async function capturePanelDiagnostics(page) {
|
||||
try {
|
||||
return await page.evaluate(function() {
|
||||
var bl = document.querySelector('[data-testid="wolai-page-settings-local-index-backlinks"]');
|
||||
var tg = document.querySelector('[data-testid="wolai-page-settings-local-index-tags"]');
|
||||
var st = document.querySelector('[data-testid="wolai-page-settings-local-index-status"]');
|
||||
var popover = document.querySelector('[data-testid="wolai-page-settings-popover"]');
|
||||
var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
|
||||
return {
|
||||
backlinksHtml: bl ? bl.innerHTML : '(missing)',
|
||||
tagsHtml: tg ? tg.innerHTML : '(missing)',
|
||||
@@ -131,6 +157,9 @@ async function run() {
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const refreshedBeforeSearch = await browserRefreshLocalIndex(page, root);
|
||||
assert.equal(refreshedBeforeSearch.status, 200, `刷新本地索引应成功: ${JSON.stringify(refreshedBeforeSearch)}`);
|
||||
debug.refreshedBeforeSearch = refreshedBeforeSearch.payload;
|
||||
const first = await browserSearch(page, root, token);
|
||||
assert.equal(first.status, 200, `初次搜索应成功: ${JSON.stringify(first)}`);
|
||||
debug.first = first.payload;
|
||||
@@ -140,6 +169,29 @@ async function run() {
|
||||
`新建页面应立即可搜索: ${JSON.stringify(firstResults)}`,
|
||||
);
|
||||
|
||||
await page.goto(navigationUrl(root), {
|
||||
waitUntil: "domcontentloaded",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
await page.locator('[data-testid="mnote-local-index-settings-toggle"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('[data-testid="mnote-local-index-settings-popover"]').waitFor({
|
||||
state: "visible",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
debug.navigationIndexSettings = await page.evaluate(function() {
|
||||
var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
|
||||
var ranges = document.querySelector('[data-testid="wolai-page-settings-local-index-ranges"]');
|
||||
var pageSettingsIndexTab = document.querySelector('[data-page-settings-tab="index"]');
|
||||
return {
|
||||
path: window.location.pathname,
|
||||
visible: Boolean(popover && !popover.hidden),
|
||||
rangeInputs: ranges ? ranges.querySelectorAll('[data-local-index-range-input]').length : 0,
|
||||
hasPageSettingsIndexTab: Boolean(pageSettingsIndexTab),
|
||||
};
|
||||
});
|
||||
assert.equal(debug.navigationIndexSettings.visible, true, `导航页应能打开索引设置: ${JSON.stringify(debug.navigationIndexSettings)}`);
|
||||
assert.equal(debug.navigationIndexSettings.hasPageSettingsIndexTab, false, `索引设置不应留在页面设置页签: ${JSON.stringify(debug.navigationIndexSettings)}`);
|
||||
|
||||
await page.goto(documentUrl(root, firstRelativePath), {
|
||||
waitUntil: "domcontentloaded",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
@@ -162,8 +214,7 @@ async function run() {
|
||||
if (msg.type() === 'error') { diagApiResponses._consoleErrors = (diagApiResponses._consoleErrors || []).concat([msg.text()]); }
|
||||
});
|
||||
|
||||
await page.locator('[data-testid="wolai-page-settings-trigger"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('[data-page-settings-tab="index"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('[data-testid="mnote-local-index-settings-toggle"]').click({ timeout: UI_TIMEOUT_MS });
|
||||
|
||||
// 等待本地索引面板渲染;超时时捕获 DOM 诊断再抛
|
||||
try {
|
||||
@@ -195,6 +246,9 @@ async function run() {
|
||||
debug.diagApiResponses = diagApiResponses;
|
||||
|
||||
fs.renameSync(path.join(root, firstRelativePath), path.join(root, renamedRelativePath));
|
||||
const refreshedAfterRename = await browserRefreshLocalIndex(page, root);
|
||||
assert.equal(refreshedAfterRename.status, 200, `重命名后刷新本地索引应成功: ${JSON.stringify(refreshedAfterRename)}`);
|
||||
debug.refreshedAfterRename = refreshedAfterRename.payload;
|
||||
const second = await browserSearch(page, root, token);
|
||||
assert.equal(second.status, 200, `重命名后搜索应成功: ${JSON.stringify(second)}`);
|
||||
debug.second = second.payload;
|
||||
|
||||
Reference in New Issue
Block a user