The Brushes panel's Delete Brush button and its panel menu item now ask
before deleting the selected brushes, as Swatches and Graphic Styles do.
OK deletes them in one undo step and strokes that used them become plain
strokes; Cancel leaves the document unchanged. Alt-click (Option-click on
macOS) on the button deletes without asking. The brush.delete command
still deletes at once.
A live rectangle, rounded rectangle, ellipse or polygon selected with the
Selection tool shows a Shape Properties button where Lock was. It opens a
popup with the shape's rows from the Transform panel, which stays open
while the fields are edited. Every other selection, and the Direct and
Group Selection tools, keep Lock; Object > Lock and Cmd+2 still lock a
shape.
The Transform panel, the Properties panel and the popup draw the rows
through one live_shape_rows function.
The Symbols panel's Delete Symbol button and menu item now ask before
deleting. A symbol no instance uses gets "Delete the selected symbol?"
with Yes and No. A symbol with placed instances gets Expand Instances,
which turns the instances into ordinary art, Delete Instances, which
removes them, and Cancel; either Instances button deletes the symbol in
one undo step. Alt-click on the button deletes an unused symbol at once
and still asks about one with instances.
The question is bound to the document it was asked in: once another
document is active, or the symbol is gone, Yes deletes nothing, and a
symbol that gained instances while the question was open gets the
in-use question instead. symbol.delete itself does not ask.
The shared dialog frame takes a Cancel label (DialogSpec::cancel) for
the No button. The new strings have rows in every catalog.
SVG export ignored an image's colorize paint and wrote the image's own
black, white and gray pixels. EPS export flattened every colorized image
into a process-color raster, so a spot color lost its plate. Both now
write the image in the fill color, with the coverage the canvas and PDF
export use (darkness times alpha), so white and transparent pixels take
no color.
- SVG writes a coverage PNG as a mask over a rectangle in the color,
shared between repeated images while each keeps its own color. Linked
mode writes the coverage PNG as a file, and SVG Tiny 1.2 writes a PNG
in the color. A colorized image whose pixels are missing, or whose
grayscale pixels can't be decoded, is left out with a warning.
- EPS writes fully covered or uncovered pixels as an imagemask in the
color's process or Separation ink (Registration on every plate), and a
grayscale image as ink samples scaled by coverage. Level 3 masks its
uncovered pixels out and Level 2 writes them white; gray midtones are
written over white, with the existing warnings.
- EPS flattening no longer counts a solid colorize paint on a grayscale
or 1-bit image as transparency by itself. Opacity, blending modes,
opacity masks, effects and overlapping transparent art still flatten
it, and so does a colorize paint the writer can't draw.
The svg crate's tests turn on resvg's raster-images feature to render
the embedded PNGs.
Docs: docs/mcp.md, docs/target-app-parity.md.
The tip preview square in the Blob Brush Tool Options set the Angle by a
drag but had no accessible name, no value and no keyboard control.
Screen readers now get it as the Angle slider with the Angle in degrees
as its value. A click or a drag gives it the keyboard focus, and the
arrow keys, Shift with an arrow, Page Up and Page Down, Home and End
step the Angle within -180 to 180 degrees, as the dialog sliders built
on color_slider_stepped do (crates/ui-egui/src/widgets/color.rs). The
new widgets::stepped_area gives a caller-drawn control that slider's
screen-reader node, focus, keys and focus outline.
Homography::then_after returned products whose entries could overflow to
infinity or become NaN. A move, transform or grid reproject of type or a
symbol instance whose stored projection has large finite entries stored such
a product, and the type was then drawn flat, since a projection that is not
finite reads as none.
- Homography::checked_then_after returns None when an entry of the product
is not finite.
- project_node composes each type and symbol instance projection with it and
returns false, leaving the object as it is, when one would not be finite;
moves and transforms in perspective and Define Perspective Grid with
reproject then return the horizon error and leave the document unchanged.
- transform_map, attach_homography and size_homography return None for such
a product.
- Edit Text returns the horizon error, and does not enter isolation mode,
when the type cannot be shown flat where it is drawn.
A built-in add-on in Object › Plug-ins › Arrange Tools (#4830, #4831, #4832, #4833, #4834):
- addon.arrange.grid lays the top-level selected objects out in rows and
columns, a row or a column (stacking order from the back, visual bounds,
each object centred in its cell), from the selection's top-left or
centred on the artboard.
- addon.arrange.swap swaps two objects' bounds centres, and with `size`
their widths and heights.
- addon.arrange.replace puts a copy of the topmost selected object centred
on each of the others, in its stacking place, kept, stretched or fitted
in proportion; the replaced objects are deleted unless `keep`.
- addon.arrange.size gives each selected object an exact width and/or
height about a reference point.
- addon.arrange.zoomSelection (a UI command) fits the active view to the
selection.
Places and sizes are measured as object.setBounds measures them (its
transform, now also available per object), strokes scale as Scale Strokes
& Effects says, each command is one undo step, and Arrange in Grid,
Replace with Top Object and Set Size of Each open previewed dialogs.
Catalog rows, command sweep cases, docs/mcp.md, parity docs and the
ROADMAP progress log.
The tangent and perpendicular case is done for every tool the public pages name; its evidence stays in the revision row and in gaps.md and ui-parity.md.
The canvas keeps a hairline visible by raising a stroke's weight to half a device pixel. A width profile multiplies that weight, so a 1 pt stroke with a 6x width point swelled to twice its size at 25% zoom and shrank back on zooming in. The minimum now goes to the profile's widest point instead; plain strokes are unchanged.
The grid options' public page lists a Preview option: the repeat on the canvas follows the fields while the dialog is open. Object > Repeat > Options had none, so a change showed only after OK. Now a Grid Repeat's dialog has the checkbox: Cancel puts the original back without a history step, OK keeps the previewed values as one undo step. Radial and Mirror Repeat Options are left as they are, since their public pages list no Preview.
A mesh read from a file can have any number of patches, and the view any zoom: a 150 x 150 mesh from a 3 MB file exported at 20x asked for about 23 million quads (2 GB), and the command's own cap of 50 rows and columns does not apply to a loaded one. One tessellation now makes at most 1,048,576 quads, the subdivision per patch giving way before that, and a mesh with more patches than that is malformed, like a point count that does not fit.
perspective.move takes constrain: true instead of a plane-space
direction. For each object, the command snaps the move to the nearest
axis or 45-degree diagonal of that object's own plane, so a selection on
several planes keeps every object on a line of its plane. The tool sends
the raw pointer with the flag.
On a diagonal, Snap to Grid now moves along the diagonal by the nearer
per-axis correction, so that edge lands on its gridline.
Holding Shift while dragging objects with the Perspective Selection tool
snaps the move, in the plane's own coordinates, to the nearest plane axis
or 45 degree diagonal. This applies to plain moves and to Alt and
Ctrl+Alt copies. The preview, the final move and the measurement label
use the snapped point. Pressing or releasing Shift, pressing 5 and
pressing 1 to 4 during the drag recompute the preview from the pointer;
a move perpendicular to the plane is not constrained.
Blob Brush Tool Options gain Angle (-180 to 180 degrees, 0 by default)
and Roundness (0 to 100 percent, 100 by default), with a preview of the
tip and three dabs; a drag in the preview square sets the Angle. The
elliptical tip keeps its angle on the page along a stroke, a 0 percent
tip is drawn 0.1 percent as wide as it is long, and Size set to
Pressure still varies its size. path.blob takes angle and roundness,
and both values are kept with the tool's options.
While a non-round tip drags, the Blob Brush shows the outline of the
area it covers with its length label, and an Alt drag that smooths
shows the Smooth tool's thin line.
An open path with a single straight segment was written as a two-vertex
LWPOLYLINE (POLYLINE before R14). It is now a LINE entity from its start
to its end point. A stroke with a weight in R12, R13 and R14 keeps its
polyline, since a LINE has no width. Closed paths and paths with more
than one segment are unchanged.
Part of #1299.
The confirmation for deleting several swatches and groups now goes
through i18n::tn, as the Graphic Styles prompt does, and each catalog
gives the count-dependent forms in an @plural row.
eframe's web build sends a keystroke's text before its key press. On a
layout without Latin letters, the one-key shortcuts paired each text with
the last key press before it, so in the web build a lone keystroke had no
key and ran no shortcut, and a second keystroke in the same frame used
the first one's key.
Each text now takes the press right before it, unless an earlier text in
the frame took that press, and otherwise the press right after it. The
one-key shortcuts and the shortcut names a tool sees while it is busy use
the same pairing. The desktop order (key, then text) pairs as before.
With Document Swatches as the library, traced fills and strokes take the
library color without a swatch link. Global, spot and tint swatches no
longer link the trace, so editing a swatch later leaves the trace
unchanged, and swatch order no longer decides whether a fill is linked.
Each region still takes the nearest library color, and a tint traces at
the color the tint swatch shows.
The Tabs panel's ruler shows an upper marker at the first line's position
(left indent plus first-line indent) and a lower marker at the left
indent. Dragging the upper marker sets the first-line indent. Dragging the
lower marker sets the left indent and keeps each paragraph's first line
where it was; Ctrl-dragging it (Command on macOS) moves both markers.
Marker drags land on whole points, or on ruler ticks with Snap to Unit or
Shift, and each release is one undo step through text.setFormat. With a
marker selected, the X (or Y) field shows and sets its position.
When the touched paragraphs have different left indents, only the upper
marker is shown, and its position applies to every touched paragraph.
Path Precision Tools gains the Shared Edge Merge Tool, kept out of the
toolbars (All Tools drawer and Object › Plug-ins › Path Precision Tools,
no shortcut), and Keep Shared Points Together:
- Shared Edge Merge Tool (sharedEdge): over two paths touching at two
coincident anchors (tolerance 0 to 3 pt) hovering shows the two
stretches and the merged edge; a click replaces both with it, half way
between them, or following the upper (Shift) or lower (Alt) path. Both
paths stay closed or open, anchors either stretch has inside are
matched on the other, one undo step.
- Making ends: near anchors meet on a click and an anchor one path lacks
is added to match; Shift+Alt asks for this inside a stretch.
- Keep Shared Points Together: an option that makes Slide Anchor Along
Path, Extend Path and Set Anchor take coincident anchors along. Nothing
else in the app changes.
- Shared Edges section in the Anchor Inspector: brush diameter,
coincidence and the option.
- Commands addon.pathtools.mergeEdge and movePointsTogether, params
validated and covered by the command sweep.
- Cursor stand-in, catalog rows in every language, docs and tests.
With the Live Paint Bucket, Alt-click on vector art samples its fill,
stroke and stroke weight into the paint defaults, and on a placed image
it samples the pixel as a solid fill with no stroke. Alt+Shift-click
samples only the color under the pointer, including an intermediate
gradient color or image pixels, into the active fill or stroke. Neither
click paints or changes the selection or selected art, and the next
Bucket click paints with the sampled paint. While Alt is held, the
Bucket shows the Eyedropper cursor and no face or edge highlight.
appearance.copyFrom takes `defaultsOnly: true` to update only the paint
defaults, and paint.sampleColor takes `clearStroke: true` to clear the
stroke default after an image sample into the fill.
A knockout element with one opaque Normal solid fill (a path or
compound, no mask, effects or isolation) is drawn once under
AntiAlias::None, including the internal hard-edge frame of Art
Optimized supersampled exports, instead of being erased and added back
through per-element backdrop layers. Analytical antialiasing keeps the
existing compositor and rounding.
Exact-pixel tests compare against the general knockout path for paths
and compound holes across precision, supersampling, translucent parents
and copied backdrops, and check that the backdrop rasters are skipped;
translucent fills keep their knockout compositing.
# Conflicts:
# docs/roadmap.md
# docs/target-app-parity.md
With a path selected that the Smooth tool can change, Alt held at a
Paintbrush press now switches the drag to the Smooth tool: it smooths the
selected paths under the drag with the Smooth tool's radius and Smooth Tool
Options' Fidelity, and the cursor shows the Smooth tool before the press.
Alt pressed after a stroke begins still closes the path on release. With no
such path selected (nothing, only text or an image, or only a two-point
line), Alt at the press draws the closed stroke as before.
The Paintbrush no longer reads the Pencil's "Alt key toggles to Smooth Tool"
setting: its Alt close and Alt smoothing do not depend on it.
#4757 gives a blend the Xy written after its record. When the blend is a
layer's last object, the layer's own state block (Xy, Xd, 7 () XW, LB)
went to the blend instead, and the layer stayed opaque. The blend's state
is now tentative like a mesh's (#4748): its own 6 () XW confirms it, the
layer's LB gives it to the layer and the blend keeps what it had.
Also records the .ai/EPS audit fixes under G9, the File formats row and M4.
Path Precision Tools gains two tools, kept out of the toolbars (All Tools
drawer and Object › Plug-ins › Path Precision Tools, no shortcuts):
- Corner Tool (cornerTool): editable corners on any path through the
existing Live Corners. Hover shows an eligible anchor, a click cuts a
corner with the tool's radius and kind, a drag sizes it (Shift: whole
values), a corner's marker selects it and loads its values, resizes it
and removes it at 0 or on a double-click; a corner never grows past
what fits; an Alt-marquee cuts every eligible anchor inside.
- Corners section in the Anchor Inspector: radius, kind, selected count,
Apply to Selected Anchors.
- Slide Anchor Along Path (slideAnchor): a drag runs an anchor along its
curve, the neighbouring segments refitted by the keep-shape kernel;
Shift snaps where the tangent is a multiple of 45° from the constrain
angle.
- Commands addon.pathtools.cornerSet, cornerRemove and slideAnchor, one
undo step each, params validated and covered by the command sweep.
- Cursor stand-ins, catalog rows in every language, docs and tests.
Holding backquote while dragging with the Selection tool (move, bounding-box
scale or rotate) or the Rotate, Reflect, Scale or Shear tool applies the drag
to the selected objects' pattern fills and strokes and leaves their geometry
in place. The key state at release decides the result: the key may be pressed
after the mouse button goes down, and releasing it before the button comes up
commits an ordinary transform. Objects without pattern paint do not move.
PointerEvent gains a `backquote` flag (false when absent). The desktop canvas
reads it from the held key on every pointer event, and `ui.pointer` and the
`pointer_gesture` tool accept it per event or as a default for the gesture.
Apply Last Color keeps `,` and also gets `Shift+,` as a default key;
Apply Last Gradient keeps `.` and also gets `Shift+.`. The keymap
catalog takes the extra defaults from a small alias table, the
Keyboard Shortcuts dialog lists every default key of a row, and reset
restores both.
A shifted punctuation chord that the active keymap binds now runs from
its key press, once per press, so a user binding such as `Shift+;`
works and layouts where Shift+Period types another character still run
the bound command. Text editing still types the characters.
The toolbar tips read "Color (, or <)" and "Gradient (. or >)", with
rows in every catalog.
object.lock.unselected is now reached by its key and as a command only:
the Object > Lock > Unselected Artwork menu item is removed, and the
command stays in the registry, the shortcut editor and automation.
In a partly selected clip group or compound path it now locks the
unselected members, the clipping path included, and leaves the container
and the selected members unlocked. It locks unselected guides while Lock
Guides is on. Hidden art and art on a locked layer stay unlocked, and a
selected layer's art counts as selected.
With a selection in isolation mode the command is enabled and locks only
the unselected art inside the isolated group. The other Lock and Hide
items keep their isolation behavior.
Lock and Hide still share one selector. A lock flag on it selects the
clip group, compound path and guide cases above; Hide > Unselected
Artwork keeps its behavior, and a test covers both directions.
Docs: docs/mcp.md, docs/target-app-parity.md.
The reference app locks all deselected artwork on Cmd/Ctrl+Alt+Shift+2.
VectorCraft had no command for it: Object > Lock held Selection, All
Artwork Above and Other Layers.
The new command, object.lock.unselected (Object > Lock > Unselected
Artwork), locks in one undo step the objects that Hide > Unselected
Artwork hides: every object that Select All takes and that is neither
selected nor holds a selected object. It returns the count. Hidden
objects stay unlocked. The selection stays as it is, Unlock All unlocks
the objects again and selects them, and with nothing to lock the
command records no undo step. Both commands now share one function,
which locks or hides the objects that the existing unselected helper
returns.
In isolation mode the menu item is disabled, and the command returns
the error "not available in isolation mode" that Hide > Unselected
Artwork returns there, and changes nothing.
Docs: docs/mcp.md, docs/target-app-parity.md.
Adds the evidence and revision rows that target-app-parity.md and
roadmap.md expect for the .ai/EPS import changes of 2026-10-30 (#4708,
#4664, #4723, #4742, #4658, #4727, #4738, #4744, #4748, #4743). Scores
and estimates unchanged.
When Brushes, Color, Layers, Info, Gradient, Stroke, Attributes, Graphic
Styles, Appearance, Align, Transform, Pathfinder, Transparency or
Symbols is in front of an expanded dock group or a floating group, its
function key now hides the whole group instead of closing only that
tab. The next press brings the group back in the same place with every
tab and the panel in front, and Window > a hidden sibling also restores
the group. A background tab still comes to the front on the first
press, and in an icon column the key opens and closes the flyout.
Other panel shortcuts and plain window.panel calls keep their behavior.
The function key shortcuts for Brushes, Color, Layers, Info, Gradient,
Stroke, Attributes, Graphic Styles, Appearance, Align, Transform,
Pathfinder, Transparency and Symbols now run window.panel with
toggle: true, as the Window menu items do. A second press hides an
active panel in the expanded dock, and the next press restores it in
its group. A chord the user assigns to one of these panels toggles the
same way. Plain window.panel calls still only show the panel.
In Full Screen Mode and Full Screen Mode with Menu Bar, an Escape that
leaves a focused text field, cancels a ruler-origin drag or cancels a
floating Tools panel move no longer also returns to Normal Screen Mode;
the next Escape does. Tests cover a dialog, a focused field, a tool that
claims Escape, pattern edit, Global Edit, a tool flyout and the rotation
reset pair in both modes.
In Full Screen Mode and Full Screen Mode with Menu Bar, an unmodified
Escape that nothing earlier in the Escape chain takes (a popup, dialog,
field, drawer, tool gesture, edit mode, flyout or the view rotation
reset) runs view.screenMode with mode 0. Presentation Mode and the F
cycle are unchanged.
In vertical type, Alt+Down/Up (with or without Shift) steps kerning at a
caret or tracking of selected text, Alt+Left/Right steps leading, and
Alt+Shift+Right/Left steps baseline shift; Cmd/Ctrl makes five steps.
Horizontal type keeps its mapping. Selected type objects follow the same
rule when all of them are vertical, and a selection that mixes horizontal
and vertical type takes the horizontal mapping in one undo step.