Layers: the active layer falls back to the top layer when it is removed

The active layer lives on DocState, outside the undo snapshots, and
nothing re-checked it when the document's layers changed. After New
Layer → Undo (or Delete Layer / Delete Unused Layers / Merge Layers on
the active layer) it kept pointing at a layer that no longer exists, and
every object created next (frames, lines, placed images, pasted items)
was put on that phantom layer: not drawn on the canvas, missing from PDF
export, ItemLayer referencing an undefined layer in IDML, and preflight
reported nothing.

After each command, if the active layer is gone, make the top layer
active (as on opening a document).

Test: active_layer_survives_undoing_new_layer.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
storybored379andClaude Opus 5.5 authored and Brandon Thomas committed 2026-10-04 09:52:29 -04:00
1 parent 01753f90f3
commit eee64ae7c9
2 files changed
+31

No files matched your search

+7
View File
@@ -345,6 +345,13 @@ impl Session {
(spec.enabled)(self).map_err(|e| EngineError::Disabled(id.into(), e))?;
let before = self.active().map(|d| (d.uid, d.doc.clone()));
let r = (spec.run)(self, params)?;
// Undo, redo or deleting layers can take the active layer away: new objects would land on
// a layer that isn't there (invisible, not exported). Fall back to the top layer.
if let Some(st) = self.active_mut()
&& st.doc.layer(st.active_layer).is_none()
{
st.active_layer = st.doc.default_layer();
}
self.record_transform(id, params);
if self.prefs.smart_text_reflow && spec.undoable {
self.smart_reflow();
+24
View File
@@ -254,6 +254,30 @@ fn serialized_document_opens_again() {
assert_eq!(d["stories"][0]["preview"], "Saved on the web");
}
#[test]
fn active_layer_survives_undoing_new_layer() {
let mut s = session();
let layer_of = |s: &mut Session, id: u64| {
let d = s.execute("document.inspect", &json!({})).unwrap();
let items = d["spreads"].as_array().unwrap().iter().flat_map(|sp| sp["items"].as_array().unwrap().clone()).collect::<Vec<_>>();
let it = items.into_iter().find(|i| i["id"] == id).unwrap();
let layers: Vec<Value> = d["layers"].as_array().unwrap().iter().map(|l| l["id"].clone()).collect();
(it["layer"].clone(), layers)
};
// New Layer, then Undo: the new layer is gone, so it can't stay the active one.
s.execute("layer.new", &json!({})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
let f = s.execute("frame.create", &json!({"rect": [36, 36, 300, 200], "content": "text", "text": "Visible"})).unwrap();
let (layer, layers) = layer_of(&mut s, f["id"].as_u64().unwrap());
assert!(layers.contains(&layer), "frame on layer {layer} not in {layers:?}");
// Delete Unused Layers removing the (empty) active layer.
s.execute("layer.new", &json!({})).unwrap();
s.execute("layer.deleteUnused", &json!({})).unwrap();
let f = s.execute("frame.create", &json!({"rect": [36, 236, 300, 400], "content": "text", "text": "Also visible"})).unwrap();
let (layer, layers) = layer_of(&mut s, f["id"].as_u64().unwrap());
assert!(layers.contains(&layer), "frame on layer {layer} not in {layers:?}");
}
#[test]
fn step_and_repeat_grid() {
let mut s = session();