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.
Found while testing crates/engine/src/codegen.rs:122-150 (lengths_to_rem calls num, which rounds to 2 decimals, at :112)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A layer with individual stroke sides (a header with only a bottom divider,
strokeSides [0, 0, 1, 0]) came out of the CSS, HTML, React and Leptos
code as `border: 1px solid ...` on all four sides, because the border
was written from the stroke weight alone. With individual sides it is now
a border-top/right/bottom/left for each side that has a weight, and a
side bound to a variable names it (border-top: var(--border-top, 3px)).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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.
A behaviour suite for export and inspect, written from public documentation of design tools'
export settings and developer handoff modes (`crates/engine/src/export_behaviour_tests.rs`,
`crates/engine/src/inspect_behaviour_tests.rs`: 19 tests, 139 table rows and asserts, each with a
one-line derivation; rows where the docs are silent say so and pin today's behaviour). 47 cases
failed on `main`; this PR fixes them in shared code.
**Fixes**
- Export bounds are what a layer paints (`Document::world_render_bounds`, used by every export):
a drop shadow adds room only in the direction it falls (it was a symmetric margin of blur +
largest offset on every side); strokes on rectangles, frames and ellipses reach their real width
(the mitre factor made a centre stroke of 4 add 8 a side); hidden children take no room; a mask
group exports the mask's area.
- Slices export everything visible in their area (they exported an empty image).
- Slashes in layer names export into folders (`icons/close` → `icons/close.png`, folders created;
`.` and `..` segments dropped).
- Copy as PNG copies the whole selection (only the first layer was copied), now from shared code
(`cmd::file::copy_png`).
- Inspect positions are measured from the frame around a layer; groups aren't frames.
**Added (documented options that were missing)**
- Export settings: contents only (off draws every layer showing in the area) and include bounding
box (the layer's own box); SVG: outline text (off writes `<text>` with a `<tspan>` per line),
id attributes (unique), simplified strokes (stroke attributes for centred strokes, and for inside
and outside strokes on boxes and ellipses). Stored per export setting, taken by `file.export`,
`export.add` / `export.update`, `file.exportLayers` and MCP `export`; a "…" popover on each row of
the Export section.
- Inspect: colour formats hex / RGB / CSS / HSL / HSB (`inspect.properties {colorFormat}`, a Color
format dropdown), a Blend row, CSS in rem (`inspect.code {unit, rootFontSize}`, px/rem dropdown),
CSS for rotation, blend modes, gradient directions and kinds (linear angle, radial, conic), text
alignment, case and decoration; icons and images in the asset list (`kind`), exported with
`file.exportLayers {assets: true}`.