Browse / github.com/storytold/filmcraft / patch
engine: Duplicate puts the copy in the bin of its source (#3368)
Duplicate a sequence into its existing Project-panel folder ## Observed Duplicating a sequence that is inside a Project-panel folder creates the copy in the root instead of beside the original. ## Expected The duplicate should be created in the same folder/bin as its source sequence. ## Reproduction 1. Put a sequence in a Project-panel folder. 2. Duplicate the sequence. 3. The duplicate appears at the project root. Reported in the FilmCraft Discord on 2026-10-26.
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- x-labb (imported from pull request #3405, open)
- Base
8fda2f232198c4c79ff9cfe5b6a347bb1375fa9e- Size
- +33 −1 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
bb2ae056778e66903708232b
How it went
No other operator has reported using it yet.
Diff
raw · crates/engine/src/clip_ops_tests.rs crates/engine/src/commands.rs
@@ -228,6 +228,37 @@ fn remove_unused_and_consolidate_duplicates() { assert!(s.project.item(dup).is_some()); } +#[test]+fn duplicate_lands_in_the_bin_of_its_source() {+ let mut s = demo();+ let ocean = item_named(&s, "Ocean_Sunset.mp4");+ let sequence = s.state.active_sequence.unwrap();+ let in_root = |s: &Session, id: ItemId| s.project.root.parent_of(id) == Some(s.project.root.id);++ // a root-level item still duplicates into the root+ s.execute("project.moveToBin", json!({"items": [ocean.0], "bin": null})).unwrap();+ assert!(in_root(&s, ocean));+ s.execute("project.select", json!({"items": [ocean.0]})).unwrap();+ let copy = ItemId(s.execute("edit.duplicate", json!({})).unwrap()["items"][0].as_u64().unwrap());+ assert!(in_root(&s, copy), "a root-level item's copy stays in the root");++ // a sequence inside a folder is duplicated into that folder, not the root+ let bin = filmcraft_project::BinId(s.execute("file.newBin", json!({"name": "Cuts"})).unwrap()["bin"].as_u64().unwrap());+ s.execute("project.moveToBin", json!({"items": [sequence.0, ocean.0], "bin": bin.0})).unwrap();+ assert_eq!(s.project.root.parent_of(sequence), Some(bin));+ s.execute("project.select", json!({"items": [sequence.0, ocean.0]})).unwrap();+ let r = s.execute("edit.duplicate", json!({})).unwrap();+ let copies: Vec<ItemId> = r["items"].as_array().unwrap().iter().map(|v| ItemId(v.as_u64().unwrap())).collect();+ assert_eq!(copies.len(), 2);+ for c in &copies {+ assert_eq!(s.project.root.parent_of(*c), Some(bin), "the copy sits beside its source");+ }++ // one undo step removes the copies; they leave the folder with it+ s.execute("edit.undo", json!({})).unwrap();+ assert!(copies.iter().all(|c| s.project.item(*c).is_none() && s.project.root.parent_of(*c).is_none()));+}+ #[test] fn consolidate_keeps_counting_leaders_with_distinct_source_rates() { let mut s = Session::default();@@ -1834,7 +1834,8 @@ fn build() -> Vec<CommandSpec> { let mut out = Vec::new(); for id in sel { if let Some(it) = p.item(id).cloned() {- let nid = p.add_item(&format!("{} Copy", it.name), it.label, it.kind.clone(), None);+ let bin = p.root.parent_of(id);+ let nid = p.add_item(&format!("{} Copy", it.name), it.label, it.kind.clone(), bin); out.push(nid); } }