Commit Graph

799 Commits

Author SHA1 Message Date
ham9zah 66a0f457e9 Add a Precise Cursors command and fix the misleading Caps Lock comment
General > Use Precise Cursors already worked and is saved with the other
preferences. This adds a `view.preciseCursors` command that toggles that same
preference, so it can be given a shortcut in Edit > Keyboard Shortcuts. It has
no default shortcut and no View menu item.

The comment in canvas.rs claimed Caps Lock gave precise cursors; nothing reads
Caps Lock (egui has no state for it), so it now describes what the code does.

Tests: for the tools whose own pointer the preference replaces (Pen, the anchor
tools, Curvature, Mesh, Eyedropper, Slice, Blend, Shape Builder) the pointer is
their own when off and a crosshair when on; every state of the Pen's pointer
turns into a crosshair; the preference round-trips through
to_json/apply_prefs; the command toggles it, updates the saved UI state, is
listed in the shortcut editor without a key and can be assigned one; switching
it creates no undo step and leaves the document saved; with system cursors the
glyph fallback is the system crosshair either way. The new label is translated
in the languages that claim complete menus.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 03:55:41 +07:00
Gert Pellin ed1e976264 Fix #1748: text.create takes justify, so point type can be centred on its point
text.create, text.createInPath and the MCP add_text tool now take a
`justify` (auto|left|center|right|justifyAll, as text.setStyle). Point
type already laid out centred or right-aligned text around its origin;
it just could not be made that way, so scripts measured the bounds and
moved the text. Changing it afterwards stays text.setStyle {justify}.
2026-10-14 03:55:41 +07:00
Marco Arnulfo d9328c7668 Select > Same: type matches on its characters' fill and stroke (fixes #1737) 2026-10-14 03:55:41 +07:00
Ayberk A. 2f4cc8a1cc Fix #1756: Object → Expand expands repeats
Expand had nothing for live repeats, so a radial, grid or mirror repeat stayed
live. Its Object option now turns every repeat in the selection (inside groups
too, inner ones first) into a group of its instances, keeping the repeat's id
and place, in the same undo step; the dialog enables Object for a repeat.
Expand Repeat shares the code.
2026-10-14 03:55:41 +07:00
bflatastic 3152436020 Brackets: keep to the tools whose size the keys are documented for
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.
2026-10-14 03:55:41 +07:00
Ayberk A. 3f1e72d983 Fix #1661: [ and ] size the Paintbrush, and reach the tools that take them
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.
2026-10-14 03:55:41 +07:00
Ayberk A. ef2d678cbf Fix #1679: a filled pencil path keeps a stroke of None
The Pencil gave every path a black stroke when the stroke was None. With Fill
new pencil strokes on and a fill set, the path now keeps no stroke; only a path
that would show nothing (no fill, no stroke) still gets black.
2026-10-14 03:55:40 +07:00
Ayberk A. 1e60a572b1 Fix #1686: Escape in type editing goes to the Selection tool
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.
2026-10-14 03:55:40 +07:00
turtwig 89501d73c2 Delete removes every selected anchor and keeps the remainder selected
A fully selected object is deleted along with partial anchors on other
paths. Several anchors on one closed path are removed by their original
indexes. What remains of a path stays selected, so a second Delete
removes it.
2026-10-14 03:55:40 +07:00
BFlat 4d54fa27e3
Merge pull request #1773 from MarekSpodBiedry/issue-1722
Linked files: a change during a drag is applied after the drag, not lost (#1722)
2026-10-14 03:52:41 +07:00
BFlat 7f93a191c7
Merge pull request #1775 from MarekSpodBiedry/issue-1720
Do not journal a failed live preview at commit (#1720)
2026-10-14 03:52:17 +07:00
BFlat 0f0380b827
Merge pull request #1774 from MarekSpodBiedry/issue-1721
Stamp File Info dates only after a save succeeds (#1721)
2026-10-14 03:51:47 +07:00
BFlat 4892384168
Merge pull request #1780 from MarekSpodBiedry/issue-1707
Fix scatter X axis ticks looping forever on data near the f64 maximum (#1707)
2026-10-14 03:49:57 +07:00
BFlat 6f56ad2e7f
Merge pull request #1777 from MarekSpodBiedry/issue-1713
Keep Export for Screens suffix inside the export folder (#1713)
2026-10-14 03:48:36 +07:00
BFlat b374fee789
Merge pull request #1786 from MarekSpodBiedry/issue-1696
Refuse to package over the source document or its linked files (#1696)
2026-10-14 03:47:20 +07:00
bflatastic 8a7e36ede8 Background save edited meanwhile: undoing back to the saved state is unmodified (#1721) 2026-10-14 03:17:51 +07:00
bflatastic 7abc1cfa10 Scatter overflow test: data that far off the plot is refused, not drawn (#1707) 2026-10-14 03:17:49 +07:00
bflatastic d82d6e3620 Test that a hostile Export for Screens suffix or prefix stays in the export folder (#1713) 2026-10-14 03:17:47 +07:00
bflatastic f05fbfc292 Pathfinder panel: count a group's shapes without copying them 2026-10-14 02:09:08 +07:00
Marco Arnulfo 7ea75b01ba Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroke keeps the Shape Modes enabled (fixes #1635) 2026-10-14 02:09:07 +07:00
bflatastic 359d9bb833 Distribute spaces the selected anchor points too, and its buttons work with the points of one path (#1636) 2026-10-14 02:09:06 +07:00
MarekSpodBiedry b50519587d Refuse to package over the source document or its linked files (#1696) 2026-10-13 18:19:12 +02:00
MarekSpodBiedry b8744f6d33 Stamp File Info dates only after a save succeeds (#1721) 2026-10-13 18:18:47 +02:00
MarekSpodBiedry 1f44a18c7a Do not journal a failed live preview at commit (#1720) 2026-10-13 18:17:49 +02:00
MarekSpodBiedry ba94976bf2 Fix scatter X axis ticks looping forever on data near the f64 maximum (#1707) 2026-10-13 18:17:33 +02:00
MarekSpodBiedry 65cc818572 Linked files: a change during a drag is applied after the drag, not lost (#1722) 2026-10-13 18:17:02 +02:00
MarekSpodBiedry a5186f5a12 Keep Export for Screens suffix inside the export folder (#1713) 2026-10-13 18:16:12 +02:00
dotCooCoo 3374d0778d Scripts: Check Script… reports what a .jsx script needs and does, without running it
VectorCraft does not run scripts written for the reference app, and
File › Scripts held only the disabled Other Script…. File › Scripts ›
Check Script… picks a .jsx, .jsxinc or .jsxbin file, reads it without
running any of it, and shows a report: what the script needs (the
script language's own objects, the document model, files, menu
commands, dialogs, or features only the reference app has), what it
would do outside the document, the menu-command strings it passes, the
document-model member chains and type names it uses, its directives,
the files it includes, and the problems that would stop it loading.
Copy Report copies the report as JSON. File › Open and drag-and-drop
show the report for those files too.

- vectorcraft-scriptcheck, a new Standalone crate with no
  dependencies, holds the checker: a lexer for ECMAScript 3 with E4X
  literals and the #name and //@name directive lines, and an analysis
  of the tokens that follows variables, function scopes and the files
  a script includes. It never recurses: open brackets, the levels of
  an XML literal and the function bodies being read are kept on stacks
  capped at 4,096. It reads at most 8 MiB of a file and 32 MiB with the
  included files, follows 64 included files 16 deep, looks up at most
  1,024 paths for them, keeps 1,000 entries in a list, tracks 1,000
  variables holding member chains and 1,000 holding File objects, and
  cuts echoed text to 120 characters. A compiled script is reported as
  compiled and isn't read.
- script.check (engine, a query) takes path, data or dataBase64. With a
  path and includes: true, it reads the files the #include lines name
  from inside the script's folder, through the automation read root,
  and when the script or one of those files assigns a member chain or a
  File object to a variable, the check treats the variable as holding
  it in every file. It doesn't follow absolute names, names, folders or
  links that lead out of the folder, names or folders longer than 4,096
  bytes, or JSINCLUDE, and it reads only regular files.
- ui.checkScript shows the report (dialog scriptCheck). File › Open
  lists Scripts (.jsx, .jsxinc, .jsxbin) and sends a script's path to
  script.check, which reads at most 8 MiB of it.

The language rules come from ECMA-262 3rd edition and ECMA-357 2nd
edition. The directive, platform and document-model root names come
from the public JavaScript Tools Guide and scripting guide pages that
catalog.rs cites by URL, and executeMenuCommand, doScript, loadAction,
unloadAction and selectTool come from the public forum posts cited
there; only names are used. Every script in the tests is written from
scratch with invented names.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-13 13:36:41 +07:00
Gert Pellin 13bd24ba79 Open by a relative path finds relative links; convert reports import warnings
document.open (and file.newFromTemplate) with a path relative to the
working directory took the SVG's folder from Path::parent(), which is
"" for a bare file name and was then dropped, so relatively linked
images were "not found" (and, linked by their bare name, not listed in
missingLinks either). A relative path is now made absolute first: its
folder is the working directory, as for the absolute path.

vectorcraft-cli convert printed only the export's warnings; what reading
the input left out now comes as importWarnings.

Fixes #1604
2026-10-13 13:36:40 +07:00
Gert Pellin eb25a85d90 Type: stylistic sets, alternates, titling, figure styles, superscript and subscript
A character style's OpenType features took nine tags. OtFeatures now
also keeps ss01-ss20, salt, titl, pnum, lnum, sups and subs (off by
default, one bit each in `extra`): they are accepted by the type
commands, stored with the style and passed to the shaper, so a font
that has them uses them.

The Affinity import test used ss01 as its example of a feature kept but
not rendered; it now uses cv01 for that (character variants still
aren't rendered) and keeps ss01 as one that is.

Fixes #1607
2026-10-13 13:36:40 +07:00
dotCooCoo f0810ca58b Object: Lock and Hide › All Artwork Above go through each top-level layer once
Lock › All Artwork Above and Hide › All Artwork Above went through the
objects above each selected object in its top-level layer, one selected
object at a time. For each object they looked up in a list whether it
was selected or already found, and they computed its bounds again for
each selected object below it until one overlapped it. With 100 tiles
selected under 25,000 rectangles, a release build of vectorcraft-cli
took 6.4 s for Lock, and with all of 5,001 objects selected it took
8.8 s and locked nothing.

The commands now go through each top-level layer that holds a selected
object once, and check each object that is not selected against the
bounds of the selected objects below it. The selected objects and the
objects found are kept in sets, and each object's bounds are computed
once. The objects they lock or hide, the count, the selection and the
undo step are the same as before. On the same documents Lock now takes
under 0.1 s.

Docs: docs/target-app-parity.md.
2026-10-13 12:58:05 +07:00
Marco Arnulfo 5e8581e42f Save: a network drive on macOS that refuses the full flush gets a plain fsync (fixes #1620) 2026-10-13 12:58:04 +07:00
BFlat 5bfe774420
Merge pull request #1627 from storytold/u/trace-gradients
Image Trace: Gradients
2026-10-13 12:24:35 +07:00
bflatastic 250da4226e Image Trace: Gradients
Gradients (params `gradients`, a checkbox in the panel for Grayscale and Color;
off unless asked for): an area whose colour changes evenly along one direction
is traced as one path filled with a linear gradient, not as bands of flat colour.

Gradients are found before the palette is made, so no colour is spent on their
bands: a connected run of smoothly changing pixels is a gradient when a plane
fits each colour channel with all three sloping the same way, the colours along
that way are at most six stops with ramps between them, and painting them back
gives the area's own pixels. Flat colour, soft edges between flat colours and
shading that goes two ways are left as they were.

The harness gains two linear-gradient inputs with known lines and stops, and
measures whether each is found, the angle of its line and the colours of its ramp.
2026-10-13 12:16:03 +07:00
bflatastic 175ad4a1e3 Effects: effect.apply and effect.setParams refuse parameters the effect doesn't take (#1592) 2026-10-13 11:53:38 +07:00
BFlat 8d2d838884
Merge pull request #1618 from storytold/u/trace-groups
Image Trace: Auto Grouping
2026-10-13 11:47:48 +07:00
BFlat bc5bdf47d1
Merge pull request #1591 from switch87/fix/1590-offset-path-message
Offset Path: an empty result says the offset leaves nothing
2026-10-13 11:29:17 +07:00
BFlat ecf4ae7a3a
Merge pull request #1577 from switch87/fix/ids-without-selection
Commands: objects given as ids stand in for the selection
2026-10-13 11:27:30 +07:00
BFlat 3126e82ebd
Merge pull request #1572 from switch87/fix/1570-pathfinder-strokes
Pathfinder: Trim, Merge and Crop remove strokes
2026-10-13 11:26:39 +07:00
bflatastic 9c42a2942a Image Trace: Auto Grouping
Auto Grouping (params `autoGrouping`, a checkbox in the panel; off unless asked
for): each traced path that has others inside its outer contour comes out in a
group with them, the path first, nested as deep as the picture is. The path that
covers the whole image is nobody's group.

The grouped result paints the same picture: where paths can overlap (stacked
layers, Flat Logo, strokes) a path only joins a path below it, and only if that
moves it under nothing that shares any of its box.

The harness measures three presets with the option on ("grouped %"), painting
them in the grouped order.
2026-10-13 11:15:02 +07:00
bflatastic dc64aaa41b Commands: ids stand in for the selection where the params description names the key, checked over every command 2026-10-13 10:52:38 +07:00
Gert Pellin 5366cf689e Offset Path: an empty result says the offset leaves nothing
With paths selected but every offset empty (an inset larger than the
shape), Offset Path failed with "select paths", sending the user to
look for a selection problem. It now says the offset leaves nothing of
the selected paths; "select paths" stays for selections without paths.

Fixes #1590
2026-10-13 10:52:38 +07:00
Gert Pellin dfc1b846cc Commands: objects given as ids stand in for the selection
Commands whose description takes `ids` (operate on these objects;
default: the selection) failed with "nothing selected" when objects
were given but none was selected: execute checked their `enabled`
predicate against the selection, with an exception for edit.clear only.
Of the 220 commands mentioning ids, 48 were refused so; their run
already used the ids. execute now evaluates `enabled` with the given
ids selected and puts the selection back, so each predicate keeps its
meaning (Make with Top Object still needs two objects).

Fixes #1575
2026-10-13 10:52:38 +07:00
Gert Pellin de8d5c96ab Pathfinder: Trim, Merge and Crop remove strokes
The public Pathfinder documentation says Trim and Crop remove any
strokes, and Merge removes them and merges same-coloured fills. The
pieces kept every stroke, and Merge, grouping by the whole style, left
same-filled objects with different strokes apart. The three now drop
stroke layers before styles are compared.

Fixes #1570
2026-10-13 10:52:37 +07:00
bflatastic 9ca74ac011 Image Trace: Shapes, and corners whose pixel is mostly outside the shape
Shapes (params `shapes`, a checkbox in the panel; off unless asked for): a filled
outline that is a circle, an ellipse, a rectangle or a regular polygon comes out
as that live shape. A shape is only made when every point of the traced outline
lies within 0.5 px (0.2% of the shape's size, if more) of it; an ellipse whose
radii differ by less than twice that is a circle of its area.

The outline before fitting: a corner's own pixel no longer pulls the two edges
that meet there, and a one-pixel step between two long edges is the corner where
they meet. Sharp corners kept: Default 69% -> 76%, the colour presets 86% -> 91%.

The harness gains plain-shape inputs and "shapes %".
2026-10-13 10:25:05 +07:00
BFlat 0810a15f6e
Merge pull request #1566 from marcoarnulfo/fix/new-symbol-options-dialog
Symbols panel: New Symbol asks for Symbol Options before making the symbol
2026-10-13 09:31:54 +07:00
BFlat acc1c9a85d
Merge pull request #1582 from storytold/fix/1533-join-coincident
Fix #1533: Join merges the selected anchors that lie on one another (after Average) into one
2026-10-13 09:08:58 +07:00
BFlat 9d2b074d12
Merge pull request #1543 from marcoarnulfo/fix/guide-objects-locked
Guides: Lock Guides locks the paths made into guides too
2026-10-13 09:08:05 +07:00
Marco Arnulfo ee97225c19 Symbols panel: New Symbol asks for Symbol Options before making the symbol (fixes #1560) 2026-10-13 08:53:06 +07:00
Marco Arnulfo 79b5d73ca7 Guides: Lock Guides locks the paths made into guides too (part of #1534) 2026-10-13 08:51:38 +07:00