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
+131 -1
View File
@@ -1,12 +1,19 @@
pub mod command;
pub mod common;
pub mod ai;
pub mod editor;
pub mod governance;
pub mod kernel;
pub mod mindmap;
pub mod page_aggregate;
pub mod query;
pub mod search;
pub mod tool;
pub use ai::{
AiEvent, AiEventKind, AiRuntimeOwner, AiSession, AiStructuredWriteKind,
AiStructuredWriteResult, AiToolCall,
};
pub use command::{
CommandEnvelope, CopyTreeDocumentPages, CreateDocumentPage, CreatePage, CreateWorkspace,
DeleteBlock, DeleteDocumentPage, DuplicateDocumentPage, EmbedBlock, InsertBlock, MoveBlock,
@@ -42,12 +49,23 @@ pub use kernel::{
KernelProjectionResult, KernelProjectionRowKind, KernelRefsPayload, KernelSubtreeRef,
KernelSubtreeResult, KernelTraverseGraph, KernelUpdateNode,
};
pub use mindmap::{MindmapNodeData, MindmapNodeInput, MindmapNodeRef, MindmapOp, MindmapTreeNode};
pub use mindmap::{
MindmapCommand, MindmapNodeData, MindmapNodeInput, MindmapNodeRef, MindmapOp,
MindmapProjection, MindmapProjectionEdge, MindmapProjectionNode, MindmapProjectionOwner,
MindmapTreeNode,
};
pub use page_aggregate::{
PageAggregateProjection, PageAggregateSource, PageBody, PageHead, PageIdentity, PageLayout,
PageOptions, PagePermissions, PageStats, PageTree,
};
pub use query::{
GetBlock, GetBridgeCommand, GetBridgeRequest, GetBridgeTrace, GetMindmap, GetPage,
GetPageContent, GetPageMeta, ListBridgeWorkspaceOverview, ListPageBlocks, ListSidebarDataset,
QueryEnvelope, SearchBlocks, SearchDocuments, SearchPages, SearchRecent,
};
pub use search::{
SearchHighlight, SearchProjectionOwner, SearchQuery, SearchResultProjection, SearchScope,
};
pub use tool::{
default_tool_registry, invocation_kind_label, tool_effect_label, tool_mode_label,
InvocationKind, ToolEffect, ToolExecutionMode, ToolInvocation, ToolRegistry, ToolSetSpec,
@@ -232,4 +250,116 @@ mod tests {
assert!(recovery.write_toolset);
assert_eq!(recovery.tool_names, &["event_replay", "index_rebuild"]);
}
#[test]
fn page_aggregate_projection_contract_covers_kernel_owned_fields() {
let projection = PageAggregateProjection {
schema: "mnote.page_aggregate.v1".into(),
projection_version: 1,
source: PageAggregateSource::KernelProjection,
page_id: "page_1".into(),
parent_id: Some("root".into()),
title: "Rust 页面".into(),
path: vec!["root".into(), "page_1".into()],
sidebar_tree_membership: vec!["workspace-sidebar".into()],
body_ref: Some("page_1:body".into()),
layout_options: serde_json::json!({"wideLayout": true}),
updated_at: Some("2026-04-29T00:00:00Z".into()),
identity: page_aggregate::PageIdentity {
document_id: "page_1".into(),
workspace_id: "ws_demo".into(),
},
head: page_aggregate::PageHead {
title: "Rust 页面".into(),
updated_at: serde_json::json!("2026-04-29T00:00:00Z"),
permissions: page_aggregate::PagePermissions {
read_only: false,
disable_download: false,
disable_copy: false,
},
},
layout: page_aggregate::PageLayout {
page_options: page_aggregate::PageOptions::default(),
},
body: page_aggregate::PageBody {
content: serde_json::json!([]),
revision: serde_json::json!(7),
conflict_detection_key: serde_json::json!("page_1:7"),
},
tree: page_aggregate::PageTree {
page_subtree: serde_json::json!({"rootNodeId": "page_1"}),
},
stats: page_aggregate::PageStats::default(),
};
assert_eq!(projection.source, PageAggregateSource::KernelProjection);
assert_eq!(projection.projection_version, 1);
assert_eq!(projection.page_id, "page_1");
assert_eq!(projection.identity.document_id, "page_1");
}
#[test]
fn search_projection_contract_marks_owner_and_scope() {
let result = SearchResultProjection {
id: "page_1".into(),
title: "Rust 搜索".into(),
snippet: "Rust Web".into(),
updated_at: Some("2026-04-29T00:00:00Z".into()),
created_at: None,
match_field: "title".into(),
has_ocr: false,
public_path: "/documents/page_1".into(),
node_id: Some("page_1".into()),
subtree_root_id: Some("page_1".into()),
evidence: vec![],
score: 3.0,
projection_owner: SearchProjectionOwner::RustKernel,
};
let query = SearchQuery {
query: "Rust".into(),
workspace_id: "ws_demo".into(),
scope: SearchScope::Workspace,
page_id: None,
limit: 20,
};
assert_eq!(query.scope, SearchScope::Workspace);
assert_eq!(result.projection_owner, SearchProjectionOwner::RustKernel);
}
#[test]
fn ai_protocol_contract_exposes_structured_write_result() {
let result = AiStructuredWriteResult {
ok: true,
write_kind: AiStructuredWriteKind::SummaryNode,
command_name: "tree.node.create".into(),
target_id: Some("summary_1".into()),
revision: Some(2),
};
assert_eq!(result.write_kind, AiStructuredWriteKind::SummaryNode);
assert_eq!(result.command_name, "tree.node.create");
}
#[test]
fn mindmap_projection_and_command_contract_cover_kernel_truth() {
let projection = MindmapProjection {
schema: "mnote.mindmap_projection.v1".into(),
map_id: "mind_1".into(),
root_node: "root".into(),
nodes: vec![],
edges: vec![],
layout_hints: serde_json::json!({"layout": "right"}),
revision: 1,
owner: MindmapProjectionOwner::RustKernel,
};
let command = MindmapCommand::RenameNode {
map_id: "mind_1".into(),
node_id: "root".into(),
title: "新标题".into(),
};
assert_eq!(projection.owner, MindmapProjectionOwner::RustKernel);
assert!(matches!(command, MindmapCommand::RenameNode { .. }));
}
}