Commit Graph
3401 Commits
Author SHA1 Message Date
dotCooCoo f4496d61aa Paths: Add Anchor Point keeps the same anchors and segments selected
path.insertAnchor (the Add Anchor Point tool, the Pen's Auto Add, the
Smart Pen's Alt-click on a segment and Alt-click with the Scissors)
added the anchor without changing the selection, so the direct-selected
anchors and selected segments after it on the subpath kept their old
numbers and the selection shifted onto the next points along. It now
renumbers them with renumber_after_insert, the helper path.reshape
uses. A selected segment that the new anchor splits stays selected as
its two halves, and the new anchor is direct-selected with them. Undo
restores the path and the selection in one step.

Docs: params text, docs/mcp.md, docs/target-app-parity.md.
2026-11-10 22:17:08 -08:00
BFlat 0695db3ec0 Merge pull request #6519 from dotCooCoo/feat/reshape-focal-points
Reshape: a click sets a focal point and a drag moves every focal point
2026-11-11 12:53:37 +07:00
BFlat b34c652a60 Merge pull request #6514 from ruchirguitar/fix/5820-general-units-for-unitless-files
#5820: files that record no units open in Preferences › Units › General
2026-11-11 12:35:44 +07:00
BFlat 22ec820569 Merge pull request #6513 from dotCooCoo/fix/break-link-keeps-hidden-locked
Symbols: Break Link keeps a hidden or locked instance's art hidden or locked
2026-11-11 12:00:47 +07:00
BFlat bc814012d9 Merge pull request #6511 from dotCooCoo/feat/artboard-color-labels
Artboards: an artboard's guide label colors its own guides
2026-11-11 11:27:16 +07:00
ruchirguitar f5daa27a2f #5820: files that record no units open in Preferences › Units › General
A PDF, an EPS or .ai without ruler units, a raster image, a metafile, an
Affinity file or an importer plug-in's document opened (and reverted) in
points whatever the General unit was. They now open in the General unit,
as the reference app opens files that record no unit. Native, SVG and DXF
files and a .ai or EPS with %AI5_RulerUnits keep their own units. Only the
displayed units change, not the art.
2026-11-11 09:56:19 +05:30
dotCooCoo bd39674469 Reshape: a click sets a focal point and a drag moves every focal point
A click with the Reshape tool on an anchor of a selected path makes it
the focal point, and a click on a segment adds an anchor there that
becomes the focal point. Shift-click adds an anchor or a point on a
segment to the focal points, or removes an anchor that is one already.
Each focal point shows a filled anchor square in a larger hollow one.
A drag from a focal point moves all of them; a drag from another point
moves that point alone, and Shift only keeps the drag's angle.

path.reshape takes the other focal points as focal: [[subpath,
anchor]...]. Anchors that are not direct-selected stay in place, and so
do the ends of an open path unless they are focal points. A selected
anchor between two focal points moves by the whole drag, and one
between a focal point and an anchor that stays takes u² (3 - 2u) of
the drag, as before. A closed subpath with every anchor selected moves
whole: a Reshape drag on a fully selected closed path, and path.reshape
without radius on a closed path selected whole or not selected, move
the whole path instead of a local area. With radius, only the grabbed
point's subpath changes, with the falloff as before.

- An anchor added on a segment of a partly selected path is selected,
  and the selected anchors and segments after it keep their points.
- A focal pair that names no anchor is refused ("`focal` names [0, 7],
  which is not an anchor of the path"), and so is focal with radius.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 20:18:23 -08:00
dotCooCoo 2faadd28f4 Symbols: Break Link keeps a hidden or locked instance's art hidden or locked
Break Link to Symbol on a selected group, and Delete Symbol with its
instances expanded, turned a hidden or locked instance into visible,
unlocked art. The art now keeps the instance's hidden and locked state,
as Object > Expand does, and Break Link leaves hidden or locked art out of
the selection.
2026-11-10 20:09:28 -08:00
BFlat 726d2b6e13 Merge pull request #6508 from jmverges/fix/3179-warps-of-type-as-mesh
.ai import: warps of type open as mesh envelopes on their saved grid
2026-11-11 10:48:16 +07:00
BFlat 98fe060bb0 Merge pull request #6499 from dotCooCoo/feat/script-console
Scripts: Window › Script Console shows script output, errors and run reports
2026-11-11 10:48:06 +07:00
dotCooCoo 29437fed48 Merge main into feat/artboard-color-labels 2026-11-10 19:38:12 -08:00
dotCooCoo beef32b8a3 Artboards: an artboard's guide label colors its own guides
Each artboard can carry a guide label: one of the layer colors or a
custom RGB. Guides that belong to an artboard draw in its label color,
and guides without an artboard, or on an artboard without a label, keep
the guide color from the preferences. Selected guides still draw in the
selection color.

- Artboard Options has a Guide Label row (None, the layer colors with
  swatches, or Custom with a color swatch).
- artboard.new and artboard.setProps take guideLabel: null, a layer
  color's name, or #rrggbb. Any other value is an error. The inspect
  summary lists guideLabel when one is set.
- The native format saves the label and skips it when unset; a preset
  index outside the layer colors reads as no label.
- artboard.setProps no longer adds an undo step when nothing changes.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 19:37:17 -08:00
BFlat aaa2ea51a8 Merge pull request #6493 from dotCooCoo/fix/pattern-options-null-docs
Docs: pattern.options keeps a pattern's current values for omitted params
2026-11-11 10:09:23 +07:00
jmvergesandClaude Opus 5.5 d85a967fb7 docs: envelope grid orientation and retry budget for warps of type
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 03:06:48 +00:00
jmvergesandClaude Opus 5.5 8820a15e20 docs: warps of type as mesh envelopes on their saved grid
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 03:06:24 +00:00
BFlat 858b63082f Merge pull request #6500 from jmverges/fix/6472-ai-type-stroke-join
.ai import: stroked type keeps its cap, join and miter limit
2026-11-11 10:01:10 +07:00
BFlat ec89f3f26e Merge pull request #6501 from dotCooCoo/feat/prefs-export-import
Preferences: export and import preferences as a JSON file
2026-11-11 10:01:01 +07:00
dotCooCoo e4830a0e2c Docs: the Script Console note shows only after dropped or cut lines
The panel's note and the doc comment on ui.copyScriptConsole now say the
count of dropped and cut lines appears only when there are any, and the
returned lines count leaves that line out.
2026-11-10 18:51:13 -08:00
dotCooCoo 6643991710 Docs: ui.copyScriptConsole adds the count line only after dropped or cut lines
A copy of every Script Console line starts with the count of dropped and
cut lines only when there are any, and the returned lines count leaves
that line out. The end-line text is described without a code span that
ends in a space.
2026-11-10 18:30:11 -08:00
dotCooCoo d399b17605 Docs: pattern.options accepts a null width or height as omitted
The width and height check in pattern.options applies only to values
that are not null; null keeps the current value.
2026-11-10 18:27:23 -08:00
BFlat 4c2b8df865 Merge pull request #6498 from dotCooCoo/fix/smart-guides-snap
Smart Guides: tools do not snap to Smart Guides while Pixel Preview is on
2026-11-11 09:26:08 +07:00
jmvergesandClaude Opus 5.5 83245cfe54 .ai import: stroked type keeps its cap, join and miter limit (#6472)
The text document reader set a type stroke's paint and weight but never
its cap, join or miter limit, so every stroked text object imported from
a .ai or EPS editing copy drew with a miter join at a limit of 10. Type
the file strokes with round joins, or with a miter limit of 4 or less,
drew long spikes at its sharp corners.

Keys 61, 62 and 64 of a run's character attributes (the run, its
character style, then the document defaults) are now read as the
stroke's cap (0 butt, 1 round, 2 projecting), join (0 miter, 1 round,
2 bevel) and miter limit, as PostScript numbers caps and joins. In the
users' files the page strokes type with exactly these settings. Other
values keep the defaults, a miter limit is capped at 500, and type
without a stroke keeps the defaults.

- ate.rs: Style reads the three keys and char_style passes them on,
  with a unit test over defaults, character styles, run overrides,
  hostile values and unstroked type.
- tests_native.rs: an imported story with round, bevel, default and
  unstroked runs.
- docs/file-format-parity.md: revision row, status line, one line on
  the mapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 02:20:38 +00:00
dotCooCoo d426c70a92 Preferences: export and import preferences as a JSON file
Part of #6435. An optional VectorCraft feature: the Preferences dialog's
Export Preferences... saves the applied preferences to a JSON file (the
web build downloads it), and Import Preferences... applies such a file and
shows the imported values. They run the new commands prefs.export {path?}
and prefs.import {path | data}.

The file holds every preference prefs.list lists and the eyedropper,
perspectiveWidget, pathfinder and glyphSnap groups, without the four
folder preferences and saved presets. prefs.import reads at most 1 MB and
checks every value first: another format, a newer version, an unknown
preference, a bad value or a folder preference is an error, and nothing
changes. It keeps the open document's units, records no undo step, and is
refused as a command.batch step. prefs.set now rejects a group value that
is not an object.
2026-11-10 18:11:01 -08:00
BFlat af66951c41 Merge pull request #6496 from dotCooCoo/feat/ruler-artboard-guides
Guides: an option makes Cmd/Ctrl ruler drags artboard guides
2026-11-11 09:04:35 +07:00
dotCooCoo e60b0c3aa6 Docs: pattern.options rejects a bad width or height while sizeTileToArt is on
With sizeTileToArt on, pattern.options ignores width and height for sizing but
still rejects a value that is not a positive number, such as width 0, and
changes nothing.
2026-11-10 17:57:41 -08:00
BFlat 69d36e961d Merge pull request #6494 from dotCooCoo/feat/dimension-horizontal-vertical
Dimension tool: linear dimensions measure horizontally, vertically or aligned
2026-11-11 08:50:56 +07:00
dotCooCoo 4060480ef1 Smart Guides: tools do not snap to Smart Guides while Pixel Preview is on
With View > Pixel Preview on, the tools still snapped to Smart Guides
and showed their guides; only the Object Highlighting outline was
hidden. Work with Smart Guides says Smart Guides cannot be used while
Snap to Grid or Pixel Preview is on.

ViewInfo has a pixel_preview field, and Session::with_tool_cx turns
Smart Guides (and Snap to Glyph, which needs them) off in the tools'
context while it is set. VectorcraftApp::view_info passes View > Pixel
Preview. View > Smart Guides keeps its setting.

Docs: docs/target-app-parity.md.
2026-11-10 17:43:29 -08:00
BFlat 4302b7bde4 Merge pull request #6492 from dotCooCoo/fix/reshape-stretches-whole-path
Reshape: a drag stretches an open path from the dragged point to its two ends
2026-11-11 08:31:45 +07:00
dotCooCoo 8430715ec7 Docs: pattern.options sizes the tile to the art while sizeTileToArt is on
A brick tileType or gridType with brickOffset omitted or null sets the
offset to 0.5. While sizeTileToArt is on, every pattern.options call
sizes the tile to the art plus hSpacing and vSpacing again and ignores
width and height, so a new hSpacing changes the tile's width.
2026-11-10 17:27:50 -08:00
dotCooCoo df9c8d992d Merge main into feat/script-console 2026-11-10 17:27:35 -08:00
dotCooCoo 342d082c2b Scripts: Window › Script Console shows script output, errors and run reports
Scripts write to a new Script Console with $.write(...values) and
$.writeln(...values), each value as String(value) gives it. vc.log
entries, a run's uncaught error as name:line:column: message, and a
start, end and report line for each run go there too. The report line
gives the run's time, host calls and slowest commands.

- The console keeps at most 2,048 lines and 1 MiB of text, dropping the
  oldest and counting what it dropped. A line is cut at 4,096 bytes and
  a script's name at 256 bytes, with a count of cut lines. Line feeds,
  carriage returns and CRLF break lines; other control characters show
  as Unicode Control Pictures.
- script.run's result and scriptReport gain runTimeMs, timingAvailable,
  hostCallCounts, slowestCommands and the run's own console. A failed
  run's report has them too.
- script.console and script.console.clear read and empty the console;
  ui.copyScriptConsole {first?, last?} copies lines as the panel shows
  them.
- The panel lays out only the rows in view, selects with click and
  Shift-click, and has Clear, Copy Selected and Copy All. Output goes on
  while it is closed, and Clear works during a run.

Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
2026-11-10 17:26:30 -08:00
BFlat 6889e49628 Merge pull request #6486 from dotCooCoo/fix/perspective-snap-grid-docs
Perspective grid: docs say when Snap to Grid moves an edge onto a gridline
2026-11-11 08:07:33 +07:00
BFlat afab109722 Merge pull request #6491 from dotCooCoo/feat/prefs-reset-confirmation
Preferences: Reset Preferences asks first
2026-11-11 08:03:03 +07:00
dotCooCoo 17b986d11c Docs: pattern.options keeps current values for omitted params
The pattern and Repeat paragraph in docs/mcp.md said that every omitted
or null param keeps its default. pattern.options and
object.repeat.options keep the pattern's or the repeat's current value
instead (except the brick offset, which a brick tileType or gridType
without brickOffset sets to 0.5), object.pattern.edit and
pattern.options fall back to the selected object's pattern and the
pattern being edited when name is omitted, and pattern.delete requires
name.
2026-11-10 16:59:15 -08:00
dotCooCoo ed17e8fc17 Dimension tool: linear dimensions measure horizontally, vertically or aligned
A linear dimension between two clicked points, or dragged out from a
segment, always measured the straight distance between its points and
drew its line parallel to them, so two points at different heights gave
only a slanted dimension. The linear dimensions help describes aligned,
horizontal and vertical linear dimensions: after the second click,
clicking again gives an aligned one and moving the pointer gives a
horizontal or vertical one.
https://helpx.adobe.com/illustrator/desktop/measure-and-align/plot-and-measure/plot-linear-dimensions-of-objects.html

While a linear dimension is being placed, the pointer's position sets
whether it is aligned, horizontal or vertical. The rule is VectorCraft's
own. With the pointer beside the two points (the nearest point of the
line through them lies between them) or on that line, the dimension is
aligned. Past either point, the line through the points and the
perpendicular through that point bound a quarter on each side of the
line. The quarter that holds the direction straight up or down gives
the horizontal distance, and the quarter that holds the direction
straight left or right gives the vertical distance. A click within
reach of the second point, or of where the pointer was for the second
click, places it aligned. Points on one horizontal or vertical line
give an aligned dimension. The dimension stays linked to the anchors it
measures.

- Dimension recipes carry an orientation (aligned by default; files
  without it open as before). The line runs along the axis, and each
  extension line runs from its point to it.
- dimension.create and dimension.update take orientation: aligned,
  horizontal or vertical, for linear dimensions only. Any other value
  is an error. A new orientation keeps the line on the side of the
  points it was on, and an update that changes nothing records no
  undo step.
- A horizontal or vertical dimension keeps its orientation when it or
  the art it measures turns, and shows 0 when its points come into line
  across its axis. A line that lay outside the points stays outside
  them after a turn.
- The hint bar names the two-click placement.

Docs: docs/dimensions.md, the Evidence list in docs/target-app-parity.md.
2026-11-10 16:54:35 -08:00
BFlat b08e9df95c Merge pull request #6478 from dotCooCoo/fix/3d-member-opacity-masks
3D: an opacity mask on a group member masks its solids
2026-11-11 07:44:06 +07:00
BFlat 6dbbdb769c Merge pull request #6477 from dotCooCoo/fix/text-style-param-types
Type: character formats, Ruby and Move Type Path Handle reject a param of the wrong type
2026-11-11 07:39:31 +07:00
dotCooCoo 4038232411 Perspective grid: docs say a constrained move snaps only within a quarter cell
A constrained move keeps only the snap correction along its direction, and that correction exists only when an edge ends within a quarter cell of a gridline. When no edge on those axes is that close, the move does not snap.
2026-11-10 16:28:09 -08:00
dotCooCoo a2d13cd6a8 Merge main into feat/ruler-artboard-guides 2026-11-10 16:27:09 -08:00
BFlat e60da871bc Merge pull request #6485 from dotCooCoo/fix/spot-gradient-cmyk-doc-docs
Docs: spot gradients with RGB or Lab stops keep the ink in a CMYK document
2026-11-11 07:26:40 +07:00
dotCooCoo 91509f1776 Guides: an option makes Cmd/Ctrl ruler drags artboard guides
Preferences > Guides & Grid gets Artboard Guides with Cmd/Ctrl + Ruler
Drag (cmdRulerDragArtboardGuides), off by default. With it on, Cmd/Ctrl
held when a drag leaves a ruler makes a guide of the active artboard,
with any tool but the Artboard tool; with the Artboard tool it makes a
canvas guide instead. The key is read when the drag starts, so letting
it go during the drag keeps the kind of guide. Such a guide shows and is
made only where it lies within the active artboard; released elsewhere,
it makes no guide and no undo step. A ruler click still makes no guide.

This is VectorCraft's own option. The documented way to limit a guide
to one artboard, dragging it out with the Artboard tool, is unchanged.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 16:26:14 -08:00
dotCooCoo 8413ca6226 Merge main into feat/prefs-reset-confirmation 2026-11-10 16:16:18 -08:00
dotCooCoo 1d41174876 Reshape: a drag stretches an open path from the dragged point to its two ends
A Reshape drag on an open path moved only the anchors within half the
diagonal of the path's bounding box, measured along the path. On a
zigzag, a wave or a spiral that is one or two anchors, so dragging an
end moved the end and left the rest of the path where it was (#2723).

path.reshape now stretches an open path from the grabbed point to both
ends. The end anchors stay in place. Each anchor between an end and the
grabbed point takes u² (3 - 2u) of the drag, where u is its distance
from that end along the control polygon divided by the grabbed point's
distance from that end. A handle takes the share on the tangent line of
that curve at its anchor, so the handles of a smooth anchor stay in
line with it. A handle that is long beside the span takes a share below
0 or above 1.

This changes the default for every caller that leaves radius out on an
open path. A closed path still moves the anchors within half the
diagonal of its bounding box, and a call that gives radius uses that
radius on open and closed paths.

- A negative radius is refused ("`radius` must be 0 or more"). It gave
  anchors beyond the radius a share above 1.
- A drag of no length on an anchor no longer records an undo step.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 16:11:52 -08:00
dotCooCoo 4574ab047d Preferences: Reset Preferences asks first
Part of #6435.

Reset Preferences in the Preferences dialog now opens a question over
the dialog (`resetPreferences`). Cancel or Escape goes back to
Preferences as it was, with its edits, category and search kept. OK goes
back with every preference at its default value; OK in Preferences
applies them and Cancel there discards them, as before. `prefs.reset`
still resets at once.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-10 16:07:25 -08:00
BFlat 745fb4edd7 Merge pull request #6482 from dotCooCoo/fix/prefs-reset-keeps-document-units
Preferences: Reset Preferences leaves the open document's units alone
2026-11-11 07:04:48 +07:00
dotCooCoo 17f149ae2b Perspective grid: docs say which edge Snap to Grid lands on a constrained move
A constrained move keeps only the snap correction along its direction: a horizontal or vertical move lands the edge on its own axis, and a 45° move lands the edge on the axis whose nonzero correction is smaller. The per-axis rule now applies to unconstrained moves only.
2026-11-10 16:00:25 -08:00
BFlat c6f861808b Merge pull request #6481 from dotCooCoo/fix/repeat-param-types
Repeat, Pattern: the Repeat and pattern commands reject a param of the wrong type
2026-11-11 06:57:27 +07:00
BFlat fb10a6092f Merge pull request #6479 from dotCooCoo/fix/cut-ellipse-end-drag
Scissors: a cut leaves one of the two new end points selected
2026-11-11 06:52:41 +07:00
BFlat 8b34f0f826 Merge pull request #6476 from dotCooCoo/fix/answer-dialog-one-request
MCP: answer_dialog sets the fields and answers the dialog in one request
2026-11-11 06:41:16 +07:00
dotCooCoo fc34817d77 Perspective grid: docs say when Snap to Grid moves an edge onto a gridline
docs/mcp.md and docs/target-app-parity.md now state that a move in
perspective snaps an edge of the joint bounds only when it ends within
a quarter cell of a gridline (PerspectiveGrid::snap_offset), and that a
move perpendicular to the plane does not snap (perspective.move skips
snap_fix when perpendicular is true).
2026-11-10 15:37:42 -08:00