Commit Graph
60 Commits
Author SHA1 Message Date
Trevor BrownandClaude Fable 5.1 fd1a78bbb8 Local variables: the docs say bulk actions skip inherited variables (#2002)
Delete, Move to group and Scope act on the selected local variables; inherited ones are left out and the bulk menu needs two or more local variables selected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 21:41:58 -05:00
Trevor BrownandClaude Fable 5.1 47c5796ed5 Merge upstream/main into feat/variables-multi-select-2002
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 21:39:51 -05:00
bflatastic 3ebd14ad2d Merge main into #1876 2026-11-07 09:10:47 +07:00
bflatastic 48fbffd25b Merge main into #2025 2026-11-07 08:57:50 +07:00
Trevor BrownandClaude Fable 5.1 1ba0e1d62a Merge upstream/main into feat/text-style-paragraph
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 20:40:40 -05:00
Trevor BrownandClaude Fable 5.1 7aad58cf2d Merge upstream/main into feat/variables-multi-select-2002
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 20:40:15 -05:00
bflatastic 7c3a3b2703 Merge main into #2055 2026-11-07 08:07:48 +07:00
Oleksii Sukholita 104aa3deb1 M3: a glass's bound light angle turns round past ±180 instead of stopping
`Glass::set_bound` clamped a bound light angle to ±180, so a variable of
270 drew the light from 180, a quarter turn off, while a typed 270 draws
from -90. A bound angle now turns round into -180–180 (270 is -90, -0 is
0), the same direction the angle draws in.
2026-11-06 19:39:12 +01:00
Oleksii Sukholita 5bdedc3930 M3: a layer binds an effect's field only when its effect has it
`variable.bind` on a layer checked only the variable's type against the
field's scope, so `effects/0/refraction` on a drop shadow, or a binding on
an effect the layer doesn't have (`effects/9/radius`), was kept and did
nothing. An effect style already refuses both. The layer now uses the same
check: effect `i` must exist and have the field, and the field's type
comes from that effect. A binding an older file has on such a field still
does nothing, and still unbinds.
2026-11-06 19:38:52 +01:00
Denis GomesandClaude Opus 5.5 76af4e3f43 M3: Go to main component opens the library file of a library component
On an instance of a library component the command refused with "open
that file to edit it". The help center describes it opening the file that
holds the main ("Edit main components"), so it now does: the linked
library file opens in a tab, or its open tab comes to the front, on the
main's page with the main selected and zoomed to.

The library keeps the file's layer ids, so the copy's id map
(ImportedComponent::ids) leads from the local copy back to the exact
layer: a nested instance reaches its own main and a variant that variant.

The new tab takes the canvas size of the tab it came from. A tab still at
the default 1200x800 is refitted to the whole page on its first frame,
which would replace the zoom to the main.

A main deleted from the library file since it was published leaves the
file open with a note. A moved library file, a team library on a server
and a path off this computer are refused before anything opens; the last
uses the same check that guards library reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 02:21:20 -05:00
Trevor BrownandClaude Fable 5.1 2e331ecd70 Merge upstream/main into feat/text-style-paragraph
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-05 16:45:00 -05:00
Trevor BrownandClaude Fable 5.1 527307178e Merge upstream/main into feat/variables-multi-select-2002
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-05 16:41:32 -05:00
8da11395e9 M3: glass effects take variables, and their frost stays within 0–100 (#1886)
* M3: glass effects take variables, and their frost stays within 0–100 (#1885)

Glass's light angle, intensity, refraction, depth, dispersion, frost and
splay bind to number variables, on a layer (`variable.bind`,
`effects/<i>/<field>`, `radius` being the frost) and in an effect style
(`style.bind`), and the Design panel's glass fields take a variable like
the shadow and blur fields. A variable reads as the field shows it:
intensity, refraction, dispersion and splay in percent.

A glass's frost stays within 0–100, the reference tool's range: a larger
frost from an older file draws at 100, and the field, a bound variable and
scaling stop there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M3: a frost from an older file scales and shows as it draws (#1885)

Review follow-up: a frost saved past 100 draws at 100, so scaling a layer
starts from that (150 halved draws at 50, not 75) and the Design panel's
Frost field shows it. The revision row's binding path escapes its pipes, so
the table keeps three columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M3: Dev Mode and .fig export show a frost from an older file as it draws (#1885)

A frost saved before the 0–100 range draws at 100, and the canvas, CSS and
scaling already read it that way, but Dev Mode's Glass row and the .fig
export still read the stored number, so they said 150 where the canvas
shows 100. Both now read the sanitized frost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Oleksii Sukholita <oleksii.sukholita@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-05 22:08:15 +01:00
Trevor BrownandClaude Fable 5.1 4fcb95eeab M3: the Local variables window selects several variables with Shift and Cmd/Ctrl, and acts on them at once (#2002)
The window keeps a selection of variable ids: a click selects one row, Shift+click the range from the anchor through the rows shown, Cmd/Ctrl+click toggles a row, and Escape or a click on the empty space clears it. Selected rows use the sidebar's selected-row colour.

Right-clicking a row that is part of a selection of several offers Delete, Move to group and Scope for all of them, each sent as one command.batch so it is one undo step. Rename and value edits stay single-row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-05 15:54:11 -05:00
Trevor BrownandClaude Fable 5.1 695ca07528 M3: text styles carry paragraph spacing and indent, the list style and the OpenType features (#1829)
ParagraphStyle beside TextStyle in StyleKind::Text: style.create takes it from the layer or a value, style.apply sets it (the list on the paragraphs touched for a range), style.update moves it, editing it unlinks, .fig files keep it with the style; tests in styles_behaviour_tests and fig_export_assets_tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:43:02 -05:00
Trevor BrownandClaude Fable 5.1 65bd00f739 M3: Delete on a layer inside an instance hides it instead of being refused (#1834)
* M3: Delete on a layer inside an instance hides it instead of being refused

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* The components structure test: Delete inside an instance hides

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 23:49:58 -05:00
BFlat 87254913af M3: library analytics list how each style and variable is used (#1730)
* M3: library analytics list how each style and variable is used

A file reports how many of its layers use each of a team library's styles and
variables, and Show usage lists them by a Type choice with their layers and
files. Layers in instances count for their component. Also ignores plugins/target.

* M3: the style analytics test waits for the instance report too
2026-10-31 09:13:08 +07:00
BFlat 2e92d36af0 M6: SwiftUI and Compose code name a bound variable by its code syntax (#1716)
Bound variables with an iOS or Android code syntax are named in generated SwiftUI and Compose in place of their value: paints, stroke weight, sizes, corner radius, gaps, padding, opacity and text. A variable with none for the platform keeps the value.
2026-10-31 06:49:41 +07:00
BFlat 2d784c363d M3: library analytics count insertions and show them over time (#1704)
Gap 10: a file counts the library instances it places, creates, pastes or duplicates and reports them like its detaches. The server counts what a file's totals grow past their highest in the current week and keeps a year of weeks (actions.json). library.usage {days} and Show usage give insertions and detachments over the past 30 days, 90 days or year. New variants are appended; an older server refuses them and Show usage says so.
2026-10-31 04:13:14 +07:00
BFlat bbd1188c81 M3: a collection of this file can be extended (#1679)
Gap 14: variable.extendCollection and Extend collection on a collection's row in the Variables editor. Extensions follow edits of the collection they extend at once (normalising keeps them in step whenever variables change), extensions extend again, and a loop of extensions from a hostile file comes apart into plain collections.
2026-10-31 02:21:53 +07:00
BFlat be6a7af25f M3: team library analytics count detached instances (#1668)
* M3: team library analytics count detached instances

Detaching an instance of a library component counts it in the file (libraries.detached, saved with it, capped). The background library check reports the counts after its fetch (ReportDetached); the server keeps them per file beside the file's usage report in usage.json (same caps) and answers Detached with them. Show usage and library.usage list detaches per component and per file (detached, detachesKnown). The new request and reply variants are appended, so every older library message keeps its bytes; an older server refuses the new requests, which the app ignores. Gap 10.

* M3: the detach test waits for the instance count too (the two arrive in separate reports)
2026-10-30 23:29:18 +07:00
BFlat e4a7e89f39 M3: typed and stored variable, collection and mode ids find exactly that object (#1613) (#1635)
Builds on #1618. Variables::get and Variables::collection take an exact id before a name; stored mode ids go through Collection::mode_by_id (expressions, the prototype's mode switch); font family variables and the colour picker, prototype editor and panels read stored ids exactly. Ported from fix/stored-ids-exact.
2026-10-30 18:00:34 +07:00
Christopher Linnett bc76665c65 M3: colour variables can be calculated from others, faded or mixed per mode (#1280)
A colour variable's value in a mode can now be {"alpha": {"variable", "opacity"}} - that
variable's colour with its alpha replaced - or {"mix": {"from", "to", "t"}}, a per-channel blend
of two colour variables or literals over the stored values (in the document's colour profile),
alpha included. Calculations resolve lazily like aliases, so bound layers, mode switches,
instances and undo follow their sources; they compose, and a loop, or a calculation that reads
more than 32 variables in its collection's modes (other collections in their default modes), is
refused when it is set. Operands are stored by id and read by exact id, as stored references
are. Files keep them; REST, CSS, Penpot and .fig exports write the colour they show in each mode;
deleting a source leaves its readers on the colour they showed; library import and swap
re-point their operands. The Variables panel shows them with their formula and can swap one for
an alias or detach it to its colour.
2026-10-30 16:25:26 +08:00
bflatastic ad8bad3cc3 Merge main into #1618 2026-10-30 13:35:24 +07:00
BFlat 47e9e0a50d M8: a component set's property rows fit the 240 px right panel (#1629) 2026-10-30 13:32:36 +07:00
bflatastic b157a38e99 Merge main into #1618 2026-10-30 13:05:24 +07:00
BFlat 7b886404c3 M8: Opening a file contacts no team library server it names until you check it (#1621)
* M8: opening a file contacts no team library server it names until you check it (#1097)

* M8: translate the team library check strings (#1097)

* M8: the Assets panel library test checks what allows a library, without its own fake link (Sonar duplication and coverage)
2026-10-30 13:03:31 +07:00
2f47014917 M3: stored variable and collection references keep their identity (#1613)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Co-Authored-By: GPT-6.1 Terra <noreply@openai.com>
2026-10-30 06:49:06 +03:00
9093afac10 M8: an explicit variables file is refused past 256 MiB or when it isn't a regular file (part of #1114)
variable.import and the variables parameter of file.open and file.import read an explicit path
with the uncapped reader, while the sibling variables.json next to a REST file already went
through read_capped with MAX_VARIABLES_FILE. variables_param now uses that capped read for paths
on native builds; the browser keeps its no-file-system error. Inline objects and JSON text, and
access roots, are unchanged. The check reads the size before the file, so it does not bound a
file that grows or is replaced in between. Primary file reads and image.place stay uncapped.
variable.import's help and docs/mcp.md name the limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
2026-10-30 05:31:20 +03:00
BFlat 7d76b0c673 M3: component properties keep their order on rename; Make slot asks for the name (#1536, #1537) (#1565)
Properties were listed by name, so a rename moved one. Components now keep prop_order (the names as they were made; a rename keeps the place) and every listing follows it. Make slot opens a name field under the frame's Slot row; Enter makes the slot, Escape cancels, a taken name keeps the field open.
2026-10-30 04:49:00 +07:00
BFlat 0d9c2c3f73 M3: components that would contain themselves are refused, and loops in files are repaired (#1084) (#1540)
* M2: components can't contain their own instances (#1084)

Edits that would make a component hold an instance of itself, directly, through other components or through another variant of its set, are refused with the loop named. Files holding such a loop open with one instance per loop detached and a note; a rebuild never expands an instance inside a copy of itself, so a loop merged in a live session stops growing.

* M2: a variant may hold a sibling variant's instance; only true loops are refused (#1084)

Each variant counts as its own component: refusals and open-time repairs apply only to a component leading back to itself through instances (an instance-swap value included), so files with nested sibling variants open unchanged. sync_instance_depth is split into match_layers and copy_layers.
2026-10-29 21:05:32 +07:00
BFlat 3efb2699b6 M3: the instance-swap menus have a search field that filters as you type (#1338) (#1527) 2026-10-29 19:00:15 +07:00
叶林橦 27d5d99e56 M7: land Flutter code generation on main's code targets
main split `codegen.rs` into `dev/codegen.rs` (the shell: `CodeLang`,
`code_for`, `Ctx`, the shared helpers) and one module per native target
(`css`, `html`, `swift`, `compose`, `arkui`), and added Vue and ArkUI from
other PRs. Flutter arrives as the tenth target, in its own module:
`dev/codegen/flutter.rs`, wired from `codegen.rs` (`CodeLang::Flutter`, `dart`
as an alias, rem left to the web targets), with `flutter_definition` /
`flutter_type` / `flutter_default` beside main's `arkui_definition`.

Every language list in the tree is a union of the two sides now: the
`inspect.code` and `inspect.componentCode` specs, `codeConnect` (parse, import
line, expression, template, path checks, messages), the playground CSS/HTML
assets, the 21 UI catalogues (main added eight after this branch: bn, fi, it,
ka, nl, pl, th, vi, each with Flutter in its own conjunction) and the docs.
Flutter is an extra code target; nothing main gained is dropped.

The reviewer's absolute-child bug (#1382-1403: a child that ignores its auto
layout parent fell through to `Place::Flow`) is fixed in `flutter_children`:
flow and absolute children are split, the absolute ones go in a `Stack` around
the Row/Column at the parent's own box (so their offsets stay the parent's and
the padding moves onto the line instead of insetting them), the line fills that
Stack (`Positioned.fill`, so `Expanded` still has a bounded width), and only
flow children now decide `CrossAxisAlignment.stretch` and `Expanded` — the
absolute child no longer joins the line's spacing or loses its offsets.

`flutter_lifts_a_child_that_ignores_its_auto_layout_parent_out_of_the_line`
covers both kinds of child in one auto-layout frame: a 50×20 flow child and a
30×30 absolute one asking for `fill` on both axes, in a parent with 4 pt
padding. It checks the offset, that the absolute child is out of the Row, that
no `Expanded`/stretch comes from it, and that the padding insets the line only.

ROADMAP and dev-mode-parity keep main's header (2026-10-26) and take this
branch's history row in date order (2026-10-16).

`cargo test`: engine 1391 pass, ui-egui 814 + 8 + 1 pass; fmt and clippy clean.
The one engine failure, `board_tables_tests::a_double_click…`, fails the same
way on a pristine `origin/main` here: this sandbox has no `craft-fonts`
checkout, so its text doesn't wrap and the row can't grow.
2026-10-27 11:30:27 +08:00
aiongg 60c671da66 docs: update source paths for the layout moves 2026-10-26 20:36:51 +00:00
BFlat 740cc2a2c6 Merge pull request #726 from ijashuzain/feat/variables-dart-export
M6: export variables as Dart constants for Flutter
2026-10-27 00:53:10 +07:00
bflatastic 6145409859 Merge main into #726
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/variables_io.rs
#	crates/engine/src/variables_io_tests.rs
#	docs/components-parity.md
#	docs/mcp.md
2026-10-26 23:28:15 +07:00
Ridzzz0Alam acdc714537 M3: an instance-swap property's default menu scrolls (#1338)
On a main component's Properties row, the default of an instance-swap
property opened a menu of every component in the file at full height, so
in a file with many components (an icon library) the bottom ones ran off
the screen and couldn't be picked. The menu now scrolls past 320 pt like
the instance's swap menu, is at least 200 pt wide, ticks the current
default and is named for assistive technology.
2026-10-26 10:29:49 +01:00
aiongg cdf37f9fc9 Merge #1284 2026-10-25 02:21:08 +00:00
dotCooCoo 6fa00af2be Docs: components parity status line for the text style variables 2026-10-24 07:32:51 -07:00
dotCooCoo 268c0ead11 M3: apply variables to a text layer's font family and font style
The Design panel's font family and font style menus offered no way to
bind a string variable; these bindings could be made only by commands
such as variable.bind. Each menu now starts with Apply variable, which
opens the variable picker on string variables for the selected text
layers. A bound family or style shows its variable's chip in place of
the menu. A click on the chip picks another variable, and its detach
button unbinds it. While a text is being edited (a caret or selected
characters), the menus have no Apply variable item and show the plain
dropdown. A pick applies to the selected characters, or to the whole
layer when no characters are selected, as before.
2026-10-24 06:08:17 -07:00
dotCooCoo 337f9d72e0 M3: bind variables to a text style's fields from the style editor
The style editor bound variables to paint colors, shadow colors, blur
radii and grid sizes, but a text style's fields took variables only
through style.bind. The font and font style menus now start with Apply
variable, which opens the picker on string variables, and the size, line
height and letter spacing fields show a variable button on hover for
number variables, as the Design panel's number fields do. A bound field
shows its variable's chip in place of the value, as in the Design
panel. A click on the chip picks another variable, and its detach
button unbinds it.
2026-10-24 04:11:03 -07:00
Marco Arnulfo af0b150736 M3: instance-swap properties in the Dev Mode component playground 2026-10-23 03:28:55 +02:00
RiceDee bcd5b6e632 #903: Combine as variants in the context menu and Design panel; Add variant on a lone component 2026-10-20 16:18:13 +11:00
bflatastic b82369201d M3: duplicating a main component makes an instance (#892)
Duplicate and Alt-drag copied a main component as a second main. Within
its file the copy is now an instance of it (components::copy_in_file,
shared by edit.duplicate and the Alt-drag copy), as Copy then Paste
already was. A variant duplicated in its set stays a new variant.
2026-10-20 05:44:46 +07:00
BFlat 018b8166b2 M3: a bound colour variable's alpha applies once, not squared (#802) (#804)
Fixes #802.

Fixes #802.
2026-10-18 18:50:19 +07:00
BFlat 8629a6f586 M3: variable.import reads variables outside meta and never imports nothing silently (#790) (#793)
Fixes #790.

Fixes #790.
2026-10-18 14:59:14 +07:00
BFlat c6d6433473 Docs: parity rows for the 2026-10-17 landings (#756)
Brings the progress docs up to date with what landed on 2026-10-17: community batches #727, #728, #733, #735, #736 and #753, plus #729, #745, #746 and #751. Docs only.
2026-10-17 21:56:26 +07:00
bflatastic 6cdfe2ab8e M3: layers inside instances keep their resizing overrides (#731)
A layer inside an instance can now override its resizing (hug, fill or
fixed, with min and max sizes; override group `resizing`) and its size.
They were refused, and imported ones were dropped at the first relayout.
Kept through rebuilds, undo, save, .fig and REST import, .fig export and
detach; reset per group and with Reset all changes. Position, constraints
and absolute or grid placement stay the main's.
2026-10-17 19:27:35 +07:00
Ijas Hussain 95b91e602b M6: export variables as Dart constants for Flutter
variable.export takes format "dart", and Local variables > Export...
offers "Dart constants for Flutter...". The file has an abstract final
class per collection and mode (the default mode named after the
collection, the others after the collection and the mode) with a
static const Color, double, String or bool per variable.

Names become camelCase Dart identifiers, kept apart when two read the
same, with reserved words escaped; strings are escaped for Dart;
aliases are resolved to their value in that mode, so each class stands
alone. A generated file passes the Flutter analyzer and its constants
work in const widgets.
2026-10-17 12:31:58 +05:30
叶林橦 e4562a0072 M7: Flutter code generation
- Code generation gains a Flutter target (ours; the incumbent generates CSS, iOS and Android only), which makes Dart the seventh language: `language: flutter` (with `dart` as an alias, so `CodeLang::from_id` maps both, `id()` is `flutter` and the label `Flutter`).

- `inspect.code` writes a Dart widget tree for the layer: a `Container` for a box, with its size, padding and a `BoxDecoration` (colour, radius, border, shadows); `Row` / `Column` for auto layout, with `spacing`, `crossAxisAlignment` and `textBaseline`, and `stretch` wherever a child fills the cross axis; `Expanded` for a child that fills the main axis; `Stack` with `Positioned` for free children; `Text` with a `TextStyle` (family, size, weight, style, height, letter spacing, decoration and alignment); `Image.asset` for images; `Opacity` for layer opacity; `SizedBox` for a capped subtree. Colours are `Color(0xAARRGGBB)` and `$` is escaped in string literals, as in Compose.

- `inspect.componentCode` writes a `StatelessWidget` for a component or set: properties become constructor parameters with their defaults, a variant property becomes a Dart enum (lowerCamel members, never a keyword), a boolean a `bool`, a text a `String`, a swap a `Widget` and a slot a `List<Widget>?`. Instances are calls that pass only what they change, and an instance's snippet puts the `import 'package:flutter/material.dart';` line before the call, since Dart's imports come first.

- Code connect takes Flutter too: `codeConnect.set {language: flutter}` stores an import path, a name and a template, and the snippet carries a Dart `import '…';` line. The default template passes named parameters (`Card(size: {size})`), as SwiftUI does, and import paths are checked as Dart imports (letters, digits, `_ . / : -`).

- The Inspect panel's language picker iterates `CodeLang::ALL`, so Flutter appears there and in the Connect to code note; the 13 UI catalogs and the component playground's language list follow.

- Docs: README, ROADMAP, docs/mcp.md, docs/gaps.md, docs/components-parity.md, docs/file-format-parity.md, docs/target-app-parity.md and docs/dev-mode-parity.md.

- Tests: Flutter cases in the codegen, component codegen and inspect behaviour suites.
2026-10-16 18:40:59 +08:00