diff --git a/crates/ui-egui/src/io.rs b/crates/ui-egui/src/io.rs index 2c700a919..55a39f923 100644 --- a/crates/ui-egui/src/io.rs +++ b/crates/ui-egui/src/io.rs @@ -440,8 +440,9 @@ fn plan(app: &VectorcraftApp, mode: SaveMode, p: &Value) -> Result, +/// Options, the Save PDF dialog, or the save options dialog: native files on Save As only, `.ai` +/// files on Save As and Save a Copy), and so does Save As or Save a Copy to a given SVG path. +/// → `{path, format, warnings…}` once written, or `{pending: , /// path?}` while a dialog is open. pub fn save(app: &mut VectorcraftApp, mode: SaveMode, p: &Value, ask_options: bool) -> Result { remember_view(app); @@ -524,12 +525,14 @@ fn ask_before_losing(app: &mut VectorcraftApp, path: &str, command: &str, params } /// Does a save (`mode`) to a picked file of format `f` ask for its options first? Native and `.ai` -/// files only on Save As (the options dialog after the save panel); Save, a Copy and templates -/// reuse the remembered options or Use Compression. +/// files on Save As (the options dialog after the save panel), and `.ai` files also on Save a Copy, +/// so the copy is not written in the version last saved whatever the panel's `.ai` type says +/// (#3141); Save and templates reuse the remembered options or Use Compression. fn asks_options(mode: SaveMode, f: &Format) -> bool { !f.options.is_empty() && match f.id { - "vectorcraft" | "ai" => mode == SaveMode::SaveAs, + "vectorcraft" => mode == SaveMode::SaveAs, + "ai" => matches!(mode, SaveMode::SaveAs | SaveMode::Copy), "template" | "ait" => false, _ => true, } diff --git a/crates/ui-egui/src/tests_aisave.rs b/crates/ui-egui/src/tests_aisave.rs index b6af7a31b..03a17dd1d 100644 --- a/crates/ui-egui/src/tests_aisave.rs +++ b/crates/ui-egui/src/tests_aisave.rs @@ -35,9 +35,30 @@ fn save_as_ai_writes_a_pdf_that_reopens_editable() { fn save_a_copy_as_ai_keeps_the_path() { let (mut app, written) = app("/tmp/copy.ai"); app.session.doc_mut().unwrap().path = Some("/tmp/doc.vectorcraft".into()); - app.run("file.saveCopy", json!({"compression": {"compressText": false}})).unwrap(); + // Save a Copy asks for the .ai options like Save As (#3141), then writes. + let r = app.run("file.saveCopy", json!({"compression": {"compressText": false}})).unwrap(); + assert_eq!(r["pending"], "saveOptions", "{r}"); + crate::dialogs::confirm(&mut app).unwrap(); let w = written.borrow(); assert_eq!(w[0].0, "/tmp/copy.ai"); assert!(vectorcraft_pdf::editing(&w[0].1).is_some_and(|e| e.intact)); assert_eq!(app.session.active().unwrap().path.as_deref(), Some("/tmp/doc.vectorcraft")); } + +/// A document last saved as legacy version 7 .ai: Save a Copy as .ai asks for the options (Version +/// included) instead of silently writing another legacy file, and choosing PDF-compatible there +/// writes a PDF (#3141). +#[test] +fn save_a_copy_as_ai_asks_for_the_version() { + let (mut app, written) = app("/tmp/copy.ai"); + app.run("file.saveAs", json!({"path": "/tmp/legacy.ai", "options": {"aiVersion": "7"}})).unwrap(); + written.borrow_mut().clear(); + let r = app.run("file.saveCopy", Value::Null).unwrap(); + assert_eq!(r["pending"], "saveOptions", "{r}"); + assert!(written.borrow().is_empty()); + let d = app.ui.dialog.as_mut().unwrap(); + assert_eq!((d.str("format").as_str(), d.str("__action").as_str()), ("ai", "file.saveCopy")); + d.fields.insert("aiVersion".into(), json!("pdf")); + crate::dialogs::confirm(&mut app).unwrap(); + assert!(written.borrow()[0].1.starts_with(b"%PDF")); +} diff --git a/docs/mcp.md b/docs/mcp.md index d8028ab51..1d5a9fcb1 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3208,8 +3208,8 @@ or in `options` (`file.formatOptions {format}` lists them with their values): (default true) with false write blank pages around the native document (smaller; other apps show empty pages). - `.ai` only: `compress` (default true) is the PDF option `compression.compressText`. -File → Save As asks for these in the save options dialog (VectorCraft Options) after the save panel; Save, Save a Copy -and Save as Template reuse the options as last saved (or Use Compression). +File → Save As asks for these in the save options dialog (VectorCraft Options) after the save panel, and Save a Copy +does too for a `.ai` file; Save, Save a Copy to a native file and Save as Template reuse the options as last saved (or Use Compression). ```json {"name":"run_command","arguments":{"command":"file.saveAs","params":{"path":"/tmp/set.vectorcraft","separateArtboards":true,"range":"2-3","includeLinked":true}}}