Commit Graph
4065 Commits
Author SHA1 Message Date
BFlat 5fd9ef537a Merge pull request #7294 from dotCooCoo/fix/live-shape-unknown-keys
Live Shapes: object.setLiveShape refuses unknown keys and invalid sides or radius
2026-11-14 05:41:41 +07:00
BFlat 7b992a416e Merge pull request #7287 from dotCooCoo/fix/recovery-error-guidance
Data Recovery: a failed copy names the folder in use and how to change it
2026-11-14 05:41:33 +07:00
BFlat df581b2521 Merge pull request #7282 from dotCooCoo/feat/banner-hems-grommets
Cutting Tools: Banner Hems and Grommets marks a banner's hems and grommet holes
2026-11-14 05:41:10 +07:00
BFlat 04015f75cc Merge pull request #7277 from dotCooCoo/fix/break-text-thread-lookup
Artwork Cleanup: Break Text and Remove Breaks read the text threads once
2026-11-14 05:41:00 +07:00
BFlat 35d7c5d523 Merge pull request #7271 from dotCooCoo/fix/pdf-long-page-warning-names-option
PDF: the long-page warning names the in-points option
2026-11-14 05:40:30 +07:00
BFlat e21e2622d0 Merge pull request #7281 from dotCooCoo/fix/path-precision-picks-one-pass
Path Precision Tools: group selected segments and crossings by subpath in one pass
2026-11-14 05:40:21 +07:00
BFlat e244a65981 Merge pull request #7293 from dotCooCoo/fix/offset-path-parallel
Offset Path: offsets several selected paths on several threads
2026-11-14 05:40:00 +07:00
BFlat 4558196e16 Merge pull request #7053 from dotCooCoo/feat/warichu
Type: Warichu stacks selected characters on lines inside their line
2026-11-14 04:32:40 +07:00
BFlat e5913e30e1 Merge pull request #7204 from dotCooCoo/fix/trace-overlapping-two-pass
Image Trace: Overlapping fills each shape whole and stacks what it encloses above it
2026-11-14 04:31:56 +07:00
BFlat 4a039aa63c Merge pull request #7269 from dotCooCoo/fix/free-distort-box-turns
Free Distort: the bounding box turns with the warped art
2026-11-14 04:31:23 +07:00
BFlat 7a80e194a2 Merge pull request #7266 from dotCooCoo/fix/doc-info-clear-stop-spots
Document Info: spot colors from gradient stops that color nothing are not listed
2026-11-14 04:31:11 +07:00
BFlat 0511d22d3d Merge pull request #7259 from dotCooCoo/fix/threaded-text-one-pass
Threaded Text: thread edits find frames and threads in one pass
2026-11-14 04:30:36 +07:00
BFlat d15a725626 Merge pull request #7216 from dotCooCoo/feat/trim-at-crossings
Path Precision Tools: Trim at Crossings removes the stretch of a path between crossings
2026-11-14 04:30:27 +07:00
dotCooCoo ac10c51381 Data Recovery: a failed copy names the folder in use and how to change it
When a recovery copy could not be written, the status bar said only
"Couldn't save recovery data: <error>", and a copy that failed on the
background worker showed as "Saving recovery data for <title> failed:
<error>". Neither said which folder was in use or how to pick another.

The message keeps the error text (and the document's title when one
copy fails), then names the folder copies go to, says whether it is
the default recovery folder or the folder set in Preferences, and
names the setting that picks another folder: Preferences → File
Handling → Data Recovery → Folder (recoveryFolder). The recovery code
writes the same line to the log. The folder is the one in use when the
round started, so a preference changed while a copy runs does not
change the folder the message names.

A copy that fails now reports its own message, so the job's result is
Ok rather than an error, and the job no longer adds "<label> failed:
<error>" over it. A store the host installed (browser storage) has no
folder to name: its failed copy shows "Saving recovery data for
<title> failed: <error>", as a copy on the worker did before, also
when the copy was written without the worker.

- engine: recovery::folder returns the folder in use and whether the
  recoveryFolder preference names it; recovery::store uses it.
- Four @msg rows in each complete-message catalog, and in the bg, bs,
  da and id catalogs, which translate the old message.
- Control replies to a client confined to read roots show a recovery
  folder outside them, and the paths under it, by the folder's name, as
  they already show other paths outside the roots. That covers the
  folder in use and the last eight folders a failure message named.
- docs/target-app-parity.md: a line next to the Data Recovery Folder
  preference.
2026-11-13 12:51:12 -08:00
dotCooCoo 1eaf421e69 Cutting Tools: Banner Hems and Grommets marks a banner's hems and grommet holes
Object › Plug-ins › Cutting Tools › Banner Hems and Grommets…
(`addon.cutting.banner`) marks where a printed banner is hemmed and where
its grommets go. The banner is the bounds of the selected art, or each
artboard when nothing is selected. On each chosen side the command adds a
hem line a set depth inside the edge (1 in by default) and grommet marks a
set inset inside it (0.5 in): one at each end of the side and the rest
spread evenly, as few as keep them at most a set spacing apart (24 in). A
corner that two chosen sides share gets one grommet. Each grommet mark is a
circle (27 pt by default) with a cross through its center. The marks are
stroked in a chosen swatch ([Registration] by default) with no fill, in one
group named Banner Marks per banner on top of the current layer, selected,
as one undo step. While a clip group is edited in isolation, the group goes
outside the outermost clip group, as Weed Box and Lines does.

The command refuses every side off, no hem and no grommets, and bad params
with a message. It also refuses a hem or inset that reaches the far edge of
the banner when one of two opposite sides is chosen, or that meets the marks
of the opposite side when both are chosen, an inset of more than half a
chosen side's length, and more than 100000 grommets. The dialog has Preview.

The grommets' distance from the edge is `grommetInset`, so the dialog shows
it as Grommet Inset with its own catalog rows. The new Diameter catalog
rows also translate the Diameter field of the `shape.flare` command dialog,
which had no rows.

Docs: docs/mcp.md (Cutting Tools add-on), docs/target-app-parity.md and
ROADMAP.
2026-11-13 12:49:43 -08:00
dotCooCoo 4dc5c4f719 Live Shapes: object.setLiveShape refuses unknown keys and invalid sides or radius
object.setLiveShape was in params::UNCHECKED, so a key it does not read,
such as `path`, ran the command with the key ignored. Its description
names every key it reads, so it now goes through check_keys like other
commands, for MCP, scripts and command.batch steps. Each of `items` takes
`id` and `corners` alone; another key is refused as
`items[0].corner`, with the nearest accepted key. Saved steps still
replay leniently.

A `sides` that is not a whole number from 3 to 1000 (2.5, -1, 2, 1001)
and a negative `radius` are now errors naming the key, and leave the
document and its undo history as they were; a radius of 0 still
removes rounding. The Transform panel's Sides field takes 3 to 1000,
the Corners dialog's radius field stops at 0, and the panels' corner
radius fields send 0 for a negative value typed in.
2026-11-13 12:46:15 -08:00
dotCooCoo 98d5708df4 Artwork Cleanup: Break Text and Remove Breaks read the text threads once
Break Text checked every thread of the document for each selected type
object to skip threaded frames, and Remove Breaks searched every thread
for each selected object to find its story, so the work grew with the
number of selected objects times the number of threaded frames.

Both commands now index the document's threads once per command: Break
Text tests membership in that index, and Remove Breaks takes the story
from the indexed thread through threads::story_in, the second half of
story_of. Split Text at Cursor, which takes one object, checks it once
as before. The results are unchanged.

Tests: Break Text on two threaded frames and six point type objects
skips the frames unchanged, breaks the rest in place as one undo step
and goes through the threads once; Remove Breaks reads the selected
story as one text and the other objects on their own, going through
the threads once; Split Text at Cursor refuses a threaded frame.
2026-11-13 12:42:39 -08:00
dotCooCoo 804177bddd Offset Path: work out the offsets of several selected paths on several threads
Offset Path took each selected path in turn, so offsetting many paths, or a
group of many paths, used one CPU. It now reads the selected paths first,
works out their offsets on as many threads as the computer has logical CPUs
(one thread on the web and for a single path), and then makes the new
objects one after another in the selection's order on the editing thread.

The new objects, their ids, their stacking, the selection, the messages and
the undo step are the same as before. When several paths fail, the error is
the one for the first of them in the selection's order, and the paths after
a failure that have not started yet are skipped.
2026-11-13 12:41:24 -08:00
dotCooCoo 4276245469 Path Precision Tools: group selected segments and crossings by subpath in one pass
Add Points and Divide into Dashes read each subpath's selected segments
with a range lookup in the path's ordered segment set instead of
filtering the whole set once per subpath. Add Points at Crossings and
Split at Crossings sort a path's crossing places into a list per
subpath once, in the order they come, instead of filtering the whole
list for every subpath. Places on a subpath that no longer exists are
still left out. The results are unchanged.

Tests cover paths with several subpaths, an empty subpath, an
untouched subpath and selection on the last subpath, for all four
commands, plus the anchor fallback and the grouping of out-of-order
crossing places.
2026-11-13 12:39:13 -08:00
BFlat c9b2119552 Merge pull request #7176 from dotCooCoo/feat/monitor-profile-windows-linux
View: the canvas is drawn through the monitor profile on Windows and X11
2026-11-14 03:24:26 +07:00
BFlat 3ec0cdec71 Merge pull request #7245 from dotCooCoo/fix/not-files-real-params
Plug-ins: file-key exemptions name only params a command takes
2026-11-14 03:24:13 +07:00
BFlat f05cb5e7a4 Merge pull request #7223 from dotCooCoo/fix/inline-graphics-in-symbol-art
Type: inline graphics in symbol, mask and pattern art follow their symbols
2026-11-14 03:24:03 +07:00
BFlat cc0413ee34 Merge pull request #7217 from dotCooCoo/fix/brush-repeated-names-numbered
Brushes: repeated brush names are numbered when a document loads
2026-11-14 03:23:54 +07:00
BFlat 50084b789f Merge pull request #7243 from dotCooCoo/fix/smooth-segment-under-drag
Smooth: a drag along a segment smooths it when its anchors are out of reach
2026-11-14 03:23:45 +07:00
BFlat 9107dad274 Merge pull request #7194 from dotCooCoo/fix/item-3829
Envelopes: a partly transparent image keeps its alpha where its warp pieces overlap
2026-11-14 03:23:28 +07:00
BFlat e8aa3ae34c Merge pull request #7249 from dotCooCoo/fix/cleanup-selection-one-pass
Artwork Cleanup: update the selected objects in one pass after each fix
2026-11-14 03:22:08 +07:00
BFlat 00fba7ae24 Merge pull request #7121 from dotCooCoo/fix/graph-series-paint-other-commands
Graphs: stroke, gradient and recolor commands update a series' stored paint
2026-11-14 03:21:58 +07:00
dotCooCoo 89ccf89461 Document Info: spot colors from gradient stops that color nothing are not listed
Spot Color Objects listed every spot color linked from a gradient's stops
once any stop was above opacity 0. A spot stop at opacity 0 between other
clear stops, at the end of the ramp after a clear stop, or meeting an
opaque stop only at one offset was listed though it prints no ink, and so
was an opaque stop out of order whose pairs have no width.

A stop now counts when it bounds a stretch of some width that the
gradient sampler mixes and either stop of that stretch is above opacity
0, or when it is the first or last stop and above opacity 0, since those
color the offsets before and after the others. Stops in any order are
judged by the stretches the sampler uses. A freeform gradient keeps the
old rule, since it draws from its points. Type runs follow the same rule.
The document.info params text, docs/mcp.md and the progress line
describe it.
2026-11-13 12:11:20 -08:00
dotCooCoo f42ec13bd2 PDF: the long-page warning names the in-points option
In a PDF without a standard, the warning for a page longer than 14,400 pt
written with a UserUnit above 1 now ends with "turn on Write pages longer
than 200 in at full size in points". The warning already said that apps
ignoring UserUnit show such a page smaller; it now names the Save PDF
option that writes the page at its size in points instead. PDF/A files,
which keep the UserUnit with the option on, and .ai saves, whose options
do not have it, keep the warning as it was.

The longer message has a row in every complete-message catalog. The
params text of document.exportPdf, docs/mcp.md and the evidence line in
docs/target-app-parity.md describe it.
2026-11-13 12:08:49 -08:00
dotCooCoo 291afd8aa6 Free Distort: the bounding box turns with the warped art
object.distort warped the anchors of paths, groups, compound paths and
clip groups but left their bounding-box angle as it was, so a rotated
path kept its old box after a warp and an upright one stayed square to
the page however far the warp tilted it. Text, images and symbol
instances already took the angle of the warp's affine approximation.

- Each path, group, compound path and clip group now takes the angle
  the warp turns its x axis to at its own center before the warp, the
  members of a warped group and the art of a linked opacity mask each
  at their own. An unlinked mask's art keeps its angle. Mirroring keeps
  the nearer angle and a collapsing warp keeps the old one. Reset
  Bounding Box squares the boxes again.
- Each subtree's bounds are measured once while it is warped.
- Corners that equal the corners of the box being warped change
  nothing and add no undo step.

Docs: object.distort params text, docs/mcp.md, docs/target-app-parity.md.
2026-11-13 12:07:21 -08:00
dotCooCoo fdedfb0e59 Merge main into feat/warichu 2026-11-13 11:59:22 -08:00
dotCooCoo d9c1f4335e Threaded Text: thread edits find frames and threads in one pass
Several threaded-text paths in threads.rs searched a list once for each
frame, so their work grew with the product of the frame count and the
list length:

- leave_threads (Create Outlines, Expand Appearance, Rasterize, Flatten
  Transparency, symbol replacement, opening with text as outlines)
  searched the threads for each frame that stayed and ran continued over
  that frame's whole thread again, about n^2 frame visits for a thread of
  n frames. It now marks each changed thread with one continued call,
  and keeps the frames that left in a map instead of a list.
- has_thread_selection (the availability of Release Selection and Remove
  Threading), with_threads, keep_shown_text and cut_to_shown, release and
  remove test ids against a hash set instead of a list.
- create finds the thread of each selected frame from a table built
  once, and each frame still takes in the first thread not yet merged
  that lists it.
- release writes each thread back at its own index instead of searching
  the thread list for an equal thread.

Marks, thread membership, frame order, text and overflow are unchanged.

A 256-frame thread that loses one frame visited 65,025 frames before
this change and at most 256 now. Six tests on many threads count the set
lookups: with a list in place of the set they make 67,650 to 258,226.
Another test compares the marks with a scan of the frames before each
one.
2026-11-13 11:40:39 -08:00
BFlat a105e14aef Merge pull request #7220 from dotCooCoo/feat/image-indexed-color
Edit Colors: convert embedded images to indexed color
2026-11-14 02:40:36 +07:00
BFlat 5f8e4c1913 Merge pull request #7240 from dotCooCoo/feat/smooth-reach-overlay
Smooth tool: the drag shows the band the tool smooths
2026-11-14 02:40:24 +07:00
BFlat 90a9e00fdd Merge pull request #7205 from dotCooCoo/fix/svg-interactivity-hide-script-paths
SVG Interactivity: replies name JavaScript files outside the read roots by file name
2026-11-14 02:40:06 +07:00
BFlat 3a7295e588 Merge pull request #7201 from dotCooCoo/fix/revert-drops-pending-place-reads
Place: File › Revert drops reads started for the content it discards
2026-11-14 02:39:57 +07:00
BFlat 58dd9e8908 Merge pull request #7230 from dotCooCoo/fix/docinfo-hidden-object-paints
Document Info: hidden objects and objects at an opacity of 0 list no brushes or paints
2026-11-14 02:39:39 +07:00
BFlat 4dc9c5c5d3 Merge pull request #7218 from dotCooCoo/fix/batch-fit-journal-index
Artboards: a batch's Fit to Artwork Bounds step journals the artboard it fits
2026-11-14 02:39:07 +07:00
BFlat 1e385ba06e Merge pull request #7210 from dotCooCoo/fix/envelope-empty-type-box
Envelope: type that draws nothing adds no box next to other content
2026-11-14 02:38:27 +07:00
BFlat b004977dba Merge pull request #7202 from dotCooCoo/fix/raster-effects-skipped-transform-budget
Effects: raster effects before a Transform effect past the budget stay with the art
2026-11-14 02:38:10 +07:00
dotCooCoo b6e3e96db3 Brushes: tests expect the first brush of a repeated name
With repeated names numbered when a brush library is read, a stroke that
names a repeated brush draws the first brush of that name, and a later
brush of the name is read as "Name 2". Three tests built a library with a
repeated name and expected the later brush:

- Document Info lists a name two stored brushes share with the first
  brush's type (art), not the last one's.
- The two render memory tests add their own `Dots` scatter brush beside
  the default library's `Dots`. They now remove the default `Dots` first,
  so the stroke draws their brush.
- The .ai writer test that leaves out a Pattern brush "X" gets the
  Calligraphic brush after it back as "X 2", the name it had in the
  document.
2026-11-13 11:23:40 -08:00
dotCooCoo 05500f7ddb Artwork Cleanup: update the selected objects in one pass after each fix
With the selection as its scope, a fix that removes or unwraps objects
(Needless Groups, Stray Points, Empty Text and the others) kept the list
of selected objects current by searching it and splicing it once for each
object it changed. Removing many selected objects above a long run of
others took time that grew with the square of their number.

The list is now rebuilt in one walk after each pass: each changed object
gives way to what replaces it, in its place, and the others keep their
order. The objects found, the counts, the document and the undo step are
the same as before.

Empty Artboards deleted each empty artboard with its own Vec::remove and
walked every ruler guide again for each one. It now drops the empty
artboards in one walk and deletes their guides in one more. The same
artboards and guides are deleted as before.

Tests cover the order after unwrapping nested and empty groups, Fix All
reaching an unwrapped member on a later round, the rebuild itself, the
number of objects looked at for 150 and 300 removals, and the number of
walks over the artboards and guides when 40 empty artboards are deleted.
2026-11-13 11:20:05 -08:00
dotCooCoo b7dbb4b59c Merge main into fix/graph-series-paint-other-commands 2026-11-13 11:18:16 -08:00
dotCooCoo af91a37669 Merge main into feat/monitor-profile-windows-linux 2026-11-13 11:12:25 -08:00
BFlat 5c1f206213 Merge pull request #7207 from dotCooCoo/fix/engine-test-temp-folders
Tests: engine tests remove the temp folders they make
2026-11-14 01:54:55 +07:00
BFlat ca8e49e9e2 Merge pull request #7158 from dotCooCoo/fix/export-unused-option-warning
Export: warnings name an option the chosen format doesn't read
2026-11-14 01:54:29 +07:00
BFlat 820b82b8d3 Merge pull request #7188 from dotCooCoo/fix/color-gamut-cache-profile
Color: the out-of-gamut warning updates after a profile change
2026-11-14 01:54:13 +07:00
BFlat a8db64db47 Merge pull request #7208 from dotCooCoo/feat/live-paint-selection-cursors
Live Paint Selection: cursor shows a face, an edge or nothing to select
2026-11-14 01:54:04 +07:00
BFlat 5010861362 Merge pull request #7199 from dotCooCoo/fix/close-background-tab-keeps-front
Documents: closing a background tab keeps the document in front
2026-11-14 01:53:56 +07:00
BFlat 9bad58b73e Merge pull request #7215 from dotCooCoo/fix/new-profile-errors-hide-path
New Document profiles: errors name the profile file by its file name outside the read roots
2026-11-14 01:53:47 +07:00