Commit Graph
884 Commits
Author SHA1 Message Date
dotCooCoo b9f4ea29f7 Actions: Save Actions… and Load Actions… save a set to a file and load it back
Squashed from #5192 onto main.
2026-11-02 14:32:55 +07:00
dotCooCoo 9c120c735e Keyboard Shortcuts: commands a key cannot run without a value are not listed
Edit > Keyboard Shortcuts listed commands whose params are all optional
but that do nothing or fail when a key runs them with none, such as
Transparency (transparency.set) and Opacity Mask Options
(transparency.setOpacityMask). These are now left out while they have
no shortcut, along with Align and Distribute, Delete Swatch, Move
Swatch, Spot Options, Adjust Color Balance, the library and data set
loaders, stroke.set, layer.setProps and menu queries that no menu item
runs. A key saved for one of them before keeps it listed so it can be
cleared.

A shortcut for a command whose menu bar items all give it the same
params now runs what a click on that item runs, so Clean Up..., Print
Ink Fixes... and Find and Replace... open their dialogs.
2026-11-02 14:32:55 +07:00
dotCooCoo 82806d1441 Fonts: Find Font and Replace Missing Fonts reach the type in symbols
Exports warn about type in a symbol's art that is set in a missing
font and point to Find Font, but Find Font and Replace Missing Fonts
only looked at the type on the layers.

- text.fonts lists the fonts of the type in the document's symbols too;
  with selectionOnly, of the symbols the selected instances show.
- select.font (Find) also selects the instances of the symbols whose
  art uses the font in type that is not hidden or locked.
- text.replaceFont changes that type in the symbols in scope, so every
  instance follows. Type hidden or locked in a symbol's art, and the
  type of symbols that only a selected hidden or locked instance shows,
  keeps its font and is counted in skipped.
- addon.cleanup.replaceFonts replaces missing fonts in symbols' type.

While a symbol is edited, its type is taken from the editing layer
only, so it is counted and changed once.
2026-11-02 14:32:55 +07:00
dotCooCoo e8cd6c173a Variables: Save Variable Library… writes the variables and data sets as an XML library
Squashed from #5147 onto main.
2026-11-02 14:32:52 +07:00
dotCooCoo 642df312b3 Actions: the panel menu's Action Options… renames the selected action
Action Options… opens the New Set and Set Options dialog, headed Action
Options, with the selected action's name; OK renames the action and keeps
its steps. The new command ui.action.rename {set, action, name} does the
rename and refuses an index past the last set or action or an empty name.

Part of #1629.
2026-11-02 14:32:50 +07:00
Brandon Thomas cfb30d8139 Merge pull request #5214 from dotCooCoo/feat/recolor-global-adjust
Recolor: the Global Adjust sliders change every color at once
2026-11-02 00:34:59 -05:00
Brandon Thomas 6daefef735 Merge pull request #5212 from dotCooCoo/feat/document-info-save-category
Document Info: Save writes the category the panel shows
2026-11-02 00:34:54 -05:00
Brandon Thomas c4a394c536 Merge pull request #5203 from dotCooCoo/fix/empty-text-clip-kept
Artwork Cleanup: Empty Text and Off-Artboard Art leave type that clips art
2026-11-02 00:34:49 -05:00
Brandon Thomas 9269cb7cf3 Merge pull request #5197 from dotCooCoo/fix/cleanup-leaves-hidden-locked-art
Artwork Cleanup: Simplify Anchors and Deep Ungroup leave hidden and locked art alone
2026-11-02 00:34:43 -05:00
dotCooCoo ab0072a39e Variables: Edit Data Set's Name field renames the data set
Squashed from #5121 onto main.
2026-11-02 12:04:22 +07:00
dotCooCoo 899a4c2bcb Variables: Import Data Sets reads CSV files that are not UTF-8 as Windows-1252
Squashed from #5116 onto main.
2026-11-02 12:04:21 +07:00
dotCooCoo 0af8096364 Symbols: a refused switch to another symbol keeps the current edit, and isolation stays on the editing layer
symbol.edit checks the new placement before it ends the symbol being edited, so a call with an out-of-range x or y fails and leaves the current edit open. While a symbol is edited, object.isolate refuses art outside the editing layer, and object.exitIsolation goes back to the editing layer only from a group inside it; from any other isolation it leaves symbol editing mode.
2026-11-02 12:04:18 +07:00
dotCooCoo 7c1d73d818 Symbols: test the Symbols panel, panel menu and Control bar entries to symbol editing mode
Headless tests cover a double-click on a symbol in Thumbnail View and List View, the panel
menu's Edit Symbol with and without a selected instance, the Control bar's Edit Symbol button,
and the Control bar's Exit Symbol Editing Mode and Exit Isolation Mode buttons inside the
symbol's art. The panel's module comment now says a double-click opens the symbol in symbol
editing mode, and docs/mcp.md lists symbol.edit's ids parameter.
2026-11-02 12:04:18 +07:00
dotCooCoo 94a6917c68 Character Styles: Advanced Character Formats takes scales and baseline shift up to the document limit
Horizontal Scale and Vertical Scale accept any number above 0 and at most 4000000 %, and Baseline Shift accepts -4000000 to 4000000 pt, the limits the document model keeps a character style in. Character Rotation is stored from -180 to 180 degrees, as text.setFormat stores it. A scale outside its limit shows the new message "{label} must be a number above 0 and at most {max}", translated in every catalog that has Basic Character Formats.
2026-11-02 12:04:17 +07:00
dotCooCoo 6e7945773e Character Styles: Style Options sets Advanced Character Formats
Character Style Options and New Character Style get an Advanced
Character Formats section with Horizontal Scale, Vertical Scale,
Baseline Shift, Character Rotation and Language. Empty fields stay
unset, an explicit 100%, 0 degrees or English: USA stays distinct from
unset, and Reset Panel on the section clears only these five. Scales
take 1% to 10000% and baseline shift -1296 pt to 1296 pt, as
text.setRangeStyle does; rotation is stored from -180 to 180 degrees.
Language sets the language the characters are shaped in; hyphenation
and spelling do not change.

A field still as it opened keeps its stored value without the range
check, so a style captured from a scaled type object can be renamed.
The same applies to Basic's Size and Leading minimum.

charStyle.edit keeps a language tag that is not in the list when the
style already has it, so a style read from a file with such a tag can
be edited.
2026-11-02 12:04:16 +07:00
dotCooCoo a6c64e2cf2 Type: choosing Optical kerning clears manual pair values, as Auto does 2026-11-02 12:04:15 +07:00
dotCooCoo 8b8b0c371f Objects on Path: the Rotate All widget turns every attached object about its attachment point
With one Objects on Path group selected, the Selection and Objects on
Path tools show a round handle beyond the top of the group's first
object, joined by a line to its attachment point. Dragging it adds the
angle the pointer turns about that point to the group's Rotate, previewed
while dragging and kept as one undo step on release
(object.objectsOnPath.options). Shift turns in 15 degree steps, Escape
cancels the drag, and the pointer shows the rotate cursor over the
handle.

Docs: docs/target-app-parity.md, docs/mcp.md.
2026-11-02 12:04:07 +07:00
dotCooCoo cb7df83217 Swatch libraries: Patterns › Geometric Patterns adds a pattern with its swatch
Window › Swatch Libraries and the Swatches panel's library menu get a
Patterns submenu with Geometric Patterns, a built-in library of eleven
black pattern swatches drawn in code (dots, stripes, a checkerboard, a
grid, diamonds, triangles, crosses and a zigzag).

- Clicking a pattern in its library panel adds the pattern and its
  swatch to the document and applies it to the active fill or stroke
  (Alt: the other one), as one undo step. A second click reuses the
  swatch; a different pattern under the same name keeps its name and
  the library's pattern comes in numbered.
- swatch.library.list lists the pattern libraries after the gradient
  libraries (category "patterns"), and swatch.library.add brings a
  pattern swatch's pattern as pasting art painted with it does.
- The library panel draws each pattern tile from the library's pattern.
- "Geometric Patterns" is translated in every catalog.
2026-11-02 12:04:02 +07:00
dotCooCoo 65dcc83bf1 Actions: the panel menu's Duplicate copies the selected action or set
The Actions panel menu has Duplicate, between New Set… and Delete. With
an action selected it copies that action to right after it in the same
set; otherwise it copies the current set, with all its actions, to right
after that set. The copy is named after the original with " copy" added
and is selected, and a copied set gets an id of its own. Copies are
saved with the other sets.

ui.actionSet.duplicate {set?: index, action?: index} does the copy and
returns {set, action?}. An index past the last set or action is an
error. A recording into a later set and an open Set Options or Batch
dialog stay on their own set and action.
2026-11-02 12:04:01 +07:00
dotCooCoo 775effdbb3 Live Paint: the Live Paint Bucket highlights an edge thinner than a face
The bucket outlines the face under the pointer at its Width setting and an
edge at half of it (4 px and 2 px by default), as the help page says faces
are highlighted with a thick line and edges with a thin line. The Live
Paint Selection tool keeps one width for faces and edges.
2026-11-02 12:04:00 +07:00
dotCooCoo 11adb715b5 Place: a Word .doc document with too many property runs, paragraphs or properties is refused
A bin table that lists more than 100,000 runs of character or of paragraph
properties is refused while its pages are read. Every paragraph now counts
against the 100,000-paragraph limit, hidden and deleted ones and those in
field instructions included, and so does every line break. The properties
of the styles and of the property runs are read from one budget of 16 MiB,
and a document with more is refused.

Text inside a field's instructions is found with a count of the fields
open in their instructions instead of a search of the open fields for each
character.
2026-11-02 12:03:59 +07:00
dotCooCoo a6db4c9112 Place: a Word .doc style sheet that holds fewer styles than it counts is refused
Stshif.cstd counts the LPStd records of the style sheet and must be less than
0x0FFE. A sheet that ended before cstd records was read as far as it went, and
paragraphs in the missing styles were placed with the document defaults; a
cstd of 0x0FFE or more was cut to 0x0FFE. Both are now refused as a damaged
style sheet.
2026-11-02 12:03:58 +07:00
dotCooCoo ecd184510c Place: Word .doc styles are resolved once and damaged property tables are refused
Each style's formatting is now worked out once, on top of the style it
is based on, and each property run's formatting once, so a document with
many hidden paragraphs or long style chains no longer resolves the
chains again for every paragraph and run. Character styles are applied
as a change to the paragraph's formatting.

A bin table whose file positions do not rise, a property page listed
twice, pages whose runs overlap, and styles based on each other in a
loop are refused as damaged, as MS-DOC requires (PlcBteChpx, ChpxFkp,
StdfBase). Property runs refer to their page instead of copying it.
2026-11-02 12:03:57 +07:00
dotCooCoo f024f5a5f2 Variables: Export Data Sets marks visibility and image columns with # and @
Squashed from #5141 onto main.
2026-11-02 12:03:52 +07:00
dotCooCoo a6ce22da5c Variables: an import or library load that changes nothing adds no undo step
dataset.import and variable.loadLibrary added an undo step, cleared Redo
and marked a saved document modified even when the variables and data
sets came out as they were, such as the same CSV or library loaded
again. Both now build the new variables on a copy and record a step only
when it differs. variable.loadLibrary's params text and docs/mcp.md say
so.
2026-11-02 12:03:45 +07:00
dotCooCoo d0749dde41 Variables: Update Data Set keeps a row's values when every bound object is locked
Squashed from #5126 onto main.
2026-11-02 12:03:41 +07:00
dotCooCoo d9024f9c31 Variables: a command that changes nothing adds no undo step
Squashed from #5130 onto main.
2026-11-02 12:03:41 +07:00
dotCooCoo 06221ce545 Variables: a CSV header that names one variable twice is refused
dataset.import read two header cells that named one variable (City
and City, or Shown and #Shown) into the same column, so each row kept
only the last non-blank cell and the other value was dropped without a
word. The import is now refused with the two column numbers, and the
message has a row in every complete-message catalog.
2026-11-02 12:03:40 +07:00
dotCooCoo 386bfc881e Docs: move the import value error note next to the data set warning 2026-11-02 12:03:39 +07:00
dotCooCoo 660102064f Variables: Import Data Sets reports why a cell was refused
dataset.import replaced the reason for a refused cell with "value for
`X` must match its variable kind", so a visibility value such as
maybe and a graph value over 262,144 bytes gave the same message. The
error now keeps the line and gives the reason, and both messages have
a row in every complete-message catalog.
2026-11-02 12:03:38 +07:00
Brandon Thomas b676c626e0 Merge pull request #5186 from dotCooCoo/fix/legacy-ai-character-rotation
Legacy .ai: type with rotated characters is written as outlines that keep the rotation
2026-11-01 23:45:34 -05:00
Brandon ThomasandClaude Opus 5.5 bce3de36ca Legacy .ai: docs say when the rotated-type warning shows, and record the change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 23:27:52 -05:00
dotCooCoo 7389cc22d6 Document Info: Save writes the category the panel shows
Save… in the Document Info panel menu now writes only the category the
panel shows, with Selection Only applied. With All Categories shown it
writes every category, as before. docInfo.save takes an optional
category (a document.info category id) and passes it to document.info.
2026-11-01 18:18:19 -08:00
dotCooCoo 1ef52dd50e Artwork Cleanup: Simplify Anchors and Deep Ungroup leave hidden and locked art alone
Simplify Anchors (Keep Shape) simplified every path under the selection,
locked and hidden paths and paths in locked or hidden groups included.
It now takes only the paths Document::editable allows, as the health
check and Rejoin Path Pieces already do.

Deep Ungroup took apart hidden and locked groups inside the selection,
so their members became visible or unlocked. A hidden or locked group
now stays whole, a locked or hidden object named in ids is left alone,
and members that are locked or hidden themselves keep that and are not
selected. With no group to take apart, the reply's ids list the
selection, which stays as it was.
2026-11-01 17:36:25 -08:00
dotCooCoo 0567eaffbf Recolor: the Global Adjust sliders change every color at once
The Edit tab's color mode menu offers Global Adjust, with Saturation,
Brightness, Temperature and Luminosity sliders from -100% to 100% that
open at 0%, which changes nothing. One slider changes every recolored
color, each one separately, in its own model; the rows keep their new
colors and the art previews the change. The Edit tab also shows the
color sliders of the selected new color in the other modes.

recolor.apply takes globalAdjust {saturation, brightness, temperature,
luminosity}. Excluded rows and colors no row holds stay as they are, a
rewritten color group takes the same change, and with Limit to Library
each row's new color takes it before it snaps to the library.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
2026-11-01 17:20:56 -08:00
dotCooCoo 880a078053 Legacy .ai: type with rotated characters is written as outlines that keep the rotation
A version 7 text object has no arbitrary-angle character rotation: its
character attributes include baseline shift (Ts), scaling (Tz) and
tracking (Tt), Tv only turns Roman characters 90 degrees in vertical
text, and the positioning operators such as Tm are ignored on reading.
Type with Character Rotation was written as editable text with the
rotation left out, so rotated characters opened upright.

Such type is now written as its glyphs' outlines, the way type 8-bit text
can't hold already is, with the warning "type with rotated characters is
written as outlines, which keep the rotation, and is no longer editable
text". The outlines keep each character's rotation, baseline shift and
scale. Type without rotated characters, and type turned a whole number of
times, is still written as text.

Docs: docs/file-format-parity.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-01 16:35:49 -08:00
dotCooCoo 2adf5a9a61 Symbols: Replace Symbol clears the old symbol's dynamic overrides
An override on a dynamic symbol instance names an object of its symbol's
art by path. Replace Symbol kept every override whose path the new
symbol's art also had, so a change made to one object of the old symbol
was applied to an unrelated object of the new one. An instance given a
different symbol now drops its overrides; an instance replaced with its
own symbol keeps them. Undo brings them back.
2026-11-01 16:24:33 -08:00
dotCooCoo 6024f86da1 Artwork Cleanup: Empty Text and Off-Artboard Art leave type that clips art
A clipping path hides the art outside it. Empty Text removed empty type
that clips a clipping group, and Off-Artboard Art removed type that clips
a layer from off every artboard, so the art they hid showed. Both now
leave type that is a clipping path, as the other kinds already did, also
when the clipping path itself is selected.

Docs: docs/mcp.md (Artwork Cleanup kinds table).
2026-11-01 15:05:54 -08:00
Brandon Thomas 639c9d6ce9 Merge pull request #5113 from dotCooCoo/feat/recolor-color-sliders
Recolor Artwork: color sliders in a chosen color mode edit the selected row's new color
2026-11-01 15:28:03 -05:00
Brandon ThomasandClaude Opus 5.5 872effcf77 Recolor: docs say a map row's model can't be null either
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 15:12:13 -05:00
dotCooCoo be20929877 Variables: a text value on threaded type replaces the whole story
Applying a data set wrote a text value into the bound frame only, and the
thread's reflow then joined it with the other frames' old text. The art no
longer matched the row it had just applied, a captured row applied back lost
text, and a value bound to a later frame deleted the rest of the story.

- A text value bound to any frame of threaded text replaces the whole story,
  which then flows through the frames again. `overflow` names the story's
  last frame.
- Capture Data Set, Update Data Set and the Variables panel's match check
  read such a frame as the whole story, frames joined in thread order.
- When any frame of the story is locked, the text value is skipped and
  counted in `skipped`, and the story is not read.
- Two text variables bound in one story apply in definition order, as for
  two on one object: the one defined last wins.

Docs: docs/mcp.md.
2026-11-01 08:58:35 -08:00
dotCooCoo 9b853004d5 Recolor Artwork: a slider edit changes the color mode of its own row only
The color sliders now mark the row they edit with `model: "new"`
instead of a dialog-wide flag, and `recolor.apply` reads a row's own
`model` before the command's. Rows the sliders did not edit keep each
color's model, so with RGB art and a CMYK color group, a slider edit of
one row no longer turns the other rows' art CMYK. Another edit of the
row's new color, Reset and rows reduced again remove the mark, and
`recolor.randomize` moves it with its new color.
2026-11-01 02:06:46 -08:00
dotCooCoo f242b9caa6 Recolor Artwork: a row's tint stays with its spot color
A wheel drag or a harmony rule on the Edit tab now removes a row's tint
when it gives the row another new color, as the color sliders, the hex
field and the Color Picker already do. With Limit to Library, an edit
that snaps back to the row's own color keeps the tint.

recolor.randomize moves each row's tint with its new color when it
shuffles the order, and removes the tint from a color that saturation
and brightness changes.
2026-11-01 01:55:46 -07:00
dotCooCoo 46cb088eae Recolor Artwork: color sliders edit the selected row's new color in a chosen mode
The Assign tab gets a Color Mode menu (HSB, RGB, CMYK, Lab, and Tint when
the row's new color is a spot color of the Limit to Library library) and
its sliders under the rows. They edit the first selected row's new color
and are disabled with no row selected. Once they have edited a color in a
mode, the preview and OK send recolor.apply with model "new", so the art
takes the color in that mode.

recolor.apply rows take an optional tint (percent, default 100): art linked
to the row's global or spot library swatch takes that share of its tint.
recolor.randomize keeps a row's tint.
2026-10-31 21:42:42 -07:00
bflatastic a76d9215da Fix #4923: Find Nearest Swatch matches colours to a library's swatches
Object > Plug-ins > Color Effects > Find Nearest Swatch... picks a
library (the document's swatches, a built-in library, or a library or
colour book the user saved or loaded) and Fill and Stroke, and lists
each colour of the selection with its three closest swatches and their
CIEDE2000 difference. Apply gives each colour its nearest swatch as one
undo step.

- addon.color.nearestSwatch (a query): the closest swatches of a
  library to each colour of the selection's fills and strokes (solid
  colours and gradient stops, inside groups and type), or to a colour
  given, with deltaE; count 1..50.
- addon.color.applyNearestSwatch: recolours those paints (and gradient
  meshes) to their nearest swatch; global and spot swatches link, and a
  library's swatches the colours take are added to the document first
  (as swatch.library.add adds them), so a spot colour stays spot.
- The library picker Image Trace's Document Library uses moves to the
  Swatches panel module and is shared.
- No colour book data ships: only libraries VectorCraft computes or the
  user loads are searched.
2026-11-01 11:09:11 +07:00
dotCooCoo 7e5e099f8e Export for Screens: a second file with a taken name is numbered instead of skipped
Squashed from #5028 onto main.
2026-11-01 11:09:08 +07:00
dotCooCoo ff036557fb Symbols: Edit Symbol opens the symbol in its own editing mode, and every instance follows each edit
Squashed from #5064 onto main.
2026-11-01 11:09:03 +07:00
dotCooCoo 5716a996ab Type: Touch Type moves a character rotated near 90 degrees across its line
Baseline shift now moves a character in the line's frame, outside
Character Rotation, so a positive shift moves a character up from its
line at any rotation, and the character turns about its shifted pen
position. The Touch Type tool splits a drag into its part along the line
(kerning) and its part up from the line (baseline shift) at every
rotation; before, within about 6 degrees of a quarter turn it dropped
the cross-line part.

Characters that already combine a rotation with a baseline shift are
drawn in a new position. Their stored values do not change.

- text: glyph transform order, PositionedGlyph::pivot (the turning
  point), tab leaders in the same order
- tools: Touch Type splits drags against the line's up axis and measures
  rotate-handle turns about the shifted pen position
- engine: text.touchType description; dy is up from the line
- docs: mcp.md Touch Type paragraph, target-app-parity.md progress line
2026-11-01 11:08:57 +07:00
dotCooCoo 94ef9b87c2 Type: Character panel toggles show off for a mixed selection, and text.setFormat checks on/off values
The Character panel's All Caps, Underline and Strikethrough buttons, and
its menu's All Caps, Underline, Strikethrough, Proportional Metrics,
Tate-chu-yoko and No Break, read the first selected character's style.
With only some characters marked they could show on, and choosing one
turned the attribute off for every character. They now show on only
when every selected character has the attribute, and a click on a mixed
selection turns it on for all, as Small Caps and Standard Vertical Roman
Alignment do. type.underline (Cmd+Shift+U) underlines a mixed selection
the same way. The Character Alignment and Digits submenus check no item
while the selected characters differ.

text.setFormat ignored a value that was not true or false for underline,
strikethrough, allCaps, proportionalMetrics, tateChuYoko,
standardVerticalRoman, noBreak, hyphenate, hyphenateCapitalized and
romanHangingPunctuation. It now refuses it with "`<key>` must be true or
false" and changes nothing, as text.setRangeStyle does.
2026-11-01 11:08:56 +07:00
dotCooCoo 427bce12b0 Character: Font Size can measure the cap height, x-height or ICF box
Squashed from #5026 onto main.
2026-11-01 11:08:55 +07:00