The drag threshold kept `last` at the press position until it was crossed, so
Space held when the move that crossed it came moved the anchor by the whole
distance since the press. `last` now follows the pointer while the threshold
swallows the moves, and the threshold is measured from a separate `pressed`.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A press released within a few screen pixels is a click: the anchor stays a
corner with no handles, as in Illustrator, instead of getting near-zero
handles from a trembling hand. The threshold is in screen pixels, so it
does not change with zoom. It applies to new anchors, closing a path and
pulling a handle out of the last anchor.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The typed [ and ] now reach the Blob Brush, the Eraser and the Symbolism
tools, whose bracket keys are in the public shortcut lists. A size of the
Paintbrush's own (and path.freehand's scale) is left out until its
behaviour is decided: #1661 stays open for that.
The tools took `[` and `]` (the Blob Brush's and the Eraser's size, the
Symbolism tools' diameter and, with Shift, intensity), but the app never
passed the keys on: only `ui.key` over MCP reached them.
- The keyboard handler gives a typed `[`, `]`, `{` or `}` (Shift) to the
active tool when it claims the key. It reads the typed character, so
layouts where a bracket takes Option or AltGr (Option-8 on a Turkish
Mac) work too. A field with the keyboard, the Type tool and Cmd/Ctrl
shortcuts are left as they were.
- The Paintbrush has a size of its own, 10 to start as the Blob Brush's:
`[` and `]` take a point off or add one, and the next stroke is drawn
at size / 10 of the stroke weight (path.freehand `scale`). Public
references describe the keys as changing the size the Paintbrush draws
with, before drawing, the brush definition left as it is; so the
stroke weight new art takes stays, and the selected path keeps its
stroke. Every brush type, as the report and the Tuts+ guide describe;
the step is our own.
Escape still ends editing (an empty new point type is discarded) and the type
stays selected; the Type tool now claims Escape while editing and asks for the
Selection tool, so tool shortcuts work again without the mouse.
Escape while dragging anchors, handles, segments, objects, a type area, a blend
spine or a marquee with the Direct (or Group) Selection tool cancels the drag:
what it moved goes back and the release after it does nothing.
Marquee, anchor clicks, blend spines, and Group Selection only pick the
isolated object and what is inside it. A regression test covers a path
outside an isolated group.
A point placed by the Pen, the shape tools and the other drawing tools, and a
dragged anchor, lands where two paths cross, where a path crosses a ruler guide
and where two ruler guides cross, with the Smart Guides label "intersect".
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.
Rounding a single corner is done by selecting that corner, then
dragging its widget: a drag on a path selected whole rounds every
corner with both tools, as before. What was missing is selecting the
corner on the widget itself. With the Direct Selection tool a plain
click on a corner widget (press and release, no drag) now selects that
corner, as a click on the corner's anchor does; Shift-click toggles it.
The widget is then the only one showing, and the next drag on it rounds
that corner alone.
Unchanged: Alt-click cycles the corner kind, the Selection tool's click
on a widget does nothing, corners picked by their anchors edit together.
Test: corners::tests::a_click_on_a_widget_with_direct_selection_selects_its_corner.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rarely set fields move into one optional boxed NodeExtras, and large NodeKind payloads (images,
live shapes, blends, envelopes) are boxed. Every object, and every undo step that copies it,
shrinks: a 50k-path move keeps ~24.5 MB per step instead of ~40 MB. Files stay byte-identical
(Node keeps its flat JSON through a borrowed Serialize and a from-wire Deserialize, no flatten),
and loading is slightly faster. Undo and redo reuse their step's size estimate instead of
walking the document again (8.3 -> 5.6 ms for a 50k-path move).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
# Conflicts:
# crates/engine/src/cmd/graph.rs
# crates/svg/src/export.rs
With Smart Guides off, the Selection tool grabbed the selection by the
pressed spot, so Snap to Point brought the pointer onto another anchor
while the object's own anchor stayed a few pixels off. A press within
reach of an anchor now grabs the object by that anchor (Smart Guides on
or off, also without a hover event first), and the arrow turns hollow
over such an anchor while Snap to Point is on.
Fixes#1274
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes#1273. A selected group or compound path shows its paths' anchors
with the Pen, but Pen + Alt only looked at paths selected directly, so an
Alt-click there started a new path instead of converting the anchor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clicking an anchor of a path selected whole (or one of several selected
anchors) with the Direct Selection tool left the selection as it was,
so the clicked anchor never became the selected one and Live Corners
and the anchor controls kept acting on every corner. A plain click now
selects that anchor alone, as the public documentation describes (a
click selects the anchor point, Shift-click adds others):
https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/select-anchor-points-in-paths.html
The press keeps the selection so a drag still moves every selected
anchor with the one held; only the release without a drag narrows it.
Shift still toggles, Alt still picks the whole path, and an anchor that
wasn't selected is still selected on the press.
Test: direct::tests::a_click_on_a_selected_anchor_selects_it_alone.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Agents could set and clear the key object but not read it:
document.inspect, the control channel's ui.pointer reply and the
headless pointer_gesture reply left it out, and select.key returned
null. select.key with an id that was not selected, or with a value
that is not an id, such as "x", cleared the key without an error. A
compound-path member or an object inside a selected group could become
the key.
select.key now checks its id as select.set, select.add and
select.toggle do (#784). It returns an error for an id that is not a
selected top-level object and leaves the key as it was. Layers,
compound-path members and objects inside another selected object are
not top-level objects. select.key {id: null} used to clear the key and
is now an error; select.key {} still clears it. select.key returns
{key}, and document.inspect and both pointer replies include the key
object as "key".
In isolation mode a Selection-tool click can pick an object that
select.key does not accept as the key: a sublayer inside an isolated
layer, a member inside an isolated compound path, or an object in an
isolated group that is also selected. The tool no longer sends
select.key {id} for such an object. When the object is already the
key, the tool still sends select.key {}, which clears the key.
A recorded action whose select.key step names an object that is not a
selected top-level object when the action plays now fails and is
rolled back, as an action with a select.set step naming a deleted
object has failed since #784.
Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
Every edit of an object copied its whole appearance (its fills, strokes and effects, about
540 bytes for a basic object) into the new node, though a move leaves it as it is. Over 45
whole-selection moves of 10,000 paths, undo history grew by 589 MB; now 349 MB (41 % less), and
a nudge or a drag of the selection takes 12 ms instead of 15.
- Node::appearance is a vectorcraft_doc::Shared<Appearance>: copy-on-write. A clone shares it,
and the first change through DerefMut copies it (Arc::make_mut). Reads, writes, equality (also
against a plain Appearance) and the saved form are those of the Appearance itself, so call
sites read and change it as before. Sites that build or assign a whole appearance add .into(),
and the few that need a plain copy take (*n.appearance).clone().
- Node::transform changes the appearance only when it has gradients to transform, and pattern
paints are transformed only when there are some: a move no longer copies an appearance it
doesn't change.
The workspace suite (5,301 tests) and clippy pass with the change; the doc and engine suites
pass again after the transform guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
With Smart Guides on, a selection moved by the Selection tool only lined
its bounds up with the other art: grabbed by an anchor and dropped near
another object's anchor, it could stop a fraction of a point beside it
where an edge lined up instead. When the grabbed point comes within the
snapping tolerance of an anchor or a centre, it now lands on it, as
Smart Guides' anchor snapping does.
The Type tool's Left and Right arrows and Delete moved one code point at
a time, so the caret could stop between a Thai consonant and its vowel
or tone marks (or inside an accented letter or emoji sequence), where
typing or deleting splits the cluster. They now step over extended
grapheme clusters. Backspace still takes back one character at a time,
as Thai typists expect for a mistyped tone mark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Anchor Point tool drags any segment of a selected blend's spine into
a curve, straight ones too, and Direct Selection reshapes a curved spine
segment as it does a path's (a straight one it leaves, as on paths). Both
run the new `object.blend.spine.reshapeSegment`, which bends the segment
with the same math as `path.reshapeSegment`, now shared as
`SubPath::bend_segment`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A corner is now any anchor that isn't smooth where two sides meet at an
angle, straight or curved, as in Illustrator. The cut meets each side at
its setback measured along the curve, leaves both sides along their
tangents there, and each curved side is trimmed to what the cuts at its
ends leave of it. The largest radius is still the one taking the cut
halfway along the shorter side, measured along the curve. Corners
between straight sides draw exactly as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Artboard tool and the Artboards panel only ever had one artboard
selected, so Shift-clicking another didn't add it. As in Illustrator:
- Artboard tool: Shift-click adds an artboard to the selection (it
becomes active) or takes one out; a plain click on a selected one keeps
the selection, so a drag moves them all (Alt copies them all, the
copies becoming the selection), and Delete removes them all, one
artboard always staying. The other selected artboards show dashed
bounds; `selected` is a tool option.
- Artboards panel: with the Artboard tool, Shift- or Ctrl/Cmd-click on a
row adds or removes it, every selected row is highlighted, and Delete
Artboard removes them all.
- `artboard.move` and `artboard.delete` take `indices` besides `index`:
several artboards in one undo step, each with its art (an object moved
once where artboards overlap).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A star's inner radius was always half its outer one while drawing, so
its ratio couldn't be changed. As Illustrator documents for the Star
tool: Ctrl/Cmd during the drag keeps the inner radius where it is while
the outer one follows the pointer (the new ratio stays once the key is
let go, for the rest of the drag and the next stars), and Alt keeps the
star's shoulders straight (each point's sides in line with the next
points', a ratio of cos(2π/n)/cos(π/n); five points or more).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The snap targets of a copy were gathered when Alt went down, from the document
as previewed, where the original had already moved with the drag, so the
original was never a target where it stays. Now both target sets (for a move,
and for a copy, which includes the original) are gathered when the drag
begins, and Alt picks between them each frame: for Direct Selection drags by
an anchor and for whole-object moves (the Selection tool's, and Direct
Selection's from a segment or fill).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Pressing an anchor of paths that are all selected whole now moves them as
objects held by that anchor, so Alt pressed or released during the drag turns
the copy on and off (the original shows or not), as with the Selection tool.
The anchor keeps snapping all along, onto the original only while a copy
leaves it behind. With only some anchors selected the drag still moves just
those.
A committed move drag is now named for what it did in the end (Copy or Move),
for the Selection tool too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Alt-dragging a path by an anchor moved the copy with whole-object snapping
(bounds only, from the press point), so the anchor couldn't land exactly on
another anchor. Now the pressed anchor (its own coordinates, not the pointer's)
snaps as a dragged anchor does: onto other paths' anchors and segments and
Smart Guides, and onto the original the copy leaves behind. The history says
Copy for an Alt-drag.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Alt-pressing a segment already picked the whole path and Alt-drag copied it,
but a press on an anchor ignored Alt and only moved the selected anchors. Now
Alt on an anchor picks the whole path too, so Alt-drag duplicates it as the
Selection tool's does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A path with all its anchors selected is stored whole (no partial entry), so
pressing one of its anchors looked unselected and replaced the selection with
that anchor alone; the drag then moved just it. Now an anchor of a path
selected whole counts as selected: the press keeps the selection and the drag
moves every anchor. Shift-press still deselects that one anchor.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A ruler guide goes on the current layer (guide.add {layer}), shows,
hides, locks and is deleted with its layer, is listed in the Layers panel
as a row that selects it, and moves to another layer (guide.setLayer).
Guides on a hidden layer neither show nor snap; those on a locked layer
can't be picked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
Beside every corner of the bounding box the curved rotate cursor bowed
up. It now bows from the box's centre toward the pointer, in 45° steps
(Cursor::RotateToward, made by Cursor::rotate_about): at the top right
it opens toward the bottom left, at the bottom right toward the top left.
The Selection tool shows it over the rotate zone and while rotating (it
follows the pointer round), and Free Transform does the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cursor::precise listed every Pen state but PenJoin (added later, #794), so with Use Precise
Cursors on the Pen still showed its join badge over another open path's end.
PenJoin is a crosshair too under the preference.
The Pen join test now also checks the pointer over the other path's end is the crosshair with
the preference on (and PenJoin with it off, as before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md