Commit Graph
11530 Commits
Author SHA1 Message Date
dotCooCoo 922c1fcae5 Commands: Edit, guide and slice params of the wrong type are refused
These commands read optional numbers and flags with f64_or and bool_or,
so a value such as "10" or "yes" ran the command with the default. They
now return an error and leave the document, the selection and the undo
history as they were. Omitting a key or giving null behaves as before.

- edit.duplicate, edit.paste, guide.move, object.slice.duplicate: dx, dy
- guide.move: copy
- document.node: summary
- edit.undoGroup.end: cancel
- guide.add: vertical
- guide.select, object.slice.select: toggle
- select.editSaved: delete; newName must be a string
- select.save: name must be a string
- file.close: index must be a non-negative integer
2026-11-11 17:45:42 -08:00
dotCooCoo f3339a47f8 Actions: Insert Menu Item leaves out Image Trace and Pattern, counts recorded steps
Object > Image Trace > Make and Make and Expand, and Object > Pattern >
Make and Edit Pattern, are no longer listed or accepted by
`ui.action.insertMenuItem`. Their menu items do more than run the
command: Image Trace traces off the UI thread with progress and Cancel,
and the Pattern items open the Pattern Options panel. Action playback
runs only the command.

Insert Stop and Insert Menu Item, given a set and action, now count the
steps that the recording in progress adds to that set, so the set as
Stop Playing/Recording leaves it stays within 10,000 steps per action
and 8 MiB as a file.

The command descriptions list every kind of menu item that cannot be
a step.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 17:43:32 -08:00
dotCooCoo 93ccc6fbfa Transform: Align New Objects to Pixel Grid makes new paths pixel-aligned
Document Setup gains alignNewObjectsToPixelGrid. When it is on, every
path or compound path drawn in the document (shape tools, Pen,
Curvature, Pencil, Paintbrush, Blob Brush, grids, flares and add-on
shapes) gets the pixel-aligned property and snaps in the same undo
step. Text, paste, duplicate, Place and import are not affected.

- The Transform panel menu has an Align New Objects to Pixel Grid
  item for the active document.
- New Document's Advanced Options has the checkbox; the Mobile and Web
  presets turn it on, other presets and older saved presets leave it
  off, and file.new, Save Preset and Recent keep it.
- The setting is saved with the document only when it is on.

https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/pixel-preview-mode-for-web-graphics.html

Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md.
2026-11-11 17:41:21 -08:00
dotCooCoo c0e40f576e Keyboard Shortcuts: a plug-in installed while the menus are walked no longer leaves the keymap a row short
The shortcut entries are built from the menus under the plug-in revision
read before the walk. A plug-in installed or removed during the walk left
entries that don't match that revision, while the keymap cached for the
revision was built from the entries before the change, so the catalogue
and the entries differed by a row (the_default_shortcuts_do_not_conflict
failed when a test beside it installed a plug-in).

The menus are now walked again when the revision changes during the walk,
and a snapshot that still doesn't match is not cached.
2026-11-11 17:38:50 -08:00
dotCooCoo 4185a16a0d Type: a line's first word is measured up to a forced line break
A forced line break ends the first word when looking for room, inside a
No Break run too, as the line is set. A soft hyphen just before a forced
line break adds no hyphen to the measured width.
2026-11-11 17:37:26 -08:00
dotCooCoo e9942b69d2 Help: a frame run while a file dialog waits is timed for the hang report
The input hook, the logic and the interface of a frame now mark their
time as timed whatever the activity around them, so a frame the system
runs while an in-line file dialog waits for the user is timed too, and
the dialog's wait is not timed again once the frame ends.
2026-11-11 17:36:11 -08:00
dotCooCoo f9773f2907 EPS: Save As shows Use Artboards, off by default, and the box fits all visible art
Save and Save As as EPS always wrote the chosen artboard as the page and
%%BoundingBox, and the EPS Options hid Use Artboards, so art past the
artboard was left outside the file's bounding box.

The EPS Options of Save As now show Use Artboards with All or Range
under it, unchecked by default. Unchecked, Save writes one file whose
bounding box fits all the visible art, as Export does, without the
artboards' background colors; with no visible art the page is the first
artboard, in its color. Checked with two or more chosen artboards, the
file at the path is the central file of the visible art and each
artboard is also written to <name>_<artboard>.eps beside it. Checked
with one, that artboard is the page of the file at the path, the
behavior Save had since #4293 (mockydevs). The file the document becomes
still carries the whole editable document.

Naming artboards with artboard, artboards or range still turns Use
Artboards on. file.formatOptions now reports it on, with those artboards
as the range, and the Save As dialog opens that way.

Changed default: an EPS saved with Save or Save As, an opened EPS
included, now gets a bounding box around its visible art unless Use
Artboards is checked or the save names artboards; an opened EPS saved
back this way loses the margins its page had. selectedOnly stays an
Export option, and Save refuses it.

Docs: params text of document.save and file.formatOptions, the
useArtboards option text, docs/mcp.md save_file,
docs/file-format-parity.md, docs/target-app-parity.md, and notes on the
earlier EPS Save evidence in docs/gaps.md, docs/roadmap.md and
docs/target-app-parity.md.
2026-11-11 17:35:49 -08:00
dotCooCoo cde367b889 Plug-ins: only an approval records a grant, and installs need one
registry::grant takes a GrantToken, which the engine makes after a
person's approval and when it loads grants.json. registry::install,
install_bytes and folder loads refuse a module that lists permissions no
recorded grant gives this very module (Error::NeedsApproval), and leave
the plug-in under its id and the revision as they were. A folder load's
approve callback only narrows what installs. A module without
permissions, or with an empty list, installs as before.

A folder load in a session with a plug-in store records the store's
grant for each module it approves, so those modules still load.

docs/plugins.md says what the approval does not protect against: native
code linked into the app, and another program running as the same user,
which can edit the Plug-ins folder and its unsigned grants.json.
2026-11-11 17:35:06 -08:00
dotCooCoo 4fb98ceacd EPS export: the thumbnail is made from the preview instead of drawing the document again
With a TIFF preview, each EPS file's PNG thumbnail is now the preview
averaged down to the thumbnail's size and composited on white, so
export and Save draw the page once instead of twice. The thumbnail
keeps the size a 128 px render of the page has; a page that fits in
128 px gets the preview's own pixels. Without a preview the thumbnail
is still drawn.

- Rendered::area_resized averages premultiplied pixels over the area
  under each new pixel, weighting edge pixels by the share they cover,
  and returns an error for a 0 side or a buffer that does not match.
2026-11-11 17:32:07 -08:00
dotCooCoo 386c444ff4 MCP: a readable library id written with LOADED/ keeps its text on Windows
The loaded/ prefix is matched in any case where the ids and paths are.
2026-11-11 17:31:25 -08:00
dotCooCoo 9a4f4555c6 Graphic Styles: applying a style shares one appearance across the objects
graphicStyle.apply and graphicStyle.redefine copied the style's whole
appearance into a new allocation for every target. Targets that take
the style's appearance as stored now share one copy-on-write handle; a
target whose placed gradients are fitted to its own bounds still gets
its own. Editing one object later gives it its own copy. Saved files
and undo values are unchanged.

The undo history's memory estimate counted a shared appearance once
per object; it now counts each appearance allocation once per side.
2026-11-11 17:29:22 -08:00
dotCooCoo 64988aaa49 HP-GL: a .plt file opens when its first instruction is SI, DI or another known one
A file read as HP-GL by its extension no longer has to start with an instruction plot files usually start with. Any instruction the reader knows, or one of another set that a semicolon ends, may come first; the rest of the check still refuses files that are not HP-GL. Telling HP-GL apart by content alone keeps the stricter check.
2026-11-11 17:27:49 -08:00
dotCooCoo 62b6f53533 Color: the panel menu's Show Recent Colors shows or hides the Recent Colors row
The Color panel's menu gets a checked Show Recent Colors item under Show
Options / Hide Options. Unchecking it hides the panel's Recent Colors row;
checking it shows the row again. Applied colors still join the list while
the row is hidden, Hide Options hides the row either way, and the Swatches
panel keeps its own row.

The setting is on by default and is kept with the preferences
(ui.color_show_recent_colors); a saved value that is not true or false
reads as on. ui.colorPanel takes recentColors: bool and answers it.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
2026-11-11 17:26:16 -08:00
dotCooCoo 7f5e4519a5 Help: input in a focused floating window is timed for the hang report
The end of a frame counted the renderer as busy only when the main
window had the focus, so a stall drawing input in a focused floating
panel or document window wrote no hang report. A floating window that
has the focus and is not minimized now counts too. A frame that sends
that window commands to minimize or hide it, or to move or resize it,
ends idle, including commands its own pass sent, which are noted
before egui passes them on.
2026-11-11 17:26:09 -08:00
dotCooCoo 45987bc948 Plug-ins: a grant one session stops counting stops counting for every session on its folder
The grants that should be gone from grants.json but could not be removed
were kept per Session, so a second Session attached to the same Plug-ins
folder in the same process still counted them for plugin.inspect,
plugin.install and loading plug-ins. They are now kept for the process,
by the folder's canonical path and the plug-in id, and a successful
write of grants.json for the id in any session makes the grant count
again. Attaching another folder no longer clears them.

A failed removal does not override a write another session made for
the id in the meantime. The process keeps the latest 256 grant writes
for that check. Another process on the folder still reads grants.json
as it is written.
2026-11-11 17:25:19 -08:00
dotCooCoo ab91a4a705 Clipboard: a paste after a late answer reads the clipboard again
The app that answers a late read may have lost the clipboard while the
paste waited. Once the answer comes, the paste makes the same read again
and uses the new answer, for arboard's reads and the core protocol's
text read alike.
2026-11-11 17:23:43 -08:00
dotCooCoo 2d2e8fa53f MCP: a screenshot's reply hides library paths outside the read roots
The reply is sent after the request was handled, and now hides what any other reply hides.
2026-11-11 17:22:51 -08:00
dotCooCoo 892d87ce55 Clipboard: a paste's reads after a late answer wait as long as the paste
The text and file-list reads that follow a late answer now wait until
10 s after the unanswered read was sent, not 2 s from when they start,
so a slow text answer is still pasted. Text that is blank or names a
picture is replaced by the bitmap only when the paste asks for one.
2026-11-11 17:16:05 -08:00
dotCooCoo 7de42c294d Render: a long path is checked against the rasterizer's limits from its kept boxes
Before drawing a shape, the renderer checks that its control points stay
within the rasterizer's limits. For a long path it measured every point
of the path for this check in each frame. The kept path of a long path
now keeps the box of the points of each run of segments next to the run's
bounds, updates it with the runs an edit changes, and answers the check
from those boxes.

With a closed path of 200,000 anchors and no paint (release build,
single-threaded, medians of 21 frames), the part of the frame redrawn
after one anchor moves took 1.93 ms instead of 2.69 ms, and drawing the
same part again with nothing changed took 0.015 ms instead of 0.343 ms.
2026-11-11 17:11:14 -08:00
dotCooCoo 5187e4b8a1 Edit Colors: a switch or number of the wrong type is an error
The Edit Colors commands read `fill`, `stroke`, `includeImages` and
`includePatterns` with a reader that fell back to the default for any
value that is not true or false, so `edit.colors.toGrayscale
{"fill": "false"}` converted the fills it was asked to leave alone.
These keys, and `convert` and `mode` in `edit.colors.adjustBalance`,
are now read with `switch_or` and `text_param`, and a wrong type is an
error that changes nothing. The same applies to `addon.color.filter`,
which shares the scope.

`edit.colors.overprintBlack` now refuses a `remove`, `fill`, `stroke`,
`includeCmyBlacks` or `includeSpotBlacks` that is not true or false
and a `percentage` that is not a number. `color.harmony` refuses a
`rule` or `variation` that is not a string; a `variation` such as 5
was dropped before.

A null still counts as a key left out. docs/mcp.md describes the
errors.
2026-11-11 17:08:11 -08:00
dotCooCoo 027035668b MCP: a readable library id keeps its text when a space follows the hidden part
A hidden id or path stays only where it begins another loaded library's whole id with no `.` or `..` part, also when that id has a space after the hidden part.
2026-11-11 17:07:30 -08:00
dotCooCoo 9998f020ac Edit: Paste in Place, in Front and in Back put a copy on each selected artboard
With the Artboard tool selecting several artboards, Paste in Place, Paste
in Front and Paste in Back put a copy of the copied art on each, where it
was on the artboard it was copied from, in one undo step. The three
commands take `artboards: [index...]`; the app passes the Artboard tool's
selection there, and without it the engine uses that selection while the
tool is chosen and records it in the journal. Art copied from no artboard
keeps its place on the first artboard and its offset to it on the others.
Copied artboards still paste once, where they were.

An `artboard` or `artboards` value that is not an artboard index of the
document, a repeated index or an empty list is now an error, and nothing
is pasted. An out-of-range `artboard` used to paste the art where it was.
A batch keeps the `artboards` a paste step names, and a paste step that
names neither pastes on the Artboard tool's selection while that tool is
chosen with several artboards selected.

Help page: https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/organize-manage-artboards/cut-copy-and-paste-artboards.html
2026-11-11 17:00:44 -08:00
dotCooCoo 711fc9bccd Data Recovery: on Windows the lock file check compares file identity
On Windows a recovery area's lock file counted as the one that was
opened when the two had the same creation time. NTFS gives a file made
within 15 seconds under a removed file's name that file's creation
time, so a second running copy could take an area another copy had
just made again. The check now compares the volume serial number and
file index, as Unix compares the device and inode, and counts a file it
cannot read as not the same. The test that a lock file made again is
not the one opened now also runs on Windows.
2026-11-11 16:59:13 -08:00
dotCooCoo cce2a685bc MCP: a failed request's error hides a file URL it sent that names another host
The error shows the URL's file name, as it does for a local path the request sent outside the read roots.
2026-11-11 16:43:49 -08:00
dotCooCoo 0a62ef657d Select: Object › Not Aligned to Pixel Grid selects paths without the pixel-aligned property
select.object.notAlignedToPixelGrid selects the paths (not guides) and
compound paths whose pixel-aligned property is off, whatever their
coordinates, and returns {count}. It looks into groups but not into
compound paths, which take the property as one object, and leaves out
hidden, locked and template art and, in isolation mode, everything
outside the isolated object. It replaces the selection, clears it when
nothing matches, and records no undo step.

The menu item goes under Select › Object with rows in every complete
catalog, and plug-ins may call the command like the other Select ›
Object filters. Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 16:41:46 -08:00
dotCooCoo 23d371ca28 Graphs: graph.setType, setData and create refuse params of the wrong type
graph.setType read `axisMin: "10"` as a cleared bound, so the axis went
back to the calculated range, and it ignored a string or number given for
a true/false option, a string for a number and a number for a text option.
graph.setData and graph.create ignored a `csv` that is not a string and
`series`, `categories` or `rows` that are not lists, and read a string in
`rows` as a blank cell.

The three commands now refuse such a value with an error naming the key,
and leave the graph and the undo history as they were. A null is still
read as a key left out, and a null axis bound still goes back to the
calculated value. A negative tick count is refused too.
2026-11-11 16:41:13 -08:00
dotCooCoo 6b4056a447 Control bar: the panel menu turns each group of options on or off
The Control bar's panel menu lists the bar's groups of options above
Dock to Top and Dock to Bottom, each checked while it is on. A click
turns that group off or on, and a group that is off stays out of the
bar even while its tool or object is selected. A group that is off
takes no room, so the groups after it fit a narrower window. The menu
scrolls in a window too short for all its items.

- window.control.options {group, visible?} sets or toggles a group and
  returns {group, visible}; an unknown group or a non-boolean visible
  is an error and changes nothing.
- The groups are off only by choice: every group starts on. The choice
  is kept with the preferences and with each workspace; files saved
  without it show every group, and unknown ids are dropped on read.
- The Artboard tool's bar follows the Artboard Options and Transform
  groups; with Image Options off, an image shows neither its controls
  nor Fill and Stroke.
- New labels have rows in every complete catalog.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 16:40:10 -08:00
dotCooCoo 45e6aa4948 Tests: MCP Pen closing drag keeps a clicked first anchor a corner
tests::pen::a_drag_from_the_first_anchor_closes_the_path expected the
closing drag to give the first anchor an outgoing handle mirroring the
incoming one, which is the behavior #6634 reports. The first anchor is
placed by a click, so after the close it stays a corner with its
outgoing handle at the anchor. The test now checks that.
2026-11-11 16:39:55 -08:00
dotCooCoo e4a126f08d Paragraph Styles: Style Options edits indents, spacing, composition, hyphenation and justification
Double-clicking a paragraph style, or Paragraph Style Options... in the
Paragraph Styles panel menu, opens Paragraph Style Options
(`ui.paraStyleOptions`) instead of the rename dialog. Its sections are
General (name and stored attributes), Basic Paragraph Formats (Alignment,
indents, space before and after), Composition (composer, Roman Hanging
Punctuation), Hyphenation and Justification. OK runs the new
`paraStyle.edit {name, to, attrs}`, which replaces the name and attributes
in one undo step; paragraphs that use the style keep their local
overrides. Fields left as they were keep the style's values, empty fields
leave an attribute unset, Preview can be canceled, and each section's
Reset Panel (`ui.paraStyleOptions.resetPanel`) clears only its own
attributes. New Paragraph Style shows the same sections, filled in from
the selected paragraph or the interface language's paragraph defaults.

`paraStyle.new`, `paraStyle.setAttrs` and `paraStyle.edit` now reject a
changed indent or space outside -1296 to 1296 pt, Hyphenation counts and
zone outside their ranges, Justification values outside their ranges or
out of Minimum/Desired/Maximum order, and tab positions outside 0 to
100000 pt. A value the style already holds is kept.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 16:34:42 -08:00
dotCooCoo db8726e94b MCP: replies hide library paths outside the read roots in more forms
- Every request under a read root records the hidden libraries before it
  runs, and the reply hides those and the ones hidden after it. A panic
  is answered inside the request, so its error is hidden too.
- A hidden path followed by more path text stays only when the longer
  text is another loaded library's id with no `.` or `..` part.
- Encoded file data is skipped only for a base64 string under
  `dataBase64`, `pngBase64` or `thumbnailBase64`; objects and lists under
  those keys, and other keys ending in `Base64`, are searched.
- The error of a failed request hides a path outside the read roots that
  the request sent by its file name, so it reads the same whether or not
  a library was loaded from that path.
- Paths written with `%XX` escapes are hidden, and on Windows paths in
  another letter case.
- Recorded steps in `ui.inspect` leave out `~`, paths under `~` and paths
  starting with `./` or `../` outside the read roots under any param.
- Hidden libraries are numbered in load order and keep their id from one
  request to the next.
2026-11-11 16:33:37 -08:00
dotCooCoo c0be73df11 Symbols: the panel menu sorts by name and offers Small and Large List views
The Symbols panel menu gains Sort by Name, which puts the document's
symbols in name order, ignoring case, as one undo step
(`symbol.sortByName`). Symbols whose names differ only in case keep their
order, the current symbol stays current, and no undo step is added when
the symbols are already in order.

The menu's Thumbnail View and List View become Thumbnail View, Small List
View (18-point rows, 14-point thumbnails) and Large List View (the former
List View's 26-point rows and 22-point thumbnails), as in the Graphic
Styles panel. Clicking, double-clicking and dragging a symbol work the
same in every view.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 16:29:20 -08:00
dotCooCoo d8be61ddb1 Help: making a save dialog's folder is timed for the hang report
An in-line save dialog that first makes its folder marked the folder
work as the dialog too, so a stall in making the folder wrote no hang
report. The folder is now made before the dialog is marked, on the
background dialog thread as well, and a test checks the order.
2026-11-11 16:27:24 -08:00
dotCooCoo d262c65b14 .ai/EPS import: gradient mesh corners in several inks take their tints last ink first
Under an `/NChannel` color space a mesh corner gives one tint for each
ink. The reader now pairs the tints with the inks in the opposite order
to the listing, for process and spot inks alike: under
`[[Yellow] [Magenta] [Cyan] 3 /NChannel]`, `0 0 1` is full yellow. No
public document describes these records. This is VectorCraft's reading
of them, and under it the meshes match the flattened page that the same
file carries.

The reader gave the first tint to the first ink listed, so mesh areas
came in with the wrong inks, yellow as cyan. Where enough of the art
changed, the layers no longer matched the file's page, and the file
opened as its page instead of its objects.
2026-11-11 16:24:48 -08:00
dotCooCoo 118c106839 Control bar: an Align To menu follows the Align buttons
The Control bar shows an Align To menu after its Align buttons. The
menu shows the icon of the choice the Align panel's Align To shows
(Artboard while one object is selected), and it lists Align to
Selection, Align to Key Object and Align to Artboard with that choice
checked. A pick sets the same choice as the Align panel's Align To
buttons, which now call the same function. Align to Key Object is off
while no key object can be made. Screen readers get a combo box named
"Align To:" whose value is the choice.

When the bar runs short of room, it drops the Distribute buttons first,
then the Align To menu, then the Align buttons.

The public documentation offers Align To in the Control panel as well
as in the Align panel:
https://web.archive.org/web/20250115013546/https://helpx.adobe.com/illustrator/using/moving-aligning-distributing-objects.html
2026-11-11 16:23:28 -08:00
dotCooCoo 788d0f517f Actions: Insert Menu Item adds a menu command to an action
The Actions panel menu's Insert Menu Item… opens a dialog that lists the
menu items an action can hold. Find picks the one item that matches the
words typed, or a click on a listed item picks it, and OK inserts its
command as a step after the selected step, at the end of the selected
action, or into the recording in progress, without running it.
Playback runs the step as the menu does.

Items that open a dialog, Object > Align, and the File, Window and Help
menu items are not listed. `ui.action.insertMenuItem` does the same for
agents and refuses a command no listed item runs.

Insert Stop and Insert Menu Item refuse a step that would give an action
more than 10,000 steps or its set a file larger than 8 MiB, the most
Load Actions… reads, for actions and recordings alike.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-11 16:21:55 -08:00
dotCooCoo 2ce702eda0 Clipboard: a paste after an unanswered Wayland text read waits for its answer
The core-protocol text clipboard (used where the compositor has no
data-control protocol and there is no X11) failed a paste at once after
its 2 s read timeout, so the paste took the internal clipboard even when
the other app's text came within the paste's 10 s limit. It now keeps a
text read that got no answer in its wait, as the arboard path does, and
the paste waits for that answer on its background thread until 10 s
after the read was sent. The answer is pasted unless it is empty or the
text copied here.
2026-11-11 16:20:52 -08:00
dotCooCoo b14831d964 Type: a soft hyphen inside a No Break run does not end the first word
When layout looked for room for a line's first word in a frame that is
not a rectangle, a soft hyphen ended the word even inside a No Break
run, although the line does not break there. The word is now measured
across a soft hyphen where kinsoku or a No Break run keeps the line from
breaking after it.
2026-11-11 16:20:48 -08:00
dotCooCoo 0b98154c78 Shape tools: the constrain-angle drawing test deselects between shapes
The test drew an ellipse from the top-left corner of the rectangle it had
just drawn, which stays selected. With a shape tool, a press on a handle of
the selection's box now scales the selection, so the press scaled the
rectangle instead of drawing. The test deselects before each tool draws.
2026-11-11 16:18:26 -08:00
dotCooCoo fe89fc39a2 Package, Place, Export: commands reject a param of the wrong type
file.package, file.place, file.place.queue, file.place.info,
document.serialize, document.export, document.exportSelection and
document.exportForOffice read their switches, strings and numbers with
helpers that fell back to the default for a value of the wrong type.
file.package {"copyFonts": "false"} copied the fonts, and
document.exportForOffice {"path": 5} returned the bytes instead of
writing a file. Each such value is now an error naming the key, and
nothing is placed or written. Null still means the key was left out.
2026-11-11 16:17:10 -08:00
dotCooCoo 74ea25cd6c File formats: HP-GL (.plt) plot files open and place
File › Open and File › Place refused plotter files: HP-GL/2 files
(.plt, .hpgl, .hgl, .hpg) were not a format VectorCraft reads.

The format table gets a read-only HP-GL format, read by a new
vectorcraft_cad::hpgl module. Each line the pen draws becomes a stroked
path in its pen's color and width, with the line type as a dash pattern
and the line attributes as caps and joins. Circles, arcs, wedges and
Bézier curves are drawn as curves. Filled and edged rectangles, wedges
and polygons (polygon mode) become filled or stroked paths, and encoded
polylines, scaling points, user units and rotation are read. Plotter
units (1016 to the inch, y up) become points, and the artboard holds
what the art paints. Place brings the art in as one group. Older HP-GL
files are read by the HP-GL/2 rules for the instructions the two share.

A file is recognized by its first instructions, after any printer
escape sequences and PJL lines, so a plot file also opens under an
extension no other format reads. VectorCraft's own rules where the
device decides: before the first SP the pen is pen 1, and the hard-clip
limits are 11 x 8.5 in, which P2 takes when the file sets no scaling
points. Labels, symbol-mode symbols and raster images are not drawn,
the input window does not clip, hatched fills are filled solid, and
later pages are drawn over the first; the warnings list each. Objects,
path points and pen widths are capped, and the import fuzzer covers
random, mutated and garbage plot files.

An importer plug-in that lists .plt, .hpgl, .hgl or .hpg now opens
those files only when document.open names it with `importer`.

Docs: docs/mcp.md, docs/file-format-parity.md,
docs/target-app-parity.md, docs/releasing.md.
2026-11-11 16:03:41 -08:00
Dylan M. Taylor da33e8616a Metafile: drop PNG ICC and text chunks before decoding; Folder batch lists aliased folders once 2026-11-12 00:00:55 +00:00
dotCooCoo e0fa8c4254 Merge main into fix/read-roots-other-replies 2026-11-11 15:58:07 -08:00
dotCooCoo 4591653f33 MCP: library lists leave out paths outside a confined client's read roots
swatch.library.list, brush.libraries, symbol.libraries and
graphicStyle.libraries returned every library's file path and the user
library folder to a client confined by --automation-read-root, including
paths outside its read roots. They now return null for those paths, and a
library loaded from a file outside the roots is listed as
loaded/<file name>, numbered (2), (3) when names repeat. That id finds the
library in the other library commands, and their replies show it too.

- The naming moves from control.rs into
  swatchlib::concealed_library_ids, which numbers the session's loaded
  libraries first, so ui.inspect, ui.docking and the library commands show
  a library under the same id.
- swatch.library.copyToUser refuses a library whose file is outside the
  read roots without naming the file; it and the library saves with
  `user: true` leave out `path` when the user library folder is outside
  the read roots.

Docs: docs/mcp.md (read roots), docs/target-app-parity.md.
2026-11-11 15:58:01 -08:00
dotCooCoo 4c8155cb98 Safe Mode: list a plug-in found under several files once
A plug-in turned off by its id gets an entry for each file it is later
found under, so a later load skips each file before running it. Help ›
Safe Mode listed every entry, showing the same plug-in once per file.
It now lists each id once. Enable on Re-launch already turns on every
entry with that id.
2026-11-11 15:55:03 -08:00
dotCooCoo 335acab01b Scripts: a missing file below a link that leads nowhere is refused
A missing file below a folder link whose target does not exist got
"does not exist", while one below a link to an existing outside folder
was refused, so a script could tell whether the folder a link leads to
exists. A missing path is now refused when the nearest entry above it
that exists is a link that leads to nothing.
2026-11-11 15:53:26 -08:00
dotCooCoo 74f523ae8b Merge main into fix/plugin-removed-rename-back 2026-11-11 15:52:10 -08:00
dotCooCooandGert Pellin 1b744eae52 Gradients: conic gradients sweep the stops once around the center
A fourth gradient kind, conic (also accepted as `angle` or `conical`),
sweeps the stops once around the center (the gradient's start),
clockwise on the page from the vector, with a seam where the last stop
meets the first. A negative aspect mirrors the sweep, and reflecting an
object turns its sweep the other way, also for a conic without a
vector or with a flat extent.

- Gradient panel: a Conic button in the Type row; the Aspect Ratio
  field works for conic gradients and keeps a mirrored sweep mirrored.
- Gradient tool: the extent ellipse, aspect handle and center ring of a
  radial gradient, without a focal point.
- Rendering, swatch chips, transforms, warps, plug-in round trips, and
  Object > Expand into wedges or a mesh of columns around the center.
- Commands: a negative `aspect` is an error for linear and radial
  gradients, and so is a `focal` point for a conic gradient.
- PDF export writes a function-based shading whose calculator function
  turns the angle into the ramp, and PDF import reads a function-based
  shading that turns around the origin of its space back as a conic
  gradient, also when that origin lies outside the painted area. A
  PDF/A file, or a conic with more than 97 stops, gets an image
  instead.
- SVG export writes 360 solid wedges clipped to the shape for opaque
  stops and an image otherwise, and a linear gradient on characters.
- CSS export writes `conic-gradient(from ...)`, with the stops mirrored
  for a mirrored sweep.
- EPS and legacy .ai export write 360 solid wedges; the .ai editing data
  holds a linear gradient along the vector with the same stops.
- .af files: conical gradients open as conic gradients.
- An export that writes the gradient in another form gives a warning.

Supersedes #2326 and includes switch87's code from it.

Co-authored-by: Gert Pellin <gert@pellin.be>
2026-11-11 15:51:11 -08:00
dotCooCoo ef7fbaac31 Plug-ins: a failed rename back of a removed plug-in's settings is reported and undone later
When plugin.remove could not remove the grant and then could not rename
the plug-in's settings folder back from <folder>.removed, the failure was
only logged: the plug-in got empty settings, and the next removal deleted
the .removed folder with the settings in it.

- The error now names the .removed folder, the plug-in folder and the
  reason, and the plug-in gets no storage until the settings are back.
- The next load, install or plugin.remove of the plug-in renames a kept
  folder back while the plug-in has no folder of its own. When it has one,
  that folder is used and the kept one stays until plugin.remove deletes
  both. A removal while a kept .removed folder exists renames the plug-in
  folder to <folder>.removing.
- Before deleting a renamed folder, the store writes an empty
  <folder>.removed.delete file and removes it once the folder is gone, so
  what a failed deletion leaves is not moved back. When that file can't
  be written, nothing is deleted.
- A .removed folder without a .delete file, including one an earlier
  version left, counts as kept settings: a new install of the id needs
  replace: true, and plugin.remove of the id deletes it.
2026-11-11 15:51:11 -08:00
dotCooCoo 14c8385df3 Clipboard: a paste after an unanswered read waits for its answer
When the check whether the clipboard still holds what VectorCraft copied,
or one of a paste's own reads, gets no answer within its 2 s, the read is
kept. The paste then waits for that answer on its background thread, up
to 10 s after the read was sent, instead of failing its other reads at
once and pasting the internal clipboard. When the answer is what
VectorCraft copied, or none comes, the paste takes the internal
clipboard; otherwise the clipboard is read as a paste reads it, with that
answer standing in for its read. An answer that came before the paste is
dropped and the clipboard is read again.
2026-11-11 15:49:27 -08:00
dotCooCoo b89a6e8ba1 Type: Block's word gap leaves the paragraphs' word spacing as set
The word gap is now the Desired word spacing the layout uses for area type
with Block on (TextObject::layout_justification), with Minimum and Maximum
moved out to it when it lies outside them. The paragraphs keep their own
Justification values, so a wide gap no longer leaves a 1,000 % Maximum
behind, and turning Block off brings back the paragraph's own Desired
value. The SVG writer reads the same values.

- The Contextual Task Bar offers Dynamic Text for area type outside a
  thread, beside Convert To Point Type.
- type.dynamicText with wordGap or lineGap and no area type in the
  selection is an error instead of being ignored.
- Docs: docs/mcp.md, params text, docs/target-app-parity.md.
2026-11-11 15:49:16 -08:00