mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
Merge main into feat/plugin-filter-inputs
This commit is contained in:
+70
-12
@@ -11,7 +11,8 @@ There are four kinds of plug-in:
|
||||
and return the objects to keep, change or add. A run is one undo step. Use them for recolouring, path
|
||||
clean-up, halftones, generative patterns and the like.
|
||||
- **Live effects** rewrite one object's geometry each time it is drawn, from its appearance stack, like the
|
||||
built-in Distort & Transform effects. The art stays editable: move or reshape it and the effect follows.
|
||||
built-in Distort & Transform effects. The art stays editable: move or reshape it and the effect follows. A live
|
||||
effect may also paint what it returns ([painted live effects](#painted-live-effects)).
|
||||
- **Exporters** write the whole document in a file format of their own, for pen plotters, cutting machines,
|
||||
laser cutters and the like. File › Export › Export As lists them after the built-in formats, and the host writes the
|
||||
bytes they return: a plug-in still never touches a file.
|
||||
@@ -26,7 +27,7 @@ This page is the contract for plug-in authors and the reference for the host (`c
|
||||
|
||||
| Command | Params | What it does |
|
||||
|---|---|---|
|
||||
| `plugin.list` | `{}` | `{"plugins": [{id, name, version, kind, author, description, params, paramsSchema, effect?, format?, extensions?, budget?, commands?, size, source, api, permissions, hash, cacheBytes}], "folder": report}` (`api` is the manifest's [plug-in API level](#permissions); `budget: "heavy"` for a [heavy filter](#heavy-filters); `commands` lists its [commands](#commands); `permissions` lists what it [may do](#permissions), `hash` is the module's SHA-256; `cacheBytes` is the size of its [cache](#settings-and-cache)) |
|
||||
| `plugin.list` | `{}` | `{"plugins": [{id, name, version, kind, author, description, params, paramsSchema, effect?, format?, extensions?, budget?, commands?, size, source, api, paintOutput?, permissions, hash, cacheBytes}], "folder": report}` (`api` is the manifest's [plug-in API level](#permissions); `budget: "heavy"` for a [heavy filter](#heavy-filters); `commands` lists its [commands](#commands); `paintOutput: true` marks a [painted live effect](#painted-live-effects); `permissions` lists what it [may do](#permissions), `hash` is the module's SHA-256; `cacheBytes` is the size of its [cache](#settings-and-cache)) |
|
||||
| `plugin.info` | `{"id"}` | One plug-in: the `plugin.list` entry plus `manifest`, `defaults` and `lastError` (a live effect's last failure). |
|
||||
| `plugin.run` | `{"id", "params"?: {…}, "ids"?: [...]}` (plug-in params may also be top-level keys) | Runs an object filter on the selection (or `ids`; an `ids` that is not a list of object ids is an error). One undo step; the result is selected, except the objects it locked or made guides. |
|
||||
| `plugin.install` | `{"path": "/x/y.wasm"}` (desktop) or `{"dataBase64": "…", "name"?}`, `"replace"?: bool`, `"allowHeavy": bool = false`, `"grant"?: [permission]`, `"hash"?: "sha256 hex"` | Validates and installs a module; returns its `plugin.list` entry. The desktop app and `vectorcraft-cli` keep it in the `Plug-ins` folder next to the preferences, so it is installed again after a restart (#2541). A module with other bytes than the plug-in installed with its id, or, with none installed, one whose id has [settings and a cache](#settings-and-cache) in the `Plug-ins` folder, installs only with `replace: true`, once the user agreed; it takes that plug-in's place and gets its settings and cache. The same module installs again without `replace`, and `replace: false` refuses a module whose id is installed. A [heavy filter](#heavy-filters) installs only with `allowHeavy: true`, once the user agreed. A plug-in whose manifest lists [permissions](#permissions) installs only when `grant` lists every one of them, once the user agreed, unless the user already granted a module with the same SHA-256. With `hash`, a module whose SHA-256 differs is refused. |
|
||||
@@ -223,10 +224,12 @@ capability model proposed for PhotoCraft plug-ins
|
||||
`documentData` is `null` and an output that sets it is refused; without `doc.write` an output that changes the
|
||||
document is refused. A refused output leaves the document as it was.
|
||||
- `api` is the plug-in API level the manifest is written for: `1` (the default) or `2`, which adds the [host
|
||||
imports](#host-imports-api-level-2) and a filter's `inputs` ([reading type, images and
|
||||
symbols](#reading-type-images-and-symbols)). A higher one is refused. `plugin.list` and `plugin.info` give it as
|
||||
`api`. `inputs` is not a permission: the objects it asks for are read under `doc.read`, which a filter that lists
|
||||
`inputs` must list in `permissions`, so installing it asks the user to grant them.
|
||||
imports](#host-imports-api-level-2), a filter's `inputs` ([reading type, images and
|
||||
symbols](#reading-type-images-and-symbols)) and a live effect's [`paintOutput`](#painted-live-effects). A higher one
|
||||
is refused. `plugin.list` and `plugin.info` give it as `api`. `inputs` is not a permission: the objects it asks for
|
||||
are read under `doc.read`, which a filter that lists `inputs` must list in `permissions`, so installing it asks the
|
||||
user to grant them. A level 2 effect that declares `paintOutput` needs no other permission than `doc.read` and
|
||||
`effect.render`.
|
||||
- Parameter declarations are unchanged by permissions (`params`, below).
|
||||
|
||||
Every run gets a **fresh instance**, so no state survives between calls except what a plug-in writes to its
|
||||
@@ -244,8 +247,10 @@ takes as many instructions the first time as the next. Charged to the run, as wa
|
||||
could fail it: when the 20 M-instruction slice the run was in ran out while a function compiled, wasmi ended the run
|
||||
with an error rather than a pause the host resumes, and the run stopped with most of its budget left.
|
||||
|
||||
Drawing evaluates effects often, so live-effect results are cached by plug-in installation, parameters and input
|
||||
geometry: a plug-in runs again only when one of them changes.
|
||||
Drawing evaluates effects often, so live-effect results are cached by plug-in installation, parameters, input
|
||||
geometry, fill rule, reference box and the paints the effect is given: a plug-in runs again only when one of them
|
||||
changes. Failures are cached too. The swatches and patterns a painted result names are checked against the document
|
||||
each time it is drawn, and a paint linked to a global swatch is drawn in that swatch's current color at its own tint.
|
||||
|
||||
## ABI v1
|
||||
|
||||
@@ -372,6 +377,11 @@ pass the 1 GiB returns `-7` before it reads or writes anything.
|
||||
filter gets paths and compound paths alone, as before. An unknown, repeated or wrongly typed entry, `inputs` on
|
||||
another kind, below `api` 2, or without a `permissions` list that holds `doc.read`, is refused when the plug-in is
|
||||
installed, and so is `api` 2 on hosts from before it.
|
||||
- `paintOutput` (live effects at `api` 2 only): `true` makes the effect return [painted pieces](#painted-live-effects)
|
||||
instead of geometry alone. `true` is the only value the key takes; `false`, `null`, another type, another kind or
|
||||
`api` 1 is refused when the module is installed, and the error names `paintOutput`. Without the key the effect returns
|
||||
geometry, at any level. Hosts at `api` 1 refuse the manifest for its `api`, so such an effect never draws unpainted
|
||||
there.
|
||||
- `extensions` (exporters and importers only, and required): 1–8 file extensions, lower-case letters and digits
|
||||
without the dot (`["plt"]`); the first is the one Export As gives the file. A built-in format keeps an extension both
|
||||
list (`svg`): `document.export` then writes the exporter's format only when asked for by its format id, and
|
||||
@@ -422,7 +432,8 @@ which a filter never touches unless its manifest lists them in `inputs` ([readin
|
||||
symbols](#reading-type-images-and-symbols)). With nothing selected the list is empty (a generator then adds its own objects). A live effect gets one
|
||||
object: its geometry (`type`, `path`, `fillRule`, `bounds`) and its `fills` and `strokes`, as a filter gets them (hosts
|
||||
from before these give geometry only). The paints are read only: an effect may draw as wide as a stroke or use a
|
||||
fill's colour to decide its shape, but it changes only the geometry.
|
||||
fill's colour to decide its shape, but it changes only the geometry, unless it declares `paintOutput` ([painted live
|
||||
effects](#painted-live-effects)).
|
||||
|
||||
An **exporter** gets something else: the native `.vectorcraft` document JSON without indentation, optional preview/PDF
|
||||
renditions or save-preference metadata (the format is documented in [`crates/format/src/lib.rs`](../crates/format/src/lib.rs)). That is every layer, object,
|
||||
@@ -590,7 +601,53 @@ document, so it can read what every plug-in keeps there, not only its own. A liv
|
||||
is worked out from the object alone.
|
||||
|
||||
For a **live effect**, the geometry of every returned object's `path` (their subpaths, in order) replaces the
|
||||
object's; the rest is ignored (the object keeps its own appearance). An empty list removes the geometry.
|
||||
object's; the rest is ignored (the object keeps its own appearance). An empty list removes the geometry. A live effect
|
||||
that declares `paintOutput` returns painted pieces instead ([below](#painted-live-effects)).
|
||||
|
||||
#### Painted live effects
|
||||
|
||||
A live effect whose manifest has `"api": 2` and `"paintOutput": true` returns `{"objects": [...]}` too, but each entry
|
||||
is a piece drawn by itself, in the order returned, with paints of its own:
|
||||
|
||||
```json
|
||||
{"objects": [
|
||||
{"path": {…}, "fills": [{"type": "solid", "color": {"model": "rgb", "r": 0, "g": 0.6, "b": 0.3}}]},
|
||||
{"path": {…}, "fillRule": "evenodd", "strokes": [{"paint": null}, {"paint": {…}, "width": 4, "join": "round"}]},
|
||||
{"path": {…}}
|
||||
]}
|
||||
```
|
||||
|
||||
- `path` is required; `fillRule` defaults to the object's. `type` and `bounds` may be handed back as given and are
|
||||
ignored. Any other key (`opacity`, `effects`, `guide`, `locked`, `name`, a top-level `documentData`…) refuses the
|
||||
result, as does a key that a paint, its color, a gradient stop, a gradient's placement or a freeform point does not
|
||||
have, such as `opacity` on a solid paint.
|
||||
- `fills` and `strokes` take the forms a filter returns ([Input](#input)). A piece without `fills` (or `strokes`) is
|
||||
painted with the ones the effect was given; an empty list paints none. Given ones replace the object's in order, as
|
||||
a filter's output sets them: an existing fill or stroke keeps its own effects, opacity and blending, and a new stroke
|
||||
starts at 1 pt, miter join, butt cap, miter limit 10.
|
||||
- An effect on a fill (`_context.item` `{"fill": i}`) returns only `fills`, which replace that fill on each piece; an
|
||||
effect on a stroke returns only `strokes`. The object's other fills and strokes stay where they are.
|
||||
- The effects after it in the stack apply to the pieces together, the reference box being the bounds of all of them.
|
||||
Bounds, raster effects, Pathfinder effects, compound shapes and Envelope Options › Distort Appearance use the pieces
|
||||
as they are drawn. Outside outline mode, an image is also hit where the pieces of its own fills and strokes paint; a
|
||||
path or compound path is hit-tested on its source path and appearance. On an object with no geometry effect of its
|
||||
own, Pathfinder effects and compound shapes use the object's path for an effect on a fill or stroke, as they do for
|
||||
every other effect on a fill or stroke; on an object that has one, they also use the pieces of a painted effect on a
|
||||
fill or stroke. A 3D effect later in the stack uses the
|
||||
object's input path when the pieces name a swatch or pattern. Each piece counts against the budget of the
|
||||
object's geometry effects as well as its anchors, so all the painted effects in a stack make at most 100,000 pieces
|
||||
together; a run whose pieces would pass the budget draws its input as it was.
|
||||
- The result is checked whole before any of it is drawn: the limits of a filter's output (100,000 objects, 4,000,000
|
||||
anchors, 256 fills and 256 strokes per piece, 256 stops per gradient), finite numbers in range with nothing clamped
|
||||
(color components, tints, offsets, opacities, midpoints 0.13 to 0.87, stops in order of `offset`, stroke
|
||||
widths 0 to 1,000 pt, miter limits 1 to 500), and every swatch a solid paint, gradient or stop links to and every pattern named in
|
||||
the document. One fault refuses the whole result: that step of the stack draws its input as it was, the effect
|
||||
record stays, and `plugin.info`'s `lastError` names the plug-in and the field, for example
|
||||
`Test Tone: plug-in output: objects[1].strokes[0].width: …`.
|
||||
- The pieces are never written into the document while it is drawn, measured, previewed, saved or exported. PDF and SVG
|
||||
export draw them as the canvas does, from a copy of the document. Object › Expand Appearance writes each piece as an
|
||||
ordinary path or compound path with its fills and strokes, as one undo step. Object › Expand of gradient and pattern
|
||||
fills expands the fills of each piece, and leaves a piece with other paints as it is drawn.
|
||||
|
||||
An **exporter** returns `{"dataBase64": "…", "warnings"?: ["…"]}`: the file, in base64, which the host writes as it
|
||||
is, and what the export left out or changed, for the user (at most 100 warnings of at most 500 characters each, shown
|
||||
@@ -632,6 +689,7 @@ per change of their input, parameters or paints (results are cached), not per fr
|
||||
- Tool plug-ins (pointer events on the canvas, a guide line or rectangle, geometry back), panel plug-ins, and
|
||||
importers that place files (File › Place reads only the built-in formats) or make type, images, layers or
|
||||
artboards.
|
||||
- Live effects that change paint (they reshape geometry; an object filter recolors), and filters that change type,
|
||||
images or symbol instances (a filter at `api` 2 reads them, see [`inputs`](#reading-type-images-and-symbols)).
|
||||
- Live effects that change opacity, blending or effects (at `api` 2 one may [paint](#painted-live-effects) the pieces
|
||||
it returns), and filters that change type, images or symbol instances (a filter at `api` 2 reads them, see
|
||||
[`inputs`](#reading-type-images-and-symbols)).
|
||||
- Apply Last Effect and Last Effect work for plug-in effects; there is no "last filter" for object filters yet.
|
||||
|
||||
Reference in New Issue
Block a user