From c66e4d9a1eb03cdf1172359538e8417be5b4ce4e Mon Sep 17 00:00:00 2001 From: Fini Date: Sat, 6 Jun 2026 23:01:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20exclude=20badge/pill/tag=20from=20c?= =?UTF-8?q?ard-row=20equalization=20(P3=20dashboard=20=E7=AD=89=E5=AE=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit equalizeHorizontalSiblings (the dashboard column-equalization pass) is the same logic as the equalizeCardRow ported in I4, except it also excludes badge/pill/tag roles from card candidates. Add those exclusions so equalize_card_row covers both — closing the P3 "dashboard 等宽" item. The other two P3 items are architectural non-gaps, not ports: - 变量 seeding: plans carry no palette (variables.rs is a deliberate no-op); the design-system path (design_system_to_seed_commands) already seeds variables. - intent-modify: the Rust Design path is AGENTIC — an agent::Provider whose modify-level tools (update_node / batch_design / ...) handle create-vs-modify organically, so the 2-way Design/Chat gate suffices. TS's 3-way classify existed for its non-agentic routing and would be redundant here. --- crates/op-orchestrator/src/role_post_pass.rs | 14 +++++++++++-- .../src/role_post_pass_tests.rs | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/op-orchestrator/src/role_post_pass.rs b/crates/op-orchestrator/src/role_post_pass.rs index f06a42c8c..0a7bd17cc 100644 --- a/crates/op-orchestrator/src/role_post_pass.rs +++ b/crates/op-orchestrator/src/role_post_pass.rs @@ -439,7 +439,10 @@ fn size_number(node: &Value, key: &str) -> f64 { } /// Equalize a row of fixed-width card frames to `fill_container` so they stretch -/// evenly (port of `equalizeCardRow`). Pure property fix — taffy then lays out. +/// evenly (port of `equalizeCardRow` AND the near-identical +/// `equalizeHorizontalSiblings` in design-canvas-ops.ts — the dashboard 等宽 +/// pass; the badge/pill/tag exclusions come from the latter). Pure property fix +/// — taffy then lays out. fn equalize_card_row(node: &mut Value) { if node.get("layout").and_then(Value::as_str) != Some("horizontal") { return; @@ -458,7 +461,14 @@ fn equalize_card_row(node: &mut Value) { .enumerate() .filter(|(_, c)| { c.get("type").and_then(Value::as_str) == Some("frame") - && !matches!(role_of(c), Some("divider") | Some("phone-mockup")) + && !matches!( + role_of(c), + Some("divider") + | Some("phone-mockup") + | Some("badge") + | Some("pill") + | Some("tag") + ) && size_number(c, "height") > 88.0 }) .map(|(i, _)| i) diff --git a/crates/op-orchestrator/src/role_post_pass_tests.rs b/crates/op-orchestrator/src/role_post_pass_tests.rs index a8f6fe8c7..044102e5b 100644 --- a/crates/op-orchestrator/src/role_post_pass_tests.rs +++ b/crates/op-orchestrator/src/role_post_pass_tests.rs @@ -223,6 +223,26 @@ fn card_row_left_alone_when_widths_similar() { assert_eq!(row["children"][0]["width"], json!(200)); } +#[test] +fn badge_pill_tag_rows_are_not_equalized() { + // The dashboard-pass (equalizeHorizontalSiblings) excludes badge/pill/tag, + // so a row of those keeps its widths even with a low width ratio. + for role in ["badge", "pill", "tag"] { + let mut row = json!({ + "type":"frame","layout":"horizontal","children":[ + {"type":"frame","role":role,"width":240,"height":200,"children":[]}, + {"type":"frame","role":role,"width":120,"height":210,"children":[]} + ] + }); + equalize_card_row(&mut row); + assert_eq!( + row["children"][0]["width"], + json!(240), + "{role} row must not be equalized" + ); + } +} + #[test] fn form_inputs_promoted_when_fill_sibling_present() { let mut form = json!({