收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目 - 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线 - 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径 验证: - cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1 - git diff --check - git diff --cached --check - codegraph index . --force && codegraph status . - codegraph sync . && codegraph status .
This commit is contained in:
@@ -8,8 +8,8 @@ use crate::page_aggregate::{
|
||||
use crate::routes::local_markdown_parser::{
|
||||
file_stem_title, parse_markdown_attachment_refs, parse_markdown_page, split_frontmatter,
|
||||
};
|
||||
use crate::routes::local_search_index;
|
||||
use crate::routes::snapshot_support::ProjectionSnapshot;
|
||||
use crate::routes::{local_ocr, local_search_index};
|
||||
use axum::extract::{Extension, Multipart, Path as AxumPath, Query, State};
|
||||
use axum::http::{header, HeaderMap, HeaderValue, StatusCode};
|
||||
use axum::Json;
|
||||
@@ -4076,8 +4076,13 @@ fn local_mindmap_file_name(mindmap_id: &str) -> String {
|
||||
fn is_local_mindmap_file_name(file_name: &str) -> bool {
|
||||
let trimmed = file_name.trim();
|
||||
let lower = trimmed.to_ascii_lowercase();
|
||||
if !lower.ends_with(".json") {
|
||||
return false;
|
||||
}
|
||||
lower.ends_with(".mindmap.json")
|
||||
|| (trimmed.starts_with("思维导图") && lower.ends_with(".json"))
|
||||
|| lower.starts_with("mindmap-")
|
||||
|| lower.starts_with("mindmap_")
|
||||
|| trimmed.starts_with("思维导图")
|
||||
}
|
||||
|
||||
fn resolve_local_mindmap_path(
|
||||
@@ -7395,6 +7400,9 @@ fn scan_markdown_page_tree(
|
||||
continue;
|
||||
}
|
||||
if is_markdown_file(&entry.file_name) {
|
||||
if local_ocr::is_local_ocr_sidecar_relative_path(&entry.relative_path) {
|
||||
continue;
|
||||
}
|
||||
let markdown = fs::read_to_string(&entry.path).unwrap_or_default();
|
||||
let parsed = parse_markdown_page(&markdown, &entry.file_name);
|
||||
let page_id = local_markdown_path_page_id(&entry.relative_path);
|
||||
@@ -8426,7 +8434,6 @@ fn editor_blocks_to_markdown_with_rewrite(
|
||||
lines.push(text);
|
||||
} else {
|
||||
let src = rewrite_local_open_url_to_markdown_relative(src, local_file_context)
|
||||
.map(|value| markdown_href_for_relative_path(&value))
|
||||
.unwrap_or_else(|| src.to_string());
|
||||
lines.push(format!(
|
||||
"",
|
||||
@@ -10424,6 +10431,61 @@ fn main() {}
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_markdown_generated_mindmap_id_roundtrips_as_mindmap_block() {
|
||||
let root = temp_root("mnote-local-markdown-generated-mindmap-roundtrip");
|
||||
init_workspace(&root);
|
||||
std::fs::create_dir_all(root.join("Page")).expect("create page dir");
|
||||
std::fs::write(root.join("Page").join("Page.md"), "").expect("write md");
|
||||
let root_uri = format!("file://{}", root.display());
|
||||
|
||||
write_local_mindmap_data(
|
||||
&root_uri,
|
||||
"local-md:Page~2FPage.md",
|
||||
"mindmap-123456",
|
||||
serde_json::json!({"data":{"text":"中心主题"},"children":[]}),
|
||||
false,
|
||||
)
|
||||
.expect("write generated mindmap");
|
||||
|
||||
save_local_markdown_page(
|
||||
&root_uri,
|
||||
"local-md:Page~2FPage.md",
|
||||
None,
|
||||
&serde_json::json!([
|
||||
{
|
||||
"type": "mindmap",
|
||||
"props": {
|
||||
"name": "思维导图",
|
||||
"mindmapId": "mindmap-123456",
|
||||
"rootNodeId": "root"
|
||||
}
|
||||
}
|
||||
]),
|
||||
)
|
||||
.expect("save");
|
||||
|
||||
let saved = std::fs::read_to_string(root.join("Page").join("Page.md")).expect("read md");
|
||||
assert!(saved.contains("[思维导图](mindmap-123456.json)"));
|
||||
|
||||
let aggregate = resolve_local_markdown_page_aggregate(&root_uri, "local-md:Page~2FPage.md")
|
||||
.expect("aggregate");
|
||||
let mindmap = aggregate.body.content.as_array().expect("blocks")[0].clone();
|
||||
assert_eq!(mindmap["type"], "mindmap");
|
||||
assert_eq!(mindmap["props"]["sourcePath"], "mindmap-123456.json");
|
||||
assert_eq!(mindmap["props"]["mindmapId"], "mindmap-123456.json");
|
||||
assert_eq!(
|
||||
aggregate.body.block_document["blocks"][0]["attrs"]["mindmapId"],
|
||||
serde_json::json!("mindmap-123456.json")
|
||||
);
|
||||
assert_eq!(
|
||||
aggregate.body.block_document["blocks"][0]["attrs"]["sourcePath"],
|
||||
serde_json::json!("mindmap-123456.json")
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_workspace_access_rejects_owner_mismatch() {
|
||||
let root = temp_root("mnote-local-workspace-owner-mismatch");
|
||||
@@ -12656,6 +12718,8 @@ fn main() {}
|
||||
std::fs::create_dir_all(root.join("Page")).expect("create page dir");
|
||||
std::fs::write(root.join("Page").join("Page.md"), "").expect("write md");
|
||||
std::fs::write(root.join("Page").join("思维导图123456.json"), "{}").expect("write mindmap");
|
||||
std::fs::write(root.join("Page").join("mindmap-123456.json"), "{}")
|
||||
.expect("write generated mindmap");
|
||||
std::fs::write(root.join("Page").join("report.docx"), b"docx").expect("write docx");
|
||||
std::fs::write(root.join("Page").join("sheet.xlsx"), b"xlsx").expect("write xlsx");
|
||||
std::fs::write(root.join("Page").join("slides.pptx"), b"pptx").expect("write pptx");
|
||||
@@ -12687,6 +12751,17 @@ fn main() {}
|
||||
mindmap["resourceMeta"]["workspacePath"]["objectIdentity"]["assetId"].as_str(),
|
||||
Some("local-file:Page/思维导图123456.json")
|
||||
);
|
||||
let generated_mindmap = items
|
||||
.iter()
|
||||
.find(|item| item["title"].as_str() == Some("mindmap-123456.json"))
|
||||
.expect("generated mindmap row");
|
||||
assert_eq!(generated_mindmap["rowKind"].as_str(), Some("asset"));
|
||||
assert_eq!(generated_mindmap["iconHint"].as_str(), Some("mindmap"));
|
||||
assert_eq!(
|
||||
generated_mindmap["resourceMeta"]["workspacePath"]["objectIdentity"]["objectKind"]
|
||||
.as_str(),
|
||||
Some("mindmap")
|
||||
);
|
||||
|
||||
let office = items
|
||||
.iter()
|
||||
@@ -13239,6 +13314,41 @@ fn main() {}
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_ocr_sidecar_is_filetree_resource_not_page_tree_document() {
|
||||
let root = temp_root("mnote-local-ocr-sidecar-page-tree");
|
||||
init_workspace(&root);
|
||||
let root_uri = format!("file://{}", root.display());
|
||||
std::fs::create_dir_all(root.join("docs").join("Page.ocr")).expect("ocr dir");
|
||||
std::fs::write(root.join("docs").join("Page.md"), "# Page\n").expect("page");
|
||||
std::fs::write(
|
||||
root.join("docs").join("Page.ocr").join("photo.png.ocr.md"),
|
||||
"---\nmnote_ocr_version: 1\nprovider: mock\nmodel_version: vlm\nowner_document: ../Page.md\nsource_path: ./Page.assets/photo.png\nsource_root_relative_path: docs/Page.assets/photo.png\nsource_size: 3\nsource_mtime_ms: 1\nstatus: done\ncreated_at: 1\nupdated_at: 1\n---\n\nOCR-only-token\n",
|
||||
)
|
||||
.expect("ocr markdown");
|
||||
|
||||
let file_tree = load_local_folder_file_tree_children_snapshot(&root_uri, "docs/Page.ocr")
|
||||
.expect("file tree");
|
||||
let file_items = file_tree.projection["items"]
|
||||
.as_array()
|
||||
.expect("file items");
|
||||
assert!(file_items
|
||||
.iter()
|
||||
.any(|item| item["title"].as_str() == Some("photo.png.ocr.md")));
|
||||
|
||||
let page_tree = load_local_folder_page_tree_snapshot(&root_uri).expect("page tree");
|
||||
let page_items = page_tree.projection["items"]
|
||||
.as_array()
|
||||
.expect("page items");
|
||||
assert!(page_items
|
||||
.iter()
|
||||
.any(|item| item["documentId"].as_str() == Some("local-md:docs~2FPage.md")));
|
||||
assert!(!page_items.iter().any(|item| item["documentId"].as_str()
|
||||
== Some("local-md:docs~2FPage.ocr~2Fphoto.png.ocr.md")));
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_rename_markdown_page_renames_nested_bundle() {
|
||||
let root = temp_root("mnote-local-rename-nested-bundle");
|
||||
|
||||
Reference in New Issue
Block a user