收敛树命令与投影切换
This commit is contained in:
@@ -567,8 +567,7 @@ pub async fn title(
|
||||
let title = body.title.trim();
|
||||
if title.is_empty() {
|
||||
return Err(
|
||||
WebError::bad_request_code("title_required", "缺少有效页面标题")
|
||||
.with_context(&context),
|
||||
WebError::bad_request_code("title_required", "缺少有效页面标题").with_context(&context),
|
||||
);
|
||||
}
|
||||
let effective_workspace_id =
|
||||
@@ -974,6 +973,9 @@ mod tests {
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["meta"]["commandName"], "page.layout.updateOptions");
|
||||
assert_eq!(payload["meta"]["canonicalCommand"], "page.layout.updateOptions");
|
||||
assert_eq!(
|
||||
payload["meta"]["canonicalCommand"],
|
||||
"page.layout.updateOptions"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,10 @@ mod tests {
|
||||
assert_eq!(payload["contract"]["toolEventOwner"], "rust-web-hermes");
|
||||
assert_eq!(payload["contract"]["clientActionOwner"], "rust-web-hermes");
|
||||
assert_eq!(payload["canonicalRoute"], "/api/hermes/bridge");
|
||||
assert!(payload["eventStreamEndpoint"].as_str().unwrap_or_default().contains("/api/hermes/events/"));
|
||||
assert!(payload["eventStreamEndpoint"]
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.contains("/api/hermes/events/"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -284,6 +287,9 @@ mod tests {
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["bridge"], "hermes_session");
|
||||
assert_eq!(payload["canonicalRoute"], "/api/hermes/bridge");
|
||||
assert_eq!(payload["contract"]["structuredWriteOwner"], "rust-web-hermes");
|
||||
assert_eq!(
|
||||
payload["contract"]["structuredWriteOwner"],
|
||||
"rust-web-hermes"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +230,14 @@ async fn load_search_results_with_filters(
|
||||
"customRangeTo": filters.custom_range.as_ref().and_then(|range| range.to.clone()),
|
||||
}),
|
||||
};
|
||||
match execute_runtime_query_via_convex(config, context, Some(workspace_id), runtime_query.clone()).await {
|
||||
match execute_runtime_query_via_convex(
|
||||
config,
|
||||
context,
|
||||
Some(workspace_id),
|
||||
runtime_query.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(value) => Ok(value),
|
||||
Err(_) => execute_runtime_query_against_data(
|
||||
context,
|
||||
|
||||
@@ -161,7 +161,12 @@ fn stream_event(event_name: &str, payload: &Value) -> Event {
|
||||
.map(ToOwned::to_owned)
|
||||
.or_else(|| value.as_u64().map(|number| number.to_string()))
|
||||
})
|
||||
.or_else(|| payload.get("cursor").and_then(Value::as_str).map(ToOwned::to_owned))
|
||||
.or_else(|| {
|
||||
payload
|
||||
.get("cursor")
|
||||
.and_then(Value::as_str)
|
||||
.map(ToOwned::to_owned)
|
||||
})
|
||||
.unwrap_or_else(|| "0".into());
|
||||
Event::default()
|
||||
.event(event_name)
|
||||
|
||||
@@ -4511,6 +4511,37 @@ fn create_command_wire(
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_tree_move_preflight_data(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
workspace_id: &str,
|
||||
) -> Result<Value, WebError> {
|
||||
let spec = ProjectionSnapshotSpec {
|
||||
workspace_id,
|
||||
root_node_id: None,
|
||||
depth: Some(99),
|
||||
projection: KernelProjectionKind::SidebarTree,
|
||||
query: None,
|
||||
max_results: None,
|
||||
};
|
||||
let snapshot = load_projection_snapshot(state.config(), context, &spec)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
WebError::bad_gateway_code(
|
||||
"tree_move_preflight_snapshot_failed",
|
||||
format!("移动前排序快照加载失败: {}", error.message()),
|
||||
)
|
||||
.with_context(context)
|
||||
.with_header("x-error-phase", "tree_move_preflight_snapshot")
|
||||
})?;
|
||||
let documents = snapshot
|
||||
.dataset
|
||||
.get("documents")
|
||||
.cloned()
|
||||
.unwrap_or_else(|| json!([]));
|
||||
Ok(json!({ "documents": documents }))
|
||||
}
|
||||
|
||||
async fn resolve_tree_create_workspace_id(
|
||||
state: &AppState,
|
||||
context: &RequestContext,
|
||||
@@ -4671,7 +4702,12 @@ pub async fn tree_command(
|
||||
_ => resolve_effective_workspace_id(&context, requested_workspace_id, true)?
|
||||
.expect("workspace_required 已确保存在"),
|
||||
};
|
||||
let command_wire = create_command_wire(&context, &effective_workspace_id, request)?;
|
||||
let needs_move_preflight = matches!(&request, TreeCommandRequest::Move { .. });
|
||||
let mut command_wire = create_command_wire(&context, &effective_workspace_id, request)?;
|
||||
if needs_move_preflight {
|
||||
command_wire.preflight_data =
|
||||
Some(load_tree_move_preflight_data(&state, &context, &effective_workspace_id).await?);
|
||||
}
|
||||
let execution = execute_runtime_command_via_convex_with_artifacts(
|
||||
state.config(),
|
||||
&context,
|
||||
|
||||
@@ -195,12 +195,12 @@ fn render_document_title_controller_script() -> &'static str {
|
||||
}
|
||||
if (!documentId) return;
|
||||
const escapedId = cssEscape(documentId);
|
||||
setText(`[data-node-id="${escapedId}"] .tree-link-title`, title);
|
||||
setText(`[data-document-id="${escapedId}"] .tree-link-title`, title);
|
||||
setText(`[data-doc-id="${escapedId}"] .tree-link-title`, title);
|
||||
setText(`[data-node-id="${escapedId}"] .wolai-row-title`, title);
|
||||
setText(`a[href="/documents/${escapedId}"] .wolai-row-title`, title);
|
||||
setText(`a[href^="/documents/${escapedId}?"] .wolai-row-title`, title);
|
||||
setText(`.tree-row[data-node-id="${escapedId}"] > .tree-link > .tree-link-title`, title);
|
||||
setText(`.tree-row[data-document-id="${escapedId}"] > .tree-link > .tree-link-title`, title);
|
||||
setText(`.tree-row[data-doc-id="${escapedId}"] > .tree-link > .tree-link-title`, title);
|
||||
setText(`.wolai-page-row[data-node-id="${escapedId}"] > .wolai-row-title`, title);
|
||||
setText(`a[href="/documents/${escapedId}"] > .wolai-row-title`, title);
|
||||
setText(`a[href^="/documents/${escapedId}?"] > .wolai-row-title`, title);
|
||||
};
|
||||
|
||||
const saveTitle = async () => {
|
||||
@@ -627,8 +627,9 @@ async fn build_page_aggregate_snapshot(
|
||||
}),
|
||||
)?;
|
||||
|
||||
serde_json::from_value::<PageAggregate>(projection)
|
||||
.map_err(|error| WebError::internal(format!("Page Aggregate projection 反序列化失败: {error}")))
|
||||
serde_json::from_value::<PageAggregate>(projection).map_err(|error| {
|
||||
WebError::internal(format!("Page Aggregate projection 反序列化失败: {error}"))
|
||||
})
|
||||
}
|
||||
|
||||
fn stamp_recent_page_cookie(headers: &mut HeaderMap, document_id: &str) {
|
||||
@@ -941,6 +942,9 @@ mod tests {
|
||||
assert!(html.contains("data-page-title-input=\"true\""));
|
||||
assert!(html.contains("data-title-endpoint=\"/api/documents/title\""));
|
||||
assert!(html.contains("mnote.document_title_controller.v1"));
|
||||
assert!(html
|
||||
.contains(".tree-row[data-node-id=\"${escapedId}\"] > .tree-link > .tree-link-title"));
|
||||
assert!(!html.contains("[data-node-id=\"${escapedId}\"] .tree-link-title"));
|
||||
assert!(html.contains("\"titleEndpoint\":\"/api/documents/title\""));
|
||||
assert!(html.contains("__MNOTE_TREE_LIVE_BOOTSTRAP__"));
|
||||
assert!(html.contains("mnote.tree_live_bootstrap.v1"));
|
||||
|
||||
Reference in New Issue
Block a user