diff --git a/crates/engine/src/prefs.rs b/crates/engine/src/prefs.rs index 600b015ee3..ceddb8942f 100644 --- a/crates/engine/src/prefs.rs +++ b/crates/engine/src/prefs.rs @@ -1127,7 +1127,6 @@ pub const HIDDEN_UNTIL_IMPLEMENTED: &[&str] = &[ "unitsAndRulers.gutter", "plugIns.showExtensionPanels", "plugIns.allowScriptsToConnect", - "plugIns.generatorEnabled", "type.textEngine", "enhancedControls.scrubbySliderAcceleration", "rawDefaults.colorSpace", diff --git a/crates/engine/src/web_cmds.rs b/crates/engine/src/web_cmds.rs index 1ed070c020..a68f439e17 100644 --- a/crates/engine/src/web_cmds.rs +++ b/crates/engine/src/web_cmds.rs @@ -1515,7 +1515,7 @@ fn image_assets(s: &mut Session, p: &Value) -> Result { /// After a save: regenerate the document's image assets when Generate › Image Assets is on. pub fn on_saved(s: &Session, index: usize) -> Option { let d = s.documents().get(index)?; - if !s.file_menu.image_assets.contains(&d.doc.id) || cfg!(target_arch = "wasm32") { + if !s.file_menu.image_assets.contains(&d.doc.id) || !s.prefs().plug_ins.generator_enabled || cfg!(target_arch = "wasm32") { return None; } let dir = assets_dir(d.path.as_deref()?); @@ -1532,6 +1532,16 @@ pub fn specs() -> Vec { fn has_doc(s: &Session) -> std::result::Result<(), String> { s.active().map(|_| ()).ok_or_else(|| "no document open".into()) } + + /// Generate ▸ Image Assets needs Preferences ▸ Plug-ins ▸ Generator enabled, as in + /// Photoshop (where "Enable Generator" is unchecked until turned on). + fn generator_enabled(s: &Session) -> std::result::Result<(), String> { + has_doc(s)?; + if !s.prefs().plug_ins.generator_enabled { + return Err("Enable Generator in Preferences ▸ Plug-ins".into()); + } + Ok(()) + } vec![ spec!( "file.export.saveForWebLegacy", @@ -1566,7 +1576,7 @@ pub fn specs() -> Vec { &["File", "Generate"], None, r##"{"on":bool? (default: toggle),"dir":folder? (default -assets next to the file)} → {enabled, files, errors}; layers named like "foo.png", "200% foo@2x.png", "48x48 icons/a.png8", "photo.jpg80%" are exported, now and after each save"##, - has_doc, + generator_enabled, image_assets ), ] diff --git a/crates/engine/src/web_cmds/tests.rs b/crates/engine/src/web_cmds/tests.rs index 9ba644719d..b35289bedb 100644 --- a/crates/engine/src/web_cmds/tests.rs +++ b/crates/engine/src/web_cmds/tests.rs @@ -610,10 +610,35 @@ fn generator_naming_grammar() { assert!(parse_defaults("defaults are nice", 72.0).is_none()); } +#[test] +fn image_assets_needs_the_generator_preference() { + let dir = tmp("generator_pref"); + let mut s = session(8); + let id = s.active().unwrap().active_layer.unwrap(); + s.execute("layer.renameLayer", json!({"layer": id.0, "name": "logo.png"})).unwrap(); + assert!(!s.prefs().plug_ins.generator_enabled, "off by default, as in Photoshop"); + assert!(!s.is_enabled("file.generate.imageAssets"), "unavailable until it is enabled"); + assert!(s.execute("file.generate.imageAssets", json!({"dir": dir})).is_err()); + + s.execute("prefs.set", json!({"path": "plugIns.generatorEnabled", "value": true})).unwrap(); + assert!(s.is_enabled("file.generate.imageAssets")); + assert_eq!(s.execute("file.generate.imageAssets", json!({"dir": dir})).unwrap()["enabled"], true); + s.active_mut().unwrap().path = Some(format!("{dir}/doc.psd")); + assert!(crate::automate_cmds::document_saved(&mut s, 0).is_some(), "assets are written on save"); + + // Turning it off again takes the menu item away and stops the export, even for a document + // left switched on. + s.execute("prefs.set", json!({"path": "plugIns.generatorEnabled", "value": false})).unwrap(); + assert!(!s.is_enabled("file.generate.imageAssets")); + assert!(crate::automate_cmds::document_saved(&mut s, 0).is_none(), "no assets while Generator is off"); +} + #[test] fn image_assets_are_generated_now_and_on_save() { let dir = tmp("assets"); let mut s = session(8); + // Preferences ▸ Plug-ins ▸ Generator enabled: the feature is opt-in. + s.execute("prefs.set", json!({"path": "plugIns.generatorEnabled", "value": true})).unwrap(); let id = s.active().unwrap().active_layer.unwrap(); s.execute("layer.renameLayer", json!({"layer": id.0, "name": "logo.png, 50% small/logo.gif, logo.jpg80%"})).unwrap(); let r = s.execute("file.generate.imageAssets", json!({"dir": dir})).unwrap(); diff --git a/crates/ui-egui/src/file_ui.rs b/crates/ui-egui/src/file_ui.rs index e014ca9e36..18ac75478a 100644 --- a/crates/ui-egui/src/file_ui.rs +++ b/crates/ui-egui/src/file_ui.rs @@ -1822,6 +1822,10 @@ mod tests { assert_eq!(checked(&app, "view.lockSlices"), Some(false)); crate::menus::invoke(&mut app, &ctx, "view.lockSlices", json!({})).unwrap(); assert_eq!(checked(&app, "view.lockSlices"), Some(true)); + // Generator is opt-in (Preferences ▸ Plug-ins), as in Photoshop: the menu item is + // unavailable until it is enabled. + assert!(crate::menus::invoke(&mut app, &ctx, "file.generate.imageAssets", json!({})).is_err()); + app.run("prefs.set", json!({"path": "plugIns.generatorEnabled", "value": true})).unwrap(); crate::menus::invoke(&mut app, &ctx, "file.generate.imageAssets", json!({})).unwrap(); assert_eq!(checked(&app, "file.generate.imageAssets"), Some(true)); // Form dialogs open for the rest.