An embedded expression used as a tag or attribute name must now be one XML
name, and one used as a run of attributes must hold only name="value"
attributes separated by white space. Anything else throws SyntaxError, so a
script string cannot add an attribute, close the tag or add a child.
{name}={value} in a tag now reads as a computed attribute name, as in the
example in ECMA-357 section 11.1.4.
Replacing a child with a list, inserting a list, and assigning or
replacing by name now check the depth and take all fuel and heap before
the first change. A limit no longer leaves the old child removed, part
of a list inserted or one of several matches gone.
appendChild and assignment past the end of a list remove their empty
placeholder when the assignment stops. A list that takes an earlier
sibling at an index keeps that node in its slot.
The XML parser now throws SyntaxError when markup binds the xml or xmlns prefix or either fixed URI against the Namespaces in XML rules. addNamespace, setName, setLocalName and setNamespace throw TypeError for the same bindings and leave the node unchanged.
A new element or attribute cannot be in the xmlns namespace, and an attribute in no namespace cannot be named xmlns. The xmlns URI cannot be the default XML namespace. These assignments throw TypeError.
insertChild, prependChild, replace and list assignment take their heap
and fuel before moving a node out of its old parent. Replace past the
last child charges before the push, so a child is never left without
its parent link. Deleting a child or attribute by name, assigning an
attribute or assigning text to an element take the fuel for the whole
step before emptying the node. normalize removes the next text node
before it stores the joined text.
With `Options::e4x` set, vectorcraft-js runs ECMA-357 (2nd edition):
XML and XMLList literals with embedded expressions, the `..`, `.@`,
`.*`, `.()` and `::` selectors, assignment and `delete` on selected
nodes, `for each`, `default xml namespace`, and the four objects with
their methods and settings. The option is off by default, and without
it the syntax and globals do not exist.
Parsing, serializing, comparing and copying trees use no native stack,
and each takes fuel and counts against the heap limit. Inserting a node
into itself or its descendants throws a TypeError before the tree
changes. A literal or XML string nested past `Options::max_depth` gives
a SyntaxError, and a tree built past that depth stops at the heap limit.
`XML.prototype.xpath` is not provided.
A PDF image mask (`/ImageMask true`) painted in a solid color opened as an
RGB image with the color in its pixels: Fill and Recolor Artwork didn't
reach the color, a spot color lost its ink, and the same mask painted again
in another color took the first one, because the painted image was cached
by the mask alone, and so did an uncolored pattern whose cell draws a mask.
It now opens as a grayscale image of what it paints, colorized by that
color, as the .ai reader opens a 1-bit image mask (#5790). A spot color
keeps its swatch and tint. The page's walk counts paths, not images, so the
ink is found from the color space. Readings of a pattern cell are told
apart by colorized images' colors too. Inside an alpha soft mask, or
painted with a pattern, a mask opens as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
copyFrom only writes paints enabled for pickup and apply, and paint, Transparency and the Eyedropper record an undo step only when the override changes.
copyFrom still edits the part when reverse, append and defaultsOnly are false, and reverse copies onto source. transparency.set records an undo step only when the override changes.
A picked part takes transparency and the Eyedropper only when those calls omit their other targets. Art, Pattern and Bristle brushes, native Wayland and the web still do not take tilt, bearing or rotation.
A dithered fill or a Perceptual linear or radial gradient whose stops
are all CMYK was written as an RGB image. It is now a CMYK image.
The stops keep their inks, an off-center midpoint is halfway between
its two stops' inks, and a Perceptual curve uses the ink mixes a
DeviceN shading uses. Dither's noise is added per ink and adds no ink
where the gradient has none.
The Output settings convert the inks as they do for any CMYK image.
A gradient with an RGB, gray or Lab stop, a stop linked to a spot
swatch, or a Perceptual curve of more than 4,096 stops is still written
as an RGB image.
Direct Selection of a path, compound path or type inside a dynamic instance now applies transparency and gradient or pattern fills through the usual paint commands, and the G5 docs catch up on Overlap and Pattern auto corners.
On macOS the canvas paints tool cursor glyphs into the frame, so they
trail the pointer by a frame or more (#2292). This interim adapter
delivers the same glyphs as native NSCursors, which AppKit moves
independently of canvas redraws.
ui-egui prepares cached 1x/2x RGBA requests from the existing cursor
rasterizer and hotspots (transparent padding only) and keeps returning
a visible system fallback icon. The desktop host builds NSImage/NSCursor
objects with point-sized images and hotspots, owns cursor-rectangle
suspension and restoration, and hands the cursor back on menu tracking,
focus loss, empty passes, cursor overrides and shutdown.
Windows/X11, Wayland and web delivery are unchanged. No artwork, no
production unsafe, no new dependency package: only four AppKit features
are enabled on the already-shipped objc2-app-kit. Remove once the pinned
winit sizes macOS cursor images and hotspots in points.
Addresses #2292.
docs/mcp.md: vectorcraft-cli's mcp and run sessions keep the words in the file too; only without preferences do they last until the app closes. docs/type-parity.md: the custom dictionary commands are done and only the dialogs and Auto Spell Check remain, with the evidence at the end of the row.
- A custom dictionary file that is there but can't be read whole (not a
file, unreadable, or larger than the 1,000,000 bytes craft-proof reads)
is not loaded and never written over: attaching it logs why, and every
add, delete or change is refused with that reason. A missing file is
still created by the first save.
- When a save fails, the command takes back only its own change, so memory
and file agree; words the save took in from the file stay.
When the page's type is matched with the text objects, page words are also
split at private-use characters (from uniXXXX ligature glyph names such as
/uniE028 for a "ti" ligature) and each piece is matched as its own word, so
"Quality" set with that ligature matches its text object and the layers are
read without a false note.
Spine stored a full clone of the blend's PathData but only ever read its
first subpath. Store that SubPath instead, which drops the extra
allocation and makes the field's purpose clear. Document the field and
the two cases where Spine::new returns None.