Commit Graph
1608 Commits
Author SHA1 Message Date
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
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
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
Trevor BrownandClaude Opus 5.5 62e7e3a41a M8: the parity docs scope the points' X and Y to the layer being edited
CodeRabbit on #2121: with points selected in several edited layers, the X and Y fields read and set only the active layer's points (`selected_parent_points` and `vector.edit.position` use the edited layer), so the status lines, the vector edit row and the revision entries now say so, and the row lists X and Y over several edited layers under Not yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 09:32:09 -05:00
FredApps 23ed778562 M6: preserve scaled instance export overrides (#2097) 2026-11-08 15:23:36 +01:00
Trevor BrownandClaude Opus 5.5 194a4a1329 M8: the parity row lists the points' X and Y as done, not under Not yet
CodeRabbit on #2121: the Vector edit mode row in ui-parity.md put the new Point-section X/Y behaviour after "Not yet:", contradicting the row's done status and the revision entry. It now sits with the implemented features; only areas of other layers edited at once stay under Not yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 08:31:06 -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
Trevor BrownandClaude Sonnet 5.5 ad701088a2 Vector edit: Cut deletes exactly what it copies for a selected region (#2105 review)
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-08 00:38:49 -05:00
Trevor BrownandClaude Opus 5.5 1b7ce67966 Docs: a stored width that isn't a number falls back to 240; one date; no local notes path (#2103 review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 00:31:58 -05:00
Trevor BrownandClaude Opus 5.5 bceb5476e0 Docs: the new history row below the table's separator (#2107 review)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-08 00:31:58 -05:00
Trevor BrownandClaude Sonnet 5.5 a285fd293b Selection: Escape clears the selection instead of stepping out to the parent, as in Figma
Measured in Figma: with a layer inside a group inside a frame selected (from the canvas, from the Layers panel, or after Tab), one Escape selects nothing; FigCraft selected the parent first. Escape still leaves text and vector editing, ends gestures and the pen first. Three tests updated to the measured rule (selection_behaviour, devfocus, proto_editor); the rest of the suites pass.

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
2026-11-08 00:11:49 -05:00
Trevor BrownandClaude Sonnet 5.5 75f54e0ae9 View: the arrow keys pan 64 px, 320 with Shift, as measured in Figma
With nothing selected, one arrow press panned 20 px (200 with Shift); Figma pans about 64 px and five times that with Shift (measured at 100%, about +/-3 px). New engine test pins both steps; no existing test pinned the old values.

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
2026-11-07 23:59:04 -05:00
Trevor BrownandClaude Sonnet 5.5 0fec344f3c M8: copy, cut and paste in vector edit mode act on the selected points and segments (#1875)
Measured in Figma: in vector edit mode Copy takes only the selected segments (a lone point, the segments touching it) and a paste outside makes a new vector in place; Cut removes them, splitting the path in the same layer and staying in edit mode; Paste while editing adds the clipboard's segments or a copied layer's outline to the vector's network in place, selecting them. FigCraft copied the whole layer, Cut left nothing to paste, and Paste while editing did nothing. 16 new engine and UI tests cover copy, cut, paste, undo and junk parameters; the whole engine suite passes bar the known font test.

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 high
2026-11-07 23:38:58 -05:00
Trevor BrownandClaude Sonnet 5.5 ad7a886e3c Panels: the sidebars' width limits are Figma's (left 240 to 450, right 240 to 500)
Measured in Figma's web app: left sidebar 240 default, 240 min, 450 max; right
240 default, 240 min, 500 max; a double-click on the edge resets. FigCraft had
left 200 to 480 and right 240 to 480. Widths saved under the old limits are now
clamped when the app starts, not only when drawn. The narrow-window give-way
(canvas keeps 160 px) is unchanged, with the new 240 floor.

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-07 23:17:00 -05:00
Trevor BrownandClaude Sonnet 5.5 34c44e6063 Text: a font loaded with text.loadFont wins over the built-in and installed faces of the same family and style (#2092)
FigCraft chose the first face added for a family and style, so the embedded Inter 4.x kept Regular, SemiBold and Bold after Inter 3.19 was loaded and only Medium refitted. A loaded font is the counterpart of a font uploaded to the organization, which Figma's help center ranks first ("Manage conflicting fonts"). The test builds its faces from any embedded or installed font (skipped when there is none) and checks a loaded face beats a built-in one, the latest load beats earlier ones, and other styles resolve as before.

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-07 22:15:22 -05:00
Trevor BrownandClaude Sonnet 5.5 62d761a7bc M6: .fig import draws a scaled instance's layers at its scale factor (#2091)
An instance scaled with the Scale tool in Figma stores `symbolData.uniformScaleFactor`; the importer never read it, so the layers of a 128 px instance of a 256 px component came in at 256 px with the main's radii, strokes and spacing. Layers of an instance now take the main's lengths times the factor (composed through nested instances), the same values the Scale tool scales, unless an override or derived row gives the value, which is already at the instance's scale. Eight tests: the reproducer, the round trip, a derived size row not scaled twice, override rows, auto layout gap/padding/text, nested composition, factor 1 or none, and junk factors ignored.

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 high
2026-11-07 22:06:35 -05:00
Trevor BrownandClaude Sonnet 5.5 524ad8b1dd Components: Create component set in the component button's menu (#2088)
The third entry of the menu several selected layers get: each layer becomes a component and they are wrapped in a component set in place, 20 px around, named as Figma names them (measured): "Property 1=<layer name>" in left-to-right order, prop=value names kept, and layers that all share a name give a set of that name with "Default" variants. Engine tests cover the three measured cases, geometry, selection, one undo and the refusal of instances; the UI test checks the three menu entries in order and that the third makes one set.

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-07 21:38:30 -05:00
Trevor BrownandClaude Opus 5.5 071cc86bdf Docs: the .fig note on text styles leaves out the wrap style (#2094 review)
components-parity.md said a text style's wrap style is kept by .fig files; the .fig mapping is still open (ui-parity.md, mcp.md and the PR say so), so the row now says so too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote
2026-11-07 21:30:39 -05:00
Trevor BrownandClaude Sonnet 5.5 a1ac22f12f Transform: Shift while rotating lands on multiples of 15°, not 15° steps from the start
Measured in Figma: a layer at 7° (or -4.27°) Shift-rotated by its corner ends at 15°; FigCraft rounded the turn since the drag began, so a layer at 7° landed on 7 + k*15. The rotate gesture keeps the selection's shared rotation and Shift rounds base + turn; a selection with different rotations keeps rounding the turn. Test: a layer at 7° Shift-turned by 37° ends at 45°, by 3° at 15°, and a free turn is unchanged.

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-07 21:23:25 -05:00
Trevor BrownandClaude Sonnet 5.5 f5a52ea528 Components: several layers selected, the component button offers Create multiple components (#2088)
With two or more layers selected, the Design panel header's component button opens a menu with Create component and Create multiple components, as Figma's does; one layer keeps the plain button. Headless UI tests cover both cases (three frames: menu order, names kept, all selected, one undo; one frame: plain click), and an engine test pins createMultiple's names, positions and single undo step.

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-07 21:20:56 -05:00
Trevor BrownandClaude Sonnet 5.5 136ca4cda7 M4: a wrap style per paragraph and in text styles, overriding the layer's (#1827)
"You can apply a wrap style at three levels: Text layer, Paragraph, Text style". The layer level was done in #1871; this adds the other two, following the per-paragraph text direction (#1830) and the list style in text styles (#1876).

ParaList::wrap (None follows the layer) with para_wrap_style, set_wrap_style (the layer level, clears overrides), set_para_wrap_styles and common_wrap_style; restyle_breaks takes a style per paragraph; ParagraphStyle::wrap_style; text.wrapStyle and node.set wrapStyle with start/end; inspect lineWrapStyles; CSS text-wrap only when the paragraphs agree; the Wrap style control follows the paragraph; a read-only row in the style editor. The .fig mapping is left for a later change.

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-07 21:13:39 -05:00
Trevor BrownandClaude Sonnet 5.5 35ac3a92d5 Menus: a submenu opens as soon as its row is hovered, as measured in Figma
Figma's main menu opens a submenu on the first frame its row is hovered (median 11 ms) and switches between rows in about 13 ms; FigCraft waited 0.15 s. The hover delay is now zero (the constant stays, so the pending-row bookkeeping the aim uses is untouched); the safe-triangle aim stays until the maintainer decides, since Figma has none.

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-07 20:56:31 -05:00
Trevor BrownandClaude Sonnet 5.5 a20415e154 Variables: Edit variables edits Hide from publishing and scopes of several variables at once, with mixed states (#2082)
With several variables selected, the bulk menu's Edit variables opens the reference's compact editor: Hide from publishing and the scopes, each shown mixed when the selection differs, each change applied to every selected variable in one undo step.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and briefed from a Chrome measurement; Sonnet 5.5 wrote at medium
2026-11-07 20:47:09 -05:00
dotCooCoo 97f086e091 Docs: the Display P3 export sentence names RGB PDF and says CMYK PDF ends as DeviceCMYK
The colour profile paragraph in docs/mcp.md said SVG and PDF export
Display P3 files converted to sRGB, without the RGB qualifier the export
tool text now carries. It now says SVG and RGB PDF convert to sRGB, and
PDF with `cmyk` converts to sRGB the same way and then to DeviceCMYK.
The comment above the export match in file.rs says the same.
2026-11-07 16:36:59 -08:00
dotCooCoo 12ade9c78c Docs: the 2026-10-31 export entry keeps the JPG tagging it had on that date 2026-11-06 23:31:02 -08:00
dotCooCoo 297426c595 Export: color profile text states which formats are tagged and that CMYK PDF is DeviceCMYK
The export tool and command descriptions, docs/mcp.md and the file-format
table said PDF is always sRGB, that sRGB JPG is untagged and that every
exported file is tagged with its profile. RGB PDF is sRGB and PDF with cmyk
is DeviceCMYK; JPG embeds an ICC profile for sRGB and Display P3; WebP embeds
one for Display P3 only. The JPG quality (1 to 100, default 90) now has one
row in the format table.
2026-11-06 22:21:51 -08:00
bflatastic 2c09046a27 Merge main into #1729 2026-11-07 12:47:51 +07:00
bflatastic 4106c26e8c Merge main into #1729 2026-11-07 12:30:05 +07:00
bflatastic 70821e3645 Merge main into #1879 2026-11-07 12:29:18 +07:00
bflatastic 3f6aa299ea Merge main into #1873 2026-11-07 12:15:09 +07:00
bflatastic 9278ee5451 Merge main into #1729 2026-11-07 12:14:41 +07:00
bflatastic 12293e2e55 Merge main into #1879; shortcut counts with #1858's row 2026-11-07 12:14:30 +07:00
BFlat 9bec55c05a Merge pull request #1911 from TrevorBrown-dev/fix/layers-component-contrast
Layers panel: component rows in the theme's component tint, at AA contrast (#1903)
2026-11-07 13:12:56 +08:00
BFlat 808e60eeae Merge pull request #1772 from SoldierPePe/i18n/rtl-window-mirror
M10: a right-to-left UI language mirrors the window
2026-11-07 13:11:40 +08:00
bflatastic 1546d29a66 Merge main into #1729 2026-11-07 12:07:03 +07:00
BFlat 5cbc8207fd Merge pull request #2065 from helenkwok/web/page-api
figcraft-web: window.figcraft takes its request functions from craft-web-api
2026-11-07 13:03:10 +08:00
bflatastic 6304904317 Merge main into #1911 2026-11-07 11:58:09 +07:00
bflatastic 651a7743c1 Merge main into #1873 2026-11-07 11:57:34 +07:00
bflatastic e056dc7608 Merge main into #1879 2026-11-07 11:56:47 +07:00
bflatastic f1eb55f58b Merge main into #1772 2026-11-07 11:56:11 +07:00
BFlat b64f13ac56 Merge pull request #1759 from dotCooCoo/feat/export-color-profile
Export: each export setting chooses a color profile
2026-11-07 12:54:29 +08:00
Trevor Brown 3c607075f9 Merge remote-tracking branch 'upstream/main' into fix/layers-component-contrast
# Conflicts:
#	crates/ui-egui/src/panels/layers.rs
#	docs/ui-parity.md
2026-11-06 23:53:00 -05:00
BFlat 8d1b183e7d Merge pull request #1913 from tommy1616/feat/traditional-chinese-taiwan
M10: Traditional Chinese (Taiwan) interface draft
2026-11-07 12:52:13 +08:00
BFlat 5999408d1e Merge pull request #1858 from TrevorBrown-dev/fix/padding-all-sides
M2: a Cmd/Ctrl-click on a padding field sets all four sides with the value typed
2026-11-07 12:49:06 +08:00
Trevor Brown 0f8f780c19 Merge remote-tracking branch 'upstream/main' into fix/scrub-speed 2026-11-06 23:49:05 -05:00
Trevor Brown 39b93b7950 Merge remote-tracking branch 'upstream/main' into feat/pick-select 2026-11-06 23:49:01 -05:00
BFlat 764c416633 Merge pull request #1912 from TrevorBrown-dev/feat/layers-horizontal-scroll
Layers panel: the list is as wide as its widest name and scrolls sideways (#1904)
2026-11-07 12:47:49 +08:00
bflatastic bc47bcd17a Merge main into #1772 2026-11-07 11:41:20 +07:00
BFlat 1a035285de Merge pull request #1998 from cheyras/fix/live-offline-merge
M9: joining a live session again merges what you did while disconnected
2026-11-07 12:40:49 +08:00