From 60a356ea142359234eeb642ba4536ea580260430 Mon Sep 17 00:00:00 2001 From: SusieeTheFemboy <96988330+SusieeTheLinuxUser@users.noreply.github.com> Date: Thu, 29 Oct 2026 05:22:56 +0100 Subject: [PATCH] Edit: duplicating during a text edit ends the edit (#2226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Edit > Duplicate replaces the layer selection with the copies, but an open in-canvas text edit stayed on the original layer: typing kept editing a layer that was no longer selected. When the duplicate leaves the edited layer deselected, end the edit the way #2096's Deselect All does (text_edit::end_edit), in the command every caller — menu, shortcut, MCP — dispatches through. Co-Authored-By: GLM-5.3-Flash --- crates/engine/src/cmd/edit.rs | 5 +++++ crates/engine/src/tests/text_edit.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 0130aff70..33294e4bb 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -146,6 +146,11 @@ fn duplicate(s: &mut Session, p: &Value) -> Result { st.selected_layers = created.clone(); Ok(created) })?; + // Duplicating selects the copies, so an open text edit on a now-deselected layer ends + // (#2226): typing must not keep editing an unselected layer. + if s.state.text_edit.as_ref().is_some_and(|e| !s.state.selected_layers.contains(&e.layer)) { + super::text_edit::end_edit(s, &Value::Null)?; + } Ok(json!(new.iter().map(|l| l.0).collect::>())) } diff --git a/crates/engine/src/tests/text_edit.rs b/crates/engine/src/tests/text_edit.rs index 17d0345d8..f52eafcfe 100644 --- a/crates/engine/src/tests/text_edit.rs +++ b/crates/engine/src/tests/text_edit.rs @@ -199,6 +199,20 @@ fn typing_deleting_and_caret_movement() { assert!(s.state.text_edit.is_none()); } +/// Duplicating during a text edit moves the selection to the copy: the edit on the original ends +/// (#2226), as in After Effects — typing must not keep editing an unselected layer. +#[test] +fn duplicate_during_a_text_edit_ends_the_edit() { + let (mut s, t) = session("Hello"); + s.execute("text.edit", json!({"layer": t, "caret": 5})).unwrap(); + let copy = s.execute("edit.duplicate", json!({"layers": [t]})).unwrap()[0].as_u64().unwrap(); + assert_ne!(copy, t, "the duplicate is created"); + assert_eq!(s.state.selected_layers, vec![effectcraft_project::LayerId(copy)], "the copy is selected"); + assert!(s.state.text_edit.is_none(), "the edit ended with the selection move (#2226)"); + assert_eq!(doc(&s, t).text, "Hello", "the original keeps its text"); + assert_eq!(doc(&s, copy).text, "Hello", "and the copy carries it"); +} + #[test] fn caret_style_applies_to_the_next_typed_text() { let (mut s, t) = session("ab");