From beb87d86965dfc507050bab2cd6c9349d18ab4d8 Mon Sep 17 00:00:00 2001 From: xiaou61 <113765024+xiaou61@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:14:02 +0800 Subject: [PATCH] M1: describe the fallback the export actually takes (#465) The warning still said fixed and content-sized tracks were written as equal tracks, which the export no longer does. It now reports only the tracks whose size the format can't hold, and only for the tracks an exported grid has. Co-authored-by: xiaou61 <113765024+xiaou61@users.noreply.github.com> --- crates/format/src/fig/export.rs | 19 ++++++++++++------- crates/format/src/fig/export_tests.rs | 2 +- docs/file-format-parity.md | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/format/src/fig/export.rs b/crates/format/src/fig/export.rs index 190f4055..2d02539e 100644 --- a/crates/format/src/fig/export.rs +++ b/crates/format/src/fig/export.rs @@ -1436,20 +1436,25 @@ impl<'d> Exporter<'d> { f.extend([("gridColumnGap", fl(l.gap)), ("gridRowGap", fl(l.cross_gap)), ("gridColumns", map(&cols)), ("gridRows", map(&rows))]); // Sizing is keyed by track id like the order map. A track the document doesn't set is // left out, and one the format can't hold keeps its id (the import equalises it). - let sizing = |tracks: &[GridTrack], ids: &[u32]| -> Option> { + let mut dropped = false; + let sizing = |tracks: &[GridTrack], ids: &[u32], dropped: &mut bool| -> Option> { let mut entries = Vec::new(); for (i, id) in ids.iter().enumerate() { let Some(track) = tracks.get(i) else { continue }; - if let Some((kind, value)) = grid_track_size(track) { - let size = || o(vec![("type", e(kind)), ("value", fl(value))]); - entries.push(o(vec![("id", guid(AUX, *id)), ("trackSize", o(vec![("minSizing", size()), ("maxSizing", size())]))])); + match grid_track_size(track) { + Some((kind, value)) => { + let size = || o(vec![("type", e(kind)), ("value", fl(value))]); + entries.push(o(vec![("id", guid(AUX, *id)), ("trackSize", o(vec![("minSizing", size()), ("maxSizing", size())]))])); + } + None => *dropped = true, } } (!entries.is_empty()).then(|| o(vec![("entries", arr(entries))])) }; - let (column_sizing, row_sizing) = (sizing(&l.column_tracks, &cols), sizing(&l.row_tracks, &rows)); - if l.column_tracks.iter().chain(&l.row_tracks).any(|t| grid_track_size(t).is_none()) { - self.warn("grid tracks of fixed or content size were written as equal tracks"); + let column_sizing = sizing(&l.column_tracks, &cols, &mut dropped); + let row_sizing = sizing(&l.row_tracks, &rows, &mut dropped); + if dropped { + self.warn("grid tracks whose size the format can't hold were written as equal tracks"); } for (name, sizing) in [("gridColumnsSizing", column_sizing), ("gridRowsSizing", row_sizing)] { if let Some(v) = sizing { diff --git a/crates/format/src/fig/export_tests.rs b/crates/format/src/fig/export_tests.rs index adb7be6d..cc47371f 100644 --- a/crates/format/src/fig/export_tests.rs +++ b/crates/format/src/fig/export_tests.rs @@ -1055,7 +1055,7 @@ fn grid_track_sizes_round_trip() { l.column_tracks[0] = GridTrack::fixed(-5.0); } let (bytes, warnings) = export(&odd, None).unwrap(); - assert_eq!(warnings, vec!["grid tracks of fixed or content size were written as equal tracks".to_string()]); + assert_eq!(warnings, vec!["grid tracks whose size the format can't hold were written as equal tracks".to_string()]); let (back, _) = import(&bytes, "File").unwrap(); let l = back.node(back.pages[0]).and_then(|p| p.children.first()).and_then(|c| back.node(*c)).and_then(|n| n.layout.clone()).unwrap(); assert_eq!(l.column_tracks[0], GridTrack::fill(1.0), "an unwritable track reads back equal"); diff --git a/docs/file-format-parity.md b/docs/file-format-parity.md index 6b020d72..a745d8c3 100644 --- a/docs/file-format-parity.md +++ b/docs/file-format-parity.md @@ -165,7 +165,7 @@ each. | Date | Change | Summary | |---|---|---| -| 2026-10-20 | minor | `.fig` export writes grid track sizes (`gridColumnsSizing` / `gridRowsSizing`): fixed px, fractional fill and content-sized tracks keep their sizes and come back through our own importer (#465). Tracks whose sizing the format has no name for (a negative or out-of-range size, a constrained min/max) keep their id and read back equal, with the existing warning. Numbers unchanged | +| 2026-10-20 | minor | `.fig` export writes grid track sizes (`gridColumnsSizing` / `gridRowsSizing`): fixed px, fractional fill and content-sized tracks keep their sizes and come back through our own importer (#465). Tracks whose size the format can't hold (a negative or out-of-range value) keep their id and read back equal, with a warning. Numbers unchanged | | 2026-10-18 | minor | REST file JSON import keeps `SLOT` layers (#792): they were dropped with everything placed in them, which emptied every instance's slot. A slot in a main component becomes a slot frame bound to its `SLOT` property (the one it references, else the one named like it, else the only one left), and an instance's slot holds the instance's own content, kept across syncs, moves of the mains and saving, unless the instance's property value is the main's default content (without values, a slot that holds layers keeps them). Instances in slot content link to their own mains. Slot content limits, preferred-only and preferred components are warnings. Synthetic files only. Numbers unchanged | | 2026-10-17 | minor | REST file JSON import reads grid track sizes from the CSS track strings `gridColumnsSizing` / `gridRowsSizing` (fixed px, fill fr, hug `auto`, `minmax(0, 1fr)`, `repeat()`; other tracks stay equal) (#465). Numbers unchanged | | 2026-10-17 | minor | The community batches #727 and #728 in the `.fig` row: instance identity and cached library components, overrides by key, text style precedence, grid track sizes, slot content, labels of connectors saved with their path, skip ink, paragraph options, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments (also REST), images in the message; budgets from the file's size; the journal keeps no file bytes; the import report. Not read cut to what's left. A `.fig` copy from Save as. Pasted content: layers copied from another design app, best effort (tested only with clipboard HTML made from our own export), and images under Wayland (not run there). All on synthetic files: summary and hours unchanged |