chore: 收口 review 执行清单与 runtime 验证

- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录

- 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目

- 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑

- 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
This commit is contained in:
lix-2026
2026-05-14 05:52:08 +08:00
parent b4a452a8b7
commit 96e03645f7
69 changed files with 4780 additions and 979 deletions
+78 -1
View File
@@ -9,6 +9,9 @@ pub struct WorkspaceShellProjection {
pub workspace_name: String,
pub active_page_id: Option<String>,
pub active_page_title: Option<String>,
pub degraded: bool,
pub degraded_reason: Option<String>,
pub dev_fixture: bool,
pub starred_items: Vec<WorkspaceShellItem>,
pub my_page_items: Vec<WorkspaceShellItem>,
pub bottom_entries: Vec<WorkspaceShellEntry>,
@@ -88,6 +91,23 @@ pub fn build_workspace_shell_projection(
starred_items.sort_by_key(|item| (item.depth, item.title.clone(), item.id.clone()));
let active_page_title = active_title_from_items(&my_page_items, active_page_id.as_deref());
let degraded = dataset
.get("degraded")
.or_else(|| dataset.get("is_degraded"))
.and_then(Value::as_bool)
.unwrap_or(false);
let degraded_reason = dataset
.get("degraded_reason")
.or_else(|| dataset.get("degradedReason"))
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned);
let dev_fixture = dataset
.get("dev_fixture")
.or_else(|| dataset.get("devFixture"))
.and_then(Value::as_bool)
.unwrap_or(false);
WorkspaceShellProjection {
schema: "mnote.workspace_shell.v1".into(),
@@ -95,6 +115,9 @@ pub fn build_workspace_shell_projection(
workspace_name,
active_page_id,
active_page_title,
degraded,
degraded_reason,
dev_fixture,
starred_items,
my_page_items,
bottom_entries: vec![
@@ -278,6 +301,8 @@ mod tests {
assert_eq!(projection.workspace_name, "开发用户 的工作区");
assert_eq!(projection.active_page_id.as_deref(), Some("page_home"));
assert_eq!(projection.active_page_title.as_deref(), Some("个人"));
assert!(!projection.degraded);
assert!(!projection.dev_fixture);
assert_eq!(projection.starred_items.len(), 1);
assert_eq!(projection.starred_items[0].title, "个人");
assert_eq!(projection.my_page_items.len(), 2);
@@ -368,6 +393,42 @@ mod tests {
assert!(html.contains("data-testid=\"wolai-sidebar-empty-state\""));
}
#[test]
fn workspace_shell_sidebar_html_marks_degraded_and_dev_fixture_states() {
let degraded_dataset = json!({
"workspaces": [{ "id": "ws_demo", "name": "开发用户 的工作区" }],
"documents": [],
"degraded": true,
"degraded_reason": "projection_unavailable"
});
let degraded_projection = build_workspace_shell_projection(
&degraded_dataset,
"ws_demo",
None,
"开发用户 的工作区",
);
let degraded_html = render_workspace_shell_sidebar_html(&degraded_projection, None, None);
assert!(degraded_projection.degraded);
assert!(degraded_html.contains("data-mnote-workspace-shell-degraded=\"true\""));
assert!(degraded_html.contains(
"data-mnote-workspace-shell-degraded-reason=\"projection_unavailable\""
));
let dev_dataset = json!({
"workspaces": [{ "id": "ws_demo", "name": "开发用户 的工作区" }],
"documents": [],
"dev_fixture": true
});
let dev_projection =
build_workspace_shell_projection(&dev_dataset, "ws_demo", None, "开发用户 的工作区");
let dev_html = render_workspace_shell_sidebar_html(&dev_projection, None, None);
assert!(dev_projection.dev_fixture);
assert!(dev_html.contains("data-mnote-dev-fixture=\"true\""));
assert!(dev_html.contains("data-mnote-dev-fixture-kind=\"workspace-shell\""));
}
}
pub fn render_workspace_shell_sidebar_html(
@@ -434,9 +495,25 @@ pub fn render_workspace_shell_sidebar_html(
})
.collect::<Vec<_>>()
.join("");
let status_markers = format!(
r#"{degraded_marker}{dev_fixture_marker}"#,
degraded_marker = if projection.degraded {
format!(
r#"<span hidden data-mnote-workspace-shell-degraded="true" data-mnote-workspace-shell-degraded-reason="{}"></span>"#,
escape_html(projection.degraded_reason.as_deref().unwrap_or("projection_unavailable")),
)
} else {
String::new()
},
dev_fixture_marker = if projection.dev_fixture {
r#"<span hidden data-mnote-dev-fixture="true" data-mnote-dev-fixture-kind="workspace-shell"></span>"#.to_string()
} else {
String::new()
},
);
format!(
r#"<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title">{}星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-sidebar-tabs" data-testid="wolai-sidebar-tree-tabs" role="tablist" aria-label="页面树和文件树"><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-active" data-mnote-sidebar-tree-tab="page" aria-selected="true" aria-controls="wolai-sidebar-page-tree-panel">我的页面</button><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-muted" data-mnote-sidebar-tree-tab="filetree" aria-selected="false" aria-controls="wolai-sidebar-file-tree-panel">{}Explorer</button><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div><div class="wolai-sidebar-tab-panels"><div id="wolai-sidebar-page-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="page">{my_pages}</div><div id="wolai-sidebar-file-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="filetree" hidden>{file_tree_content}</div></div></section><div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
r#"{status_markers}<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title">{}星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-sidebar-tabs" data-testid="wolai-sidebar-tree-tabs" role="tablist" aria-label="页面树和文件树"><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-active" data-mnote-sidebar-tree-tab="page" aria-selected="true" aria-controls="wolai-sidebar-page-tree-panel">我的页面</button><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-muted" data-mnote-sidebar-tree-tab="filetree" aria-selected="false" aria-controls="wolai-sidebar-file-tree-panel">{}Explorer</button><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div><div class="wolai-sidebar-tab-panels"><div id="wolai-sidebar-page-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="page">{my_pages}</div><div id="wolai-sidebar-file-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="filetree" hidden>{file_tree_content}</div></div></section><div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
render_symbol("star", "wolai-section-icon"),
render_symbol("folder_open", "wolai-folder-icon"),
escape_html(&projection.workspace_id),