Commit Graph
692 Commits
Author SHA1 Message Date
BFlat 3a1b698ed4 Merge pull request #2055 from aleksei-suholita/fix/effect-binding-checks
M3: a layer binds an effect's field only when its effect has it, and a bound glass light angle turns round
2026-11-07 09:53:26 +08:00
BFlat 5762212ced Merge pull request #2006 from JonRC/fix/jpeg-quality-srgb-icc
M6: JPG exports take a quality and embed the sRGB profile (#2004)
2026-11-07 09:47:15 +08:00
BFlat 6bbaa27f97 Merge pull request #2007 from JonRC/fix/stroke-cap-error-values
fix(engine): an unknown stroke cap lists the caps node.set takes (#2005)
2026-11-07 09:42:05 +08:00
bflatastic 468cab6b2b Merge main into #2055 2026-11-07 08:40:31 +07:00
BFlat a3460e4711 Merge pull request #1777 from Silvertree2010/fix/h4857-node-reorder-silently-turns-a-ne
Reject negative and fractional indexes in node.reorder
2026-11-07 09:34:48 +08:00
BFlat 97b16702d5 Merge pull request #1900 from Hamza0101/fix/text-layer-name-follows-characters
M4, #1747: a text layer's name follows the characters typed into it
2026-11-07 09:34:20 +08:00
bflatastic 642d847329 Merge main into #2055 2026-11-07 08:28:47 +07:00
BFlat 6864ffff4f Merge pull request #1778 from Silvertree2010/fix/h4878-node-create-vector-create-silent
Reject invalid entries in vector.create points instead of dropping them
2026-11-07 09:27:01 +08:00
BFlat 57496ae937 Merge pull request #1783 from Silvertree2010/fix/h5009-page-move-silently-turns-a-negat
Reject negative or fractional index in page.move
2026-11-07 09:21:59 +08:00
bflatastic 4e5f0f8c04 Merge main into #1900 2026-11-07 08:18:31 +07:00
BFlat 8f2f194cf4 Merge pull request #1787 from Silvertree2010/fix/h5159-variable-movemode-silently-turns
Reject negative and fractional index in variable.moveMode
2026-11-07 09:15:57 +08:00
bflatastic 7c3a3b2703 Merge main into #2055 2026-11-07 08:07:48 +07:00
Oleksii Sukholita d23cebb12f M3: an effects key naming no effect's field binds nothing (#2055)
Review follow-up: `effects/x/radius`, `effects/0` and `effects` name no
effect's field, but `list_binding` doesn't parse them, so the layer's
check took them for some other field and kept an inert binding. A key
under `effects` that names no effect's field is now refused as well.
2026-11-06 20:18:10 +01:00
Oleksii Sukholita 5bdedc3930 M3: a layer binds an effect's field only when its effect has it
`variable.bind` on a layer checked only the variable's type against the
field's scope, so `effects/0/refraction` on a drop shadow, or a binding on
an effect the layer doesn't have (`effects/9/radius`), was kept and did
nothing. An effect style already refuses both. The layer now uses the same
check: effect `i` must exist and have the field, and the field's type
comes from that effect. A binding an older file has on such a field still
does nothing, and still unbinds.
2026-11-06 19:38:52 +01:00
Denis GomesandClaude Opus 5.5 6a31ea7360 M3: Go to main component names a library copy with no link
A file imported from another app can hold library copies in its library
store with no record of the library they came from. On an instance of
one, the command took the local path and failed in selecting it with
"isn't on a page (it is a library component)", which says nothing about
what to do. It now says the copy has no link to its library and that
enabling the library reaches it.

Found on a real imported file, where copies of the same library set were
cached more than once and only one of them could be linked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:53:58 -05:00
Denis GomesandClaude Opus 5.5 76af4e3f43 M3: Go to main component opens the library file of a library component
On an instance of a library component the command refused with "open
that file to edit it". The help center describes it opening the file that
holds the main ("Edit main components"), so it now does: the linked
library file opens in a tab, or its open tab comes to the front, on the
main's page with the main selected and zoomed to.

The library keeps the file's layer ids, so the copy's id map
(ImportedComponent::ids) leads from the local copy back to the exact
layer: a nested instance reaches its own main and a variant that variant.

The new tab takes the canvas size of the tab it came from. A tab still at
the default 1200x800 is refitted to the whole page on its first frame,
which would replace the zoom to the main.

A main deleted from the library file since it was published leaves the
file open with a note. A moved library file, a team library on a server
and a path off this computer are refused before anything opens; the last
uses the same check that guards library reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:21:20 -05:00
8da11395e9 M3: glass effects take variables, and their frost stays within 0–100 (#1886)
* M3: glass effects take variables, and their frost stays within 0–100 (#1885)

Glass's light angle, intensity, refraction, depth, dispersion, frost and
splay bind to number variables, on a layer (`variable.bind`,
`effects/<i>/<field>`, `radius` being the frost) and in an effect style
(`style.bind`), and the Design panel's glass fields take a variable like
the shadow and blur fields. A variable reads as the field shows it:
intensity, refraction, dispersion and splay in percent.

A glass's frost stays within 0–100, the reference tool's range: a larger
frost from an older file draws at 100, and the field, a bound variable and
scaling stop there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M3: a frost from an older file scales and shows as it draws (#1885)

Review follow-up: a frost saved past 100 draws at 100, so scaling a layer
starts from that (150 halved draws at 50, not 75) and the Design panel's
Frost field shows it. The revision row's binding path escapes its pipes, so
the table keeps three columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M3: Dev Mode and .fig export show a frost from an older file as it draws (#1885)

A frost saved before the 0–100 range draws at 100, and the canvas, CSS and
scaling already read it that way, but Dev Mode's Glass row and the .fig
export still read the stored number, so they said 150 where the canvas
shows 100. Both now read the sanitized frost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Oleksii Sukholita <oleksii.sukholita@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 22:08:15 +01:00
JonRC a55a728c4c M6: JPG exports take a quality and embed the sRGB profile (#2004)
file.export always encoded JPG at quality 90, and an sRGB document's JPGs
carried no colour profile, so viewers could only assume sRGB. PNG exports
always get an sRGB chunk, and Display P3 JPGs already embedded their ICC
profile.

- ExportSetting gets a quality (1 to 100, default 90, saved only when it
  isn't 90), parsed with the other export options: file.export, export.add,
  export.update, file.exportLayers and MCP export take it.
- encode_export_with takes the quality; encode_export keeps quality 90.
  Every JPG now embeds the document's profile, sRGB included.
2026-11-05 00:09:19 -03:00
JonRC 5244b0075d fix(engine): an unknown stroke cap lists the caps node.set takes (#2005)
strokeCap, startCap and endCap answered only "unknown stroke cap". They now
name the key and every cap, as strokeAlign, strokeJoin and dashCap do.
2026-11-04 23:53:43 -03:00
Dylan M. Taylor ee4edc7364 perf(engine): replace linear scans that made document-sized inputs quadratic (#1921)
* perf(engine): replace linear scans that made document-sized inputs quadratic

enum_of re-classified the whole string for every character; widget names,
comment mentions and devcompare's common siblings each searched a growing
Vec; data.fill's export names and codegen's class names retried every
suffix from 2 against a list, cubic in the number of copies of one name;
and a batch rename template had no length cap although it is expanded
again for every match.

* address review: report the pattern in its own length error
2026-11-03 23:48:20 -05:00
Hamza 58af1a6237 M4, #1747: a text layer's name follows the characters typed into it 2026-11-03 12:41:29 +05:00
0e86f3597a M4: wrap style on a text layer: Auto, Balance and Pretty decide where its lines break (#1827) (#1871)
* M4: wrap style on a text layer: Auto, Balance and Pretty decide where its lines break (#1827)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Docs: no table pipe inside the wrap-style row's code span

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Wrap styles: no new emergency breaks, hanging punctuation in Pretty's width, wrap style as an override

Balance and Pretty reject a re-break with more mid-word breaks than the greedy lines; Pretty narrows a line by its text less its hanging marks; wrapStyle joins the textStyle override group; docs wording. Tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:13:48 -05:00
ad20c56874 Reject invalid dashes in node.set instead of dropping entries (#1899)
* Reject invalid dashes in node.set instead of dropping entries

Found while testing crates/engine/src/cmd/props.rs:580

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Reject boolean dash entries in node.set (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:49 -05:00
7d9d2bda50 Reject fractional index in layout.setTrack (#1892)
Found while testing crates/engine/src/cmd/layout.rs:46-48 (set_track)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:39 -05:00
033e943dc7 Reject fractional index in proto.interaction.remove and set (#1874)
* Reject fractional index in proto.interaction.remove and set

Found while testing crates/engine/src/cmd/proto.rs:37 (proto.interaction.remove) and :412 (set)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Name the upper bound in the interaction index error (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:12:29 -05:00
NetterSilverandClaude Haiku 5.5 41ec33eeec Reject negative and fractional indexes in node.reorder
Found while testing crates/engine/src/cmd/edit.rs:329

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 01:41:31 +01:00
e65ce42000 M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835) (#1865)
* M7: Minimize collapses the navigation bar and both sidebars, the right one back while something is selected (#1835)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* M7: a narrow window ignores Minimize, and a rename started while minimized waits without repaints

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:32 -05:00
1f0acd2bbd M4: Alt+Enter while editing text opens the link at the caret (text.openLink) (#1860)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:21 -05:00
f9d507f7b0 M2: a child resized to the full available space of its auto layout frame becomes Fill container (#1859)
* M2: a child resized to the full available space of its auto layout frame becomes Fill container

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Auto layout: a child becomes Fill for the full space only across the flow or alone along it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Auto layout: the full available space is what layout leaves (padding and an included inside stroke), judged after a new path's size

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* M2: a rotated child resized to the frame's room stays Fixed (review)

CodeRabbit on #1859: a rotated layer's local size isn't the room layout
gives it, so Fill would squash its geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:11 -05:00
e9eb7ce7f4 M4: max lines on auto-width text keep it auto width, hugging that many lines (#1864)
* M4: max lines on auto-width text keep it auto width, hugging that many lines

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* M4: an auto-width text with max lines hugs the ellipsis on its last line, and the editing test expects auto width

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:29:59 -05:00
f9b5693621 M2: a text keeps a max height or a number of max lines, not both (#1832)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 00:02:56 -05:00
a953a04e33 Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized (#1846)
* Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Interface size keys: live while editing text too, and the new size draws on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* A preference set during a frame repaints this window too, so the interface size applies on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Control channel: a preference changed by a hidden window's command repaints every window

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-01 23:59:08 -05:00
Trevor BrownandClaude Fable 5.1 5050631bcf M5: Hide UI in the presenter: the bars go away and Cmd/Ctrl+\ brings them back (#1820) (#1826)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 23:50:03 -05:00
Trevor BrownandClaude Fable 5.1 65bd00f739 M3: Delete on a layer inside an instance hides it instead of being refused (#1834)
* M3: Delete on a layer inside an instance hides it instead of being refused

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* The components structure test: Delete inside an instance hides

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 23:49:58 -05:00
Trevor BrownandClaude Fable 5.1 2abd8c5f52 M2: Remove all auto layout takes it off a frame and every frame nested in it (#1833)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 23:49:53 -05:00
Trevor BrownandClaude Fable 5.1 3b8ded69c0 M4: a link on text is underlined by default, and Underline takes it off (#1841)
* M4: a link on text is underlined by default, and Underline takes it off

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* text.link: only a new link takes the default underline; Underline reads the texts inside a target

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 23:49:48 -05:00
NetterSilverandClaude Haiku 5.5 f26226590a Reject negative and fractional index in variable.moveMode
Found while testing crates/engine/src/cmd/variables.rs:267 (move_mode)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 05:13:36 +01:00
NetterSilverandClaude Haiku 5.5 650601593e Reject negative or fractional index in page.move
Found while testing crates/engine/src/cmd/page.rs:132

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 21:05:43 +01:00
NetterSilverandClaude Haiku 5.5 1aa6b81d01 Reject invalid entries in vector.create points instead of dropping them
Found while testing crates/engine/src/cmd/create.rs:~286 (create, the `points` branch using filter_map)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 17:23:54 +01:00
NetterSilverandClaude Opus 5.5 96dcb3eda9 Give each variable a unique custom property in CSS export (#1738)
Found while testing crates/engine/src/cmd/variables_io.rs:~80-105 (css, non-tailwind `name` closure using css_var_of) with crates/engine/src/dev/codegen/css.rs:41 (css_var_name)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:37:26 -04:00
NetterSilverandClaude Haiku 5.5 556d44d47a Reject '+' sign in comment parse_link thread ids (#1740)
Found while testing crates/engine/src/cmd/comment.rs:55 (parse_link)

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-31 10:37:22 -04:00
BFlat 6b28ef57dc M4: Dash cap setting for Dash and Custom strokes (#1728) 2026-10-31 09:41:39 +07:00
BFlat 3b77c236af Merge pull request #1731 from zral/docs/vector-path-xy-offset
M0.9: document that x/y offset a vector path by its own box
2026-10-31 09:14:25 +07:00
BFlat 87254913af M3: library analytics list how each style and variable is used (#1730)
* M3: library analytics list how each style and variable is used

A file reports how many of its layers use each of a team library's styles and
variables, and Show usage lists them by a Type choice with their layers and
files. Layers in instances count for their component. Also ignores plugins/target.

* M3: the style analytics test waits for the instance report too
2026-10-31 09:13:08 +07:00
BFlat 3837b08a71 M8: a damaged image is refused with a reason instead of showing as an empty box (#1724)
* M8: a damaged image is refused with a reason instead of showing as an empty box (#1474)

* M8: replacing an image refuses a damaged file too
2026-10-31 07:22:44 +07:00
BFlat 920aafb805 M1: the colour picker's Image tab has a placeholder, a preview and takes a dropped file (#1682) (#1713)
Choosing Image shows a checkered placeholder and Upload from computer instead of a dialog at once; an image paint shows its asset preview. A file dropped on the open picker replaces the image, keeping the fill mode, crop, position and adjustments (image.replace, one undo step; paths capped as image.place's). The browser's file picker reaches the tab too. Also fixes a race in library_detach_tests.
2026-10-31 06:32:59 +07:00
BFlat 1ea19482d3 M1: the Frame tool's Phone presets have the iPhone 17 family (#1691) (#1720)
iPhone 17, 17 Pro, 17 Pro Max and Air, at the sizes the prototype device list uses; a test keeps presets named like a device at its size.
2026-10-31 06:13:47 +07:00
BFlat 2d784c363d M3: library analytics count insertions and show them over time (#1704)
Gap 10: a file counts the library instances it places, creates, pastes or duplicates and reports them like its detaches. The server counts what a file's totals grow past their highest in the current week and keeps a year of weeks (actions.json). library.usage {days} and Show usage give insertions and detachments over the past 30 days, 90 days or year. New variants are appended; an older server refuses them and Show usage says so.
2026-10-31 04:13:14 +07:00
BFlat 760aece238 M5: an Open link action can open in place of the prototype, with Open in new tab off (#1685) 2026-10-31 02:37:54 +07:00
BFlat bbd1188c81 M3: a collection of this file can be extended (#1679)
Gap 14: variable.extendCollection and Extend collection on a collection's row in the Variables editor. Extensions follow edits of the collection they extend at once (normalising keeps them in step whenever variables change), extensions extend again, and a loop of extensions from a hostile file comes apart into plain collections.
2026-10-31 02:21:53 +07:00