Commit Graph
4196 Commits
Author SHA1 Message Date
Trevor BrownandClaude Sonnet 5.5 74bc74e4f6 Merge upstream/main into feat/tidy-up-figma-rule
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-13 10:38:49 -05:00
BFlat bdf14ae7f0 Merge pull request #2286 from TrevorBrown-dev/fix/icon-buttons-16pt-2238
M8: every 24 pt icon button paints its glyph at 16 pt (#2238)
2026-11-13 22:36:15 +07:00
BFlat 6fb76233ec Merge pull request #2087 from TrevorBrown-dev/feat/variables-duplicate-group-2084
Variables: Duplicate group copies a group's variables as "<name> 2" in one undo step
2026-11-13 22:27:09 +07:00
BFlat b98f3dd39f Merge pull request #2241 from TrevorBrown-dev/fix/mirror-sharp-point-2110
M4: Vector edit: a mirror mode turns a sharp point smooth (#2110)
2026-11-13 22:24:58 +07:00
Dylan M. Taylor b5b4fb8c28 Merge pull request #2340 from storytold/ci/sonar-pinned-test-data
ci: provision pinned data for Sonar coverage
2026-11-13 09:09:58 -05:00
Dylan M. Taylor 4084642c45 ci: exclude pinned fonts from Sonar scan 2026-11-13 13:24:26 +00:00
Dylan M. Taylor eb660e776e ci: use shared instrumented test failure gate 2026-11-13 13:03:44 +00:00
Dylan M. Taylor af26ce1a6b ci: provide pinned test data to Sonar coverage 2026-11-13 12:17:21 +00:00
FredApps 011f08e588 Merge pull request #2259 from dotCooCoo/chore/craft-update-6e03f65
Updates: take craft-update from craft-libs main at e98299546
2026-11-13 10:28:21 +01:00
Trevor BrownandClaude Sonnet 5.5 fb9df220b1 M6: .fig export writes a page's prototype background (#2326) (#2328)
* M6: .fig export writes a page's prototype background (#2326)

The export writes a page's prototype background as prototypeBackgroundColor (field 141, a plain colour) when it differs from the default, as Figma does for a page whose background was set. The device-settings warning now fires only for the device, rotation, colour, custom size and responsive settings. Measured on a synthetic file saved from Figma's web app.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 measured and wrote at medium

* M6: the .fig export writes a prototype background opaque and warns for its opacity, and its tests read the written record (#2326)

The colour is written with alpha 1, as the measured record has it and as the import reads it, and a background that isn't opaque warns that its opacity was left out. The tests now read the written record, and each device setting alone is checked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 reviewed; Sonnet 5.5 wrote at medium

* M6: the .fig export writes every page's prototype background, so Figma shows the same colour (#2326)

A page at FigCraft's default background used to write no field, so Figma showed its own default instead. Every page now writes its colour, opaque. The test for a default page reads the written record and the import.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 09:34:55 +01:00
Trevor BrownandClaude Opus 5.5 1a68ded581 M8: Tidy up keeps the second layer after a narrow first one when the gap is negative
raise_negative_gap raised a negative gap from the interior widths only, so a first layer narrower than the gap's size let the second start before it (widths 10, 100 x5 at X 0, 80, 200, 260, 400, 460: the modal -40 gave X 0, -30, 30, ...). The second must start after the first, g > -w(0), in whole pixels floor(-w(0)) + 1. Figma kept the order in every measured run; a narrow first layer was not measured, so this bound keeps the order rather than follow a reading. Test: tidy_tests::a_narrow_first_layer_still_keeps_the_second_after_it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-13 01:43:34 -05:00
Trevor BrownandClaude Opus 5.5 fc90078333 Docs: Tidy up keeps the first layer and the other axis for a row or a column, not a grid
The row's opening said the first layer and the other axis are always kept, but a grid centres each layer in its column and can move it on both axes. The claim is now limited to rows and columns; the order and sizes are kept for all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-13 00:36:40 -05:00
Trevor BrownandClaude Sonnet 5.5 a7a9e62721 M8: Tidy up takes a row whose layers overlap in a chain, and raises a negative gap so no layer reaches the one two before it, as in Figma
Measured in Figma's web app on 2026-11-13. Co-Authored-By below.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 measured and wrote at medium
2026-11-13 00:00:02 -05:00
Trevor BrownandClaude Sonnet 5.5 3cf7385f3d Docs: keep one Vector edit mode row (main's, plus this PR's mirror clause)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 22:07:04 -05:00
Trevor BrownandClaude Opus 5.5 7d94fa78e9 Merge upstream/main into fix/mirror-sharp-point-2110
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 22:06:21 -05:00
Trevor BrownandClaude Sonnet 5.5 7057fbf3a5 Merge upstream/main into feat/variables-duplicate-group-2084
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 20:41:57 -05:00
Trevor BrownandClaude Opus 5.5 a0a0570d64 Merge upstream/main into feat/tidy-up-figma-rule
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 20:36:50 -05:00
dotCooCoo f53f3ee53e Merge main into chore/craft-update-6e03f65 2026-11-12 16:55:18 -08:00
NetterSilverandClaude Opus 5.5 7b34dbab00 Keep library links working when the project folder is moved or cloned (#2132)
Fixes #2122

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 01:48:30 +01:00
Trevor BrownandClaude Sonnet 5.5 39acc36438 Docs: the interactions measured against Figma on 2026-11-08, case by case (#2111)
* Docs: the interactions measured against Figma on 2026-11-08, case by case

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at low

* Docs: the measured values for snapping, menus and Escape, and what FigCraft did before each fix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote

* Docs: the safe triangle unresolved, the Hug notice fixed in #2099, the variant warning not added (#2111 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote

* Docs: partly fixed defined, the test row a match, open questions without repeats, ui-parity's line reworded (#2111 review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:47:38 +01:00
Trevor BrownandClaude Sonnet 5.5 a4e330774b M6: .fig export writes list spacing, hanging punctuation and lists, and vertical trim (#2305) (#2306)
* M6: .fig export writes list spacing, hanging punctuation and lists, and vertical trim (#2305)

The export wrote none of a text's list spacing, hanging punctuation, hanging lists or vertical trim, although the import reads all four. It now writes listSpacing, hangingPunctuation, hangingList and leadingTrim, each only when it differs from its default (as Figma saves them), and an instance override back to the default clears the field. The warning narrows to OpenType features, which the import does not read from a .fig.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 briefed; Sonnet 5.5 wrote at medium

* M6: test that an instance override back to the default clears it (#2305)

Adds a round-trip test for the unset entries: an instance whose text goes back to the defaults must not take the main's list spacing, hanging flags and vertical trim again. Also scopes the comment and the docs row to a design file: a text at the defaults carries none of the four there, and a text style holds none of them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 briefed; Sonnet 5.5 wrote at medium

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:46:25 +01:00
Trevor BrownandClaude Sonnet 5.5 a60cb1a6fa M7: the Design panel shows the page's variable modes with nothing selected (#2215) (#2312)
Pages take a variable mode, set from the Page section of the right sidebar once a local collection has more than one mode. With nothing selected the Page section now shows the same Variable modes section the frames have: a row per multi-mode collection, Auto or one of its modes, one undo step per pick (variable.setMode without layers sets the page's mode). The frame's row drawing is shared, not copied.


Model-Effort: Opus 5.5 drove; Fable 5.2 queued; Opus 5.5 briefed; Sonnet 5.5 measured and wrote at medium

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:45:48 +01:00
Trevor BrownandClaude Sonnet 5.5 8ffa1bfbd1 Docs: one Vector edit mode row after the merges (#2287)
Three parallel merges (#2121, #2195, #1875) each left a copy of the Vector edit mode row with its own additions. They are folded into one row: the Point section Mixed X and Y, Flatten baking a point corner radius, and vector edit Copy, Cut and Paste, in the order of the shared text. Nothing dropped.


Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:45:10 +01:00
Trevor BrownandClaude Sonnet 5.5 3d22c606a7 M10: Tailwind mobile merge keeps a max-width both frames share (#2272) (#2296)
inspect.code {language: "tailwind", mobile} dropped every mobile max-width and put the desktop one behind lg:, so a fixed-size layer that is the same size on both frames (a 48 x 48 badge) stretched to the full width below 1024 px. A mobile max-width that equals the desktop one is now kept as a plain class, with no lg: copy. A differing max-width still goes fluid on mobile with the desktop cap behind lg:.

Tests: the issue's two frames (a 1440 and a 390 page, each with a 48 x 48 badge) keep max-w-[48px] unprefixed, and a page-wide banner still gets its lg: cap only. The slashed-names test gave the CTAs frame the same 342 px on both frames, which the new rule rightly keeps, so its desktop frame is now 600 px wide. docs/mcp.md notes the rule.


Model-Effort: Opus 5.5 drove; Fable 5.2 queued; Opus 5.5 briefed; Sonnet 5.5 wrote at medium

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:44:17 +01:00
Trevor BrownandClaude Sonnet 5.5 2408d0aa81 M8: Tab while renaming moves only the rename and wraps from the last layer, as in Figma (#2311)
* M8: Tab while renaming the last layer wraps to the first, as in Figma

Measured in Figma's web app on 2026-11-12: Tab on the last row of the Layers panel commits the name and opens the first row's rename. FigCraft ended the rename there. Shift+Tab on the first row is unmeasured and unchanged, and Tab still moves the selection with the rename.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 queued; Sonnet 5.5 measured and wrote at medium

* M8: Tab while renaming moves the rename, not the selection, as in Figma

Measured in Figma's web app (second run, 2026-11-12): while renaming, Tab and Shift+Tab open the next or previous row in edit, and the selection stays on the layer first renamed. FigCraft moved the selection to the renamed row. Tab now moves only the rename field.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 queued; Sonnet 5.5 measured and wrote at medium

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-13 01:43:49 +01:00
NetterSilverandClaude Haiku 5.5 832e790ca9 Accept compact SVG arc flags in svg_path size check (#2299)
Found while testing crates/geom/src/lib.rs:57-87 (number_past, called from svg_path at :49)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-13 01:43:25 +01:00
NetterSilverandClaude Haiku 5.5 04a6c76a9b Return an error for non-numeric miterLimit, cornerSmoothing and paragraph props (#2303)
Found while testing crates/engine/src/cmd/props.rs:660 (also :710, :984, :985)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-13 01:42:52 +01:00
NetterSilverandClaude Haiku 5.5 c47de279a7 Keep huge finite values finite in round_to (#2309)
Found while testing crates/geom/src/transform.rs:58 (round_to), used by crates/engine/src/automation/inspect.rs:517 (round)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-13 01:42:15 +01:00
Trevor BrownandClaude Sonnet 5.5 0262539bd0 Merge branch 'fix/rustc-1-100-lints' (conflict: kept main's #2316 form of the same lint fix)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 19:39:25 -05:00
Trevor BrownandClaude Opus 5.5 477f004400 Merge upstream/main into fix/mirror-sharp-point-2110
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 19:39:01 -05:00
.CooCoo 40360c4a2a Fix main: clippy on Rust 1.100 stops at shortcuts.rs and the CLI script tests (#2316)
Rust 1.100 marks `ExitCode` as must-use and extends `needless_bool`,
so `cargo clippy --workspace --all-targets -- -D warnings` fails on main:

- `crates/ui-egui/src/shortcuts.rs`: the end of `key_event` returns the
  condition directly instead of `if ... { return true; } false`.
- `apps/figcraft-cli/src/files.rs`: three script tests now assert the exit
  code that `run_script` returns (`SUCCESS` for the two scripts that run,
  1 for the script whose step fails) instead of dropping it.
2026-11-13 01:38:53 +01:00
Trevor BrownandClaude Sonnet 5.5 d3f7ae9029 M8: Tidy up rounds the gap, tidies overlapping rows and grids along their longer axis, as measured
Third Figma run (web app, 2026-11-12): the mean gap rounds half up (X 0, 60, 135, 215 gives 0, 72, 144, 216; 0, 60, 135 gives 0, 68, 136); a tie between repeated gaps takes the smaller; a row is one row group of layers and may overlap along X, a negative gap counting in the mean (X 0, 60, 105, 170 gives 0, 57, 114, 171); a grid is packed along the axis with more groups (2 x 3 by centred columns, 3 x 2 by centred rows); a diagonal, an irregular layout and layers that all overlap are unavailable. The earlier cell rule is removed.

Choices, not measured: a 2 x 2 grid as columns, auto layout flow children unavailable, the 2,000 layer cap, rotated layers by their box, spacing with two layers, no rounding with Snap to pixel grid off. Grid positions along a stack can differ by a pixel from Figma's (it read 52 and 51 for the same mean 51.67).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 briefed; Sonnet 5.5 measured and wrote at medium
2026-11-12 16:32:48 -05:00
Trevor BrownandClaude Sonnet 5.5 cfd6bf5273 M8: Tidy up keeps the order and sizes and evens the gaps, as measured in Figma
Measured in Figma's web app on 2026-11-12 (top-level rectangles): a row, a column and a 2 x 3 grid of unequal sizes, eight runs on the gap, and the cases where Tidy up is greyed out (fewer than three layers, overlapping layers). Figma keeps the order, the sizes and the first layer, evens one gap per axis (the most common existing gap, else the mean taken down to a whole number) and tidies a grid by columns.

Choices, not measured: the smaller of two equally common gaps, a mean ending in .5 going down, gaps a pixel apart differing, a diagonal being a row, a grid with an empty place, a grid always read by columns, one overlapping pair making it unavailable, auto layout flow children making it unavailable, the 2,000 layer cap, rotated layers by their box, spacing with two layers, and no rounding with Snap to pixel grid off. A selection that is none of a row, a column or a grid keeps the earlier rule unchanged.

The Design panel's Tidy up row is greyed out with a tip when unavailable; the command then moves nothing and adds no undo step.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 briefed; Sonnet 5.5 measured and wrote at medium
2026-11-12 16:18:06 -05:00
Trevor BrownandClaude Sonnet 5.5 b72112ff78 Clippy 1.100: fix the new needless_bool and unused ExitCode lints
CI now installs rustc/clippy 1.100.0, which flags code nobody changed. No behaviour changes.

- clippy::needless_bool: crates/ui-egui/src/shortcuts.rs (end of the arrow-key handler), the `if ... { return true; } false` tail becomes the boolean expression itself.
- unused_must_use (`test::ExitCode`): apps/figcraft-cli/src/files.rs, three tests that call `run_script(...).unwrap()` and only check the output file; the result is now bound with `let _ =`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 14:42:55 -05:00
dotCooCoo 976e57a205 Updates: take craft-update from craft-libs main at e98299546
craft-libs e98299546 includes #923: the check of whether the copy can install itself now runs on the update-check thread instead of in Updater::tick on the UI thread.
2026-11-12 11:01:03 -08:00
dotCooCoo f31fc83f26 Merge main into chore/craft-update-6e03f65 2026-11-12 10:53:41 -08:00
Trevor BrownandClaude Sonnet 5.5 3d781caa92 Merge remote-tracking branch 'upstream/main' into feat/variables-duplicate-group-2084
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 05:00:10 -05:00
Trevor BrownandClaude Sonnet 5.5 70552482c4 M8: the popover's close glyph is 16 pt too, and the style picker's 24 pt check allows rounding (#2238)
The popover's 24 pt close button painted its cross through rect.shrink(6.0), a 12 pt glyph; it now goes through icon_glyph_rect like the other 24 pt buttons, with a painted-size test that failed before the change. The style picker's menu_icon picked the 16 pt glyph with an exact size equality, so a button at a fractional x that measured a hair off 24 in f32 painted at 14 pt; it now allows 0.5 pt of rounding.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Opus 5.5 briefed; Sonnet 5.5 wrote at medium
2026-11-12 02:51:37 -05:00
BFlat 0d6ce1124c Merge pull request #2163 from TrevorBrown-dev/fix/caret-colour-2156
M3: the text caret takes the text's fill colour (#2156)
2026-11-12 14:38:29 +07:00
bflatastic 5b20fac623 Merge main into #2163 2026-11-12 14:22:18 +07:00
BFlat 54e476b4bb Merge pull request #2200 from TrevorBrown-dev/feat/rotation-field-page-2196
M8: Rotation field shows and sets the rotation on the page (node.set absoluteRotation)
2026-11-12 14:19:47 +07:00
bflatastic 64e3684a11 Merge main into #2163 2026-11-12 14:05:23 +07:00
bflatastic 12b293efe5 Merge main into #2200 2026-11-12 14:04:50 +07:00
BFlat 5034dcef92 Merge pull request #2192 from TrevorBrown-dev/fix/eyedropper-magnified-pixels-2189
M3: the eyedropper picks a magnified image's pixel under the pointer (#2189)
2026-11-12 14:02:19 +07:00
bflatastic 47f5518155 Merge main into #2163 2026-11-12 13:49:58 +07:00
bflatastic 7a052d73a4 Merge main into #2192 2026-11-12 13:49:25 +07:00
bflatastic d3f14f5bd8 Merge main into #2200 2026-11-12 13:48:51 +07:00
BFlat 8e65cab3b4 Merge pull request #2191 from TrevorBrown-dev/fix/selection-colors-single-2188
M3: Selection colors shows for a layer with children from one colour (#2188)
2026-11-12 13:47:25 +07:00
bflatastic b9486affb9 Merge main into #2163 2026-11-12 13:30:33 +07:00
bflatastic d50203228d Merge main into #2191 2026-11-12 13:29:57 +07:00