Commit Graph
2416 Commits
Author SHA1 Message Date
dotCooCoo 0f13edaabe Graphs: centered labels with kept leading stay centered
Category labels beside an axis and labels in pie wedges are centered
on their place by the default leading. A multi-line label that keeps
more leading or paragraph spacing ran down from that place instead.
The graph now puts the middle of such a label's first and last
baselines on its place.
2026-11-11 01:40:36 -08:00
dotCooCoo da765088a0 Graphs: the legend measures its labels with the document's System Layout
The legend measured each label with System Layout off, while the graph
draws the labels with the document's setting. With System Layout on,
a long legend label could run over the next swatch in Legend Across
Top. The legend now measures labels with the same setting.
2026-11-11 00:35:52 -08:00
dotCooCoo cba56d2602 Merge main into fix/graph-labels-keep-paragraph-settings 2026-11-10 23:41:28 -08:00
dotCooCoo e76524fd8b Graphs: labels keep paragraph settings and anti-aliasing when the graph is drawn again
Paragraph settings given to all of a graph label's text with
text.setFormat (indents, spacing, hyphenation, Justification options,
composer, direction, kashida, kinsoku, mojikumi, burasagari and the
rest) and the label's anti-aliasing method were lost when the graph
drew its labels again, because the graph kept only a character style
for each kind of label. Each kind now also keeps its paragraph settings
and anti-aliasing method, saved with the graph in its label styles, and
the graph still sets each label's alignment for its place.

The legend spaces its entries by the kept legend settings: Legend
Across Top measures each label with its indents and spacing, both
legend layouts make room for extra leading or paragraph spacing, and a
label with a negative indent or negative space before moves right or
down so it stays clear of its swatch and of the entry above it.
2026-11-10 23:40:25 -08:00
BFlat 0127ed3ec9 Merge pull request #6536 from dotCooCoo/feat/script-include-directives
Scripts: #include and the other preprocessor lines run, and $.evalFile runs a file
2026-11-11 14:31:44 +07:00
BFlat 3e642efce6 Merge pull request #6530 from dotCooCoo/fix/vertical-path-sideways-glyphs
Type: on a vertical path, letters lie a quarter turn clockwise from upright glyphs, in a column centered on the path
2026-11-11 14:22:33 +07:00
BFlat fe346e53be Merge pull request #6527 from dotCooCoo/fix/dimension-follow-both-ends
Dimension tool: both ends of a dimension follow the art it measures
2026-11-11 14:03:40 +07:00
dotCooCoo 08387a22ec Merge main into feat/script-include-directives 2026-11-10 22:39:32 -08:00
BFlat b421c03d03 Merge pull request #6525 from dotCooCoo/feat/scripts-folder
Scripts: File › Scripts lists the Scripts folder, subfolders as submenus
2026-11-11 13:33:56 +07:00
dotCooCoo 5a8a57d5f6 Type: upright glyphs on a vertical path keep vertical point type's cells, and the caret spans the column
On a vertical path, an upright glyph is now placed in its column as
vertical point type places it, then the column is centered on the path.

- Its cell starts at its place on the path; it started 0.12 em earlier
  with fonts whose cell top is less than a full cell above the baseline.
- A baseline shift moves it across the column instead of along the path.
- A horizontal scale widens it evenly about the column's center.
- A combining mark stays on its upright letter.
- The caret and the selection highlight span the column, half its em on
  each side of the path, instead of the font's ascent and descent.

With Skew, 3D Ribbon and Stair Step on a path that runs to the left,
letters are a quarter turn counterclockwise from upright glyphs: Skew and
3D Ribbon mirror both kinds there, and Stair Step keeps letters level. The
test, the progress doc and the params text of Tate-chu-yoko Settings now
state these conditions and the block's directions on a path.
2026-11-10 22:27:53 -08:00
dotCooCoo fd9c7a2073 Merge main into feat/script-include-directives 2026-11-10 22:13:57 -08:00
BFlat 99cbdf9b73 Merge pull request #6520 from dotCooCoo/fix/break-link-selects-only-selectable-art
Symbols: Break Link selects no art inside a hidden or locked group
2026-11-11 13:05:00 +07:00
dotCooCoo 693031c509 Scripts: #include and the other preprocessor lines run, and $.evalFile runs a file
Before a script runs, `script.run` reads its preprocessor lines (`#` or
`//@` at the start of a line, outside strings and comments):

- `#include` and `//@include` put another file's text in place of the
  line, looked for beside the including file, then in each
  `#includepath` folder, then in each `JSINCLUDE` folder. A later
  `#includepath` replaces the list.
- `#script` names the run unless `name` is given; `#target` takes any
  value; `#strict off` is taken. `#strict on`, `#targetengine` and
  `#engine` are refused as unsupported, and any other `#name` as
  unknown.

`$.evalFile(path, timeout?)` runs a file in the same global environment
and returns its last value, `$.fileName` is the file being run and
`$.includePath` the include folders.

A script run from a file reads these files only in its folder and the
folders below it. Paths with a `..` name, links or junctions that lead
out, device names, streams and network paths are refused; a script
given as text reads none. A run reads at most 64 files 16 levels deep,
32 MiB of source in all and 1,024 paths looked up. An error in an
included or evaluated file is reported at that file's line and column,
and the report's new `file` field names it.

Docs: docs/mcp.md (script.run, Script Console), docs/target-app-parity.md.
2026-11-10 21:59:22 -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
dotCooCoo ef6d14dc22 Dimension tool: both ends of a dimension follow the art it measures
A linear dimension clicked from an anchor to a point that is not an
anchor (a point on a segment, or a point a Smart Guide gave) followed
the art at the anchor end only, so scaling the art turned it into a
long slanted dimension.

- A point on a segment is stored by its curve parameter on that
  segment, and stays on it when the segment's anchors or handles are
  edited.
- Any other point is stored against anchors (and, for a path whose
  anchors lie on one line, handles) of the path under it, or of the
  path at the other end, and moves, scales, turns and reflects with
  that path. Beside a path whose anchors and handles all lie on one
  line, such as a straight line, it keeps its distance along and
  across the path: a reflection moves it as a half turn does, and a
  scale that is not uniform moves it as a uniform scale does.
- When linked art is reflected, a linear dimension's line goes to the
  reflected side, except for art whose anchors and handles lie on one
  line.
- Deleting the art, changing its anchor count or whether it is closed,
  turning an angle's arm into a curve, or making a measured circle or
  arc no longer circular leaves the dimension where it was, no longer
  linked, instead of removing it. Undo links it again.

Docs: docs/dimensions.md, docs/target-app-parity.md.
2026-11-10 21:25:59 -08:00
dotCooCoo d069e0e4be Symbols: Break Link selects no art inside a hidden or locked group
Break Link to Symbol on a selected group selected the art of every
instance it expanded that was itself shown and unlocked, including art in
a hidden or locked group inside the selection. The art now goes into the
selection only when it and every group and layer holding it are shown and
unlocked, the check Select All uses (select::is_selectable).
2026-11-10 21:08:57 -08:00
dotCooCoo 39996d3d2c Merge main into feat/scripts-folder
# Conflicts:
#	crates/engine/src/cmd/scripts.rs
#	crates/engine/src/tests.rs
#	docs/gaps.md
#	docs/mcp.md
#	docs/target-app-parity.md
2026-11-10 21:08:10 -08: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 acc6acfdb2 Merge pull request #6266 from Silvertree2010/fix/h13526-graph-csv-round-trip-drops-a-bla
Keep blank value row in single-column graph CSV round trip
2026-11-11 11:52:07 +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
NetterSilverandClaude Haiku 5.5 aaf27e1ec6 Keep blank value row in single-column graph CSV round trip
Found while testing crates/engine/src/cmd/graph/data.rs:51 (parse_csv); written by crates/doc/src/graph.rs:497 (GraphSpec::to_csv)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 04:16:30 +00: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 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 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
dotCooCoo 259bd8ee25 Merge main into feat/scripts-folder 2026-11-10 19:10:00 -08: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 04c20fc8b9 Merge main into feat/scripts-folder 2026-11-10 18:24:29 -08: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 5bc51a7c35 Merge main into feat/scripts-folder 2026-11-10 18:04:28 -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 899b7bdb3d Merge main into feat/scripts-folder 2026-11-10 17:47:11 -08:00
dotCooCoo 52efba54aa Scripts: File › Scripts lists the Scripts folder, subfolders as submenus
File › Scripts now lists the .js and .jsx files of the Scripts folder next
to the preferences, each named after its file without the extension, with
each subfolder as a submenu, above Check Script… and Other Script…. The
help page describes this listing:
https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/install-and-run-scripts.html

Choosing a listed script runs it through script.run without the warning
Other Script… shows. script.listInstalled returns the same listing, and
ui.runInstalledScript {path} runs one entry.

VectorCraft's own rules: the folder is read off the UI thread when the
app starts, when its window becomes active and when the File menu of the
window's menu bar opens; only names are read; links, junctions, hidden
and system entries are left out and checked again before a run; one read
stops at 2,048 entries, 512 scripts, 128 subfolders, 4 levels or 250 ms
and the menu then notes that some scripts were not listed. Under an
automation read root, script.listInstalled and ui.menu.list leave out a
folder outside the root.

#1183 (LloydNicholson) also proposed a Scripts menu listing a folder.
2026-11-10 17:46:36 -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 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
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 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
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
BFlat b3bb905bb6 Merge pull request #6484 from dotCooCoo/fix/batch-folder-stem-case-tests
Actions: test Batch folder names that differ only in case or are empty
2026-11-11 07:21:26 +07: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 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