mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 17:32:42 -06:00
Plug-ins: inputs need a granted doc.read and stay within totals
A filter manifest that lists `inputs` must now list `doc.read` in `permissions`, so installing it asks the user to grant them and the Additional Plug-ins Folder loads it only once granted. Left out, the manifest does not install. The pixels of all images in one filter input take at most 16 MiB of base64, and the runs and paragraphs of all its type at most 16 MiB of JSON. An image whose pixels do not fit what is left comes with `pixelsOmitted`, checked before it is decoded, and type that does not fit comes with `truncated`. The read-only objects are kept once and written into the input from there. A field a filter adds to a read-only object is named in the refusal with control and bidirectional characters escaped, cut to 64 characters.
This commit is contained in:
+5
-3
@@ -6087,9 +6087,11 @@ them again at startup (`plugin.remove` deletes them there):
|
||||
`plugin.install {path | dataBase64}` installs one, `plugin.list` / `plugin.info {id}` describe them (parameters,
|
||||
schema, defaults). An object filter runs on the selected paths and compound paths with `plugin.run {id, params}` (one
|
||||
undo step; it may change, delete or add objects, and the result is selected). A filter whose manifest lists `inputs`
|
||||
(`type`, `image`, `symbolInstance`; `inputs` in its `plugin.list` entry) is also given the selected objects of those
|
||||
kinds to read: its output returns each of them as `{"id": …}` or exactly as given, otherwise `plugin.run` returns an
|
||||
error naming the object and the field, and the document and undo history are unchanged. A live-effect plug-in is an effect with
|
||||
(`type`, `image`, `symbolInstance`; `inputs` in its `plugin.list` entry) also lists `doc.read` in its `permissions`, so
|
||||
`plugin.install` needs `grant`. It is also given the selected objects of those kinds to read, with at most 16 MiB of
|
||||
pixels and 16 MiB of type runs over one run (later ones come with `pixelsOmitted` or `truncated`): its output returns
|
||||
each of them as `{"id": …}` or exactly as given, otherwise `plugin.run` returns an error naming the object and the
|
||||
field, and the document and undo history are unchanged. A live-effect plug-in is an effect with
|
||||
id `plugin.<id>`: `effect.apply`, `effect.setParams` and `effect.expandAppearance` work as for built-in effects, and
|
||||
`effect.list` lists it. An exporter adds a write-only file format `plugin.<id>` (listed by `document.formats`):
|
||||
`document.export {format: "plugin.<id>", params?, path?}` writes it, as does a `path` ending in one of its extensions
|
||||
|
||||
+12
-7
@@ -148,7 +148,7 @@ capability model proposed for PhotoCraft plug-ins
|
||||
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
|
||||
`permissions` and `inputs` must list.
|
||||
`inputs` must list in `permissions`, so installing it asks the user to grant them.
|
||||
- Parameter declarations are unchanged by permissions (`params`, below).
|
||||
|
||||
Every run gets a **fresh instance**, so no state survives between calls. Traps, exhausted budgets, malformed
|
||||
@@ -243,7 +243,7 @@ still returns `1`.
|
||||
- `inputs` (filters at `api` 2 only): the kinds of object besides paths and compound paths the filter reads, any of
|
||||
`"type"`, `"image"` and `"symbolInstance"`, each at most once (`"inputs": ["type", "image"]`). Left out or `[]`, the
|
||||
filter gets paths and compound paths alone, as before. An unknown, repeated or wrongly typed entry, `inputs` on
|
||||
another kind, below `api` 2, or with `permissions` that leave out `doc.read`, is refused when the plug-in is
|
||||
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.
|
||||
- `extensions` (exporters 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`):
|
||||
@@ -359,12 +359,16 @@ In this example the character and paragraph styles are cut short; a filter gets
|
||||
attributes as the native file stores them (`inline` when a run is an inline graphic), and `paragraphs` holds one
|
||||
paragraph style per paragraph (the paragraphs are split at `\n`). No font data or glyph outlines are given. Type
|
||||
whose runs hold more than 256 KiB of UTF-8 text, or more than 4,096 runs or paragraphs, comes with `truncated:
|
||||
true` and empty `runs` and `paragraphs`.
|
||||
true` and empty `runs` and `paragraphs`. The runs and paragraphs of all the type in one input take at most 16 MiB
|
||||
of JSON: type whose runs and paragraphs don't fit what the type before it in paint order left comes with
|
||||
`truncated: true` too.
|
||||
- An image (`"image"`): `pixelSize` is its width and height in pixels; `colorMode` is `"rgb"`, `"cmyk"`, `"gray"` or
|
||||
`"unknown"`, read from the image itself. An embedded image whose pixels take at most 1 MiB in base64 (443 × 443
|
||||
pixels fit) has them in `pixelsBase64`: straight RGBA, 8 bits a channel, row by row from the top, and
|
||||
`pixelsOmitted: false`. A larger one, and a linked image (`linked: true`), has `pixelsOmitted: true` and no pixels.
|
||||
The host never reads a linked image's file for a filter and never gives its path.
|
||||
`pixelsOmitted: false`. The pixels of all the images in one input take at most 16 MiB in base64. A larger image, one
|
||||
whose pixels don't fit what the images before it in paint order left, and a linked image (`linked: true`) have
|
||||
`pixelsOmitted: true` and no pixels; the host checks an image's size before it decodes the image. The host never
|
||||
reads a linked image's file for a filter and never gives its path.
|
||||
- A symbol instance (`"symbolInstance"`): `symbolId` is the name of the symbol it shows, with `missing: true` when the
|
||||
document has no symbol of that name. `overrides` is `{}`.
|
||||
|
||||
@@ -372,8 +376,9 @@ These objects are read only. The output must list each of them exactly once, as
|
||||
was given (numbers compare by value, so `1` and `1.0` are the same). A changed or added field, one left out, an object
|
||||
listed twice or left out, and a new object with one of these types are refused, and the document and undo history
|
||||
stay as they were: `objects[1] (id 17): read-only field 'text.runs[0].text' changed`, `objects (id 17): a read-only
|
||||
object was left out`. New objects listed after one of them go just above it. Their text and pixels count towards the
|
||||
input's half of the memory cap, and an echo of them towards the 64 MiB output cap.
|
||||
object was left out`. The field is named with control and bidirectional characters escaped (`\u{202E}`), cut to 64
|
||||
characters. New objects listed after one of them go just above it. Their text and pixels count toward the input's
|
||||
half of the memory cap, and an echo of them toward the 64 MiB output cap.
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -1488,7 +1488,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **Plug-in instruction budget at a bulk memory instruction:** a run that pauses where one slice of its instruction budget ends keeps the instructions the slice still held. A `memory.fill` or `memory.copy` that the slice can't pay for in full runs on the next slice, so a run whose budget covers its work finishes; a run ends with `Limit("instruction budget")` only when the instructions it has left can't pay for the next instruction. Regressions: `crates/plugins/tests/abi.rs`. Scores and hours unchanged.
|
||||
- **Plug-in permissions (G17):** a manifest may list `permissions` (`doc.read`, `doc.write`, `state`, `effect.render`, `export.write`; the first three follow storytold/photocraft#662) and an `api` level; without the list a plug-in gets what its kind implies and installs as before. Install Plug-in… and File › Open show the name, id, version, file SHA-256 and each permission in plain words, with control and bidirectional characters escaped, and install nothing on Cancel; `plugin.install` needs `grant`, and folders install only granted plug-ins. The grant is kept with the module's hash, a module asking for more asks again, and a filter run refuses output its permissions don't allow. Regressions: `crates/plugins/src/permissions.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`, `crates/ui-egui/src/tests/plugins.rs`. Scores and hours unchanged.
|
||||
- **Plug-in progress, Cancel and log lines (G17):** at plug-in API level 2 a plug-in with the `progress` permission imports `env.vc_progress`, `env.vc_cancelled` and `env.vc_log`. A heavy filter's and an export's progress windows show its progress as a percentage bar, their Cancel reaches it through `vc_cancelled`, and its log lines go to the app's log with its id, capped per line and per run. Installing checks every import and names the one it refuses. Regressions: `crates/plugins/src/host.rs`, `crates/plugins/tests/abi.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`, `crates/ui-egui/src/tests/plugin_runs.rs`. Scores and hours unchanged.
|
||||
- **Filter plug-ins read type, images and symbol instances (G17, #4333):** a filter at plug-in API level 2 lists `inputs` (`type`, `image`, `symbolInstance`) and is also given the selected objects of those kinds, read only: type with its runs, character and paragraph attributes (no text past 256 KiB or 4,096 runs or paragraphs), images with their size, color mode and, when embedded and at most 1 MiB in base64, their RGBA pixels (never a linked file's), and symbol instances with their symbol and transform. Its output returns each as `{id}` or exactly as given; any change or omission is refused before the document changes. Filters without `inputs` get the same input as before. Regressions: `crates/plugins/src/objects/tests.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`. Scores and hours unchanged.
|
||||
- **Filter plug-ins read type, images and symbol instances (G17, #4333):** a filter at plug-in API level 2 lists `inputs` (`type`, `image`, `symbolInstance`) with `doc.read` in its `permissions`, so installing it asks for approval, and is also given the selected objects of those kinds, read only: type with its runs, character and paragraph attributes (no text past 256 KiB or 4,096 runs or paragraphs, or 16 MiB of runs over the input), images with their size, color mode and, when embedded and at most 1 MiB in base64 (16 MiB over the input), their RGBA pixels (never a linked file's), and symbol instances with their symbol and transform. Its output returns each as `{id}` or exactly as given; any change or omission is refused before the document changes. Filters without `inputs` get the same input as before. Regressions: `crates/plugins/src/objects/tests.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`. Scores and hours unchanged.
|
||||
|
||||
- **Plug-in effects after a plug-in changes:** when a plug-in is installed for the first time, installed again or removed (`plugin.install`, `plugin.reload`, `plugin.remove`, File › Open of a `.wasm` file, the Additional Plug-ins Folder preference), the next frame the canvas draws uses the plug-ins then installed for objects with a plug-in effect and their raster effects, for the blends, envelopes and repeats that show such objects, and for pattern tiles and symbol instances whose art holds one.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user