Commit Graph
4562 Commits
Author SHA1 Message Date
dotCooCoo ead3cd1ebd Variables: Import Data Sets reads UTF-16 CSV files without a byte-order mark
A CSV file that holds NUL bytes and has no byte-order mark was refused.
It is now read as UTF-16 when at least half of its two-byte units hold
a NUL byte, in the byte order that reads a line break. A file with fewer
NUL bytes (UTF-8 or Windows-1252 text with stray NUL bytes, or UTF-16
text made mostly of characters above U+00FF) is still refused, and so is
a file that reads a line break in both byte orders, has an odd length,
isn't valid UTF-16 in that order or holds a NUL character. Refused files
get the same message as before.
2026-11-13 16:34:43 -08:00
dotCooCoo c88da287e7 Graphs: the label gradient test places the gradient before the redraw drops its placement
The test now sets the edited label's gradient vector and aspect with paint.setGradientGeom and checks that the gradient is placed before the graph redraws, so the later check that the kept gradient has no placement fails if the redraw keeps it.
2026-11-13 16:34:27 -08:00
BFlat 92d9604726 Merge pull request #7264 from dotCooCoo/fix/confined-svg-export-script-paths
MCP: SVG export by a confined caller refuses script links outside its read roots
2026-11-14 07:34:26 +07:00
BFlat c79a21c8b7 Merge pull request #7320 from dotCooCoo/fix/graph-design-art-appearance
Graphs: Appearance and effect commands leave a design's art in a series out
2026-11-14 07:33:59 +07:00
BFlat 8009ea8e6d Merge pull request #7313 from dotCooCoo/fix/background-document-bounds
Type: a document replaced in the back measures its type when it is replaced
2026-11-14 07:33:42 +07:00
BFlat c6d580af4b Merge pull request #7280 from dotCooCoo/fix/colored-solid-faces-one-order-limit
3D: solid-color fills and strokes under colored lights draw their faces in one depth order
2026-11-14 07:33:34 +07:00
dotCooCoo 0cb2b435a8 Live Paint: a Live Paint Selection double-click selects the connected faces or edges
A double-click with the Live Paint Selection tool on a face now selects
that face and the faces of its group that no stroked edge parts from it:
the selection floods across edges that are hidden or draw no stroke, and
hidden faces are left out. On an edge (Alt-double-click, or a
double-click with Select Fills off) it selects the edge and the edges of
its group joined to it end to end that draw with the same stroke paint.
With Shift held, the faces or edges reached leave the selection or join
it. The tool options, locks and isolation mode apply as they do to a
click.

The reach is computed by two read-only functions in the builder lookup
module, flood_faces and connected_edges, so the Live Paint commands can
use the same rules. An edge's stroke paint is the paint of its topmost
stroke that draws. Face outlines are matched to edges within the planar
map's precision, and edges join only at the ends of their subpaths.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 16:23:32 -08:00
dotCooCoo cc8cdd62fd PDF: knockout masks keep symbol and brush budgets for visible art
The copies of symbol instances drawn in a knockout group's soft masks
counted toward the objects the export's visible symbol art may hold, so
overlapping instances could use that limit up and leave later visible
instances out. Mask copies now count toward a limit of their own. When a
group's masks would leave out symbol art because that limit runs out,
the masks are discarded and the group is written as a PDF knockout
group, with a warning.

The object count that decides whether a group is drawn through masks
left out the art a brushed stroke expands into. A group in which a mask
would draw a brushed stroke is now written as a PDF knockout group, with
its own warning. The search follows what the export draws: visible
members, opacity masks that are on, symbol and inline graphic art, live
objects and placed documents as evaluated, and pattern tiles, and it
skips strokes without paint, width or a known brush.

A knockout group whose elements need no masks is written as a PDF
knockout group without a warning, so the two engine PDF tests that
expected the old warning now check that case and the brushed one.
2026-11-13 16:20:19 -08:00
dotCooCoo d84e81aec4 Live Paint: params text names its parameter on every line
livePaint.fill's description listed "not with points" as its own entry, which
the params parser reads as a parameter name, so every_description_parses
failed. The note now sits inside the samePaint value's parentheses and says
that "connected" and "samePaint" are refused with points, as lp_fill does.
2026-11-13 16:15:22 -08:00
dotCooCoo 5b4e613d0b 3D: colored-light facets close their shared edges over an underlay
Under a colored light, Inflate's facets were antialiased one by one.
Where two facets met, their edge pixels added up to less than full
coverage. The solid-color composite then showed its black channel
backdrop there, so internal edges drew darker at 1:1, and the per-facet
gradient art let the background through.

- The solid-color composite draws, in each channel, an underlay of the
  facets with 2 pt round seams between the black backdrop and the
  facets themselves.
- The per-facet routes put each run of consecutive faces of one surface
  over an underlay of its own faces, clipped to the union of the faces
  and their seams. Runs that carry a gradient's opacity mask are sealed
  with that mask. A face clip's footprint keeps only the outer contours
  of its seam stroke.
- The paint-copy allowance for cut faces counts 12 copies per facet.

At 1:1, inside the surface each color channel of an opaque fill is
within 3 levels of the same art drawn at 3x and averaged down, and
every pixel is opaque. Before, 2,267 channels differed by up to 10
levels on the solid Inflate, and 1,088 pixels of the gradient Inflate
were below full alpha. Under white light, the rendered pixels, the SVG
text, the PDF read back and the expanded art of Inflate, Extrude,
Rotate and Revolve are the same as before.

Docs: docs/three-d.md (Seams), docs/target-app-parity.md.
2026-11-13 16:03:37 -08:00
dotCooCoo 187032fbb2 Path Precision Tools: Corner commands refuse unknown item keys
addon.pathtools.cornerSet and cornerRemove now refuse a key other than
id and anchors in an `items` member, with the standard unknown-parameter
error, instead of ignoring it. A misspelled `anchor` used to fall back
to the request's `anchors`, else the path's direct-selected anchors,
else every corner of the path. They also refuse `items` given together
with `ids`, which `items` replaces. Saved steps played with replay
still run as recorded.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 16:03:08 -08:00
dotCooCoo 89ee320591 Graphs: a series group also keeps its isolation and knockout
transparency.set and object.setProps with isolate, knockout or
knockoutShape on a series group now store them for the series
(groupIsolate, groupKnockout, groupKnockoutShape), and the series group
has them again when the graph is drawn again. Randomize Opacity, Sort
Objects and Global Edit store the opacity they give a series group, so
a redraw no longer puts back an older stored value over it.

A move or copy of an appearance onto a series group whose stored values
differ from its own now counts as a change, so the edit is kept. Tests
cover graphicStyle.redefine of a style linked to a series group.
2026-11-13 16:02:54 -08:00
dotCooCoo c1572562de Merge main into fix/file-folder-param-classification 2026-11-13 15:55:47 -08:00
dotCooCoo d30831bd5a Merge main into feat/live-paint-cursor-swatch-preview 2026-11-13 15:54:47 -08:00
dotCooCoo 252ff7782a Merge main into fix/graph-series-gradient-per-mark
# Conflicts:
#	docs/mcp.md
2026-11-13 15:49:27 -08:00
dotCooCoo 76d99d6afb Merge main into fix/ai-graphs-keep-data 2026-11-13 15:46:07 -08:00
dotCooCoo d7da446cd7 Text Wrap: Offset is a distance in the document's units
Text Wrap Options showed Offset as a plain number in points. It is now a
distance field in the document's General unit: a bare number is in that
unit, a typed unit is read as typed, and OK sends points.

The field's range is the one object.textWrap.options keeps an Offset to,
-1000 to 1000 pt, read from the engine's TEXT_WRAP_OFFSET_RANGE. A value
typed outside it, or text that doesn't read, is refused: the field
flashes and the dialog stays open. The ends as the field shows them in
another unit (352.7778 mm for 1000 pt) read back as the ends.

In the dialogs drawn by the shared dialog window, a click on OK no
longer confirms the dialog when the field it takes the focus from
refuses its text, as Enter already did not. OK pressed from the keyboard
after such a click still confirms.
2026-11-13 15:39:15 -08:00
dotCooCoo c477db87ba Live Paint: an edit that changes nothing records no undo step
A Live Paint Bucket click or drag over faces that already have the
fill, a stroke on an edge that already has the paint and width, Gap
Options with the options the groups have and no gap to close, and Make
or Merge given one Live Paint group alone now leave the document, the
undo and redo steps and the saved state as they are, and reply as
usual. Such a fill or stroke also leaves the recent colors as they are.

Gap Options with Close Gaps With Paths makes a group's faces again only
when it adds a path or what paint stops at changes, so the faces keep
their ids otherwise.
2026-11-13 15:39:04 -08:00
dotCooCoo a8a0316da7 Type: Create Outlines keeps the type object's own fills and strokes
Type > Create Outlines dropped the fills and strokes of the type object
itself (the items around the Appearance panel's Characters row), so the
outlines no longer drew what the type drew. On type without effects of
its own, Object > Expand outlines the type with the same command, so it
lost them before Fill or Stroke could expand them.

- Create Outlines puts those items on paths of the type's whole outline:
  the items below the Characters row on one path under the glyph paths,
  the others on one path over them, in their order. Unplaced gradients
  are fixed to the type's layout box, as the canvas paints them.
- object.expand.info reports the type object's own strokes, pattern
  fills and gradient fills, measuring a gradient on the whole outline.
  It counts them only when the type has an outline: type of only spaces
  reports none of them.
- The stroke flag of object.expand.info counts every stroke Outline
  Stroke outlines, not only the topmost one, for paths as well as type.
  Expand skipped Stroke for a visible stroke under a stroke with no
  paint.

Create Outlines still does not carry over the type object's own effects.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-13 15:39:04 -08:00
dotCooCoo 06031b7c9d Graphs: a series mark's gradient written unchanged leaves the kept gradient
A command that writes a series mark's paint compared the mark's gradient
with the series' kept gradient placed on the mark's box exactly. After
the mark was moved, the two differed by rounding, so the graph stored a
slightly different gradient and recorded an undo step. The vector,
focal point and freeform points now match within 1e-6 pt.
2026-11-13 15:35:01 -08:00
dotCooCoo 3429bbc76a Save, Export for Screens: warnings name an option the file doesn't read
The save commands' warnings now follow an export's .ai rules: a legacy
.ai file (aiVersion "7") reads only its own options, and the other .ai
and .ait files don't read outlineText, hiddenObjects, transparentImages
or preserveEditing. They name any option given beside the path that the
file doesn't read, not only compress, version, preview and precision,
and any such key in options. The bytes a save writes don't change.

The Save Options dialog sends a .ai save only the options the chosen
version reads, so a save from the dialog gets none of these warnings.
When the document's own file is already a .ai, the .ai options that a
save doesn't give and its file doesn't read keep their remembered
values, so the dialog still shows them; the save doesn't write them, and
a retry in another folder doesn't send them.

Export for Screens and Export Assets already accepted a key in a format
row or in a format's settings that only another format takes. Their
warnings now name it as given (formats[0].quality, settings.png.quality).
A key in a format's settings is named only when no row of that format,
as the export writes it, reads it. The options of an exportPreset, and
for assets.export the settings the document keeps, get no warning.

Docs: params text of document.save, document.exportForScreens and
assets.export; docs/mcp.md; docs/target-app-parity.md.
2026-11-13 15:34:25 -08:00
dotCooCoo f6d90063fc Merge main into fix/graph-label-paint-recolor 2026-11-13 15:33:30 -08:00
dotCooCoo af48843ff8 Tests: Image Trace's top-level paths key names a file
The location sweep listed imageTrace.make's `paths` as a document part
at the top of its params, so catalog_path_keys_match_location_policy
failed: Image Trace's path fidelity `paths` is exempt only inside its
tracing settings, and a top-level `paths` names a file, as it already
does for imageTrace.makeAndExpand.
2026-11-13 15:31:05 -08:00
dotCooCoo 9594d39bd5 Edit Colors: no undo step when a recolor changes nothing
Invert Colors, the Convert to commands, Saturate, Adjust Color Balance,
the Blend commands and Recolor Artwork recorded an undo step even when
they changed no color, for example Invert Colors with fills and strokes
off, Saturate at 0 or a second Blend Front to Back. The empty step also
cleared the redo steps. paint.invert and paint.complement on whole
objects, Apply as Filter (addon.color.filter) and Apply Nearest Swatch
run the same pass and did the same.

These commands now run on a copy of the document and record the step
only when something changed. Convert to Indexed Color uses the same
helper. Recolor Artwork also compares the documents when it counted no
recolored paint, so rewriting a color group's swatches still records
its step.
2026-11-13 15:29:04 -08:00
dotCooCoo 6829dc02b8 Merge main into feat/smooth-cursor-without-path 2026-11-13 15:27:54 -08:00
BFlat 0b29b2b1f6 Merge pull request #7267 from dotCooCoo/fix/doc-info-values-translated
Document Info: values show in the interface language
2026-11-14 06:27:27 +07:00
BFlat 4a0f4b924e Merge pull request #7305 from dotCooCoo/fix/one-anchor-edit-no-remeasure
Path: anchor edits on a long selected path skip measuring it for the canvas check
2026-11-14 06:27:14 +07:00
BFlat 26de97ee06 Merge pull request #7299 from dotCooCoo/fix/type-commands-thread-lookup
Type: thread checks look up the threads once per command
2026-11-14 06:26:56 +07:00
BFlat efba8bfd89 Merge pull request #7295 from dotCooCoo/fix/negative-wrap-offset
Text Wrap: a negative Offset lets text run into the wrap object
2026-11-14 06:26:39 +07:00
BFlat be1810e0c9 Merge pull request #7263 from dotCooCoo/feat/smooth-free-end-tangent
Smooth tool: an open path's end turns with the smoothing when a drag reaches it
2026-11-14 06:26:31 +07:00
BFlat 6fa41d4f76 Merge pull request #7304 from dotCooCoo/fix/graph-series-object-opacity-other-commands
Graphs: Global Edit, Randomize Opacity and Sort Objects keep what they give a series
2026-11-14 06:26:22 +07:00
dotCooCoo 4129587abe Graphs: recolor, stroke and gradient edits on labels are kept for their kind
A recolor of a graph (Invert and Complement on the proxy, the Edit Colors
commands, Recolor Artwork, the color filters, Apply Nearest Swatch and the
ink limit of Print Ink Fixes) stored the new paint of the series marks but
not of the labels. stroke.set, the stroke weight steps, the gradient edits,
Swap Fill and Stroke and Default Fill and Stroke stored nothing when they
changed a label's characters. When the graph was drawn again, the labels
went back to their stored style.

- These commands now store, for every label of the kind they reach, the
  label's object rows when they change, and the fill, stroke, stroke weight,
  caps, joins, miter limit, dashes and stroke alignment of its characters
  when all of them show the change alike.
- A gradient is stored without its placement, and each label fits it to its
  own box. A stroke weight and dashes are stored as a scaled label shows
  them.
- When labels of one kind change differently, the frontmost one sets what
  the kind keeps.
- On a pie with Legends in Wedges, the legend labels' fill is stored only
  when every legend label reached ends up with the same fill, so a recolor
  of the whole pie leaves them black or white by their wedge.
- Axis lines, ticks and radar rings are not stored and are drawn again with
  their default paint.

Docs: docs/mcp.md, the commands' params text, docs/target-app-parity.md.
2026-11-13 15:22:55 -08:00
dotCooCoo d132c330d0 Merge #7304 (fix/graph-series-object-opacity-other-commands) into fix/graph-series-group-opacity 2026-11-13 15:18:09 -08:00
dotCooCoo 9d3717b0c0 Merge main into fix/colored-solid-faces-one-order-limit 2026-11-13 15:16:25 -08:00
dotCooCoo 6280c90b0b Tests: split the file-key depth test
a_file_key_counts_at_any_depth grew past clippy's cognitive complexity
limit (34 of 30) and stopped the gate's clippy step. Its Image Trace
`paths` cases, its custom arrowhead cases and its deep-nesting case now
run as three tests of their own, with the same assertions.
2026-11-13 15:15:58 -08:00
dotCooCoo 5d543c906a Merge main into fix/item-4459 2026-11-13 15:13:10 -08:00
dotCooCoo 419698529c Merge main into fix/graph-series-gradient-per-mark
# Conflicts:
#	crates/engine/src/cmd/gradient.rs
#	crates/engine/src/cmd/graph.rs
#	docs/target-app-parity.md
2026-11-13 14:54:23 -08:00
dotCooCoo 2ae9a8f0f0 Graphs: an opacity or blend mode set on a series group is kept
transparency.set and object.setProps with an object opacity or blend mode,
given a series group picked with Group Selection, set the group's own
opacity and blend mode, and the graph drew its series groups again
without them. The graph now stores them for the series (groupOpacity and
groupBlend) and puts them back on the series group when it is drawn
again. Commands that clear the group's own opacity and blend mode
(appearance.clear, graphicStyle.apply, appearance.transfer onto the
group, graphicStyle.redefine of a linked group) remove what the graph
stores.
2026-11-13 14:52:08 -08:00
dotCooCoo 0d26724a7c Graphs: Appearance and effect commands leave a design's art in a series out
A graph draws the art of a column or marker design inside a series again
from the stored design. Rows, effects and graphic styles that a command
gave that art were lost the next time the graph's data, type or size
changed. The paint commands already left the art out.

- The appearance.* row and whole-appearance commands, effect.apply,
  appearance.addEffect and the effect.* commands that edit, move or
  remove an effect, threeD.mapGraphic, graphicStyle.apply,
  graphicStyle.addFromLibrary with apply, transparency.set with a fill
  or stroke item, and appearance.copyFrom (as the target) leave out any
  object inside a series group other than a path directly in it, given
  by id, selected or reached through target: "contents". They edit the
  other objects given, and a command whose objects are all design art
  is an error.
- appearance.transfer with design art as its target, or as its source
  without copy, is an error.
- A Pathfinder effect no longer groups selected design art.
- The paint commands given a design's copy with a fill or stroke item
  leave it out as well.

docs/mcp.md states the rule, and the Graphs line in
docs/target-app-parity.md names it.
2026-11-13 14:51:31 -08:00
BFlat 517b725be5 Merge pull request #7089 from dotCooCoo/fix/smooth-fidelity-screen-pixels
Smooth: Fidelity is in screen pixels, so a drag smooths as much at any zoom
2026-11-14 05:42:42 +07:00
BFlat 64c6387b84 Merge pull request #7260 from dotCooCoo/fix/tool-options-refuse-invalid
Live Paint and Shape Builder: tool.setOption refuses invalid options
2026-11-14 05:42:33 +07:00
BFlat 2fe70b2483 Merge pull request #7258 from dotCooCoo/fix/graph-label-appearance-kept
Graphs: Appearance edits on a label are kept for every label of its kind
2026-11-14 05:42:08 +07:00
BFlat c36aaec289 Merge pull request #7296 from dotCooCoo/fix/delete-artboards-one-pass
Artboards: delete several artboards and their guides in one pass
2026-11-14 05:41:59 +07:00
BFlat 1b039350a3 Merge pull request #7297 from dotCooCoo/fix/global-edit-enabled-for-supported-groups
Global Edit: Start Global Edit is offered only for a group it can start on
2026-11-14 05:41:50 +07:00
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
dotCooCoo ac5e9c6618 Graphs: a kept gradient takes the same place on every mark of its series
A graph series kept a gradient with the document coordinates it had on
the one mark it was stored from, and gave that paint unchanged to every
mark when the graph was drawn again. The other marks showed the gradient
placed over the edited mark, and the placement stayed behind when the
data or the graph moved the marks.

The graph now stores a gradient relative to the unit box of the mark it
comes from (its geometric bounds, or its stroke's box for a stroke).
Each path of the series, the legend swatch and a Filled Lines band
included, takes it on its own box. Every store route does this:
paint.setFill and paint.setStroke, the Eyedropper, graphic styles,
Appearance commands, paint.setGradientGeom and the freeform point
edits. A command that writes a mark's paint as the graph drew it leaves
the stored gradient as it is and records no undo step.

The series paints save this as fillUnitBox and strokeUnitBox. A
gradient in a file saved without these flags is drawn where it was
stored. So is a gradient stored from a path with no width or height.

Docs: docs/mcp.md, the paint.setGradientGeom params text and the Graphs
line in docs/target-app-parity.md.
2026-11-13 14:41:12 -08: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