mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
M6: SVG import fidelity (#90)
SVG import comes in editable and looking right: groups named from their ids with transforms baked into the layers (rotated, scaled and skewed groups, viewBox scaling and <use> offsets used to be lost); rectangles, ellipses and lines recognised and kept as those shapes; gradients with exact handles, reflect/repeat and focal points; patterns as image fills; clip paths and masks as vector, luminance and alpha masks; shadow and blur filter chains as drop shadows, inner shadows and layer blurs; blend modes; text as text layers (it used to vanish); images and nested SVG images. What can't be represented is reported as import warnings (file.import / file.open warnings, figcraft-cli info notes). Linked files are never read, and documents nested too deep for the parser are refused instead of crashing. Pasted SVG is recognised after XML declarations, comments and doctypes and lands centred in the view (file.import center). SVG export writes vector masks as clip paths and alpha/luminance masks as the mask layer drawn, so masks survive a round trip. Tests: a corpus of 21 synthetic SVG files with hand-worked expected pixels (158 probes), structure checks and export round trips; unit tests for shape recognition, paints, filters, structure, mask export; hostile input and byte-mangled corpus fuzzing; engine file command tests. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+21
-1
@@ -135,7 +135,7 @@ Or in a project's `.mcp.json`:
|
||||
| `select {ids}` | Replace the selection (`[]` deselects). |
|
||||
| `export {ids, format, scale \| size, path?}` | png, jpg, svg, webp, pdf; `size` is `2x`, `512w` or `512h`. PDF: frames one page each (sized in points), anything else together on one page; `run_command` `file.exportFramesPdf {page?, path?}` writes every top-level frame of a page. Without `path`, images come back as MCP image content, SVG as text. Layers' own export settings: `run_command` with `export.add` / `export.update {index, size?, suffix?, format?}` / `export.remove` and `file.exportLayers {ids?, dir?}` (every layer once per setting, named `<layer><suffix>.<format>`). |
|
||||
| `screenshot {ids?, scale?, maxSize?, window?}` | PNG of the page (with background) or of layers, scaled to fit `maxSize` (default 1600). `window: true` captures the app window when attached. |
|
||||
| `open_file {path, variables?}` / `save_file {path?}` | Open `.figcraft`, `.json` (FigCraft JSON or REST API file JSON with its variables, see below), `.svg` or an image; save as `.figcraft`. |
|
||||
| `open_file {path, variables?}` / `save_file {path?}` | Open `.figcraft`, `.json` (FigCraft JSON or REST API file JSON with its variables, see below), `.svg` (see Importing SVG below) or an image; save as `.figcraft`. |
|
||||
| `undo` / `redo` | Step through history. |
|
||||
| `pointer {kind, x, y, to?, steps?, tool?, shift, alt, cmd}` | Mouse gestures in page coordinates with the current tool: `click`, `double`, `drag`, `down`, `move`, `up`, `hover`. |
|
||||
| `key {key, shift, alt, cmd}` | A shortcut or key as if typed on the canvas (every default shortcut, tool keys, Escape, Delete, arrows, number keys for opacity; while editing text, the text shortcuts and then text editing keys). Keys are names like `A`, `0`, `Enter`, `ArrowLeft`, `=`, `/`, or egui's (`Plus`, `Questionmark`); `cmd` is Cmd on macOS and Ctrl elsewhere. |
|
||||
@@ -352,6 +352,26 @@ click or drag across regions with `pointer` to merge, add `alt` to remove; `shap
|
||||
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"run_command","arguments":{"command":"shapeBuilder.merge","params":{"ids":[2,3],"points":[[120,80],[160,80]]}}}}
|
||||
```
|
||||
|
||||
### Importing SVG
|
||||
|
||||
`open_file {path}` opens an SVG as a new document; `run_command {command: "file.import", params:
|
||||
{path | data + name, x?, y?, center?}}` adds it to the current page as a frame (top-left at `x, y`,
|
||||
or centred there with `center: true`, as pasting does). It comes in editable: groups named from
|
||||
their `id`, transforms baked into the layers, `<rect>`/`<circle>`/`<ellipse>`/`<line>` (and paths
|
||||
tracing those outlines) as rectangles with their corner radius, ellipses and lines, everything else
|
||||
as vectors; strokes with their width, dashes, caps, joins and miter limit; linear and radial
|
||||
gradients with their exact handles (`gradientTransform`, `reflect`/`repeat` unrolled, a focal
|
||||
point as the centre); patterns as image fills (fill, fit, crop or tile); clip paths as vector masks
|
||||
and masks as luminance or alpha masks; shadow and blur filters (`feDropShadow`, the offset / blur /
|
||||
flood / composite chains, colour-matrix shadows, inner shadows, `feMorphology` spread) as drop
|
||||
shadows, inner shadows and layer blurs; opacity and `mix-blend-mode`; text as text layers (fonts
|
||||
the SVG names when installed, anchors, letter spacing, styled `<tspan>` runs, stacked lines as one
|
||||
layer); images as image fills, and SVG images imported in place. CSS, `<use>`/`<symbol>`, units
|
||||
and `viewBox`/`preserveAspectRatio` are resolved. The result's `warnings` list what was left out
|
||||
(filters that aren't shadows or blurs, rotated repeating patterns, a clip path's own clip path).
|
||||
Linked files are never read, only data URIs; documents nested more than 512 elements deep are
|
||||
refused.
|
||||
|
||||
### Importing REST API file JSON
|
||||
|
||||
The JSON returned by the public REST API's file endpoint (`GET /v1/files/:key`, best with
|
||||
|
||||
Reference in New Issue
Block a user