Commit Graph
4786 Commits
Author SHA1 Message Date
Dylan M. Taylor 0cdcf3039b engine, plugins: cap radar dimensions, resume slice-name suffixes, weigh cache keys 2026-11-04 11:45:15 +07:00
Davit MatchakhelidzeandClaude Opus 5.5 cd53262faf i18n(ka): keep the 23 rows the maintainer asked for
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-11-04 11:45:09 +07:00
Davit MatchakhelidzeandClaude Sonnet 5.5 288bca85c2 i18n(ka): bring the Georgian catalog up to the other catalogs
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-11-04 11:45:09 +07:00
BFlat 15670ca9f3 Merge pull request #4968 from dotCooCoo/feat/ai-editing-data 2026-11-04 11:41:38 +07:00
Dylan M. Taylor 5cb58c0507 3D effects: every ring pair counts toward the containment budget; no zero-sized inflate cells (#5413)
* fix(three-d): charge every ring pair; refuse zero-sized inflate cells

* fix(three-d): clippy-friendly cell check

* address review: reject subnormal inflate cells
2026-11-03 23:21:52 -05:00
Dylan M. Taylor daf224755a Fonts: cap the name records and decoded bytes read per name id 2026-11-04 10:52:13 +07:00
dotCooCoo 5e4f0720b0 Layers: Layer Options for several rows sends only the settings set in it
With several rows highlighted, Layer Options shows the first row's
settings, and OK sent Show, Lock, Color, Template, Print, Preview and
Dim Images to layer.setProps for every row. Changing one setting gave
every row the first row's values for all of the others as well.

The dialog now records each setting set by one of its controls or by
ui.dialog.set, and for several rows OK sends only those. A setting set
to the value the dialog shows is still sent, so choosing the first row's
color gives every row that color.

When Template changes and Lock and Dim Images are not set in the dialog,
OK does not send them. layer.setProps then locks each row that becomes
a template and dims it to 50%, unless a percentage is set in the dialog,
and unlocks and stops dimming each row that stops being one. When
Template is turned on, the dialog shows that 50%. When Template is set
back to the first row's value, the dialog shows the first row's Lock,
Dim Images and percentage again. A Dim Images percentage set in the
dialog is sent whenever Dim Images is on, also when Template turned it
on.

ui.dialog.set of template now also sets locked and dimImages, as the
Template checkbox does, for one row as well as several. Before, a script
that set template to true and confirmed the dialog left the layer
unlocked and undimmed.

The Dim Images percentage field now has the accessible name "Dim Images
to:", as its checkbox has.
2026-11-04 10:51:43 +07:00
Dylan M. Taylor bb03081a70 EPS import: bound and charge the filter chain over a pending file 2026-11-04 10:51:39 +07:00
dotCooCoo 38bac1114b Preview dimensions on placement press 2026-11-04 10:51:37 +07:00
dotCooCoo 46bb91275d Merge main into feat/ai-editing-data 2026-11-03 19:47:02 -08:00
dotCooCoo de78142dd8 Toolbars: Manage Toolbars renames, copies and deletes the toolbars made with New Toolbar
Window › Toolbars › Manage Toolbars… and Manage Toolbars… in the All Tools
drawer's menu open a dialog that lists the toolbars made with New Toolbar.
A name typed for the selected toolbar renames it, + copies the selected
toolbar or, with none selected, makes a new one, and the trash can deletes
it (https://helpx.adobe.com/illustrator/using/tools.html, Manage toolbars).

VectorCraft's own details:
- A rename applies at Enter, when the text box loses focus, or at OK. The
  name follows New Toolbar's rules: not empty, at most 200 characters, and
  not another toolbar's in any case.
- A copy is called "<name> Copy" (then "<name> Copy 2"), shows a little
  below the toolbar it copies and below earlier copies; a new toolbar is
  called "Toolbar N".
- Each change applies at once; deleting also removes the groups torn off
  the toolbar.
- When the typed name is refused, a click on +, the trash can or another
  toolbar is not applied, so the name can be corrected. When the selected
  toolbar is deleted elsewhere while the dialog is open, none is selected.
- Renaming or deleting a toolbar closes the All Tools drawer and a group's
  flyout open on it; a change of case only leaves them open, and the Tools
  panel's flyouts and other toolbars' stay open.
- Each change shows under Window › Toolbars and is kept with the workspace.

New commands: window.toolbar.manage, window.toolbar.rename,
window.toolbar.duplicate and window.toolbar.delete, with catalog rows for
their labels in every language.

Docs: control-protocol.md, gaps.md, target-app-parity.md, ui-parity.md.

Part of #1759
2026-11-04 10:26:01 +07:00
dotCooCoo a0a4633c26 Restore Type editing after a dropped press 2026-11-04 10:25:57 +07:00
Don Omondi 4f60ee47e8 Fix #2920: an open path in a PDF-compatible .ai opens as drawn
Squashed from #4616 onto main.
2026-11-04 10:25:55 +07:00
dotCooCoo b5c69e25fd Add command to begin recording Actions 2026-11-04 09:43:35 +07:00
dotCooCoo 5e495cf900 .ai import: objects that draw differently from the page come in as its art, and the layers stay
When the layers fail the comparison with the file's own page because some objects draw
differently (an object in another color, or one that only one side draws), the import finds
each top-level object whose bounds hold a patch of such pixels (the smallest one that holds it)
and replaces it with the page's art in its bounds, in the same way as an unread object. It then
compares the layers with the page again. When that comparison passes, the layers stay, and a
warning names the layer or sublayer of each replaced object (`<Layer>` for one without a name)
and how many were replaced on it. Like the warning for unread objects, this warning counts as
a loss, so an export or Save As to the file's own path needs `acknowledgeLoss: true`. A
replaced wrap object keeps its wrap options, and area type is wrapped again around the page's
art. Placed files at their other placement are tried too when the first placement can't be
kept.

The file still comes in as its PDF part, with the same reason, when only edges or type differ,
when a difference lies outside every top-level object, when more than half of the top-level
objects (or more than 64) hold one, when such an object is or holds type or holds a wrap object
inside a group, when the page's type of a text object that stays would be copied into a
replacement, when the page's art can't replace an object for the reasons it can't replace an
unread one, when matching the differences to the objects would take too much work, or when the
second comparison still fails. Trusting the editing data keeps the objects as read.
2026-11-04 09:43:28 +07:00
dotCooCoo 8cf4b90a65 Contextual Task Bar: type on a path offers Type on a Path Options
The task bar offered no Type on a Path item for a selected type on a
path: of the type actions, it showed only Create Outlines. It now also
offers Type on a Path Options, which opens the same dialog as Type ›
Type on a Path › Type on a Path Options, with the selected type's
effect, alignment and spacing filled in. Point and area type keep
their Convert To Area Type and Convert To Point Type buttons.
2026-11-04 09:43:25 +07:00
dotCooCoo 7fe42986c2 Select: the font Same commands take their reference from type that can be selected
Select > Same > Font Family, Font Family & Style, Font Family, Style & Size,
Font Size and the text fill and stroke color commands took the type to match
from the first type found among the selected objects. Type that is hidden or
locked inside a selected group, type on a template layer and type outside the
isolated group could set the reference. They now take it from the type that
Link Block Series starts from, in the same selection order. A selection whose
only type cannot be selected returns "select a text object".
2026-11-04 09:43:21 +07:00
dotCooCoo e03665a215 Window: New Window opens a second view of the current document
Window > New Window was grayed out. It now opens another view of the
current document, with its own zoom, center, rotation, artboard and
Outline or Preview, as a tab of its own. While a document has more than
one view, each tab shows the view's number after the name
("Untitled-1:2"). An edit in one view shows in every view of the
document.

Tile, Cascade, Float in Window, Float All in Windows and Consolidate All
Windows arrange each view as they arrange a document, and Tile and
Cascade are enabled with two views of one document. A new view of a view
in a floating window opens in a floating window too. File > Close and a
tab's close button close the view; the document's last view closes the
document, asking to save it first. Next Document and Previous Document
step through the views in tab order.

Each Tile pane and floating window draws its view in its own Outline or
Preview, and switching views with the Artboard tool targets the artboard
the view shows. A switch to another view of the current document resets
the tool, as a switch to another document does (Session::reset_tool).
Artwork held over another view's tab during a drag switches to that
view, and a drop there moves it.

Fixes #4116
2026-11-04 08:13:58 +07:00
FredAppsandClaude Opus 5.5 b91f527c91 About: credits tables fit without side-scrolling and their columns resize
The Contributors table and the Models table were egui Grids inside a
two-way scroll area: they opened wider than the dialog, so they scrolled
sideways, and Company and Model sat almost touching. Both are now
egui_extras tables. Each column opens at the width of its header and widest
cell, the spare width goes to the text columns (Name; Company and Model),
and every column edge can be dragged. Widening a column grows the table
like a spreadsheet, and only then does it scroll sideways; the header stays
put while the rows scroll. Counts and dates are right-aligned and every
cell is vertically centred.

The sorted column stays highlighted and its direction shows on the
toolbar's sort-order button, so headers no longer reserve room for an
arrow. The opening widths come from fit_widths, a pure function with unit
tests that a fitted table fills its room exactly and that only the text
columns give way when room is short.

The About dialog opens 820 points wide, so every column fits at its natural width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:13:55 +07:00
Franco Jacquet c16c833c58 Check stroke.set params: a misnamed key is refused (#1442)
Squashed from #5453 onto main.
2026-11-04 08:13:52 +07:00
vangel76andClaude Sonnet 5.5 f258124086 Component fit chunking: review fixes (#5464)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:32 +07:00
vangel76andClaude Sonnet 5.5 007e15ad8b Image Trace: fit the components of a layer in parallel, in chunks (#5464)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:32 +07:00
vangel76andClaude Sonnet 5.5 6ede10de4d all_within hasher: mix the high bits into the low ones (#5463)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:28 +07:00
vangel76andClaude Sonnet 5.5 f4ba106e39 Image Trace: all_within hashes its grid cells with a small hasher (#5463)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:28 +07:00
vangel76andClaude Sonnet 5.5 c0465969c8 Denoise: comment fixes from review (#5462)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:25 +07:00
vangel76andClaude Sonnet 5.5 63b8c88168 Image Trace: noise removal keeps its regions in u32 arrays (#5462)
Regions are a linked list of pixels with a union-find over region ids instead of a Vec<usize> per region and BTreeMaps per merge. Same merge order and output; about 2.7x less peak memory and up to 1.9x faster on 25 MP noisy labels.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:25 +07:00
vangel76andClaude Sonnet 5.5 df3f18329e Trace: cover the column pass and mixed pairs in the smoothing tests (#5454)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:22 +07:00
vangel76andClaude Sonnet 5.5 5bc34676ce Trace: look up the smoothing weights instead of computing them per pixel (#5454)
The bilateral smoothing pass worked out a squared colour distance, a powi, a
division and a weight for every pixel and neighbour, and called round() per
channel. The distance is a whole number up to 195,075, so the weights come from
a table built once per trace, and the rounding is a truncation plus a compare
that gives the same level as round() for 0..=255. Rows are read through slices.

The output is byte for byte what it was; tests compare the pass with the old
formula on several images and on every pair of grey levels.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:22 +07:00
Dylan M. Taylor fc83e5c5b9 address review: same distance test in width profile lower bound; preview the whole drag when a symbolism tool deactivates 2026-11-04 08:13:18 +07:00
Dylan M. Taylor 13b0bbe639 Linear-time scans: Add Anchor Points, swatch library adds, placed-document export, width profiles, symbolism previews
Add Anchor Points builds the new anchor list in one pass, Swatch Library add looks names up in sets, full_documents dedups placed keys with a set, width profile lookups and mark insertion use binary search, and the symbolism tools cap and thin the previews of a long drag as Liquify does.
2026-11-04 08:13:18 +07:00
vangel76andClaude Sonnet 5.5 63aeefbd6c Offset Path: scale the box slack with the coordinates and tighten the tests (#5455)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:15 +07:00
vangel76andClaude Sonnet 5.5 445e954ec9 Offset Path: skip far segments and stop at the first near one in the spurious-contour check (#5455)
For every hole (when growing) or island (when insetting), the check measured the
distance to every segment of the input with the nearest-point search and took the
minimum, so the cost grew with holes x segments. Segment bounding boxes are now
computed once, a segment whose box is farther than the offset distance is skipped,
and the search stops at the first segment that is close enough. The decision is the
same as before; a test compares it with the old full scan on random segment sets.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:15 +07:00
vangel76andClaude Sonnet 5.5 0a2935cf8f Booleans: limit of one unit, crop outside shortcut, tests (#5465)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:12 +07:00
vangel76andClaude Sonnet 5.5 e951095617 Path booleans: refuse out-of-range input, guard the contour pass, cuts report refusals (#5465)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 08:13:12 +07:00
Dylan M. Taylor 9cd997b9aa address review: draw a line for curves with far-out start or control points in flatten 2026-11-04 08:13:07 +07:00
Dylan M. Taylor 0e64aca557 Bound work: flattening of huge curves, print layout past the page limit, script-check file names 2026-11-04 08:13:07 +07:00
ruchirguitarandClaude Opus 5.5 079f5389a6 opening::poll stops once a file it opens asks a question
Of two files read off the UI thread and ready in the same frame, the
first could open and ask about its links or fonts, and the second then
became the active document under that dialog. poll now checks
may_switch after each file it opens too, and leaves the rest for a
later frame.

The queued-reopen test also checks that a later queued activation
stays queued.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:13:04 +07:00
ruchirguitarandClaude Opus 5.5 e2c8c503c1 opening::poll stops once a queued reopen asks a question
A document asked for again while a dialog was open, and closed
meanwhile, opens again when the dialog closes and may ask about its
links or fonts. poll went on to finish the files read meanwhile, and
each became the active document under that dialog. It now checks
may_switch after each queued activation and leaves the rest, and the
files ready to open, for a later frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:13:04 +07:00
ruchirguitarandClaude Opus 5.5 72cf1a9b3c Queued tab activations keep the file access scope they were asked in
A document asked for again while a dialog was open was queued by path
only, and opened later in the scope of the frame that ran the queue. A
request confined to automation roots could then read a file outside
them, if the document was closed meanwhile and its path became a link
out of the roots. Each queued activation now carries its ScopeHandle
and runs in it, as background opens already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:13:04 +07:00
ruchirguitarandClaude Opus 5.5 8f66e2b694 Focusing an open tab closes Home, checks read roots and waits for dialogs
Follow-ups to #5369 (#5420), from its last review:

- Home shown over the document asked for again gives way to it.
- Automation confined to some folders can't reach an open document
  outside them: open_file checks read access before looking for a tab.
- While a dialog or a drag acts on the active document, a document asked
  for again comes to the front once it ends (opening::poll), as a file
  that finishes opening already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 08:13:04 +07:00
dotCooCoo 65f55d65ea .ai save: errors and notes name the legacy or non-PDF-compatible .ai
Option errors and notes for a .ai save called the file a
"PDF-compatible .ai" even when the save wrote a version 7 file
(aiVersion: "7") or a file without PDF content (pdfCompatible: false).

- Option errors: a version 7 .ai is now called a "legacy .ai
  (version 7)" in the errors for its artboard, artboards and range
  options. A .ai saved without PDF content is called a
  "non-PDF-compatible .ai" in every .ai option error (includeLinked and
  embedProfiles included), and a "non-PDF-compatible .ait template"
  when Save as Template writes it. aiVersion: "7" takes precedence over
  pdfCompatible: false, as it does when the file is written.
- The error for encoding a version 7 .ai of several artboards as one
  file names the legacy .ai.
- The note for a native option the file does not read (version,
  preview, precision) names the same kind of file.
- The error for aiVersion: "7" on a .ait template no longer says that a
  .ait template is always PDF-compatible, since Save as Template writes
  one without PDF content. It now reads "aiVersion 7 writes legacy .ai
  files, not .ait templates", with its rows in the message catalogs
  updated.

A PDF-compatible file keeps its name.
2026-11-04 08:12:56 +07:00
dotCooCoo 1246c62095 DXF: paths of lines and circular arcs export as polylines with bulges
A path made of straight segments and circular arcs (a slot, a D shape,
a rounded rectangle, a pie) was written as one SPLINE, or as a flattened
polyline in R12. It is now one polyline whose vertices carry a bulge
(group 42) for each arc: an LWPOLYLINE in R14 and later, a POLYLINE with
VERTEX entities in R12 and R13, closed when the path is closed. Each run
of cubics on one circle becomes one arc. A path with any other curve
stays a SPLINE, and whole circles, arcs and ellipses keep their CIRCLE,
ARC and ELLIPSE entities.

- The circle test's tolerance is 5e-4 of the smaller of the radius and
  the curve's extent, so a shallow curve that is not circular is no
  longer written as an ARC.
- The drawing extents take each ARC's and each bulge's furthest points.

Part of #1299
2026-11-04 08:12:52 +07:00
dotCooCoo 40175d90c7 Geometry: inverses and conjugates by small affines are no longer refused
A projection now counts as invertible when its determinant, or its
adjugate's determinant, is more than 1e-15 times the largest of the six
products that determinant sums. The adjugate of the adjugate is the map
times a number, so the inverse of a projection that inverts also inverts,
is stored and reads back. The adjugate's cofactors are formed with exact
products, so the inverse of that inverse gives back the original map up
to scale, within 1e-6 of each row's largest entry, and a map whose third
row is the sum of its first two is still refused.

Homography::conjugated and Node::transform_projection judged the affine
map by an absolute determinant of 1e-15, so a scale by 1e-8 left type and
symbol instances in perspective with their old projection. Both now use
Homography::affine_is_invertible, which compares the determinant with
the two products it sums at any scale of the map. An affine map whose
determinant or inverse is not finite, such as a scale by 1e200 or
1e-200, is inverted through Homography::inverse. When the direct product
underflows to a map that is not invertible, as for a projection stored
at a subnormal scale, conjugated forms it again with its powers of two
kept apart.

- crates/geom/src/projective.rs: the_inverse_of_an_invertible_map_is_invertible,
  conjugating_does_not_depend_on_the_scale_of_the_affine,
  conjugating_a_map_at_a_tiny_scale_gives_an_invertible_map
- crates/doc/src/node/tests.rs: a_projection_follows_the_transform_or_is_removed
  covers an invertible scale by 1e-8 and a singular map at that size
2026-11-04 08:12:48 +07:00
dotCooCoo dc8808fe5e Read compound effects when opening effect dialogs 2026-11-04 08:12:44 +07:00
dotCooCoo 9051b70028 Measure tool: Shift does not turn the end to the opposite step
With Snap to Point alone, an anchor behind the start could pull the
projected end behind it, and the measurement read 180 degrees from the
pointer's step. Such an end is now turned back onto the pointer's step,
keeping its length, as an end rounded nearer another step is.
2026-11-04 08:12:37 +07:00
dotCooCoo aeafc359ea Measure tool: Shift keeps the end on the pointer's step after the grid rounds it
With a Constrain Angle other than 0, the grid point a Shift-drag's end
rounds to can lie nearer another 45-degree step, and the measurement then
reported that step. A Shift-drag from (0, 0) to (5, -3) with a 30-degree
Constrain Angle and a 9 pt grid measured -15 degrees instead of 30. The
end is now turned back onto the step nearest the pointer's direction,
keeping its length.
2026-11-04 08:12:37 +07:00
dotCooCoo 4c0e0b959a Commit pending dimensions only on pointer release 2026-11-04 08:12:34 +07:00
dotCooCoo aca6e98423 Keep Blend reset after a dropped press 2026-11-04 08:12:31 +07:00
dotCooCoo 9fd7d0a733 Type: place a contour-point caret on a composite as the outline is drawn
With SCALED_COMPONENT_OFFSET, a component's offset now scales each axis
by the length of that axis's basis vector, as the outline loader does,
instead of going through the whole matrix. A rotated or skewed
component's caret no longer lands away from its drawn point.
UNSCALED_COMPONENT_OFFSET now turns the scaling off when both flags are
set.
2026-11-04 08:12:22 +07:00
dotCooCoo 98ef01a6d3 Type: bound the glyphs a contour-point caret lookup reads
A composite glyph whose components expand into glyphs with no points
could make the contour-point caret lookup read billions of glyphs. The
whole lookup now shares a budget of 65,536 glyph reads and gives no
caret when it runs out, so the caret falls back to dividing the advance.
2026-11-04 08:12:22 +07:00