From 6b7503348506751d1903c00fb6ce10fc84455b7f Mon Sep 17 00:00:00 2001 From: bflatastic Date: Fri, 30 Oct 2026 03:17:09 +0700 Subject: [PATCH] Import: a numbered still named without `sequence` imports as itself, as in After Effects (#2018) file.import without the flag (scripts, agents, files opened from the command line, the CLI's templates) turned `lockin_line1.png` into a one-frame image sequence. ImportOptions.sequence defaults to false in After Effects, so picked and dropped files now import as stills unless `sequence: true`; folders' numbered runs still import as sequences, and the Import dialog's Sequence checkbox is unchanged. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/file.rs | 2 +- crates/engine/src/footage/sequence.rs | 7 +++---- crates/engine/src/tests/sequence.rs | 9 +++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/engine/src/cmd/file.rs b/crates/engine/src/cmd/file.rs index b3922c7d6..194845803 100644 --- a/crates/engine/src/cmd/file.rs +++ b/crates/engine/src/cmd/file.rs @@ -745,7 +745,7 @@ pub fn specs() -> Vec { "File...", ["File", "Import"], Some("Cmd+I"), - "{paths: [string] (files or folders), sequence?: bool (the Sequence checkbox: true imports numbered stills of one run as one image sequence, one picked file bringing its whole run and several picked files that range; default: a numbered still brings its run, except dropped files (drag), which import as stills as in After Effects; folders' runs import as sequences; false imports stills and a folder's files individually), alphabetical?: bool (Force Alphabetical Order: every image of that type in the folder, by name), frameRate?: fps (image sequences; default Settings ▸ Import ▸ Sequence Footage), importAs?: footage|composition|compositionLayerSizes (Photoshop, PDF, Illustrator and EPS files), layer?: name|index (footage of one Photoshop layer), page?: number from 1 (PDF / Illustrator page), drag?: bool (dropped files: Settings ▸ Import ▸ Default Drag Import As; files import as stills unless sequence), addToComp?: bool (also add them to the active comp, at time?, index?, position? as in layer.addItem), background?: bool (probe the files in a background job, jobs.list / jobs.wait; returns {job})}", + "{paths: [string] (files or folders), sequence?: bool (the Sequence checkbox: true imports numbered stills of one run as one image sequence, one picked file bringing its whole run and several picked files that range; default false: picked or dropped files import as stills, as in After Effects; folders' runs import as sequences; false imports stills and a folder's files individually), alphabetical?: bool (Force Alphabetical Order: every image of that type in the folder, by name), frameRate?: fps (image sequences; default Settings ▸ Import ▸ Sequence Footage), importAs?: footage|composition|compositionLayerSizes (Photoshop, PDF, Illustrator and EPS files), layer?: name|index (footage of one Photoshop layer), page?: number from 1 (PDF / Illustrator page), drag?: bool (dropped files: Settings ▸ Import ▸ Default Drag Import As; files import as stills unless sequence), addToComp?: bool (also add them to the active comp, at time?, index?, position? as in layer.addItem), background?: bool (probe the files in a background job, jobs.list / jobs.wait; returns {job})}", always, import_cmd ), diff --git a/crates/engine/src/footage/sequence.rs b/crates/engine/src/footage/sequence.rs index 46dca154c..fc2dab2b0 100644 --- a/crates/engine/src/footage/sequence.rs +++ b/crates/engine/src/footage/sequence.rs @@ -45,14 +45,13 @@ impl SequenceOptions { /// `file.import`'s options. Dropped files (`drag: true`) import as stills, as in After /// Effects, while a folder's numbered stills import as sequences unless `sequence: false` /// (Alt-drag of a folder). The Import dialog passes its `" Sequence"` checkbox (off - /// unless ticked) as `sequence`. Without `sequence` or `drag` (scripts, agents) a numbered - /// still brings its run, as it always has. + /// unless ticked) as `sequence`. Without `sequence` (scripts, agents) a numbered still imports + /// as itself, as `ImportOptions.sequence` defaults to false in After Effects (#2018). pub fn for_import(p: &serde_json::Value) -> SequenceOptions { let flag = |k: &str| p.get(k).and_then(serde_json::Value::as_bool); let (sequence, alphabetical) = (flag("sequence"), flag("alphabetical").unwrap_or(false)); - let dropped = flag("drag").unwrap_or(false); // Force Alphabetical Order is part of the Sequence option. - SequenceOptions { sequence: sequence.unwrap_or(alphabetical || !dropped), folders: sequence.unwrap_or(true), alphabetical } + SequenceOptions { sequence: sequence.unwrap_or(alphabetical), folders: sequence.unwrap_or(true), alphabetical } } } diff --git a/crates/engine/src/tests/sequence.rs b/crates/engine/src/tests/sequence.rs index 3e81961ce..f2849a6be 100644 --- a/crates/engine/src/tests/sequence.rs +++ b/crates/engine/src/tests/sequence.rs @@ -104,10 +104,11 @@ fn numbered_stills_import_as_stills_by_default() { let r = s.execute("file.import", json!({"paths": [dir.clone()], "drag": true})).unwrap(); let (name, f) = footage(&s, &r["items"][0]); assert_eq!((name.as_str(), f.kind), ("shot_[0001-0005].png", FootageKind::Sequence)); - // Scripts and agents naming one frame without `sequence` still get its run. - let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0003.png")]})).unwrap(); - let (_, f) = footage(&s, &r["items"][0]); - assert_eq!(f.kind, FootageKind::Sequence); + // #2018: scripts and agents naming one frame without `sequence` get that still, as + // `ImportOptions.sequence` defaults to false in After Effects. + let r = s.execute("file.import", json!({"paths": [at(&dir, "shot_0005.png")]})).unwrap(); + let (name, f) = footage(&s, &r["items"][0]); + assert_eq!((name.as_str(), f.kind), ("shot_0005.png", FootageKind::Still)); } #[test]