mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-18 00:56:30 +03:00
Preserve approvals for unchanged isolated component previews
This commit is contained in:
@@ -599,6 +599,24 @@ fn visual_approvals_survive_shared_source_edits_but_not_changed_scope_or_pixels(
|
||||
before["packet"]["revision"].as_str().unwrap()
|
||||
)))
|
||||
.unwrap();
|
||||
// Component decisions approve the isolated preview, not its contextual
|
||||
// surroundings or the dependency closure of a shared document.
|
||||
let mut component_previous = previous.clone();
|
||||
component_previous["packet"]["stage"] = json!("components");
|
||||
let mut component_current = after.clone();
|
||||
component_current["packet"]["stage"] = json!("components");
|
||||
component_current["packet"]["components"][1]["dependencies"] = json!(["shared.css", "unrelated.png"]);
|
||||
component_current["packet"]["components"][1]["context"] = json!({"kind":"image","url":"/files/unrelated-context.png"});
|
||||
component_current["capture"]["components"][1]["views"]["preview"]["rasterElements"] = json!(12);
|
||||
assert_eq!(super::visual_approval::carry(&component_previous, &mut component_current, &dir.join("blobs")), 1);
|
||||
assert_eq!(component_current["draft"]["decisions"]["control"]["action"], "approve");
|
||||
assert!(component_current["receipt"].is_null());
|
||||
for field in ["box", "medium", "note", "preview"] {
|
||||
let mut changed = component_current.clone();
|
||||
changed["draft"]["decisions"].as_object_mut().unwrap().remove("control");
|
||||
changed["packet"]["components"][1][field] = json!("changed");
|
||||
assert_eq!(super::visual_approval::carry(&component_previous, &mut changed, &dir.join("blobs")), 0, "component scope: {field}");
|
||||
}
|
||||
for field in ["box", "medium", "note", "context"] {
|
||||
let mut changed = after.clone();
|
||||
changed["packet"]["components"][1][field] = json!("changed");
|
||||
|
||||
@@ -14,9 +14,20 @@ fn identity(state: &Value, component: &Value, blobs: &Path) -> Option<Value> {
|
||||
let prefix = format!("/files/{}/", state["packet"]["revision"].as_str()?);
|
||||
let mut definition = component.clone();
|
||||
definition.as_object_mut()?.remove("revision");
|
||||
// Dependency paths remain part of scope; their bytes are checked by sources_current.
|
||||
let component_stage = state["packet"]["stage"] == "components";
|
||||
if component_stage {
|
||||
// The checkpoint approves the isolated component. Context is explicitly
|
||||
// reference-only; shared document dependencies and thumbnails are not
|
||||
// additional things the user approves. Source integrity still binds the
|
||||
// full frozen closure and is checked separately by sources_current.
|
||||
for key in ["dependencies", "context", "thumbnail"] {
|
||||
definition.as_object_mut()?.remove(key);
|
||||
}
|
||||
}
|
||||
let mut views = serde_json::Map::new();
|
||||
for key in ["preview", "context", "thumbnail", "comp"] {
|
||||
let keys: &[&str] = if component_stage { &["preview", "comp"] }
|
||||
else { &["preview", "context", "thumbnail", "comp"] };
|
||||
for &key in keys {
|
||||
let view = if key == "comp" {
|
||||
&state["packet"]["comp"]
|
||||
} else {
|
||||
@@ -60,6 +71,9 @@ fn identity(state: &Value, component: &Value, blobs: &Path) -> Option<Value> {
|
||||
"rasterElements",
|
||||
"semanticControls",
|
||||
] {
|
||||
// These counts describe the entire shared document, including
|
||||
// components outside this decision's isolated preview.
|
||||
if component_stage && matches!(field, "svgElements" | "rasterElements" | "semanticControls") { continue; }
|
||||
scoped[field] = proof[field].clone();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user