Commit Graph
857 Commits
Author SHA1 Message Date
bflatastic ba45afb564 Merge main into #2200 2026-11-12 11:03:48 +07:00
BFlat 3740806656 Merge pull request #2221 from TrevorBrown-dev/fix/export-backdrop-blur-2213
M10: Export: a background blur or glass exported on its own keeps the content behind it (#2213)
2026-11-12 11:01:42 +07:00
bflatastic cdaee03e5b Merge remote-tracking branch 'origin/main' into landb/2200 2026-11-12 10:51:44 +07:00
bflatastic cf28715c18 Merge main into #2221 2026-11-12 10:47:13 +07:00
bflatastic 15d07d049d Merge remote-tracking branch 'origin/main' into landb/2143 2026-11-12 10:45:42 +07:00
BFlat d3f68c9f77 Merge pull request #2269 from TrevorBrown-dev/fix/instance-lists-its-variant-props-2022
M3: an instance lists the properties its variant uses; long property names stay on one line (#2022)
2026-11-12 10:07:37 +07:00
bflatastic 7e68a5b891 Merge main into #2200 2026-11-12 10:06:48 +07:00
bflatastic 4565ed9686 Merge main into #2143 2026-11-12 10:06:02 +07:00
BFlat 4531e99e5c Merge pull request #2070 from dotCooCoo/fix/export-color-docs
Export: color profile text states which formats are tagged and that CMYK PDF is DeviceCMYK
2026-11-12 09:56:27 +07:00
bflatastic 17104743b4 Merge main into #2143 2026-11-12 09:52:05 +07:00
BFlat ff749aa831 Merge pull request #2128 from Silvertree2010/fix/h12225-file-openrecent-silently-turns-a
Reject negative and fractional index in file.openRecent
2026-11-12 09:51:01 +07:00
bflatastic 98b2e55baf Merge main into #2200 2026-11-12 09:48:58 +07:00
BFlat 7006141600 Merge pull request #2105 from TrevorBrown-dev/feat/vector-edit-clipboard-1875
M8: copy, cut and paste in vector edit mode act on the selected points and segments (#1875)
2026-11-12 09:47:24 +07:00
bflatastic 31f014286e Merge main into #2200 2026-11-12 09:46:31 +07:00
BFlat b9c1bee665 Merge pull request #2262 from storytold/fix/sec-load-penpot-svg-fonts
load: share one layer allowance across Penpot raw SVGs, sum font-run coverage once
2026-11-12 09:41:46 +07:00
BFlat 483582c8aa Merge pull request #2078 from Silvertree2010/fix/h8510-image-savepreset-silently-turns
Reject negative and fractional index in image.savePreset
2026-11-12 09:40:22 +07:00
BFlat 73d252970f Merge pull request #2140 from ljkx/fix/pencil-stroke-spikes
M4: a Pencil stroke stays where it was drawn, without spikes off it (#1741)
2026-11-12 09:36:42 +07:00
BFlat 96430480a7 Merge pull request #2266 from storytold/fix/sec-rest-link-main-paths
components: work out each main's layer paths once while linking imported instances
2026-11-12 09:35:06 +07:00
bflatastic d1e7a299b0 Merge main into #2269 2026-11-12 09:33:25 +07:00
BFlat d0c4aca65b Merge pull request #2095 from TrevorBrown-dev/feat/create-multiple-components-dropdown-2088
Components: several layers selected, the component button offers Create multiple components (#2088)
2026-11-12 09:28:25 +07:00
BFlat fe5e0eb35f Merge pull request #2112 from Silvertree2010/fix/h10143-file-export-exports-the-whole-pa
Reject invalid layer ids in file.export and file.exportLayers
2026-11-12 09:25:28 +07:00
bflatastic 1a4824e9e9 Merge main into #2143 2026-11-12 09:23:31 +07:00
BFlat f2c593ff6e Merge pull request #2117 from Silvertree2010/fix/h11295-node-reparent-silently-turns-a-n
Reject negative or fractional index in node.reparent
2026-11-12 09:20:42 +07:00
bflatastic 82da8f513a file.openRecent: check the index with the shared req_index 2026-11-12 09:19:05 +07:00
Trevor BrownandClaude Opus 5.5 7d6274d6be Merge upstream/main into feat/vector-edit-clipboard-1875
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:55:32 -05:00
Trevor BrownandClaude Opus 5.5 d5eec3ec6e Merge upstream/main into feat/annotation-tool-1840
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:49:22 -05:00
Trevor BrownandClaude Opus 5.5 6f478a4ed6 Merge upstream/main into feat/rotation-field-page-2196
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:49:05 -05:00
Dylan M. Taylor cf77dfdabc load: an SVG that doesn't fit the allowance is taken out again; charge after cleanup 2026-11-11 21:31:26 +00:00
24cf70329b M5: Locked layers follow Figma's rules: no nudge, a locked parent's children stay locked (#1560) (#2219)
* M5: Locked layers follow Figma's rules: no nudge, a locked parent's children stay locked (#1560)

Pins Figma's locked-layer behaviour (measured in Figma's web app, 2026-11-10; help center "Lock and unlock layers") in a new lock_behaviour_tests suite. Fixes the divergences: transform.nudge and transform.nudgeSize skip locked layers, object.toggleLock without a value counts a child of a locked parent as locked (so it never locks the child on its own), and the Layers panel padlock on such a child is inert.

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

* Layers panel: a padlock sweep skips the children of a locked layer (#1560)

A padlock drag sweep no longer collects rows with `locked_above`, so
dragging across a locked frame's children leaves their own flags alone
and draws no swept value on them. An eye sweep 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

* M5: Lock/Unlock leaves a locked layer's child alone in a mixed selection (#1560)

With a locked frame's child and an unlocked layer selected, Lock/Unlock set both own flags, so
the child stayed locked once the frame was unlocked. With no explicit `locked` value, the command
now skips a layer whose parent is locked, as its padlock does; an explicit value (scripts) still
sets it.

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>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-11 21:06:56 +01:00
Dylan M. Taylor 6bc8bbaef1 inspect: work out a grid's cells once per inspection (#2260) 2026-11-11 14:48:38 -05:00
Trevor BrownandClaude Sonnet 5.5 14670b99d4 M3: an instance lists the properties its variant uses; long property names stay on one line (#2022)
In Figma's data a component set holds one text property per variant (plus leftovers of deleted variants), and an instance lists only the ones its current variant's layers read (measured in Figma's web app). FigCraft listed every definition on every instance, and wrapped a long property name inside an 84 x 20 box so its lines painted over the rows around it.

- components::instance_prop_list: prop_list keeping slots and the properties a layer of the component references (a nested instance counts, its layers and slot content are not entered). component.properties and the Properties panel use it for instances; sets, components and variants still list every definition.
- prop_label cuts the name to one line with an ellipsis and shows the full name on hover.
- Tests: an_instance_lists_only_the_properties_its_variant_uses, a_long_property_name_stays_on_one_line, and the pin a_set_keeps_each_named_text_definition_and_layers_read_their_own. Two tests and one setup that listed an unbound boolean on an instance now bind it to a layer.
- docs/components-parity.md: the properties row, header and revision history.

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-11 14:42:37 -05:00
Dylan M. Taylor a8e34e75a8 components: work out each main's layer paths once while linking imported instances 2026-11-11 18:38:59 +00:00
Dylan M. Taylor 2f8dd85f42 load: share one layer allowance across Penpot raw SVGs, sum font-run coverage once 2026-11-11 17:43:55 +00:00
Trevor BrownandClaude Opus 5.5 a1cb5309f4 Merge upstream/main into feat/vector-edit-clipboard-1875
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 11:39:44 -05:00
Trevor BrownandClaude Sonnet 5.5 020388cf1f M10: Export: a background blur or glass exported on its own keeps the content behind it (#2213)
An export of a layer draws only that layer, so a background blur or glass in it read an empty
backdrop. The export now renders the page's layers below the first exported layer, with the margin
the effects reach, as the backdrop (render_area_over), clipped to the export area; only the exported
layers are drawn on top. Layers without those effects, and every other export path, are 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-10 10:31:10 -05:00
Trevor BrownandClaude Opus 5.5 cc36771713 M8: absoluteRotation turns a selected parent before the layers in it (#2196)
A page angle depends on the parents', so a frame and a layer inside it set together both land on the typed page angle whatever the order of the ids (the layer went to 80 for 45 when it came first).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove and wrote (review fix, no brief)
2026-11-10 03:06:32 -05:00
Trevor BrownandClaude Sonnet 5.5 d0379dc70d M8: Rotation field shows and sets the rotation on the page (node.set absoluteRotation)
The Design panel's Rotation field now reads and writes the layer's rotation on the page (its own angle plus its parents'), as measured in Figma's web app, 2026-11-10. A new additive node.set key, absoluteRotation, does the work (one call for every selected layer, so one undo step and the moved rotation origin still apply); rotation stays the angle to the parent for scripts, inspect, codegen and the formats. Closes #2196.

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-10 03:02:09 -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 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
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
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
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
Trevor BrownandClaude Sonnet 5.5 dda27030c8 Components: the component button shows its menu arrow, Create component set is offered only where it works; tests for every refusal (#2095 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 01:47:50 -05:00
Trevor BrownandClaude Sonnet 5.5 87ce9612e6 Vector edit clipboard: Cut and Copy with only lone points are refused; one x/y refused; the system clipboard gets the partial copy; specs match (#2105 review)
Cut with nothing to copy (only points that touch no segment) is Disabled and changes nothing, as Copy now is (Copy used to fall through to the whole layer); paste borrows the clipboard instead of cloning it; a partial copy or cut mirrors its SVG to the system clipboard; x without y (or y without x) is refused; the junk test asserts refusals change nothing and accepted results are finite; the edit.copy, edit.cut and edit.paste specs state the behaviour and the mode-specific paste response.

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 01:44:23 -05:00
NetterSilverandClaude Haiku 5.5 db1bec08c8 Reject invalid layer ids in file.export and file.exportLayers
Found while testing crates/engine/src/cmd/file/export.rs:89 (export; same pattern at :332 in export_layers)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-08 06:31:13 +00: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 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 253e0c3df3 Components: Create component set refuses nested layers and is translated (#2095 review)
component.createSet now refuses a selection where one layer is inside another, as component.combineVariants does, before changing the document (the nested layer was silently dropped from the set). Adds the component.createSet label to the 22 locale catalogs that list component.createMultiple.

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:49:04 -05:00