Merge PR #8509: fix(atn): preserve unsupported event kinds

This commit is contained in:
David Mann
2026-10-22 23:40:13 +02:00
2 changed files with 43 additions and 4 deletions
+15 -4
View File
@@ -3711,7 +3711,11 @@ pub struct Imported {
fn photoshop_step(item: &AtnItem) -> (String, Value) {
let descriptor = item.descriptor.as_ref().map(|d| photocraft_paint::tile::b64_encode(&d.to_bytes()));
(PHOTOSHOP_STEP.into(), json!({"event": item.event.id(), "name": item.label(), "descriptor": descriptor}))
let event_kind = match &item.event {
AtnEvent::Code(_) => "code",
AtnEvent::Name(_) => "name",
};
(PHOTOSHOP_STEP.into(), json!({"event": item.event.id(), "eventKind": event_kind, "name": item.label(), "descriptor": descriptor}))
}
/// Integral floats become integers (`2.0` → `2`), as the commands' own docs and recordings
@@ -3795,9 +3799,16 @@ pub struct Skipped {
fn export_item(id: &str, params: &Value, set: &str) -> Option<AtnItem> {
if id == PHOTOSHOP_STEP {
let event = jstr(params, "event")?;
let event = match event.as_bytes() {
[a, b, c, d] => AtnEvent::Code([*a, *b, *c, *d]),
_ => AtnEvent::name(event),
let event = match jstr(params, "eventKind") {
Some("code") => AtnEvent::Code(event.as_bytes().try_into().ok()?),
Some("name") => AtnEvent::name(event),
Some(_) => return None,
// Older PhotoCraft action data did not retain the event kind. Keep its previous
// best-effort export behavior for compatibility.
None => match event.as_bytes() {
[a, b, c, d] => AtnEvent::Code([*a, *b, *c, *d]),
_ => AtnEvent::name(event),
},
};
let descriptor = match jstr(params, "descriptor") {
Some(b64) => Some(Descriptor::from_bytes(&photocraft_paint::tile::b64_decode(b64)?).ok()?),
+28
View File
@@ -886,6 +886,34 @@ fn unsupported_shapes_become_photoshop_steps_not_errors() {
assert!(imp.actions[0].disabled.is_empty(), "steps with a document effect stay on so playback reports them");
}
/// #8482: four-byte string events must not become `long` event codes when retained and saved.
#[test]
fn save_preserves_the_kind_of_unsupported_atn_events() {
let source = AtnFile {
name: UnicodeString::new_nul("S"),
expanded: false,
actions: vec![AtnAction {
name: UnicodeString::new_nul("A"),
items: vec![
AtnItem { event: AtnEvent::Name(b"make".to_vec()), dictionary: b"Make".to_vec(), ..Default::default() },
AtnItem { event: AtnEvent::Code(*b"Qwer"), dictionary: b"Unknown code".to_vec(), ..Default::default() },
],
..Default::default()
}],
warnings: vec![],
};
let mut session = Session::new();
session.execute("actions.load", json!({"data": b64(&atn::write(&source).unwrap())})).unwrap();
let saved = session.execute("actions.save", json!({"set": "S"})).unwrap();
let bytes = photocraft_paint::tile::b64_decode(saved["data"].as_str().unwrap()).unwrap();
let round_trip = atn::parse(&bytes).unwrap();
assert_eq!(
round_trip.actions[0].items.iter().map(|item| item.event.clone()).collect::<Vec<_>>(),
[AtnEvent::Name(b"make".to_vec()), AtnEvent::Code(*b"Qwer")]
);
}
/// #6745: the events of a Photoshop 2026 retouching set (ID-photo background swap, whitening,
/// highlight cleanup, high-pass sharpening), in the stringID form Photoshop wrote them.
#[test]