Commit Graph
20 Commits
Author SHA1 Message Date
BFlat ee82cdd138 M3: extended variable collections, variables import/export, gradient stop and variant bindings (#114)
Library collections can be extended in a consuming file (inherited modes, per-mode overrides with reset, extra variables; library updates keep overrides). Variables export to the REST JSON shape or CSS custom properties and import back (merged by name, validated by the REST reader, one undo step). Gradient stops and variant properties bind to variables. Also fixes clicking a gradient stop adding a new stop.
2026-10-11 10:49:16 +07:00
BFlatandClaude Opus 5.5 c662e56de4 M5: drag to reorder prototype actions; variable mode lookups in expressions (#94)
- Interaction editor: actions (top level and inside conditional blocks) reorder by dragging
  their row's grip (list_drag, now also for lists shown in model order), one proto.action.move
  per drop; the open action stays open where it lands; Escape cancels a row drag without
  deselecting or closing what holds the list. The ... menu keeps Move up / Move down.
- Prototype expressions: `accent[Dark]` reads a variable's value in one mode of its collection
  (Expr::InMode) and `mode(Theme)` is the name of the mode a collection is in where the
  interaction runs (Expr::Mode); parsed, type-checked, displayed, stored by id (names accepted in
  JSON), evaluated with the layer's modes. REST import maps VAR_MODE_LOOKUP over a variable and
  one of its modes.
- Overlays opened through commands close on a click outside unless told not to, as in the editor.
- Collection::find_mode / current_mode replace repeated lookups.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:21:53 +07:00
BFlatandClaude Opus 5.5 3c63e6d7c8 M3: components at scale; Alt-drag to swap instances (#91)
* M3: components at scale; Alt-drag to swap instances

- Synthetic design system (engine `synthetic_ds`): ~300 components (icons; button, input,
  card, nav and badge sets of size x state x type x theme variants), boolean/text/instance
  swap/slot properties, instances nested four deep, text and paint styles, light/dark
  variables bound across it, and 2,000 instances with overrides at every level.
- Propagation: value, size and added/removed-layer edits of mains reach their copies
  directly; other edits rebuild each affected instance once, inner mains first; rebuilds keep
  unchanged layers shared; bound variables no longer touch unchanged layers; auto layout
  re-flows only where sizes or places changed.
- Fixes: values from variables (theme switches, bound radii) are no longer recorded as
  overrides; a detached variable on an instance layer is an override that survives main
  edits; exposed nested instances stay exposed; swapped nested instances keep their layer
  names and the ids/overrides of layers their own main adds; renamed nested instances keep
  their name when detached out.
- Instance swap properties have preferred values. `instance.swapAt` / `library.swapAt` and
  Alt-drop from the Assets panel onto an instance swap it (highlighted while dragging).
- Instances pasted into other files follow their mains (library copies shared, published
  components used from their library, other mains left behind as a missing main).
- Assets panel lays out only visible tiles and renders thumbnails lazily with a cache
  invalidated per changed component; instance panel lists components once per revision.
- `figcraft-cli perf --instances`, UI frame bench and tests in components_scale_tests.

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

* M3: the swap perf case drops on a glyph the hit test reaches

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

* Merge origin/main into feat/components-scale; weighted depth 79%

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:07:43 +07:00
BFlatandClaude Opus 5.5 fe550fc14a M6: PDF print options (bleed, marks, CMYK) (#92)
PDF export gains print options, saved on PDF export settings and taken
by file.export, file.exportLayers, file.exportFramesPdf, export.add /
export.update, figcraft-cli export and the MCP export tool:

- a bleed (points or a length like 3mm / 0.125in, up to 2 in) that grows
  each page and shows what lies past the frame: its fills reach out to
  the bleed edge and its children are clipped there instead; a print
  page of one frame is trimmed to the frame's box;
- crop and bleed marks: 0.25 pt hairlines outside the bleed in
  registration colour;
- MediaBox / BleedBox / TrimBox;
- CMYK colour: fills, strokes, text, gradient stops, images and
  rasterised effects converted from sRGB with the naive, deterministic
  K = 1 - max(R, G, B) formula; DeviceCMYK without an output intent (no
  CMYK ICC profile can ship in the repository), soft masks stay RGB.

The Design panel shows a PDF row's print settings as a summary line that
opens them; the Export dialog has print options for its PDF preset.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:50:29 +07:00
BFlatandClaude Opus 5.5 70b2666c90 M3: progressive blur, inner shadows under strokes, noise and texture in SVG (#86)
- Inner shadows paint over the fills and under the children and strokes, as documented (canvas,
  PNG, PDF and SVG alike; text fills every run before the inner shadow and the strokes).
- Layer and background blur gain a progressive type: the radius ramps linearly from a start radius
  at a start point to the end radius at an end point (unit-box points, like gradient handles).
  Rendered by blending a few geometric blur levels, each blurred only around the pixels that use
  it; bands and partial renders match one piece. Design panel type menu and Start/End fields,
  on-canvas handles while the settings are open, effect.add/effect.set params, startRadius
  variable bindings, scale tool, REST import (blurType, startRadius, startOffset, endOffset),
  Inspect row; CSS leaves progressive blurs out.
- SVG export embeds renders for what filters can't match: noise grains over the vector layer
  (crisp, blended), texture and progressive layer blur whole, background blur and glass from what
  lies under the layer among the exported layers. PDF and SVG share the renderer's helpers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:20:07 +07:00
BFlatandClaude Opus 5.5 340b673666 M3: text depth: RTL names, text style browser, axis previews (#72)
Right-to-left and mixed-direction names in the panels: layers, pages,
Assets, tab titles, the file header and canvas frame titles show in
visual order (Unicode bidi, joined Arabic letters, mirrored brackets),
elided at their end (the left end of a right-to-left name). egui shapes
each font run itself, so shown names are laid out a cluster at a time.
Rename fields keep egui's editing and draw the shown text over it: the
caret, selection, clicks, drags and arrow keys follow what's on screen.
System fonts are added for scripts the built-in UI fonts lack.

The Typography section's text style browser: list and grid views with
each style in its own font, search, folders, this file's and enabled
libraries' styles, apply, detach, edit the typography, create from the
selected chars, hover to preview on the canvas, and "Mixed" when the
chars use several styles. Text styles now link chars (style_id on text
styles), so a range can take a style; editing what a style sets
unlinks the chars edited; files from before link on open.

Live text previews (text.preview / text.endPreview): a hovered text
style, font style or named instance and a dragged axis slider show on
the canvas without an undo step; release commits one step, Escape
cancels. ui.input sends raw window input over the control channel.

Visual QA: list markers keep the paragraph's font, renames start with
the name selected and centred, Max lines shows while truncating.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 13:03:09 +07:00
BFlatandClaude Opus 5.5 f9b4609631 M3: effects fidelity: backdrop blur in clipped frames, exact shadow spread, glass, noise and texture (#69)
- Background blur sees the whole backdrop under the layer: the renderer keeps the stack of
  isolated surfaces it draws into (clipping frames, rounded clips, faded groups, masks) and
  flattens it for the layer's area, reading past the layer's own box. A blur inside a clipping
  frame now shows the frame's fill instead of the sharp layers below, on the canvas and in PNG
  exports. The PDF export draws background blur and glass through the same code from a render of
  what lies under the node (`backdrop::render_beneath`, which the PDF's backdrop now shares), and
  its images match the PNG's pixels.
- Shadow spread offsets the outline: the shape filled and stroked twice the spread wide with
  mitred joins (grow) or cut away (shrink), the stroked bands cached per outline. Ellipses stay
  ellipses, stars keep sharp points, text keeps its letterforms, rounded corners grow their
  radius. Inner shadows shrink the outline the same way. Canvas, PNG and PDF share the shadow
  code; SVG writes spread shadows as the same geometry and now exports inner shadows.
- Glass effect (light angle and intensity, refraction, depth, dispersion, frost, splay): the
  frosted backdrop bent towards the inside within `depth` of the edge, colour fringes, light on
  the rim, from an exact distance transform with smoothed normals.
- Noise: mono, duo and multi colour modes, grain size, density, opacity and blend mode; grains
  live in the layer's coordinates (stable when panning and zooming). Texture is now drawn
  (pixels scattered by the radius, grain size, clip to shape).
- Design panel rows for noise mode/colours/opacity, texture clip to shape and every glass value;
  `effect.add` takes `glass`; REST import of NOISE, TEXTURE and GLASS; Inspect rows and CSS;
  the scale tool scales grain sizes, texture radius, glass depth and frost.
- Tests in new modules: render backdrop/spread/grain tests, PDF backdrop tests, SVG, doc,
  engine and REST effect tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 11:59:59 +07:00
BFlatandClaude Opus 5.5 9ddf1b3a4e M5: responsive presentation, device colours and custom sizes, library modes (#66)
- Responsive presentation: `proto.settings {responsive}` (saved in the
  page's prototype settings). The player resizes the presented frame to the
  device's screen, or without a device to its whole area at 100%, and lays
  it out again exactly as an editor resize does (constraints, auto layout,
  text; an axis the frame hugs keeps hugging), live as the window, device or
  file change. The player keeps its design document and lays the screen out
  from it each time (`cmd::proto::respond`). In the presenter's Options menu
  (Responsive beside the size options, or Responsive / Fixed size on a
  device) and the Prototype panel.
- Device colours: each device model lists its colour variants
  (`proto.settings {color}`), drawn in code as the body's band round black
  glass; screen corners are masked exactly. Colour picker with swatches in
  the Prototype panel.
- Custom size: a bare screen of any size (`proto.settings {width, height}`
  picks it), W/H fields and a swap button in the Prototype panel, imported
  from REST `CUSTOM` devices.
- Library collections in the Design panel's Variable modes section, under
  their library's name (`library.setVariableMode`; copies whose library
  isn't read stay listed).
- Flow descriptions in the presenter's flow picker (and its hover) and under
  each flow in the Prototype panel.
- Tests in new modules: engine proto_responsive_tests, ui-egui
  proto_responsive_tests; REST device test updated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 11:14:46 +07:00
BFlatandClaude Opus 5.5 044cfef834 M6: PDF export depth and REST conditional and variable-mode actions (#65)
PDF export
- Vector masks clip to the mask's outline, luminance masks are luminosity
  soft masks (alpha masks as before).
- Background blur: the backdrop the page draws under the layer is rendered,
  blurred and cut to its shape, under the layer's vector content.
- Text hyperlinks become clickable link annotations (one per line and link).
- Frames-to-PDF shares one path (`frames_pdf`): sRGB conversion for Display
  P3 files, hidden frames left out, the file name as the PDF title.
- `figcraft-cli export -o x.pdf` and `convert x.figcraft x.pdf` write the
  page's frames one PDF page each.
- Tests: mask types, backdrops, links, image deduplication, page sizes in
  points, hostile inputs; CLI end to end. Checked by rendering the PDFs back
  to PNG and diffing against the PNG export.

Prototyping
- Model: conditional actions (if / else if / else blocks), set variable mode,
  and expressions (literals, variable references, + - * /, comparisons,
  and/or/not, negate) for set-variable values and conditions; walk helpers
  for nested actions (remap on paste, colours, destination checks, library
  copies and pruning, canvas connections).
- REST import maps CONDITIONAL, SET_VARIABLE_MODE and SET_VARIABLE with
  variable references and EXPRESSION values; unsupported functions (mode
  lookups) skip the action with a warning.
- The presenter runs them in order (`figcraft_engine::proto_run`); the
  Prototype panel summarises conditionals, edits set-variable-mode, and
  `ui.set {protoEditor}` opens an interaction's editor.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:57:58 +07:00
BFlatandClaude Opus 5.5 5f54fecca2 M8: 50k-layer zoomed-out renders and faster save/load (#63)
Canvas previews zoomed out to 50% or less draw levels of detail
(crates/render/src/lod.rs): subtrees under 2 device px are splatted as
their averaged paint colours (box filter, no paths or effects), text
under 4 px is greeked into bars, small solid rectangles are splatted and
rectangular clip layers are composited without the blend pipeline.
Image paints and masks keep the exact path. Exports, thumbnails and
every other render stay exact (Options::lod is off by default); render
bands are assembled with a row copy instead of a pixmap draw.

.figcraft documents are serialized straight into the compressor and
parsed straight from the text (a header pass reads version and image
index without building a value tree); layer and text fields at their
default values are left out (63 -> 22 MB of JSON at 50k layers, older
files still load); JSON members use deflate level 3.

At 50k layers: zoomed-to-fit render ~58 -> ~8 ms, save ~1.0 s -> ~0.09 s,
load ~0.86 s -> ~0.16 s.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:27:51 +07:00
BFlatandClaude Opus 5.5 b2738d8f0b M5: prototype device rotation, presentation scaling, scroll momentum, mode picker (#62)
- Device rotation: phones and tablets turn to landscape (`proto.settings {rotation}`), saved in
  the file and imported from REST files (`CCW_90`); the presenter and the preview pane size the
  screen to the turned device and draw its body turned. A rotate toggle sits beside the device
  picker in the Prototype panel.
- Presentation options: fit, fit width, fill and 100% (`proto.presentOptions {scale?,
  deviceFrame?}`, an Options menu in the presenter, Z cycles them, R restarts), the device frame
  shown or hidden, kept between presentations; what overflows the window pans by wheel or drag.
  Presenter textures are capped to the GPU's limit (big frames at 100% no longer overflow it).
- Scroll momentum: drags fling and slow down exactly per frame-rate-independent sub-steps, the
  wheel coasts after it stops, a drag past the ends stretches and springs back; repaints only
  while moving, a press catches a fling.
- Variable modes in the Design panel: frames pick a mode per multi-mode collection, or Auto
  (inherited); `variable.setMode {mode: null}` sets Auto.
- Tests in new modules (engine proto_present_tests, UI present_view_tests); docs and ROADMAP.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:48:29 +07:00
BFlatandClaude Opus 5.5 d7fd41cd13 M3: library variables (#60)
Publishing a library now includes the file's variable collections (modes,
aliases, scopes), versioned per variable and collection; variables hidden
from publishing stay out unless a published variable aliases them or a
published component binds them. Files using the library see its
collections read-only in the Variables editor and the variable pickers;
binding, aliasing or setting a collection's mode copies the variables in
as read-only linked copies, library components keep their bindings, and
"Library updates available" covers changed variables (bound layers follow).

New commands: library.importVariable, library.bindVariable,
library.aliasVariable, library.setVariableMode; variable.update takes
hiddenFromPublishing.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:06:18 +07:00
BFlatandClaude Opus 5.5 77a0e33b61 M5: scrolling inside device frames and a prototype preview pane (#48)
* M5: scrolling inside device frames and a prototype preview pane

On a device, the presented top-level frame is sized to the device's screen:
what doesn't fit is clipped, or scrolls (wheel and drag) when its overflow
allows it. Fixed layers stay on screen, pinned to the edges their
constraints name; "Scroll to" moves every scrolling frame around the layer;
navigating starts at the top unless the new "Preserve scroll position"
option keeps it.

The preview pane is a resizable window over the canvas running the same
player (present.rs now shares layout, drawing and input between the
full-window presenter and the pane). It follows edits live, keeps the
current screen while it exists, shows a newly selected top-level frame, and
takes the keys its prototype uses only while hovered. Opened from the play
menu, the Prototype tab (flows, settings, frame) and `proto.preview
{start?}` / `proto.closePreview`; the control channel gets `ui.set
{preview, presentScroll}` and the player's state in `ui.inspect`.
Alt+Cmd+Enter now presents.

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

* M5: reconcile with main: one device list, REST navigate keeps preserveScrollPosition

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:54:45 +07:00
BFlatandClaude Opus 5.5 d0a21d2a7b M6: REST import of prototypes and variables; export panel polish (#42)
* M6: REST import of prototypes and variables; export panel polish

REST API file JSON import now brings prototyping and variables along:
- interactions: on click/hover/press/drag, after delay, mouse enter/leave/down/up and key
  triggers; navigate, open/swap/close overlay, back, scroll to, change to, open link and set
  variable actions (lone unconditional blocks inlined); dissolve, smart animate, move, push and
  slide transitions with easing incl. custom bezier and spring; the legacy transitionNodeID
- flow starting points (and the deprecated start node), overflowDirection, scrollBehavior,
  prototypeDevice and prototype background
- the local variables response (collections, modes with the default first, values, aliases,
  scopes), passed as `variables` to file.open / file.import (object, JSON text or path), found
  next to the file (<name>.variables.json / variables.json) or embedded; boundVariables, paint
  and text style bindings and explicitVariableModes map onto the fields variable.bind uses
- file.import appends variables, flows and prototype settings with the pages
- unsupported parts (video, gamepad, conditionals, variable modes, library variables) warn

Export panel: rows with a typed or preset size (2x, 512w, 512h), suffix and all five formats
through the new export.update command; export.add steps up (1x, 2x @2x, ...); a preview;
"Export N layers" through the new file.exportLayers (one file per setting, unique safe names)
in the panel and the Export dialog; file.export takes `size`. MCP open_file/export pass the new
params.

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

* M6: Inspect tab: properties with styles and variables, redlines on hover (#41)

The Inspect tab lists the selected layer's properties for handoff:
layout (size, position from the parent, rotation, opacity, radius), auto
layout (direction, gap, padding, alignment), typography, fills, strokes
and effects, each with the style or variable it comes from. Clicking a
value copies it. With the Inspect tab open, hovering measures distances
from the selection without holding Alt.

New command inspect.properties returns the same groups for agents.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:17:54 +07:00
BFlatandClaude Opus 5.5 e0200acbae M6: autosave checkpoints in version history (#38)
Every 30 minutes, each open document with unsaved changes since its last
version gets an automatic "Autosave" version (flagged `auto` in the file).
The newest 20 autosaves are kept; older ones make room and user versions
are never dropped for them. Runs of autosaves fold into one line in the
Version history panel. New command version.autosave.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:48:21 +07:00
BFlatandClaude Opus 5.5 360b6df206 M4: local libraries: publish, enable, place and apply, updates with overrides kept (#32)
- Publish (`library.publish {description?, include?, exclude?}`, Libraries > Publish library…):
  the file's components, component sets and styles (by default all but names starting with `.` or
  `_`, or what the last publish offered) and every component and style they use go into a snapshot
  with a manifest (library id, version, per-asset version stamps). An asset keeps the version it
  last changed in; publishing without changes is refused.
- .figcraft: `library/manifest.json`, `library/document.json` (the snapshot) and
  `library/images/<sha256>` for images only the library still uses. Older files and readers are
  unaffected (extra members); a damaged library is skipped and reported when the author's file
  opens. Files using a library read only these members (size-limited), never unpublished edits.
- Version history: the library entries are written and read alongside the versions (a library
  survives saving with versions and reopening); restoring a version keeps what the file published.
- Enable: `library.add {path}` / `library.remove {id}` / `library.list {assets?}`; the document
  keeps its enabled libraries (id, name, path, last seen version) in its data. The session caches
  each library file and reads it again when it changes on disk; in the browser these commands
  return a clear error (no file system).
- Use: `library.place {library, component, x?, y?}` / `library.import` copy the component (with
  the components and styles it uses) into a hidden library store (a page-like root outside the
  page list) as read-only main components and place an instance; instances behave like any other
  (overrides, properties, variants, swap, reset, detach). `library.applyStyle` copies a style in as
  a linked remote style. Editing the copies or library styles is refused; Go to main component
  names the library. Importing a file's pages brings its library copies along (shared with copies
  the document already has).
- Updates: `library.updates` and `library.acceptUpdates {ids?}` re-import changed copies in place
  (layer ids kept, so instance overrides survive, also when layers are added); unused copies are
  dropped when a library is removed or updated.
- Assets panel: each enabled library's components (thumbnails, search, click or drag to place,
  shared with the local tile code) and styles (click to apply, paint swatches), collapsible per
  library; "Library updates available" banner with Update; a libraries view (book button,
  Libraries menu) to publish this file (choose assets, description) and add or remove library
  files. Local styles in the panels list only this file's styles.
- Tests: manifest round trip and versions, add/place/update keeping overrides (incl. a structural
  update), variants/swap/undo, library styles and importing pages, missing/corrupt/truncated/
  oversized/never-published files; format tests for damaged and oversized members.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:24:46 +07:00
BFlatandClaude Opus 5.5 72b8c5d154 M6: version history: versions saved in the .figcraft file, version commands and panel (#21)
- Versions now belong to the document (DocState.versions) instead of a process-global map keyed by
  document uid, so they survive closing the file. A new, renamed or deleted version marks the file
  as changed.
- .figcraft: `versions/index.json` (id, name, description, time, image hashes) plus one deflated
  `versions/<id>.json` snapshot per version; images only versions use are stored once and kept with
  them. Snapshots read from a file are parsed on first use (and written back as-is). Readers that
  don't know versions ignore the entries; files without versions load unchanged. Damaged, missing,
  oversized or duplicate entries are skipped and reported, never failing the load. At most 100
  versions per file; names and descriptions are clipped.
- Commands: `file.saveVersion {name?, description?}` returns the version; `version.list`;
  `version.restore {id | index}` (one undo step, keeps the file name, stays on the current page);
  `version.rename {id | index, name, description?}`; `version.delete`; `version.duplicate` (opens
  the snapshot in a new tab). Indexes are validated: a negative index used to restore version 0.
  `req_index` param helper shared with the guide commands.
- Version history panel in the left sidebar in place of Pages and Layers (File > Show version
  history; Save to version history opens it with the form): save form (title, description),
  "Current version" on top and the versions newest first on a timeline with name, relative time and
  description; per-version menu (also right-click): Restore, Rename (title and description inline),
  Duplicate, Delete (confirmed inline). New shared `widgets::button`.
- `figcraft-cli run --out x.figcraft` keeps the version history.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 04:16:18 +07:00
BFlatandClaude Opus 5.5 e718518a08 M6: import REST API file JSON: pages, layers, paints, text runs, vectors, auto layout, components (#13)
- New `figcraft_format::rest`: imports the public REST API file JSON (`GET /v1/files/:key`, and
  the nodes endpoint's response) using the published reference only. Pages (name, background,
  flow starting points), the layer tree, names, visibility, lock, opacity, blend mode, masks.
- Geometry from `relativeTransform` (children of groups/booleans checked against
  `absoluteBoundingBox` to settle their space), else absolute bounds + rotation; sizes, corner
  radii (uniform and per corner), smoothing, clip content, constraints, export settings, layout
  grids.
- Paints: solid, linear/radial/angular/diamond gradients (handles map 1:1), image fills (fill,
  fit, tile, stretch, rotation, filters) with bytes from a resolver or a grey placeholder keyed by
  the imageRef; paint opacity, visibility and blend. Strokes: weight, per-side weights, align,
  caps, join, dashes, miter angle. Effects: drop/inner shadow (offset, radius, spread, show behind),
  layer and background blur.
- Vectors from `fillGeometry` (stroke-only vectors use the `strokeGeometry` outline), ellipse arc
  data, stars/polygons, boolean operations. Text: characters, type style (family, style or weight
  + italic, size, line height px/%/auto, letter spacing, case, decoration, alignment, auto resize,
  truncation, paragraph spacing/indent, OpenType flags, links), per-character overrides → styled
  runs (UTF-16 aware), list type.
- Auto layout: direction incl. grid, alignment, padding, gaps, wrap, hug/fixed/fill sizing (legacy
  and per-axis fields), absolute positioning, min/max, grid cells.
- Components and sets with descriptions and property definitions (names lose their `#id`
  suffix); instances of components in the file become instances with their property values and
  listed overrides; outside components and component cycles stay frames. Shared styles are created
  from the layers that use them, named from the metadata.
- Unknown types and fields are skipped with a counted warning list; sizes, depth (256), layer count
  (200,000), path length and list lengths are capped; nothing panics on junk.
- Engine: `file.open` / `file.import` / CLI detect the JSON by content, link imported instances to
  their mains (sources and overrides), read image fills from an `images` folder next to the file,
  never save over the JSON, and return `warnings`. `file.import` now remaps ids across all
  imported pages at once (instances keep their main and layer sources) and brings styles and page
  backgrounds along. `figcraft-cli info` prints load notes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:36:30 +07:00
Brandon ThomasandClaude Opus 5.5 b36fd771ab M0.9: MCP server + figcraft-cli, PDF export, web app, release/CI packaging, vector edit mode, workspace lint cleanup
- crates/mcp: JSON-RPC MCP server (18 tools, resources, prompts), headless and attached backends, end-to-end agent tests
- apps/figcraft-cli: info/export/convert/render/run/commands/mcp/perf
- crates/pdf: vector PDF export via krilla with embedded text; file.exportFramesPdf
- apps/figcraft-web + packaging/{macos,windows,linux,freebsd,web} + release/ci workflows
- engine vectoredit: edit points/handles/bend/add/delete-heal; canvas overlay and edit strip

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:31:53 -04:00
Brandon ThomasandClaude Opus 5.5 56a312ba5f M0.5: layout (auto layout + constraints), format, svg, engine (commands, history, tools, components, styles, variables)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:40:18 -04:00