.jam import: board items only on frames, authors without control characters, board items in the dump (#1808)

From the pre-ready review: a sticky or shape item on a non-frame layer is ignored with the warning; the author drops control characters as board.sticky does; dump_samples writes a whiteboard's board items; a doc link made plain text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote; Fable 5.1 reviewed
This commit is contained in:
Trevor BrownandClaude Opus 5.5 committed 2026-11-10 09:43:30 -05:00
1 parent 4201fed27d
commit a415598e11
4 files changed
+18 -7

No files matched your search

+2 -2
View File
@@ -56,8 +56,8 @@ pub(super) fn to_rest(msg: &Kv, name: &str, max_nodes: usize) -> (Value, Vec<Str
to_rest_for(msg, name, max_nodes, false)
}
/// [`to_rest`] for a FigJam whiteboard (`board`) or a design file: a whiteboard's stickies come
/// out as native board items.
/// The REST JSON (as `to_rest`) for a FigJam whiteboard (`board`) or a design file: a
/// whiteboard's stickies come out as native board items.
pub(super) fn to_rest_for(msg: &Kv, name: &str, max_nodes: usize, board: bool) -> (Value, Vec<String>, Vec<Option<VectorNetwork>>) {
let mut m = Mapper::new(msg, max_nodes);
m.board = board;
+2 -1
View File
@@ -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();
}
}
+7 -3
View File
@@ -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 {
@@ -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:?}");
}