Artboard Options showed Name, Width and Height only: the generic form
hides x and y, which in the shape dialogs are the click position. The
artboard's position could be dragged but not typed.
The dialog now draws its own rows after the form's, in the same grid:
the reference point locator the Transform panel, the Control bar and
Properties share, then X and Y. They show where the reference point
lies, counted from the ruler origin (#1212), as the Transform panel and
the Control bar show an artboard's position. A typed X or Y moves the
artboard so that its reference point lands there, and a new Width or
Height keeps the reference point in place. The fields still hold the
top-left corner in document points, so OK runs artboard.setProps as
before, in one undo step. The shape dialogs keep their x and y hidden.
form::grid_then lets a dialog add rows to the generic grid.
Each swatch or graphic style library now opens in a panel of its own,
`library:<kind>:<library id>`, in the shared docking system: several can be open
at once, and they float, group as tabs, dock and close like any panel.
Opening a library that is open brings its panel forward; Previous / Next
Library show the neighbouring library in the same panel.
The library panel menu gains Persistent. The panels' view, Show Find Field
and Persistent are kept in the UI preferences (`library_panels`); at startup
the persistent panels reopen where the saved layout had them, a library
loaded from a file read again from its path (a missing file is skipped with
a status-bar note). `ui.libraryPanel {panel, library?, view?, showFind?,
persistent?}` reaches all of it; docs/mcp.md documents the panels.
A click with the Group Selection tool on a compound path selects the path
under the pointer (its outline within the pick tolerance, else the innermost
path whose area holds the click), so the outer shape and each hole can be
selected on their own; Shift-click adds another, and a click on a selected
path adds the compound path, as a click on a group member climbs to its group.
A path selected this way is an object of its own for Move, Transform, Rotate
and the other transforms, as it already was inside an isolated compound path:
the hole of a ring moves without the ring. Paint and the other commands still
go to the compound path, which holds the paint.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
# Conflicts:
# docs/target-app-parity.md
View › Rulers › Change to Global Rulers (Cmd+Alt+R) was a disabled
stub: the rulers always counted from one document zero point. The
reference app's public help describes two modes: artboard rulers, the
default, count from the active artboard's top-left corner, and each
artboard can have its own zero point; global rulers keep one zero point
for the whole document whichever artboard is active.
https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/about-rulers.htmlhttps://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html
`view.globalRulers {global?}` switches the mode (toggle by default),
undoable and saved with the document (`rulers`). The menu item reads
Change to Artboard Rulers while global rulers are on. A new document has
artboard rulers; a file saved before the mode existed has no `rulers`
key and opens with global rulers, so it measures as it did.
With artboard rulers, dragging the ruler corner sets the active
artboard's zero point, kept as an offset from its corner
(`artboard_ruler_origins`, by artboard id) so it moves with the
artboard, and double-clicking the corner resets it. The global zero
point (`ruler_origin`) stays separate. `document.rulerOrigin`,
`document.setRulerOrigin` and `document.resetRulerOrigin` take
`artboard`; `Document::ruler_origin_for` is the zero point with an
artboard active, falling back to the corner or the global one when a
stored value is out of range.
The rulers, the Transform, Properties and Control bar X/Y, the Info
panel's X/Y (which ignored the zero point before), ruler-guide
readouts and Shift ruler-tick snapping all count from that zero point:
the view's active artboard reaches the tools through `ViewInfo` and
`ToolContext::ruler_origin`. Command coordinates stay absolute.
Tests: engine/tests/ruler_origin.rs (default mode, per-artboard origin
following its artboard, global origin apart, toggle and undo, save and
legacy files, out-of-range values), ui-egui/src/tests_ruler_origin.rs
(position fields and corner drag on the active artboard, menu label and
shortcut), tools/src/rulerguide.rs (guide readouts and tick snapping).
Tests written for the single global zero point now turn global rulers
on first. "Change to Artboard Rulers" is translated in every catalog.
Docs: docs/ui-parity.md, docs/gaps.md (G7), docs/target-app-parity.md,
docs/mcp.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2JbUtM4MkzWLm9FgYiEsq
Window › SVG Interactivity was a disabled menu item. VectorCraft could
not attach JavaScript to an object's events or link JavaScript files
for SVG export.
The SVG Interactivity panel lists 19 events, from onfocusin to
onscroll. Pick one, type its JavaScript and press Enter to add it to
the selected objects; the list shows their events. Remove Selected
Entry removes the picked event, and the panel menu's Clear Events
removes all of them. Link JavaScript Files… opens the JavaScript
Files dialog, which adds a path or URL to the document's list and
removes the selected one. Its Choose… button puts a picked file in
as a file:/// URL. The commands are svgInteractivity.info,
setEvent, removeEvent, clearEvents, addFile and removeFile, and
ui.javascriptFilesDialog. Each edit is one undo step. A document
links at most 64 files.
SVG export writes each event as an event attribute on the object's
element and each file as a <script xlink:href> element. With the SVG
Tiny 1.2 profile, export leaves the events out with a warning. SVG
import reads the listed event attributes and the script links back
as text. It never reads a linked file or runs a script, and it leaves
inline scripts and the links after the first 64 out with a warning.
The native format keeps both.
An unnamed <g> with data-* attributes of its own around a single
object now imports as a group. Before, import folded it into the
object, which then took the group's data-* or, when it had its own,
dropped the group's. An unnamed <g> with event attributes stays a
group in the same way. The SVG import fuzz tests now include event
attributes and <script> elements.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md,
docs/ui-parity.md.
The reference app has a Blend panel, opened with Window > Blend, that
makes and edits blends in one place. VectorCraft had the Blend tool,
Object › Blend and the Blend Options dialog, but no Blend panel.
Window › Blend opens a panel with Make, the selected blend's Spacing and
Orientation, Reverse Front to Back, Reverse Spine, Replace Spine, Expand
and Release. Each control runs its Object › Blend command, so a change
of the selected blend's spacing or orientation shows on the canvas at
once and is one undo step. With two or more objects selected and no
blend, Spacing and Orientation set the options new blends start with,
as Blend Options does. Make is dimmed until two or more objects are
selected. Spacing and Orientation are dimmed until a blend or two or
more objects are selected, and the other buttons are dimmed until a
blend is selected. The Blend Options dialog's orientation toggles now
have names for screen readers.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
Native files remember the grid like the saved view, so reopening restores it.
Toggling it is still view state: no undo step, document not marked modified.
Fixes#1968
A corner's radius was capped at half its shorter side whatever its neighbours did, so one corner of a 100 pt square rounded alone stopped at 50 pt. Each corner now rounds until its cut reaches the cuts its neighbours ask for: the whole side when the neighbour is square, halfway when both ask for more. The corner widget drag, the red limit outline and CSS border-radius export follow the same limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI parity row (and G12 in gaps.md, M12 in roadmap.md) said quick Image
Trace presets reveal the editable controls. Since baadbe2c6 a quick trace
leaves the panel as it was (test a_quick_trace_leaves_the_panel_as_it_was)
and the Image Trace button in the Control bar or Properties opens the
controls. That matches the public documentation: choosing a preset traces
the image with it straight away, and the panel is opened with the Image
Trace button in the Control panel or the Properties panel's Image Trace
section
(https://helpx.adobe.com/illustrator/using/image-trace-results-optimization.html).
Reworded the three rows, cited the URL, added revision-history entries and
status-line notes. Wording only; scores, statuses and hours unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tBcdwNhptqtmX32JCGFzB
Node editing as the public manual describes it: Direct Selection's anchor squares (hollow
or filled), Reshape Segment over a curved segment, Group Selection's plus (also Direct
Selection with Alt), the arrow hollow while a drag snaps, and Alt-copy arrowheads. The
Anchor Point caret, the Curvature pen and Free Transform's side-shear arrows (ShearH,
ShearV) are drawn in code. Lasso, the freehand, symbolism and other tools return cursors
of their own that borrow their old look until drawn (Cursor::stand_in).
crates/tools/src/cursors.toml lists every Cursor value, when it shows and its hotspot,
for whoever draws them; `cargo xtask cursors` (in ci) warns about a missing, unknown,
repeated or incomplete entry and never fails. The busy cursor is listed as missing (G1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On macOS, the save sheets of Save As, Save a Copy and the first Save of
a new document have a Format menu again, and the Save as Template sheet
now has one too. The panel is an NSSavePanel built in
apps/vectorcraft/src/mac_save_format.rs, and the menu is its accessory
view, set before beginSheetModalForWindow:completionHandler: shows the
sheet. The menu from #1235 was added after rfd had begun the sheet, and
recent macOS versions dismissed the sheet at once (#1800, #1831,
#1518); #1855 removed it.
Choosing a format changes the extension in the name field and puts
that format's extension first in the allowed file types, which AppKit
adds to a name typed without one. A name typed with another listed
extension is saved in that format. Save panels with one file type and
the open and folder panels are still shown by rfd.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
Issue #1935 asks for batch exports of many logo variations in many
formats, each format in its own folder. This builds on Export for
Screens, which already writes every chosen artboard in every format row.
Names: a file name pattern (`name`) names every file alike. Tokens:
<filename> (the document), <name> (the artboard, asset, layer or
object), <index>, a run of # (a counter, zero-padded to the run's
length, from counterStart; counterPer item or file), YYYYMMDD,
YYYY-MM-DD, <date>, <time>, <YYYY>, <MM>, <DD>, <format>, <scale> and
<suffix>. A trailing .ext is dropped. Words are not changed: only the
date patterns and the bracketed tokens are. `date` sets the day (the
default is today, UTC). Different files that a pattern names alike get
-2, -3... Without a pattern, two rows with one name still write the
first row only, as before.
Folders: `folders` puts all files in one folder, a folder per row (as
Create Sub-folders did), per extension, per item, or per item and then
extension.
What is exported: besides artboards, assets and the full document,
`layers` (each layer's art alone, sublayers included) and `selection`
(one file of the selection, or a file per object).
Formats: rows can also be EPS, AI and VectorCraft (a document of the
item alone, its one artboard). A PDF row with printer's marks (PDF
Print in the dialog: trim, registration, colour bars, page information,
suffix -print) sits next to a plain PDF row.
Presets: document.exportPresets.list, .save and .delete keep batch
export presets with the preferences, and `exportPreset` uses one under
the call's own params. Built-in: Logo Package (SVG, PNG 1x and 2x, PDF
and print PDF, a folder per format) and Print-Ready PDF.
Dialog: Layers and Selection tabs with thumbnails; an Export Preset
menu with Save and Delete; Folders; File Name with Insert for the
tokens, Start at, Prefix, and an example of the first file's name. The
OK button says Export Layer or Export Selection on those tabs.
Tests: the pattern tokens, the request's example (SVG, PNG, PDF and
print PDF for artboards 1-3, a folder per extension, the print PDF a
bigger page), folders by item and both, the counter by item and by
file, numbered duplicates, layers and the selection, EPS, AI and
VectorCraft per artboard, presets (built-in, saved, used, replaced,
deleted, kept with the preferences), bad params, and the dialog (every
tab, the example name, a pattern export into folders, the Layers and
Selection tabs, presets applied and saved). The three tests that
refused EPS and VectorCraft rows now check those files. New labels
are translated in all 19 catalogs.
The Simplify dialog had Tolerance, Preview and the anchor point counts.
It now also has three of the options of the reference app's advanced
Simplify dialog:
- Corner Point Angle Threshold is a slider from 0° to 180° that opens
at 150°, with its label above it so the dialog stays under the
420-point width that menu_parameter_dialogs_are_compact allows. An
anchor whose corner is sharper than the threshold stays a corner, so
moving the slider right keeps more corners. The dialog sends
object.path.simplify's cornerAngle as 180° less the threshold, so the
opening value gives the same result as before.
- Convert to Straight Lines sends straightLines.
- While Preview is on, Show Original Path draws the paths as they were
before the preview, in a faint band of their layer's color under the
selection outlines.
The three labels have rows in the 19 catalogs that translate every
label.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
The Selection tool shows two pie widgets just outside a selected live
ellipse while the bounding box shows: one at the Pie Start Angle and
one at the Pie End Angle. Dragging one sets its angle to the angle of
the outline's point that lies in the direction of the pointer from the
ellipse's center, in one undo step, through `object.setLiveShape`'s
`pieStart` or `pieEnd`. Double-clicking one makes the ellipse whole
again (0° to 360°). Where the two widgets coincide, as on a whole
ellipse, a press takes the End widget.
Docs: docs/target-app-parity.md, docs/ui-parity.md.
After Object › Path › Remove Anchor Points the anchor that follows each
removed one is selected: round the end of a closed subpath, or the one
before it at an open subpath's end. Running the command again goes on
along the path, without picking each anchor first. Several anchors,
subpaths and paths each get their own next anchor; a path that goes
leaves nothing selected; one undo step restores the anchors and the
selection.
The Delete key still selects the remainder whole so a second Delete
removes it, and the Delete Anchor Point tool still leaves the path
selected whole.
Fixes#2241
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unit fields and length strings read a percentage operand as a share of
a value. After * or /, the percentage scales the term before it:
3cm*50% is 1.5 cm and 3cm/50% is 6 cm. After + or -, it adds or takes
away that share of everything before it: 50pt+25% is 62.5 pt and
100pt+20pt-50% is 60 pt. Before this change these expressions read as
nothing, and the field kept its old value.
The parser now splits a sum at its last + or -, so a percentage applies
to the whole sum before it. Text without % gives the same values as
before, apart from float rounding. A percentage on its own (50%) still
reads as nothing, and percent and degree fields still read % as their
suffix.
Command length strings use the same parser: file.new's width and
height, the distance params and the length preferences.
Docs: docs/mcp.md, docs/ui-parity.md, docs/target-app-parity.md.
Store the registration point with the symbol and place instances so that point lands on (x, y). Symbol Options exposes the same 9-point control as Transform.
Co-authored-by: Cursor <cursoragent@cursor.com>
Expose existing Warp styles, orientation and percentage controls through the shared live effect dialog while preserving preview history and effect targets. Keep ordinary Enter owned by focused non-searchable combo controls.
Co-authored-by: Codex <noreply@openai.com>
Keep surviving drawable runs separate when Delete or Backspace removes an open-path anchor. Preserve appearance, selection and one-step Undo.
Co-authored-by: Codex <noreply@openai.com>
The Curvature tool built its curve as a uniform Catmull-Rom: both handles of a
smooth point a sixth of the chord between its neighbours, the ends of an open
path without handles. Uneven spacing overshot or looped, a J or U from three
points bent sharply at the middle with straight legs, and the end segments
looked flat.
- `curvature_spline` (draw2.rs) makes each stretch of three points follow the
circle through them: a smooth point's tangent is that circle's
(`smooth_handles`), each handle `chord / (3 cos²(α/2))` long, α between
handle and chord capped at 90° (`arc_handle`). The ends of an open path
mirror their neighbour's tangent in their chord (`end_handle`). A turn
tighter than 30° (`TIGHT_TURN`) shortens its handles, so a narrow V comes to
a point. Degenerate points become corners. `path.curvature` and the rubber
band use it.
- `catmull_rom` is unchanged for the Pencil and Paintbrush fitting.
- `bend_last` (from: smooth), `curvature_move` on a smooth point, the point
`curvature_insert` adds on a straight segment, and `path.convertAnchor` to
smooth without x/y (`curvature_smooth`) use the same rule.
- `curvature_extend` gives the new end its end handle unless Alt-clicked
(`corner` param on `path.curvatureEdit`).
- Neighbours of a point dragged on an existing path keep their tangents (a
Pen-drawn shape isn't reshaped); the path being drawn re-curves throughout.
A Pencil or Paintbrush stroke always left the path it drew or continued
selected. The reference app's Pencil and Paintbrush options have Keep
selected for this, and VectorCraft's Tool Options had no such row.
The Pencil and Paintbrush Tool Options now have a Keep selected
checkbox, on by default, which keeps today's behavior. When it is off,
a new path is not selected and the selection stays as it was, and a
continued path is removed from the selection while the rest stays. The
stroke is still one undo step. The option persists like the tools'
other options, and path.freehand and brush.freehand take keepSelected.
Docs: docs/target-app-parity.md, docs/ui-parity.md, docs/control-protocol.md.
The Corner Radius field in the Properties and Transform panels has a
link toggle behind it (the "singleCornerRadius" preference, on by
default). When the link is on, one field sets every corner, as before.
When it is off, the panels show one field for each corner of a live
rectangle: Top Left, Top Right, Bottom Right and Bottom Left. Each field
sets its own corner through object.setLiveShape {corners, radius}.
This is the part of #1594 that does not change the corner widget drag
or its modifier keys, split out for review.
Tests: a Properties panel test (the link on shows one field; off shows
the four labelled fields with each corner's radius, in line and in
order). The new labels are translated in all 15 catalogs.
With Double Click To Isolate on, the Selection tool isolated groups only. A
double-click on a single path, compound path, image, mesh or placed file now
isolates it too, as the public documentation describes. Type still switches to
the Type tool; blends, envelopes, repeats and symbol instances keep their own
editing modes.
While a single object is isolated only it is hit, marqueed and taken by Select
All, new art goes beside it in its group or layer, and a double-click on the
empty canvas leaves with it selected. object.isolate accepts any object except
type.