Commit Graph
49 Commits
Author SHA1 Message Date
Michael GiesonandClaude Opus 5.5 2d0da1472f Cursors: 36 more tool states get cursors of their own, listed in cursors.toml
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>
2026-10-17 12:26:12 +07:00
dotCooCoo 3b683bbeec Ellipse: dragging a pie widget on the canvas sets its pie angle
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.
2026-10-16 15:27:53 +07:00
dotCooCoo 5527418234 Selection: pressing E while dragging a marquee selects only the objects inside it
A Selection tool marquee selected every object it touched, a Group
Selection marquee selected the anchors inside it, and E pressed during
the drag did nothing. Now E switches either marquee to selecting only
the objects that lie entirely inside it, measured by their geometric
bounds, and E pressed again switches it back. Each marquee starts in
the default mode.

- The Selection tool selects a group only when the whole group lies
  inside. Group Selection selects the objects inside groups, each as a
  whole object. Shift still toggles. Direct Selection does not take E.
- The ruler guides the marquee selects with the art are limited the same
  way: an artboard guide is selected when its whole length lies inside,
  and a canvas guide is not selected.
- Tools receive letter keys as ToolKey::Letter. While a tool is busy, the
  desktop app sends it each press of a letter it claims, without Cmd,
  Ctrl or Alt and without key repeats, and headless press_key sends a
  claimed letter the same way. With nothing dragged, press_key {key: "e"}
  still chooses the tool bound to E.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 15:27:53 +07:00
dotCooCoo 21b4e70150 Shaper: an arrow widget switches a Shaper Group between Face Selection and Construction mode
A Shaper Group had no arrow widget for switching between Face Selection
and Construction mode.

With the Shaper or Selection tool active, a selected Shaper Group, or a
group with one of its faces selected, now shows an arrow beside the
middle of the right side of its source shapes' bounds. A click on it
isolates the source shapes (object.isolate). A second click leaves
isolation with the group selected (object.exitIsolation). The arrow
points right in Face Selection mode and left in Construction mode.

In Construction mode a Shaper click selects an original shape of the
isolated group. Before, it selected the whole group outside the
isolation, and shaper.select with source could pick an original of
another Shaper Group lying above it.

Docs: docs/shaper.md, docs/target-app-parity.md.
2026-10-16 15:00:07 +07:00
sanyamsingh001andCodex 74573d6bad G1: Cancel existing-guide drags with Escape
Co-authored-by: Codex <noreply@openai.com>
2026-10-16 04:44:59 +07:00
bflatastic 2843687ef4 Text: leave out the bottom-handle Auto Size double-click until its behaviour is settled (#1947) 2026-10-16 04:44:31 +07:00
Marco Arnulfo a69371656b Text: Ctrl+Shift+C aligns centre off the Mac, a double-click on area type's bottom handle turns on Auto Size (fixes #1940) 2026-10-16 04:44:29 +07:00
dotCooCoo a14abc3177 Control bar: buttons isolate the selected object and exit isolation mode
The Control bar had no isolation controls. In isolation mode it now
shows an Exit Isolation Mode button (`object.exitIsolation`), except
while a pattern is edited: the isolation bar's Done and Cancel end
that edit. Outside isolation mode, with one group, compound path,
path, image, mesh or placed file selected, it shows an Isolate
Selected Object button (`object.isolate`). After a Direct Selection
click on a path inside a group, the button isolates that path. An
envelope's contents get no button: for them the Control bar shows the
envelope's Edit Envelope and Edit Contents buttons.

The button offers the kinds of object that the Selection tool's
double-click isolates. The double-click's check moves unchanged into
`vectorcraft_tools::select::isolates`, and both use it.

Docs: docs/target-app-parity.md.
2026-10-16 02:25:49 +07:00
bflatastic ed96b2ff65 Marquee guide selection: one shared helper for both selection tools 2026-10-14 08:01:50 +07:00
Murat Vural 8796aa9d85 feat: marquee selection selects unlocked ruler guides 2026-10-14 08:01:50 +07:00
bflatastic 50911d8608 Fix #1534: a dragged selection snaps to ruler guides with Smart Guides on too 2026-10-13 08:51:43 +07:00
Vikram Bhaskaran bf2b5643dc Hide the selection bounds while a corner-radius drag is underway.
The sharp outer box stayed on screen because a corner drag was not treated as a transform.
2026-10-12 19:50:12 +07:00
Ayberk A. 3073f0ab03 Isolation mode: double-clicking a single object isolates it
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.
2026-10-12 18:56:41 +07:00
arhammusheer 5beac4f122 #1244: smaller objects: Node 352 -> 152 bytes, NodeKind 200 -> 80, and a cheaper undo estimate
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
2026-10-12 11:02:32 +07:00
Fabio LussiandClaude Opus 5.5 8b839dd50f Snap to Point: drag a selection by the anchor pressed near
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>
2026-10-12 07:49:47 +07:00
bflatastic 4ab69b877e Tests and Czech: select.key's new error is translated in cs, and the select tool test sets a shared appearance 2026-10-12 00:34:57 +07:00
dotCooCoo 845aa6848f MCP: select.key checks its id, and agents can read the key object
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.
2026-10-12 00:16:23 +07:00
bflatastic 30ddaa78ff #955: a move grabbed by an anchor lands it on another anchor
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.
2026-10-11 22:08:30 +07:00
Michael GiesonandClaude Opus 5.5 a75b465419 Selection tools: Alt-drag copies snap to the original they leave behind
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>
2026-10-11 12:24:46 +07:00
mateususaandClaude Opus 5.5 0b0ccfb35a #1026: the rotate cursor wraps the corner it is at
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>
2026-10-11 05:30:24 +07:00
bflatasticandClaude Opus 5.5 fdb7d7a183 Part of #812: Smart Guides label an object's centre and anchors on hover
With Smart Guides on, hovering the Selection tool near an object's centre
or one of its anchors now labels it ("center", "anchor"), as
Illustrator does, and a press there grabs the object by that point, so
the point follows the pointer and snaps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:28:38 +07:00
bflatasticandClaude Opus 5.5 7dd69aaf81 Fix #740: moved art snaps to the grid
With View › Snap to Grid on, a selection moved with the Selection, Group
Selection or Direct Selection tool lands on the grid: on each axis,
whichever of its two edges and its centre is nearest a gridline (every
subdivision, 9 pt by default) moves onto it. Snap to Grid takes over from
Smart Guides and Snap to Point while it is on, as it does for drawn points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:36:42 +07:00
bflatasticandClaude Opus 5.5 f8a7695c3a Fix #712: the bounding box hides while a drag moves, scales or rotates
During a Selection-tool drag the bounding box and its handles stayed on
screen while the art moved, so the content looked as if it slid inside
a box of its own. The box now hides once a drag moves, scales or
rotates the selection, as in Illustrator, and comes back on release. A
press that hasn't moved, and a marquee, keep it. Tools gain
`transforming()` (Session::tool_transforming) for this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:39:12 +07:00
bflatasticandClaude Opus 5.5 1575b14294 Part of #394: Smart Guides › Spacing Guides space a moved selection evenly
A selection moved with the Selection or Direct Selection tool that lines
up with nothing along an axis lands as far from its neighbour as two
other objects of its row (or column) are apart, or halfway between its
two neighbours, within the snapping tolerance; the equal gaps are marked
with a line and end ticks in the Smart Guides colour. The objects are
taken as wholes on their layers (groups too), and inside a group holding
the selection, its other objects. Off, moves only line up as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:22:22 +07:00
David Whiting 7e82dbc05b M2.9: add editable Shaper groups and shape orientation steps 2026-10-08 22:52:23 +01:00
bflatasticandClaude Opus 5.5 f31d6874aa Fix #541: a click on a selected object makes it the key object
With the Selection tool, a click (no drag, no modifier) on one object of a
selection of several makes it the key object (select.key); a click on the
key again lets it go. The key object is drawn with a thicker outline,
Align aligns to it (the Align panel shows Align to Key Object while there
is a key, and choosing Selection or Artboard lets it go), and
object.align without `to` aligns to the key when there is one, so the
Control bar's and the Properties panel's Align buttons do too. With a
spacing, Distribute Spacing keeps the key object still and spaces the
others from it, and it reports too few objects instead of indexing an
empty list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:08:56 +07:00
bflatasticandClaude Opus 5.5 1d14f337db Fix #511: Live Corners on any path's corners (stars, polygons, pen paths)
Corner widgets only ever showed on live rectangles: CornerWidgets::of bailed
out unless the selection's live shape was LiveShape::Rectangle, and
object.setLiveShape only edited rectangle radii. A star is a plain path, so it
had no widgets with either selection tool.

- geom::corners: the corners of any path (anchors without handles between two
  straight sides) and the path with them cut round, inverted round or
  chamfered, each corner a circle tangent to both sides, its radius no larger
  than takes the cut halfway along its shorter side. Rectangles now cut their
  corners with it (bit-identical anchors and layout).
- doc::corners::LiveCorners/set_corners: one model for a live rectangle's,
  a live polygon's (new radii/kinds) and any other path's corners (new
  LiveShape::Path keeping the uncut outline, plain again once nothing is cut);
  radii scale with transforms (Scale Corners) and stay circular.
- object.setLiveShape takes anchor indices for `corners` on any path; polygons
  keep a shared radius as their sides change.
- Widgets: Direct Selection on any path, Selection on live rectangles and
  polygons; placed on each corner's bisector, the drag follows it, the red
  limit outline marks corners at their own limit; the angle preference and
  View > Hide Corner Widget apply. Corners dialog and the Properties and
  Transform panels' Corner Radius (now on polygons too) work on them.
- Tests for the star, the polygon and a pen path in geom, doc, tools, engine
  and ui-egui; docs and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:05:41 +07:00
bflatasticandClaude Opus 5.5 377f236687 Fix #483: Shift-drag marquees toggle the selection; the Lasso's Alt keeps the rest
A Shift-drag marquee only ever added to the selection, so there was no way to
take some of the selected objects out with it. As in Illustrator, Shift-drag
now toggles what the marquee reaches: the selected ones are deselected, the
others selected, the rest of the selection stays.

- Selection tool: the Shift marquee runs select.toggle, which now also takes
  {ids} (each one toggles).
- Direct Selection and Group Selection: the Shift marquee toggles the anchors
  inside it (select.anchorsMany mode "toggle"); a path left with none leaves
  the selection, one with all of them is selected whole again.
- select.anchorsMany takes mode "set"|"add"|"toggle"|"subtract" (add: true is
  still mode add). Adding anchors of a path selected whole keeps it whole, and
  anchor references outside the path are ignored.
- Lasso: Shift adds and Alt subtracts through the same command, so an Alt lasso
  no longer drops selected type, groups and other non-path objects.
- Tests at the tool, engine (Session::pointer) and MCP (pointer_gesture) levels,
  command sweep cases for the new modes; docs/mcp.md and ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:21:36 +07:00
bflatasticandClaude Opus 5.5 940285dd9e Type: the point/area type widget converts between them; area type made by dragging resizes
Discord feedback: type in a frame dragged with the Type tool could not be resized or switched
between area and point type the way the reference app allows.

- The Selection tool shows the type widget beside a single selected point or area type object:
  a small circle outside the middle of its bounding box's right side, hollow on point type and
  filled on area type (vectorcraft_tools::typewidget, drawn by the canvas from the same geometry).
  Hovering it shows the arrow with a type badge; its clicks neither drag nor deselect, and a
  double-click runs type.convertToAreaType / type.convertToPointType (the Type menu's commands),
  keeping the text and its styles in place, one undo step, reachable over MCP.
- Resizing area type made by dragging already works on main (#252): the bounding box resizes the
  type area and the text reflows. A regression test now covers the reported flow.
- docs/mcp.md and ROADMAP.md describe the widget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:54:12 +07:00
wstockandClaude Fable 5.1 14d7b3ddce Part of #394: Smart Guides display options and Snapping Tolerance
Color, Alignment Guides, Anchor/Path Labels, Measurement Labels,
Transform Tools, Object Highlighting and Snapping Tolerance are read:

- tools: `guides::GuideStyle` on `Targets` (`styled(cx)`) colours the
  lines and labels and leaves them out when their option is off, the
  snap itself unchanged; `ToolContext` gains the six fields and
  `snap_tol()`, replacing the 5 px literal for every smart guide snap
  (drawn points, moves, bounding-box handles, ruler guides, artboards,
  reference points). The tools gate their `Overlay::Measure` readouts on
  Measurement Labels (drawing, moving, ruler guides, artboards) or
  Transform Tools (scaling, rotating, Free Transform, transform tools).
- engine: `with_tool_cx` fills them from `Prefs` (the colour parsed, magenta
  as fallback); the colour's default is `#ff3dfc`, what the tools drew.
- ui-egui: the hover outline needs Object Highlighting and Smart Guides.
- Tests in tools (guides, select), engine (tests_prefs) and the canvas;
  docs/mcp.md and ROADMAP.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 15:46:15 +07:00
bflatasticandClaude Opus 5.5 5d65699043 Part of #451: guides dragged out of the rulers snap, artboard guides
Guides dragged out of a ruler did not snap at all (only Shift to the ruler's
ticks): they now go through the engine (`Session::ruler_guide`, a
`NewGuide` previewing `guide.add`, one undo step) and snap as a moved guide
does: with Smart Guides to the edges, centres (so the sides' midpoints) and
anchors of the art and of the artboards, labelled; with Smart Guides off,
View > Snap to Point pulls new and moved guides onto anchors. Moving art
with the Selection tool already snapped flush to artboard edges and centres
with Smart Guides on; a test now covers it.

Artboard guides: `Guide.artboard` holds an artboard id (written only when
set, so canvas guides save as before and older files open unchanged).
`guide.add {artboard}` makes one, `guide.list` reports it; a guide dragged
out of a ruler with the Artboard tool belongs to the active artboard. It is
drawn, picked and snapped to across its artboard only, moves with it
(`artboard.move`, a pure move in `artboard.setProps`, `artboard.rearrange`),
is copied with it (`artboard.duplicate`, Alt-drag copy) and deleted with it
(`artboard.delete`, the guide selection kept in step).

Guides as Layers rows with their own lock and hide (part d) stay open: ruler
guides are not layer nodes; Lock Guides locks them all, and Make Guides
paths are Layers rows already. Listed in the ROADMAP parity row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:23:18 +07:00
bflatasticandClaude Opus 5.5 1706e84829 Fix #429: Type on a path brackets, flip and Type on a Path Options that keep the type in place
Type on a path could only be repositioned by Type on a Path Options, whose menu item sent
`{start: 0}`, so OK threw the text to the start of its path; nothing showed or dragged its
brackets. Now, as in Illustrator:

- Type on a path flows between a start and an end bracket (`TextKind::OnPath { end }`, None:
  the end of the path or once round a closed one; `TextKind::path_span`). Flipping
  (`TextObject::flip_on_path`) reverses the path keeping a closed path's first anchor and swaps
  the brackets, so the type keeps its stretch of the path.
- Type on a Path Options gains Align to Path (ascender, descender, center, baseline) and
  Spacing (glyphs spaced as if set that far above the path: closed up round the outside of
  curves, opened up round the inside), laid out by the text engine.
- `type.pathOptions {start?, end?, flip?, effect?, alignToPath?, spacing?, ids?}` sets them in
  one undo step; with none of them it queries. The menu item opens the dialog with the selected
  type's Effect, Flip, Align to Path and Spacing (dropdowns for the choices) and leaves the
  brackets alone, so OK as opened changes nothing.
- The Selection and Direct Selection tools show selected type on a path's path and its start,
  centre and end brackets (`tools::pathtype`, with a bracket cursor): dragging the start or end
  bracket sets where the type runs, dragging the centre bracket slides it along the path and,
  across the path, flips it (Cmd/Ctrl held: it only slides). Each drag is one undo step.
- Arc-length measuring moved to `vectorcraft_geom::ArcPath` (layout, the tools and
  `text.createInPath` share it). SVG export outlines path type whose end bracket, alignment or
  spacing `<textPath>` can't express.
- docs/mcp.md documents the command and the drags; ROADMAP updated; dialog strings translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:50:25 +07:00
bflatasticandClaude Opus 5.5 d1536bc355 Direct Selection: dragged anchors snap to points and smart guides
Dragging direct-selected anchors never snapped: the anchor drag sent the raw
pointer delta. Now the anchor pressed on (or the pointer on a straight segment)
snaps and the other selected anchors follow by the same delta:

- Smart Guides: onto other anchors (including the anchors of its own path that
  stay put), onto path segments ("path", new segment targets gathered only for
  anchor drags, lines exactly on straight segments), else into line with
  anchors, edges, centres and artboards ("align"). The anchors that move, their
  segments and the reshaped paths' bounds are no targets, so an anchor can't
  snap to itself. Targets are gathered once when the drag begins, capped.
- Smart Guides off: Snap to Point pulls it onto anchors and ruler guides within
  its distance; Snap to Grid / Snap to Pixel apply as for drawn points.
- Shift keeps the 45-degree constraint unsnapped.

Group Selection and Direct Selection's whole-object moves now snap like the
Selection tool: its move snapping moved into a shared `MoveSnap`, and
`snap_with` into a reusable `PointSnap` that caches its targets per drag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:25:07 +07:00
bflatasticandClaude Opus 5.5 8e3dbd5369 Part of #394: Preferences for snapping, handles, rubber bands, pattern tiles, tints and type
Fourteen more Preferences options that were stored but read by nothing now
act, for the mouse and for agents (prefs.set, then pointer_gesture,
press_key or the commands):

- Selection & Anchor Display: Snap to Point and its distance (with Smart
  Guides off, View > Snap to Point lands a dragged selection's grabbed
  point, drawn points and reference points on anchors and ruler guides);
  Move Locked and Hidden Artwork with Artboard (artboard.move, the Artboard
  tool and Rearrange; off, hidden art now stays too); Enable Rubber Band for
  the Pen and Curvature tools; Handles (solid, hollow, large);
  Show handles when multiple anchors are selected (canvas and Direct
  Selection's handle picking); Hide Corner Widget for angles greater than.
- General: Transform Pattern Tiles is the default of a new `patterns`
  param on object.transform/move/rotate/scale/reflect/shear/transformEach
  (journalled when the art uses a pattern), and the transform dialogs get a
  Transform Patterns checkbox; Select Same Tint % (off, Select > Same
  Fill/Stroke Color takes every tint of a global or spot swatch).
- Type: new type.step {attribute, by} steps size, leading, tracking, kerning
  and baseline shift by the Size/Leading, Tracking and Baseline Shift
  increments; Increase/Decrease Font Size (Cmd+Shift+. and Cmd+Shift+,);
  the Type tool's Alt+arrows (kerning at a caret or tracking, leading,
  Shift for baseline shift, Cmd/Ctrl for five steps). Enable Missing Glyph
  Protection keeps characters a new font lacks in the font that had them
  (text.setStyle, text.setRangeStyle). Fill New Type Objects With
  Placeholder Text: type the Type tools place starts with placeholder text,
  selected (text.create / text.createInPath take `placeholder`).

Units > General, Stroke and Type were already the single source of truth
(units.rs). Auto Size New Area Type, Show Font Names in English and
Highlight Alternate Glyphs need subsystems the app doesn't have yet and
stay open with the rest of #394.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 09:43:12 +07:00
bflatasticandClaude Opus 5.5 2f710d3acc Fix #414: select, move and delete ruler guides with the selection tools
Guides dragged out of the rulers could not be touched again: no tool hit-tested
them, so a click on a guide went to the art under it.

- The Selection, Direct Selection and Group Selection tools pick a ruler guide
  within the selection tolerance (over the art; Direct Selection takes an
  anchor on it first) through a shared tools::rulerguide::GuideEdit. A drag
  moves the selected guides in one undo step (Alt copies them, Shift snaps the
  dragged one to the ruler's ticks, otherwise it snaps to pixels, the grid or,
  with Smart Guides, the art's edges, centres and anchors); dropped off the
  canvas onto its ruler, the guide is deleted. Hidden or locked guides can't be
  picked; the pointer shows which way a guide moves.
- Selection::guides holds the selected guides (on their own, like slices); the
  canvas draws them in the selection colour. Delete/Backspace (edit.clear)
  delete them, the arrow keys (object.nudge) nudge them, Lock Guides and Hide
  Guides deselect them.
- Commands for agents: guide.list, guide.select {indexes, toggle?},
  guide.move {dx?, dy?, copy?} (besides {index, pos}) and guide.remove {index?};
  guide positions out of range are refused. Documented in docs/mcp.md.
- Shift while dragging a new guide out of a ruler snaps it to the ticks too
  (Unit::ruler_step, shared with the rulers' drawing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:55:51 +07:00
bflatasticandClaude Opus 5.5 47bed05ff4 Part of #394: Preferences that act on selection, the wheel, tool tips and the grid
Fifteen Preferences options were stored but read by nothing. They now act,
through the existing plumbing (ToolContext for the tools, the prefs read by the
canvas for the view), and reach agents through prefs.set:

- General: Show Tool Tips (off: no widget's tool tip comes due), Double Click
  To Isolate, Zoom with Mouse Wheel (the wheel zooms about the pointer,
  Shift-wheel scrolls up and down, Cmd/Ctrl-wheel sideways), Use Precise
  Cursors (Pen, Eyedropper, Slice and Blend pointers become a crosshair).
- Selection & Anchor Display: Tolerance (objects, anchors and handles, in
  place of the fixed 3 and 4 px), Object Selection by Path Only (compound
  paths too), Command Click to Select Objects Behind (cycles down the objects
  under the pointer; not when Cmd only borrows the selection tool), Zoom to
  Selection (Zoom In/Out centre the selection), anchor Size, Highlight anchors
  on mouse over (Direct Selection).
- Type: Number of Recent Fonts (up to 15 Recent Fonts slots).
- Guides & Grid: guide and grid Color and Style (lines or dots), Grids In Back.

The canvas's context menu, panel drops and hover highlight pick objects like
the selection tools. The control channel gets ui.wheel (with modifiers held
like ui.click) so agents can turn the wheel. The rest of #394 stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:22:24 +07:00
bflatasticandClaude Opus 5.5 b05c14f8eb Fix #381: round single corners with Direct Selection (per-corner Live Corners)
A live rectangle stored four corner radii but drew them all with the largest,
and the corner widgets always rounded all four corners, so one corner could not
be rounded on its own.

- geom: rectangle_with_corners draws each corner with its own radius and kind
  (Round, Inverted Round, Chamfer); a sharp corner is one anchor, a cut one two.
  Uniform corners keep the exact rounded-rectangle anchors.
- doc: LiveShape::Rectangle gains `kinds` (saved only when a corner isn't round;
  older files read as round), plus helpers mapping anchors to corners.
- engine: object.setLiveShape takes `corners` and `kind`; without `corners` it
  edits the Direct-Selected corners, else all four, and keeps the selected
  corners selected as their anchors change.
- tools: with Direct Selection, only the selected corners show widgets and a
  drag rounds just those; Alt-click cycles the corner kind; double-click opens
  the new Corners dialog (also `ui.corners`).
- ui: Properties/Transform radius fields show and set the selected corners;
  CSS export keeps per-corner radii and skips cut-in corners.
- docs: mcp.md (Live Corners), control-protocol.md (Corners dialog), ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:56:32 +07:00
Fabian GrajalesandClaude Opus 5.5 3bc4365ee2 Smart Guides: a turned bounding box snaps while it is resized
The resize snap skipped a bounding box standing at an angle. It now works
on the page whatever the angle: a corner handle takes the target's x, y or
both, and a side handle (or a proportional corner) slides along its own
direction until the handle or an end of its side is on a target. A target
the side runs almost along is skipped, so the handle stays near the pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013j16ENkbsNYyHSCDhxYkYR
2026-10-07 14:32:11 -05:00
Fabian GrajalesandClaude Opus 5.5 7de04544e4 Smart Guides: bounding-box resizes snap to the other objects
Dragging a bounding-box handle with the Selection or Free Transform tool
now lands the edges it moves on the edges and centres of the other objects
and artboards, with the guide line, as a move already did. A proportional
scale snaps the closer axis only; a turned bounding box doesn't snap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013j16ENkbsNYyHSCDhxYkYR
2026-10-07 13:05:37 -05:00
bflatasticandClaude Opus 5.5 d6eb526186 Fix #252: area type resizes its frame with the mouse
Dragging a bounding-box handle of area type with the Selection tool now
resizes the type area: the text reflows at its own size (threads too)
instead of being scaled, and History says "Resize Type Area". With other
objects selected they scale as before while the area type resizes its
frame. Direct Selection picks the frame's corners and edges and reshapes
the area ("Reshape Type Area", new command text.reshapeArea), and the
selected frame's anchors and outline show. Area Type Options gains Width
and Height. Point type, rotation, Object > Transform > Scale, the Scale
tool and the Transform panel still transform the type.

Root cause: every handle drag previewed object.transform, which folds the
matrix into the text's xf (TextObject::transform), so area type scaled its
glyphs; and Direct Selection's anchor hit test only looked at paths, so the
frame's corners could not be picked. object.transform now takes
typeAreas, which reshapes the frame in text space (xf^-1 * m * xf), and
the tools use it and text.reshapeArea.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:02:55 +07:00
theironchefandClaude Opus 5.5 80eb5844c7 Selection tool: Shift+drag on a selected object moves it, constrained
Pressing with Shift on an object that was already selected toggled it
out of the selection at once and went idle, so the drag that followed
moved nothing. The toggle now waits for the release: a Shift-click
still takes the object out of the selection, while a Shift-drag moves
the selection constrained, as it already did for unselected objects
and when Shift was pressed mid-drag.

Fixes #224.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9TrG4w49oSRYbP3cqxA3D
2026-10-06 12:16:02 -07:00
bflatasticandClaude Opus 5.5 96604acffb Fix #39: rotated objects keep their angle and a rotated bounding box
Rotating an object baked the rotation into its geometry and kept nothing
else, so the bounding box, the handles and the Transform/Properties
panels fell back to the page-aligned box (the Rotate field went back to 0).

Root cause: nodes carried no record of their own axes. Each node now
keeps `bbox_angle` (append-only, written only when non-zero), which every
transform turns with the object (mirroring transforms keep the nearer of
the two possible boxes). The geometry is still baked. The selection's box
(`Document::oriented_bounds`) stands at the angle the selected objects
share. The Selection tool hit-tests and scales its handles in that frame,
and the canvas draws it rotated. `object.setBounds` measures W/H and the
reference point along it. `object.rotate {absolute}` turns the selection
to an angle, which the panels' Rotate field now shows and sets. Object >
Transform > Reset Bounding Box (previously a no-op) clears the angle in
one undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:49:36 +07:00
bflatasticandClaude Opus 5.5 3b99df3b9a M14.5: Preferences > Units drive every length the UI shows and reads
The General unit is the active document's units: Preferences > Units >
General sets them (one undo step) and is the units new documents start
in (and the unit shown with no document open); Document Setup changes
only the document. The Transform panel, Properties, the Control bar, the
Info panel, rulers, the status bar, Align spacing, Tabs, paragraph
indents, Pattern Options, dialog distances (Move, shape sizes, path
dialogs, Artboard Options, effect distances, Transform Each, parameter
dialogs), New Document and the canvas measurement labels (smart-guide
sizes, moves, the Measure tool, the Live Corners radius) all follow it,
live. Units > Type drives font size, leading and baseline shift; Units >
Stroke also reaches the Appearance panel and the Magic Wand weight
tolerance. Typed units ("5 mm", "12 pt") work in any field; all ten
units are choices; length preferences show in their unit and accept a
unit; offset/simplify/grid/type-size params read typed units. Typed text
starting with a multi-byte character no longer panics the parser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 07:43:41 +07:00
bflatasticandClaude Opus 5.5 65f0f34cd3 M3.67: Use Preview Bounds measures strokes in transforms, the bounding box and Align
With Use Preview Bounds on, the Transform panel, Properties and Control bar
X/Y/W/H, the Selection tool's bounding box, Align, Distribute and
Distribute Spacing measure visual bounds (the whole stroke). setBounds sets
the visual box, scaling the path so that kept or scaled strokes land on
the typed size. object.align/distribute/distributeSpacing take
bounds?: "preview"|"geometric"; the Align flyout item shows and toggles the
preference through prefs.set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 03:59:37 +07:00
bflatastic 8276d0a303 M2.10: Live Corners — drag a live rectangle's corner widget (Selection or Direct Selection) to round or sharpen all four corners as one undo step (object.setLiveShape preview, radius readout, follows rotated/scaled shapes, square to fully round); widgets drawn from the same geometry the tools hit-test, hidden on tiny shapes and with View → Hide Corner Widget; corner-radius cursor 2026-10-02 22:42:49 +07:00
Brandon ThomasandClaude Opus 5.5 8e735f1de9 M11.3: View → Snap to Pixel (drawing points and moved selections land on whole pixels)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:16:36 -04:00
Brandon ThomasandClaude Opus 5.5 4878f1c5dd Rename DrawCraft → VectorCraft: crates (vectorcraft-*), apps, binaries, types, env vars, docs, examples (.vectorcraft); old .drawcraft files and preferences still load. Fix: text bounds exact after opening a file; selection no longer marks a document modified (dirty = content differs from the saved document). Screenshots and renders regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:30:09 -04:00
Brandon ThomasandClaude Opus 5.5 874c0e6ca2 M2.8/M6.2/M7.2: smart guides + snapping, Type tool, Pathfinder/path/type commands, web build
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:28:59 -04:00
Brandon ThomasandClaude Opus 5.5 bfee80a22c M0: workspace skeleton — geom, color, doc, render, text, pathops, svg, tools, engine
Engine-first Cargo workspace for DrawCraft (clean-room Illustrator):
- geom: anchor-based path model, shapes, hit testing, snapping
- color: colours, paints, gradients, swatches, blend modes, harmonies
- doc: persistent Arc document tree, appearance stacks, text model, selection
- render: vello_cpu renderer (appearance, gradients, clip groups, blend modes)
- text: font DB, harfrust shaping, point/area/path layout
- pathops: curve booleans (linesweeper), Pathfinder, offset, simplify
- svg: import (usvg) and export
- tools: selection/direct/pen/shape tools emitting commands
- engine: Session, history with interactions, ~110 commands

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 05:53:29 -04:00