Commit Graph
3465 Commits
Author SHA1 Message Date
ljkx 375a7b8abb M1: a click on a fill, stroke or effect row highlights it (#1494)
Fill, stroke and effect rows in the Design panel couldn't be selected.
A click on a row's reorder grip (the strip left of its fields) or on its
background between its controls now highlights it in the selected-row
colour, one row at a time, as the help center's "Copy and paste
properties between layers" describes ("Click to highlight the property
on the right sidebar").

- The row is a click target under its controls (`row_target`), and the
  grip of these lists also takes a click (`ListDrag::row_clicked`).
  Neither is focusable, so Tab still goes from control to control.
- The highlight belongs to the selection, file and Design tab it was made
  on. Escape lets it go before it deselects; so does a press on the
  canvas or elsewhere in the Design panel, or a frame its row isn't
  drawn in (an undo took its paint away). It follows its paint or effect
  when rows are reordered or removed.

Tests: property_rows_tests (7).
2026-11-09 20:30:25 +00:00
ljkx 938f8b2ad9 M8: test that .. after a link is taken by name on Windows (#1094)
A junction, which any account can make, leads out of the root; the path
through it and back with `..` is the file in the root, to the check and to
Windows when it opens it.
2026-11-09 21:29:18 +01:00
ljkx 966aa7b263 M8: automation roots refuse a path they can't look at, and review follow-ups (#1094)
Review of the fix for #1094:

- A path that can't be made absolute is refused instead of going on to be
  resolved.
- A name on the way that can't be looked at for a reason other than nothing
  being there (no permission, for one) is refused: it may be a link that
  resolving would follow. Only "not found", "not a directory" and "invalid
  filename" count as nothing there.
- Tests: the link part of the Windows test is its own test, so the spelling
  and device checks no longer share a test with a part that is skipped where
  links can't be made; the timing assertion is gone (the messages tell the
  cases apart); link chains of 39, 40 and 41 links; a root on a share against
  every network spelling, in both of the root's spellings; devices for read
  and write; `..` after a link; paths that aren't UTF-8.
- Docs brought in line with the code.
2026-11-09 21:23:20 +01:00
Trevor BrownandClaude Sonnet 5.5 e415a3a0fb M2: pin the leading-edge drag reorder at its boundaries, zero delta and several layers (#2142 review)
Tests for the -129 leftward boundary, the vertical boundaries both ways, a drag with no move along the flow, and several moving layers using their combined box. Doc comments say whose edge is "leading" and what the caller passes; the parity row names the unchanged cases and the web-app measurement source.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 triaged and briefed; Sonnet 5.5 verified and wrote at medium
2026-11-09 15:16:36 -05:00
Trevor BrownandClaude Sonnet 5.5 1ef0c16fdf M8: invert zoom direction tests: Ctrl wheel, panning unchanged, pinch both ways, off persists (#2147 review)
Tests only. Ctrl+wheel joins Cmd+wheel; panning (plain wheel, Cmd/Ctrl+wheel and Shift+wheel with Scroll wheel zoom on) is compared between invert on and off; the pinch runs with Scroll wheel zoom on and off and with factors above and below 1; the pref's off value is saved and reloaded.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 triaged and briefed; Sonnet 5.5 verified and wrote at medium
2026-11-09 15:11:14 -05:00
Trevor BrownandClaude Sonnet 5.5 ff7305266c M8: the Annotation tool checks its target first, tool.set asks for the note too, one Developer Mode tool list (#2143 review)
tool.annotation validates a named id or ids before changing the tool: a page or unknown layer is an error, and a named layer is always selected. Choosing the tool with tool.set (the toolbar) asks for the selected layer's note like Shift+T. annotation.new only sets the focus request for a selected, non-page layer, so it cannot wait and take the keyboard later. ToolId::works_in_dev_mode is the single list behind the pointer routing, the command allowlist and the toolbar. The press never picks the page. Tests for the invalid targets, the toolbar path, a drag, the re-focus, and a request for an unselected layer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 triaged and briefed; Sonnet 5.5 verified and wrote at medium
2026-11-09 14:59:32 -05:00
Trevor BrownandClaude Sonnet 5.5 7a8d422dec M8: the Scale field only takes the keyboard when the tool is chosen or the panel returns, and Ctrl/Alt+Enter keep the tool (#2148 review)
A stale "field had the keyboard" flag no longer ends the tool on the first frame back from another tab. A selection appearing after K with nothing selected no longer moves the keyboard from the canvas to the Scale field. Ctrl+Enter and Alt+Enter are not the tool's exit. Tests pin the full text selection, the 2x and 1.5 factors, the field letting go after Enter and Escape, idle frames after Tab, and a leftover edit dropped on return.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 triaged and briefed; Sonnet 5.5 verified and wrote at medium
2026-11-09 14:47:06 -05:00
Trevor BrownandClaude Opus 5.5 499717d6a0 M7: the variant connection docs name the grouped-set case and target_at's None cases (#2161 review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 checked and wrote
2026-11-09 14:32:49 -05:00
ljkx a54eafc0fc M1: test that picker Crop only sets the mode where Crop image doesn't apply (#2150)
Choosing Crop in the colour picker starts crop mode only for one layer's
own image fill. The new test chooses Crop for a stroke, for two layers'
fills at once, and for a region's fill in vector edit mode (the layer's
own fill an image too). In each case it checks that the paint takes the
Crop mode, crop mode does not start, and the picker stays open. It also
checks that crop_on_canvas gives nothing for a solid fill, an index with
no fill, the page background and a style.
2026-11-09 20:08:46 +01:00
Trevor BrownandClaude Sonnet 5.5 bb6d1c35ee M12: a board table's height change goes into its rows (#2172)
Resizing a board table by its bottom edge, a corner or a height command left it at a fixed height, so its rows stayed at 40 with blank space below them (or were clipped when shrunk) and typing in a cell no longer grew the table. Measured in FigJam's web app (2026-11-09): a table has no lock-proportions control and never scales proportionally; a bottom edge grows only the last row, a corner drag shares the change equally over every row and column, text keeps its size, and a cell has a minimum size.

board_table::settle_resize runs first in the normalize pass (so a drag, node.set, transform.resize, MCP and live-session peers all agree): a table left at a fixed height gets the change in its rows' minimum height (the last row, or every row when both axes changed), is kept at least columns x 40 wide, and hugs its rows again. New cells get a minimum width of 40. FigJam's floor is about 52 and FigCraft's is 40; there are no per-column widths yet.

Docs: boards-parity Tables row, revision history and status line; the board_table module doc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Fable 5.1 (work seat) scoped; Sonnet 5.5 measured in FigJam and wrote at medium
2026-11-09 14:02:07 -05:00
Ragulsundaram 6108efd4d2 i18n(ta): shorten Min W to match the other size labels 2026-11-10 00:11:02 +05:30
skylar taylor-barrick a2e87433d8 Naming: Developer Mode follow-ups (#2165)
* Naming: record Developer Mode in the feature-names doc and the stage paragraph

docs/feature-names.md gains the entry for the rename: the old label, the new one, Figma's
US registration for DEV MODE (7574348) as the reason with its TSDR record, the identifiers
that keep their names, and the translations that moved from a generic "dev mode" to the
developer mode. The stage paragraph in ROADMAP.md listed the mode as Dev Mode.

* Actions palette: put each search doc comment back on its function

search_text had grown a second doc line under matches's, so the sentence about the query sat
above a function that takes none. search_text now says what it reads and matches keeps its own.
2026-11-09 15:44:02 +01:00
Trevor BrownandClaude Opus 5.5 ff3e4e6010 M12: sticky growth checked with a measured font; the normalize note drops sticky size (#2164 review)
The growth test scans the system fonts, as the other text-measuring engine tests do (#333), and
asserts the note grows taller unconditionally instead of only when text happened to measure.
docs/boards-parity.md no longer lists a sticky's text size among what the normalize pass derives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 checked the findings and wrote
2026-11-09 09:38:56 -05:00
Trevor BrownandClaude Opus 5.5 36cd59dada M7: a grouped component set's background takes no connection either (#2161 review)
A set in a group has no top-level frame, so a drop on its background fell through to frame_at
and made a Navigate to the section around it. target_at now returns nothing for any point
inside the source's set that misses a sibling variant, as Figma does; red-first UI test with the
set in a group in a section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 checked the finding and wrote
2026-11-09 09:38:54 -05:00
Trevor BrownandClaude Sonnet 5.5 1ee3087bc9 M12: a sticky's text keeps its size and the note grows taller (#2157)
FigJam's sticky text is 18 (Small) and never changes as the note fills; the note keeps its width and grows taller (measured in the web app, 2026-11-09). A new sticky's text is STICKY_TEXT_SIZE (18) and the normalize pass no longer resets a sticky's text size by its length, so saved stickies keep the size they have. No size menu yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 measured in Figma and wrote at medium
2026-11-09 08:46:12 -05:00
Trevor BrownandClaude Sonnet 5.5 5c50025270 M7: a connection dropped on a sibling variant creates Change to (#2158)
A connection dragged from a variant (or a layer in it) onto a sibling variant of its set now creates Change to that variant (On click, Instant) and outlines the sibling while dragging, as measured in Figma. The set background and the variant itself add nothing; a frame outside the set still gets Navigate to.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 measured in Figma and wrote at medium
2026-11-09 08:37:11 -05:00
Ragulsundaram 50a76d0914 i18n: Tamil (ta) interface catalog (#2159)
Adds ta.toml with every UI string, command label and menu wording, marked draft.
Desktop draws Tamil with an installed face (#2018); the web picker leaves Tamil out
until craft-fonts has Noto Sans Tamil, like Georgian and Arabic, with a test.
docs/translating.md gains Tamil's entry, typography notes and a Tamil draft section.
2026-11-09 18:45:27 +05:30
ljkx 91a76c72c4 M1: the picker-Crop test drags the picture's corner outside the layer too (#2150) 2026-11-09 12:53:10 +01:00
ljkx 8330ffb32a M1: Crop chosen in the colour picker enters crop mode (#2150)
Choosing Crop from the image fill's mode dropdown only set scale_mode: crop
mode never started, the first press on a handle closed the picker and went
to the tool, and the picture lost its place (no placement crop). For one
layer's image fill it now runs image.crop, as Object > Crop image and the
Fill row's button do; other targets only set the mode, as before.

Fixes #2150
2026-11-09 12:44:21 +01:00
ljkx 984270fbec M8: automation roots refuse a path on another computer before resolving it (#1094)
On Windows, Roots::check resolved every path (canonicalize opens it) before
comparing it with the roots, so `\server\share\x` contacted the server, and
could send it the user's credentials, before it was refused. Now the path as
given and made absolute is classified by its spelling with craft_docpath
first: another computer that no root's share is on, a device path, or a DOS
device name anywhere in it is refused, and so is a link on the way that
leads to one (followed by its target's spelling, up to 40 links).

Fixes #1094
2026-11-09 12:38:29 +01:00
Carbocode 481b6831cc M1: composite translucent pages over a themed checkerboard 2026-11-09 11:21:38 +01:00
Carbocode a45d932a34 M1: reuse the sidebar paint field for page backgrounds 2026-11-09 11:21:38 +01:00
Trevor BrownandClaude Opus 5.5 197a390a84 M8: the Scale field refocuses after another tab, and Enter elsewhere keeps the tool (#2148 review)
The focus request was reset only while the Design panel drew, so choosing the tool again on the
Prototype tab left the field without the keyboard; it now follows the frame the section last
showed in. Whether the factor field had the keyboard was read before the widgets ran, so an
Enter right after a click into W or H could end the tool; it is read after them now. A number
field's leftover edit from when it was last shown no longer swallows the focus request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-09 03:40:27 -05:00
Trevor BrownandClaude Opus 5.5 6856437479 M8: the Scale panel keeps FigCraft's origin; the centre default is a follow-up (#2146)
The panel set the origin to the centre once per app. The origin is the Scale tool's drag pivot
too (scale_factor), so that also changed handle drags, unmeasured against Figma, while the
engine and its tests keep top-left. This PR keeps to the focus and the Enter and Escape
endings the issue asks for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-09 03:03:26 -05:00
Trevor BrownandClaude Sonnet 5.5 92145e6f8d M8: K focuses the Scale field, so K, 2, Enter scales by two (#2146)
The Scale tool now opens its factor field focused with its text selected (once per activation), the origin starts at the centre, and Enter or Escape in the field returns the tool to Move with the selection kept. Measured in Figma web, 2026-11-09. Docs: ui-parity Scale tool row and revision.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 measured and wrote at medium
2026-11-09 02:59:53 -05:00
Trevor BrownandClaude Sonnet 5.5 6fce958ae6 M8: Preferences > Invert zoom direction reverses wheel zoom (#2145)
Adds the invert_zoom preference and the pref.invertZoom command, a checkbox in the Preferences window after Scroll wheel zoom. With it on, Cmd/Ctrl+wheel and a plain mouse wheel (with Scroll wheel zoom on) zoom the other way by the same factor; a pinch and panning are unchanged. Documented in docs/ui-parity.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 measured and wrote at medium
2026-11-09 02:53:44 -05:00
Trevor BrownandClaude Opus 5.5 7e2752d577 M4: a text style made from a whole layer counts a trailing empty paragraph's wrap style (#2094 review)
`paragraph_style` compared the paragraphs `para_span` gives for the whole text, which leaves
out an empty paragraph after a final newline, so Balance paragraphs over an Auto trailing one
made a Balance style. It now compares every paragraph by index, as the list style does. The
parity row says the wrap styles are lost through a .fig round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-09 01:34:38 -05:00
Trevor BrownandClaude Opus 5.5 8b15c00f84 M8: the MCP pointer tool lists the Annotation tool (#1840)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-09 01:22:52 -05:00
Trevor BrownandClaude Sonnet 5.5 2839497d62 M8: Shift+T picks the Annotation tool, which opens a layer's note in the Inspect tab (#1840)
Adds ToolId::Annotation and tool.annotation {id?}. With a layer selected (or an id) it asks the UI for that layer's note at once; in the tool a press on a layer selects it and asks, a press on empty canvas does nothing, and the engine never writes the note. annotation.new shows the Inspect tab (not a toggle) and focuses the layer's new-note field once. The tool passes Developer Mode's gates and stays selected until V or Escape (inferred; Figma's placement, cursor and stay-in-tool behaviour are unmeasured). Docs, shortcut counts (137 documented, 136 bound) and tests updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.1 briefed; Sonnet 5.5 wrote at medium
2026-11-09 01:21:43 -05:00
Trevor BrownandClaude Sonnet 5.5 cf567c3f63 M2: a child dragged in auto layout passes a sibling when its leading edge reaches the sibling's middle
Within its own row or column, a dragged child's slot follows the box it would fill under the pointer (start bounds plus the unsnapped drag), by the edge facing the drag, not the pointer. Measured in Figma's web app, 2026-11-09. Dragging in from outside, wrapping layouts and grids keep their rules. Several moving layers use their combined box.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed; Sonnet 5.5 measured and wrote at medium
2026-11-09 00:51:03 -05:00
Trevor BrownandClaude Opus 5.5 046238d392 Merge main into feat/wrap-style-paragraph-and-style-1827 (#2135 Developer Mode)
docs/ui-parity.md conflicted on the wrap style and text direction rows: kept this branch's text with #2135's Developer Mode naming.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-09 00:35:59 -05:00
skylar taylor-barrick 3839fe4a0c Naming: use Developer Mode instead of Figma's Dev Mode (#2135)
Figma holds a US registration for DEV MODE (US 7574348). Display text only: the view.devMode label, Inspect tab, shortcut row, refusal messages, CLI help, docs and all locale catalogs now say Developer Mode. Command ids, preference keys and saved fields are unchanged; "Dev Mode" stays a search alias in the Actions palette. Adds a naming guard test.
2026-11-09 06:24:44 +01:00
FredApps 72cfe7be30 fix(naming): use direct FigCraft feature descriptions (#2131)
Drops the "Figma-class" self-description from the MCP instructions and the "Accessibility at Figma" aside from pref.uiScaleUp help. Help text only; ids and preference keys unchanged. Adds a display-name guard over mcp/src and engine/src/cmd string literals.
2026-11-09 06:22:48 +01:00
ljkx 0aba3f7632 M4: a Pencil stroke stays where it was drawn, without spikes off it (#1741)
kurbo's simplify_bezpath, which smooths Pencil samples and runs Simplify
path, fitted two common shapes with a curve that ran far off the path: a
stroke that goes out and back to the very point it left (kurbo keeps a
reversal as a smooth join) and nearly collinear samples (kurbo measures
its error across the path, not along it).

figcraft_geom::simplify_path cuts the path where it turns back by more
than a right angle, simplifies each run with kurbo, and keeps a run as it
was when its fit strays more than twice the accuracy from it. The Pencil
and vector.simplify use it.
2026-11-09 06:15:33 +01:00
Trevor BrownandClaude Opus 5.5 92cf984902 Selection: Escape tests for a top-level layer, several layers and the focused frame (#2107 review)
One Escape selects nothing for a top-level frame, a top-level layer, two layers in one group,
layers in different parents, and a nested layer with a top-level one. With the focused frame
itself selected, one Escape leaves the focus view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 17:38:13 -05:00
dotCooCoo 892cda8e34 Updates: the updater reports installing, ready, installed and failed, and installs at exit
craft-update moves to a craft-libs branch that merges storytold/craft-libs#687 and #696.

- The check result window shows for every status but idle and stays
  closed while the startup prompt is open, so the first start after an
  update reports that it installed or why it failed. Restart to Update
  calls Updater::restart_to_update.
- on_exit calls Updater::start_pending_update before the process ends,
  which starts the install of an update waiting for the app to close.
2026-11-08 13:32:01 -08:00
Davit Matchakhelidze 9c2c2cb74e i18n(ka): place the new rows next to their English keys (review) 2026-11-09 00:59:12 +04:00
Trevor BrownandClaude Opus 5.5 dc841f8548 M8: vector.edit.position moves points in one pass; refusal, empty-selection and scrub tests
The handler grouped equal offsets by a linear scan per point, quadratic when every point moves
by its own offset. Only a single move covering every point carries the regions' paint frames,
so it now checks in one pass whether all offsets are equal: if so the points move as one,
otherwise each moves by its own offset. The result is the same as before.

Tests: booleans, a valid x with a refused y, and the overflow case are refused with no undo
step; nothing selected is refused; a shared Y scrubs every point in one undo step and a Mixed
X doesn't scrub. selection_position's doc comment names its one user, vector.edit.bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 14:44:47 -05:00
NetterSilverandClaude Haiku 5.5 0ff3a3682e Reject negative and fractional index in file.openRecent
Found while testing crates/engine/src/cmd/file/open.rs:33 (open_recent, `.nth(i.max(0.0) as usize)`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 19:22:04 +00:00
Trevor BrownandClaude Opus 5.5 8c24cbf691 Merge upstream/main into fix/loaded-font-wins-2092
Co-Authored-By: Claude Opus 5.5
2026-11-08 12:32:40 -05:00
impulsado a6d9b74df9 M3: resolve inherited slot properties by their defining component 2026-11-08 18:23:08 +01:00
Giovanni Almeida a6be897d31 M6: a vector point without a corner radius of its own takes the layer's on .fig import (#2114)
Figma rounds every point whose style doesn't set a corner radius with the
layer's cornerRadius. The import read only the point's style, so those
vectors came in sharp. Points now fall back to the layer's radius, at the
scale of the layer that carries it (#2091), and the network cache keys on
it. The export writes an explicit 0 for a sharp point on a rounded vector
so it stays sharp.
2026-11-08 13:55:56 -03:00
FredApps d54439be8b M6: scale imported vector point radii with their instance row (#2097) 2026-11-08 16:10:54 +01:00
FredApps a07a21b697 M6: bound exported instance factors to the supported range (#2097) 2026-11-08 16:08:34 +01:00
FredApps 23ed778562 M6: preserve scaled instance export overrides (#2097) 2026-11-08 15:23:36 +01:00
Trevor BrownandClaude Opus 5.5 8edcc02aee M8: points moved by one offset still carry the regions' paint frames
Setting each point's own coordinate moved the points one at a time, so when every point of a layer moved by the same offset (`{"x": {"add": 10}}` on all of them) `ops::move_vertices` no longer saw the whole network move and left the regions' paint frames behind. Points with the same offset now move together.

Verified: `moving_every_point_by_one_offset_carries_the_regions_paint_frame` failed before this and passes; vectoredit tests 92 passed, junk sweep 49 passed, clippy and fmt clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 07:57:54 -05:00
Trevor BrownandClaude Sonnet 5.5 767ebb67cc M8: store the new vector point tests with LF line endings
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove, measured and briefed; Sonnet 5.5 wrote at medium
2026-11-08 07:53:27 -05:00
Trevor BrownandClaude Sonnet 5.5 6ac20c7d53 M8: points' X and Y in vector edit mode show Mixed and set every selected point, as in Figma
Cause: with several points selected, the Point section's X and Y showed the selection's top-left and `vector.edit.position` moved the group so that its top-left landed on the typed value. Figma shows the value the selected points share (Mixed when they differ) and sets every selected point's own coordinate.

Fix: `vector.edit.position {x?, y?}` takes, per axis, a number (every selected point's own coordinate becomes it, in the parent's coordinates) or `{add}`, `{mul}`, `{div}` applied to each point's own coordinate; each point moves by its own delta with its handles, in one undo step; a zero divisor, a non-finite result, unknown keys and other junk are refused. A new `selected_parent_points` gives the panel the points in the parent's coordinates. The panel's X and Y are mixed-value fields: the shared value (compared within 1e-6) or Mixed, a typed value sets every point, and a relative edit typed into a Mixed field sends the object form. A scrub only starts from a shared value and sets it absolutely each frame inside one interaction, as the other point fields do.

Verified: engine tests for two points at different x, one point, add, mul and div, curve handles, one undo, the parent's coordinates and refused junk; panel tests for Mixed, typing -200 and a relative edit on a Mixed field, and a single point's value; the engine lib tests, the vector and field panel tests, clippy and fmt. Measured in Figma on 2026-11-08.

Not changed: the W and H row, other edited layers' points, and scrubbing semantics (the Mixed field cannot be scrubbed, as no start value exists).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove, measured and briefed; Sonnet 5.5 wrote at medium
2026-11-08 07:53:14 -05:00
NetterSilverandClaude Haiku 5.5 6222fce8d1 Reject signs and exponents in ExportSize::parse
Found while testing crates/doc/src/props.rs:1394 (ExportSize::parse, `num.parse::<f64>()`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 12:22:45 +00:00
NetterSilverandClaude Haiku 5.5 10d97e2716 Reject negative or fractional index in node.reparent
Found while testing crates/engine/src/cmd/edit/props.rs:248 (reparent, `index = num(p, "index").map(|i| i.max(0.0) as usize)`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 11:00:14 +00:00