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

清理历史 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
+49 -31
View File
@@ -521,6 +521,7 @@ pub async fn content(
}
pub async fn page_body_write(
State(state): State<AppState>,
Extension(context): Extension<RequestContext>,
Json(body): Json<core_protocol::PageBodyWriteRequest>,
) -> Result<(StatusCode, HeaderMap, Json<Value>), WebError> {
@@ -548,7 +549,7 @@ pub async fn page_body_write(
}
ensure_local_workspace_access(&context, root_uri)
.map_err(|error| error.with_context(&context))?;
let result = write_local_markdown_page_body(&body)?;
let result = write_local_markdown_page_body(&body, Some(&state.buffer_store))?;
Ok(ok_response(&context, result))
}
@@ -580,23 +581,26 @@ pub async fn save(
.expected_file_version
.as_deref()
.or(body.conflict_detection_key.as_deref());
let result = write_local_markdown_page_body(&core_protocol::PageBodyWriteRequest {
document_id: document_id.to_string(),
workspace_id: body.workspace_id.clone().unwrap_or_default(),
source_kind: core_protocol::WorkspaceSourceKind::LocalFolder,
root_uri: root_uri.to_string(),
expected_file_version: expected_file_version.map(ToOwned::to_owned),
base_content_hash: body.base_content_hash.clone(),
content_format: body
.content_format
.clone()
.unwrap_or_else(|| "editorBlocks".into()),
content: body.content.clone(),
editor_source: body
.editor_source
.clone()
.or_else(|| Some("documents/save-compat".into())),
})?;
let result = write_local_markdown_page_body(
&core_protocol::PageBodyWriteRequest {
document_id: document_id.to_string(),
workspace_id: body.workspace_id.clone().unwrap_or_default(),
source_kind: core_protocol::WorkspaceSourceKind::LocalFolder,
root_uri: root_uri.to_string(),
expected_file_version: expected_file_version.map(ToOwned::to_owned),
base_content_hash: body.base_content_hash.clone(),
content_format: body
.content_format
.clone()
.unwrap_or_else(|| "editorBlocks".into()),
content: body.content.clone(),
editor_source: body
.editor_source
.clone()
.or_else(|| Some("documents/save-compat".into())),
},
Some(&state.buffer_store),
)?;
return Ok(ok_response(&context, result));
}
let effective_workspace_id =
@@ -1368,10 +1372,10 @@ mod tests {
std::process::id()
));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).expect("create local root");
std::fs::create_dir_all(root.join("Old Local Title")).expect("create local page bundle");
std::fs::write(
root.join("README.md"),
"---\nmnote_id: local-stable\ntitle: Old Title\n---\n# Old\n",
root.join("Old Local Title").join("Old Local Title.md"),
"# Old\n",
)
.expect("write md");
let root_uri = format!("file://{}", root.display());
@@ -1380,7 +1384,7 @@ mod tests {
&root_uri,
)
.expect("init local workspace");
let document_id = "local-mdid:local-stable";
let document_id = "local-md:Old~20Local~20Title~2FOld~20Local~20Title.md";
let title_response = app()
.oneshot(
@@ -1404,6 +1408,14 @@ mod tests {
.await
.expect("title response");
assert_eq!(title_response.status(), StatusCode::OK);
let title_body = to_bytes(title_response.into_body(), usize::MAX)
.await
.expect("title body");
let title_payload: Value = serde_json::from_slice(&title_body).expect("title json");
let renamed_document_id = title_payload["result"]["documentId"]
.as_str()
.expect("renamed document id")
.to_string();
let save_response = app()
.oneshot(
@@ -1415,7 +1427,7 @@ mod tests {
.header("x-mnote-actor-type", "user")
.body(Body::from(
serde_json::json!({
"documentId": document_id,
"documentId": renamed_document_id,
"sourceKind": "local_folder",
"rootUri": root_uri,
"content": [
@@ -1460,7 +1472,7 @@ mod tests {
.header("x-mnote-actor-type", "user")
.body(Body::from(
serde_json::json!({
"documentId": document_id,
"documentId": renamed_document_id,
"sourceKind": "local_folder",
"rootUri": root_uri,
"options": {
@@ -1476,17 +1488,23 @@ mod tests {
.expect("options response");
assert_eq!(options_response.status(), StatusCode::OK);
let markdown = std::fs::read_to_string(root.join("README.md")).expect("read md");
assert!(markdown.contains("mnote_id: local-stable"));
assert!(markdown.contains("title: New Local Title"));
let markdown =
std::fs::read_to_string(root.join("New Local Title").join("New Local Title.md"))
.expect("read md");
assert!(markdown.contains("## Saved Heading"));
assert!(markdown.contains("Saved body"));
let options = std::fs::read_to_string(root.join(".mnote").join("page-options.json"))
.expect("read page options");
let options_json: Value = serde_json::from_str(&options).expect("options json");
assert_eq!(options_json["pages"][document_id]["wideLayout"], true);
assert_eq!(options_json["pages"][document_id]["showToc"], false);
assert_eq!(
options_json["pages"][&renamed_document_id]["wideLayout"],
true
);
assert_eq!(
options_json["pages"][&renamed_document_id]["showToc"],
false
);
let _ = std::fs::remove_dir_all(&root);
}
@@ -1510,7 +1528,7 @@ mod tests {
&root_uri,
)
.expect("init local workspace");
let document_id = "local-mdid:expected-file-version";
let document_id = "local-md:README.md";
let aggregate = crate::routes::local_folder_source::resolve_local_markdown_page_aggregate(
&root_uri,
document_id,
@@ -1582,7 +1600,7 @@ mod tests {
assert!(payload["details"]["conflict"]["currentDiskVersion"]
.as_str()
.unwrap_or("")
.starts_with("local-md:local-mdid:expected-file-version:"));
.starts_with("local-md:local-md:README.md:"));
assert!(payload["details"]["conflict"]["suggestedActions"]
.as_array()
.unwrap()