The web build had no clock for plug-in runs, so its first view deferred
every plug-in live effect until Effect > Refresh Deferred Effects, and a
run's wall-clock limit was not enforced there. The deferrals clock and
the run deadline now read `web_time::Instant`, which is the system's
monotonic clock on the desktop and the page's `performance.now()` in the
web build. The web's first view now shares the same 2,000 ms budget, and
its runs stop at the 5 s, 60 s and 300 s limits as on the desktop.
Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
The text document writer wrote a type stroke's paint and weight but not
its cap, join or miter limit, which .ai import reads since #6500 (keys
61, 62 and 64). A file saved here and opened again came back with butt
caps, miter joins and a miter limit of 10, and the save reported those
attributes as left out. A stroked run now writes the three keys,
numbered as PostScript numbers caps and joins, with the miter limit as
import takes it back (1 to 500, else 10), and they no longer count as
lost. A run without a stroke no longer counts them as lost either: they
draw nothing.
Follow-up to #6472.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saving a legacy .ai of version 7 flattens each transparent image at its own
resolution, limited only by total pixels. The renderer draws at most 32,768
pixels a side, so an image that came out longer than that at its resolution
was cut off and placed as if whole: the part past that length showed the art
under it without the image. The resolution is now lowered until the longest
side fits.
Print as Bitmap limited each page's image to 65,535 pixels a side, but the
renderer draws at most 32,768. A 9000 x 100 pt artboard at a 300 ppi raster
effects resolution printed as an image that stopped at 7,864 pt and was
placed as if whole. The resolution is now lowered until the longest side
fits.
Pasted art whose SVG filter is renamed to a free name ("Soft 2") had
the new name written into the filter's id. For a filter at the 64 KB
limit the longer id made it too long to draw, so the pasted art lost
the effect. Paste now takes the first free name under which the filter
still fits: a shorter start of its name ("Sof"), else a free letter or
digit. The filter's id is always the effect's name.
Threaded type read from a file was flowed through its frames before
the document's composite fonts were registered, so a story set in one
was measured in the fallback font. Frames then held the wrong share of
the story after opening, until a frame was edited. load_with now lends
the document's composite fonts while it lays out its threads.
Object > Rasterize and the redrawn path of Crop Image allowed images up to
65,535 pixels a side, but the renderer draws at most 32,768. A 100 x 5569 pt
rectangle rasterized at 600 ppi came back 32,768 pixels tall, and the bottom
29% of the art was gone. Such an image is now refused like one over the
64 megapixel limit, and the art stays as it was.
A centered label such as Q||1 has no baseline for its empty middle
line, so a label that keeps more leading was not moved back to its
place. The graph now centers a label whose first and last lines hold
characters, whatever the lines between them hold.
The tool-edit sweep sent `{"<key>": "on"}` for every documented key, so
`prefs.export` wrote a preferences file named `on` into crates/engine on
every run, and `folder`, `destinationFolder` and `diffPath` strings from
`junk_params` named locations under the working directory.
- `is_path_key` covers `path`, `diffPath`, `folder` and
`destinationFolder`; `safe_value` sends a temp file for a file key and
a temp folder for a folder key, and both sweeps use it.
- A new test builds both sweeps' cases for every command and fails when
a string under one of those keys is outside the temp folder, without
running any command.
effect.deferredStatus only lists the document's deferred stages, so it joins reviewed::ALLOWED. effect.refreshDeferred runs plug-in effects again, so it joins reviewed::DENIED, which fixes every_command_is_reviewed.
The flattener limited an image's resolution only by its total pixels, while
the renderer draws at most 32,768 pixels a side. A long area, such as a narrow
see-through gradient strip down a 2 m banner at 1200 ppi, came back cut off
at that length and was placed as if whole, so the rest of the strip was lost
in Flatten Transparency and in PDF/X-1a, PDF/X-3 and PDF 1.3 exports. The
resolution is now also limited by the longest side, as raster effects are.
A merge left a second copy of the G9 `.ai`/EPS Missing bullet's opening inside the bullet. That copy says
writing the editing data is out of scope, while the same bullet and file-format-parity.md say a
PDF-compatible `.ai` carries an editing copy of the art. The bullet now has one copy, with the newer
brush wording that matches the 2026-11-05 entry above it.
The graph still stores an opacity or blend mode set on a series mark's row
that has no paint, as it does for transparency.set. The sentence now says
that such a row leaves only the stored paint unchanged.
`#strict on` and `//@strict on` no longer end the run as unsupported. They
turn on the checks the dollar-object help page gives for `$.strict`
(https://extendscript.docsforadobe.dev/extendscript-tools-features/dollar-object/):
an assignment to a read-only property throws a TypeError, and so does a new
property on an object that takes none.
VectorCraft's own rules: a getter-only property counts as read-only; the
objects that take no new properties are the non-extensible ones
(preventExtensions, seal, freeze, and the frozen vc and $ objects); the last
`#strict` line of the script and its includes sets the whole run, and runs
without one are unchanged; eval and Function take the checks of the code
that compiles them; a file $.evalFile runs keeps its own line's checks for
its code and functions, or takes the caller's without one. The rest of ES5
strict mode stays with 'use strict'.
The js crate carries the setting per compiled source
(Realm::set_strict_writes, and a strict_writes callback on
HostItem::Evaluator), and applies it only to property assignments.
When text.setGlyphAlternate leaves the text unchanged, it now reads
the glyph from the unchanged text. A character after the first in a run
that holds a glyph choice could be reported with its own glyph instead
of the ligature drawn for it, which text.glyphAlternates reports.
Pasted art whose svg.filter effect named one of the document's SVG filters
but drew another definition kept that name, so editing or deleting the
document's filter rewrote the pasted art's filter. Paste now renames such
a filter to a free name ("Soft 2") when the art comes from another
document or from SVG, and takes the document's filter when the art is
pasted back into the document it was copied from. Symbols, patterns,
brushes and graphic styles on the clipboard follow the same rule.
Category labels beside an axis and labels in pie wedges are centered
on their place by the default leading. A multi-line label that keeps
more leading or paragraph spacing ran down from that place instead.
The graph now puts the middle of such a label's first and last
baselines on its place.
#6090 moved the pinned window width in
the_control_bar_shows_the_paragraph_alignment_of_the_selected_type from
1440 to 1536 pt, since the bar's right end now keeps room for the panel
menu button. The progress line still named 1440 pt.