Files
figcraft/docs/architecture.md
T
BFlatandClaude Opus 5.5 4d7a9187ff M9: live sessions: host from the app, invite links, live cursors and presence (#104)
* M9: real-time collaboration protocol (design)

docs/collab-protocol.md: WebSocket sessions with a server-ordered,
per-property last-writer-wins document sync, client-scoped ids,
fractional positions for children, ephemeral presence (cursors,
selections, follow), per-user undo, hosting from the app first and a
self-hosted server next, and the implementation plan. ROADMAP gains M9.

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

* M9: collaboration core: ids, change sets, server ordering, convergence

Step 1 of docs/collab-protocol.md, no networking:

- Client-scoped node ids: Document::set_client_id makes alloc_id hand out
  client << 32 | counter; single-user files allocate as before; the id
  counter is no longer part of document equality.
- crates/collab (layer 2): the synced object/property model (every saved
  Node field, plus document-level styles, comments, variables, images,
  dev snapshots, library data), Change batches, an O(changed) diff, strict
  and lenient apply, fractional sibling positions, snapshots.
- Server (ordering, validation, rejects with fixes) and Client (optimistic
  view, replay under remote batches, author-side repair of derived data)
  as pure state machines; binary (version byte + postcard, JSON values) and
  JSON message forms, fuzzed decoders.
- Engine: normalize as the recompute hook, collab.diff.
- Tests: round trips over random real commands, an N-client convergence
  fuzz test over delayed in-order links, junk batches.

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

* M9: fmt after merge

* M9: live sessions: host from the app, invite links, live cursors and presence

Step 2 of docs/collab-protocol.md, networking and presence half:

- crates/collab-net (layer 6): WebSocket transport (sync tungstenite, a
  reader and a writer thread per connection, no async runtime), the
  session host the app embeds (runs figcraft_collab::Server as the
  authority, carries its messages), invite links, presence throttling
  (~30 Hz, latest wins), limits (sockets, hello/idle timeouts, message
  size, rate, slow clients), keep-alive, clean close, reconnect with
  backoff; a browser stand-in until the web-sys client (step 4).
- engine: live session state (status, peers, presence, follow) and the
  collab.host/stop/join/leave/peers/follow/cursor commands; the network
  is reached only through the LiveLink the app installs.
- ui-egui: remote cursors (pointer drawn in code, name tag, gliding),
  selection outlines (once documents sync), avatars in the right panel
  with follow, the Share popover (start, invite link + URL with Copy,
  stop), File > Join live session.
- desktop app and MCP headless session install the link; xtask layers
  keeps networking crates out of L0-L5.
- Documents don't sync between apps yet (follow-up).

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

* M9: live sessions: document sync between apps

- The host's session runs figcraft_collab::Server seeded with the shared
  document; every app (the host's too, through the hub without a socket)
  is a collab Client. Guests open the host's file from the welcome as a
  new tab; leaving keeps it as a local copy.
- Engine: after each local action (a gesture once it ends; nothing during
  version or text previews) the session document is diffed into one
  Changes batch; server batches go through Client::receive with
  EngineRecompute and are installed with Session::apply_remote, which
  never touches the undo history. Batches arriving mid-gesture wait.
- Transport: Changes/Reject/Welcome reach the engine as Event::Sync;
  LiveLink::send carries batches in order; hellos are limited to 64 KiB,
  joined clients to the core's message size.
- Tests: engine sync against a real Server (ids in the client's space,
  one batch per gesture, remote edits skip undo, deferral, closing the
  file leaves), three apps over loopback converging (create, move,
  recolour, delete, rename) and a late joiner getting the server's state.
- Docs: collab-protocol, mcp, README, roadmap docs; the UI's
  "cursors only" note is gone, replaced by the per-person undo caveat.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:40:59 +07:00

6.9 KiB
Raw Blame History

FigCraft architecture

Last reviewed: 2026-10-10 · Last updated: 2026-10-10 · Change: minor (added the plugin and collab crates from #101 and #103) · 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
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 ~2.4k Live sessions over the network: the WebSocket transport (tungstenite, a reader and a writer thread per connection), the session host the app embeds (runs the collaboration core's server), invite links, presence relay and limits; the only crate allowed networking dependencies
apps figcraft, figcraft-cli, figcraft-web ~1.9k Desktop app (eframe + wgpu), command line, browser build (WASM, WebGPU/WebGL2)
plugins plugins/ (own workspace) Rust plugin SDK and two example plugins, built for wasm32 by cargo xtask plugins (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).

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, recovery copies after a crash). 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).

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-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