* Prototype player: no clicks reach the prototype behind the link notice
* Selection colors: scopes filter the variable menu; the command binds as variable.bind does
* Style picker: color variables scoped to every selected layer's fill or stroke
* Corner button: named Corner smoothing on polygons and stars
* docs/gaps.md: known gaps from the #1311 review
* cargo fmt
* Style picker: clippy
The player opened a prototype's Open link at once, also from an After
delay trigger with no click. The help center's Prototype actions page
says that the viewer is told they are leaving before an external
website opens, with a checkbox that skips this page on the current
device.
An Open link to an http or https address now shows a notice with the
link, Open link, Cancel and "Don't show this again". Open link opens
it; Cancel, the close button or Escape drops it. While the notice
shows, the prototype takes no keys, and the presenter's C, Shift+C and
F keys do nothing. When Open link is pressed with the box ticked, the
desktop app saves the choice with its preferences; the web build keeps
it until the page reloads. proto.linkNotice {show} turns the notice on
or off. A mail link opens at once, as before, also while the notice
shows. A second link to a website that fires while the notice shows is
dropped.
text.onPath, and the Design panel's Put text on path button that runs
it, kept the text layer's own fill and effects when the text went onto
a path. The help center's Guide to text says that the vector object's
fill and effects are transferred to the text layer.
The text now takes the path layer's fills, with their fill style and
variable bindings, when one of them shows, and the path layer's effects,
with their effect style and bindings, when it has any. The text's own
bindings on those fields are dropped. When the path has no visible fill
or no effects, the text keeps its own.
Text went onto a path only with the Design panel's Put text on path
button (a text layer and a path selected together) or with text.onPath.
The Text on a path tool, in the Text flyout of the toolbar
(tool.textOnPath), shows the I-beam over a shape's or vector's path, and
a click there puts new, empty text on the path, starting at the click,
and starts typing. The text takes the path layer's place, transform,
box, constraints and effects, and its fills when one of them shows; the
path layer is removed. Ending the edit with nothing typed puts the path
back with no undo step.
The tool measures the distance on the page, so a layer scaled more one
way than the other, or shown smaller, is reached where it shows. Shapes
in front whose outline is away from the click do not hide a path behind
them, and paths that a mask or a clipping frame hides at the click are
skipped, as are locked layers and layers inside instances.
A double-click into a text layer that was just made no longer makes it
count as an existing layer, so leaving it empty records no undo step,
with this tool and with the Text tool.
A Selection colors row could take a color style but not a color
variable, and a row linked to a style or variable could not be
unlinked from there.
selection.bindColorVariable binds a color variable to every solid fill
and stroke slot a row stands for. It replaces a color style on that
list and any earlier binding of the slot. A text range with fills of
its own takes the variable's value in the layer's modes. Gradients and
slots outside the variable's scopes are refused.
selection.detachColor unlinks the style or variable of a row's slots
and keeps their colors.
In the Design panel, the Styles menu of an unlinked row lists the
document's color variables after its color styles, and a linked row
shows a Detach button in the Styles button's place.
* M6: Dev Mode measurements kept on the canvas, a read-only layers panel
Measurements placed between two layers, along a side or to the layer
around one (devMode.addMeasurement / removeMeasurement / measurements),
stored on the layer they start from (saved, synced, undoable; old files
open without them), drawn in Dev Mode as labelled redlines that follow
the layers and listed, placed and removed in the Inspect tab. In Dev Mode
the layers panel only selects and expands.
* M6: Dev Mode measurements: explicit imports, a measurement row of its own (Sonar)
Tailwind v4 drops theme variables no utility uses, but page code also
reads tokens through var() and (--name) utilities, so the export keeps
them all. Two test assertions mixed && and || without parentheses.
- Text that already fits keeps its exact size; shrunk sizes round to a
tenth but never below minSize (per run, its share of it).
- A last line after a hard break is not an orphan, and the no-break space
is only placed inside the last paragraph.
- docs: data.fill documents formats? and errors?.
On a device, a presented frame bigger than the screen now scrolls through
its whole size whatever its overflow, as Figma documents for top-level
frames. Before, "No scrolling" frames only clipped, so the landing page
template's long Phone frame couldn't be scrolled in Present. Content past
a frame's own edges still scrolls only on the axes its overflow allows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Board files (File > New board) with sticky notes, shapes with text, connectors that follow what they join (straight or elbow, arrowheads, labels), marker, highlighter and eraser, and stamps; a board toolbar and a contextual bar; the Design panel hidden on boards, Prototype and Dev Mode refused there. Boards are ordinary FigCraft files built from ordinary layers, so saving, undo, export, MCP and live sessions work unchanged. File tabs, recent files and Open files show a pen nib for design files and a note for boards (original icons drawn in code).
Import lines for connected components inside generated layer trees; Compare changes against any saved version; a Dev Mode behaviour suite with an inspect-only canvas (menus, shortcuts, paste and Revert to saved refuse design edits; a drag under way is dropped on entering), Changed in the ready list, statuses only on top-level frames and sections; read-only plugins' panels from the Inspect tab.
* Dev profile: line tables for workspace crates, no debug info for dependencies
* Tests: one integration-test binary per crate with several test files
* xtask ci: nextest, doctests, tests without examples, --keep-going; add xtask test
node.create, node.set and the other prop appliers map fill/stroke
"none" (either case) and null to an empty fills/strokes list, as
fills: [] already did (undocumented), so an outline-only shape is one
call - agents write "none" for no paint. KEYS and docs/mcp.md
document it; the error names the accepted forms. fill_none_tests
covers create, set, undo, text and the error.
A Button main's override on its icon instance's glyph (a red fill) was
lost when an instance swapped the icon: the outer rebuild re-copies the
glyph from the main's layer with no overrides of its own, so the nested
rebuild matched it by name and carried nothing. Before that rebuild, each
old layer of the swapped instance now takes the overrides held along the
source chain it had before this rebuild, through layers inside instances
in the mains that place the swapped instance. Geometry, slot content and
groups the layer overrides itself are left out. Matched by name, the
values carry onto the new main's layer as the instance's own: inherited
at the swap, frozen after, and back to the icon main's value on reset.
An icon main's own overrides stay with it.
apply_order gave fillStyle, strokeStyle, effectStyle, gridStyle and
textStyle the rank of plain values, so the JSON map's byte order decided:
fills, strokes, effects, grids, layoutGrids and variationSettings sort
after their link and overrode it (node.set then unlinked the style;
node.create kept a link its values contradicted). Style links now apply
after the values and x/y, just before boundVariables. A deviation set in a
later call still unlinks.
- inspect.code {language: "tailwind"}: a layer tree as a React component
styled with Tailwind CSS v4 utilities, no stylesheet. Auto layout maps to
flex utilities on the spacing scale; fixed-width auto layout frames, text
and wide layers become fluid (w-full max-w-[…]); instances are inlined
with their overrides, except those connected to code (codeConnect), which
are their snippet with one import each; bound variables are theme
utilities (bg-accent, rounded-card).
- mobile: <frame id> (same layer names): mobile-first code, desktop classes
that differ behind lg:, resets for desktop defaults, padding compared per
side; layers only on desktop are hidden below lg, those only on mobile are
listed in the header.
- variable.export {format: "tailwind"}: @import "tailwindcss" and an @theme
block (--color-*, --radius-*, --text-*, --spacing-*, --font-*), names
unique per file and shared with page code.
Builds on the token roles of the CSS export px fix (#1008).
- data.fill {id, rows, gap?, formats?, dir?, format?, scale?}: repeats a
frame once per row in one undo step; each row key names layers in the
copy (text, or {text?, fill?, visible?, fit?}). `formats` makes each row
once per size (feed, story, square), the copy resized so its auto layout
reflows. With `dir`, each copy is exported under a unique file name; a
failed export is reported in `errors` and keeps the copies.
- text.fit {ids?, maxLines?, minSize?, orphans?}: the largest font size,
down to a floor, at which fixed-width text stays within N lines and its
box; avoids a one-word last line; `fits` comes from the final text.
- Plugins can't call data.fill: it runs other commands inside, past the
plugin policy, like command.batch.