# FigCraft Roadmap **Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents) > **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (boards: a board area row and progress entry; community fixes in #507, #510, #529 and #530: auto-save, a text box by dragging, batch step references, removing someone from a live session, Russian and Turkish catalogs reviewed, Czech and Indonesian drafts, the Japanese draft completed, zoom previews, the web clipboard, Alt-drag snapping and Cmd+D repeat, recovery copies, crash and rollback fixes, colour emoji, `.fig` import fixes; earlier changes are in the revision history below) · **Target:** Figma Design (desktop app 126.10.7) FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow, native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through the CLI, a JSON control channel and MCP. This page is the one-page summary; agents choosing work start with [`docs/gaps.md`](docs/gaps.md). ## Headline numbers | | Value | Kind | |---|---|---| | Menu/command breadth | **100%** (212 / 212) | measured, [`docs/parity-checklist.md`](docs/parity-checklist.md) (`cargo xtask parity`) | | Feature breadth | **~85%** (core editor ~94%) | estimated, [`docs/target-app-parity.md`](docs/target-app-parity.md) | | Ready for real work | **~53%** | estimated, same doc (written dimension weights and scores) | | Essentials user | **~59%** | estimated: core features 77.8% × 0.92 launch/stability × 0.94 discoverability × 0.88 opening files people send ([method](docs/target-app-parity.md#essentials-user)) | | Mainstream practitioner | **~50%** | estimated: weekly areas 72.7% × 0.9 interaction × 0.9 stability × 0.85 file exchange with Figma users ([method](docs/target-app-parity.md#mainstream-practitioner)) | | Remaining to beta | **~155–280 h** one agent | estimated | | Remaining to full parity | **~262–519 h** one agent (~83–145 h wall-clock with 5 agents) | estimated | ### Readiness by audience | Audience | Ready % | Opus 5.5 agent wall-clock hours to ~95% | Work that dominates | |---|---|---|---| | Full target (ready for real work) | ~53% | ~268–522 h | multiplayer (42–84), localization (29–51), plugins (8–26), the interaction pass (40–60), GPU canvas (30–60), AI (30–60), the `.fig` import's tail (5–10) | | Mainstream practitioner | ~50% | ~120–229 h | core editor depth incl. the `.fig` import's tail (49–98), the interaction pass (40–60), real-file corpus and platform QA (25–50), checking the rest of the experimental `.fig` export elsewhere: effects, images, prototypes, real files (5–15) | | Essentials user | ~59% | ~55–100 h | the `.fig` import's tail so the files people send open well (5–10), core tool polish (25–45), interaction pass on the core tools (15–25), stability QA (10–20) | Hours are one agent working sequentially, calibrated from this repo's history (about 1 agent-hour per 1,000 added lines, 1–2.5 h per feature PR, ~7 h per honest depth point in the polish tail; see [how the hours are calibrated](docs/target-app-parity.md#how-the-hours-are-calibrated)). Each audience's work is a subset of the next. About 70–80% parallelizes across 4–6 agents on disjoint crates (roughly ÷4 in wall-clock); the `.fig` reader, the multiplayer core and a plugin runtime each need one owner, and native-speaker review, real-file licensing and screen-reader QA need people. Hours are Opus 5.5 agent wall-clock hours, calibrated from this repo's history: the app was built in ~25 wall-clock hours by 4–6 parallel agents (~110–150 agent-hours, ~1 agent-hour per 1,000 added lines, a typical feature PR 1–2.5 h). Details and what needs a human: [`docs/target-app-parity.md`](docs/target-app-parity.md#how-the-hours-are-calibrated). ## Stage **Alpha.** Core workflows exist end to end (frames, shapes, vectors, text, auto layout, components with variants and overrides, styles, variables, prototyping, Dev Mode, SVG/PNG/JPG/PDF), but FigCraft opens `.fig` files only best effort, has only a preview of multiplayer, and nothing has been checked against real Figma documents. Ready-for-real-work is ~53%, inside alpha's ~40–75% band, and the alpha gate passes: every daily core workflow (design screens, build a design system, prototype, hand off to developers) works end to end on FigCraft's own files, which save and reopen; review works only asynchronously and `.fig` import is unproven on real files, both beta gaps ([alpha gate](docs/roadmap.md#alpha-gate)). Beta needs ~75% **and** reliable `.fig` import: the `.fig` import's tail (5–10 h), a real-file corpus with measured fidelity (15–30 h), the interaction pass (25–40 h of the 40–60), core depth to ~80% (40–75 h), basic multiplayer on the landed core (30–55 h), stability and platform QA (20–40 h), a partial GPU path (15–30 h); native speakers' reviews of the nine language drafts need people. ## By dimension | Dimension | Ready | Hours | Doc | |---|---|---|---| | Features (depth) | 63% (core editor 72%) | 28–67 | [target-app-parity](docs/target-app-parity.md) | | UI/UX fidelity | 58% | 40–60 | [ui-parity](docs/ui-parity.md) | | File formats | 60% (`.fig` read best effort with editable vectors; experimental write, round trips through our own importer and written files opened in other software) | 5–10 | [file-format-parity](docs/file-format-parity.md) | | Collaboration | 13% (core in; live sessions with document sync, cursors and text carets, cursor chat and spotlight, the browser joins, invite links open the app; a self-hosted server with edit and view links and team libraries; preview) | 46–92 | [collaboration-parity](docs/collaboration-parity.md) | | Stability | 55% | 20–40 | [gaps](docs/gaps.md) | | Performance | 60% | 30–60 | [hardware-parity](docs/hardware-parity.md) | | Hardware | 60% | 10–21 | [hardware-parity](docs/hardware-parity.md) | | Localization | 39% (layer; English; Russian and Turkish reviewed by native speakers; Spanish with a Latin American overlay, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese, Indonesian and Czech drafts; CJK UI fonts) | 29–51 | [localization-parity](docs/localization-parity.md) | | Platforms | 75% | 10–20 | [hardware-parity](docs/hardware-parity.md) | | Ecosystem / plugins | 38% (own WASM plugins with panels, settings and widgets; a self-hosted plugin directory) | 8–26 | [gaps](docs/gaps.md) | | AI features | 0% | 30–60 | [gaps](docs/gaps.md) | ## Features | Area | Breadth | Ready | Hours | Doc | |---|---|---|---|---| | Canvas & navigation | 98% | 74% | 1–4 | [ui-parity](docs/ui-parity.md) | | Selection & transform | 97% | 72% | 3–5 | [ui-parity](docs/ui-parity.md) | | Layers panel | 99% | 85% | 1–2 | [ui-parity](docs/ui-parity.md) | | Shapes & vector | 99% | 73% | 1–3 | [target-app-parity](docs/target-app-parity.md) | | Paint | 90% | 71% | 2–5 | [target-app-parity](docs/target-app-parity.md) | | Strokes | 95% | 75% | 1–2 | [target-app-parity](docs/target-app-parity.md) | | Effects | 100% | 75% | 2–5 | [target-app-parity](docs/target-app-parity.md) | | Frames, groups, sections, masks | 97% | 80% | 1–3 | [target-app-parity](docs/target-app-parity.md) | | Auto layout & constraints | 98% | 74% | 3–6 | [auto-layout-parity](docs/auto-layout-parity.md) | | Text | 90% | 70% | 3–7 | [target-app-parity](docs/target-app-parity.md) | | Components & variants | 96% | 72% | 4–9 | [components-parity](docs/components-parity.md) | | Styles & variables | 95% | 76% | 2–4 | [components-parity](docs/components-parity.md) | | Prototyping | 92% | 74% | 2–5 | [prototyping-parity](docs/prototyping-parity.md) | | Import/export | 86% | 58% | 5–10 | [file-format-parity](docs/file-format-parity.md) | | Dev Mode / inspect | 97% | 76% | 2–4 | [dev-mode-parity](docs/dev-mode-parity.md) | | Comments, versions, misc | 87% | 63% | 1–3 | [collaboration-parity](docs/collaboration-parity.md) | | Multiplayer & sharing | 17% | 5% | 42–84 | [collaboration-parity](docs/collaboration-parity.md) | | Plugins & widgets | 60% | 35% | 8–26 | [plugins](docs/plugins.md) | | Team libraries | 70% | 40% | 4–8 | [components-parity](docs/components-parity.md) | | Figma Draw extras | 88% | 60% | 1–3 | [gaps](docs/gaps.md) | | AI features | 0% | 0% | 30–60 | [gaps](docs/gaps.md) | | Boards (whiteboarding; not in the weighted numbers) | ~35% | ~20% | 30–60 | [boards-parity](docs/boards-parity.md) | ## Languages | Language | Code | Strings | Status | |---|---|---|---| | English | en | 100% | full | | Simplified Chinese | zh-Hans | 95% | draft (needs native review) | | Spanish | es, es-419 | 100% (es-419: an overlay of 101 entries) | draft (needs native review) | | Hindi | hi | 0% | none | | Arabic | ar | 0% | none | | French | fr | 95% | draft (needs native review) | | Portuguese (Brazil) | pt-BR | 95% | draft (needs native review) | | Indonesian | id | 99% | draft (needs native review) | | Japanese | ja | 99% | draft (needs native review) | | German | de | 96% | draft (needs native review) | | Korean | ko | 95% | draft (needs native review) | | Vietnamese | vi | 0% | none | Percentages measured 2026-10-17 over 2,272 entries (strings added since a draft show English). Also shipped: Russian (`ru`, 99%, reviewed by VanTan), Swedish (`sv`, 99.6%, command labels since 2026-10-17; native review welcome), Turkish (`tr`, 99%) and Czech (`cs`, 99%, draft). Figma ships English, Japanese, Korean, French, German, Spanish and Portuguese (Brazil); FigCraft has the localization layer, English and Spanish (Spain and Latin America), French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese and Indonesian drafts (Preferences ▸ Language; Traditional Chinese has no catalog; CJK UI text uses an installed CJK font, loaded only when needed; adding a language: [`docs/translating.md`](docs/translating.md)). Document text in any script (bidi, Arabic shaping, IME) already works (macOS IME composition fixed for #747, not yet checked on a Mac). Detail: [`docs/localization-parity.md`](docs/localization-parity.md). ## Upcoming Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md). 1. `.fig` import's tail: fixes from real files, connector labels, table cells, filled areas with their own fills (the best-effort import, editable vector networks, the other layer kinds and variable prototype actions landed 2026-10-11): 5–10 h; then checking the rest of the experimental `.fig` export (landed 2026-10-12; written files open and draw in other software) on effects, images, prototypes and real files: 5–15 h. 2. Real-file corpus and measured fidelity: 15–30 h. 3. Tool-by-tool interaction pass against Figma: 40–60 h. 4. Languages: native reviews of the es (with es-419), fr, de, pt-BR, ja, ko, zh-Hans, id and cs drafts (people), the entries added since each catalog was written, then Hindi, Vietnamese and Arabic ([localization-parity](docs/localization-parity.md)). 5. Multiplayer (M9): accounts and teams on top of the live sessions and the standalone server (transport, hosting, invite links that open the app, document sync, presence with remote text carets, cursor chat and spotlight, per-user undo, reconnect with offline edits, collision-free ids for styles, variables and comments, the browser client, `figcraft-server` with persistence and share links to edit or view, team libraries on the server landed): 42–84 h. ## Progress log - **2026-10-24** · Boards (M12): File ▸ New board makes a board, a file kind of its own saved in `.figcraft` and carried to people joining a live session. Sticky notes (8 colours, text that shrinks as the note grows, the author's name, Shift+Cmd/Ctrl+Enter for the next one), shapes with text (7 outlines), connectors that re-route when what they join moves (computed in the engine's normalize pass, so local edits, undo and others' edits agree), marker, highlighter and eraser, stamps with the placer's name on hover; a board toolbar, a contextual bar over the selection and the Design panel hidden until asked for; prototyping and Dev Mode refuse on boards. Design files and boards have their own marks (a pen nib, a note) on tabs, the home screen and the Open files list. Checked in the app, with two app instances in one live session, and through MCP; the web build compiles ([boards-parity](docs/boards-parity.md)). - **2026-10-15** · Auto-save (M1, #305): Preferences ▸ Save files automatically (`pref.autoSave`, off by default, desktop only) saves each open file that has a path 3 s after its last change, never during a gesture, a live text preview or while a version is shown; the file is encoded off the UI thread, a failed save says why and is tried again a minute later. Numbers unchanged. - **2026-10-15** · A text box by dragging (M1, #363): a drag with the Text tool makes a fixed-size text box the size of the drag; a click still makes auto-width text. Numbers unchanged. - **2026-10-15** · Batch step references (M0.9, #400): a `command.batch` step can use the id an earlier step made (`{"$step": n}`, a reserved key), so a nested tree takes one call and one undo step. Numbers unchanged. - **2026-10-15** · Removing someone from a live session (M9, #338): the host or the file's owner removes a person from the avatar's menu or with `collab.removePerson`; they keep their copy and can't rejoin while the session runs. Numbers unchanged. - **2026-10-15** · Languages (M10, #257, #263, #469, #477, #288): Russian (one/few/many plurals) and Turkish catalogs reviewed by native speakers, Czech and Indonesian drafts, the Japanese draft completed; Preferences ▸ Language lists 12 languages and Latin American Spanish. Numbers unchanged. - **2026-10-15** · Zoom previews (M8, #312): when renders from scratch are slow, a new zoom shows the whole view coarsely first, then refines it. Numbers unchanged. - **2026-10-15** · Web clipboard (M1, #375): copy and paste in the browser go through the system clipboard. Numbers unchanged. - **2026-10-15** · Alt-drag copies snap to their original and Duplicate (Cmd+D) repeats the last copy's offset (M1, #283). Numbers unchanged. - **2026-10-17** · Control channel while the window is minimised (M8, #720): a minimised or covered window answers every request that acts on the app's state (edits, `ui.inspect`, `ui.pointer`, `ui.key`, `ui.type`, `ui.set`, `app.quit`…) without drawing, and what needs a drawn frame (`ui.screenshot`, `ui.input`, `window.new`) is an error at once; before, those waited up to 60 s and held up every later request on the connection (an attached agent's `inspect_ui` or window screenshot stalled until the window came back). Measured on Windows 11, minimised: requests ~100 ms each (eframe wakes a minimised window at most ten times a second; ~2 ms on screen), a window screenshot fails in ~100 ms instead of 60 s, idle CPU 0. Numbers unchanged. - **2026-10-17** · Number variables on text content (M3, #629): the Typography section's variable picker also lists number variables for a text layer's content, as the engine already accepted (the text shows the number, e.g. 42.5, and follows the variable). Other text fields keep their own types. Numbers unchanged. - **2026-10-17** · Frames and sections convert into each other (M1, #719): `object.convertToSection` turns a frame on the page or in a section into a section in place (same box, name, fill and layers, which stay where they are; what only a frame has goes: auto layout, clipping, layout grids, guides, scrolling), and `object.convertToFrame` now also turns a section into a frame that keeps its fill and doesn't clip, so the look stays. Nested frames, instances, main components and sections holding sections are refused with the reason; one undo step each. Both are in the canvas context menu (Convert to section / Convert to frame) and in Spanish. Numbers unchanged. - **2026-10-15** · Dev Mode tail (M6, gap 16): generated layer trees start with the import lines of the connected components they call, once each per language; Compare changes takes any saved version as the before; a behaviour suite made the Dev Mode canvas inspect-only (no moving, resizing, drawing or editing; menus and shortcuts refuse design edits with a note), added Changed to the ready list and refused statuses on layers inside frames; read-only plugins show their panels from the Inspect tab. Dev Mode 97% / 76%. - **2026-10-14** · Corner handles win over the corner-radius handles (M1, #292): a press on the inner half of a corner handle's square picked the radius handle 10 px in, so the drag neither resized nor (dragging outward) rounded anything. Inside the drawn square the corner now resizes both sides (in ratio with Shift or the link); further in the radius handle still answers. Regression test in the transform behaviour suite. Numbers unchanged. - **2026-10-14** · Home screen recent files as cards (M7): each card shows the picture its file carries on its first page's colour, the file's icon, name and "Edited ... ago"; sort by name, creation or last change either way; a table view; a click selects, a double-click or Enter opens, arrow keys move; Open, Show in folder and Remove from recents on right-click. Numbers unchanged. - **2026-10-13** · Vector edit mode regions (M4): with the Move tool, the filled area under the pointer is hatched with "/" lines in the selection colour, clipped by the area's own fill rule so holes stay clear; the hatch goes near points (3.5 px) and segments (2 px), and its spacing follows octave folding (8 px at every power-of-two zoom), fixed to the shape. A click selects the area with its points (they move with it but keep the unselected look), Shift adds or removes one, a press on a selected area drags the whole selection as one undo step, and a click on an empty spot clears the areas and stays in edit mode. `vector.edit.select {regions}` and `vector.edit.state` (`regions`, `regionVertices`, `regionHover`); `vector_regions_tests` and `regions_tests`. Numbers unchanged. - **2026-10-14** · SVG/PDF export depth (M0.9, #208): visible descendants deeper than 256 levels below an exported root are refused before bounds or drawing, including masks and raster fallbacks. SVG has a checked API; the engine and CLI report the limit and suggest selecting an inner layer. Existing destination files survive refusal. Boundary, inner-root, hidden-subtree, multi-root/page and command/CLI regressions cover the change. Numbers unchanged. - **2026-10-13** · MCP/control transport hardening (#201): 32 MiB incoming frames are bounded while reading; oversized frames return an error and close without draining. The opt-in loopback control listener caps active workers at 8, applies 60 s socket read/write inactivity timeouts and caps both request queues at 8, discarding timed-out requests including those deferred to hidden windows. Reader, queue, idle-client and blocked-writer regressions cover the limits. Numbers unchanged. - **2026-10-13** · Attached MCP (M0.9, #194): a transport failure no longer replays a request that may already have edited the document. The caller gets an outcome-unknown error and must inspect before retrying; the next explicit call reconnects. Loopback tests cover a lost mutation reply, a single applied edit, reconnect and a connection failure before sending. Numbers unchanged. - **2026-10-13** · Constrain proportions in the Layout panel (M1, #220): with the W/H link on, typing a width or height scales the other side by the layer's own ratio; the property edit dropped the ratio, so the link only worked while dragging resize handles. Both sides in one edit stay as typed, the `w`/`h` aliases work the same, each layer of a multi-selection keeps its own ratio, and a zero side has no ratio to keep. Regression test in the transform behaviour suite. Numbers unchanged. - **2026-10-13** · Repeat last command (M1, #181): repeated edits keep the underlying command and parameters, so repeated nudges accumulate with one undo step each. A stored self-reference is refused instead of recursing. Regression tests cover three repeats, their undo steps, and missing or self-referencing commands. Numbers unchanged. - **2026-10-13** · Component playground (M3): with an instance, a component or a component set selected, the Inspect tab shows a Playground: a preview of the component and a control for each variant, boolean and text property. What is tried there changes the preview and the code in the Code section, never the file (no undo step, the canvas instance stays as it is); Reset goes back to the component's or the instance's own values. `component.playground {id?, properties?, language?, path?}` gives the same to scripts: the properties with their values, the code, and a PNG on request (`playground_tests`). Left: instance-swap and slot properties in the playground (the command takes an instance swap; the panel has no control for it), a code connect template's snippet with the tried values. Numbers unchanged. - **2026-10-13** · Auto layout, on-canvas handles of automatic gaps (M2): the handle between spread rows or columns (and between items with an auto gap) shows the distance the layout gives the gap instead of the stored value, and dragging it makes the gap a fixed one from there, as typing a gap in the panel does; one undo step (`gap_handle_tests`). Before, the drag changed a value that had no effect. The documentation is silent on dragging an automatic gap. Numbers unchanged. - **2026-10-13** · Variables inside styles (M3): a shared style can bind variables to fields of its value (`style.bind {style, field, variable?}`: a paint style's colours and gradient stops, an effect style's colour, radius, spread and offsets, a grid style's size, count, gutter and offset, a text style's font size, line height, letter spacing, family and style). Every layer using the style shows those fields resolved in its own modes and stays linked when a variable or a mode changes; the style keeps them resolved in the default modes. A style made from a layer keeps that layer's bindings; a new value drops bindings to what is gone. The style editor binds from its property rows (paint colour, shadow colour, blur radius, grid size) and detaches from the variable's chip; the style picker marks styles that use variables. `.fig` export and import carry the bindings on the style, REST layers hand theirs to the style they use. Left: binding text style fields and the other effect and grid fields from the editor (commands only), library variables in the picker, a style's library copy. Numbers unchanged. - **2026-10-13** · Effects on groups, inner shadows and background blur (M3): a group is no outline of its own, so its inner shadows are cast inwards from around what its layers draw together (their pixels, over them: no shade at a seam between two layers) and its background blur and glass show the backdrop through the outlines of its layers (under them). Canvas and PNG; SVG embeds a group with an inner shadow as a render and its background blur as an image under the layers; PDF places both as images, the blur matching the PNG. Where the group holds one shape the pixels match that shape, or a frame filled like it, carrying the effect (`group_effects_tests` in render, svg and pdf). Offsets and spreads of a group's inner shadow go by whole pixels. Numbers unchanged. - **2026-10-13** · Text made by commands on a dark page (M1): in the app, `text.create` and `node.create {type: text}` that name no fill give white text on a dark page, by the same rule as the Text tool (the page's colour decides, also inside a frame); a named `fill`, `fills` or `fillStyle` is kept. Sessions without an interface (CLI, MCP, exports) still make black text everywhere. Numbers unchanged. - **2026-10-13** · Frames and groups, a one-step convert (M1): `object.convertToGroup` and `object.convertToFrame` (the Design panel's type menu on a frame or a group) change the layer in place, its layers staying where they are on the page. A frame turned into a group loses what only a frame has (auto layout, with the positions it gave kept and fill children fixed; fills, strokes, corner radius, clipping, layout grids, guides, scrolling) and takes its layers' box; a group turned into a frame keeps its box, with no fill and no clipping. Instances, main components, layers inside instances and empty frames are refused with the reason, all or nothing; one undo step. Numbers unchanged. - **2026-10-13** · Prototyping, delays on mouse triggers and manual overlay offsets (M5): mouse enter, leave, down and up wait `delayMs` before their actions run (once while waiting; dropped when the screen or overlay they were on goes away; a pointer that leaves early doesn't cancel, the documentation is silent), and a manual overlay sits at `offset: [x, y]` from the top-left of the layer that opens it (without one, right below it, as before). Delay and X/Y fields in the interaction editor, the player on its own clock, REST import (`delay`, `overlayRelativePosition`), `.fig` import and export (the export's field for mouse delays is unchecked against other software). Numbers unchanged. - **2026-10-13** · Auto layout, wrapped-line alignment (M2): a wrapping layout's lines (rows of a horizontal one, columns of a vertical one) are packed where the cross alignment says (start, centre, end; before, they always sat at the start), stretch when every child fills across the flow, or take the free space between them (`contentAlign: spaceBetween`, the Design panel's Row/Column gap mode, the alignment pad lighting the whole cross axis). Read from REST JSON and `.fig`, written to `.fig`, `align-content` in CSS, an instance override with the other alignment settings. 13 hand-computed layout cases and command tests. Numbers unchanged. - **2026-10-13** · `.fig` export: instance sizes, variables, styles (gap 1): instances write what they change from their main (size, name, paints) as an override addressed by the main's id, what follows for their layers as derived data and a scale factor when scaled as a whole; variable collections, modes, variables and bindings, the modes frames and pages set, shared styles with their links and variable-width strokes are written, and the import reads all of it back. Checked by opening written files in other software: resized and scaled instances keep their size and lay their layers out for it, variables with their modes and colour bindings resolve there (a frame set to another mode shows that mode's colours, also inside instances), colour, text, effect and grid styles are listed, and variable-width strokes that are the same on both sides of the path draw with their width; number, string and boolean bindings and bindings on effects, layout guides and gradient stops come across as the values they give but did not show as bound there (their entries now also name the layer field they set, not yet checked there), and strokes wider on one side of the path are written as filled outlines, no longer editable strokes there (the stroke itself is kept as plugin data and our own import puts it back; not yet checked there). Numbers unchanged. text, effect and grid styles, number, string and boolean bindings, bindings on effects, layout guides and gradient stops, and variable-width strokes are not yet checked there. Numbers unchanged. - **2026-10-13** · Starter templates and first-run help: eight templates built in code (mobile app, landing page, dashboard, design system starter, wireframe kit, social post sizes, presentation slides, icon set starter), a Templates tab on the home screen, `template.list` and `file.newFromTemplate` for scripts and agents, and a Getting started card that can be closed and comes back from the Help menu - **2026-10-12** · Your online files: a file shared or published online stays yours after its tab closes or the app quits. The app remembers it (with its owner token, kept with the preferences and never shown) and Home ▸ **Online** lists what you own: open a file again as its owner, start or stop sharing, copy its code, rename it, remove it from the server or forget it here. Servers are asked once, when the list opens; tabs mark files in a live session; a crash reopens such a file from its server and puts back the changes that hadn't reached it. `collab.files`, `collab.openFile`, `collab.forgetFile`; servers need an upgrade only to tell which files are still there. The six draft catalogs translate every string again - **2026-10-12** · Everyday editing partials: number fields (scrub rates with Shift and Alt, arrow keys by the nudge preferences, arithmetic with brackets, relative edits of Mixed values per layer), outline mode's hidden layers and object bounds, Hide when publishing in the Assets panel, a cursor for every tool and modifier, panel widths that are kept, and dark pages (white text on them) for files made in the dark theme - **2026-10-12** · Sharing with a join code: Share ▸ **Online** (through the server in Preferences, built in) or **Local network**, one Start sharing button and a short code to read out (`K7M-2QX-9TD`; a longer one locally that carries the host's address); others type it under Join with a code (the popover, the home screen), where a link works too. The server names links with codes, keeps them hashed and limits wrong ones; `collab.share`, `collab.join {code}`; servers must be upgraded to hand codes out (older ones keep working with links) - **2026-10-12** · Text metrics measured: our example documents, re-saved by other software, were read back and compared layer by layer. Line heights are now whole pixels, advances sit on a 1024-per-em grid and letter spacing goes between glyphs only; all 230 self-sizing text boxes in the 7 documents match to the pixel (24 of 131 before in the design-system example). One font family; real files still unchecked (gap 3). No number moves - **2026-10-12** · Live sessions are per document: every open file has its own session or none, several run at once (host one, join another, publish a third) and keep syncing behind the tab in front, the Share popover, people, cursors, chat, spotlight and follow are the file in front's, `collab.peers` lists them all (`sessions`), and closing a file ends its session only (before, one session followed every tab and starting another replaced it) - **2026-10-12** · A public `figcraft-server`: `open_publish = true` lets anyone publish without the server key (Share ▸ Publish to server asks the server first and shows who hosts it, its terms and how long unopened files are kept), with an owner token shown once that manages the file later (`collab.manageFile`); limits per address, idle expiry, reports (Share ▸ Report this file…), `block-ip` and `files list --sort`, `GET /healthz`, and `packaging/server/install.sh` that sets up a Debian/Ubuntu machine with Caddy, ufw and backups ([server.md](docs/server.md#running-a-public-server)). Numbers unchanged (hosting, not a Figma feature). - **2026-10-12** · `.fig` export opens elsewhere (gap 1): written files were checked by opening them in other software: layers, fills, gradients, strokes, shadows, vectors, components and text draw as in FigCraft. The export now says that the document and its pages are visible and opaque, numbers its schema as the format does, and writes outlines, text settings, standard font style names and line height units; effects such as glass and noise, images and prototype interactions are still unchecked there, and variables are not written. Numbers unchanged. - **2026-10-12** · Experimental `.fig` export (gap 1): `file.export {format: "fig"}`, File ▸ Export… ▸ .fig (experimental), `figcraft-cli export --format fig` and MCP's `export` write the whole document: layers, geometry, paints with their images, strokes, effects, styled text, vector networks, auto layout, components and instances with their overrides, and prototype interactions; what's left out (style links, variables, comments, Dev Mode data…) comes back as warnings. Checked only by reading the files back with our own importer. File formats 60%. - **2026-10-12** · Keyboard shortcut parity (gap 4): a table test with a row for each of the 129 shortcuts in the public documentation, macOS and Windows/Linux keys each, and the 48 bindings it is silent on pinned; 126 bound (110 before), Run last plugin moved off a documented conflict on Windows, 2 missing. New: others' cursors toggle, list levels on Cmd/Ctrl+] and [ while editing, keyboard box selection on Alt+Space / Ctrl+Space, arrows that pan with nothing selected, Control suspending snapping on macOS, Space/N/C/F while presenting, the Frame tool on A, pixel grid on Shift+', link on Shift+Cmd+U, ungroup on Cmd/Ctrl+Backspace, suggest auto layout and go to main component keys; shifted symbols on other keyboard layouts. - **2026-10-12** · Plugin directory on `figcraft-server` (gap 6): publish a plugin folder with the server key (`figcraft-cli plugin publish`), browse a server's plugins in Plugins ▸ Manage plugins ▸ Browse, install after a confirmation that shows the permissions it asks for, update when a newer version appears, remove; bundles are checked in full on the server and again in the app, downloads must match their SHA-256, installed plugins live per user in the same sandbox; `plugin.directory.*` for scripts and MCP; the operator moderates with `figcraft-server plugins`. Plugins & widgets 60%. - **2026-10-12** · Plugin widgets and the panel tail (gap 6, plugin API 3; older plugins run unchanged): a widget plugin puts a layer of its own on the canvas (Plugins ▸ Widgets, `widget.insert`), described by the plugin, validated and built with locked layers inside; clicking a part it named calls it back with the widget's state (kept in the layer, so it saves, copies, undoes, exports and syncs) as one undo step, and it redraws; `widget.click` and `widget.list` for agents; a Counter example. Panels of several plugins open side by side and plugin colour fields open the colour picker. Plugins & widgets 55%. - **2026-10-12** · Layers and Assets panels behaviour suite (gap 4): several layers dropped together land together, Collapse layers keeps the selection's rows open, dragging across eyes or padlocks sets several layers at once, a Rename layers dialog for several layers (number codes as documented, regular expressions, a start value), pages dragged to reorder, dividers, "Copy of" duplicates, Assets folders by page, frame and slash names, and hidden-from-publishing names that decide both ways. - **2026-10-12** · Vector multi-edit and the vector edit tail (gap 9): several vector layers in edit mode at once (select, marquee, drag, nudge, delete, mirroring and radius across them), several Bezier handles dragged together, rounded points beside curved segments, a stroke cap and join per point, and the Pen connecting to its own points or continuing a selected open path. - **2026-10-11** · Canvas navigation, guides and grids behaviour suite (gap 4): zoom, panning, the pixel grid (now from 400%), rulers, guides (top-level frames only; select, Delete, nudge, Remove guide, Alt-drag copies, Alt measures while dragging), layout grids (Auto counts; hidden on rotated frames), outline mode (clicks reach layers behind) and measuring, each rule from the documentation. - **2026-10-11** · Export and inspect behaviour suite (gap 4): exports cover what a layer paints (shadows only where they fall, true stroke widths, no room for hidden layers, a mask group's mask), slices export what's under them, layer names with slashes export into folders; export settings gained contents only, include bounding box and the SVG options (outline text, ids, simplified strokes); Inspect shows colours in hex, RGB, CSS, HSL or HSB, CSS in rem, blend modes and positions from the frame, and lists icons and images as assets. Dev Mode 70%. - **2026-10-11** · `.fig` import's tail (gap 1): vectors open as editable vector networks with their corner radii, handle mirroring and end caps (outlines when the network is missing or damaged); sticky notes, shapes with text, connectors, tables and similar layers import as frames, text and vectors instead of being dropped; set-variable and conditional prototype actions come across. File formats 58%, ready ~49%. - **2026-10-11** · Paint and effects behaviour suite: every documented rule for fills, blend modes, gradients, images, colour models, shadows, blurs and effect exports is a table-driven case. Fixes: Normal groups isolate and clipping frames don't, plus darker is drawn, drop shadows hide behind translucent layers in every export, Fill images reach the box's edges, "Normal" no longer means pass-through. Paint 70%, effects 75%. - **2026-10-11** · `.fig` import, best effort (gap 1): `file.open`, `file.import`, drag and drop, the CLI and MCP open `.fig` files. A generic kiwi decoder reads each file's own schema, and the decoded nodes map by field name onto the REST importer: layers, paints and styles, text runs, vectors as outlines, auto layout, components and instances (expanded with their overrides and property values), variables, prototyping and images from the archive; the rest is a warning. File formats 55%, ready ~49%. - **2026-10-11** · Gamut warnings (gap 18): the colour picker of a Display P3 file flags colours outside sRGB and View ▸ Soft-proof sRGB stripes them on the canvas. Showing P3 itself waits on eframe/egui-wgpu exposing the surface colour space wgpu 30 already supports. - **2026-10-11** · Several windows (gap 18): File ▸ New window, Open in new window on a tab or a tab dragged off the strip; each window has its own tabs, panels and canvas, draws only when it needs to and asks about unsaved changes when it closes (the main window too); the control channel and MCP open, list, focus and capture windows. Hardware 60%. - **2026-10-11** · Hosting `figcraft-server`: the desktop app and `figcraft-cli` open `wss://` links (rustls, certificates always verified; `FIGCRAFT_CA_FILE` for a private authority), releases ship the server for Linux x86_64 and aarch64, it stops gracefully on SIGTERM or Ctrl+C, and `docs/server.md` walks through deploying it on a rented Linux server (systemd unit, Caddy or nginx, firewall, Docker, upgrades, backups). - **2026-10-11** · Plugin panels and settings (gap 6, ABI v2; v1 plugins run unchanged): a plugin shows a panel of fields and buttons drawn with FigCraft's widgets and is called back for each button or changed value (one undo step per callback, same sandbox limits), `plugin.ui` / `plugin.event` / `plugin.close` drive panels from scripts and MCP, and per-plugin settings are kept with the preferences; Swatch grid gained a palette panel. Plugins & widgets 45%. - **2026-10-11** · Canvas fixes: clicks hit boolean groups where their result is (a click in a subtracted hole reaches what's behind), a layer dragged inside a group stays in it, View ▸ Frame outlines and View ▸ Slices draw, and a flaky idle test now waits for the render thread (gap 13). - **2026-10-11** · Team libraries on `figcraft-server` (gap 10): Assets ▸ Libraries ▸ Publish to server keeps the file's library on the server under the file (per-asset versions; the server's version is followed); other files enable it by pasting a share link (a view link is enough), get "Library updates available" from a background check and accept with overrides kept; the server counts which files use which components (Show usage, `library.usage`); `library.swap` re-points instances, styles and variables to another library by names. Team libraries 70%. - **2026-10-11** · Animated GIF fills (first frame on the canvas and in exports, played in the presenter and the preview, waking the window only for the next frame) and TIFF images (gap 12). - **2026-10-11** · The last three menu items got commands: File ▸ Open recent (kept in the preferences, missing files greyed, Clear recent), outlines for the selection and mask outlines (gap 21). Menu breadth 203/203. - **2026-10-11** · Extended variable collections (a library collection's modes and variables in a collection of the file, overridden per mode with reset, following library updates), variables import/export (the REST local variables JSON, merged by name; CSS custom properties), gradient stops and variant properties bound to variables (gap 14). - **2026-10-11** · Live boolean editing: operands stay editable layers (double-click or Cmd/Ctrl-click selects one, moving, resizing or editing its points redraws the result during the drag with every operand outlined, per-operand operations from the boolean controls, prefix-cached recompute); Design panel arc fields; the shape builder on strokes and open paths; one-sided variable widths (gap 13). Feature breadth ~79%. - **2026-10-10** · WASM plugins (#101: sandboxed wasmi runtime, manifest permissions, Plugins menu, Run last, Manage, Rust SDK and two examples), docs refresh (#102), the multiplayer core without networking (#103: client-scoped ids, change sets, server ordering and validation, client reconciliation, convergence tests). Menu breadth 200/203; ready for real work ~47%. - **2026-10-10** · Full re-measure against Figma desktop 126.10.7 under the craftrules progress-docs standard: ready for real work ~46%, feature breadth ~76%, stage alpha; progress docs split out (`docs/target-app-parity.md`, `gaps.md`, `roadmap.md`, `architecture.md`, the per-area parity docs); `docs/parity.md` renamed `docs/parity-checklist.md`. - **2026-10-10** · Day 2 (PRs #1–#99): vector edit mode depth (transform box, point snapping, arc handles, shape builder, variable width), text depth (variable fonts, bidi, RTL names, style browser), auto layout nesting and keyboard reordering, slots and library variables, components at scale (2,000 instances), effects fidelity (glass, noise, texture, progressive blur), prototyping (conditionals, expressions, device frames, responsive presentation), REST import of prototypes and variables, SVG import fidelity, PDF print options, Dev Mode (code, annotations, ready for dev, compare changes), comments polish, version history, keyboard shortcut parity, accessibility, retained canvas and 50k-layer performance, no redraws while idle; v0.2.0 released for every platform; multiplayer protocol designed. - **2026-10-09** · Day 1: foundation (13 crates, layering, command registry, renderer, engine, UI3 frontend, control channel), CLI and MCP, web build, PDF export, packaging, first depth passes on text, components, prototyping and comments. ## Revision history | Date | Change | Summary | |---|---|---| | 2026-10-24 | minor | Boards (M12): a file kind of its own (`file.newBoard`, saved in `.figcraft`, carried to live-session joiners) with sticky notes, shapes with text, connectors that re-route when what they join moves (an engine recompute, the same for local edits, undo and others' edits), marker, highlighter and eraser, stamps with the placer's name, a board toolbar and a contextual bar; prototyping and Dev Mode refuse on boards; design files and boards have their own marks on tabs and file rows. Boards row added outside the weighted numbers; headline numbers unchanged at this rounding | | 2026-10-15 | minor | Auto-save (#305, #296): Preferences ▸ Save files automatically (`pref.autoSave`, off by default; desktop only, the browser has no files to save to) saves each open file that has a path 3 s after its last change, so steady editing doesn't save every few seconds, and never during a gesture, a live text preview or while a version is shown. The file is encoded on a thread of its own and written as of that moment (edits made meanwhile stay unsaved); a failed save says why in a toast and is tried again a minute later, and version history checkpoints keep coming with it on. Numbers unchanged | | 2026-10-15 | minor | A text box by dragging (#363, #360): a drag with the Text tool makes a fixed-size text box the size of the drag (at least one line tall); a click, or a drag shorter than the drag threshold, still makes auto-width text. Numbers unchanged | | 2026-10-15 | minor | Batch step references (#400): inside `command.batch` (MCP `batch`), `{"$step": n}` anywhere in a step's params stands for the `id` step n returned (`{"$step": n, "field": …}` for another field; `$step` is a reserved key), so a frame with its children takes one batch and one undo step; a malformed reference fails the batch, which rolls back, and a nested batch keeps references to its own steps. Numbers unchanged | | 2026-10-15 | minor | Removing someone from a live session (#338): right-click a person's avatar ▸ Remove *name* from session, or `collab.removePerson {client}`, when you host the session or own the file on a server; the session ends for them (they keep their copy), the code or link they hold no longer gets them back in while it runs, and everyone else stays. Numbers unchanged | | 2026-10-15 | minor | Pages list (#272, #258): a long pages list scrolls in its own area, so the layers below stay in view; page and layer drops count only inside the list's visible rows. Numbers unchanged | | 2026-10-15 | minor | Variables export in the browser (#368): Export… ▸ Variables JSON / CSS custom properties did nothing in the web build and now arrives as a download; importing there still needs a file picker. Numbers unchanged | | 2026-10-15 | minor | Font picker keys (#352, #345): Up and Down step through the font families from the current one, keeping the highlight in view and previewing it on the canvas; Enter applies it, Escape leaves the text as it was. Numbers unchanged | | 2026-10-15 | minor | Leaving Design ends the pen path (#308, #307): switching to Prototype or Inspect while the Pen draws ends the path and goes back to Move (pressing Design again keeps drawing), and a click on the toolbar no longer reaches the canvas under it. Numbers unchanged | | 2026-10-15 | minor | Vector edit mode keeps the canvas still (#322): the edit mode hints in the Design panel wrap, so entering edit mode and picking a region no longer shift the canvas by about 25 points in the middle of the click (which dragged the region picked). Numbers unchanged | | 2026-10-15 | minor | Drop shadows under a background blur (#317, #255): a layer with both casts its shadows from what it paints (a translucent fill casts a lighter shadow than an opaque one) and reads the backdrop only after drawing them, so a shadow shown behind transparent areas is seen through the blur. Numbers unchanged | | 2026-10-15 | minor | When the window can't open (#306, #261): Windows release builds have no console, so a graphics failure at start quit without a word; the error is now logged and shown in a message box in the interface language, with what to try (unset `WGPU_BACKEND`, update the driver, OpenGL) and where to report it, skipped with `--control`. Numbers unchanged | | 2026-10-15 | minor | Crashes on Windows (#271, #314, #337; #224 and two more): a text field focused before it was drawn aborted the screen-reader layer; double-clicking a page, creating a variable collection and renaming a style folder no longer close the app. Numbers unchanged | | 2026-10-15 | minor | Deeply nested layers (#330, #210): one depth guard (256) on the three recursive layout paths, so a deep file can't run the layout out of stack. Numbers unchanged | | 2026-10-15 | minor | Image memory budgets (#326; #203, #189): decoded images and a file's images have budgets; the decode cache evicts its least recently used entries. Numbers unchanged | | 2026-10-15 | minor | macOS file dialogs (#253, #250) no longer re-enter the event loop; a dialog's result goes to the file and window it was opened for, and dialogs are parented to the main window. Numbers unchanged | | 2026-10-15 | minor | Recovery copies (#297, #303, #331, #336; #202, #185, #187): a copy counts only after its write succeeds and a failed one is retried; saves and closes wait for the write in flight; copies keep version history and branches; a recovered copy stays until this run has its own. Numbers unchanged | | 2026-10-15 | minor | Text edits (#280, #278) no longer roll back earlier changes; a drag released off the canvas ends at the pointer's last canvas position. Numbers unchanged | | 2026-10-15 | minor | Rollback (#282, #301; #196): a failed command rolls back during a drag or other gesture too; a failed batch keeps the redo steps. Numbers unchanged | | 2026-10-15 | minor | Atomic save and export (#284): staging files are uniquely named and created exclusively, so a file or link already at the old fixed staging name is never written through, and two writers of one file no longer share one. Numbers unchanged | | 2026-10-15 | minor | Alt-drag copies (#283) snap to their original, and Duplicate (Cmd+D) then repeats the last copy's offset. Numbers unchanged | | 2026-10-15 | minor | The pen (#277) shows its Bezier handles while a point is dragged out. Numbers unchanged | | 2026-10-15 | minor | Corner handles (#293, #292): a press on a corner handle resizes instead of picking the corner-radius handle. Numbers unchanged | | 2026-10-15 | minor | New text (#372, #362) takes a readable colour from the frame it is drawn in, not only the page (text drawn in a white frame on a dark page was white). Numbers unchanged | | 2026-10-15 | minor | Outline stroke (#386) works on every selected layer. Numbers unchanged | | 2026-10-15 | minor | Send to back (#444) counts per parent when the selection spans frames. Numbers unchanged | | 2026-10-15 | minor | Align buttons (#276) are off when the selection has nothing to align to. Numbers unchanged | | 2026-10-15 | minor | Gradient type icons (#275) show their gradient. Numbers unchanged | | 2026-10-15 | minor | Corner resize cursors (#388) stay diagonal at any proportions; the text selection highlight (#390) stays inside the box on the last line. Numbers unchanged | | 2026-10-15 | minor | New pages (#311, #295) take the current page's background; the theme still decides a new file's page. Numbers unchanged | | 2026-10-15 | minor | A slice's export preview (#265) shows the content under it. Numbers unchanged | | 2026-10-15 | minor | SVG export (#327, #325) keeps a nested frame's fill and stroke inside its parent's clip. Numbers unchanged | | 2026-10-15 | minor | Home screen (#318, #324): recent files notice files deleted while it is open; a long path no longer draws over the file name. Numbers unchanged | | 2026-10-15 | minor | The Small caps sample in Type settings (#355) draws from capitals, no longer a box where the UI font lacks the small-capital letter. Numbers unchanged | | 2026-10-15 | minor | Web clipboard (#375): copy and paste in the browser go through the system clipboard, so a paste after a copy brings the copied layers instead of whatever text the clipboard held. Numbers unchanged | | 2026-10-15 | minor | Zoom previews (#312): when renders from scratch are slow, a new zoom first shows the whole view coarsely instead of the page colour, then the refined image; images of another file or page never show. Numbers unchanged | | 2026-10-15 | minor | Hidden windows (#313, #332; #269): a minimised or covered window answers control and MCP requests (commands, inspection, `ui.render`) at once, each window on its own; screenshots and input still wait for it to be on screen. Numbers unchanged | | 2026-10-15 | minor | Server (#340, #302): a compaction cut short closes the file instead of confirming lost edits (#183); each team library version is kept in its own file (#206). Numbers unchanged | | 2026-10-15 | minor | Counters (#343, #344, #353; #309): collab sequence numbers, layer name counters and opacity digits can't overflow. Numbers unchanged | | 2026-10-15 | minor | Commands refuse bad indexes and ids (#475, #384, #310): `page.select` and `doc.switch`, `effect.set` and `export.update` / `export.remove`, `page.move` with a non-page id. Numbers unchanged | | 2026-10-15 | minor | Find and replace (#358, #361) changes every occurrence, case-insensitively, also when the replacement contains the search text. Numbers unchanged | | 2026-10-15 | minor | The gradient shorthand (#254, #252) is read by the engine, so the CLI, control channel and MCP share it. Numbers unchanged | | 2026-10-15 | minor | .fig import (#279, #281): resized instances lay out their layers by their constraints (#266); explicit variable modes whose collection is named by its asset key, as library collections are, come across (#267). Numbers unchanged | | 2026-10-15 | minor | Colour emoji (#300, #268) draw from their own colour tables (sbix and CBDT/EBDT bitmap strikes, COLR v0 layers), a colour face tried first; COLR v1 still draws outlines. Numbers unchanged | | 2026-10-15 | minor | macOS (#290, #289): `.figcraft` files open from Finder, Open With and the Dock. Numbers unchanged | | 2026-10-15 | minor | Windows (#294, #291): the first preferred language alone picks the UI language. Numbers unchanged | | 2026-10-15 | minor | Unix font discovery (#474) follows `XDG_DATA_HOME`. Numbers unchanged | | 2026-10-15 | minor | Linux packaging (#346, #328): the window id matches the desktop entry (FreeBSD keeps its class); the RPM requires glibc 2.35. Numbers unchanged | | 2026-10-15 | minor | Tests (#472, #471, #339): the rename shortcut test, the hanging punctuation test and the font-scanning tests no longer depend on the platform, font or test order. Numbers unchanged | | 2026-10-15 | minor | Languages (#257, #263, #469, #477, #288): Russian (with one/few/many plural forms) and Turkish catalogs, reviewed by native speakers; Czech and Indonesian drafts; the Japanese draft completed. 2,177 entries now (1,587 UI strings, 559 command labels, 31 menu wordings); strings added since a catalog was written read English there. Localization 39% and 29–51 h unchanged at this rounding (Indonesian is now a draft, while the five older drafts lack 88 newer entries) | | 2026-10-20 | minor | A document dropped on the canvas opens (#916): a `.figcraft`, `.fig` or file JSON dropped on the canvas opens as a file of its own, as on the home screen, in the browser too; it used to add its pages to the file in front. Images and SVG still import at the drop point, File ▸ Import still adds pages, and dragging only documents over the canvas says "Drop to open" (not in the browser, which names no file until the drop). Numbers unchanged | | 2026-10-20 | minor | 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, as Copy then Paste already was (a variant duplicated in its set stays a variant). Numbers unchanged. | | 2026-10-20 | minor | Readable disabled and selected text (#890): the dim text colour (disabled buttons, placeholders, hints) is at least 3:1 against panels, fields and selected rows in both themes (it was 2.1:1 in the light theme), a disabled button is grey with that text instead of fading to 40%, and selected text (renaming a page or layer, any text box) takes the theme's readable green on the highlight (1.4:1 in the dark theme before, now 4.2:1 or more); checked with the colour contrast helper (`theme::tests`). Numbers unchanged | | 2026-10-20 | minor | Layer data reads back (#879): `node.get` (the command, the control method and MCP `get_node`) and `document.inspect` report a layer's or page's `pluginData` as `{key: value}` when it has any, so a tool that keeps state in a file with `node.set {props: {pluginData}}` can read it on its next run (`plugin_data_tests`). Numbers unchanged | | 2026-10-20 | minor | Measuring while nudging (#894): with Alt held over another layer, the arrows nudge the selection (Shift: the big step) and the distances follow every nudge (Alt+arrows used to do nothing); while measuring, the selection's handles and size label hide and both layers keep their outlines (`measure_nudge_tests`). Numbers unchanged | | 2026-10-20 | minor | Release fonts (#885): the web release embeds craft-fonts again (Inter), so the browser version draws text and lists fonts; it had none since releases moved to the shared workflow. The craft-fonts pin goes back to the commit with the Latin manifest (the Georgian change had moved it to one without Inter, which would have failed the next release). Numbers unchanged. | | 2026-10-17 | minor | Community batch #753 (10 PRs): Selection colors for mixed selections (#403); smart-selection spacing fields and canvas handles (#402); a Present tab with device and scale controls (#537); adding auto layout to an empty frame keeps its size (#369); `node.set` applies auto layout keys to frames only and warns about font styles a family lacks (#603); canvas images on whole device pixels (#359); Adwaita window controls on Wayland without server decorations (#592, not run on GNOME or KDE by the maintainers); the keyboard shortcuts panel closes on the home screen (#548); macOS paste modifiers in tests (#750); credits column tests (#347). `cargo xtask parity` re-run: 212 / 212 menu items (100%; the catalog gained View ▸ Full screen with #227 on 2026-10-14), 561 commands. Language table re-measured from the catalogs (2,272 entries): the drafts read 95–99% (strings added since 2026-10-12 show English), Indonesian has a draft (99%; it read none), and Russian, Swedish (its command labels from #599), Turkish and Czech are listed with their counts. Per-area parity rows for the day's landings in `docs/gaps.md`, `target-app-parity.md` and the area docs. Numbers unchanged | | 2026-10-17 | minor | Control channel while the window is minimised (M8, #720): a minimised or covered window answers every request that acts on the app's state (edits, `ui.inspect`, `ui.pointer`, `ui.key`, `ui.type`, `ui.set`, `app.quit`…) without drawing, and what needs a drawn frame (`ui.screenshot`, `ui.input`, `window.new`) is an error at once; before, those waited up to 60 s and held up every later request on the connection (an attached agent's `inspect_ui` or window screenshot stalled until the window came back). Measured on Windows 11, minimised: requests ~100 ms each (eframe wakes a minimised window at most ten times a second; ~2 ms on screen), a window screenshot fails in ~100 ms instead of 60 s, idle CPU 0. Numbers unchanged. | | 2026-10-17 | minor | Community fixes from #524 up (batches #727, #728, #733, #735, #736 and #729; 55 PRs) and a crash guard. `.fig` import: explicit instance identity, overrides by key, text style precedence, grid track sizes, slot content, connector labels, skip ink, paragraph options and hanging lists, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments and embedded images; large files open within budgets from the file's own size; an import report after opening; imported bytes stay out of the command journal. Files and paste: save a copy as `.fig`; Cmd/Ctrl+V pastes images copied in other apps and layers copied from another design app (`edit.pasteDesignLayers`); paste over and paste properties; clipboard reads on Linux and the BSDs time out after 2 s. Editor: live font preview on hover; Shift+I and Resources open Assets; Shift-drag moves the selection; thin strokes take a nearby double-click; dropped points merge; Flatten keeps open paths; the Actions palette gets tabs, ranked search, a keyboard highlight and recents; the colour picker opens on a new fill or stroke; text strokes follow their alignment; selection highlights after line breaks; a draggable pages/layers divider; variant properties reorder by drag; submenus keep their hover with a safe triangle; menu actions that need a value ask for it; File ▸ Rename edits the name in place. Engine: bad paint indexes, non-finite geometry and pages in copy/cut are refused; a param naming a layer says so when it isn't one; a CLI script is one undo step and resolves `$step`. Server and collaboration: message limits follow `max_file_mb` (client capped at 1 GiB); a damaged generation field no longer drops flushed edits; the web build joins a session from a `#join=` link. Also Swedish command labels, Nix CI through the shared workflow, paint bounds once per render, and a guard that turns a panic inside the boolean-op library into bad input (the stroke draws unclipped). Numbers unchanged. | | 2026-10-17 | minor | Number variables on text content (M3, #629): the Typography section's variable picker also lists number variables for a text layer's content, as the engine already accepted (the text shows the number, e.g. 42.5, and follows the variable). Other text fields keep their own types. Numbers unchanged. | | 2026-10-17 | minor | Frames and sections convert into each other (M1, #719): `object.convertToSection` turns a frame on the page or in a section into a section in place (same box, name, fill and layers, which stay where they are; what only a frame has goes: auto layout, clipping, layout grids, guides, scrolling), and `object.convertToFrame` now also turns a section into a frame that keeps its fill and doesn't clip, so the look stays. Nested frames, instances, main components and sections holding sections are refused with the reason; one undo step each. Both are in the canvas context menu (Convert to section / Convert to frame) and in Spanish. Numbers unchanged. | | 2026-10-16 | minor | Community fixes from #478 up (batches #591, #598, #602; 69 PRs) and a control-channel fix: a line that isn't JSON now ends a `--control` connection, so a web page can't drive the app through a browser request (#589). Highlights: page copies keep navigation, flows, prototype settings and guides; Find and Replace keeps line endings and case-insensitive matches; text hyperlinks survive import and export; recent colours record only what was picked; MCP screenshots respect `maxSize`; Swedish catalog (UI strings), Czech plurals; bounded canvas overscan while panning; Korean fallback at the run's weight; many vector-edit, guide, page and .fig round-trip fixes. Numbers unchanged | | 2026-10-15 | minor | Drop files anywhere to open them (M7): dragging files over the window shows a "Drop to open" (home) or "Drop to import" (canvas) overlay. On the home screen each supported file opens as a document of its own and other files are turned away with a toast; on the canvas the files import at the drop point. Both wait behind a small card that is on screen before a large file blocks the window. A canvas import stays with the document and page that received the drop even if the tab or page changes meanwhile, and is cancelled with a message if that document or page is gone. Numbers unchanged. | | 2026-10-15 | minor | M7: system font discovery skips entries observed as non-regular files before opening them, avoiding the reproduced named-pipe stall. Regular-file symlinks remain supported; directory depth, font limits and priorities are unchanged. Numbers unchanged. | | 2026-10-15 | minor | Canvas retains a bounded margin while panning so newly exposed strips can reuse the previous render. Numbers unchanged. | | 2026-10-15 | minor | Dev Mode tail (M6, gap 16): import lines for connected components inside generated layer trees (definitions, slots and swapped-in instances too); `dev.compare {version}` and the compare view's Compare with menu compare with any saved version; behaviour suite (5 tests; 4 failed before the fixes): an inspect-only Dev Mode canvas with menus, shortcuts, nudges, opacity keys and paste refusing design edits, toolbar drawing tools returning to the design, Changed in the ready list (`inspect.readyForDev {status: changed}`), statuses only on sections and top-level frames; read-only plugins' panels from the Inspect tab. Dev Mode 96%/72% → 97%/76%, 0–1 h → 2–4 h (measurements kept on the canvas and a read-only layers panel counted as missing); header totals not recomputed | | 2026-10-15 | minor | Typography spacing icons scrub with the shared number-field gesture, modifier rates and one Undo step per drag (#373). Units and selected-character targeting are preserved. Numbers unchanged. | | 2026-10-14 | minor | System theme on first run (M7): preferences without a theme default to System; the first frame reads the OS appearance directly, including when returning from a manual theme. Saved Light/Dark choices stay explicit; unavailable OS appearance falls back to Light. Numbers unchanged. | | 2026-10-14 | minor | Home screen recent files as cards (M7): each card shows the picture its file carries on its first page's colour, the file's icon, name and "Edited ... ago"; sort by name, creation or last change either way; a table view; a click selects, a double-click or Enter opens, arrow keys move; Open, Show in folder and Remove from recents on right-click. Numbers unchanged. | | 2026-10-14 | minor | Lost images reported on loading (#207): a `.figcraft` file whose image member is damaged, too large or missing no longer loads in silence; each lost image (up to 20, then a count) is listed in `repairs` on `file.open` with its hash and the reason, and the app shows it as a toast. Saving still writes the file without the lost images. Numbers unchanged | | 2026-10-14 | minor | SVG import budgets (#190): gzipped SVG is unpacked by FigCraft within 64 MiB before parsing (it was unpacked without a limit), so the nesting check sees the real document; SVG documents embedded in `` data URIs share one 32 MiB budget and at most 4 levels, and one past a budget is left out. Linked files stay unread. Numbers unchanged | | 2026-10-14 | minor | Leptos code (#223): `inspect.code`, `inspect.componentCode` and `codeConnect.set` take `language: leptos`: a `#[component]` with `#[prop]` defaults, variant enums and `view!` markup, instances as calls, Code Connect `use` lines; the Dev Mode language list and rem units include it. Numbers unchanged | | 2026-10-14 | minor | Full screen (#227): View ▸ Full screen (`view.fullscreen {on?}`, F11 on Windows and Linux, Ctrl+Cmd+F on macOS) makes the window fill the screen and leaves it again; FigCraft's own item. Numbers unchanged | | 2026-10-14 | minor | Colour picker: wrapped rows of page and recent swatches stay inset on both sides instead of running past the picker's right edge. Numbers unchanged | | 2026-10-14 | minor | Server snapshots (#188): figcraft-server never replaces a readable snapshot with one too large to be read back, and reads snapshots up to twice the file cap (recent batches kept for reconnecting clients may take a file over it). Numbers unchanged | | 2026-10-14 | minor | Preferences owner-only on Unix (#200): `state.json`, which holds online owner tokens, is written with mode 0600 from the first byte, and an older world-readable file is narrowed. Numbers unchanged | | 2026-10-14 | minor | ruzstd 0.9.1 (#182): `.fig` import requires the release that fixes RUSTSEC-2026-0344 (a zstd decompression bomb). Numbers unchanged | | 2026-10-14 | minor | Layer cycles (#205): `validate` reports children listed twice, pages with a parent and layers not reachable from a page (cycles, detached subtrees); `repair` drops them in one linear pass. Numbers unchanged | | 2026-10-14 | minor | AppImage CLI: `figcraft.AppImage cli ` runs the bundled `figcraft-cli` (MCP server included) on Linux without a second download. Numbers unchanged | | 2026-10-14 | minor | Boolean outline depth (#193): nested boolean groups share one outline depth budget instead of each level getting its own. Numbers unchanged | | 2026-10-14 | minor | Command batches roll back as one: a failed `command.batch` (MCP `batch`) restores every open document and its state, and puts the tab in front back. Numbers unchanged | | 2026-10-14 | minor | Desktop builds no longer rebuild the text crate on every build when the craft-fonts manifest is missing. Numbers unchanged | | 2026-10-14 | minor | Direct3D 12 on Windows: the desktop app asks wgpu for Direct3D 12 there instead of Vulkan, so it starts without Vulkan loader errors on machines with stray overlay layers. Numbers unchanged | | 2026-10-14 | minor | Plugin metering (#192): every host call and every byte copied between a plugin and the host costs fuel, so a plugin can't stall the app through cheap-looking host calls. Numbers unchanged | | 2026-10-14 | minor | SVG/PDF export depth (#208): explicit preflight refusal beyond 256 descendant levels, propagated through public APIs, commands and CLI, with boundary/content and destination-preservation regressions. Numbers unchanged. | | 2026-10-13 | minor | MCP/control transport hardening (#201): frame limits during reads, bounded connections and both UI request queues, socket inactivity timeouts; percentages and estimates unchanged | | 2026-10-13 | minor | Plugin log limits (#191): cap each UTF-8 line at 8 KiB, total log bytes at 1 MiB and lines at 1,000 per run/callback before copying guest memory or forwarding to the host. Over-budget runs fail and roll back document edits; callbacks get fresh budgets. Added sandbox boundary/forwarding tests and an engine rollback regression. Percentages and hours unchanged. | | 2026-10-13 | minor | Live-session revert guard (M9, #198): `file.revert` is refused while its document belongs to a live session, including publishing, reconnecting and hosting before the welcome. The document, previews, branch tabs and session stay intact; leave the session before reverting a local save. Offline revert is unchanged. Scripted access and sync regressions in `live_access_tests`. Numbers unchanged. | | 2026-10-13 | minor | Repeat last command (M1, #181): keep the underlying command and parameters across repeats, reject self-reference, and retain one undo step per repeated edit. Numbers unchanged. | | 2026-10-13 | minor | Component playground (M3): with an instance, a component or a component set selected, the Inspect tab shows a Playground: a preview of the component and a control for each variant, boolean and text property. What is tried there changes the preview and the code in the Code section, never the file (no undo step, the canvas instance stays as it is); Reset goes back to the component's or the instance's own values. `component.playground {id?, properties?, language?, path?}` gives the same to scripts: the properties with their values, the code, and a PNG on request (`playground_tests`). Left: instance-swap and slot properties in the playground (the command takes an instance swap; the panel has no control for it), a code connect template's snippet with the tried values. Numbers unchanged. | | 2026-10-13 | minor | Auto layout, on-canvas handles of automatic gaps (M2): the handle between spread rows or columns (and between items with an auto gap) shows the distance the layout gives the gap instead of the stored value, and dragging it makes the gap a fixed one from there, as typing a gap in the panel does; one undo step (`gap_handle_tests`). Before, the drag changed a value that had no effect. The documentation is silent on dragging an automatic gap. Numbers unchanged. | | 2026-10-13 | minor | Variables inside styles (M3): a shared style can bind variables to fields of its value (`style.bind {style, field, variable?}`: a paint style's colours and gradient stops, an effect style's colour, radius, spread and offsets, a grid style's size, count, gutter and offset, a text style's font size, line height, letter spacing, family and style). Every layer using the style shows those fields resolved in its own modes and stays linked when a variable or a mode changes; the style keeps them resolved in the default modes. A style made from a layer keeps that layer's bindings; a new value drops bindings to what is gone. The style editor binds from its property rows (paint colour, shadow colour, blur radius, grid size) and detaches from the variable's chip; the style picker marks styles that use variables. `.fig` export and import carry the bindings on the style, REST layers hand theirs to the style they use. Left: binding text style fields and the other effect and grid fields from the editor (commands only), library variables in the picker, a style's library copy. Numbers unchanged. | | 2026-10-13 | minor | Effects on groups, inner shadows and background blur (M3): a group is no outline of its own, so its inner shadows are cast inwards from around what its layers draw together (their pixels, over them: no shade at a seam between two layers) and its background blur and glass show the backdrop through the outlines of its layers (under them). Canvas and PNG; SVG embeds a group with an inner shadow as a render and its background blur as an image under the layers; PDF places both as images, the blur matching the PNG. Where the group holds one shape the pixels match that shape, or a frame filled like it, carrying the effect (`group_effects_tests` in render, svg and pdf). Offsets and spreads of a group's inner shadow go by whole pixels. Numbers unchanged. | | 2026-10-13 | minor | Text made by commands on a dark page (M1): in the app, `text.create` and `node.create {type: text}` that name no fill give white text on a dark page, by the same rule as the Text tool (the page's colour decides, also inside a frame); a named `fill`, `fills` or `fillStyle` is kept. Sessions without an interface (CLI, MCP, exports) still make black text everywhere. Numbers unchanged. | | 2026-10-13 | minor | Frames and groups, a one-step convert (M1): `object.convertToGroup` and `object.convertToFrame` (the Design panel's type menu on a frame or a group) change the layer in place, its layers staying where they are on the page. A frame turned into a group loses what only a frame has (auto layout, with the positions it gave kept and fill children fixed; fills, strokes, corner radius, clipping, layout grids, guides, scrolling) and takes its layers' box; a group turned into a frame keeps its box, with no fill and no clipping. Instances, main components, layers inside instances and empty frames are refused with the reason, all or nothing; one undo step. Numbers unchanged. | | 2026-10-13 | minor | Prototyping, delays on mouse triggers and manual overlay offsets (M5): mouse enter, leave, down and up wait `delayMs` before their actions run (once while waiting; dropped when the screen or overlay they were on goes away; a pointer that leaves early doesn't cancel, the documentation is silent), and a manual overlay sits at `offset: [x, y]` from the top-left of the layer that opens it (without one, right below it, as before). Delay and X/Y fields in the interaction editor, the player on its own clock, REST import (`delay`, `overlayRelativePosition`), `.fig` import and export (the export's field for mouse delays is unchecked against other software). Numbers unchanged. | | 2026-10-13 | minor | Auto layout, wrapped-line alignment (M2): a wrapping layout's lines (rows of a horizontal one, columns of a vertical one) are packed where the cross alignment says (start, centre, end; before, they always sat at the start), stretch when every child fills across the flow, or take the free space between them (`contentAlign: spaceBetween`, the Design panel's Row/Column gap mode, the alignment pad lighting the whole cross axis). Read from REST JSON and `.fig`, written to `.fig`, `align-content` in CSS, an instance override with the other alignment settings. 13 hand-computed layout cases and command tests. Numbers unchanged. | | 2026-10-13 | minor | `.fig` export (gap 1): instance sizes (resized, filling an auto layout, scaled), variables with modes and bindings, shared styles and variable-width strokes are written and read back. Checked by opening written files in other software: resized and scaled instances keep their size and lay their layers out for it, variables with their modes and colour bindings resolve there (a frame set to another mode shows that mode's colours, also inside instances), colour, text, effect and grid styles are listed, and variable-width strokes that are the same on both sides of the path draw with their width; number, string and boolean bindings and bindings on effects, layout guides and gradient stops come across as the values they give but did not show as bound there (their entries now also name the layer field they set, not yet checked there), and strokes wider on one side of the path are written as filled outlines, no longer editable strokes there (the stroke itself is kept as plugin data and our own import puts it back; not yet checked there). Numbers unchanged | | 2026-10-13 | minor | Starter templates and a first run that explains itself: eight templates built in code through the command layer (mobile app, landing page, dashboard, design system starter, wireframe kit, social post sizes, presentation slides, icon set starter; original and synthetic, deterministic, about 10–190 ms each to build), `template.list` and `file.newFromTemplate` for the app, CLI and MCP, File ▸ New from template…, a Templates tab on the home screen (cards with thumbnails rendered from the documents, search, kind filter, keyboard and screen-reader names) in place of the Community placeholder, a row of templates on Recents while there are no recent files, a Getting started card that can be closed and comes back from Help ▸ Getting started (`help.gettingStarted`), and a first run that opens on the home screen. The Drafts placeholder is gone (the app has no drafts folder). Essentials ~57% → ~59% (discoverability ×0.92 → ×0.94), its hours ~60–110 h → ~55–100 h; other numbers unchanged | | 2026-10-12 | minor | Your online files (M9): a file put on a server without its key stays its owner's after its tab closes or the app quits. The app records each one with the preferences (server, id, name, owner token, the owner's links, the codes others join with; at most 200; the token never in a command result, `ui.inspect`, a log or debug output) and Home ▸ **Online** lists them: open one as its owner (a new tab, or its tab comes to the front), Start / Stop sharing, Copy code / Copy link, Rename on the server, Remove from server (confirmed), Forget (the file stays on the server). Nothing is asked of a server until the section opens; then one request per server (`Admin owned`, a compatible addition within protocol version 2) says which files are still there, and a server that is offline or older leaves the list as recorded. Tabs of files in a live session carry a mark (shared, or open only to you), toasts about a file that isn't in front name it, and after a crash a recorded file reopens from its server as its owner, its recovery copy kept until its unsent changes are back on the server (or it is opened beside the file). `collab.files`, `collab.openFile`, `collab.forgetFile`; `collab.manageFile` takes its server and token from the record and gains `share` / `unshare`. The draft catalogs (French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese) translate the 71 strings and 5 command labels they lacked and the new ones. Numbers unchanged | | 2026-10-12 | minor | Everyday editing partials closed (gap 4): number fields scrub a step a point (×10 with Shift, ×0.1 with Alt, on past the screen edge where the platform reports it), step with the arrow keys by the nudge preferences, take brackets, units and relative edits (`+10`, `*2`, `50%`), commit on Tab and nothing when left untouched, and on Mixed apply relative edits to each layer in one undo step; outline mode's Include hidden layers and Include object bounds (`view.outlinesHiddenLayers`, `view.outlinesObjectBounds`), drawn and clickable; Hide when publishing from the Assets panel (`component.hideWhenPublishing`); a cursor for every tool, modifier and hover target (rotate turned to its corner, pen states, eyedropper, comment, copy, not-allowed), with guides and handles keeping theirs; panel widths with limits, double-click reset and the preferences (`view.panelWidths`); a file or page made in the dark theme has a #1E1E1E page (sessions without an interface keep #F5F5F5) and text drawn on a dark page is white. Menu breadth 211/211. UI/UX fidelity 58% and the interaction pass 40–60 h unchanged at this rounding: the side-by-side comparison is what remains | | 2026-10-12 | minor | Sharing with a join code (M9, from the owner's use: sharing wasn't intuitive): the Share popover is one flow, **Online** (the file is published to the server in Preferences, built in `wss://collab.craftapps.dev/`, and the server names its edit and view links with nine-symbol codes like `K7M-2QX-9TD`) or **Local network** (the app hosts; a 20-symbol code carries its IPv4 address, port and a 44-bit secret, with check bits), one Start sharing button, the code large with Copy code and Copy link, who is here, can edit / can view online, and Stop sharing (online: the others' links and codes are revoked and they are disconnected, while the file stays on the server with its owner in it and can be shared again with new codes; removing it is a separate, confirmed step); **Join with a code** is one field (popover, home screen, Join dialog) for a code of either kind or a link; addresses, keys and ports moved under Advanced and Preferences ▸ Server for online sharing (reset, or empty to turn online sharing off). Protocol additions within version 2 (`Resolve` / `Resolved`, `Admin code`): the server keeps a stretched hash and the wrapped token, never the code, and limits wrong codes (10 a minute per address, a 15-minute lockout after 10 wrong, 1,000 wrong an hour for everyone); codes go with their link and file. Older servers leave the links in place of a code; older apps join by link. `collab.share`, `collab.join {code}`, `code` and `share` in `collab.peers`; the browser joins by code. Numbers unchanged | | 2026-10-12 | minor | Live sessions per document (M9, a fix from real use): the engine keeps one session per open document instead of one for the app, the link carries several under their own ids (desktop, browser, tests), the window and the `collab.*` commands follow the active document, `collab.peers` gains `session` and `sessions`, `ui.inspect`'s `live` gains `session` and `sessions`; joining opens the session's file in its own tab without touching other sessions and closing a file ends its session only. Numbers unchanged | | 2026-10-12 | minor | `.fig` export checked by opening written files in other software (gap 1): layers, fills, gradients, strokes, shadows, vectors, components and text draw as in FigCraft, after fixes to what the export writes (visible document and pages, the format's schema numbering, outlines, text settings, font style names, line height units); effects such as glass and noise, images and prototype interactions still unchecked there. Numbers unchanged | | 2026-10-12 | minor | Public `figcraft-server` hardened against resource exhaustion after a security review: messages capped by what a connection is for (a file's size when open), byte-bounded send queues and a bound on bytes waiting for the hub, files checked as they grow (refused batches roll back with the reason; per-address and total caps count growth), the core's recent-batch memory bounded in bytes, no confirmation for changes that couldn't be written, team libraries only in key-published files on public servers with unpacking bounded by the file cap, usage reports kept in memory, layered IPv6 limits (/64, /56, /48), a global publish rate, connections reserved for sessions, per-address download shares, library connections cut on revoke and removal, a private data directory and key, and safer install.sh, systemd and backups. Percentages and hours unchanged | | 2026-10-12 | minor | A public `figcraft-server` (open hosting): with `open_publish = true` anyone publishes a file without the server key and gets its edit and view links plus an owner token (128 bits, shown once, stored hashed) that lists, makes and revokes its links, renames it or removes it (`collab.manageFile`); limits per address (connections at once and a minute, messages a second, publishes an hour, files and MiB kept; IPv6 by /64; `X-Forwarded-For` believed only from `trusted_proxy`; tables capped at 10,000 addresses) and for everyone (`max_total_mb`), each refusal saying why; files published without the key expire after `idle_days` unopened (the date shown to everyone in the file); moderation (`files list --sort size|age|idle`, `block-ip`, `reports`, a Report button and `collab.report`); the operator's contact and terms shown in the Share dialog; `GET /healthz` on the same port; `packaging/server/install.sh` for a Debian/Ubuntu machine behind Caddy and a nightly backup timer. Protocol additions within version 2, gated like signals (older apps get clear refusals). Hosting infrastructure rather than a Figma feature: percentages and hours unchanged | | 2026-10-12 | minor | Experimental `.fig` export (gap 1): `file.export {format: "fig"}`, File ▸ Export… ▸ .fig (experimental), the CLI and MCP write the whole document (layer tree, geometry, paints with image bytes, strokes, effects, text with styled runs, vector networks, auto layout, constraints, components, sets, instances with their overrides and property values, prototype interactions) with a kiwi encoder mirroring the decoder and the schema of the fields written; what's left out is a warning; verified only by round trips through our own importer. File formats 59% → 60%; import/export 84%/57% → 86%/58%; file exchange stays ×0.85; mainstream hours ~130–244 h → ~120–229 h; feature breadth (~84%), ready for real work (~53%), essentials (~57%), mainstream (~49%), beta (~155–280 h) and full parity (~265–523 h) unchanged at this rounding | | 2026-10-12 | minor | Keyboard shortcut parity suite and bindings (gap 4): a table test with one row per shortcut in the public documentation, macOS and Windows/Linux keys each (129 rows: 126 bound, 110 before; 1 moved off a documented conflict; 2 missing; 2 kept by a platform), plus 48 pinned undocumented bindings so every default binding is in the table; newly bound: A (Frame), Shift+' (pixel grid), Alt+Cmd+\ (others' cursors, new `view.multiplayerCursors`), Ctrl+F6, Shift+Cmd+U (link), Cmd/Ctrl+] and [ (list levels while editing, new `text.listIndent` / `text.listOutdent`), Cmd/Ctrl+Backspace (ungroup), Alt+Space / Ctrl+Space (keyboard box selection, Cmd/Ctrl+arrows draw it out), suggest auto layout, go to main component, arrows that pan with nothing selected (new `view.pan`), Control suspending snapping on macOS, Space/N/C/F while presenting; symbols another layout types with Shift count without it; no default binding uses a system-reserved chord (tested). Menu breadth 207/207 (View ▸ Multiplayer cursors). UI/UX fidelity 58% and the interaction pass 40–60 h unchanged at this rounding | | 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Localization 36% → 39%, 31–54 h → 29–51 h; ready for real work unchanged (~53%); full parity ~267–527 h → ~265–524 h | | 2026-10-12 | minor | Latin American Spanish overlay (gap 5): `es-419.toml` lists only the 87 entries (61 UI strings, 26 command labels) whose wording differs in Latin American usage (agregar for añadir, presionar for pulsar, configuración for settings, Enter for the Intro key, control for a gamepad, resorte for a spring) and reads `es.toml`, then English, for the rest (a regional catalog overlays its language's); same tú register as Spanish; a draft awaiting native review. Preferences ▸ Language lists Español (Latinoamérica); System maps es-419, es-MX, es-AR, es-CO and the other Latin American and US Spanish tags to it, es-ES and es to Spanish. Localization 35% → 36%, 32–55 h → 31–54 h; ready for real work unchanged (~53%); full parity ~268–528 h → ~267–527 h | | 2026-10-12 | minor | Prototyping tail (gap 15, the part that doesn't need video): key/gamepad triggers take controller buttons (`Gamepad A` … `Gamepad Home`, the standard layout's 17 buttons, from a Controller menu beside the key field; the REST import maps controller triggers instead of skipping them); the player reads controllers on Windows (Windows.Gaming.Input through the `windows` crate the app already used; no new dependency) and in the browser (the Gamepad API), polling only while the prototype has such a trigger and the window has the focus, and the control channel presses buttons (`ui.input {gamepad}`); macOS, Linux and FreeBSD read no controllers yet. Widgets react to clicks in a presented prototype (the canvas's callback and sandbox limits, on the prototype's copy: the file and its history don't change, restarting starts over). `proto.evaluate` computes an expression where a layer's interaction would run (MCP); the mode lookup and set-variable-mode rules are cases in a behaviour suite (they already held). Prototyping breadth 90% → 92%, ready unchanged at 74%, 3–6 h → 2–5 h; area hours 120–260 → 119–259 (core 33–75 → 32–74), Features (depth) 29–68 h → 28–67 h; percentages unchanged at this rounding; full parity ~276–538 h → ~275–537 h | | 2026-10-12 | minor | Plugin directory on `figcraft-server` (gap 6): the server keeps plugin bundles (manifest, module, up to 32 assets; an uncompressed length-prefixed format named by its SHA-256) published with the server key (`figcraft-cli plugin publish`, `plugin.directory.publish`), checked in full before they are kept (size cap, manifest and plugin API version, a version newer than the one kept, the module compiled but never run, plain asset names) and capped (`max_plugins`, `max_plugin_mb`, `max_plugins_mb`); anyone who reaches the server lists, searches and fetches them (hash-checked again on read, a shared download budget); the operator moderates with `figcraft-server plugins [remove]`. The app's Plugins ▸ Manage plugins gains a Browse tab: a server's plugins with Install / Update / Remove, an install confirmation that shows the permissions asked for, the source and the SHA-256, the download checked against the listed hash before anything is read, plugins kept per user in hash-named folders; `plugin.directory.list` / `install` (needs `confirm: true`) / `publish` / `unpublish` for scripts and MCP. Not in the browser build yet; no public catalogue, publisher accounts, signatures, ratings or background update checks. Plugins & widgets 55%/32% → 60%/35%, 10–30 h → 8–26 h; Ecosystem dimension 34% → 38%; weighted totals unchanged at this rounding; full parity ~281–548 h → ~279–544 h | | 2026-10-12 | minor | Japanese and Korean draft catalogs and CJK UI fonts (gap 5): `ja.toml` and `ko.toml` translate every UI string (1,351 keys; plurals give only `other`, and `plural_one` never picks "one" in either), command label (519) and menu wording (29), machine-drafted and marked as drafts awaiting native review; Preferences ▸ Language lists 日本語 and 한국어, System picks ja* and ko*. CJK UI text renders: once the UI language, the open file's names or comments, typed text or the language picker needs Japanese kana, Hangul or Han, the desktop app appends one installed face per script to egui's fallbacks (Windows Yu Gothic UI / Meiryo / Malgun Gothic / Microsoft YaHei, macOS Hiragino Sans / Apple SD Gothic Neo / PingFang SC, Linux Noto Sans CJK; the craft-fonts build's BIZ UDPGothic first), reading only that face's file and sharing its bytes with egui (English users load none: startup and memory unchanged); the web shows Japanese and Korean only when built with craft-fonts' Noto Sans CJK (`FIGCRAFT_EMBED_CJK`), else it keeps them out of the picker and System. Localization 30% → 35%, 40–65 h → 32–55 h; ready for real work ~52% → ~53% (52.9 → 53.1); full parity ~284–554 h → ~276–544 h; beta ~162–292 h → ~155–280 h | | 2026-10-12 | minor | Plugin widgets and the panel tail (gap 6, plugin API 3; v1 and v2 plugins unchanged): a plugin marked as a widget puts a layer of its own on the canvas from Plugins ▸ Widgets or `widget.insert`: it describes the layer (frames with auto layout, text, rectangles, ellipses, images it carries) and FigCraft validates the description (size, layer count, depth, numbers, colours, text and image sizes) and builds it as a frame with locked layers inside; a click on a part the plugin named (on the canvas with the Move tool, a pointing hand over it, or `widget.click`) calls the plugin back in a fresh, metered sandbox with the widget's state, as one undo step, and the widget is redrawn; the state lives in the widget's own layer data, so widgets survive save and reload, copying, undo, export and live sessions; no plugin can write that data or call `widget.*`. `widget.list` lists them. A Counter example widget. Plugin panels of different plugins open side by side, and a colour field's swatch opens the colour picker (the plugin hears of the colour when a drag ends). Plugins & widgets 45%/28% → 55%/32%, 15–40 h → 10–30 h; Ecosystem dimension 28% → 34%; weighted totals unchanged at this rounding; full parity ~286–558 h → ~281–548 h | | 2026-10-12 | minor | French, German and Brazilian Portuguese draft catalogs (gap 5): every UI string, command label and menu wording (1,899 entries) machine-drafted in each and marked as drafts awaiting native review, falling back to English per key (new strings need only English and Spanish); 0 counts as singular in French and Brazilian Portuguese; System picks the first OS language with a catalog (pt-PT and pt read pt-BR); long translations elide with a tooltip in dropdowns, two-column labels and the home cards. Localization 20% → 30%, 45–75 h → 40–65 h; ready for real work unchanged (~52%); full parity ~289–564 h → ~284–554 h; beta ~170–305 h → ~162–292 h | | 2026-10-12 | minor | Cursor chat and spotlight in live sessions: `/` opens a chat bubble at your pointer that everyone sees as you type and that fades a few seconds after the last change; a spotlight (your avatar's menu, `collab.spotlight`) makes everyone follow your view and page until they move their own. Ephemeral presence the host validates and rate-limits, passed only to clients that read it (older clients keep working). Multiplayer & sharing breadth 15% → 17%, ready unchanged (5%), 45–90 h → 42–84 h; collaboration 13% unchanged, 49–98 h → 46–92 h; feature breadth (~84%) and ready for real work (~51%) unchanged at this rounding; full parity ~301–587 h → ~298–581 h | | 2026-10-12 | minor | Layers and Assets panels behaviour suite written from the public documentation (6 engine tests with about 50 table rows and 5 panel tests driven through the UI; about 30 rows that the code before the fixes contradicts) and its fixes (several layers dropped together, Collapse layers, eye and padlock sweeps, Rename layers dialog with the documented number codes, page dragging, dividers, "Copy of" duplicates, Assets folders, hidden-from-publishing names both ways). Layers panel 98%/82% → 99%/85%; percentages and full parity hours unchanged at this rounding | | 2026-10-12 | minor | Vector multi-edit and the vector edit tail, with a behaviour suite written from the public documentation (5 tests, about 30 table rows and steps, and a junk-parameter sweep of every vector command in multi-edit mode): several vector layers are edited at once (Enter, double-click or `vector.edit.begin {ids}`; a press on another layer's point, handle or segment makes it the active layer; Shift-click, marquee and lasso select across layers; dragging, the arrows, Delete, delete and heal, mirroring, smoothing, corner radius and point caps and joins reach every layer's selected points in one undo step; the pen, bend, paint bucket, cut and eraser work in the active layer); several Bezier handles are selected with Shift-click and dragged together (`vector.edit.moveHandles`); a sharp point next to a curved segment rounds (the curve is cut where the arc meets it and keeps its shape); a point can have its own stroke cap and join (`vector.edit.strokeCap`, `vector.edit.strokeJoin`, the Point section of the Design panel; canvas, PNG, SVG and PDF, outline stroke); the Pen outside edit mode connects to one of its own points and ends the path, and continues a selected open path from an end point. Left: corner smoothing beyond rectangles, the transform box around points of several layers, per-point caps and joins on dashed, variable-width and brush strokes. Shapes & vector 98%/72% → 99%/73%, 2–4 h → 1–3 h; Figma Draw extras 80%/55% → 88%/60%, 2–5 h → 1–3 h (gap 9: pen pressure on macOS and Linux tablets left). Totals recomputed by the doc's method, which rows since the last recompute had left stale: the area table's weighted breadth is 83.7% (~83%; core 94.1%, ~94%) and ready 63.1% (~63%; core 72.1%, ~72%), hours 133–284 (core 38–83); Features (depth) 61% → 63%, 46–94 h → 34–76 h; ready for real work 52.6% (~52%); mainstream weekly areas 71.0% → 72.1% (× 0.9 × 0.9 × 0.85 = 49.7%, ~49%, rounded down); essentials unchanged (~57%); full parity ~306–595 h → ~294–572 h (on top of the rows merged since: ~301–587 h → ~289–564 h; area-table totals recomputed on the merged table and rounded down: breadth ~84%, ready ~63%, core ~94%/~72%, mainstream weekly areas 72.4%, ~49%) | | 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-12 | minor | Extended collections' tail (gap 14): variables a library adds to a collection this file extends now count as a library update of their own (listed by name without a copy yet) and arrive inheriting when accepted, instead of waiting for the library's next other update; a library collection that is itself an extended collection can be extended again, values flowing down the chain as each file accepts updates with every level's overrides kept (checked end to end). Styles & variables breadth 94% → 95%, ready unchanged (76%), 3–5 h → 2–4 h; gap 14 1–2 h → 0–1 h (real-library checks left); feature breadth (~82%) and ready for real work (~51%) unchanged at this rounding; full parity ~305–594 h → ~304–593 h | | 2026-10-12 | minor | A branch's own version history and finer list merges (gap 17): versions saved or autosaved on a branch tab go into the branch (viewed, restored, renamed, deleted and duplicated there, saved in the file with it); merging adds a version to the main file's history and the merged or archived branch keeps its own; fills, strokes and effects merge item by item, conflicting only when the same item changed both ways, an item was removed on one side or both sides added different items. Comments, versions, find, misc breadth 85% → 87%, ready 60% → 61%, 2–5 h → 1–4 h; gap 17 1–3 h → 1–2 h; feature breadth (~82%) and ready for real work (~51%) unchanged at this rounding; full parity ~306–595 h → ~305–594 h (the Features table here still showed 80%/58%/4–8 h from before branches landed) | | 2026-10-12 | minor | Headline percentages recomputed from the area table by its own method (weights × written scores): feature breadth ~82% → ~84% (core editor ~93% → ~94%), weighted ready ~61% → ~63%, core ready ~70% → ~72%, mainstream weekly areas 71.3% → 72.3% and mainstream ~49% → ~50%; several earlier rows had moved the area scores without carrying them into the totals. Hours are unchanged (left for the next full re-measure) | | 2026-10-12 | minor | Text lists, hanging punctuation and decoration styles, with a behaviour suite written from the public documentation (7 layout tests and 7 command tests, about 60 table rows): lists are set per paragraph (the list buttons, shortcuts and Type settings act on the paragraphs the selection touches, the whole layer when it isn't being edited), nest up to 8 levels (Tab and Shift+Tab; numbers go 1. a. i., bullets • ◦ ▪, counted per level), Enter on an empty item and Backspace at an item's start step out a level and then out of the list, typing `- `, `* ` or `1. ` at a paragraph's start starts one; list spacing (between items, in place of paragraph spacing), hanging lists (markers outside the box) and hanging punctuation (opening punctuation outside a start-aligned edge, closing punctuation outside an end-aligned one, taking no room when lines wrap); underline and strikethrough style (solid, dotted, wavy), thickness, underline offset, skip ink and colour. Canvas, PNG, SVG and PDF; `node.set` keys (`indentation`, `listSpacing`, `hangingPunctuation`, `hangingList`, `textDecorationStyle`/`Thickness`/`Offset`/`SkipInk`/`Color`), inspect (`lineTypes`, `lineIndentations`), CSS (`text-decoration-style`/`-thickness`/`-color`, `text-underline-offset`, `text-decoration-skip-ink`) and the REST import (`lineTypes` with `lineIndentations` per paragraph, which collapsed to one list style before). Text 87%/68% → 90%/70%, 5–10 h → 3–7 h; mainstream weekly areas 71.0% → 71.2%; other percentages unchanged at this rounding; full parity ~306–595 h → ~304–592 h | | 2026-10-12 | minor | Comments and version history behaviour suite written from the public documentation (15 tests, about 80 table rows plus stepwise checks; 4 tests failed before the fixes) and its fixes: in a live session, threads made in the session couldn't be replied to, edited, reacted to, resolved, moved, deleted or opened (their ids, from their creator's id space, were refused as out of range); restoring a version brought back that version's comments, losing newer threads and replies (comments now stay, those on layers the version lacks staying where they were); restoring now keeps the replaced design in the history as an autosave first, unless a version of it was just saved or restored; giving an autosave a title makes it a named version (it could be dropped to make room); merging a branch adds the main file as it was and then a `Merged “”` version to its history. Pinned where the documentation is silent: pins go to the innermost frame under them, copies, duplicates and duplicated pages come without comments, replying reopens a resolved thread, versions stay with whoever saved them in a live session (a shared history needs accounts). Left: a branch's own version history, comparing two versions. Comments, versions, misc 85%/60% → 85%/62%, 2–5 h → 2–4 h (this table still showed 80%/58%/4–8 h from before branches landed); percentages unchanged at this rounding; full parity ~306–595 h → ~306–594 h | | 2026-10-11 | minor | Canvas navigation, rulers, guides, layout grids, outline mode and measuring behaviour suite written from the public documentation (18 tests, about 120 table rows and checks) and its fixes: the pixel grid shows from 400% (it waited for 800%); guides belong to the page or a top-level frame (a guide dropped over a nested frame goes to the frame holding it, and a frame moved into another keeps its guides but they stop showing, snapping and measuring); a guide can be selected, deleted with Delete, nudged with the arrows and removed from the context menu (`guide.select`, `guide.remove` without an index); Alt-dragging a guide drops a copy and holding Alt while dragging one measures to the nearest edges in the frame it crosses; columns and rows count Auto (as many fixed-size ones as fit); layout grids don't show or snap on a rotated frame; in outline mode shapes are clicked on their outlines, reaching layers behind. Left: outline mode's Include hidden layers and Include object bounds. Canvas & navigation 97%/72% → 98%/74%, 2–5 h → 1–4 h (this table still showed 95%/72%/3–6 h from before the outlines landed); percentages unchanged at this rounding; full parity ~320–615 h → ~319–614 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 ``), 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}`). Import/export 80%/50% → 82%/53% (the row still showed the figures from before `.fig` import); Dev Mode 90%/64% → 93%/70%; mainstream weekly areas 68.1% → 68.4%; percentages and full parity hours unchanged at this rounding | | 2026-10-11 | minor | `.fig` import's tail (gap 1): vectors open as editable vector networks (points, curved segments, filled areas with their winding, per-point corner radius and handle mirroring, end caps from the end points), falling back to outlines when the network is missing or damaged; sticky notes, shapes with text, connectors, tables, code blocks, slides and similar layers import as the closest frames, text layers and vectors with a warning instead of being dropped; set-variable, set-variable-mode and conditional prototype actions come across. New parsers fuzzed on fixtures generated in code. File formats 55% → 58%, 8–15 h → 5–10 h; import/export breadth 80% → 82%, ready 50% → 54%; feature and area totals and ready for real work (~49%) unchanged; beta ~173–310 h → ~170–305 h; full parity ~321–618 h → ~318–613 h | | 2026-10-11 | minor | Shapes and vector behaviour suite written from the public documentation and its fixes: rounded vector points are drawn and exported, a boolean group looks like its top layer (a subtraction like the bottom one), Alt draws lines from the centre, stars keep to 3–60 points, delete and heal follows the curve, Shift-click on a point at a corner of the points' box deselects it, outline stroke keeps the stroke's colour style, opacity and effects, the miter field shows degrees, individual corners only for rectangles and frames, Option/Alt+Shift+F flattens, MCP `key` reaches the edit tools. Added: corner radius and star ratio handles, Count and Ratio fields, cut drags that divide shapes into layers, paint bucket drags. Shapes & vector 97%/68% → 98%/72%, 1–3 h → 2–4 h. Totals recomputed from the area table, which earlier rows had left stale: feature breadth ~80% → ~82% (core ~91% → ~93%), Features (depth) 57% → 61% (core 68% → 69%), 60–115 h → 56–109 h, ready for real work ~49% → ~51%, mainstream ~46% → ~48%; full parity ~321–618 h → ~319–615 h (the selection suite's −3/−5 h hadn't reached the totals) | | 2026-10-11 | minor | Paint and effects behaviour suite written from the public documentation and its fixes: Normal groups and frames isolate their children and clipping frames don't (canvas, SVG, PDF); plus darker drawn on the canvas and in PNG exports; drop shadows hidden behind what the layer covers unless shown behind transparent areas, SVG included, with filter regions that reach as far as the effects; Fill images cover the box to its edges; "Normal" names Normal in commands and the blend menu. Paint 85%/66% → 87%/70% (breadth from the GIF/TIFF row), 5–9 h → 3–6 h; effects 72% → 75%, 3–6 h → 2–5 h; percentages unchanged at this rounding; full parity ~321–618 h → ~319–616 h | | 2026-10-11 | minor | Frames, groups, sections and masks behaviour suite written from the public documentation (15 frame, group and section tests, 6 mask tests) and its fixes: a layer a mask hides can't be clicked there (clicks reach what's behind; Select layer agrees), a mask group's box and exports are the mask's (plus any layers under it), sections stay on the page or in sections (moves, drags, pastes, grouping and new sections in frames refused or kept on the page), drawing a frame or section around layers puts those fully inside it in it, resizing a section leaves its contents where they are (and a frame resized with Cmd/Ctrl held keeps its layers' sizes too), and X/Y of layers in groups are measured in the frame (groups have no coordinate space). Frames, groups, sections, masks 95%/75% → 97%/80%, 2–4 h → 1–3 h; percentages unchanged at this rounding; full parity ~323–621 h → ~322–620 h | | 2026-10-11 | minor | Styles and variables behaviour suite written from the public documentation (18 command-level tests) and its fixes: editing a styled fill, stroke, effect or layout grid list (directly, a paint row, an added fill or a bound variable) unlinks that style instead of being overwritten by the next style edit; applying a style replaces variables bound to what it sets; text styles go on text layers only; deleting a variable (or its collection) unbinds the layers using it, which keep their last value, and aliases to it take its values; bindings are type-checked (a colour variable can't drive a width) and number variables fill text content; aliases are checked when a variable is created. Added: duplicate, move (reorder, into a folder) and rename folder for styles, with a Local styles list grouped by type and folder (click to edit, right-click, drag to reorder); duplicate mode, set as default and move column for modes; per-platform code syntax (Web, iOS, Android) used by generated CSS and the variables JSON; font weight, paragraph spacing/indent and text content scopes for number variables. Found missing: variables inside styles. Styles & variables 92%/72% → 94%/76%, 1–2 h → 3–5 h; percentages unchanged at this rounding; full parity ~323–621 h → ~325–624 h | | 2026-10-11 | minor | Prototyping behaviour suite written from the public documentation (7 model tests, 12 player tests with 21 gestures) and its fixes: on drag starts only for a drag its animation's way and the animation follows the pointer, key combinations (`Shift+K`) fire (and are recorded and imported), an overlay's after-delay timer starts when it opens, instances of interactive components play their variant's interactions and "Change to" changes the right instance, spring presets are physical springs that play until they settle, smart animate overshoots with spring and back easings, fixed layers draw above scrolling content; added scroll to offsets. Prototyping 88%/70% → 90%/74%, 5–9 h → 3–6 h; mainstream weekly areas 68.1% → 68.4%, mainstream ~46% → ~47%; other percentages unchanged at this rounding; full parity ~320–615 h → ~318–612 h | | 2026-10-11 | minor | Components behaviour suite written from the public documentation (10 tests, 45 table cases) and its fixes: instances now keep overrides of auto layout padding, gap and alignment, layout grids, export settings, prototype interactions and every text property (resizing, paragraph spacing, lists…; these were lost when the main changed); moving, rotating, resizing or re-constraining layers inside an instance, and its auto layout direction, are refused (the instance itself resizes); switching variants keeps an override only where both variants had the same value (text layers keep theirs); swapping from the Assets panel keeps only text overrides; the default variant is the top-left one (instances, Assets thumbnails, code); hidden exposed instances hide their properties; pushing changes from a nested layer goes to the outer main. Added: variant property reordering and duplicate combination warnings. Components 95%/68% → 96%/72%, 6–12 h → 4–9 h; with #133's selection and transform change, core editor ready ~68% → ~69% and mainstream ~46% → ~47% (weekly areas 68.1% → 68.8%); full parity ~320–615 h → ~315–607 h (the totals now also carry #133's area 2 hours) | | 2026-10-11 | minor | Embedded image colour profiles (ICC in PNG/JPG/WebP/TIFF, PNG sRGB/gAMA/cHRM) converted into the file's colour space when decoded. Paint 85%/66% → 90%/67% (the row had lagged the parity doc), 5–9 h → 3–6 h; totals unchanged at this rounding | | 2026-10-11 | minor | Layers panel keyboard and stroke variables: F2 renames the selected layer (also from a layer row with the keyboard, which gets it back; Escape there cancels), Shift+F10 (`view.contextMenu`) opens the context menu at the focused row or below the canvas selection and the menu takes the keyboard, F8 (`select.area`) draws a selection box for keyboard-only users (arrows move it, Shift+arrows size it, Enter selects like a marquee; agents pass a box), long lists checked at 10k layers (revealing a layer, expanding or collapsing everything and arrow keys take at most 1.3 ms a frame in the release UI bench; the revealed row takes the keyboard); colour styles on strokes were already complete, and each side's stroke weight now binds to a number variable (`strokeTopWeight`…, bindable fields in the Design panel and REST import). Layers panel 95%/78% → 98%/82%, 2–4 h → 1–2 h; Strokes 90%/72% → 95%/75%, 1–3 h → 1–2 h; mainstream depth 67.8% → 68.1%; feature breadth (~80%), ready for real work (~49%), essentials (~57%) and mainstream (~46%) unchanged at this rounding; full parity ~321–618 h → ~320–615 h | | 2026-10-11 | minor | Text behaviour suite written from the public documentation (13 text cases, 5 command tests) and its fixes: CJK lines break between ideographs, kana and Hangul and before and after Latin words without breaking inside them, with the line-start and line-end rules (closing punctuation, small kana and the prolonged sound mark never start a line, opening brackets never end one); a style's line height, baseline and decoration come from its own font, so lines with CJK or emoji drawn by fallback fonts keep the spacing of the others; paragraph indent applies to start-aligned text only; carets, Delete and Backspace step over whole grapheme clusters (emoji sequences, flags, combining marks), which also shape with one font; double-click and word moves take one script's run in text without spaces. Added: vertical trim (cap height to baseline; `leadingTrim`, Type settings, REST import, inspect, CSS `text-box`). Text 85%/64% → 87%/68%, 7–13 h → 5–10 h; percentages unchanged at this rounding; full parity ~323–621 h → ~321–618 h | | 2026-10-11 | minor | Selection and transform behaviour suite written from the public documentation (5 selection tests with 38 table rows, 11 transform tests) and its fixes: Alt-resizing shrinks about the centre too (it could only grow); groups, frames with Scale or stretch constraints and several layers with different rotations scale each layer along its own axes, keeping its rotation and flip (children turned 90° grew along the wrong axis, nested turned groups too); several layers dragged past an edge mirror and flip; a flipped layer reads back the rotation typed; resizes land on whole pixels with the pixel grid on; a press inside a layer smaller than its handles moves it and the nearest handle wins (the first in reach did), with its handles hidden under 16 px; locked layers selected in the layers panel get no handles and aren't dragged with the rest; the background of a top-level frame with layers starts a marquee inside it (it moved the frame); Select inverse skips hidden layers like Select all. Added: equal spacing snapping with labelled gaps, Cmd/Ctrl while resizing a frame ignores its constraints. Selection & transform 95%/68% → 97%/72%, 6–10 h → 3–5 h; core editor ready ~67% → ~68%; feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~345–666 h → ~342–661 h | | 2026-10-11 | minor | Windows QA pass (gap 8): everyday workflows on Windows 11 in the desktop app and through MCP; eight fixes: recovery copies of unsaved documents reopen after a crash or a forced quit (`file.recover`), Ctrl moves and deletes by word in text on Windows and Linux, scrubbed fields keep their value, typing on a field Tab reached edits it, Escape closes dialogs, linear gradients keep square bands, panel hints wrap, the window-close question and "Open in new window" are translated. Percentages and hours unchanged | | 2026-10-11 | minor | Auto layout behaviour suite written from the public documentation (64 layout cases, 8 command tests) and its fixes: a frame is never smaller than its padding, only the frame's inside stroke counts when strokes are included (and new auto layout includes them), auto gaps stop at 0, grid `fr` tracks behave like the CSS `fr` unit (a track never gets less than its content), canvas stacking is drawn, exported and hit (it was stored only). Added: auto gaps evenly and around, vertical wrap, grid cell alignment, strokes and canvas stacking controls. Auto layout 95%/68% → 98%/74%, 6–12 h → 3–6 h; percentages unchanged at this rounding; full parity ~348–672 h → ~345–666 h | | 2026-10-11 | minor | Display P3 honest minimum (gap 18): the colour picker of a Display P3 file warns when a colour is outside sRGB (Outside sRGB, with what the canvas and exports do), and View ▸ Soft-proof sRGB (`view.proofSrgb`) stripes the canvas pixels sRGB can't show. Why the canvas still shows sRGB: wgpu 30 can present Display P3 or extended-range sRGB surfaces (Metal, DX12, Vulkan, WebGPU canvases), but eframe/egui-wgpu 0.36 configure an 8-bit sRGB surface with no option for the format or colour space, and egui's renderer writes sRGB-encoded UI colours; showing P3 needs those two upstream (or a patched egui-wgpu). Hardware 13–27 h → 12–26 h; gap 18 6–12 h → 5–11 h; percentages, feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~363–697 h → ~362–696 h | | 2026-10-11 | minor | Several windows (gap 18): File ▸ New window, a tab's Open in new window or a tab dragged off the tab strip opens files in another window with its own tabs, panels and canvas, sharing the files, preferences, libraries and clipboard; each window draws only when it needs to (idle: ~0% CPU), closing one closes its files after asking about unsaved changes (the main window too, which takes over another window instead of quitting; the last window quits), and the control channel and MCP open, list, focus and capture windows (`window.new`, `window.list`, `window.focus`, `window` on any request); the screen-reader tree covers the main window only (eframe 0.36), so with a screen reader running files stay tabs; one window in the browser. Hardware 55% → 60%, 13–27 h → 10–21 h; gap 18 6–12 h → 4–8 h (P3 on screen left); feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~363–697 h → ~360–691 h | | 2026-10-11 | minor | Localization layer landed (gap 5): per-language catalogs baked in at build time with English text as keys and fallback, placeholders and plurals, command labels by id, Preferences ▸ Language (System follows the OS or the browser) applied live and `pref.language`; English catalog and a complete Spanish draft awaiting native review. Localization 10% → 20%, 60–100 h → 45–75 h; ready for real work unchanged (~47%); full parity ~363–697 h → ~348–672 h; beta ~200–365 h → ~195–355 h | | 2026-10-11 | minor | Hosting `figcraft-server`: the desktop app and `figcraft-cli` open `wss://` links (rustls with Mozilla's root certificates, always verified; `FIGCRAFT_CA_FILE` adds a private authority), releases ship the server for Linux x86_64 and aarch64, it stops gracefully on SIGTERM or Ctrl+C, and `docs/server.md` has a deployment guide (systemd unit, Caddy, nginx, Docker); percentages and hours unchanged until the next re-measure | | 2026-10-11 | minor | Plugin panels and settings landed (gap 6, ABI v2; v1 plugins unchanged): a plugin shows a panel of notes, fields (text, number, checkbox, menu, list, colour) and buttons drawn with FigCraft's widgets; a button or a changed value calls it back with every value (same limits per callback, one undo step per callback that edits, rolled back on failure), its module staying loaded while the panel is open; `plugin.ui` / `plugin.event` / `plugin.close` drive panels headless; per-plugin settings (64 KB) kept with the preferences; the Swatch grid example gained a palette panel that remembers the last palette. Plugins & widgets breadth 35% → 45%, ready 20% → 28%, 25–55 h → 15–40 h; ecosystem 20% → 28%; feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~373–712 h → ~363–697 h | | 2026-10-11 | minor | Canvas fixes (gap 13 closed): boolean groups are clicked where their result is (a click in a subtracted hole reaches what's behind; with the group or an operand selected its operands are hit, so each can still be picked), a layer dragged inside a group stays in the group (frames and auto layout still take other drops), View ▸ Frame outlines and View ▸ Slices draw on the canvas, and the idle tests wait for the render thread instead of counting frames. Shapes & vector 1–3 h → 1–2 h (vector QA with real files left); percentages, feature breadth (~79%), ready for real work (~47%) and full parity hours 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 ~378–722 h → ~375–717 h | | 2026-10-11 | minor | Stretch and scatter brushes and pen pressure landed (gap 9): eight brushes drawn in code (four stretched along the path, four scattered with spacing, size, rotation and scatter jitter from a seed) in the stroke settings, drawn everywhere strokes are and flattened by outline stroke; the Pencil records pen pressure as the stroke's width profile (Windows pens, browser styluses; macOS and Linux tablets report none) and can draw with a brush; Figma Draw extras breadth 45% → 65%, ready 30% → 45%, 10–18 h → 5–9 h; Strokes 2–4 h → 1–3 h; Hardware 50% → 55%, 15–30 h → 13–27 h; feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~378–722 h → ~358–687 h | | 2026-10-11 | minor | Text on a path and pattern fills landed (gap 9): text follows a path or shape (start, side, alignment; edited in place; canvas, PNG, SVG and PDF; SVG `` imports as text on a path) and pattern fills tile a layer (grid or half-shifted rows or columns, scale, spacing, alignment; live as the source changes; SVG ``, PDF tiling patterns); Figma Draw extras breadth 45% → 65%, ready 30% → 45%, 10–18 h → 5–10 h; Text 8–14 h → 7–13 h; feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~380–725 h → ~375–715 h | | 2026-10-11 | minor | Team libraries on `figcraft-server` landed (gap 10): a file's library is published to the server under the file and enabled in other files by a share link (per-asset versions; updates noticed in the background and accepted with overrides kept), the server counts which files use which components, and a whole library can be swapped for another by names; Team libraries breadth 40% → 70%, ready 25% → 40%, 10–20 h → 4–8 h; collaboration 12% → 13%, 55–110 h → 49–98 h; feature breadth (~79%) and ready for real work (~47%) unchanged; full parity ~380–725 h → ~375–715 h | | 2026-10-11 | minor | Branches landed (gap 17): File ▸ Create branch… copies the file into a branch tab that remembers its base; Review and merge shows both sides' changes (layers with Inspect-style property changes, styles, variables, comments) and the conflicts (the same property changed on both sides, or a layer deleted on one side and edited on the other), each resolved for the branch or main; merging is one undo step and archives the branch; Update from main; branches are saved in the `.figcraft` file. Comments, versions, find, misc breadth 80% → 85%, ready 58% → 60%, 4–8 h → 2–5 h; collaboration 12% → 13%; menu breadth 204/204 (the catalog gained File ▸ Create branch…); feature breadth (~79%) and ready for real work (~47%) unchanged at this rounding; full parity ~380–725 h → ~378–722 h | | 2026-10-11 | minor | Animated GIF fills and TIFF images (gap 12, without video and HEIC): GIF fills keep the original file, show the first frame on the canvas and in exports and play in the presenter and the preview (frames decoded on first play, at most 500 frames and 128 MB; always looping; the window wakes only for a GIF's next frame); TIFF images open, import, place and paste. Paint breadth 85% → 87%, 5–9 h → 4–7 h; gap 12 6–12 h → 5–10 h (video playback and HEIC left, both needing non-Rust decoders); feature breadth (~79%), ready for real work (~47%) and full parity hours unchanged at this rounding | | 2026-10-11 | minor | The last three catalogued menu items got commands (gap 21): File ▸ Open recent (recent files kept in the preferences, at most 20, a file that's gone greyed in the menu and dropped when picked, Clear recent), View ▸ Outlines ▸ Show outlines for selection and View ▸ Mask outlines: menu breadth 98.5% → 100% (203/203); Canvas & navigation breadth 95% → 97%, 3–6 h → 2–5 h; feature breadth (~79%), ready for real work (~47%) and full parity hours unchanged at this rounding | | 2026-10-11 | minor | Extended collections, variables import/export and gradient stop and variant property bindings landed (gap 14): Styles & variables breadth 85% → 92%, ready 70% → 72%, 4–8 h → 1–2 h; feature breadth unchanged (~79%); ready for real work unchanged (~47%); full parity ~385–730 h → ~380–725 h | | 2026-10-11 | minor | Collaboration wording after the standalone server: `figcraft-server` keeps many files, persisted as a snapshot plus a log of what changed (compacted, checksummed, damaged files skipped and reported), restarts lose nothing confirmed and apps resume where they were; the app publishes its file to it; share links per file let people edit or only view (the server refuses a viewer's edits), are revocable and stored as hashes; TLS comes from a reverse proxy (the desktop app has none of its own yet); percentages and hours unchanged until the next re-measure | | 2026-10-11 | minor | Live boolean editing, Design panel arc fields, the shape builder on strokes and open paths and one-sided variable widths landed (gap 13): Shapes & vector breadth 92% → 97%, ready 66% → 68%, 8–14 h → 1–3 h; feature breadth ~78% → ~79%; ready for real work unchanged (~47%); full parity ~390–745 h → ~385–730 h | | 2026-10-11 | minor | Collaboration wording after the collaboration polish: styles, variables, collections, modes and comments created in a live session take ids from their creator's id space (concurrent creations no longer overwrite each other; the server checks it), others' text carets and selections show on the canvas, and typing in a layer someone else is typing in says so; percentages unchanged | | 2026-10-11 | minor | Wording only: live sessions (#104), then the browser joining them and `figcraft://` invite links opening the desktop app; percentages and hours unchanged until the next re-measure | | 2026-10-10 | minor | Merged main after #101–#103: WASM plugins (plugins area 0% → 35% breadth, 20% ready; ecosystem 5% → 20%) and the multiplayer core without networking (multiplayer 5% → 15% breadth, 5% ready; collaboration 8% → 12%); menu breadth 97.5% → 98.5% (200/203); ready for real work ~46% → ~47%; full parity 420–790 h → 390–745 h; beta 210–380 h → 200–365 h. Fixed the stated weight sums (areas 1–16 sum to 94, total 113; percentages were already computed with those) | | 2026-10-10 | minor | Readiness by audience with hours to ~95% (full ~400–750, mainstream ~180–330, essentials ~85–160) | | 2026-10-10 | minor | Added essentials user (~53%) and mainstream practitioner (~43%); ready for real work unchanged at ~46% | | 2026-10-10 | minor | Alpha gate (core workflows) checked under the revised stage rules: passes, stays alpha | | 2026-10-10 | major | Rewritten to the craftrules progress-docs shape after a full re-measure; rubric, area narrative and inventory moved to docs/target-app-parity.md, gaps to docs/gaps.md, milestones to docs/roadmap.md. Depth ~79% (self-graded, 16 areas) → ready for real work ~46% (all dimensions); full parity 115–195 h → 420–790 h | | 2026-10-10 | minor | Day-2 refreshes after each batch of PRs (depth 58% → 79%) | | 2026-10-09 | major | First roadmap |