diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index 6cf62385c9..8196ad27c2 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -3184,7 +3184,7 @@ mod tests { (json!(long.to_string()), "an action in the action set file has more than 10,000 steps"), (json!(" ".repeat(files::MAX_SET_FILE + 1)), "the action set file is larger than 8 MiB"), ( - json!("{\"format\": \"vcactions\", \"version\": 5, \"name\": \"S\", \"actions\": []}"), + json!("{\"format\": \"vcactions\", \"version\": 6, \"name\": \"S\", \"actions\": []}"), "the action set file needs a later version of VectorCraft", ), ] { @@ -4693,19 +4693,22 @@ mod tests { let (mut app, _, _) = app_with_two_rectangles(); app.run(INSERT_SELECT_PATH, json!({"set": 0, "action": 0, "step": 0})).unwrap(); let saved = files::write_set(&app.ui.action_sets[0]); - assert_eq!(serde_json::from_str::(&saved).unwrap()["version"], json!(4), "older builds must refuse a Select Path step"); + assert_eq!(serde_json::from_str::(&saved).unwrap()["version"], json!(5), "older builds must refuse a Select Path step"); let loaded = files::read_set(&saved, "Test").unwrap(); assert_eq!(loaded.actions[0].steps, app.ui.action_sets[0].actions[0].steps); let step = app.ui.action_sets[0].actions[0].steps[1].clone(); let nested = json!({"commands": [{"command": SELECT_PATH_STEP, "params": step.1}]}); let set = ActionSet::new("Nested", vec![Action::new("A", vec![("command.batch".into(), nested)])]); - assert_eq!(serde_json::from_str::(&files::write_set(&set)).unwrap()["version"], json!(4), "a step in a recorded batch counts"); + assert_eq!(serde_json::from_str::(&files::write_set(&set)).unwrap()["version"], json!(5), "a step in a recorded batch counts"); + let mut both = app.ui.action_sets[0].clone(); + both.actions[0].steps.push((SELECT_OBJECT_STEP.into(), json!({"note": "a", "caseSensitive": false, "wholeWord": false}))); + assert_eq!(serde_json::from_str::(&files::write_set(&both)).unwrap()["version"], json!(5), "with a Select Object step too"); for params in [json!({"id": 3}), json!({"id": -1, "fingerprint": step.1["fingerprint"]}), json!({"id": 3, "fingerprint": "v1:xyz"}), json!(42)] { let bad = json!({"command": SELECT_PATH_STEP, "params": params}); for s in [bad.clone(), json!({"command": "command.batch", "params": {"commands": [bad]}})] { - let file = json!({"format": "vcactions", "version": 4, "name": "Bad", "actions": [{"name": "A", "steps": [s]}]}); + let file = json!({"format": "vcactions", "version": 5, "name": "Bad", "actions": [{"name": "A", "steps": [s]}]}); assert_eq!(files::read_set(&file.to_string(), "Test").unwrap_err(), "not an action set file", "{s}"); } } @@ -5786,7 +5789,7 @@ mod tests { json!({"format": "vcactions", "version": version, "name": "Old", "actions": [{"name": "A", "steps": [{"command": "object.move"}]}]}); assert_eq!(files::read_set(&old.to_string(), "Test").unwrap().actions[0].steps.len(), 1, "version {version}"); } - let later = json!({"format": "vcactions", "version": 5, "name": "New", "actions": []}); + let later = json!({"format": "vcactions", "version": 6, "name": "New", "actions": []}); assert_eq!(files::read_set(&later.to_string(), "Test").unwrap_err(), "the action set file needs a later version of VectorCraft"); for params in [json!({"message": 5}), json!({"allowContinue": "yes"}), json!(42)] { let stop = json!({"command": "action.stop", "params": params}); diff --git a/crates/ui-egui/src/panels/actions/files.rs b/crates/ui-egui/src/panels/actions/files.rs index 14f7717e64..6aa8b95df4 100644 --- a/crates/ui-egui/src/panels/actions/files.rs +++ b/crates/ui-egui/src/panels/actions/files.rs @@ -1,6 +1,6 @@ //! Action set files: the Actions panel menu's Save Actions… and Load Actions…. //! -//! An action set file (`.vcactions`) is JSON: `{"format": "vcactions", "version": 1 to 4, "name", +//! An action set file (`.vcactions`) is JSON: `{"format": "vcactions", "version": 1 to 5, "name", //! "actions": [{"name", "steps": [{"command", "params", "excluded"?}], "key"?, "color"?}]}`. Each step is a //! command id and its params, as `command.batch` takes them, or a stop: the command `action.stop` //! with `{"message", "allowContinue"}`. A Select Object step is the command `select.byNote` with @@ -10,9 +10,9 @@ //! or with a value that is not one, has none. `excluded: true` keeps a step but skips it during //! playback. Sets with exclusions use version 2 so older builds refuse them rather than replay //! excluded steps, sets with stops use version 3 so older builds refuse them rather than fail at a -//! stop, and sets with Select Object or Select Path steps use version 4 so older builds refuse them -//! rather than fail at that step. Other sets use version 1, and earlier version 1 to 3 files still -//! work. +//! stop, sets with Select Object steps use version 4 and sets with Select Path steps use version 5 so +//! older builds refuse them rather than fail at that step. Other sets use version 1, and earlier +//! version 1 to 4 files still work. //! Load Actions… also reads the action sets another app saves as text (`.aia`, through //! `actionSet.read`). @@ -173,11 +173,12 @@ pub fn write_set(set: &ActionSet) -> String { let has_select_object = steps().any(|(command, params)| !select_objects_in(command, params).is_empty()); let has_select_path = steps().any(|(command, params)| !select_paths_in(command, params).is_empty()); let has_stop = steps().any(|(command, params)| !stops_in(command, params).is_empty()); - let version = match (has_select_object || has_select_path, has_stop, set.actions.iter().any(|a| !a.excluded_steps.is_empty())) { - (true, ..) => 4, - (false, true, _) => 3, - (false, false, true) => 2, - (false, false, false) => 1, + let version = match (has_select_path, has_select_object, has_stop, set.actions.iter().any(|a| !a.excluded_steps.is_empty())) { + (true, ..) => 5, + (false, true, ..) => 4, + (false, false, true, _) => 3, + (false, false, false, true) => 2, + (false, false, false, false) => 1, }; let file = SetFile { format: ACTIONS_EXT.into(), version, name: set.name.clone(), actions }; serde_json::to_string_pretty(&file).unwrap_or_default() @@ -192,7 +193,7 @@ pub fn read_set(text: &str, name: &str) -> Result { if f.format != ACTIONS_EXT { return Err("not an action set file".into()); } - if !matches!(f.version, 1..=4) { + if !matches!(f.version, 1..=5) { return Err("the action set file needs a later version of VectorCraft".into()); } if f.actions.len() > MAX_SET_ACTIONS { diff --git a/docs/mcp.md b/docs/mcp.md index 15fa2b061b..1e0571c3c2 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2987,14 +2987,15 @@ already walks Image Trace. Short agent entry points: the file at `path`, else a picked one, or the file's text `data`, adds its set after the other sets and returns `{set, name, actions}`. A set named like one already in the panel gets the next free number (`Print 2`), and the other sets and the current set stay as they are. An action set file (`.vcactions`) is JSON: - `{"format": "vcactions", "version": 1 to 4, "name", "actions": [{"name", "steps": [{"command", "params", "excluded"?}]}]}`, each step + `{"format": "vcactions", "version": 1 to 5, "name", "actions": [{"name", "steps": [{"command", "params", "excluded"?}]}]}`, each step a command id and its params as `command.batch` takes them, or a stop: the command `action.stop` with `{message, allowContinue}`. A Select Object step is the command `select.byNote` with `{note, caseSensitive, - wholeWord}`. `excluded: true` preserves a step but omits it from playback; - an absent or false flag includes the step. Files with Select Object steps use version 4, files with stops and no - Select Object step use version 3, and files with excluded steps and neither use version 2, so older builds refuse them - instead of playing the excluded steps or failing at a stop or a Select Object step; other files use version 1. A file with another `format` or a `version` other than - 1 to 4, a stop whose message is not text or whose `allowContinue` is not true or false, a Select Object step whose params `ui.action.insertSelectObject` would refuse, a file larger than 8 MiB, one with more than 1,000 actions and one with an action of more than 10,000 steps are + wholeWord}`, and a Select Path step is the command `select.storedPath` with `{id, fingerprint}`. `excluded: true` preserves a step but omits it from playback; + an absent or false flag includes the step. Files with Select Path steps use version 5, files with Select Object steps + and no Select Path step use version 4, files with stops and neither use version 3, and files with excluded steps and + none of these use version 2, so older builds refuse them instead of playing the excluded steps or failing at a stop, a + Select Object step or a Select Path step; other files use version 1, and version 1 to 4 files still load. A file with another `format` or a `version` other than + 1 to 5, a stop whose message is not text or whose `allowContinue` is not true or false, a Select Object step whose params `ui.action.insertSelectObject` would refuse, a file larger than 8 MiB, one with more than 1,000 actions and one with an action of more than 10,000 steps are refused, and no set is added. So is a file whose set `ui.actionSet.save` would write larger than 8 MiB (a file with less white space, a set name that gets a number, or an `.aia` set), with the error `the action set would be larger than 8 MiB`. The Actions panel menu's Save Actions… and Load Actions… run these; Load Actions… is @@ -3097,7 +3098,7 @@ already walks Image Trace. Short agent entry points: index past the last set, action or step, no action and no recording, or a step past the 10,000-step and 8 MiB limits is an error, and then nothing changes. The panel menu's Insert Select Path runs this for the selected step or action, or the recording in progress, and is off while the selection is not such a path. The step's row in the panel reads - Select Path. An action set file holding the step is version 4, and Load Actions… refuses a step whose params are not + Select Path. An action set file holding the step is version 5, and Load Actions… refuses a step whose params are not an id and such a fingerprint. - Actions: `select.storedPath {id, fingerprint}` selects only the path `id` while its fingerprint is still `fingerprint` and it can be selected: not hidden or locked, nor in a hidden or locked layer or group, a template diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 9d3107a2ae..57df2225e6 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -743,7 +743,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Actions panel: the panel menu's Playback Options… sets how actions play: Accelerated (the default) plays every step at once, Step by Step plays one step at a time with the canvas drawn after each, and Pause For waits the given number of seconds between steps ([Specify playback speed](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/specify-playback-speed.html); `ui.action.playbackOptions`). VectorCraft's own rules, which the page does not cover: the pause is a whole number of seconds from 1 to 60, the setting is kept with the UI preferences, a paced playback ends at Stop Playing/Recording or when a step comes due while another document is active, each step of a paced playback is its own undo step, and Batch… plays at full speed. - Actions panel: the panel menu's Insert Stop… opens Record Stop, with a Message box and Allow Continue, and adds a stop to the recording in progress, after the selected command, or at the end of the selected action ([Insert stops in actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/insert-stops-in-actions.html); `ui.action.insertStop`). A playing action waits at the stop and shows the message so the user can do a manual task, and Play Current Selection plays the rest ([Illustrator CS5 help](https://help.adobe.com/archive/en_US/illustrator/cs5/illustrator_cs5_help.pdf#page=459); `ui.action.continue`). With Allow Continue, the message box also has Continue. VectorCraft's own rules, which the pages do not cover: an empty message still waits, Stop Playing/Recording ends the playback and keeps the steps already played, the rest plays on the document active then, a stop waits at every playback speed, at Accelerated the steps between two stops are one undo step, Batch… refuses an action with an included stop, and Delete Selection on a selected step deletes that step, a stop or a command, without asking (`ui.action.deleteStep`). - Actions panel: the panel menu's Insert Menu Item… adds a menu command to the recording in progress, after the selected step, or at the end of the selected action, without running it, and playback runs it as the menu does ([Insert non-recordable tasks into actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/insert-non-recordable-tasks-into-actions.html); `ui.action.insertMenuItem`). The dialog's Find box picks the one item that matches the words typed, and a click on a listed item picks it. VectorCraft's own rules, which the page does not cover: items that open a dialog, the Object › Align items, Image Trace's Make and Make and Expand, Pattern's Make and Edit Pattern, and the File, Window and Help menu items are not listed, and an action holds at most 10,000 steps and its set file at most 8 MiB, for stops too. - - Actions panel: with a path selected, the panel menu's Insert Select Path adds a step that selects that path to the recording in progress, after the selected step, or at the end of the selected action, without changing the selection ([Insert non-recordable tasks into actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/insert-non-recordable-tasks-into-actions.html); `ui.action.insertSelectPath`, `select.storedPath`). VectorCraft's own rules, which the page does not cover: the selection must be one whole path or compound path that can be selected, the step stores the path's id and a fingerprint of its anchors, and playback selects the path only while it is still there unchanged and can be selected, else the step is an error and the selection stays; the row reads Select Path, and a set file with the step is version 4. + - Actions panel: with a path selected, the panel menu's Insert Select Path adds a step that selects that path to the recording in progress, after the selected step, or at the end of the selected action, without changing the selection ([Insert non-recordable tasks into actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/insert-non-recordable-tasks-into-actions.html); `ui.action.insertSelectPath`, `select.storedPath`). VectorCraft's own rules, which the page does not cover: the selection must be one whole path or compound path that can be selected, the step stores the path's id and a fingerprint of its anchors, and playback selects the path only while it is still there unchanged and can be selected, else the step is an error and the selection stays; the row reads Select Path, and a set file with the step is version 5. - Actions panel: the panel menu's Select Object… opens Set Selection, where the note typed names the objects to select, and adds a step to the recording in progress, after the selected step, or at the end of the selected action; playback selects the objects whose Attributes note matches it, and the steps after it act on them ([Insert non-recordable tasks into actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/insert-non-recordable-tasks-into-actions.html); `ui.action.insertSelectObject`, `select.byNote`). VectorCraft's own rules, which the page does not cover: the note matches anywhere and in any letter case unless Case Sensitive or Whole Word is checked, hidden and locked objects are left out, the objects found replace the selection, a step that finds none stops the playback with an error, and the note is at most 1,024 bytes. Action sets with the step save as version 4. - Actions panel: Delete Selection deletes the selected action set with its actions and keeps the other sets ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html); `ui.actionSet.delete`). VectorCraft's own rules, which the overview does not cover: deleting a set asks first, Shift-click on a set's name selects a range of sets to delete together, the panel menu's Delete does the same, and Default Actions and the last set can be deleted. Undo does not bring a deleted set back. - Actions panel: the panel menu's Duplicate copies the selected action, else the current action set with all its actions ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html); `ui.actionSet.duplicate`). VectorCraft's own rules, which the overview does not cover: the copy goes right after the original, is named after it with " copy" added and is selected.