When loading the plug-in store can't remove the grant of an id whose stored
module lists no permissions, or can't read grants.json, the entry stays on
disk and still approved the bytes it covers. plugin.install has the same case
when the grant can't be removed and no earlier module can be put back.
The session now keeps those ids and does not count their grants in
plugin.inspect, plugin.install, plugin.reload or a later load of the store. A
successful update of the grant for the id ends that, and so does attaching a
different store.
A grants file that can't be opened or read may name the old module once
it reads again. plugin.remove and an install of a module listing no
permissions now fail on it and keep the module. A file over 1 MiB or one
that doesn't hold a list still approves nothing and doesn't stop them.
plugin.install of a module that lists no permissions ignored the result
of removing the id's grant. When grants.json could not be updated, the
formerly approved bytes could still be installed without approval. The
store now gets the old module back, so the grant and the module it
covers stay together. The new module stays when the old one can't be
read back and when grants.json can't be read, since such a file approves
nothing. Deleting the last grant now reports a failure.
plugin.remove had the same fault. It now moves the settings and cache
aside and removes the grant before anything is deleted. When the grant
can't be removed, the folder is moved back, the command returns an
error and the plug-in stays installed.
A settings get no longer reads and parses settings.json each time. The
parsed settings are kept per plug-in folder and read again only when the
file's size or modified time changes. A set to the value a key already
has writes nothing, and every other change is written with an atomic
write before the call returns. plugin.remove drops the kept copy.
cacheBytes in plugin.list, plugin.info and plugin.install comes from a
total kept while the cache folder keeps its modified time, instead of a
listing of up to 8,192 items per call. A cache write still lists the
folder to enforce the caps.
docs/plugins.md states what a run counts and that a cache call moves a
whole entry in one step. docs/plugins.md and docs/mcp.md now say when a
later plugin.remove retries a leftover .removed folder.
In Safe Mode and for a disabled stored module, load_stored_plugins does
not read the module, so grants.json keeps the grant for its id. The next
load that reads the module forgets the grant.
A piece linked to a global swatch is drawn in the swatch's current color at its own tint, and kept art and rasters follow a color or spot edit and its Undo. A value of `paintOutput` other than true is refused by a translated error. Tests in the effects crate pass the new document argument.
app.open with params and no importer ran file.open with the path alone, so the desktop app ran the importer on its defaults while headless app.open passed the params to document.open. file.open now carries params to the importer that opens the file.
The importer parameters test installs its module with replace from the second pass on, as plugin.install requires. The permission table in docs/plugins.md lists doc.read for filter, effect and exporter.
plugin.install wrote the grant and the module as two separate writes,
and either could fail with only a log record. With a grant tied to the
module's hash, the stored pair then no longer matched and the plug-in
was skipped at the next start.
The module is now written first and its grant only once it is there.
When the grant can't be written, the store gets its old module back, or
loses the new one on a first install, and a disabled plug-in stays
disabled. A stored module that can't be read back is not replaced by
one that needs a grant.
A module that lists no permissions now removes the grant from grants.json only after it has replaced the stored module. When the write fails, the old module stays stored and keeps the grant it needs at the next launch.
Installing a module that lists no permissions under a granted id now
forgets the grant, in the process and in grants.json, so the formerly
approved bytes need a new approval. A refused install leaves the grant.
Attaching a plug-in store forgets the grant of an id whose stored module
lists no permissions. A plug-in folder load forgets the process's grant
for such an id and leaves grants.json alone, because the store keeps the
module its grant covers.
Object > Expand of gradient and pattern fills now expands the pieces a
painted live effect draws one by one, so a piece keeps its own paints.
A later painted effect on a fill replaces only the first fill of a piece
and leaves the piece's other fills in place.
Document Info passes the document when it reads an image's painted fills.
File › Place reads only built-in formats, so a dropped file that only an
importer reads failed with a place error. It now opens as File › Open
opens it, with or without a drop position, also when an exporter lists
the same extension.
A plug-in of the wrong kind now gets "is a plug-in of kind `exporter`, not an importer", with rows in every complete-message catalog. The importer failure test also covers an open, edited document. docs/plugins.md states that Open Recent does not remember Open With's importer or parameters.
The reference check now applies wherever a painted effect's geometry is
used: a result whose swatch or pattern is refused keeps its input instead
of giving its geometry. Pathfinder art of type, reshaped groups, compound
shapes, image fills, the renderer's bounds and Object > Expand look the
references up in the document, and the renderer drops its kept bounds and
geometry when the names of the swatches or patterns change.
A grant records the module's SHA-256, but only its permissions were
compared, so a module with other bytes under a granted id inherited the
grant when it listed no new permission.
A grant now covers a module only when the id, the SHA-256 and every
listed permission match. plugin.inspect reports needsApproval for a
changed module, plugin.install refuses it without a new grant and leaves
the installed module and its grant as they are, Install Plug-in and
File > Open ask again, and the plug-in store and plug-in folders skip it.
The same bytes install again without asking, and a module with no
permissions list or an empty one needs no grant. A grant whose hash is
not 64 lower-case hexadecimal digits covers nothing.
plugin.install still trusts the client that sends grant; the docs say so.
File › Open With reads a picked file on the thread File › Open uses, with the importer and its parameters, so a slow importer does not freeze the window. A call that names path still gets document.open's result. Cancel in the progress window now raises a cancel token on that thread, which stops a running importer between two slices of its budget.
An importer may declare an extension that File › Open handles another
way: a plug-in, shortcut set, script, preset, library or swatch library
such as .ase, or one a built-in format reads. "All readable files" and
the importer's own filter no longer list those extensions, and an
importer with none left gets no filter. Open With still opens them.
A key that a paint does not have, such as `opacity` on a solid fill, was
dropped when the paint was read, and the fill was drawn opaque. The
result is now refused and `lastError` names the field, for keys on the
paint and on its colors, gradient, stops, placement and freeform points.
Pathfinder effects, compound shapes, Expand Appearance and pattern
expansion now pass the document to the effects they evaluate, so a
painted piece that names a swatch or pattern takes part as drawn instead
of being refused. A path with a painted effect takes part in Pathfinder
and compound shapes as its pieces, each with its own paint, inside the
path's opacity and blend mode.
The renderer's kept effect art and live evaluations now count the
document's swatch and pattern names for objects with a plug-in effect,
so deleting or restoring a swatch a piece names redraws the object.
New from Template, Load Character/Paragraph/All Styles, Recolor's Other
Library… and the library panels' Other Library… read files with the
built-in readers, so their open dialogs now use builtin_open_filters(),
which leaves out the extensions only importer plug-ins read. File › Open
keeps open_filters() with the importers' files.
The artboard of a document an importer opens is now kept within
±4,000,000 pt. Art at the edge (a zero-width path at x = 4,000,000, or a
stroke past the edge) gave an artboard beyond it, and every later edit
of the document was rejected.
File › Open With asks for the parameters of an importer that declares
any, at their defaults, once the file is picked (dialog `pluginImport`),
and opens the file with the values set. `file.openWith`, `app.open` and
MCP's `open_file` take `params` and pass them to the importer;
`app.open` without them runs the importer on their defaults, with no
dialog. A file opened with Open With is added to Open Recent, as File ›
Open adds the files it opens.