feat: 收口 mindmap Phase 6 Leptos UI shell

This commit is contained in:
lix-2026
2026-05-11 12:27:40 +08:00
parent b7dddd2a66
commit b5eb27fabc
51 changed files with 8669 additions and 1313 deletions
+191 -3
View File
@@ -51,9 +51,11 @@ pub use kernel::{
WorkspaceSourceCapability, WorkspaceSourceKind,
};
pub use mindmap::{
MindmapCommand, MindmapNodeData, MindmapNodeInput, MindmapNodeRef, MindmapOp,
MindmapProjection, MindmapProjectionEdge, MindmapProjectionNode, MindmapProjectionOwner,
MindmapTreeNode,
MindmapAdapterProjection, MindmapAssociativeLine, MindmapCommand, MindmapKernelCapabilities,
MindmapKernelCommand, MindmapKernelEdge, MindmapKernelNode, MindmapKernelProjection,
MindmapNodeData, MindmapNodeInput, MindmapNodeRef, MindmapOp, MindmapProjection,
MindmapProjectionEdge, MindmapProjectionNode, MindmapProjectionOwner, MindmapProjectionSource,
MindmapSummary, MindmapTreeNode,
};
pub use page_aggregate::{
PageAggregateProjection, PageAggregateSource, PageBody, PageHead, PageIdentity, PageLayout,
@@ -363,4 +365,190 @@ mod tests {
assert_eq!(projection.owner, MindmapProjectionOwner::RustKernel);
assert!(matches!(command, MindmapCommand::RenameNode { .. }));
}
#[test]
fn mindmap_kernel_and_adapter_projection_serialize_as_camel_case() {
let kernel_projection = MindmapKernelProjection {
schema: "mnote.mindmap.kernel_projection.v1".into(),
document_id: "doc_1".into(),
mindmap_id: "mind_1".into(),
root_node_id: "root".into(),
revision: 7,
nodes: vec![
MindmapKernelNode {
node_id: "root".into(),
parent_id: None,
text: "KMIND".into(),
order: 0,
collapsed: false,
style: Default::default(),
refs: Default::default(),
},
MindmapKernelNode {
node_id: "child_1".into(),
parent_id: Some("root".into()),
text: "二级节点".into(),
order: 1,
collapsed: false,
style: Default::default(),
refs: Default::default(),
},
],
edges: vec![MindmapKernelEdge {
edge_id: "root->child_1".into(),
from_node_id: "root".into(),
to_node_id: "child_1".into(),
edge_kind: "tree".into(),
}],
summaries: vec![MindmapSummary {
summary_id: "summary_1".into(),
parent_node_id: "child_1".into(),
node_ids: vec!["child_1".into()],
text: "概要".into(),
style: Default::default(),
}],
associative_lines: vec![MindmapAssociativeLine {
line_id: "line_1".into(),
from_node_id: "root".into(),
to_node_id: "child_1".into(),
text: Some("关联".into()),
style: Default::default(),
}],
layout: serde_json::json!({ "type": "logicalStructure" }),
theme: serde_json::json!({ "template": "classic" }),
view: serde_json::json!({ "zoom": 1.0 }),
capabilities: MindmapKernelCapabilities {
can_edit: true,
can_edit_text: true,
can_insert_child: true,
can_insert_sibling_after: true,
can_delete_node: true,
can_move_node: true,
can_patch_view: true,
can_set_layout: true,
can_set_theme: true,
},
source: MindmapProjectionSource::Kernel,
};
let adapter_projection = MindmapAdapterProjection {
schema: "mnote.mindmap.simple_mind_map_scene.v1".into(),
runtime: "simple-mind-map".into(),
root: serde_json::json!({
"data": { "uid": "root", "text": "KMIND" },
"children": []
}),
layout: serde_json::json!("logicalStructure"),
theme: serde_json::json!("classic"),
theme_config: serde_json::json!({}),
view: serde_json::json!({ "scale": 1.0 }),
config: serde_json::json!({}),
compat_payload: serde_json::json!({}),
kernel_revision: 7,
};
let kernel_value =
serde_json::to_value(kernel_projection).expect("kernel projection should serialize");
let adapter_value =
serde_json::to_value(adapter_projection).expect("adapter projection should serialize");
assert_eq!(kernel_value["documentId"], serde_json::json!("doc_1"));
assert_eq!(kernel_value["mindmapId"], serde_json::json!("mind_1"));
assert_eq!(kernel_value["rootNodeId"], serde_json::json!("root"));
assert_eq!(
kernel_value["nodes"][1]["parentId"],
serde_json::json!("root")
);
assert_eq!(
kernel_value["associativeLines"][0]["fromNodeId"],
serde_json::json!("root")
);
assert_eq!(
kernel_value["capabilities"]["canEditText"],
serde_json::json!(true)
);
assert_eq!(kernel_value["source"], serde_json::json!("kernel"));
assert_eq!(
adapter_value["runtime"],
serde_json::json!("simple-mind-map")
);
assert_eq!(adapter_value["themeConfig"], serde_json::json!({}));
assert_eq!(adapter_value["compatPayload"], serde_json::json!({}));
assert_eq!(adapter_value["kernelRevision"], serde_json::json!(7));
}
#[test]
fn mindmap_kernel_command_contract_covers_minimum_write_surface() {
let commands = vec![
MindmapKernelCommand::UpdateText {
mindmap_id: "mind_1".into(),
node_id: "root".into(),
text: "新标题".into(),
},
MindmapKernelCommand::InsertChild {
mindmap_id: "mind_1".into(),
parent_node_id: "root".into(),
node: MindmapNodeInput {
uid: Some("child_1".into()),
text: "子节点".into(),
hyperlink: None,
note: None,
refs: None,
},
},
MindmapKernelCommand::InsertSiblingAfter {
mindmap_id: "mind_1".into(),
target_node_id: "child_1".into(),
node: MindmapNodeInput {
uid: Some("child_2".into()),
text: "同级节点".into(),
hyperlink: None,
note: None,
refs: None,
},
},
MindmapKernelCommand::DeleteNode {
mindmap_id: "mind_1".into(),
node_id: "child_2".into(),
},
MindmapKernelCommand::MoveNode {
mindmap_id: "mind_1".into(),
node_id: "child_1".into(),
new_parent_node_id: "root".into(),
order: Some(1),
},
MindmapKernelCommand::PatchView {
mindmap_id: "mind_1".into(),
patch: serde_json::json!({ "scale": 1.2 }),
},
MindmapKernelCommand::SetLayout {
mindmap_id: "mind_1".into(),
layout: serde_json::json!({ "type": "mindMap" }),
},
MindmapKernelCommand::SetTheme {
mindmap_id: "mind_1".into(),
theme: serde_json::json!({ "template": "classic" }),
theme_config: serde_json::json!({ "lineColor": "#f59e0b" }),
},
];
let encoded = serde_json::to_value(&commands).expect("commands should serialize");
assert_eq!(encoded[0]["type"], serde_json::json!("updateText"));
assert_eq!(encoded[1]["type"], serde_json::json!("insertChild"));
assert_eq!(encoded[2]["type"], serde_json::json!("insertSiblingAfter"));
assert_eq!(encoded[3]["type"], serde_json::json!("deleteNode"));
assert_eq!(encoded[4]["type"], serde_json::json!("moveNode"));
assert_eq!(encoded[5]["type"], serde_json::json!("patchView"));
assert_eq!(encoded[6]["type"], serde_json::json!("setLayout"));
assert_eq!(encoded[7]["type"], serde_json::json!("setTheme"));
assert_eq!(encoded[0]["mindmapId"], serde_json::json!("mind_1"));
assert_eq!(encoded[1]["parentNodeId"], serde_json::json!("root"));
assert_eq!(encoded[4]["newParentNodeId"], serde_json::json!("root"));
assert_eq!(
encoded[7]["themeConfig"]["lineColor"],
serde_json::json!("#f59e0b")
);
}
}