diff --git a/crates/format/src/fig/map.rs b/crates/format/src/fig/map.rs index a236e1fdb..dc51f9156 100644 --- a/crates/format/src/fig/map.rs +++ b/crates/format/src/fig/map.rs @@ -56,8 +56,8 @@ pub(super) fn to_rest(msg: &Kv, name: &str, max_nodes: usize) -> (Value, Vec (Value, Vec, Vec>) { let mut m = Mapper::new(msg, max_nodes); m.board = board; diff --git a/crates/format/src/fig/tests.rs b/crates/format/src/fig/tests.rs index a8abf56fe..e024920a7 100644 --- a/crates/format/src/fig/tests.rs +++ b/crates/format/src/fig/tests.rs @@ -1583,7 +1583,8 @@ fn dump_samples() { std::fs::write(format!("{out}/{stem}.schema.txt"), txt).unwrap(); let msg = schema.decode_message(schema.find("Message").unwrap(), &message).unwrap(); std::fs::write(format!("{out}/{stem}.json"), serde_json::to_string_pretty(&debug_json(&msg)).unwrap()).unwrap(); - let (rest, ..) = map::to_rest(&msg, &stem, super::layer_budget(message.len())); + // A whiteboard's dump shows its native board items as the import makes them. + let (rest, ..) = map::to_rest_for(&msg, &stem, super::layer_budget(message.len()), a.canvas.starts_with(super::JAM_HEADER)); std::fs::write(format!("{out}/{stem}.rest.json"), serde_json::to_string_pretty(&rest).unwrap()).unwrap(); } } diff --git a/crates/format/src/rest.rs b/crates/format/src/rest.rs index 681a0defa..8797d9efa 100644 --- a/crates/format/src/rest.rs +++ b/crates/format/src/rest.rs @@ -797,12 +797,16 @@ impl<'a> Ctx<'a> { /// The `board` field (a FigCraft extension): what the object is on a board. A sticky or shape /// is set at once; anything else, or junk, is ignored with a warning. fn board_item(&mut self, n: &mut Node, b: &'a Value) { + // A sticky or shape is a frame holding its layers: on anything else it is ignored. + let frame = n.kind.is_frame_like(); let item = match text(b, "type") { - Some("sticky") => { + Some("sticky") if frame => { let author = text(b, "author").unwrap_or("").trim(); - Some(figcraft_doc::BoardItem::Sticky { author: author.chars().take(64).collect() }) + Some(figcraft_doc::BoardItem::Sticky { author: author.chars().filter(|c| !c.is_control()).take(64).collect() }) + } + Some("shape") if frame => { + text(b, "shape").and_then(figcraft_doc::BoardShape::from_id).map(|shape| figcraft_doc::BoardItem::Shape { shape }) } - Some("shape") => text(b, "shape").and_then(figcraft_doc::BoardShape::from_id).map(|shape| figcraft_doc::BoardItem::Shape { shape }), _ => None, }; match item { diff --git a/crates/format/src/rest/import_regression_tests.rs b/crates/format/src/rest/import_regression_tests.rs index 90cc4c5c3..5d5bfb48e 100644 --- a/crates/format/src/rest/import_regression_tests.rs +++ b/crates/format/src/rest/import_regression_tests.rs @@ -97,7 +97,7 @@ fn thousands_of_distinct_style_references_import_in_linear_time() { #[test] fn a_board_field_becomes_the_nodes_board_item() { let frame = json!({"id": "1:1", "name": "Note", "type": "FRAME", "relativeTransform": at(0.0, 0.0), "size": {"x": 200, "y": 200}, - "board": {"type": "sticky", "author": "A"}}); + "board": {"type": "sticky", "author": "A\u{7}"}}); let (doc, warnings) = import(&file(json!([frame])), &no_images).unwrap(); assert_eq!(named(&doc, "Note").board, Some(figcraft_doc::BoardItem::Sticky { author: "A".into() })); assert!(!warnings.iter().any(|w| w.contains("board item")), "{warnings:?}"); @@ -112,4 +112,10 @@ fn junk_board_fields_are_ignored_with_a_warning() { assert!(named(&doc, "Junk").board.is_none(), "{junk}"); assert!(warnings.iter().any(|w| w.contains("unknown board item on Junk was ignored")), "{junk}: {warnings:?}"); } + // A sticky is a frame holding its layers: on a rectangle the item is ignored. + let rect = json!({"id": "1:2", "name": "Rect", "type": "RECTANGLE", "relativeTransform": at(0.0, 0.0), "size": {"x": 20, "y": 20}, + "board": {"type": "sticky", "author": "A"}}); + let (doc, warnings) = import(&file(json!([rect])), &no_images).unwrap(); + assert!(named(&doc, "Rect").board.is_none()); + assert!(warnings.iter().any(|w| w.contains("unknown board item on Rect was ignored")), "{warnings:?}"); }