M4.66: File > Export Selection collects the selection and opens the Assets tab
Export Selection… (file.exportSelection, a UI command) now collects the selected objects as assets, one per object (multiple: false makes one), reusing assets of art already collected, and opens Export for Screens on its Assets tab with just those checked. document.exportSelection stays the one-shot export for agents and scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6757c7f9f3
commit
46dc67ca50
|
|
@ -1,5 +1,5 @@
|
|||
//! Export for Screens' Assets tab, drawn headlessly: the checked assets, the OK label, desktop
|
||||
//! folders and web downloads.
|
||||
//! Export for Screens' Assets tab and File → Export Selection…, drawn headlessly: collecting the
|
||||
//! selection, the checked assets, the OK label, desktop folders and web downloads.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -45,6 +45,46 @@ fn assets(app: &mut VectorcraftApp) -> Vec<Value> {
|
|||
app.run("assets.list", json!({})).unwrap()["assets"].as_array().unwrap().clone()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_selection_collects_the_selection_and_opens_the_assets_tab() {
|
||||
let (mut app, log, _) = app(true);
|
||||
assert!(crate::menus::enabled(&app, "file.exportSelection"));
|
||||
let r = app.run("file.exportSelection", json!({})).unwrap();
|
||||
let list = assets(&mut app);
|
||||
assert_eq!(list.len(), 2, "one asset per object");
|
||||
let ids: Vec<Value> = list.iter().map(|a| a["id"].clone()).collect();
|
||||
assert_eq!(r["assets"], json!(ids));
|
||||
assert_eq!(kind(&app), Some(export_for_screens::KIND));
|
||||
assert_eq!((field(&app, "tab"), field(&app, "assets")), (json!("assets"), json!(ids)));
|
||||
// Collecting the same art again opens on the same assets.
|
||||
super::cancel(&mut app);
|
||||
app.run("file.exportSelection", json!({})).unwrap();
|
||||
assert_eq!(assets(&mut app).len(), 2);
|
||||
assert_eq!(field(&app, "assets"), json!(ids));
|
||||
let text = painted_text(&mut app, |app, ui| super::show(app, ui.ctx()));
|
||||
assert!(text.contains("Asset 1") && text.contains("Asset 2") && text.contains("Select All"), "{text}");
|
||||
// The web downloads both as one zip.
|
||||
let r = confirm(&mut app).unwrap();
|
||||
assert_eq!(r["files"], json!(["Asset-1.png", "Asset-2.png"]));
|
||||
assert_eq!(log.borrow().len(), 1);
|
||||
assert!(app.ui.dialog.is_none());
|
||||
// Nothing selected: Export Selection is off; document.exportSelection still exports in one go.
|
||||
app.run("select.set", json!({"ids": []})).unwrap();
|
||||
assert!(!crate::menus::enabled(&app, "file.exportSelection"));
|
||||
app.run("select.set", json!({"ids": [list[0]["nodes"][0]]})).unwrap();
|
||||
assert!(app.session.execute("document.exportSelection", &json!({"format": "png"})).unwrap()["dataBase64"].is_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_selection_as_one_asset() {
|
||||
let (mut app, _, ids) = app(true);
|
||||
app.run("file.exportSelection", json!({"multiple": false})).unwrap();
|
||||
let list = assets(&mut app);
|
||||
assert_eq!(list.len(), 1);
|
||||
assert_eq!(list[0]["nodes"], json!(ids));
|
||||
assert_eq!(field(&app, "assets"), json!([list[0]["id"]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_assets_tab_exports_the_checked_assets_into_the_folder() {
|
||||
let (mut app, log, _) = app(false);
|
||||
|
|
@ -89,6 +129,7 @@ fn the_menus_reach_asset_export() {
|
|||
let (mut app, _, _) = app(false);
|
||||
let entries = crate::menus::menu_entries(&app);
|
||||
let find = |cmd: &str, label: &str| entries.iter().find(|e| e.command.as_deref() == Some(cmd) && e.label == label).map(|e| e.path.clone());
|
||||
assert_eq!(find("file.exportSelection", "Export Selection…"), Some(vec!["File".to_string()]));
|
||||
assert_eq!(find("assets.add", "As Single Asset"), Some(vec!["Object".to_string(), "Collect for Export".to_string()]));
|
||||
assert_eq!(find("assets.add", "As Multiple Assets"), Some(vec!["Object".to_string(), "Collect for Export".to_string()]));
|
||||
let window = entries.iter().find(|e| e.label == "Asset Export").unwrap();
|
||||
|
|
|
|||
|
|
@ -485,6 +485,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
|||
"",
|
||||
"{…document.exportForWeb settings} write the HTML page and its images to a temporary folder and open the page in the default browser (desktop) → {path}",
|
||||
),
|
||||
(
|
||||
"file.exportSelection",
|
||||
"Export Selection…",
|
||||
"",
|
||||
"{multiple?: true} collect the selected objects as assets (assets.add: one per object; false: one of them all) and open Export for Screens on its Assets tab with them checked → {assets: [asset id…]}; document.exportSelection exports the selection in one go instead",
|
||||
),
|
||||
];
|
||||
|
||||
/// Handle a UI command. `None` = not a UI command (the engine handles it).
|
||||
|
|
@ -871,6 +877,13 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
|
|||
"file.saveForWeb" if p.as_object().is_none_or(|o| o.is_empty()) => crate::dialogs::save_for_web::open(app),
|
||||
"file.saveForWeb" => crate::dialogs::save_for_web::save(app, p.clone()),
|
||||
"file.saveForWeb.browser" => crate::dialogs::save_for_web::browser_preview(app, p),
|
||||
// File → Export Selection…: the selection becomes assets, shown checked on Export for
|
||||
// Screens' Assets tab.
|
||||
"file.exportSelection" => app.run("assets.add", json!({ "multiple": p.get("multiple").and_then(Value::as_bool).unwrap_or(true) })).map(|r| {
|
||||
let ids: Vec<u64> = r["assets"].as_array().into_iter().flatten().filter_map(Value::as_u64).collect();
|
||||
crate::dialogs::open_export_for_screens_assets(app, Some(&ids));
|
||||
json!({ "assets": ids })
|
||||
}),
|
||||
_ => return None,
|
||||
};
|
||||
Some(r)
|
||||
|
|
@ -1106,6 +1119,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool {
|
|||
"ui.packageDialog" | "docInfo.save" => app.session.active().is_some(),
|
||||
"ui.epsOptionsDialog" => app.session.active().is_some(),
|
||||
"file.saveForWeb" | "file.saveForWeb.browser" => app.session.active().is_some(),
|
||||
"file.exportSelection" => app.session.active().is_some_and(|d| !d.selection.is_empty()),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
@ -1169,7 +1183,7 @@ pub fn menu_tree() -> Vec<(&'static str, Vec<Item>)> {
|
|||
c("Save for Web (Legacy)…", "file.saveForWeb"),
|
||||
],
|
||||
),
|
||||
c("Export Selection…", "document.exportSelection"),
|
||||
c("Export Selection…", "file.exportSelection"),
|
||||
Sep,
|
||||
c("Package…", "ui.packageDialog"),
|
||||
sub("Scripts", vec![todos("Other Script…", "Cmd+F12")]),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
| Background Save / Export | `app.save`, `app.export {path}`, `file.save`, `file.saveAs`, `file.saveCopy` | with the `backgroundSave` / `backgroundExport` preferences on (the default) the desktop app writes the file from a snapshot on a worker thread: the call returns `{path, background: true, status}` at once, `ui.inspect` lists the jobs still running in `background` (labels such as `"Saving a.vectorcraft"`), and the document counts as saved (as of the snapshot) once its file is written. Quit and Close wait for them. With the preferences off (`prefs.set`), saves return once written |
|
||||
| `engine.execute` `file.exportForScreens` | `{}` / `{…document.exportForScreens params}` | without params opens `exportForScreens` on the settings the document last exported with (`document.exportSettings`). Fields: `tab` (`artboards` \| `assets`), `select` (`all` \| `range` \| `full`), `range` ("1-3, 5"), `boards` (a bool per artboard; checking them sets `select` and `range`), `includeBleed`, `folder` (desktop; default the Desktop, else home), `openLocation`, `subfolders`, `prefix`, `preset` (`""` \| `mobile` \| `density`), `formats` (rows `{format, scale: "2x" \| "100w" \| "100h" \| "72ppi", suffix, quality?, folder?}`), `settings` (`{png \| png8 \| jpg \| webp \| gif \| svg \| pdf: {…options}}`, the gear's Format Settings; `__settings` names the format shown). `ui.dialog.confirm` exports (a bad range or no folder keeps it open); on the web OK reads "Download" and the files download (several as one `.zip`). With params it runs `document.exportForScreens`: no `folder` returns the files (or with `zip: true` one store-only zip) as `dataBase64`; with `openLocation` the first file is shown in the file manager |
|
||||
| Export for Screens' Assets tab, Asset Export panel | `ui.dialog.set` `tab: "assets"`, `assets: [asset id…]`; `window.panel {panel: "assetExport"}` | on the Assets tab of `exportForScreens` the `assets` field lists the checked asset ids (all of them when it opens); `ui.dialog.confirm` runs `document.exportForScreens {assets}` (nothing checked keeps it open) and OK reads "Export Asset". The Asset Export panel acts through `assets.*` and shares the format rows with the dialog (`assets.settings.set`) |
|
||||
| `engine.execute` `file.exportSelection` | `{multiple?}` | File › Export Selection…: collects the selected objects as assets (`assets.add`; one per object unless `multiple` is false) and opens `exportForScreens` on its Assets tab with just them checked → `{assets}`. `document.exportSelection` still exports the selection in one go, without the dialog |
|
||||
|
||||
| `ui.dialog.*` on `importPdf` | `{field: "page" \| "allPages" \| "range" \| "cropTo" \| "password", value}` | opening (`app.open`, `file.open`) a PDF or `.ai` with several pages or a password, or placing one (`file.place` without `page`), opens the Import PDF dialog (`mode`: `open` or `place`; `pageCount`). While `__locked` is true, set `password` and `ui.dialog.confirm` to unlock it (a one-page file then opens or is placed). Then set `allPages: false` and `range` ("2-3, 5") to open some pages, or `page` to place one, and `cropTo` (`bounding`, `art`, `crop`, `trim`, `bleed`, `media`); `ui.dialog.confirm` opens or places them (placing keeps the other `file.place` params, such as `link`, `template` and `at`, in `__place`). `document.open {pages, cropTo, password}` and `file.place {page, crop, password}` do the same without the dialog |
|
||||
|
||||
|
|
|
|||
|
|
@ -1415,7 +1415,8 @@ defaults); presets and remembered settings are preferences.
|
|||
|
||||
## Asset Export
|
||||
|
||||
Assets are pieces of art collected for export on their own (Window › Asset Export, Object › Collect for Export).
|
||||
Assets are pieces of art collected for export on their own (Window › Asset Export, Object › Collect for Export, File ›
|
||||
Export Selection…).
|
||||
`assets.add {ids?, multiple?}` collects the selected objects (or `ids`): one asset per object, or with
|
||||
`multiple: false` one asset of them all, named after the object or `Asset 1`, `Asset 2`…; art already collected keeps
|
||||
its asset. Assets name their objects, so they follow edits: moving the art moves the crop, and deleting it
|
||||
|
|
|
|||
Loading…
Reference in New Issue