serde_json writes NaN and infinity as null, which doesn't read back as a number, so one NaN
anywhere in the model (a rotation, position, crop, stroke width…) made the saved file (and its
AutoRecover copy) fail to open as "damaged".
- Writer: presentation.json goes through finite::Finite, a serializer adapter that writes
non-finite floats as 0.
- Loader: files already written that way open; each null where a number belongs reads as 0
(inside tagged enums, where serde only reports the enclosing object, candidates are tried
nearest first and kept only when the parse gets further). Other damage still fails.
- repair() brings shape geometry into range: positions and sizes finite and within ±1e6 pt,
rotation 0–360, picture crop -10..1, font sizes 1–4000 pt, line widths 0–1584 pt.
Animation timings are u32 and can't carry NaN.
Co-authored-by: Cursor <cursoragent@cursor.com>