Files
figcraft/docs/dev-mode-parity.md
T
Trevor BrownandClaude Sonnet 5.5 e276b1e4ff M10: SwiftUI, Compose and Flutter code export writes every colour and gradient fill, in paint order (#2271)
Part of #2271. The native targets took ONE colour from a layer (`first_fill`: the bottom visible solid, or a gradient's first stop) and dropped every other fill, so a frame with white, a gradient and 50% black came out as flat white.

Now SwiftUI, Compose and Flutter write every visible solid and gradient fill of a frame, group, rectangle or ellipse, in paint order, and a gradient as a gradient. A shared `visible_fills` gives each target the same list (document order, bottom first); each target only formats it.

- SwiftUI: chained `.background(...)`, top fill first, since each one goes behind the ones before it. A rectangle or ellipse fills with the bottom paint and lays a copy of the shape over it (`.overlay`) for each paint above, bottom first.
- Compose: chained `Modifier.background(...)`, bottom fill first, since each one draws over the ones before it. A linear gradient snaps to one of eight directions (a modifier doesn't know pixels); radial and angular ones sit at the box's centre.
- Flutter: a `BoxDecoration` paints one colour or gradient, so the Container keeps the top fill and each fill under it becomes a `DecoratedBox` around the widget, the bottom fill outermost. The drop shadow goes on the outermost box so it never lies over a lower fill. An instance's call is now made once, in `flutter_placed`.

A layer with one solid fill gives the same code as before, variable names included (only the paint at index 0, when solid, takes the `fill` variable's name). A lone gradient used to come out as its first stop's colour and is now a gradient.

Still not written: image and pattern fills in the native targets, ArkUI (one background and one gradient per component, so a stack needs extra nodes) and text with several fills. Figma's iOS and Android output was not measured: the order follows how each platform paints.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Model-Effort: Opus 5.5 drove; Fable 5.2 briefed; Sonnet 5.5 wrote at medium
2026-11-12 17:04:23 -05:00

18 KiB
Raw Blame History

Dev Mode parity

Last reviewed: 2026-10-24 · Last updated: 2026-11-12 · Change: patch (SwiftUI, Compose and Flutter code write every visible colour and gradient fill of a frame, group, rectangle or ellipse in paint order, gradients as gradients, instead of one colour per layer (chained .background in SwiftUI, chained Modifier.background in Compose, a DecoratedBox for each fill under the top one in Flutter); image and pattern fills, ArkUI and text still take one colour (#2271); the Annotation tool (Shift+T, tool.annotation {id?}): with a layer selected (or an id) it asks the UI for that layer's note at once, and in the tool a press on a layer selects it and asks for its note, a press on empty canvas does nothing; the Inspect tab then opens (never toggles away) with the caret in that layer's new-note field, and the engine never writes the note itself (#1840); the handoff mode reads Developer Mode in the interface, its ids and saved fields unchanged; class names transliterate non-ASCII layer names (#1235); measurements kept on the canvas; a read-only layers panel in Dev Mode; earlier: import lines in generated layer trees; compare with any saved version; Dev Mode behaviour suite: an inspect-only canvas, Changed in the ready list, statuses only on frames and sections; plugin panels from the Inspect tab) · Target: Figma Design Dev Mode (desktop app 126.10.7)

Breadth ~98%, ready ~78% (estimated). The inspect surface is broad, components connect to the code components of a repository (with their imports, also inside generated layer trees), changes compare with any saved version, the canvas and the layers panel only inspect in Dev Mode, and measurements stay on the canvas for everyone. Left: publishing mappings from the code repository, third-party integrations. Remaining: 1–2 h.

Feature Status Notes
2026-11-12 patch SwiftUI, Compose and Flutter code export kept one colour per layer (the bottom visible solid, or a gradient's first stop) and dropped every other fill (#2271). Now each writes every visible solid and gradient fill of a frame, group, rectangle or ellipse, in paint order, and a gradient as a gradient. SwiftUI chains .background(...) with the top fill first, since each one goes behind the ones before it (a rectangle or ellipse fills with the bottom paint and lays a copy of the shape over it for each paint above). Compose chains Modifier.background(...) with the bottom fill first, since each one draws over the ones before it (a linear gradient snaps to eight directions, radial and angular ones sit at the box's centre). Flutter keeps the top fill in the Container and wraps each fill under it in a DecoratedBox, the bottom fill outermost and the drop shadow on the outermost box. A layer with one solid fill gives the same code as before; a lone gradient is now a gradient, not its first stop's colour. Still not written: image and pattern fills in the native targets, ArkUI (one background and one gradient per component, so a stack needs extra nodes) and text with several fills. The incumbent's iOS and Android output wasn't measured: the order follows how each platform paints. Percentages and hours unchanged
2026-11-07 patch Naming: the handoff mode is called Developer Mode in the interface, its shortcut row, its toasts, the control channel's refusal, the board refusal, the measurement parameter doc, the .fig export note, the CLI help and every catalog (24 languages, translations awaiting native review), since Figma holds a US registration for Dev Mode (7574348); command ids (view.devMode, devMode.*), preference keys and saved-document fields are unchanged, and the Actions palette still finds the command by the old name
Dev Mode toggle (Shift+D), Inspect panel done the canvas only inspects: presses select, pan and comment but never move, resize, draw or edit text and vectors; menus and shortcuts refuse design edits with a note (Dev Mode's annotations, statuses, dev resources, measurements, code connections and comments still work); a drawing tool chosen in the toolbar goes back to the design. The layers panel only selects and expands (no renaming, reordering, hiding or locking; a double click says why), and the Pages list only opens pages (no adding, renaming, reordering, duplicating or deleting). Files dropped on the canvas aren't imported (a dropped document still opens as a file of its own), and the Assets panel places, swaps and applies nothing, doesn't change Hide when publishing, and doesn't accept library updates or add, remove or swap libraries; each says why
Properties with their styles and variables; redlines on hover done positions from the frame around the layer (groups aren't frames), rotation, radius (CSS corner order), opacity, blend; colours in hex, RGB, CSS, HSL or HSB (inspect.properties {colorFormat}, the Inspect tab's Color format)
Code: CSS, iOS (SwiftUI), Android (Compose), Flutter done plus HTML, React, Vue and Leptos (ours; Leptos: #[component] functions with #[prop] defaults, variant enums and view! markup; Flutter is ours too: a StatelessWidget for a component, a widget tree for a layer); components take properties as parameters, instances are calls; CSS in px or rem (inspect.code {unit, rootFontSize}), with rotation, blend modes, gradient directions and kinds (linear, radial, conic), text alignment, case and decoration; class names from layer names transliterate non-ASCII scripts into readable ASCII (로그인 화면 → rogeuin-hwamyeon, кнопка → knopka, über → uber; symbols-only names take the node's kind, .text), and the 40-character cut lands on a word boundary (#1235); SwiftUI, Compose and Flutter write every colour and gradient fill of a layer in paint order, gradients as gradients (chained .background in SwiftUI, top fill first; chained Modifier.background in Compose, bottom fill first, a linear gradient snapped to eight directions and radial and angular ones at the box's centre; in Flutter a DecoratedBox for each fill under the top one); the native targets don't write image or pattern fills yet, and ArkUI and text still take one colour (#2271)
Assets list with export done layers with export settings, icons (small, vector shapes only: SVG) and images (PNG); the icon rule is ours
Annotations: pinned properties, categories, coloured callouts, filter done the Annotation tool (Shift+T, tool.annotation) picks a layer by a click or from the selection and opens its note field in the Inspect tab. Figma's placement, cursor and whether the tool stays selected are unmeasured (Developer Mode is paywalled in the web app); staying selected until V or Escape, like the Comment tool, is inferred, and there is no toolbar button or icon yet
Ready for dev list, statuses, "Changed" after edits done the list filters Ready for dev, Changed, Completed or any, and badges changed ones (inspect.readyForDev {status: changed}, changed per item); only sections and frames at the top of a page or section take a status (others are refused)
Focus view (one frame, previous / next) done
Compare changes (before/after side by side or overlaid, property diffs) done against the snapshot taken when a frame was first changed after it was marked ready, or any saved version (dev.compare {version}; the view's Compare with menu); a frame that isn't ready for dev compares with versions
Code connect (map components to repository code) done per language (React, Leptos, Vue, SwiftUI, Compose, ArkUI, Flutter): import path, name and a template with {property} and {property ? "a" : "b"} placeholders; instances (also inside generated layer trees) show the snippet with their values; edited in the Inspect panel with a live preview; figcraft.connect.json export/import; generated layer trees bring the import lines of the connected components they call (also in definitions, slots and swapped-in instances) once each at the top, per language. figcraft-cli connect publish publishes a repository's mappings to a design file (--dry-run to check them first)
Plugins in Dev Mode (Jira, GitHub, etc.) partial FigCraft's own WASM plugins that only read run from the Inspect tab, with their panels (callbacks can only read); no third-party integrations
Measurements (redlines on hover, Alt in the design) done distances between the selection and the hovered layer, its parent or a guide, auto layout padding and gaps (Shift+Alt); measurements kept on the canvas (devMode.addMeasurement between two layers, along a side or to the layer around it; stored on the layer, saved, synced, undoable) drawn in Dev Mode as labelled redlines that follow the layers, listed, placed and removed in the Inspect tab; a measurement whose other layer was deleted isn't drawn until an undo brings it back
VS Code extension out of scope FigCraft's MCP server and control channel already serve design data, code and connected snippets to agents and editors
Dev resources links on layers done a URL and a name per link on any layer (devResource.*, saved, synced, undoable); instances list their main component's (and its set's) read-only; URLs checked as http/https and never fetched (no page titles: a link without a name is named after its URL); the Inspect tab lists, adds, edits and removes them; get_node returns them

Revision history

Date Change Summary
2026-11-09 patch The Annotation tool (Shift+T, tool.annotation {id?}): selects the layer pressed on (or the selection, or id) and asks the UI for its note, which the Inspect tab takes in its new-note field; the engine writes nothing itself, and Developer Mode lets the tool through its gates (#1840). Figma's placement, cursor and whether the tool stays selected are unmeasured (Developer Mode is paywalled in the web app); staying selected until V or Escape, like the Comment tool, is inferred, and there is no toolbar button or icon yet
2026-10-30 minor Gap 16: figcraft-cli connect publish <file.figcraft> [--mappings figcraft.connect.json] [--dry-run] publishes a code repository's mappings to a design file (as the incumbent's Code Connect CLI publishes a repository's mappings): it links each entry's component or set by name, warns about and skips links that don't fit, saves the file in place through the engine's save (or not, with --dry-run) and fails when nothing links. Dev Mode breadth (~98%) and ready (~78%) unchanged at this rounding; hours unchanged
2026-10-26 minor Class names from layer names transliterate non-ASCII scripts (#1235): class_name runs names through deunicode (new dependency: pure Rust tables, zero deps, BSD-3-Clause), so Korean, Cyrillic, Latin-diacritic and CJK names become readable ASCII classes in HTML/CSS/React/Leptos output, component roots and data-* variant attributes (rogeuin-hwamyeon, knopka, uber, an-niu; before, every one of them came out layer-); symbols-only names take the node's kind (.text, .frame) instead of layer-, and the 40-character cut lands on a word boundary. ASCII-only names are unchanged. html_codegen_tests updated (its diacritics expectation improves from layer-1-n-code-name to layer-1-unicode-name) and extended. Percentages and hours unchanged
2026-10-24 minor Dev Mode refuses design edits from files dropped on the canvas (a dropped document still opens as a file of its own, and the drop overlay shows the Dev Mode note for other files), from the Pages list's add, rename, reorder, duplicate and delete, and from the Assets panel's place, drag, Alt-drop swap, style, Hide when publishing, Update, and adding, removing and swapping libraries. Percentages and hours unchanged
2026-10-24 minor Measurements kept on the canvas (gap 16): devMode.addMeasurement {from?, to?, side?} places one between two layers, along one side of a layer or (neither) to the layer around it, stored on the layer it starts from (saved in .figcraft, synced, undoable; old files open without them); Dev Mode draws them as labelled redlines that follow the layers, the Inspect tab lists them (those of the selected layer, or every one on the page), places and removes them; devMode.removeMeasurement and devMode.measurements remove and list them; a copy keeps those to layers copied with it. The layers panel is read-only in Dev Mode: rows select and expand, but don't rename, reorder, hide or lock. Breadth 97% → 98%, ready 76% → 78%, 2–4 h → 1–2 h
2026-10-16 minor Flutter code generation (ours: the incumbent generates CSS, iOS and Android only): inspect.code {language: flutter}, with dart as an alias, writes a Dart widget tree for a layer — Container for a frame's box (size, padding, BoxDecoration with colour, radius, border and shadows), Row / Column for auto layout (spacing, crossAxisAlignment, textBaseline; stretch where a child fills), Expanded for a child that fills the main axis, Stack + Positioned for free children, Text with TextStyle (family, size, weight, style, height, letter spacing, decoration, alignment), Image.asset for images, Opacity for layer opacity, SizedBox for a capped subtree — and a StatelessWidget for a component (properties become constructor parameters with their defaults, instances are calls passing only what they change, variants become a Dart enum, swaps a Widget and slots a List<Widget>?). Code connect takes flutter too, with a Dart import '…'; line. The Inspect panel's language picker (which iterates CodeLang::ALL) and the Connect to code note list it, and the 13 UI catalogues follow. Dev Mode breadth (~96%) and ready for real work (~72%) unchanged at this rounding; hours unchanged
2026-10-15 minor Dev Mode tail (gap 16): generated layer trees (React, Leptos, SwiftUI, Compose) start with the import lines of the connected components they call, once each, also those in a component's definition, in slots an instance fills and placed by a connected template's instance swaps; Compare changes takes any saved version as the before (dev.compare {version}, the view's Compare with menu; frames that aren't ready for dev compare with versions). Behaviour suite (5 tests; 4 failed before the fixes): the Dev Mode canvas only inspects (no moving, resizing, drawing, text or vector editing; menus, shortcuts, nudges, opacity keys and paste refuse design edits with a note; toolbar drawing tools go back to the design), the ready list filters and badges Changed items, statuses are refused on layers inside frames. Read-only plugins run from the Inspect tab show their panels (the runtime's panels, reused). Breadth 96% → 97% (measurements kept on the canvas and the read-only layers panel counted as missing), ready 72% → 76%, 0–1 h → 2–4 h
2026-10-12 minor Dev resources (gap 16): links for developers (a URL and a name) on any layer: devResource.add / edit / remove / list, undoable, saved and synced; an instance also lists its main component's (and that component's set's) links read-only with where they come from, and layers inside an instance show their main layer's; URLs are checked as web links (http or https, a host, no spaces, at most 2,048 characters) and never fetched (a link added without a name is named after its URL), opened only when clicked; the Inspect tab lists, adds, edits and removes them; get_node and inspect_document return them as devResources. The editor extension stays out of scope (the MCP server serves agents and editors). Dev Mode breadth 93% → 96%, ready 70% → 72%, 1–3 h → 0–1 h; feature breadth (~82%) and ready for real work (~51%) unchanged at this rounding; full parity ~304–593 h → ~303–591 h
2026-10-11 minor Export and inspect behaviour suite written from the public documentation (139 cases in 19 tests; 47 failed before the fixes) and its fixes: an export covers what the layer paints, so a drop shadow adds room only where it falls, strokes on boxes and ellipses reach their real width (they added up to four times as much), hidden layers take no room and a mask group exports the mask's area; a slice exports everything under it; slashes in layer names export into folders; Copy as PNG copies the whole selection. Added: the export settings' contents only and include bounding box, and for SVG outline text (off writes <text>), unique id attributes and simplified strokes (stroke attributes where exact); Inspect colour formats (hex, RGB, CSS, HSL, HSB), CSS in rem, a Blend row, positions measured from the frame around a layer (groups aren't frames), CSS for rotation, blend modes, gradient directions and kinds, text alignment, case and decoration; icons and images in the asset list (file.exportLayers {assets}). Dev Mode breadth 90% → 93%, ready 64% → 70%, hours unchanged (1–3, dev resources); feature breadth and ready for real work unchanged at this rounding
2026-10-11 minor Code connect and Dev Mode plugins (gap 16): a component or set links to its code component per language (import path, name, a template with {property} and {property ? "a" : "b"} placeholders); inspect.code, inspect.componentCode and the Inspect panel show that snippet with the instance's values (also inside generated layer trees), edited in Connect to code with a live preview; links are saved, synced and undoable, and travel as figcraft.connect.json (codeConnect.export / codeConnect.import). Plugins that only read run from the Inspect tab. Dev Mode breadth 80% → 90%, ready 62% → 64%, 4–8 h → 1–3 h; feature breadth (~79%) and ready for real work (~47%) unchanged; full parity ~380–725 h → ~375–720 h
2026-10-10 major Created from the 2026-10-10 re-measure