mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-27 11:56:50 -06:00
Merge main into #840
# Conflicts: # apps/figcraft-cli/src/files.rs # apps/figcraft-cli/src/main.rs # crates/engine/src/cmd/file.rs # crates/engine/src/lib.rs # crates/ui-egui/locales/en.toml # crates/ui-egui/locales/es.toml # crates/ui-egui/src/export_panel.rs # crates/ui-egui/src/export_preview_tests.rs # docs/file-format-parity.md # docs/mcp.md # docs/roadmap.md # docs/ui-parity.md
This commit is contained in:
+58
-9
@@ -4,7 +4,13 @@ FigCraft without a window: inspect, export, convert and render documents, run co
|
||||
commands, serve MCP and measure performance. Build it with
|
||||
`cargo build --release -p figcraft-cli` (binary `target/release/figcraft-cli`).
|
||||
|
||||
Bad arguments print usage and exit with code 2; failures exit with 1. The CLI never panics on input.
|
||||
`figcraft-cli --help` (or `-h`, or `help`) prints usage on stdout and exits with 0, and so does
|
||||
`--help` or `-h` after a subcommand (`figcraft-cli mcp --help`). Bad arguments print usage and exit
|
||||
with code 2; failures exit with 1. The CLI never panics on input.
|
||||
|
||||
Put options before `--` to pass a filename beginning with a dash, for example
|
||||
`figcraft-cli info --json -- --drawing.figcraft`. Every argument after `--` is literal,
|
||||
including a filename named `--no-system-fonts`.
|
||||
|
||||
## Fonts
|
||||
|
||||
@@ -19,6 +25,16 @@ On Linux and other Unix systems except macOS, user fonts are read from
|
||||
`$XDG_DATA_HOME/fonts`, or `$HOME/.local/share/fonts` when `XDG_DATA_HOME` is unset,
|
||||
empty or relative. The legacy `$HOME/.fonts` directory is also scanned.
|
||||
|
||||
On macOS the font folders (`/System/Library/Fonts`, `/Library/Fonts`, `~/Library/Fonts` and the
|
||||
system's downloadable fonts) are read, and so is every other font file Core Text lists as
|
||||
available: fonts a font manager such as RightFont, FontBase or Typeface activates from its own
|
||||
library, without copying them into a font folder. Fonts activated while the app runs are found by
|
||||
`text.rescanFonts`, like newly installed ones.
|
||||
|
||||
On macOS, fonts activated through Adobe Fonts are read where Creative Cloud keeps them
|
||||
(`~/Library/Application Support/Adobe/CoreSync/plugins/livetype`), so they're available while
|
||||
they're active there. Files without an extension are read when they start like a font file.
|
||||
|
||||
The collaboration server is a binary of its own, `figcraft-server` (`cargo build --release -p
|
||||
figcraft-server`): it keeps files with share links and serves their live sessions, and its
|
||||
`key`, `files`, `links`, `reports`, `block-ip` and `check` subcommands manage them (a server can
|
||||
@@ -36,7 +52,8 @@ layers}]` with `--json`); their text renders in a fallback font.
|
||||
Every subcommand that reads a file also takes the public REST API file JSON (the response of
|
||||
`GET /v1/files/:key` or of its nodes endpoint, written to a `.json` file): it is imported on the fly.
|
||||
Image fills come from `images/<imageRef>.png` (or `.jpg`, …) next to the JSON when present, else a
|
||||
grey placeholder. `.fig` files are read the same way (best effort, images from the file itself);
|
||||
grey placeholder. `.fig` files are read the same way (best effort, images from the file itself), and
|
||||
so are `.sketch` files saved by Sketch 48 or newer;
|
||||
`info` lists what wasn't imported as `note:` lines.
|
||||
|
||||
```text
|
||||
@@ -51,6 +68,10 @@ page 1 "Screens": 380 nodes, 4 top-level
|
||||
|
||||
- `<out>` is a directory: one file per top-level frame of the page (default the first page), named
|
||||
after the frame (`Home.png`, `Settings.png`…); with no frames, one file per top-level layer.
|
||||
The name is kept as it is, except for the characters file systems refuse (`/ \ : * ? " < > |`
|
||||
and control characters), which become `_`, and leading or trailing spaces and dots, which are
|
||||
dropped. A name is cut at 100 characters. The app's export replaces the same characters (there a
|
||||
`/` makes a folder). Two layers with the same name get `Name 2`, `Name 3`…
|
||||
- `<out>` has an image extension: one file with the given `--node`, or the whole page composited.
|
||||
- `--node` takes a layer id (`12` or `0:12`) or a layer name.
|
||||
- `--format fig` (or an `<out>` ending in `.fig`) writes the whole file as `.fig`, experimental:
|
||||
@@ -92,7 +113,7 @@ figcraft-cli export app.figcraft -o handoff.fig
|
||||
|
||||
### `convert <in> <out>`
|
||||
|
||||
By extension. Inputs: `.figcraft`, `.json` (FigCraft or REST API file JSON), `.fig` (best effort), `.svg` (an editable frame: groups,
|
||||
By extension. Inputs: `.figcraft`, `.json` (FigCraft or REST API file JSON), `.fig` (best effort), `.sketch` (Sketch 48 or newer, best effort), `.svg` (an editable frame: groups,
|
||||
shapes, text, gradients, masks and effects; `info` lists what it left out as `note:` lines), `.png/.jpg/.webp/.gif/.tif/.bmp`
|
||||
(an image layer). Outputs: `.figcraft`, `.json` (FigCraft JSON, pretty-printed), `.svg`,
|
||||
`.png/.jpg/.webp` (the current page rendered at 1x with its background), `.pdf` (the current page's
|
||||
@@ -112,14 +133,18 @@ Runs commands in a headless session (a new document, or `--in`), printing one JS
|
||||
```
|
||||
|
||||
The whole script is **one undo step**, and it is **all-or-nothing**, exactly like running the same
|
||||
commands through `command.batch` (the MCP tool `batch`, the control channel, plugins): `{"$step": n}`
|
||||
in a step's params is the `id` in an earlier step's result, so a step can target a layer an earlier
|
||||
step of the same script created:
|
||||
commands through `command.batch` (the MCP tool `batch`, the control channel, plugins). A step named
|
||||
with `"as"` can be used by later steps: `{"$ref": "name"}` in a step's params is the `id` in that
|
||||
step's result (`{"$step": n}` does the same by zero-based position), and `"field"` takes another key
|
||||
or a slash path such as `ids/0`. So a step can target a layer an earlier step of the same script
|
||||
created ([Step references](mcp.md#step-references) has the rules):
|
||||
|
||||
```json
|
||||
[
|
||||
{"command": "node.create", "params": {"type": "frame", "name": "Card", "width": 200, "height": 100}},
|
||||
{"command": "node.create", "params": {"type": "text", "parent": {"$step": 0}, "text": "Hi"}}
|
||||
{"command": "node.create", "as": "card", "params": {"type": "frame", "name": "Card", "width": 200, "height": 100}},
|
||||
{"command": "node.create", "params": {"type": "text", "parent": {"$ref": "card"}, "text": "Hi"}},
|
||||
{"command": "edit.duplicate", "as": "copy", "params": {"ids": [{"$ref": "card"}]}},
|
||||
{"command": "node.set", "params": {"ids": [{"$ref": "copy", "field": "ids/0"}], "props": {"name": "Card copy"}}}
|
||||
]
|
||||
```
|
||||
|
||||
@@ -159,6 +184,28 @@ figcraft-cli run start.json --out dashboard.figcraft
|
||||
figcraft-cli export dashboard.figcraft -o dashboard.png
|
||||
```
|
||||
|
||||
### Confining a run: `--automation-read-root DIR` / `--automation-write-root DIR`
|
||||
|
||||
`info`, `export`, `convert`, `render` and `run` take these two flags, with the same meaning they
|
||||
have on [`mcp`](#mcp---attach-port---automation-read-root-dir---automation-write-root-dir): each is
|
||||
repeatable, each folder must exist, and together they confine every file that subcommand touches.
|
||||
Reads are allowed inside a read or write root, writes only inside a write root, and everything else
|
||||
is refused — so a run given only a read root cannot write at all. Without either flag a subcommand
|
||||
reads and writes as the user who started it.
|
||||
|
||||
```sh
|
||||
# Renders in/doc.figcraft to out/ok.png and can touch nothing else.
|
||||
figcraft-cli render in/doc.figcraft -o out/ok.png \
|
||||
--automation-read-root in --automation-write-root out
|
||||
|
||||
figcraft-cli render in/doc.figcraft -o ./escaped.png \
|
||||
--automation-read-root in --automation-write-root out
|
||||
# error: automation path rejected: . is outside the folders that may be written: …/out
|
||||
```
|
||||
|
||||
This is for driving the CLI from a script or an agent that should not reach the rest of the disk;
|
||||
the flags leave the output identical when nothing strays outside the roots.
|
||||
|
||||
### `commands [--json] [--filter TEXT]`
|
||||
|
||||
Every command with its label, shortcut, undoable flag (`*`) and parameter docs; `--json` for
|
||||
@@ -170,7 +217,9 @@ Serve MCP on stdio: headless by default, or attached to a running app started wi
|
||||
`figcraft --control PORT`. See [mcp.md](mcp.md). The automation roots (each repeatable; existing
|
||||
folders) confine the files the agent's calls read (inside a read or write root) and write (inside a
|
||||
write root); everything else is refused ([mcp.md › Confining file access](mcp.md#confining-file-access)).
|
||||
With `--attach` they are refused: start the app with them instead. The headless session can host, join and publish
|
||||
They apply to the headless session only, and that is why `--attach` refuses them: an attached server
|
||||
is confined by the app it attaches to, from the flags that app was started with. The headless
|
||||
session can host, join and publish
|
||||
live sessions (`collab.*`: without a key on a server open to the public, with `collab.serverInfo`,
|
||||
`collab.manageFile` for the owner token and `collab.report`) and use plugin directories
|
||||
(`plugin.directory.*`); the one-shot `run` can't (no network link).
|
||||
|
||||
Reference in New Issue
Block a user