feat: continue tree rust family cutover

- add rust renderer/state-family scaffolds and inline compat host thinning for page tree, file tree, and picker
- route tree/filetree preflight, file projection, resource artifact, and stream delta contracts through rust plans
- preserve canonical move-order validation, file-tree search projection, and related frontend/runtime regression coverage
This commit is contained in:
lix-2026
2026-04-26 19:35:52 +08:00
parent 338bb2e20f
commit e564dfde02
93 changed files with 17492 additions and 1856 deletions
@@ -20,6 +20,8 @@ pub struct KernelProjectionQuery {
pub workspace_id: Option<String>,
pub root_node_id: Option<String>,
pub depth: Option<u32>,
pub query: Option<String>,
pub max_results: Option<usize>,
}
#[derive(Debug, Deserialize)]
@@ -73,6 +75,8 @@ async fn project_projection(
workspace_id: &effective_workspace_id,
root_node_id: query.root_node_id.as_deref(),
depth: query.depth,
query: query.query.as_deref(),
max_results: query.max_results,
projection,
},
)
@@ -388,4 +392,64 @@ mod tests {
.iter()
.any(|value| value == "expand"));
}
#[tokio::test]
async fn file_tree_projection_query_returns_matches_and_ancestors() {
let response = app()
.oneshot(
Request::builder()
.uri("/api/tree/projections/file?workspaceId=ws_demo&rootNodeId=page_root&query=%E9%A2%84%E7%AE%97")
.body(Body::empty())
.expect("request"),
)
.await
.expect("response");
assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), usize::MAX)
.await
.expect("body");
let payload: Value = serde_json::from_slice(&body).expect("json");
let items = payload["result"]["items"].as_array().expect("items");
let row_ids = items
.iter()
.filter_map(|item| item["rowId"].as_str())
.collect::<Vec<_>>();
assert_eq!(row_ids, vec!["doc:page_root", "asset:table_1"]);
assert_eq!(items[0]["expandedByDefault"], true);
assert_eq!(items[1]["resourceMeta"]["resourceKind"], "table");
let edges = payload["result"]["edges"].as_array().expect("edges");
assert_eq!(edges.len(), 1);
assert_eq!(edges[0]["fromNodeId"], "page_root");
assert_eq!(edges[0]["toNodeId"], "asset:table_1");
}
#[tokio::test]
async fn file_tree_projection_query_honors_max_results_and_keeps_ancestors() {
let response = app()
.oneshot(
Request::builder()
.uri("/api/tree/projections/file?workspaceId=ws_demo&rootNodeId=page_root&query=png&maxResults=1")
.body(Body::empty())
.expect("request"),
)
.await
.expect("response");
assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), usize::MAX)
.await
.expect("body");
let payload: Value = serde_json::from_slice(&body).expect("json");
let items = payload["result"]["items"].as_array().expect("items");
let row_ids = items
.iter()
.filter_map(|item| item["rowId"].as_str())
.collect::<Vec<_>>();
assert_eq!(row_ids, vec!["doc:page_root", "asset:asset_file_1"]);
assert_eq!(items[0]["expandedByDefault"], true);
assert_eq!(items[1]["resourceMeta"]["assetKind"], "image");
}
}