Commit Graph
366 Commits
Author SHA1 Message Date
RicardoandClaude Opus 5.5 c19b7d0799 .fig export: a property name used with two types in a set writes both definitions
The set wrote one definition per name, so a variant whose property of
that name had another type named a definition the file didn't hold
(CodeRabbit review on #1152).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:00:22 -06:00
aiongg fc7a744519 CI: one test binary per crate, nextest, line tables only (#1208)
* Dev profile: line tables for workspace crates, no debug info for dependencies

* Tests: one integration-test binary per crate with several test files

* xtask ci: nextest, doctests, tests without examples, --keep-going; add xtask test
2026-10-23 13:01:12 -04:00
RicardoandClaude Opus 5.5 4c6c4d9667 .fig export: warn when a set's variants disagree on a property default
The format keeps one default per property of a set; the first variant's
is written (CodeRabbit review on #1152).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:46:12 -06:00
RicardoandClaude Opus 5.5 19d6c17b6d .fig export: grid track sizes
Grids wrote their tracks as equal ones, with a warning. Track sizes
(fixed px, hug, fill shares) are now written as gridColumnsSizing /
gridRowsSizing, the fields the import already reads.

Checked on a synthetic grid in the incumbent (fixed 320/140/112/34/34 px
columns): written as equal tracks it came in with five equal columns and
its layers overflowing their cells; now its columns keep their sizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:43:55 -06:00
RicardoandClaude Opus 5.5 2c044f6b80 .fig export: an override that clears a maximum size writes it as infinite
Like an unset axis, a cleared maximum was written as 0 and could
collapse the layer (CodeRabbit review on #1152).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:16:16 -06:00
RicardoandClaude Opus 5.5 70a080af5b .fig export: files Figma imports, with component set properties
Checked by importing synthetic files into the incumbent (Drafts > Import,
black-box) and against a "Save local copy" of a synthetic document.

- Images are named and referenced by the SHA-1 of their bytes. With
  SHA-256 names, any file with an image failed to import ("Internal error
  during import").
- Component sets hold their property definitions, variant properties
  included (stateGroupPropertyValueOrders); each variant has its own copy
  of each definition (parentPropDefId) and its variantPropSpecs, and its
  layers and instances name that copy. Definitions and values also travel
  as varValue; text values carry their lines; layers get componentPropRefs
  next to the parameter consumption map. Before, sets came in without
  variant properties and with their other properties unbound.
- An axis without a maximum size is written as infinite: 0 collapsed
  auto layout frames with only a maximum width.
- Compound weights are spelt the way font families name their faces
  ("SemiBold"; "Semi Bold" in Inter): text in Libre Franklin Semi Bold was
  a missing font there and wasn't drawn.
- The kiwi encoder writes infinite floats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:56:07 -06:00
Jindřich ŠaršonandClaude Opus 5.5 7cb4168e13 M6: Sketch import: fixes from the PR review
- --no-system-fonts holds for .sketch imports too: the CLI opts the
  process out, so the import's font matching doesn't scan.
- Shared text styles get the installed family and platform tracking
  their linked layers get.
- Each text run writes its letter spacing, line height and font axes,
  neutral when it has none, instead of taking the first run's.
- The unsupported-drop hint and its catalogs list .sketch.
- A test sends damaged .sketch data through file.open and file.import.
- Stack items are matched to their source layers through a map.
- Docs: .sketch is a convert input; the Carbon kit's Sketch 47 edition
  is refused (the 68 and 83 ones open).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:03:17 +02:00
Jindřich ŠaršonandClaude Opus 5.5 054d53900f M6: Sketch import: note embedded fonts as waiting for .figcraft font support
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 3e92904cba M6: Sketch import: review fixes
Texts an auto layout places keep the layout's position (the next layout
pass would take the baseline move back). A faded layer's group takes the
layer's id, so stacks, links and overrides still find it; faded masks
stay masks. figcraft-format no longer depends on figcraft-layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 40c7cb3029 M6: Sketch import: tint copies and handed overrides count as work
Tinting copies a group's or master's layers; the copy is charged to the
import's work budget (past it, tints are skipped with a note). An
override handed to a nested instance costs its value's size, so deep
chains of large overrides can't be copied without end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 79894cfad2 M6: Sketch import: junk tints and colour overrides can't panic
A tint or fill that isn't a JSON object is left alone instead of being
indexed. A structural fuzz replaces each value of a rich file with values
of other types and imports every variant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 de0650e3f7 M6: Sketch import: layout grids come in hidden
Sketch draws an artboard's layout and square grids only while its layout
view is on; they now import hidden (still there, shown with the eye in
the Layout grid section).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 a8bade1446 M6: Sketch import: alpha masks, Fade, linear gradient geometry
Alpha masks only mask (no drawn copy of their fill); a progressive
opacity (Fade) becomes an alpha mask of its gradient over the layer;
linear gradients run square in pixels as Sketch draws them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 f02557e8f9 M6: Sketch import: fonts by PostScript name, Sketch's natural line
The font database knows each face's PostScript name; the Sketch import
finds the installed face a file's font names (CourierNewPSMT is Courier
New Regular) and reads the family from the name only when it isn't
installed. Text without a fixed line height takes the line Sketch draws:
ascent and descent rounded to whole pixels, the line gap left out, a
fifth more for classic Mac fonts, the baseline where Sketch puts it
(measured on sketchtool renders of 80 fonts).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 41ff790d77 M6: Sketch import: variable font axes; no false embedded-font note
Sketch keys a font's variation axes by the tag read as a number; they map
onto the text style's variations (a fontVariations key in the REST
mapping). Font references without font data only name the fonts a
document uses and no longer raise the embedded-font note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 c639cc2b76 M6: Sketch import: Sketch 2025 frames, stacks, tints and corners
Groups as frames and graphics, stacks as auto layout (instances laid out
by their stack), corners and per-side borders from the style, pins and
sizing, tints (layeringType, fillColor overrides), text colour and
visibility overrides, blur lists (progressive, glass), inner shadows in
the shadow list, angular gradients starting on the right, text sizing,
and texts an override changed measured again on open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 ad78ee94c3 M6: Sketch import: instances, artboards and Apple fonts like Sketch
Compared with Sketch's renders of a Sketch 64 file. Resized instances lay
out their layers the way Sketch does (measured: pins share the change in
proportion, a fixed size keeps its centre's share, and files before format
110 just scale); masters' backgrounds show in instances only when Sketch
includes them; swaps to a missing symbol keep the original; artboards
don't draw a style they carry. Apple's system fonts are found under the
names designs use (SF Pro, SF UI Text and Display, SF Compact), the
optical size follows the text size for named styles too, and imported
text keeps the platform's trak tracking as letter spacing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 5b9e18317d M6: Sketch import: match Sketch's own rendering
Compared artboard by artboard with Sketch's renders of a real Sketch 50
file (sketchtool, black-box). Shape groups whose children have no
operation are one compound path filled by the group's winding rule (read
from the layer), not a union: rings, map streets and icons keep their
holes. Filled open paths close, as Sketch fills them. Flips apply after
rotation. Text with a fixed line height sits where Sketch's line spacing
behaviour puts it. Edited ovals and rectangles keep their points. Bitmap
colour adjustments become image filters. scale 0 means unscaled. A local
test compares sample files with sketchtool's renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 6640498195 M6: Sketch import: hostile files can't panic, run out of memory or hang
From the branch review: overrides that point at junk (shared styles that
aren't objects, text runs that aren't) no longer panic; symbol expansion
spends a work budget (JSON copied, overrides handed on to nested
instances, grouped once by their first path segment), so small files
can't expand into gigabytes; far path points stay in range; notes are
capped and de-duplicated with a set; shared styles are looked up by
index; a styled second mask's drawn copy isn't clipped by the first;
JSON members are refused past 100 times their packed size or 2 GB in
all, and an unreadable document.json keeps the pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 31698164ac M6: Sketch import: prototype links, export settings, layout grids
Layers linked to artboards navigate there on click (back links go back,
slide animations slide), hotspots become transparent frames with their
link, flow-home artboards start a flow; export options become export
settings (scale, width or height, suffix); artboard layout and square
grids become layout grids, which instances show like their masters.
Smart Layout, stacks and locked overrides are noted once per file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 4be7017ed1 M6: Sketch import: shared styles and colour variables
Shared layer styles become a paint style for their fills, a paint style
for their border colours (linked to strokes) and an effect style; shared
text styles become text styles (colour stays on the layers); local and
library copies alike, unused ones listed too. Layers link to them, keeping
their own values where they differ. Color Variables (swatches, also
library ones) become colour variables in a Color Variables collection,
with fills, borders and text colours bound to them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 4437d49a72 M6: Sketch import: symbols as components, instances with overrides
Symbol masters become components, also those copied from libraries (on a
page of their own); instances carry their master's layers with their
overrides applied (text, nested symbol swaps and hiding, images, layer
and text styles, nested paths) and resize them by their constraints, so
editing a master still updates every instance. A symbol inside itself
stops with a note; instances of missing symbols are empty frames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 60cc184ab7 M6: Sketch import: PingFang families keep their own names
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 a0542e581c M6: Sketch import: text with styled runs and font names
Text layers keep their string and styled runs (font, size, colour, line
height, letter spacing, decorations, text transform; ranges in UTF-16
units, clamped), paragraph alignment and spacing, vertical alignment and
how the box sizes. Fonts named by PostScript name become family and
style; the engine then matches families to installed ones ignoring
spaces (PingFangSC → PingFang SC). Fonts embedded in newer files are
named in the import report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 ff9d966ea6 M6: Sketch import: masks that show their own style draw it
Sketch draws a clipping mask with its fills, borders and effects under
what it clips; a FigCraft mask only clips. A plain copy of such a mask
goes underneath it, so a light avatar circle with a white icon in it no
longer vanishes on a white artboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 8f32e4f504 M6: Sketch import: shape groups that are clipping masks clip
Sketch 51 and older files make masks from shape groups; the mask flag was
lost when the group became its shape or boolean operation, so a masked map
spilled out of its card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 d77d57912b M6: Sketch import: fills, borders, effects, opacity, masks
Solid, gradient (linear, radial with its ellipse, angular) and image
pattern fills (tile, fill, stretch, fit); borders as strokes with the
first enabled border's width and position, dashes, caps, joins and arrow
ends; shadows, inner shadows, layer and background blurs; layer and paint
opacity and blend modes; clipping masks, wrapped in a group of their own
where Sketch breaks the mask chain; Display P3 documents. Shape groups
pass their style to the shape or boolean operation they become. Noise
fills, motion and zoom blurs are listed in the import report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 72c7eae868 M6: Sketch import: editable paths, shape groups, booleans, stars, polygons
Paths become editable vector networks (handles, per-point corner radius,
open and closed, winding rule). A shape group with one shape becomes that
shape in the group's place; several combine as boolean operations, mixed
operations nesting from the left as Sketch applies them. Stars and
polygons stay stars and polygons while their points are the generated
ones, edited ones and triangles become vectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 5805cea9b4 M6: Sketch import: layer tree, transforms, constraints, basic shapes
Artboards (and Sketch 2025 frames) become frames with their background
(white by default, as Sketch shows them), groups and graphics groups,
rectangles with their corner radii, ovals ellipses, bitmaps rectangles
with the image, slices slices. Positions accumulate through groups,
rotation and flips keep their direction, resizingConstraint bits become
constraints. Not yet mapped layer kinds are listed in the import report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Jindřich ŠaršonandClaude Opus 5.5 a30f846ee3 M6: open Sketch files (48+): detection, pages, version cut, wiring
.sketch archives (document.json, meta.json, pages/*.json) open through
file.open, file.import, drag and drop, the CLI and MCP; files saved by a
Sketch older than 48 are refused with a message saying how to convert
them. Layers come in the next commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:20 +02:00
Ridzzz0Alam f0e75e183b M1: swap defaults past the layer cap say so (#981)
Instance swap defaults that name an in-file main skipped by the layer cap
were reported as "isn't in the file": only instance targets fed the
capped-mains list. Defaults now feed it too.
2026-10-21 21:06:59 +02:00
Ridzzz0Alam 09ff20ffd6 M1: several exported layers arrive as one zip in the browser (#969)
Cause: in the browser, exporting several layers started one download
per file. Chrome holds back every download after the first until the
site is allowed automatic downloads, so the other files didn't arrive.

Fix: file.exportLayers takes `zip: true` (without `dir`) and returns the
files together as one base64 zip `data`, written by a new
figcraft_format::zip_files (the workspace's existing zip dependency).
The UI asks for it when the platform saves by downloads and hands over
one `<document name>.zip`.

Test: ui-egui several_layers_download_as_one_zip (one download, a zip
holding both layers). It fails on the previous commit (two downloads).
2026-10-21 20:35:13 +02:00
Ridzzz0Alam e211bf5476 M1: large .fig files keep their layers and their library links (#981)
A design-system .fig file (54 pages) stopped at 244 248 layers, the rest
of its pages skipped, and most of its instances came in as frames "of
components that aren't in the file", with the cap note shown twice.

- The layer budget follows the compressed canvas at 8 bytes a layer, not
  32: design systems are mostly instances, which pack tightly. Files under
  about 1.6 MB keep the 200 000 floor and the 2 million ceiling stays
  (measured on synthetic files: about 5.4 KB and 19 µs a layer).
- The library copies the pages use (the file's hidden internal canvas) are
  mapped before the pages: at the cap the pages' last layers are skipped,
  not the mains their instances link to. The REST stage keeps building
  pages first (its ids are first-wins) and allows for the wrapper frame of
  each library copy, the one layer the mapper doesn't count; that layer
  tripped the REST stage's own cap and repeated the note.
- Instances and swap defaults whose main is in the file but past the cap
  say so instead of "aren't in the file" (en and es catalogs).
- The .fig import gives a note the mapper already gave only once.

Tests in fig/layer_cap_tests.rs: the budget for a #981-sized canvas, library
mains ahead of the pages at the cap, the note for a capped local main, and
a whole 210 000-layer file with one cap note and its library links kept.
All four fail on the previous code.
2026-10-21 18:22:16 +02:00
NetterSilverandClaude Haiku 5.5 503b6d606d Refuse to save documents containing NaN or infinite numbers
Found while testing crates/format/src/lib.rs:256 (to_json/write_json/save_bytes) and :323 (parse_json)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 01:27:12 +02:00
Hamza f3e5d2abc4 .fig import: a note says how much of the file it covers
A .fig import's notes were deduplicated and left uncounted, so each kind
was named once with no sign of how much of the file it applied to: a
file whose every text layer lost a setting read exactly like one where a
single layer did. That is what made the loss in #467 invisible -- the
reporter saw one line while three kinds of data went, one of them on all
83 text layers.

The mapper now collects its notes through the same `Warnings` the REST
importer already uses, which keeps the first-seen order and the cap and
appends `(×n)` where a note was met more than once. Nothing else
changes: the note text stays the catalog key it was, and the import
report already reads a `(×n)` suffix off before translating the base
(`ui-egui`'s `note_text`), so a counted note is still translated.
`figcraft-cli info` prints these notes, so it now names each kind with
its count.

With the count dropped again, the test reports the two notes plain:

    ["connector layers import as static frame/vector snapshots ...",
     "connector label was skipped: depth limit prevents nested ..."]

Part of #467.
2026-10-21 00:06:57 +05:00
NetterSilverandClaude Haiku 5.5 b788b55286 Keep a solid paint's own opacity when binding a colour variable
Fixes #923

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:43:51 +02:00
xiaou61 beb87d8696 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>
2026-10-20 11:14:02 +08:00
xiaou61 414aa1b63c M1: write grid track sizes in .fig export (#465)
The import has read `gridColumnsSizing` / `gridRowsSizing` since #630, but the
export wrote only equal tracks and warned about it, so a grid with a fixed
column beside two weighted ones came back equal after a save and reload.

Write the sizing map next to the order map, keyed by the same track ids: fixed
px, fractional fill and content-sized tracks keep their sizes. A track the
format has no name for (negative or out-of-range size, or a document track the
frame doesn't list) keeps its id and reads back equal, with the existing
warning.

Co-authored-by: xiaou61 <113765024+xiaou61@users.noreply.github.com>
2026-10-20 11:04:13 +08:00
MortenHustedandClaude Opus 5.5 017517fbb9 #847: an instance's values are kept under the original definition
An assignment and the layers that read it can name different copies of
one definition (the set's own id on one side, a variant's copy on the
other). The values were kept under the id the assignment names and read
under the id the layer names, so such a value never reached its layer.
Both sides now go through prop_root, the definition the copies were
made from (CodeRabbit on #847). In Untitled UI, 143 assignments name a
copy that no layer reads while another copy of it is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:53:04 +02:00
MortenHustedandClaude Opus 5.5 c530b4067d M3: .fig imports read property values written only as variables
A component property assignment carries its value in `value`, and in
current files also in the variable form `varValue`. Many assignments
leave `value` empty and say it only in `varValue` (booleans as
boolValue, instance swaps as symbolIdValue, text as textDataValue), and
those were ignored: a hidden layer showed and a swapped icon stayed the
default. In Untitled UI that is 1,643 booleans and 185 swaps, so inputs
showed their hint text, help icon and leading icon on top of the
neighbouring layers. Both forms are read now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:53:04 +02:00
MortenHustedandClaude Opus 5.5 4a54aa733f M3: .fig imports keep the properties variant layers are bound to
Each variant of a component set keeps a copy of the set's property
definitions (an id and a parentPropDefId, no name or type), and the
variant's layers and its instances' assignments use the copy's id. Those
copies were skipped, so imported variants lost every boolean, text and
instance swap binding, and instances lost their property values. In
Untitled UI (8,074 copies) an input's "Hint text" no longer followed its
property. A copy now names the definition it was made from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:53:03 +02:00
Filip Strajnar eae9546878 M6: export as Android Vector Drawable (XML)
ExportFormat::VectorDrawable ("XML", .xml) and ExportSetting.vd_compat
(bestEffort / exact / ignore, stored only when not the default) wire the
figcraft-vectordrawable writer into file.export, file.exportLayers and
export.add/update: a vdCompat param (junk refused), svgIdAttribute writes
android:name, and the results carry the writer's warnings (per file for
export layers). The Export section lists XML with an "Android Vector
Drawable" tooltip and its id attribute and compatibility options, File >
Export... gets an XML preset, and the toasts say when content was changed
or left out. CLI: --format xml / .xml outputs, --vd-compat, warnings as
note: lines. MCP: format xml returns the XML as text plus its warnings.
A .fig export writes XML settings as SVG with a note.

Docs: file-format-parity (row and revision), ui-parity and roadmap
revisions, architecture (the crate), cli and mcp.
2026-10-18 20:39:46 +02:00
BFlat ef535e49b0 M1: REST import keeps slot layers and instances' slot content (#792) (#796)
Fixes #792.

Fixes #792.
2026-10-18 15:52:30 +07:00
BFlat 8629a6f586 M3: variable.import reads variables outside meta and never imports nothing silently (#790) (#793)
Fixes #790.

Fixes #790.
2026-10-18 14:59:14 +07:00
skylar taylor-barrick 3f5f673ebc Merge pull request #777 from skylartaylor/document-link-schemes
Open only web links and local libraries from a document
2026-10-17 14:34:58 -06:00
skylar taylor-barrick c7acd1a81d chore: clear the rustc 1.96 clippy lints that fail xtask ci
Unrelated to the security changes in this branch; `main` already failed
`clippy --workspace --all-targets -- -D warnings` without them. Eight
clippy::nonminimal_bool sites across seven files, each taking clippy's own
suggested rewrite.

Every rewrite is pure boolean algebra with the negation kept outside, so the
float comparisons and their NaN behaviour are untouched: !A && !B becomes
!(A || B), and !opt.is_some_and(f) becomes opt.is_none_or(!f). Two sites needed
explicit parentheses the suggestion omitted, where the rewritten term is the left
operand of an && and || binds looser.

designcraft cleared the same lint in bc840130; this is figcraft's equivalent.
2026-10-17 13:35:44 -06:00
bflatastic c8b611a802 Merge fix/rest-grid-tracks into batch 15 2026-10-17 23:17:37 +07:00
bflatastic 68b19422ad M1: REST import reads grid track sizes (#465)
gridColumnsSizing and gridRowsSizing are CSS grid-template strings: px
tracks are fixed, fr tracks fill, auto/min-content/max-content hug,
minmax(_, Nfr) fills and repeat(n, ...) expands; other tracks stay equal
(1fr). Tracks past the axis's own count are left out. Files from .fig
keep reading the tracks from their source.
2026-10-17 22:44:09 +07:00
bflatastic 9a69f79861 #552: write and read space evenly as SPACE_EVENLY_CSS
The format's SPACE_EVENLY is its older name for equal gaps without edge
space, so files keep reading it as space between; true space evenly is
SPACE_EVENLY_CSS, which the export now writes.
2026-10-17 22:39:10 +07:00
bflatastic 6afcfc2641 Merge main into #552 2026-10-17 22:36:20 +07:00