fix(agent): keep status indicator dots out of stub cleanup
This commit is contained in:
parent
239ea6f10d
commit
10003db186
|
|
@ -252,6 +252,8 @@ fn cleanup_deletes_only_empty_decorated_small_frame_stubs() {
|
|||
{"type":"frame","id":"book","name":"Book Btn 1","padding":[8,14],"cornerRadius":9999,
|
||||
"fill":[{"type":"solid","color":"#E5E7EB"}],"children":[]},
|
||||
{"type":"ellipse","id":"dot","name":"Status Dot","width":6,"height":6,"fill":[{"type":"solid","color":"#22C55E"}]},
|
||||
{"type":"frame","id":"active","name":"Active Indicator","width":8,"height":8,"cornerRadius":4,
|
||||
"fill":[{"type":"solid","color":"#FF6B6B"}],"children":[]},
|
||||
{"type":"frame","id":"badge","name":"Real Badge","width":58,"height":24,"padding":[5,10],"cornerRadius":9999,
|
||||
"fill":[{"type":"solid","color":"#F97316"}],"children":[{"type":"text","id":"badge-t","content":"-25%"}]},
|
||||
{"type":"frame","id":"skeleton","name":"Skeleton","width":120,"height":80,"cornerRadius":12,
|
||||
|
|
@ -266,6 +268,10 @@ fn cleanup_deletes_only_empty_decorated_small_frame_stubs() {
|
|||
assert!(find_by_name(&root, "Discount Badge 1").is_none());
|
||||
assert!(find_by_name(&root, "Book Btn 1").is_none());
|
||||
assert!(find_by_name(&root, "Status Dot").is_some());
|
||||
assert!(
|
||||
find_by_name(&root, "Active Indicator").is_some(),
|
||||
"a painted semantic state dot is content, not an abandoned decorated stub"
|
||||
);
|
||||
assert!(find_by_name(&root, "Real Badge").is_some());
|
||||
assert!(find_by_name(&root, "Skeleton").is_some());
|
||||
assert!(find_by_name(&root, "Spacer").is_some());
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ fn is_empty_decorated_stub(v: &Value, resolved_size: Option<(f64, f64)>) -> bool
|
|||
// no child image node, so treating them as empty stubs destroys the
|
||||
// asset before the later avatar-repair pass can inspect it.
|
||||
&& !is_meaningful_media_leaf(v)
|
||||
// A painted state/presence dot is itself the content. Weak models
|
||||
// often emit it as a tiny childless frame rather than an ellipse; once
|
||||
// the radius pass makes it circular it must not be mistaken for an
|
||||
// abandoned badge shell.
|
||||
&& !has_state_indicator_semantics(v)
|
||||
&& has_visible_paint(v)
|
||||
&& (padding_positive(v) || numeric(v, "cornerRadius").is_some_and(|r| r > 0.0))
|
||||
&& width.is_some_and(|w| w > 0.0 && w < 80.0)
|
||||
|
|
@ -89,6 +94,47 @@ fn is_meaningful_media_leaf(v: &Value) -> bool {
|
|||
has_image_fill(v) || has_avatar_semantics(v)
|
||||
}
|
||||
|
||||
fn has_state_indicator_semantics(v: &Value) -> bool {
|
||||
let role = v
|
||||
.get("role")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
if matches!(
|
||||
role.as_str(),
|
||||
"status-dot"
|
||||
| "status-indicator"
|
||||
| "presence-dot"
|
||||
| "presence-indicator"
|
||||
| "online-indicator"
|
||||
| "notification-dot"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let name = v
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
[
|
||||
"status dot",
|
||||
"status indicator",
|
||||
"active indicator",
|
||||
"presence dot",
|
||||
"presence indicator",
|
||||
"online dot",
|
||||
"online indicator",
|
||||
"notification dot",
|
||||
"状态点",
|
||||
"状态指示",
|
||||
"在线点",
|
||||
"在线指示",
|
||||
]
|
||||
.iter()
|
||||
.any(|needle| name.contains(needle))
|
||||
}
|
||||
|
||||
fn has_image_fill(v: &Value) -> bool {
|
||||
match v.get("fill") {
|
||||
Some(Value::Array(fills)) => fills.iter().any(is_image_paint),
|
||||
|
|
|
|||
Loading…
Reference in a new issue