收口本地工作区清理与资源投影

清理历史 Electron、Graphify、沙箱和截图等仓库跟踪残留,补充 CodeGraph 与 Convex active deploy source 协作说明。

新增 tree-first 下一阶段设计稿和 2026-05-20 清理总结,记录本地工作区、路径身份和 Zed/Lapce/VSCode 参考收口方向。

扩展 Rust Web 本地文件夹、DocumentBuffer、mindmap 资源、tree runtime 和页面聚合链路,并补充 task455 local-folder mindmap clean smoke。

验证:git diff --check 通过;pnpm store status --store-dir .pnpm-store 通过;npm ls --depth=0 --json 通过;find -L node_modules 未发现断链。cargo test -p mnote-web 当前 418 passed / 35 failed。
This commit is contained in:
lix-2026
2026-05-20 10:43:38 +08:00
parent 90818cdf75
commit b4c8bcb647
73 changed files with 8073 additions and 8240 deletions
+95 -67
View File
@@ -443,48 +443,8 @@ pub(crate) fn collect_page_tree_render_rows(projection: &Value) -> Vec<PageTreeR
rows
}
fn short_mindmap_file_name(raw: &str, asset_id: Option<&str>) -> String {
let source = asset_id
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or(raw.trim());
let digits = source
.chars()
.rev()
.take_while(|ch| ch.is_ascii_digit())
.collect::<String>()
.chars()
.rev()
.collect::<String>();
let suffix = if digits.len() >= 4 {
digits[digits.len().saturating_sub(4)..].to_string()
} else {
source
.trim_start_matches("mindmap")
.trim_start_matches(|ch| ch == '-' || ch == '_')
.chars()
.take(6)
.collect::<String>()
};
if suffix.trim().is_empty() {
"思维导图.json".to_string()
} else {
format!("思维导图-{suffix}.json")
}
}
fn normalize_filetree_mindmap_title(
raw_title: &str,
asset_id: Option<&str>,
icon_kind: &str,
resource_kind: Option<&str>,
) -> String {
fn normalize_filetree_mindmap_title(raw_title: &str) -> String {
let title = raw_title.trim();
let is_mindmap = icon_kind == "mindmap" || resource_kind == Some("mindmap");
let generated = title.starts_with("mindmap-") || title.starts_with("mindmap_");
if is_mindmap && (generated || title.chars().count() > 24) {
return short_mindmap_file_name(title, asset_id);
}
if title.is_empty() {
"无标题".to_string()
} else {
@@ -592,14 +552,7 @@ pub(crate) fn collect_filetree_render_rows(
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned),
title: normalize_filetree_mindmap_title(
raw_title,
asset_id.as_deref(),
&icon_kind,
resource_meta
.and_then(|meta| meta.get("resourceKind"))
.and_then(Value::as_str),
),
title: normalize_filetree_mindmap_title(raw_title),
depth: item.get("depth").and_then(Value::as_u64).unwrap_or(0) as u32,
expandable: item
.get("expandable")
@@ -7336,8 +7289,10 @@ mod tests {
.expect("body");
let html = String::from_utf8(body.to_vec()).expect("utf8");
assert!(html.contains("local_folder"));
assert!(html.contains("Frontmatter Title"));
assert!(html.contains("Child H1"));
assert!(html.contains("README"));
assert!(html.contains("child"));
assert!(!html.contains("Frontmatter Title"));
assert!(!html.contains("Child H1"));
assert!(html.contains(">docs<") || html.contains("docs"));
assert!(!html.contains("image.png"));
}
@@ -7380,8 +7335,23 @@ mod tests {
.as_str()
.expect("document id")
.to_string();
assert!(root.join("新页面.md").exists());
assert!(root.join(".mnote").join("page-ids.json").exists());
let created_relative_path = payload["result"]["execution"]["relativePath"]
.as_str()
.expect("created relative path");
let (created_dir, created_file) = created_relative_path
.split_once('/')
.expect("created nested bundle path");
assert!(created_dir.starts_with("新页面"));
assert_eq!(created_file, format!("{created_dir}.md"));
assert!(root.join(created_dir).join(created_file).exists());
assert_eq!(
document_id,
format!(
"local-md:{}",
crate::routes::local_folder_source::encode_local_id_segment(created_relative_path)
)
);
assert!(!root.join(".mnote").join("page-ids.json").exists());
let rename_response = app()
.oneshot(
@@ -7406,8 +7376,18 @@ mod tests {
"{}",
String::from_utf8_lossy(&rename_body)
);
assert!(!root.join("新页面.md").exists());
assert!(root.join("重命名页面.md").exists());
assert!(!root.join(created_dir).exists());
assert!(root.join("重命名页面").join("重命名页面.md").exists());
let rename_payload: Value = serde_json::from_slice(&rename_body).expect("rename json");
let renamed_document_id = rename_payload["result"]["documentId"]
.as_str()
.expect("renamed document id")
.to_string();
assert_eq!(
renamed_document_id,
"local-md:~E9~87~8D~E5~91~BD~E5~90~8D~E9~A1~B5~E9~9D~A2~2F~E9~87~8D~E5~91~BD~E5~90~8D~E9~A1~B5~E9~9D~A2.md"
);
assert!(!root.join(".mnote").join("page-ids.json").exists());
std::fs::create_dir_all(root.join("docs")).expect("create docs dir");
let move_response = app()
@@ -7417,15 +7397,38 @@ mod tests {
.uri("/api/tree/commands")
.header("content-type", "application/json")
.body(Body::from(format!(
r#"{{"action":"move","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{document_id}","parentId":"local-dir:docs","sortOrder":0}}"#
r#"{{"action":"move","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{renamed_document_id}","parentId":"local-dir:docs","sortOrder":0}}"#
)))
.expect("request"),
)
.await
.expect("response");
assert_eq!(move_response.status(), StatusCode::OK);
assert!(!root.join("重命名页面.md").exists());
assert!(root.join("docs").join("重命名页面.md").exists());
let move_status = move_response.status();
let move_body = axum::body::to_bytes(move_response.into_body(), usize::MAX)
.await
.expect("move body");
assert_eq!(
move_status,
StatusCode::OK,
"{}",
String::from_utf8_lossy(&move_body)
);
assert!(!root.join("重命名页面").exists());
assert!(root
.join("docs")
.join("重命名页面")
.join("重命名页面.md")
.exists());
let move_payload: Value = serde_json::from_slice(&move_body).expect("move json");
let moved_document_id = move_payload["result"]["documentId"]
.as_str()
.expect("moved document id")
.to_string();
assert_eq!(
moved_document_id,
"local-md:docs~2F~E9~87~8D~E5~91~BD~E5~90~8D~E9~A1~B5~E9~9D~A2~2F~E9~87~8D~E5~91~BD~E5~90~8D~E9~A1~B5~E9~9D~A2.md"
);
assert!(!root.join(".mnote").join("page-ids.json").exists());
let copy_response = app()
.oneshot(
@@ -7434,7 +7437,7 @@ mod tests {
.uri("/api/tree/commands")
.header("content-type", "application/json")
.body(Body::from(format!(
r#"{{"action":"copy","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{document_id}","parentId":"local-dir:docs"}}"#
r#"{{"action":"copy","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{moved_document_id}","parentId":"local-dir:docs"}}"#
)))
.expect("request"),
)
@@ -7455,7 +7458,11 @@ mod tests {
.as_str()
.expect("copied document id")
.to_string();
assert!(root.join("docs").join("重命名页面 2.md").exists());
assert!(root
.join("docs")
.join("重命名页面 2")
.join("重命名页面 2.md")
.exists());
let folder_response = app()
.oneshot(
@@ -7480,20 +7487,22 @@ mod tests {
.uri("/api/tree/commands")
.header("content-type", "application/json")
.body(Body::from(format!(
r#"{{"action":"delete","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{document_id}"}}"#
r#"{{"action":"delete","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{moved_document_id}"}}"#
)))
.expect("request"),
)
.await
.expect("response");
assert_eq!(delete_response.status(), StatusCode::OK);
assert!(!root.join("docs").join("重命名页面.md").exists());
assert!(!root.join("docs").join("重命名页面").exists());
assert!(root
.join(".mnote")
.join("trash")
.join("重命名页面")
.join("重命名页面.md")
.exists());
assert!(root.join(".mnote").join("trash-index.json").exists());
assert!(!root.join(".mnote").join("page-ids.json").exists());
let restore_response = app()
.oneshot(
@@ -7502,14 +7511,33 @@ mod tests {
.uri("/api/tree/commands")
.header("content-type", "application/json")
.body(Body::from(format!(
r#"{{"action":"restore","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{document_id}"}}"#
r#"{{"action":"restore","sourceKind":"local_folder","rootUri":"{root_uri}","documentId":"{moved_document_id}"}}"#
)))
.expect("request"),
)
.await
.expect("response");
assert_eq!(restore_response.status(), StatusCode::OK);
assert!(root.join("docs").join("重命名页面.md").exists());
let restore_status = restore_response.status();
let restore_body = axum::body::to_bytes(restore_response.into_body(), usize::MAX)
.await
.expect("restore body");
assert_eq!(
restore_status,
StatusCode::OK,
"{}",
String::from_utf8_lossy(&restore_body)
);
assert!(root
.join("docs")
.join("重命名页面")
.join("重命名页面.md")
.exists());
let restore_payload: Value = serde_json::from_slice(&restore_body).expect("restore json");
assert_eq!(
restore_payload["result"]["documentId"].as_str(),
Some(moved_document_id.as_str())
);
assert!(!root.join(".mnote").join("page-ids.json").exists());
let purge_response = app()
.oneshot(
@@ -7525,7 +7553,7 @@ mod tests {
.await
.expect("response");
assert_eq!(purge_response.status(), StatusCode::OK);
assert!(!root.join("docs").join("重命名页面 2.md").exists());
assert!(!root.join("docs").join("重命名页面 2").exists());
let _ = std::fs::remove_dir_all(&root);
}