In Outline the canvas kept the Preview pasteboard color and filled each
artboard with white paper. Paths and type draw as black hairlines, so
art on the pasteboard was close to invisible in the dark interface
brightnesses.
In Outline the whole canvas is now white in every interface brightness
and Canvas Color, pasteboard included. The artboards draw no paper and
show as their edges, so Simulate Colored Paper, an artboard's background
color and the paper of View > Hide Artboards no longer show in Outline.
The artboard names and the ruler origin's crosshair draw in dark gray
there. In split views and floating windows the artboard names follow
that window's own Outline setting, as the canvas already did.
Escape reset a rotated view only as the second of two presses less than
a second apart, and a click or another key between them started the
count again. With the Rotate View tool one press reset it.
Now an Escape that has nothing else to end resets the rotation. An
Escape that ends something else (isolation mode, a path being drawn, a
full screen mode, a dialog and the others listed in
docs/target-app-parity.md) ends only that, and the next Escape resets
the view, however long after it comes. Tools report a path or pick that
Escape ends with `Tool::ends_on_escape`.
The rotate view tests that needed two presses now check one press, and
the screen mode test that named the old pair is renamed.
Docs: docs/target-app-parity.md.
A mask, inverted mask or knockout group over type now keeps the region
that covers any artwork. Type is measured by its layout or, on a path,
by the path, and a reader that sets live type in its own font can draw
it wider, so a region sized from that box could cut glyphs off.
A live object (blend, envelope, repeat, mesh, placed document,
intertwine) under a mask is measured as the art the writer writes for
it, expanded on its own with the same limits.
The writer keeps its symbol boxes and symbols' art reach in plain
fields instead of cells.
Opacity masks, inverted and unclipped masks, and knockout groups were
written with mask and filter regions of 200,000 x 200,000 units around
the origin. CairoSVG draws each mask into an image of its region's size
and stops with "out of memory", so a file with any of them did not
open; Firefox drew inverted masks wrong and WebKit drew knockout groups
and inverted masks wrong.
Each region now covers what the masked or knocked-out art paints: its
visual box grown by its raster effects, brush art and members' reach,
the mask art's box, and in a scaled symbol instance its strokes and
effects at their own size. A mask whose art reaches past its object
still comes back whole on import.
With Measure Type by Glyph Bounds on, point type is measured by its
glyph outlines and its underline and strikethrough bars, as the canvas
draws them. A typed W or H gives what is drawn that size, and underlined
or struck-through spaces are measured by their bars instead of the em
box.
The texts now say that each point type object selected as an object
(not inside a selected group) is measured this way, that moved type
snaps by its ink box, and which Arrange Tools commands follow the
option: Swap Objects, Replace with Top Object, Set Size of Each and
Sort Objects. Arrange in Grid and Group Overlapping Objects keep
measuring visual bounds.
A face of a group member that paints nothing (its fill or stroke at 0% opacity) was drawn without a group, so its zero opacity was lost and the member drew fully opaque. Such a face now keeps a group with the opacity and blending mode of its fill or stroke.
engine.apiReference {} -> {markdown} writes a reference from the built-in
commands' parameter schemas each time it runs: a section per command that
has a schema, in registry order, with its label, menu path, API levels,
what it writes, cost, summary and return, and a table of its parameters
(type, range with included or excluded ends, unit, JSON or worked-out
default, required, levels and description; object members as dotted
rows). Commands without a schema, plug-in commands included, are left
out. Schema text is escaped so it stays plain text in its cell, control
characters become spaces, and a reference past 1 MiB is an error rather
than a cut-short text. The command needs no document.
MCP serves the same text as the fixed resource
vectorcraft://commands/reference (text/markdown; charset=utf-8); the
other resources still read as JSON.
The command's own schema raises the schema API level to 2. Its label and
its size error have rows in every catalog that needs them.
Docs: docs/mcp.md, docs/target-app-parity.md.
Export Selection, Export for Screens by selection or by layer, Asset
Export and CSS Properties' rasterized pictures put the objects on a new
plain layer. An object inside a clip group, or on a clipping layer, was
written without its clip, in SVG, PNG and the other formats, and the
opacity masks, effects, opacity and blend modes of the groups and
layers that held it were dropped too.
These exports now keep each object in the layers and groups that hold
it, and crop the file to the bounds the renderer measures for that art
(a clip group by its clip). Those layers are drawn even when they are
hidden, template or non-printing layers, as the objects were before. A
PSD of the export names its layers after the document's layers instead
of one layer named Selection or after the asset.
document.export with selectedOnly uses the same code: an object of a
blend, envelope, repeat or intertwine is now written on its own instead
of inside a blend of one object, and a Shaper group keeps its source
art. A text export of the selection still holds only the selected type,
not that of a type clipping path around it.
Measure Type by Glyph Bounds (`typeGlyphBounds`, off by default) is in
Preferences > Type > Glyph Bounds and the Transform panel menu. With it
on, the bounding box, the Transform, Control bar and Properties X, Y, W
and H, `object.setBounds`, the transform tools' reference point and the
Arrange commands measure point type selected on its own by the ink of
its glyphs instead of its em box. A typed W or H scales the live type
until its ink has that size.
- The engine caches each type object's ink in text space with its
layout bounds; the cache is not saved.
- Use Preview Bounds adds the characters' strokes, and type with
geometry effects is measured as drawn. Skewed type is measured by its
outlines.
- Type that draws nothing, area type, type on a path and type inside a
selected group are measured as before. Align and Distribute keep
following Align to Glyph Bounds.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md,
object.setBounds params text.
When any member of a revolved group had an opacity below 100% or a
blending mode, every member's surface was drawn whole in member order,
so the opaque members lost their shared depth sort and a transparent
member drew entirely over or under the others.
Every member's faces are now sorted into one order. Opaque faces go in
that order on their own. A transparent member's faces share one group
with its opacity and blending mode, and its scopes group them further,
unless a face of another member that overlaps them on the page lies
between them in the order; only there is the member split. Faces
overlap when the convex hulls of their outlines come within the 0.75 pt
seam stroke of each other. Faces that paint nothing never split a
member. Past 4,194,304 overlap tests on one group, each further face
of such a member is drawn as a part of its own.
Docs: docs/revolve.md.
revolve_curve_samples counted a profile's contours but never compared the count with the limit,
so a profile made only of empty or one-anchor subpaths passed, although each contour takes one
profile point. HoleSearch::new then built one entry for each subpath without a limit, and mesh
export skips revolve_curve_samples when curve_samples is given. Both now refuse a profile with
more than 16,384 contours as too complex.
effect.setParams checks a Revolve before the edit, so turning a hidden Revolve back on measured
its profile after every geometry effect on the object, those after the Revolve included, and could
refuse a profile that draws. The check now measures the profile after only the geometry effects
before the Revolve, hidden or shown, as drawing does. An object holds at most one Revolve, hidden
ones included, so the check finds the same one that draws.
With Use Preview Bounds on, a curve piece that bends more tightly than
half the stroke weight after the last split had its box grown by half
the weight on every side. A handle on an end point makes the curve's
first or last control difference zero, so such a curve always reached
that fallback and measured past its butt end: a vertical 2 pt curve
from (50,10) to (50,60) with its first handle on (50,10) measured
Rect(49, 9, 51, 60) instead of Rect(49, 10, 51, 60).
The fallback now measures the piece from the directions its tangent can
take, the nonzero control differences, when they all lie within a
quarter turn of their sum. Pieces whose tangent turns further are still
grown on every side.
engine.commands, command_list and list_commands now list an inputSchema
next to schema for each command with a parameter schema. It is the same
JSON Schema 2020-12 schema with the root type "object", the form an MCP
tool's inputSchema takes
(https://modelcontextprotocol.io/specification/2026-07-28/schema);
schema keeps the root type ["object", "null"] when no parameter is
required. A command without a schema lists neither field. tools/list is
unchanged, and the commands are still run with command_run or
run_command.
brief now leaves out schema and inputSchema as well as the params text;
a listing filtered to the command's id gives them.
Docs: docs/mcp.md, docs/target-app-parity.md.
A number field took every wheel turn that came while the pointer was
over it. When a panel scrolled a field under a still pointer, the field
stepped its value with each turn that reached it.
Number fields now follow the rule #6375 gave the closed font family
field. A field takes the wheel only when the wheel stream began over
it and the pointer was moved onto it, or pressed on it, after the field
last moved or was covered by a window or overlay. A stream that began
anywhere else stays there until 0.5 s pass with no wheel input or the
pointer is pressed on a field. A stream that leaves its field does not
return to it. It leaves when the pointer moves off the field or out of
the window, and when the field moves or misses a layout pass. Trackpad
momentum never starts a stream on a field. When some of a frame's wheel
turns land on the field and some land elsewhere, the field takes none
of them and the panel scrolls with all of them. The rule and its 0.5 s
are VectorCraft's own.
Moving the pointer from one number field to another within 0.5 s of
the last wheel turn now scrolls the panel until the wheel pauses or the
pointer is pressed on the new field.
The stream state and the 0.5 s gap move to widgets/wheel_stream.rs,
which both kinds of field use.
An image in a DeviceN color space opened as an RGB image, losing its inks.
One of one colorant now opens as a Separation image does since #6347: a
grayscale image of its tints colorized by its ink. One of two to 32
colorants that are all process plates (the ink None may be among them)
opens as a CMYK image, each colorant's tint in its plate, its Decode array
applied (8 or 16 bits a sample). A DeviceN image that mixes a spot ink
with other colorants, such as a duotone, still opens as RGB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A closed font family field stayed ready for the wheel as long as it kept
its place and drew in each pass. Coverage was checked only when a pointer
move or wheel turn arrived. When a window, modal or popup covered a still
pointer and went away with no input in between, the next plain wheel
turn changed the selected type's font.
The field now stops being ready, and a wheel stream it held ends, in any
pass where the last pointer position is over the field but on another
layer. The pointer has to move onto the field again before the wheel
steps the font.