mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-28 20:36:55 -06:00
ExportFormat::VectorDrawable ("XML", .xml) and ExportSetting.vd_compat
(bestEffort / exact / ignore, stored only when not the default) wire the
figcraft-vectordrawable writer into file.export, file.exportLayers and
export.add/update: a vdCompat param (junk refused), svgIdAttribute writes
android:name, and the results carry the writer's warnings (per file for
export layers). The Export section lists XML with an "Android Vector
Drawable" tooltip and its id attribute and compatibility options, File >
Export... gets an XML preset, and the toasts say when content was changed
or left out. CLI: --format xml / .xml outputs, --vd-compat, warnings as
note: lines. MCP: format xml returns the XML as text plus its warnings.
A .fig export writes XML settings as SVG with a note.
Docs: file-format-parity (row and revision), ui-parity and roadmap
revisions, architecture (the crate), cli and mcp.
89 lines
7.7 KiB
Markdown
89 lines
7.7 KiB
Markdown
# FigCraft architecture
|
||
|
||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (the `vectordrawable` crate, L4: Android Vector Drawable export; earlier: the shared hub in `collab-net` and the `figcraft-server` app) · **Target:** Figma Design
|
||
|
||
How FigCraft is built today. Engine first, a thin swappable UI, everything a command; a Figma-shaped
|
||
data model (a scene graph of frames with auto layout, components and variables). ~108,800 lines of
|
||
Rust (2026-10-10).
|
||
|
||
## Crates and layers
|
||
|
||
`cargo xtask layers` enforces the layering (`xtask/src/layers.rs`): a crate may depend only on
|
||
lower layers. Nothing below L6 depends on egui, eframe, rfd or winit, so the UI crate can be
|
||
replaced.
|
||
|
||
| Layer | Crate | Lines | Role |
|
||
|---|---|---|---|
|
||
| L0 | `figcraft-geom` | ~0.5k | Affine transforms, shape path builders (rounded and smoothed corners, polygons, stars, arcs) on kurbo |
|
||
| L0 | `figcraft-color` | ~1.3k | Colours (HSB/HSL, hex/CSS, Display P3), paints (solid, gradients, image), blend modes |
|
||
| L1 | `figcraft-doc` | ~8.5k | Document model: pages, node tree in a persistent map (`imbl`, stable ids), properties, components, styles, variables, prototype data, comments, versions. Serde everywhere |
|
||
| L2 | `figcraft-layout` | ~1.8k | Auto layout (horizontal, vertical, wrap, grid; hug/fill/fixed; min/max), constraints, layout grid geometry |
|
||
| L2 | `figcraft-text` | ~3.1k | Font database (optional embedded craft-fonts + system scan), shaping (harfrust), bidi, line layout, glyph outlines (skrifa), variable fonts |
|
||
| L2 | `figcraft-collab` | ~3.4k | Real-time collaboration core (no networking): synced object/property model, change sets, fractional positions, server ordering and validation, client reconciliation, wire messages |
|
||
| L2 | `figcraft-plugin` | ~1.0k | Plugin manifest and the WebAssembly sandbox (wasmi, metered, memory cap, no file system, network or clock) |
|
||
| L2 | `figcraft-pathops` | ~1.1k | Booleans (linesweeper), outline stroke with alignment, arrowheads |
|
||
| L3 | `figcraft-render` | ~5.4k | Scene → pixels on tiny-skia (CPU): isolated layers for opacity/blend/effects, clips, masks, shadows, blurs (uniform, progressive, background), noise, texture, glass, text; banded in parallel; retained rendering of only what changed; levels of detail when zoomed out |
|
||
| L3 | `figcraft-format` | ~5.2k | Native `.figcraft` (zip of `document.json` + images; streamed JSON), local library files, versions, REST API file JSON import (`rest/`) |
|
||
| L4 | `figcraft-svg` | ~3.7k | SVG export and import (usvg) |
|
||
| L4 | `figcraft-pdf` | ~2.3k | PDF export (krilla) with print options |
|
||
| L4 | `figcraft-vectordrawable` | ~1.2k | Android Vector Drawable (XML) export: baked path data, gradients through `aapt:attr`, clip groups, compatibility modes and warnings |
|
||
| L5 | `figcraft-engine` | ~39.3k | Sessions, undo history (`Arc` snapshots), the command registry (`cmd/*`, `spec!`), pointer tools and vector edit mode, snapping and measuring, components sync, libraries, prototype runtime, Dev Mode code generation, the menu catalog (`catalog.rs`) and the keymap (`keymap.rs`), a panic guard |
|
||
| L6 | `figcraft-ui-egui` | ~31.9k | The UI3-style frontend: canvas (render worker), toolbar, layers, Design/Prototype/Inspect panels, colour picker, menus, dialogs, presenter, comments, accessibility (AccessKit), the control channel server |
|
||
| L6 | `figcraft-mcp` | ~2.4k | MCP server (JSON-RPC over stdio), headless or bridged to a running app |
|
||
| L6 | `figcraft-collab-net` | ~3.0k | Live sessions over the network: the WebSocket transport (tungstenite, a reader and a writer thread per connection), the hub that runs a collaboration core server per open file for the session host the app embeds and for `figcraft-server` (behind a `Backend`), the joining and managing clients (publishing), invite links, presence relay and limits; the browser client; the only crate allowed networking dependencies |
|
||
| apps | `figcraft`, `figcraft-cli`, `figcraft-web`, `figcraft-server` | ~3.2k | Desktop app (eframe + wgpu), command line, browser build (WASM, WebGPU/WebGL2), collaboration server (files, share links, persistence; [`server.md`](server.md)) |
|
||
| plugins | `plugins/` (own workspace) | | Rust plugin SDK and two example plugins, built for wasm32 by `cargo xtask plugins` ([`plugins.md`](plugins.md)) |
|
||
| tools | `xtask` | | `ci`, `layers`, `assets`, `parity`, `version`, `icon`, `wasm`, `plugins`, `bench` |
|
||
|
||
## Data model
|
||
|
||
A `Document` holds pages, a persistent map of nodes (frames, groups, sections, rectangles,
|
||
ellipses, polygons, stars, lines, vector networks, text with styled runs, boolean groups,
|
||
components, component sets, instances, slices), styles, variable collections, comments, versions
|
||
and images by hash. Instances keep their main's id and per-layer overrides; the engine keeps their
|
||
materialised children in sync when a main changes (directly for value, size and add/remove edits,
|
||
by a rebuild otherwise). Snapshots are cheap (`Arc` and structural sharing), which gives unlimited
|
||
undo with one step per gesture.
|
||
|
||
## Commands: one path for every surface
|
||
|
||
Every user-visible behaviour is a command in `crates/engine/src/cmd/*` (455 ids after #101)
|
||
with an id, label, parameter docs and undo flag. The menus (generated from the catalog), keyboard
|
||
shortcuts, Actions palette, CLI (`figcraft-cli run`), JSON control channel (`--control <port>`) and
|
||
MCP server all dispatch the same ids, so agents can do anything a person can; WebAssembly plugins call them too, within their manifest's permissions. Commands never open
|
||
dialogs themselves; they request UI, and programmatic callers drop those requests.
|
||
`cargo xtask parity` measures the catalog's coverage ([`parity-checklist.md`](parity-checklist.md)).
|
||
|
||
## Rendering
|
||
|
||
The CPU renderer is the single source of pixels for the canvas, exports and the browser. The
|
||
desktop canvas renders on a worker thread at the device pixel ratio and keeps its last image,
|
||
re-rendering only the region a pan uncovers or an edit touches; overlays (selection, handles,
|
||
guides, labels) are drawn by egui on top. A seeded random walk checks the retained canvas against
|
||
full renders. SVG and PDF export walk the same scene and embed rasters where those formats can't
|
||
express an effect.
|
||
|
||
## File I/O
|
||
|
||
`.figcraft` is the native format (atomic saves, autosave checkpoints; the desktop app keeps
|
||
recovery copies of unsaved documents that reopen after a crash or a forced quit). Imports: REST API
|
||
file JSON with variables and images, SVG, raster images, clipboard
|
||
content. Exports: PNG, JPG, WebP, SVG, PDF, code. There is no `.fig` reader yet
|
||
([`file-format-parity.md`](file-format-parity.md)).
|
||
|
||
## Robustness
|
||
|
||
Workspace lints deny `unwrap`, `expect`, `panic!` and friends in shipped code; `unsafe_code` is
|
||
forbidden. `figcraft_engine::guard` turns a panicking command into an error and rolls the document
|
||
back; the desktop app also guards each UI frame. Junk-parameter sweeps run every command with bad
|
||
input.
|
||
|
||
## Revision history
|
||
|
||
| Date | Change | Summary |
|
||
|---|---|---|
|
||
| 2026-10-18 | minor | Added `vectordrawable` (L4), the Android Vector Drawable (XML) writer |
|
||
| 2026-10-11 | minor | `collab-net` hosts through one hub for the app's sessions and the standalone server; added the `figcraft-server` app |
|
||
| 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 | major | Created from the code as of PR #99 |
|