mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
Point library style variable bindings at imported library variables
Fixes #462 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
102404e8c3
commit
76a67e7ee5
@@ -582,11 +582,15 @@ fn local_key(lid: &str, key: &str, taken: impl Fn(&str) -> bool) -> String {
|
||||
}
|
||||
|
||||
/// Copy library styles `keys` into `doc` as linked remote styles (with `update`, copies older than
|
||||
/// the library's are refreshed). Returns library style id → local id.
|
||||
/// the library's are refreshed). The variables a written copy binds are imported too and its
|
||||
/// bindings point at their copies (never at this file's variables of the same ids). Returns library
|
||||
/// style id → local id.
|
||||
pub fn import_styles(doc: &mut Document, lib: &PublishedLibrary, keys: &BTreeSet<String>, update: bool) -> BTreeMap<String, String> {
|
||||
let lid = lib.manifest.id.clone();
|
||||
let mut out = BTreeMap::new();
|
||||
let mut hashes = BTreeSet::new();
|
||||
// Local ids of the copies written now, with the library style they copy.
|
||||
let mut written: Vec<(String, &Style)> = Vec::new();
|
||||
for key in keys {
|
||||
let Some(style) = lib.doc.styles.iter().find(|s| s.id == *key) else { continue };
|
||||
let version = lib.manifest.style(key).map_or(lib.manifest.version, |s| s.version);
|
||||
@@ -598,6 +602,7 @@ pub fn import_styles(doc: &mut Document, lib: &PublishedLibrary, keys: &BTreeSet
|
||||
{
|
||||
*slot = Style { id: r.local.clone(), ..style.clone() };
|
||||
set_record(&mut doc.libraries_mut().styles, ImportedStyle { version, ..r.clone() });
|
||||
written.push((r.local.clone(), style));
|
||||
}
|
||||
r.local
|
||||
}
|
||||
@@ -605,6 +610,7 @@ pub fn import_styles(doc: &mut Document, lib: &PublishedLibrary, keys: &BTreeSet
|
||||
let local = local_key(&lid, key, |l| doc.styles.iter().any(|s| s.id == l));
|
||||
doc.styles.push(Style { id: local.clone(), ..style.clone() });
|
||||
set_record(&mut doc.libraries_mut().styles, ImportedStyle { library: lid.clone(), key: key.clone(), local: local.clone(), version });
|
||||
written.push((local.clone(), style));
|
||||
local
|
||||
}
|
||||
};
|
||||
@@ -613,6 +619,17 @@ pub fn import_styles(doc: &mut Document, lib: &PublishedLibrary, keys: &BTreeSet
|
||||
}
|
||||
out.insert(key.clone(), local);
|
||||
}
|
||||
let bound: BTreeSet<VariableId> = written.iter().flat_map(|(_, s)| s.bound.values().cloned()).collect();
|
||||
if !bound.is_empty() {
|
||||
let vars = import_variables(doc, lib, &bound, &BTreeSet::new(), update);
|
||||
for (local, _) in &written {
|
||||
if let Some(slot) = doc.styles.iter_mut().find(|s| s.id == *local) {
|
||||
// Bindings to variables the library lacks are dropped (the published value stays).
|
||||
slot.bound =
|
||||
std::mem::take(&mut slot.bound).into_iter().filter_map(|(f, v)| vars.variables.get(&v).map(|l| (f, l.clone()))).collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
copy_images(doc, &lib.doc, &hashes);
|
||||
out
|
||||
}
|
||||
|
||||
@@ -314,3 +314,33 @@ fn library_variable_commands_fail_cleanly() {
|
||||
}
|
||||
assert!(s.doc().unwrap().libraries.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn library_style_bindings_ignore_unrelated_local_variables() {
|
||||
let d = dir("style-bound");
|
||||
let path = path_str(&d.join("lib.figcraft"));
|
||||
let mut lib = Session::new();
|
||||
ex(&mut lib, "variable.createCollection", json!({"name": "Tokens"}));
|
||||
let var = ex(&mut lib, "variable.create", json!({"collection": "Tokens", "name": "Brand", "type": "color", "value": "#0000ff"}))["id"].clone();
|
||||
let style = ex(&mut lib, "style.create", json!({"type": "paint", "name": "Brand", "value": "#0000ff"}))["id"].clone();
|
||||
ex(&mut lib, "style.bind", json!({"style": style, "field": "fill", "variable": var}));
|
||||
let lid = publish_to(&mut lib, &path);
|
||||
|
||||
let mut s = Session::new();
|
||||
ex(&mut s, "variable.createCollection", json!({"name": "Local"}));
|
||||
let local = ex(&mut s, "variable.create", json!({"collection": "Local", "name": "Unrelated", "type": "color", "value": "#ff0000"}))["id"].clone();
|
||||
assert_eq!(local, var, "the ids must collide for this test");
|
||||
let r = idv(&ex(&mut s, "shape.rectangle", json!({"width": 40, "height": 40})));
|
||||
ex(&mut s, "library.add", json!({"path": path}));
|
||||
ex(&mut s, "library.applyStyle", json!({"library": lid, "style": "Brand", "id": r}));
|
||||
assert_eq!(fill(&s, r), "0000FF");
|
||||
healthy(&s);
|
||||
|
||||
// An updated style follows the library's variable, not the local one.
|
||||
ex(&mut lib, "variable.set", json!({"variable": "Brand", "value": "#00ffff"}));
|
||||
republish(&mut lib);
|
||||
ex(&mut s, "library.acceptUpdates", json!({}));
|
||||
assert_eq!(fill(&s, r), "00FFFF");
|
||||
healthy(&s);
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user