Improve local filetree view state and sidebar performance

This commit is contained in:
lix-2026
2026-05-27 11:31:12 +08:00
parent 58e2fdb5d8
commit 3ae33cc21d
56 changed files with 8614 additions and 461 deletions
+66 -29
View File
@@ -8,11 +8,12 @@ use crate::routes::local_folder_source::{
};
use crate::routes::snapshot_support::load_sidebar_dataset;
use crate::routes::web_shell::{
build_document_panes_bootstrap_json, build_editor_bootstrap_json,
build_page_aggregate_snapshot, escape_html, escape_script_json, load_file_tree_html,
load_sidebar_tree_html, load_workspace_shell_projection,
attach_sidebar_shortcuts_to_dataset, build_document_panes_bootstrap_json,
build_editor_bootstrap_json, build_page_aggregate_snapshot, escape_html, escape_script_json,
load_file_tree_html, load_sidebar_tree_html, load_workspace_shell_projection,
render_document_title_controller_script, render_editor_island_adapter_script,
render_local_file_tree_html, render_local_sidebar_tree_html,
render_editor_runtime_preload_links, render_local_file_tree_html,
render_local_file_tree_html_scoped, render_local_sidebar_tree_html,
};
use crate::transport::convex::execute_convex_mutation_by_name;
use crate::workspace_shell::{
@@ -67,6 +68,7 @@ pub(crate) struct RootEntryQuery {
workspace_id: Option<String>,
source_kind: Option<String>,
root_uri: Option<String>,
file_tree_scope: Option<String>,
tree_view: Option<String>,
restore_focus_row_id: Option<String>,
}
@@ -300,6 +302,11 @@ pub async fn root_entry(
.as_deref()
.map(str::trim)
.is_some_and(|value| value == "filetree");
let file_tree_scope = query
.file_tree_scope
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
let (
workspace_id,
workspace_projection,
@@ -318,15 +325,8 @@ pub async fn root_entry(
WebError::bad_request_code("local_folder_root_required", "缺少本地文件夹 rootUri")
})?;
ensure_local_workspace_read_access_with_state(&state, &context, root_uri)?;
let snapshot = if requests_filetree_first {
crate::routes::local_folder_source::load_local_folder_file_tree_snapshot_with_reveal(
root_uri,
requested_page_id.as_deref(),
)?
} else {
load_local_folder_page_tree_snapshot(root_uri)?
};
let workspace_id = snapshot
let page_tree_snapshot = load_local_folder_page_tree_snapshot(root_uri)?;
let workspace_id = page_tree_snapshot
.dataset
.get("workspace")
.and_then(|workspace| workspace.get("id"))
@@ -337,8 +337,15 @@ pub async fn root_entry(
.to_string();
let requested_or_recent_page_id =
choose_root_entry_active_page_id(requested_page_id.clone(), None, None, None);
let mut workspace_dataset = page_tree_snapshot.dataset.clone();
attach_sidebar_shortcuts_to_dataset(
&state,
&context,
&workspace_id,
&mut workspace_dataset,
);
let workspace_projection = build_workspace_shell_projection(
&snapshot.dataset,
&workspace_dataset,
&workspace_id,
requested_or_recent_page_id.as_deref(),
"本地文件夹",
@@ -352,20 +359,18 @@ pub async fn root_entry(
.first()
.map(|item| item.id.as_str()),
);
let sidebar_tree_html = if requests_filetree_first {
String::new()
} else {
render_local_sidebar_tree_html(root_uri, selected_active_page_id.as_deref())?
};
let sidebar_tree_html =
render_local_sidebar_tree_html(root_uri, selected_active_page_id.as_deref())?;
let restore_focus_row_id = query
.restore_focus_row_id
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty());
let file_tree_html = render_local_file_tree_html(
let file_tree_html = render_local_file_tree_html_scoped(
root_uri,
selected_active_page_id.as_deref(),
restore_focus_row_id,
file_tree_scope,
)?;
(
workspace_id,
@@ -399,8 +404,15 @@ pub async fn root_entry(
.filter(|value| !value.is_empty())
.unwrap_or("local-folder")
.to_string();
let mut workspace_dataset = snapshot.dataset.clone();
attach_sidebar_shortcuts_to_dataset(
&state,
&context,
&workspace_id,
&mut workspace_dataset,
);
let workspace_projection = build_workspace_shell_projection(
&snapshot.dataset,
&workspace_dataset,
&workspace_id,
requested_page_id.as_deref(),
&default_workspace_name,
@@ -437,6 +449,7 @@ pub async fn root_entry(
None,
);
let workspace_projection = load_workspace_shell_projection(
Some(&state),
state.config(),
&context,
&workspace_id,
@@ -489,6 +502,7 @@ pub async fn root_entry(
} else {
None
},
file_tree_scope,
);
let workspace_name = workspace_projection.workspace_name.clone();
let active_page_id = selected_active_page_id.unwrap_or_default();
@@ -593,12 +607,19 @@ pub async fn root_entry(
Err(_) => ("MNOTE".to_string(), render_workspace_entry(), String::new()),
}
};
let editor_runtime_preload_links =
if body_extra.contains("document-editor-adapter-runtime.js") {
render_editor_runtime_preload_links()
} else {
""
};
let mut response = Html(format!(
r#"<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>{}</title>
{}
<style>{}</style>
</head>
<body data-mnote-web-owner="mnote-web" data-mnote-shell="workspace" data-mnote-actor-id="{}" data-mnote-source-kind="{}" data-mnote-root-uri="{}">
@@ -607,6 +628,7 @@ pub async fn root_entry(
</body>
</html>"#,
escape_html(&html_title),
editor_runtime_preload_links,
crate::ssr::MNOTE_CSS,
escape_html(context.auth.actor_id.as_str()),
escape_html(active_source_kind.as_deref().unwrap_or("convex_workspace")),
@@ -654,21 +676,25 @@ pub async fn trash_entry(
crate::routes::local_folder_source::local_workspace_id_from_root_uri(root_uri)?;
let sidebar_tree_html = render_local_sidebar_tree_html(root_uri, None).unwrap_or_default();
let file_tree_html = render_local_file_tree_html(root_uri, None, None).unwrap_or_default();
let workspace_projection = build_workspace_shell_projection(
&json!({
"active_workspace_id": workspace_id,
"workspaces": [{ "id": workspace_id, "name": "我的空间" }],
"documents": [],
}),
let mut workspace_dataset = json!({
"active_workspace_id": workspace_id,
"workspaces": [{ "id": workspace_id, "name": "我的空间" }],
"documents": [],
});
attach_sidebar_shortcuts_to_dataset(
&state,
&context,
&workspace_id,
None,
"我的空间",
&mut workspace_dataset,
);
let workspace_projection =
build_workspace_shell_projection(&workspace_dataset, &workspace_id, None, "我的空间");
let workspace_sidebar_html = render_workspace_shell_sidebar_html(
&workspace_projection,
Some(sidebar_tree_html.as_str()),
Some(file_tree_html.as_str()),
None,
None,
);
let trash_workbench_html = render_local_trash_workbench_html(
&workspace_id,
@@ -714,6 +740,7 @@ pub async fn trash_entry(
let workspace_id =
resolve_root_workspace_id(&state, &context, query.workspace_id.as_deref()).await?;
let workspace_projection = load_workspace_shell_projection(
Some(&state),
state.config(),
&context,
&workspace_id,
@@ -732,6 +759,7 @@ pub async fn trash_entry(
Some(sidebar_tree_html.as_str()),
Some(file_tree_html.as_str()),
None,
None,
);
let dataset = load_sidebar_dataset(state.config(), &context, &workspace_id)
.await
@@ -2779,6 +2807,9 @@ mod tests {
assert!(html.contains(r#"data-mnote-shell="workspace""#));
assert!(html.contains("local_folder"));
assert!(html.contains("README.md"));
assert!(html.contains(r#"id="sidebar-tree-root""#));
assert!(html.contains(r#"data-shell-mode="page""#));
assert!(html.contains(r#"data-node-id="local-md:README.md""#));
assert!(html.contains(r#"data-row-id="local:folder:docs""#));
assert!(!html.contains(r#"data-row-id="local:markdown:docs/child.md""#));
assert!(html.contains(r#"data-row-id="local:asset:plain.txt""#));
@@ -2832,6 +2863,12 @@ mod tests {
assert!(html.contains(
r#"<script type="module" src="/api/mnote-browser-runtime/document-editor-adapter-runtime.js"></script>"#
));
assert!(html.contains(
r#"<link rel="modulepreload" href="/api/leptos-tiptap-runtime/mnote-leptos-tiptap-spike-island.js">"#
));
assert!(html.contains(
r#"<link rel="preload" href="/api/leptos-tiptap-runtime/mnote-leptos-tiptap-spike-island_bg.wasm" as="fetch" type="application/wasm" crossorigin>"#
));
assert!(
include_str!("../../browser/document-resource-tab-runtime.js")
.contains("openResourceInActiveTab")