Fit to Artwork Bounds resized the active artboard to the bounds of all
the art in the document, wherever that art was. With art on two
artboards, fitting one of them stretched it over both.
artboard.fitToArt now takes `indices` and fits each artboard to the
art on it, in one undo step. An object counts for every artboard its
drawn bounds overlap, and the artboard is fitted to all of its bounds,
also where it reaches past the artboard's edge. Art flush with an edge
from outside does not count. An artboard with no art on it keeps its
size and place, and the command fails with "no artwork" when none of
its artboards has art. Object › Artboards and the Artboards panel menu
pass the selected artboards (the Artboard tool's, else the active one),
and a recorded action leaves them out. Fit to Selected Art still
resizes the active artboard.
Docs: docs/mcp.md, docs/target-app-parity.md.
In the Transform panel, the Properties panel's Transform section and
the Control bar, Ctrl+Enter (Cmd+Return on a Mac) in W or H left the
field active and applied nothing. Now it applies the value as Enter
does and sends `proportional: true` to `object.setBounds`, so the other
side scales by the same factor with the Constrain Width and Height
Proportions link off. With the Artboard tool, the Transform panel's W
and H keep the active artboard's proportions the same way. X, Y,
Rotate, Shear and the number fields of dialogs keep Ctrl/Cmd+Enter as
it was.
- widgets::size_field is a num_field that Ctrl/Cmd+Enter also commits.
- transform::proportional gives the `proportional` param of the W and
H fields: the link, or true after Ctrl/Cmd+Enter.
Docs: docs/target-app-parity.md.
A rotated object's bounding box stands at the object's angle. No
command turned the view to the angle of the selection: `view.rotate`
turns the view only to an angle passed to it.
View › Rotate View to Selection, between the Rotate View submenu and
Reset Rotate View, runs a new UI command, `view.rotateToSelection`. It
turns the view about the center of the canvas to the angle the
selected objects share, the angle `document.inspect` reports as
`selectionRotation`, so their bounding box is square to the window.
Objects turned to different angles share no angle, and the view turns
back to 0. The zoom and the view's center stay as they were. The
command returns `{angle}`. With nothing selected it returns "nothing
selected" and the menu item is disabled; without a document it
returns "no document".
Every catalog that translates the menus has a new "Rotate View to
Selection" row.
Docs: docs/mcp.md, docs/target-app-parity.md.
With a key object, the Align panel's six Distribute Objects buttons
ignored the Distribute Spacing value. They kept the outermost objects
in place, spaced the others evenly between them and could move the
key object.
object.distribute now takes spacing. While the selection has a key
object, the key object stays in place, with the compound path or
selected group that contains it, and the objects' left edges, centers
or right edges (top edges, centers or bottom edges) end up spacing
points apart, outward from the key object's, in their order on that
line. With 0, the field's default, every object ends on the key
object's line. Two objects are enough, and to and artboard are not
used. The panel sends the value with the Distribute Objects buttons
while there is a key object, as it does with the Distribute Spacing
buttons. Without a key object, or with anchor points selected,
object.distribute does not use spacing and works as before. A spacing
that is not a number is an error.
Docs: docs/mcp.md, docs/target-app-parity.md.
Escape never changed the view's rotation.
Now a second Escape less than a second after the first runs Reset
Rotate View (`view.rotateReset`) when the view is rotated and the
second press has nothing else to end: no Presentation Mode, pattern
editing, isolation mode or open tool flyout, no tool that is busy or
takes the key, no open menu, dialog, command palette or Edit Toolbar
drawer, no text field or input method that takes the key, no number
field being dragged and no drag of the ruler origin. The first Escape
may end something, such as isolation mode, but it does not count as the
first when a menu, a dialog, the command palette, the drawer, a field
or an input method takes it. After a click, a press of another key that
is not a modifier (Shift, Control, Alt/Option or Command) or a held
Escape's repeats, the next Escape counts as the first. With the Rotate
View tool chosen, one Escape resets the view.
Docs: docs/target-app-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>
Tab set the panels, the toolbar and the Control bar to one value on
each press. Two presses turned on a Control bar that had been off.
When the toolbar was hidden with Window › Tools, Tab showed the toolbar
and the Control bar instead of hiding the panels.
Tab now records which of the three showed when it hides them, and the
next Tab shows those again. Panels shown with Shift+Tab in between
stay shown. While the panels are hidden and the toolbar or the
Control bar shows, as after Shift+Tab, Tab hides those bars instead of
showing all three. When the toolbar or the Control bar was shown another way
after Tab hid them, the next Tab hides what shows and records that
instead. A switch of workspace clears the record. When none of the
three shows and Tab hid none, Tab shows all three, as before. The
record is saved with the other UI settings, so the first Tab after a
restart shows the same bars.
Docs: docs/target-app-parity.md.
On macOS the canvas paints the tool cursor into the window and hides
the system pointer. winit hides the pointer with a cursor rect that
holds a transparent cursor, and AppKit applies cursor rects only in the
key window. Over a VectorCraft window behind another app, or under its
own File › Open… sheet, macOS showed its arrow next to the painted tool
cursor.
On macOS the canvas now paints the tool cursor only while the window
has the focus. Without the focus it sets the tool's system cursor icon,
and the system pointer shows alone. Windows, X11, Wayland and the web
are unchanged.
Part of #2020.
Docs: docs/target-app-parity.md.
In the Swatches panel, a swatch dragged onto another swatch moved next
to it whether or not Alt was held. With Alt held at the release, a
swatch dropped on a swatch of its kind now gives that swatch its color,
gradient or pattern in one undo step, and the swatch keeps its name and
place. Art linked to a global swatch takes the new color. Drags of
None, Registration or color groups, and drops on a tint swatch or on a
swatch of another kind, work as before.
A color dragged in from outside the panel, such as from a Fill/Stroke
proxy, and Alt-dropped on a tint swatch failed with an error that the
swatch is a tint. It now becomes a new swatch, as a drop on a swatch of
another kind does.
Docs: docs/control-protocol.md, docs/target-app-parity.md.
As in the reference app's Contextual Task Bar, which offers "options like
Image Trace, Retype, and Crop Image" for images and lets you "quickly
expand traced artwork from the bar" (public announcement:
https://community.adobe.com/announcements-651/contextual-task-bar-in-illustrator-2025-quick-actions-when-you-need-them-1546580):
- One image selected: Image Trace traces it at once with the Default
preset, leaving the Image Trace panel as it was (like the quick trace
from Properties and the Control bar); Crop Image shows the crop box.
Offset Path and Simplify, which don't apply to an image, are gone.
- One Image Trace object selected: Expand keeps only the traced paths,
in place of Ungroup and Isolate.
Retype isn't offered: the app doesn't have it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The family list scrolled with egui's floating scroll bar, which is drawn
over the rows' right ends, where each family's star is: with the pointer
there the bar widened over the star and a click went to the bar. The list
now takes the scroll bar the menus got for the same reason (#2086), slim
and in a column of its own; `widgets::list_scroll_style` holds it for both.
- "Font Style:" in Dimension Tool Options is Teckensnittsstil: Teckenstil
is already Character Style.
- The drawing-scale help reads "1:10 visar 1 enhet på sidan som 10".
- The layered-file fallback message and "Check an artboard to save" read
more naturally.
- "File › Place…" names the menu as the menus do (Fil, not Arkiv).
- The rows are reviewed, so the "machine-drafted; review welcome" note
above them goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second colour scheme next to Modern: the neutral greys and blue accent of
earlier versions, on the same cards, corners and layout. Picked in
Preferences › User Interface › Color Scheme (pref uiColorScheme:
modern|classic); Accent Color is dimmed while Classic is chosen, which has
its own blue.
Layers ≡ and row menus collect art via assets.add; a layer id expands to the art under it (including sublayers) so MCP and the panel match Object › Collect for Export.
The list showed [None], [Registration] and the default swatches' names in
English. Names the app gives (the default swatches and colour groups) now
show in the UI's language where it has a `swatch` row for them; documents
keep the names as they are, and a name the user gave shows as written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effect › Distort › Glass… offered only its four built-in textures. The
reference app's Texture menu also has Load Texture, which takes an
image file as the glass surface.
The Glass dialog now has a Load Texture… button. It reads a PNG, JPEG,
GIF, WebP, TIFF, BMP, PSD or PSB file within the pixel allowance the
renderer draws images in, and the Texture menu gains Loaded Texture.
The effect stores the image's gray levels in `textureImage` as a
grayscale PNG in base64, so the document saves the texture with the
effect, and copies of the effect carry it. effect.apply and
effect.setParams take `textureImage` as {path}, {name, dataBase64} or
the stored string; any other value is an error.
The texture lies one image pixel per point at Scaling 100%, centered on
the object and repeated. Each pixel's luma is the surface's height,
from 0 for black to 1 pt for white, and a transparent pixel counts as
middle gray. A Glass without `textureImage` draws and saves as before.
Docs: docs/mcp.md (Glass textures), docs/effects-parity.md,
docs/gaps.md, docs/target-app-parity.md.
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.
When a graphics driver crashed the process while the window started,
as Intel's Direct3D 12 driver (igd12umd64.dll) did on an Intel UHD
Graphics 605 (#2243), the next start picked the same adapter again. The
restart without a failing adapter and the first-frame watch run inside
the process, so the crash ended them along with it.
Each start now writes graphics-start.json in the log folder before the
graphics start, adds the adapter once one is picked, and clears both
when the first frame reaches the screen or the app ends without
crashing. A start that finds an earlier start still recorded there
leaves that adapter out, as a restart does, logs it and shows a note in
the status bar. When the crash came while the adapters were being
listed (wgpu's Direct3D 12 backend creates a device on each GPU to list
it), the next starts list OpenGL's adapters alone, except on macOS. The
adapters stay left out until the app version or the Graphics Processor
preference changes, a start shows its first frame on one, or no adapter
is left. WGPU_BACKEND and WGPU_ADAPTER_NAME choose regardless of the
record.
Docs: docs/development.md, docs/target-app-parity.md.
Adds a Finnish (Suomi, `fi`) catalog with 4,770 entries, written in
sentence case from the English labels with ordinary Finnish
vector-illustration vocabulary. It covers menus, `tl!` literals and the
668 `@msg` status and error messages: `fi` is in KEEPS_MENU_NAMES and
COMPLETE_MESSAGES. Like the other complete catalogs, it leaves the
product, workspace and perspective preset names in English.
Plurals are one/other with the partitive singular after any number but
1 (1 taso, 2 tasoa). Any fi-* locale selects Finnish; sv-FI does not.
Tests: `cargo test -p vectorcraft-ui-egui --lib i18n` passes (47),
including the new finnish_reads_as_finnish and the bundled-font glyph
check.
Clean-room, AI-assisted (Claude Code) translation; a native speaker
checked it in the running app, but it has not been proofread line by
line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Swedish (Svenska) as an interface language: 4770 strings, written from the English source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Play an action on every file in a folder or every data set, with destination
and Override Open/Save/Export. Actions panel and File › Automate › Batch…
open the dialog; new strings in pt-br.
Co-authored-by: Cursor <cursoragent@cursor.com>
VariableKind::Graph binds a graph root; dataset rows supply CSV as graph.setData.
Make Graph Dynamic, Variables dialog, capture/apply, and pt-br for the new strings.
Co-authored-by: Cursor <cursoragent@cursor.com>
Load and save variable datasets as CSV for data-merge pipelines, and
translate the new Variables panel commands in Brazilian Portuguese.
Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts:
# 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>
assets.export of abutting fills must leave no ghost edge (#983 / #1037); panel thumbnails use the same Art Optimized supersample. Progress docs mark the gap closed.
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>
Make and Edit choose the Intertwine tool: hovering highlights the overlap
area under the pointer, a click swaps which object is on top there, and a
path drawn around overlaps (Shift: a rectangle) arranges every overlap
inside it; Esc or Enter goes back to the tool chosen before. The overlap
faces move to vectorcraft-tools, shared by the tool and the command.
Clicks inside a raised area pick the member drawn on top there, the
Object menu gets the Intertwine submenu, and the new labels and the
predicate message are translated in the complete catalogs. docs/gaps.md
records what landed under G13.