- The Advanced stroke button now opens an inline settings block: style (Solid / Dash) with Dash
and Gap fields, Join with the miter angle, and Start / End point for lines and vectors.
- Frames and rectangles get an Individual strokes menu (All, Top, Right, Bottom, Left, Custom);
per-side weights show as T / R / B / L fields.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- New `figcraft_format::rest`: imports the public REST API file JSON (`GET /v1/files/:key`, and
the nodes endpoint's response) using the published reference only. Pages (name, background,
flow starting points), the layer tree, names, visibility, lock, opacity, blend mode, masks.
- Geometry from `relativeTransform` (children of groups/booleans checked against
`absoluteBoundingBox` to settle their space), else absolute bounds + rotation; sizes, corner
radii (uniform and per corner), smoothing, clip content, constraints, export settings, layout
grids.
- Paints: solid, linear/radial/angular/diamond gradients (handles map 1:1), image fills (fill,
fit, tile, stretch, rotation, filters) with bytes from a resolver or a grey placeholder keyed by
the imageRef; paint opacity, visibility and blend. Strokes: weight, per-side weights, align,
caps, join, dashes, miter angle. Effects: drop/inner shadow (offset, radius, spread, show behind),
layer and background blur.
- Vectors from `fillGeometry` (stroke-only vectors use the `strokeGeometry` outline), ellipse arc
data, stars/polygons, boolean operations. Text: characters, type style (family, style or weight
+ italic, size, line height px/%/auto, letter spacing, case, decoration, alignment, auto resize,
truncation, paragraph spacing/indent, OpenType flags, links), per-character overrides → styled
runs (UTF-16 aware), list type.
- Auto layout: direction incl. grid, alignment, padding, gaps, wrap, hug/fixed/fill sizing (legacy
and per-axis fields), absolute positioning, min/max, grid cells.
- Components and sets with descriptions and property definitions (names lose their `#id`
suffix); instances of components in the file become instances with their property values and
listed overrides; outside components and component cycles stay frames. Shared styles are created
from the layers that use them, named from the metadata.
- Unknown types and fields are skipped with a counted warning list; sizes, depth (256), layer count
(200,000), path length and list lengths are capped; nothing panics on junk.
- Engine: `file.open` / `file.import` / CLI detect the JSON by content, link imported instances to
their mains (sources and overrides), read image fills from an `images` folder next to the file,
never save over the JSON, and return `warnings`. `file.import` now remaps ids across all
imported pages at once (instances keep their main and layer sources) and brings styles and page
backgrounds along. `figcraft-cli info` prints load notes.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Effects and Layout grid sections gain the Styles button: a list of the document's effect or
grid styles (click to apply) and "Create style" from the selected layer; an applied style shows
its name with a detach button, like colour styles.
- The colour style picker and style chip are now one shared implementation for fill, stroke,
effect and grid slots.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Width, height, corner radius, gap and stroke weight fields show a variable button on hover; it
opens a picker of the document's number variables by collection (search, values shown). A
bound field shows the variable's name in a chip (click to change, detach to unbind).
- The fill and stroke style picker lists colour variables; a bound first paint shows the
variable's name with a detach button.
- Fix: editing a variable's value (or adding a mode value) now updates bound layers; before,
normalisation skipped the change because no layer was touched directly.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- With the Frame tool active the Design tab lists frame presets by category (Phone open by
default, others collapsible) with their sizes; clicking one adds the frame, selects and shows
it, and returns to the Move tool.
- `frame.create {preset}` without a position places the frame 100 to the right of the page's
right-most top-level layer (top-aligned), or centred in the view on an empty page.
- New `frame.swapOrientation {ids?}` (portrait ⇄ landscape) with a button next to the frame type
in the Design panel header.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Easing gains `customBezier {x1, y1, x2, y2}` (CSS cubic-bezier) and `customSpring {mass,
stiffness, damping}` (a damped spring's step response over its settling time, played over the
interaction's duration); presets keep their string form, so files stay compatible.
- Interaction editor: Custom bezier / Custom spring in the easing menu with value fields (typed or
scrubbed, one undo step), the curve preview follows.
- Presenter: the page's prototype device now frames the presented screen with a body drawn in
code (phones with rounded screens, side buttons and a camera pill; tablets; a laptop with its
base; a desktop monitor on a stand).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Page guides now draw on the canvas (under the rulers, shown with them).
- Drag from the top ruler for a horizontal guide, from the left ruler for a vertical one; drag
a guide (Move tool) to move it, back onto its ruler to remove it. Positions snap to whole
pixels when pixel snapping is on; a tag shows the position while dragging or hovering.
- Commands: `guide.add` returns the index, new `guide.move {index, offset}` and `guide.clear`;
`guide.remove` and `guide.move` reject bad indexes (a negative index used to remove guide 0).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Inline IME composition: egui Preedit events become a composition held in the text edit
session (`TextEdit.preedit`), shown inline at the caret with an underline and the IME cursor,
reflowing the text; the document only gets the committed text (`text.edit.insert`). Composing
over a selection replaces it; moving the caret or ending the edit drops an uncommitted
composition. The IME candidate window follows the cursor inside the composition.
New command `text.edit.preedit {text, cursor?}`; `text.edit.selection` reports it.
- Variable fonts: faces read their fvar axes (tag, name, range, default, hidden) and named
instances. Named instances are offered as styles and resolve in `find`; a style name with no
instance implies weight/italic/slant on the axes and optical size follows the font size.
Per-run custom axis values (`TextStyle.variations`, serde default, omitted when empty) are applied
in HarfRust shaping (ShaperInstance) and skrifa outlines/advances (cached per coordinates).
PDF export outlines non-default instances instead of embedding the default one.
- Commands/props: `fontVariations` / `variationSettings` props (whole layer or start..end),
`text.setVariations {variations, replace?}` (merges; edited range while editing),
`text.fonts {family, style?, axes: true}`; `node.get` and copy/paste properties carry the axes.
Picking a named style (Bold/Italic toggles, weight steps, fontStyle/fontWeight) drops custom
wght/ital/slnt values and keeps the others.
- Type settings ▸ Variable tab for variable fonts: a slider (default tick, double-click to reset)
and a field per visible axis, "Mixed" across differing styles, live drag as one undo step,
"Reset to style". Long style/family names stay on one line with an ellipsis and a tooltip.
- Tests: variable font shaping/outlines/clamping/instances (skipped without a variable font),
serde backward compatibility, preedit lifecycle and undo, variation commands/props/undo/junk;
junk sweeps extended with variation and preedit params.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Gradient stops on the canvas: drag a stop along the gradient line, click the line to add a
stop (sampled colour), the selected stop is ringed and shared with the picker; Delete removes
it (two always stay).
- Colour picker value row: a model dropdown (Hex, RGB, HSL, HSB, CSS) with its fields and the
alpha field; scrubbing works on the numeric fields.
- "On this page" swatches: the page's most used solid colours (cached per document snapshot);
swatches set a solid colour or the selected gradient stop.
- New command `paint.documentColors {page?, limit?}`; control channel `ui.set {colorPicker}`.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Arrow keys on children of an auto layout frame move them one place along the flow (Left/Up
earlier, Right/Down later; grids step by a row vertically); selected neighbours move as a
block and stop at the ends. Free layers still nudge.
- Cmd/Ctrl+arrows resize the selection by the nudge amount (Shift: big nudge), top-left fixed;
only the changed axis becomes Fixed. New command `transform.nudgeSize {dw, dh, big?}`.
- Control channel `ui.key` arrows with `cmd` resize too.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Assets tab rebuilt as its own module: local components as two-column tiles with live
thumbnails (re-rendered when a component's content changes, throttled), a search field that
filters components and styles, and the local styles list.
- Drag a tile onto the canvas: a ghost follows the pointer, the frame under it highlights and auto
layout frames show the insertion line; releasing drops an instance there, centred on the
pointer. Clicking a tile places one in the middle of the view.
- New command `instance.place {component, x, y}` (page point; drops into the frame or auto layout
slot under it; never inside its own main) for the CLI, control channel and MCP.
- The auto layout drop slot is shared with moving layers (`drop::auto_layout_slot`).
- Creating a component from a single shape names it after the shape.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Search toggle in the Layers header: a live filter that lists matching layers across the page.
- Shift-click selects the visible rows from the anchor to the clicked row (Cmd adds them to the
selection); Cmd-click toggles.
- The tree reveals a new selection once (expands its ancestors and scrolls its row into view),
so Collapse layers (Alt+L) and manual collapsing stick.
- Alt-click a disclosure triangle expands or collapses everything inside.
- Tab / Shift+Tab while renaming commits and renames the next / previous layer.
- Control channel: `ui.set {layerFilter, collapseLayers}`.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Min/max width/height: the W/H sizing menus add min/max (at the current size) and remove them;
limit rows edit or add the missing bound. The layout clamps Fixed, Hug and Fill children and
the frame itself; a hugging text wider than its max width wraps at the max. min > max is kept
ordered by node.set (the other bound follows); in the layout min wins.
- Ignore auto layout (absolute position): a toggle in the Position section; the child stays
where it is, can't Fill, shows constraints and follows them when the auto layout frame resizes
(by hugging, filling or a direct resize).
- Text baseline alignment for horizontal layouts ("Align text baselines"; crossAlign baseline):
first-line baselines line up, other layers align to the top, nested auto layout frames use
their first child's baseline. Measure gains text_baseline.
- Grid auto layout: column/row tracks (fixed px, hug, fr fill) via columnTracks / rowTracks or
layout.setTrack, cells anchored by gridColumn / gridRow with gridColumnSpan / gridRowSpan,
auto placement in reading order, rows grow to fit, hugging grids size to their tracks.
Panel: column/row gap, track size rows, a child's cell and spans. Old files load unchanged
(serde defaults; missing tracks are 1fr).
- Canvas handles: wrapped-row gaps and grid column/row gaps are draggable; a selected grid's
tracks are outlined.
- The layout section moved from right.rs to layout_panel.rs; inspect reports min/max and a
grid child's resolved gridCell; docs list the new props.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Edit-mode toolbar (Move V, Pen P, Bend hold Cmd, Paint bucket Shift+B, Lasso Q, Cut X, Done)
replaces the tools while a vector is edited.
- Pen in edit mode continues open ends, branches from any point (vector networks), adds points on
segments and closes paths into fill regions; dragging pulls out mirrored handles.
- Paint bucket fills or unfills any closed area: faces of the network (half-edge walk, nested
pieces become holes), with a hatched preview of the area under the pointer.
- Bend tool / Cmd: bend segments, pull handles out of a point, click a smooth point to sharpen it.
- Lasso and marquee point selection, segment selection (shift adds), cut at a segment or a point.
- Per-point handle mirroring (none / angle / angle and length, inferred for existing files),
Alt-drag breaks it; smooth/sharp points; join and merge selected points (Cmd+J, Shift+Cmd+J).
- Delete opens the path, Shift+Delete heals it; arrows nudge points; Cmd+A selects all points.
- Design panel shows the selected points' position, corner radius and mirroring.
- Every gesture is one undo step; edit mode ends when its layer is undone away.
- Paths that end on their start without a ClosePath (ellipse outlines) stay closed and filled.
- New commands (vector.edit.tool/pen/paint/cut/lasso/mirroring/smooth/position/nudge/connect/...)
for the CLI, control channel and MCP; docs updated.
- Clippy: simplify a few boolean expressions flagged by the current toolchain.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Without craft-fonts (CI) the overridden text keeps its size, so the old guard (`width > 0`)
asserted a hug that could never happen and failed main's CI.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>