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:
lix-2026
2026-06-04 18:51:16 +08:00
parent 9f4b5c4d48
commit 627213dc01
51 changed files with 15960 additions and 1844 deletions
@@ -19,6 +19,9 @@ pub fn manifest() -> Value {
context_resolve_target_tool(),
doc_fetch_tool(),
doc_find_tool(),
evidence_search_tool(),
evidence_read_tool(),
evidence_open_tool(),
block_fetch_tool(),
doc_plan_update_tool(),
block_replace_tool(),
@@ -249,6 +252,104 @@ fn doc_find_tool() -> Value {
})
}
fn evidence_search_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("query".into(), json!({ "type": "string" }));
map.insert("rootUri".into(), json!({ "type": "string" }));
map.insert(
"includeResources".into(),
json!({ "type": "boolean", "default": true }),
);
map.insert(
"includeOcr".into(),
json!({ "type": "boolean", "default": true }),
);
map.insert(
"mode".into(),
json!({ "type": "string", "enum": ["keyword", "tree", "hybrid", "graph"], "default": "hybrid" }),
);
map.insert("topK".into(), json!({ "type": "integer", "default": 8 }));
map.insert(
"scope".into(),
json!({
"type": "object",
"properties": {
"workspaceId": { "type": "string" },
"rootUri": { "type": "string" },
"targetDocumentId": { "type": "string" },
"includeResources": { "type": "boolean" },
"includeOcr": { "type": "boolean" }
}
}),
);
}
json!({
"name": "mnote.evidence.search",
"description": "搜索可回跳原文的文档证据,返回 quote、EvidenceLocator 与 openAction。",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["evidence.read", "page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "rootUri", "query"],
"properties": properties
}
})
}
fn evidence_read_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("locator".into(), json!({ "type": "object" }));
map.insert(
"context".into(),
json!({
"type": "object",
"properties": {
"beforeBlocks": { "type": "integer", "default": 3 },
"afterBlocks": { "type": "integer", "default": 3 },
"includeSectionSummary": { "type": "boolean", "default": true }
}
}),
);
}
json!({
"name": "mnote.evidence.read",
"description": "按 EvidenceLocator 读取原文证据及周边上下文,供回答引用。",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["evidence.read", "page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["locator"],
"properties": properties
}
})
}
fn evidence_open_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("locator".into(), json!({ "type": "object" }));
}
json!({
"name": "mnote.evidence.open",
"description": "把 EvidenceLocator 归一化为 MNote 可执行的打开/定位动作。",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["evidence.read", "page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["locator"],
"properties": properties
}
})
}
fn block_fetch_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {