fix: stabilize local folder AI document workflow

- scope local-folder PageTree revision and document sidebar rendering to fileTreeScope

- preserve projected table/image attrs for local Markdown aggregate fallback

- avoid FileTree restore forced layouts on cold design open

- add API ChatOnly provider runtime and local OCR task handling regressions
This commit is contained in:
lix-2026
2026-06-02 17:17:49 +08:00
parent 610b23d3a5
commit 9f4b5c4d48
27 changed files with 3825 additions and 229 deletions
+65 -4
View File
@@ -11,8 +11,8 @@ use crate::routes::gateway::default_workspace_name_for_context;
use crate::routes::local_folder_source::{
ensure_local_workspace_read_access_with_state, is_local_access_policy_admin_context,
load_local_folder_file_tree_children_snapshot,
load_local_folder_file_tree_snapshot_with_reveal, load_local_folder_page_tree_snapshot,
resolve_local_markdown_page_aggregate,
load_local_folder_file_tree_snapshot_with_reveal, load_local_folder_page_tree_scope_snapshot,
load_local_folder_page_tree_snapshot, resolve_local_markdown_page_aggregate,
};
use crate::routes::query_support::execute_runtime_query_against_data;
use crate::routes::snapshot_support::{
@@ -215,7 +215,8 @@ pub async fn document_page_shell(
let (sidebar_tree_html, file_tree_html) = if is_local_folder {
let root_uri = query.root_uri.as_deref().unwrap_or_default();
(
render_local_sidebar_tree_html(root_uri, Some(&document_id)).unwrap_or_default(),
render_local_sidebar_tree_html_scoped(root_uri, Some(&document_id), file_tree_scope)
.unwrap_or_default(),
render_local_file_tree_html_scoped(root_uri, Some(&document_id), None, file_tree_scope)
.unwrap_or_default(),
)
@@ -2659,7 +2660,22 @@ pub(crate) fn render_local_sidebar_tree_html(
root_uri: &str,
active_document_id: Option<&str>,
) -> Result<String, WebError> {
let snapshot = load_local_folder_page_tree_snapshot(root_uri)?;
render_local_sidebar_tree_html_scoped(root_uri, active_document_id, None)
}
pub(crate) fn render_local_sidebar_tree_html_scoped(
root_uri: &str,
active_document_id: Option<&str>,
file_tree_scope: Option<&str>,
) -> Result<String, WebError> {
let snapshot = if let Some(scope) = file_tree_scope
.map(str::trim)
.filter(|value| !value.is_empty())
{
load_local_folder_page_tree_scope_snapshot(root_uri, scope)?
} else {
load_local_folder_page_tree_snapshot(root_uri)?
};
Ok(render_local_sidebar_tree_html_from_snapshot(
&snapshot,
active_document_id,
@@ -4277,6 +4293,51 @@ mod tests {
));
}
#[tokio::test]
async fn document_shell_local_folder_filetree_scope_renders_scoped_page_tree() {
let root = std::env::temp_dir().join(format!(
"mnote-local-document-shell-scoped-{}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join("design").join("done")).expect("create design done");
std::fs::write(root.join("Home.md"), "# Home\n").expect("write home");
std::fs::write(
root.join("design").join("done").join("Target.md"),
"# Target\n",
)
.expect("write target");
let root_uri = format!("file://{}", root.display());
init_local_workspace(&root, "user_test");
let response = app()
.oneshot(
Request::builder()
.uri(format!(
"/documents/local-md:design~2Fdone~2FTarget.md?sourceKind=local_folder&rootUri={root_uri}&treeView=filetree&fileTreeScope=design"
))
.header("x-mnote-actor-id", "user_test")
.header("x-mnote-actor-type", "user")
.body(Body::empty())
.expect("request"),
)
.await
.expect("response");
let _ = std::fs::remove_dir_all(&root);
assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), usize::MAX)
.await
.expect("body");
let html = String::from_utf8(body.to_vec()).expect("html");
assert!(html.contains(r#"data-node-id="local-md:design~2Fdone~2FTarget.md""#));
assert!(
!html.contains(r#"data-node-id="local-md:Home.md""#),
"文档页带 fileTreeScope 时 PageTree 不应回退到 workspace root 全量扫描"
);
}
#[test]
fn document_conflict_panel_runtime_contains_dom_helpers() {
const DOCUMENT_CONFLICT_PANEL_RUNTIME_JS: &str =