Implement Rust web sidebar title and tree interactions

This commit is contained in:
lix-2026
2026-04-30 05:46:36 +08:00
parent 559c5ce652
commit 3cc090ba5e
35 changed files with 3029 additions and 424 deletions
@@ -10,10 +10,10 @@
//! ```
use crate::page_aggregate::{
PageAggregate, PageBody, PageHead, PageIdentity, PageLayout, PageOptions, PagePermissions,
PageStats, PageTree,
PageAggregate, PageAggregateSource, PageBody, PageHead, PageIdentity, PageLayout, PageOptions,
PagePermissions, PageStats, PageTree,
};
use serde_json::Value;
use serde_json::{to_value, Value};
/// PageAggregate 构建器。
///
@@ -23,6 +23,11 @@ use serde_json::Value;
pub struct PageAggregateBuilder {
document_id: String,
workspace_id: String,
source: PageAggregateSource,
parent_id: Option<String>,
path: Vec<String>,
sidebar_tree_membership: Vec<String>,
body_ref: Option<String>,
title: String,
updated_at: Value,
read_only: bool,
@@ -58,6 +63,11 @@ impl PageAggregateBuilder {
Self {
document_id: String::new(),
workspace_id: "default".to_string(),
source: PageAggregateSource::CompatMetaContentJoin,
parent_id: None,
path: Vec::new(),
sidebar_tree_membership: vec!["page-tree".into(), "file-tree".into()],
body_ref: None,
title: "无标题".to_string(),
updated_at: Value::Null,
read_only: false,
@@ -100,6 +110,31 @@ impl PageAggregateBuilder {
self
}
pub fn source(mut self, value: PageAggregateSource) -> Self {
self.source = value;
self
}
pub fn parent_id(mut self, value: Option<String>) -> Self {
self.parent_id = value;
self
}
pub fn path(mut self, value: Vec<String>) -> Self {
self.path = value;
self
}
pub fn sidebar_tree_membership(mut self, value: Vec<String>) -> Self {
self.sidebar_tree_membership = value;
self
}
pub fn body_ref(mut self, value: Option<String>) -> Self {
self.body_ref = value;
self
}
// ── Head ──
pub fn title(mut self, value: impl Into<String>) -> Self {
@@ -249,8 +284,40 @@ impl PageAggregateBuilder {
/// 消费 builder 并产出 `PageAggregate`。
pub fn build(self) -> PageAggregate {
let page_options = PageOptions {
wide_layout: self.wide_layout,
small_text: self.small_text,
layout_density: self.layout_density,
show_heading_numbers: self.show_heading_numbers,
show_toc: self.show_toc,
show_structure: self.show_structure,
protect_editing: self.protect_editing,
show_word_count: self.show_word_count,
collapse_backlinks: self.collapse_backlinks,
page_font: self.page_font,
hide_child_pages: self.hide_child_pages,
show_block_ref_count: self.show_block_ref_count,
embed_default_block_id: self.embed_default_block_id,
};
let layout_options = to_value(&page_options).expect("PageOptions 序列化不应失败");
let updated_at_text = self.updated_at.as_str().map(ToOwned::to_owned);
let page_id = self.document_id.clone();
PageAggregate {
schema: "mnote.page_aggregate.v1".to_string(),
projection_version: PageAggregate::VERSION,
source: self.source,
page_id,
parent_id: self.parent_id,
title: self.title.clone(),
path: if self.path.is_empty() {
vec![self.document_id.clone()]
} else {
self.path
},
sidebar_tree_membership: self.sidebar_tree_membership,
body_ref: self.body_ref,
layout_options,
updated_at: updated_at_text,
identity: PageIdentity {
document_id: self.document_id,
workspace_id: self.workspace_id,
@@ -265,21 +332,7 @@ impl PageAggregateBuilder {
},
},
layout: PageLayout {
page_options: PageOptions {
wide_layout: self.wide_layout,
small_text: self.small_text,
layout_density: self.layout_density,
show_heading_numbers: self.show_heading_numbers,
show_toc: self.show_toc,
show_structure: self.show_structure,
protect_editing: self.protect_editing,
show_word_count: self.show_word_count,
collapse_backlinks: self.collapse_backlinks,
page_font: self.page_font,
hide_child_pages: self.hide_child_pages,
show_block_ref_count: self.show_block_ref_count,
embed_default_block_id: self.embed_default_block_id,
},
page_options,
},
body: PageBody {
content: self.content,