From 1056a42a56e482b2177aea203532d86f0f9965ec Mon Sep 17 00:00:00 2001 From: llduyll10 Date: Fri, 9 Oct 2026 13:48:37 +0700 Subject: [PATCH] Open: a saved .ai with several artboards restores its document instead of asking for pages Opening a PDF-compatible .ai (or a PDF saved with Preserve Editing) that has more than one page showed the Import PDF dialog and imported pages, although the file carries its VectorCraft document. Opening now restores that document (every artboard and layer) without asking, as the engine already does when no pages are picked. Placing still asks for the page. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/dialogs/import_pdf.rs | 8 ++++- .../ui-egui/src/dialogs/tests_import_pdf.rs | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/dialogs/import_pdf.rs b/crates/ui-egui/src/dialogs/import_pdf.rs index d6328fce..c485a3d8 100644 --- a/crates/ui-egui/src/dialogs/import_pdf.rs +++ b/crates/ui-egui/src/dialogs/import_pdf.rs @@ -114,11 +114,17 @@ pub(super) fn show_preview(ui: &mut egui::Ui, tex: Option<&egui::TextureHandle>, /// Open `bytes` (or place them with the `file.place` params `place`) through the dialog when the /// file is a PDF (or `.ai`/`.ait`) with several pages or a password; false when there's nothing to -/// ask (the caller goes on). +/// ask (the caller goes on), including a file that carries its VectorCraft document when opening. pub fn offer(app: &mut VectorcraftApp, name: &str, bytes: &[u8], path: Option, place: Option<&Value>) -> bool { if !fileio::detect(name, bytes).is_some_and(|f| matches!(f.id, "pdf" | "ai" | "ait")) { return false; } + // A file carrying its VectorCraft document (a saved .ai, or a PDF saved with Preserve Editing) + // opens as that document, every artboard and layer: there are no pages to pick. Placing still + // asks, since a place takes one page. + if place.is_none() && vectorcraft_pdf::editing_with(bytes, None).is_some() { + return false; + } let (pages, locked) = match vectorcraft_pdf::info(bytes, None) { Ok(i) if i.pages.len() > 1 => (i.pages.len(), false), Err(PdfError::NeedsPassword) => (0, true), diff --git a/crates/ui-egui/src/dialogs/tests_import_pdf.rs b/crates/ui-egui/src/dialogs/tests_import_pdf.rs index 03d1aa8e..88301473 100644 --- a/crates/ui-egui/src/dialogs/tests_import_pdf.rs +++ b/crates/ui-egui/src/dialogs/tests_import_pdf.rs @@ -165,3 +165,35 @@ fn placing_a_pdf_page_asks_for_the_page_and_crop_box() { assert!(app.ui.dialog.is_none()); assert_eq!(placed(&app).1.width(), 80.0); } + +/// A saved `.ai` with several artboards carries its VectorCraft document: opening it restores that +/// document (artboards, layers) instead of asking which pages to import. +#[test] +fn opening_a_saved_ai_with_several_artboards_restores_it_without_asking() { + let mut app = app(); + app.run("artboard.new", json!({"width": 200, "height": 100, "name": "Second"})).unwrap(); + app.run("layer.new", json!({"name": "Front"})).unwrap(); + app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap(); + let saved = app.run("document.save", json!({"format": "ai"})).unwrap(); + let bytes = vectorcraft_format::base64_decode(saved["dataBase64"].as_str().unwrap()).unwrap(); + assert!(vectorcraft_pdf::info(&bytes, None).unwrap().pages.len() > 1, "a page per artboard"); + + io::open_bytes(&mut app, "two.ai", &bytes, Some("/tmp/two.ai".into())).unwrap(); + assert!(app.ui.dialog.is_none(), "no Import PDF dialog"); + assert_eq!(app.session.documents().len(), 2); + let doc = &app.session.active().unwrap().doc; + assert_eq!(doc.artboards.len(), 2); + assert_eq!(doc.artboards[1].name, "Second"); + assert!(doc.layers.iter().any(|l| l.name.as_deref() == Some("Front")), "layers come back"); +} + +/// A PDF without a VectorCraft document still asks, and placing a saved `.ai` still asks for the page. +#[test] +fn placing_a_saved_ai_still_asks_for_the_page() { + let mut app = app(); + app.run("artboard.new", json!({"width": 200, "height": 100})).unwrap(); + let saved = app.run("document.save", json!({"format": "ai"})).unwrap(); + let bytes = vectorcraft_format::base64_decode(saved["dataBase64"].as_str().unwrap()).unwrap(); + assert!(import_pdf::offer(&mut app, "two.ai", &bytes, None, Some(&json!({})))); + assert_eq!(app.ui.dialog.as_ref().unwrap().str("mode"), "place"); +}