Commit Graph
802 Commits
Author SHA1 Message Date
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
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
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 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 7ea056c7bd Merge pull request #6489 from dotCooCoo/fix/perspective-snap-first-unattached
Perspective grid: Snap to Grid reads a flat first object at the depth it attaches to
2026-11-11 07:51:50 +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 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 3fc083d732 Perspective grid: Snap to Grid reads a flat first object at the depth it attaches to
When the first object of a perspective move was not yet in perspective
and a later one was attached deeper on the same plane, Snap to Grid and
Shift read the shared offset at the later object's depth and left the
first object out of the joint bounds. The first object then moved by
that offset instead of the one under the pointer at its own depth.

The move now reads Snap to Grid and Shift with the objects that are not
in perspective attached to the active plane, as the move attaches them,
so the result matches attaching them first and then moving.
2026-11-10 16:09:01 -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 ea612dbf19 Type: use pathops' number and length helpers; refuse a number with trailing text
text.setStyle now reads `size` and `tracking` with `number_given` and
`length_given` from pathops.rs instead of its own copies.

Both helpers read a string only when all of it is a number, or for
`length_given` a length with a unit (`"5 mm"`). Before, `"20abc"` read
as 20, so `text.setStyle` applied a 20 pt size and Offset Path a 10 pt
offset for `"10abc"`. Such a string is now refused with "`size` must be
a number, not \"20abc\"" and the document is left as it was.
2026-11-10 16:00:07 -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
dotCooCoo ab27448921 Docs: spot gradients with RGB or Lab stops keep the ink in a CMYK document
Two PDF export passages in docs/target-app-parity.md said a gradient
that mixes a spot color with RGB or Lab stops is always written without
the spot ink, or as an image when Perceptual. A CMYK document, or an
export that converts colors to a CMYK destination profile, writes those
stops in CMYK first, so the gradient is a DeviceN shading that keeps the
ink; an RGB destination profile keeps them in RGB. The passages now say
so, as docs/mcp.md and the paint.editGradient params text already do.

A new test checks the Classic case in an RGB document, a CMYK document,
with a CMYK destination and with an RGB destination.
2026-11-10 15:34:06 -08:00
dotCooCoo 272dab7b0a Merge main into fix/text-style-param-types 2026-11-10 15:27:58 -08:00
dotCooCoo 6732dbab1e Preferences: Reset Preferences leaves the open document's units alone
Reset Preferences put Points in Units > General, and OK sent it to
prefs.set, which also set the open document's units as an undo step.
OK after Reset now resets the General preference and leaves the
document's units as they are. Choosing a General unit after Reset, in
the dialog or with ui.dialog.set, still sets the document's units.

prefs.set takes documentUnits (default true); false makes unitsGeneral
set only the preference. Docs: params text, docs/mcp.md.

Part of #6435
2026-11-10 15:21:18 -08:00
dotCooCoo 81dc541d5d Repeat, Pattern: the Repeat and pattern commands reject a param of the wrong type
object.repeat.radial, object.repeat.grid, object.repeat.mirror and
object.repeat.options, and object.pattern.make, object.pattern.edit,
object.pattern.saveCopy, pattern.options and pattern.delete, read their
params with helpers that fell back to the default for a value of the
wrong type: "instances": "8" made 8 instances whatever the text said,
"rows": "2" made 3 rows, "edit": "false" entered pattern editing mode,
and a `name` that was not a string edited the selected object's pattern.
Each such value is now an error naming the param, and the document, the
selection and the undo history stay as they were. An omitted or null
param keeps its default.

- pattern.options refuses an `overlap` that is not an object, and an
  `overlap.h` other than left or right or an `overlap.v` other than top
  or bottom.
- object.repeat.options refuses a `center` that is not [x, y].
- object.repeat.radial refuses a negative `radius`; it used the
  automatic radius before.
- object.repeat.grid reads `rows` and `cols` as object.repeat.options
  does: a fraction is cut to a whole number, within 1 to 500.
- object.repeat.options applies `brickOffset` given without `gridType`
  to a brick grid, and its params text lists the key.

Docs: docs/mcp.md.
2026-11-10 15:08:03 -08:00
dotCooCoo 7eb1006abb Scissors: a cut leaves one of the two new end points selected
After a Scissors cut (`path.split`) the whole path stayed selected: both
pieces of an open path, or the opened path of a closed one. A Direct
Selection drag from the cut then moved every anchor, so an ellipse cut
at an anchor could not be pulled open by dragging its end.

- `path.split` selects the first anchor of the piece after the cut and
  nothing else, as `path.cutAtAnchors` does: anchor 0 of an opened
  closed subpath, of the new path when an open path becomes two, or of
  the subpath added to a path of several subpaths.
- A Direct Selection press on anchors of a selected path that lie on top
  of each other takes the selected one. It took the first in path order,
  which in a path of several subpaths was the unselected end.

Docs: docs/mcp.md, docs/target-app-parity.md, the `path.split` params
text.
2026-11-10 15:04:50 -08:00
dotCooCoo bf09404c33 Type: character formats, Ruby and Move Type Path Handle reject a param of the wrong type
`text.setStyle` read `font`, `style` and `justify` with `str_param`,
`size` with `len_param` and `tracking` with `num_param`, so a value of
the wrong type was skipped: `{"size": 30, "style": ["Bold"]}` set the
size and ignored the style, and `{"font": "Inter", "size": [12]}` set
the font and ignored the size. `features` dropped an entry that was not
a string and applied the rest. `text.setRangeStyle` skipped a `font` or
`style` that was not a string, `type.ruby` skipped such a `text`,
`position` or `align`, and `text.setPathHandle` moved the out handle
for any `which` other than "in".

Each of these now returns an error that names the param, before the
command edits the document. Omitting a param or giving null keeps its
default. `size` and `leading` still take a length with a unit
("12 pt", "5 mm"), and `tracking` still takes a number written as a
string ("20").

- `text.setStyle`: `font`, `style`, `justify` (`text_param`), `size`
  and `tracking` (checked reads in `typecmd.rs`), `features`.
- `text.setRangeStyle`: `font`, `style`, `features`.
- `type.ruby`: `text`, `position`, `align`.
- `text.setPathHandle`: `which` must be "in" or "out".

Docs: docs/mcp.md.
2026-11-10 14:56:13 -08:00
BFlat 667237ab47 Merge pull request #6473 from dotCooCoo/fix/opacity-mask-param-types
Transparency: opacity mask commands reject a param of the wrong type
2026-11-11 05:55:33 +07:00
BFlat 3c55b60e52 Merge pull request #6467 from dotCooCoo/fix/pathops-param-types
Path commands: Offset Path, Simplify, Split Into Grid, Clean Up, Trap and Contour reject a param of the wrong type
2026-11-11 05:25:49 +07:00
dotCooCoo 68b9f6305c Transparency: opacity mask commands reject a param of the wrong type
transparency.makeOpacityMask read `clip` and `invert` with bool_or,
transparency.setOpacityMask read `clip`, `invert`, `disabled` and
`linked` with as_bool, and the New Opacity Masks and page toggles read
`value` with bool_or, so a value of the wrong type was dropped:
"clip": "false" made a clipping mask, "invert": "yes" next to another
flag was ignored, and "value": "on" toggled the page knockout group.
They are now read with switch_or, and such a value is an error that
names the param, before anything changes. Omitting a param or giving
null keeps its default.
2026-11-10 14:14:31 -08:00
BFlat 2f117ba967 Merge pull request #6466 from dotCooCoo/fix/expand-param-types
Expand: object.expand rejects a param of the wrong type
2026-11-11 05:13:27 +07:00
BFlat 54092a0a01 Merge pull request #6462 from dotCooCoo/feat/plugin-call-commands
Plug-ins: an API level 2 plug-in calls the commands its manifest lists
2026-11-11 04:37:37 +07:00
dotCooCoo bf3e6b12ed Path commands: Offset Path, Simplify, Split Into Grid, Clean Up, Trap and Contour reject a param of the wrong type
object.path.offsetPath, object.path.simplify, object.path.splitIntoGrid,
object.path.cleanUp, object.pathfinder.trap and addon.contour.set read
their params with helpers that fell back to the default for a value of
the wrong type: "offset": true offset by 10 pt, "joins": 3 gave mitered
joins, "addGuides": "yes" added no guides. Each such value is now an
error naming the param, and the document and the undo history stay as
they were. An omitted or null param keeps its default, and a distance
still takes a string with a unit ("5 mm"). An unknown `joins` name is
refused as well.
2026-11-10 13:31:54 -08:00
dotCooCoo 626db59d53 Expand: object.expand rejects a param of the wrong type
object.expand read `object`, `fill` and `stroke` with bool_or, `steps`
with f64_or and `gradient` with str_param, so a value of the wrong type
fell back to the default: "fill": "yes" expanded the fill, "steps": "16"
made 255 strips and "gradient": 3 made strips. They are now read with
switch_or, number_or and text_param, and such a value is an error that
names the param, before anything changes. Omitting a param or giving
null keeps its default.
2026-11-10 13:28:28 -08:00
BFlat 77f2198d1c Merge pull request #5035 from dotCooCoo/fix/undo-history-follows-memory-r
Undo: the history keeps steps within a quarter of installed memory on every desktop OS
2026-11-11 04:25:52 +07:00
BFlat 7bac831948 Merge pull request #6458 from dotCooCoo/fix/mesh-keeps-shape
Fix #2752: a gradient mesh keeps the outline of the path it is made from
2026-11-11 04:17:03 +07:00
dotCooCoo d1f5af011f Fix #2752: a gradient mesh keeps the outline of the path it is made from
Create Gradient Mesh, a Mesh tool click and Object > Expand of a gradient
to a mesh gave each border edge one cubic fitted between two mesh points
and picked the corners from the bounding box, so the border cut off
corners and curves: 49 pt off a five-point star, 54 pt off a notch.

- The border follows the outline of the largest subpath by length; border
  points sit on it and an edge with no anchor inside is the exact piece
  of its segment.
- Where no single cubic follows a run within 0.1 pt (a thousandth of the
  size on a small outline), a mesh line goes through an anchor, the
  sharpest corner first; at most 256 lines each way. Rectangles and
  ellipses keep the rows and columns asked for.
- Smooth runs keep their end tangents; border handles are no longer made
  collinear.
- To Center and To Edge highlight placement accounts for the extra lines
  (for_path_shaded, also used for gradient fills).
- Non-finite points give no mesh; a degenerate outline keeps the old
  bounding-box mesh.

Holes of a compound path are still left out, as before.

Docs: docs/target-app-parity.md.
2026-11-10 12:33:20 -08:00
dotCooCoo c8d0cdf6a1 Undo: the open documents' histories share one budget of a quarter of installed memory
`Session::history_budget` (default `default_history_budget()`) caps the
memory the undo steps of all open documents keep together. After each
command, tool edit, interaction commit, direct edit, framed script run
and closed undo group, `Session::trim_histories` drops the oldest steps
of any document first, undo steps before redo steps. Each document keeps
its last eight undo steps and next eight redo steps, and a held history
keeps all of its steps. While a script runs or an undo group is open,
no steps are dropped, so a canceled group leaves the other documents'
steps in place. Closing a document frees its share. The steps are not
counted again while neither the budget nor any history changed. Each
document's own `byte_budget` stays as its ceiling.
2026-11-10 12:31:14 -08:00
dotCooCoo 085609e398 Merge main into feat/plugin-call-commands 2026-11-10 12:01:07 -08:00
BFlat fa13af4b8f Merge pull request #6353 from Messhias/feat/g5-brush-lib-game-trails
G5: built-in Game Trails brush library
2026-11-11 02:53:07 +07:00
dotCooCoo 5a81c1caba Plug-ins: a failed calling run keeps the trace handed in for Image Trace
A run that calls imageTrace.make used to take the trace the app handed in
for the command, and a failed run did not give it back. The trace is now
set aside while the run goes and handed back after it.
2026-11-10 11:49:27 -08:00
BFlat 6a83f3a268 Merge pull request #6452 from dotCooCoo/fix/glyph-bounds-width-keeps-font-size
Docs: with glyph bounds, the font size follows the vertical scale
2026-11-11 02:36:28 +07:00
dotCooCoo c6c57a80ef Merge main into feat/plugin-call-commands 2026-11-10 11:26:41 -08:00
dotCooCoo 811c21e7c2 Plug-ins: calls need doc.read, and an edit in progress is kept before a calling run
A command's result describes the document, so a plug-in without doc.read
gets -6 for every call, whether or not the id names a command. A run that
calls commands commits an edit still in progress first and prepares its
input again when that changed the document or the selection.
2026-11-10 11:26:10 -08:00
BFlat 5861a719fe Merge pull request #6449 from dotCooCoo/fix/script-run-keeps-file-scope
Scripts: a framed run keeps the file-access roots it started under
2026-11-11 02:18:21 +07:00
Fabio William Conceição 9dc15b8f1f G5: built-in Game Trails brush library
Add Speed Line, Dash Trail, Burst Ray and Zigzag as original art brushes so motion and impact strokes are one library click away.
2026-11-10 19:04:40 +00:00
BFlat d2402b75bf Merge pull request #6425 from Hassan1305/feat-4936-edit-custom-dictionary
#4936: Edit › Edit Custom Dictionary… dialog
2026-11-11 02:01:47 +07:00
dotCooCoo a5d09c13f0 Docs: with glyph bounds, the font size follows the vertical scale
docs/mcp.md and docs/target-app-parity.md said that a width or height
typed with Measure Type by Glyph Bounds on changes the font size by the
scale. The font size follows the vertical scale only: a width typed
alone, out of proportion, changes the horizontal scale and keeps the
font size, and a height typed alone changes the horizontal scale the
other way. A new test checks both.
2026-11-10 10:57:34 -08:00
dotCooCoo af0d01308f Scripts: a framed run keeps the file-access roots it started under
A script.run that a control request started in the app's frames was
advanced by later frames, which run unconfined, so its vc.run file
commands could read and write outside the caller's automation roots.
The job now keeps the file-access scope of its script.run and runs
each slice in it, as background opening and the folder walker do.
2026-11-10 10:39:48 -08:00
MarekSpodBiedry 90a8912027 Merge remote-tracking branch 'origin/main' into review-5560
# Conflicts:
#	docs/target-app-parity.md
2026-11-10 18:36:27 +01:00
HassanKhan 5080a8eced text.spelling.editDictionary: say each list entry must be a single word (part of #4936) 2026-11-10 22:13:23 +05:00
HassanKhan 4a954a6a84 Edit Custom Dictionary: Done saves its deletes and adds together, all or none (part of #4936)
Done ran text.spelling.removeWord and text.spelling.addWord one word at a
time, each saving the file, so a save that failed after a delete was saved
left that word deleted even after Cancel. text.spelling.editDictionary
{remove, add} makes every change, saves once and takes all of them back when
the save fails; Done runs it once.
2026-11-10 21:48:25 +05:00
Fabio William Conceição df6065b5b4 G5: assert Mana Glow appearance after addFromLibrary 2026-11-10 14:26:37 +00:00
Fabio William Conceição 710e14a33a G5: built-in Game HUD graphic style library 2026-11-10 14:26:37 +00:00
dotCooCoo 2fb8c9e516 Merge main into fix/undo-history-follows-memory-r 2026-11-10 03:33:23 -08:00
Fabio William Conceição e3a17716ed G5: built-in Map Markers symbol library 2026-11-10 10:09:21 +00:00