Commit Graph

1139 Commits

Author SHA1 Message Date
MarekSpodBiedry 1217d58ca1 Gradient mesh: a fill colour recolours the point last clicked (#1050) 2026-10-11 07:13:33 +07:00
Michael Gieson c912ef59d1 Build: generated sources are rewritten only when they change
crates/text/build.rs (craft_fonts.rs) and crates/ui-egui/build.rs (credits.rs) wrote their
output on every run. Where cargo's recorded times lose their sub-second part (an ecryptfs home
directory, as Mint and Ubuntu offer at install), a file written just before its crate compiles
reads as newer than that compile, so the next build compiled the crate and everything above it
a second time: a full app rebuild for nothing after any build-script rerun. Unchanged output is
now left alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 07:13:32 +07:00
bflatastic f043a1a392 #1032: spot colours defined in Lab keep their Lab values
A .ai or PDF whose spot colours are defined in Lab opened them wrong:
- the PDF part gives such an ink an ICC Lab profile as its alternate
  space ([/ICCBased profile], 3 components), which was read as RGB, so
  the Lab values were clamped into wildly wrong colours (PANTONE 249 C
  came in yellow). A profile whose Alternate is [/Lab ...], or whose
  header names the Lab data colour space, is now read as Lab;
- the editing data writes them as `c m y k L a b (name) tint 2 Xx`,
  type 2 being Lab, and only the CMYK equivalent was read. Type 2 now
  gives the Lab colour, in the swatches and on the art.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:06:05 +07:00
dotCooCoo dc6e8a4253 Object: Align, Move, Arrange and other commands that change nothing record no undo step
These Object commands edited the document even when nothing changed, so
the call recorded an undo step, emptied Redo and marked the document
modified:

- object.align, object.distributeSpacing and object.distribute (three
  or more objects) when no object moves. With one object selected,
  Object › Align aligns it to its own bounds, so it recorded a step
  every time. Calls on objects that were already aligned or evenly
  spaced also recorded a step, including objects that an earlier Align
  or Distribute left a rounding error away from the position the next
  call computes.
- Move, Rotate, Scale, Shear, Transform and Nudge by an identity
  transform without a copy, Transform Again after one, Set Bounds with
  the current position and size, and a Selection tool drag that ends
  where it started.
- Nudge by 0 with anchors direct-selected or with only ruler guides
  selected, and Transform with nothing selected or with ids that name
  no object, with or without a copy.
- Bring to Front and Bring Forward on objects already in front, Send to
  Back and Send Backward on objects already at the back, and Send to
  Current Layer on objects already on top of the current layer.
- Ungroup, Release Compound Path and Release Clipping Mask with none of
  their objects selected, and Expand Shape with no live shape selected.
- Lock with every selected object already locked, Hide with every one
  already hidden, Unlock All with nothing locked and Show All with
  nothing hidden.
- object.setProps with the values the objects already have, or with no
  object.
- object.setLiveShape with the values the shape already has: a
  polygon's sides, angle, radius or equal sides, or a side length that
  gives exactly its current radius; an ellipse's pie, including Invert
  Pie on a whole ellipse; and a corner radius or kind that the corners
  already have.

These commands now return without editing the document, and Align and
Distribute count a move of 1e-9 pt or less on both axes as none. Such a
call records no undo step, keeps the step to redo and leaves the
document unmodified. In these cases Ungroup no longer resets the
selection, and Release Compound Path, Unlock All and Show All no longer
empty it. A Transform with a copy of no object keeps the selection.
Lock and Hide still deselect the objects. A Nudge by 0 no longer
expands a live shape whose anchors are direct-selected. The replies
have the same form as before. An identity transform still sets what
Transform Again repeats. The journal still records each call, so the
Actions panel records it as before.

When every value given to Set Bounds is the current one, it now applies
the identity transform instead of the transform computed from those
values, which can differ from the identity by a rounding error on a
rotated object or with Use Preview Bounds.

When a drag comes back to its start, the identity preview no longer
edits the document. Session::preview now advances the revision when it
undoes the previous preview, so views redraw that drag.

The undo and redo row of docs/mcp.md now notes that many calls that
change nothing record no undo step, so undo after one undoes the step
before it.
2026-10-11 07:02:28 +07:00
imachus 621e1a6640 Type: Proportional Metrics — full-width glyphs on the font's proportional widths, `palt` (#966)
A character attribute (Character panel menu with the East Asian options;
`text.setFormat` / `text.setRangeStyle {proportionalMetrics}`), saved only
when on. In horizontal type it applies `palt`; vertical type sets as before
until the vertical shaping change (`vpal`) follows.

Line-end Punctuation Half Width takes nothing more off the punctuation
`palt` re-spaced (the flush opening bracket, consecutive punctuation, the
line-end half width): the segment is shaped once more without `palt`, and
the glyphs whose advance differs are marked. Punctuation the font leaves
full width is trimmed as before. SVG writes `"palt" 1` with the
character's other features.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:00:35 +07:00
lollinoo c5274beb23 #1034: scaled type shows and takes its scaled sizes
After scaling 12 pt type 2x, the Character panel still showed 12 pt and
typing 10 pt gave 20 pt type. Character controls and text commands now
work in document points: the size, leading and baseline shift shown and
set are the stored ones times the text's scale along its em height, and
horizontal scale includes the ratio of the transformed em axes. Rotation,
reflection and shear stay in the transform, so text remains editable.
Groups' text counts, keyboard increments use the same units, Shrink
Text to Fit works in document tenths, and a collapsed transform rejects
dimensional edits without touching the document.

text.getRange takes effective: true for the document-space values.
2026-10-11 07:00:34 +07:00
bflatastic 75c587c8a0 #1063: a fill chosen with characters selected colours just them
Choosing a fill or stroke (Color panel, Swatches, the proxies) while the
Type tool had a few characters selected painted every run of the type
object. As in Illustrator, it now goes to the selected characters only:
the runs are split at the selection's ends and the paint is placed on
the runs in between, as one undo step; a stroke gets a 1 pt weight
where it had none. With only a caret, or with explicit ids or an item,
paint.setFill/setStroke behave as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:59:36 +07:00
bflatastic 2048084396 #1027: PostScript-form .ai files open through their layers
A .ai in PostScript form went only to the PostScript interpreter, which
stopped on the first operator its missing prolog would define
(`Adobe_packedarray`, `Lb`, `Np`): files from Rhino and other CAD apps name
the procsets without including them.

- Such a program is its own editing data: when it has layers
  (`%AI5_BeginLayer`) and no private data, the editing-data reader reads it,
  compared with the page when the program runs, from the layers alone when
  it can't be run. If the layers can't be read either, the error says why.
- The reader takes the legacy ten-operand `Lb`, ignores flatness `i`, and
  uses the bounding box as the page when nothing else says where it is.
- Tests on synthetic files; fuzz fixture and hostile-program layers added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-11 06:04:48 +07:00
bflatastic cee73d03a3 #1025: .ai editing data keeps sublayers with global colours and pattern fills
The editing-data reader stopped on `Xk`/`XK` (global process colours) and
on `p`/`P` (pattern fills), so the file fell back to its PDF part, which has
only the top-level layers.

- `Xk`/`XK` read like `Xx`: a global swatch (not a spot ink), linked at its tint.
- `%AI3_BeginPattern` sections are read by a reader of their own into
  pattern swatches (tile from `(name) llx lly urx ury`, art in pattern space);
  `p`/`P` fill or stroke with them, placed by the operator's matrix. A pattern
  it can't read whole still makes a fill with it unreadable, as before.
- Tests on synthetic editing data; fuzz tokens and fixture extended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:04:48 +07:00
bflatastic c183c47df3 Review: guides on hidden or locked layers, and through layer commands
- Guides on a hidden or locked layer leave the selection (Selection::prune,
  run after every edit and selection change), their Layers rows neither
  select nor drag them, and guide.move / guide.remove / guide.setLayer
  refuse them; guide.setLayer refuses a locked target layer, as dragged
  rows are refused there.
- A new guide goes where new art goes (the current layer, else the top
  shown and unlocked one; refused when there is none), so it never lands
  on a hidden layer and vanishes.
- Layer commands keep guides with their layers: Duplicate (and Alt-drag
  copies of rows) copy a layer's guides onto the copy, Merge Selected and
  Flatten Artwork put the merged layers' guides on the merged layer and
  Flatten discards a hidden layer's. One shared doc predicate for "the
  layer a guide is on"; the deleted-layer cleanup moves into the doc.
- Tests: engine tests for the above and their undo, a doc check that an
  old guide reads with no layer and none is written, and a headless
  Layers panel test of guide rows (select, drag to another layer, locked).
- Translations for the new message; docs and a ROADMAP progress line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:03:48 +07:00
redrun6767 2972e2e6bf Guides: drag guide rows to another layer; put older guides on a layer
Guide rows in the Layers panel drag (the selected guides when the row is
one of them) onto any row of another layer, which lights up, and go to
that layer in one undo step (guide.setLayer takes indexes). Guides on no
layer, from files saved before guides had layers, go on the top visible,
unlocked layer when a document opens or is reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:47 +07:00
redrun6767 02e93902b3 Guides: ruler guides as layer objects
A ruler guide goes on the current layer (guide.add {layer}), shows,
hides, locks and is deleted with its layer, is listed in the Layers panel
as a row that selects it, and moves to another layer (guide.setLayer).
Guides on a hidden layer neither show nor snap; those on a locked layer
can't be picked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:47 +07:00
redrun6767 db39545a85 i18n: German for the panel-set strings
German is now a complete language; add the Drag to move out of the set
tooltip and the three errors window.panel.float and window.panel.dock
gained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
redrun6767 aa17dd7d2c i18n: Japanese for the panel-set command errors
Japanese now translates every status and error message; add the three
messages window.panel.float and window.panel.dock gained.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
redrun6767 b26612142f UI: dock columns scroll when their panels don't fit the window
A dock column's groups scroll under its top strip, each open panel at
least 160 points tall, instead of running off the bottom of the window.
Drop zones in a column only use the part of a group in view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
redrun6767 a7279231cb UI: drop panels anywhere in a set or dock column, and on its tabs
A group's upper half inserts the dropped groups right above it and its
lower half right below it, in floating sets and dock columns alike, and a
dock column's room under its groups puts them at its bottom; the edge
bands were too thin to hit, and the one above a dock column's top group
lay outside the column. The tabs a drop stacks panels with now light up
over the panels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
redrun6767 77667945b1 UI: dock floating panel sets as dock columns
A floating set dropped on the left edge of the icon column or of a dock
column docks as a new dock column there, building a multi-column dock.
Its groups stack, take tabs and tear out as in a floating set; the strip
along its top floats the column again and its × docks its panels where
they live. Popped-out icon panels open left of the dock columns. Columns
are kept in preferences and workspaces; window.panel.dock takes column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
redrun6767 faa3678a3f UI: stack floating panel groups into sets
Floating panel groups dropped on another group's top or bottom edge join it
in a set: the groups stack top to bottom, move together by the top group's
title bar (whose × docks them all), and a group dragged by its tab strip
leaves the set. A double-click on a tab collapses a group to its tabs.
Sets and collapsed groups are kept in preferences and workspaces, and
window.panel.float takes below, above and collapsed.

Behaviour from public documentation:
https://helpx.adobe.com/illustrator/using/workspace-basics.html

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 06:03:25 +07:00
bflatastic f5bf327447 Review: rustfmt; ROADMAP progress log (#1028)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:41 +07:00
lloyd 1622a70f85 #1028: Offset Path finishes where the sweep looped forever
The sweep under Offset Path (linesweeper 0.4) never finished on some
miter offsets: on the reported glyph and on a synthetic outline it kept
allocating until the app ran out of memory. linesweeper 0.5 fixes that
infinite loop ("Fixed an infinite loop", 0.5.0 changelog), and builds
without changes.

0.5 can hand back a curve piece that doesn't follow the input curve it
was cut from. In the Shape Builder rosette test, a contour runs out along
one petal and back by such a piece, which became a 59 x 4 pt hole in the
merged shape. Tidy::repair rebuilds a piece that strays from the one input
curve through both its ends as that curve's exact sub-curve (input curves
are indexed by their bounds' left edge, so it stays cheap). The there-and-
back then encloses nothing and is dropped as a sliver. Pieces on their
curve, and pieces whose ends two input curves pass through (a real lens),
are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:41 +07:00
mateususa 580ada65d5 #1026: Properties › Transform reaches Scale Corners and Scale Strokes & Effects
The Transform panel has both options; the Properties panel's Transform
section had no way to them. A More Options button after the flip buttons
now opens a popup with the two (the scaleCorners / scaleStrokes prefs),
open while they are toggled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:24 +07:00
mateususa 7abce8426a #1026: the Color Picker shows the colour on the selection live
With art selected the picker changed it only on OK. Each new colour now
previews paint.setFill / paint.setStroke on the selection through the
session interaction (the spec is preview: true): OK keeps the last one as
one undo step, Cancel puts the art back. Nothing previews until the colour
first changes, nor without a selection (the colour then only becomes the
default paint, on OK).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:24 +07:00
mateususa 0eb1e463b9 #1026: the flip buttons' icons match their flips
Lucide names its flip icons for their mirror line: flip-horizontal-2
draws a horizontal line with triangles above and below (the top-bottom
flip). The Properties and Transform panels showed it on the left-right
flip, and the other on the top-bottom one. Swapped back, with a comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:24 +07:00
mateususa 0b0ccfb35a #1026: the rotate cursor wraps the corner it is at
Beside every corner of the bounding box the curved rotate cursor bowed
up. It now bows from the box's centre toward the pointer, in 45° steps
(Cursor::RotateToward, made by Cursor::rotate_about): at the top right
it opens toward the bottom left, at the bottom right toward the top left.
The Selection tool shows it over the rotate zone and while rotating (it
follows the pointer round), and Free Transform does the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:24 +07:00
mateususa 0abb4157c9 #1026: open filled paths are hit by their fill on every side
fill_contains promised to close open subpaths (as their fill does) but
counted kurbo's winding, which leaves the closing edge out: a point beside
it read as outside. An open path (an anchor deleted from a closed one) lost
its fill clicks on that side, and a reflection moved the edge across, so it
looked like Reflect broke selection. Open subpaths are now closed by a
straight line back to their start before the winding is counted; closed
paths are borrowed as they are.

Tests: the open path from the report, on both sides and even-odd; reflected
shapes, imported SVG hearts, a selected shape dragged by its fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 05:30:24 +07:00
redrun6767 a22bced150 Guides: Shift selects ruler guides and art together
A Shift-click on a guide adds it to the selected art (guide.select
toggle keeps the art), and Shift-adding or -removing art keeps the
selected guides; a new selection still clears them. With both selected,
Delete deletes the art and the guides in one undo step (not when ids are
given).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 04:35:15 +07:00
redrun6767 adad531adb PDF import: a font the file doesn't carry keeps the style its name says
A PDF that names a font without embedding it (here a standard Helvetica)
is drawn with a stand-in, so comparing its glyphs with the installed
family's styles picks whichever style happens to look most like the
stand-in: with a large Helvetica family installed, Helvetica became
Helvetica CE Medium (tests_import_fidelity::text_becomes_point_type
failed on such a Mac). Fonts without a font program now keep the face
their name resolves to; embedded fonts are still compared as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CC5DiGJEZ5ADaQMBEQk4W1
2026-10-11 04:34:52 +07:00
Katt a3316d2383 Links: wake for the first look at a link placed while idle
The link watch wakes the app every two seconds only while the last look
found links (068a934, #967). A file placed less than two seconds after a
look that found none asked for no more frames, so while the window sat
idle nothing stamped it; a change made meanwhile was taken for the file
as first seen and never acted on. The watch now remembers the active
document's uid and revision at each look and, when they differ on a
later frame, asks for one wake when the next look is due. There is
still no per-frame walk of the links, and no two-second wakes for a
document without links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:10:33 +07:00
bflatastic 683b1e9845 #1011: DXF US survey inches, yards and miles import at their size
$INSUNITS 22, 23 and 24 (US survey inch, yard and mile, as Autodesk's INSUNITS reference
lists them) had no size or name, so the drawing opened as unitless (millimetres, or inches
with $MEASUREMENT 0) and document.dxfInfo said Unitless.

They now derive from the US survey foot (1200/3937 m) like code 21, with their names.

The test reads each survey code's unit name and default scale and opens a survey-inch line at
7200.0144 pt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:47 +07:00
bflatastic a0438f73f9 #1010: SVGs with one absolute and one unitless root side import unstretched
A root such as width="96pt" height="96" took CSS pixels (0.75 pt) along x and points along y,
so a square came in 24 x 32 pt and the artboard 96 x 96 instead of 96 x 72; with a viewBox the
art was stretched too.

When either root side is absolute, both axes now take CSS pixels at 96 per inch (usvg already
parses at that resolution): the unitless or px side is the same CSS length it would be in a
browser. The root scale is one number.

The test opens width="96pt" with height 96, 96px and 72pt, and width 128 with height 72pt: a
96 x 72 artboard and a 24 x 24 square; with viewBox 0 0 32 32 a 72 x 72 square at x 12 (meet),
and with preserveAspectRatio="none" the whole artboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:43 +07:00
bflatastic b23a402df8 #1009: Use Precise Cursors makes the Pen's join pointer a crosshair
Cursor::precise listed every Pen state but PenJoin (added later, #794), so with Use Precise
Cursors on the Pen still showed its join badge over another open path's end.

PenJoin is a crosshair too under the preference.

The Pen join test now also checks the pointer over the other path's end is the crosshair with
the preference on (and PenJoin with it off, as before).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:39 +07:00
bflatastic fd52ca32c7 #1008: a polar grid with no radial dividers draws none
shapes::polar_grid drew at least one radial divider (`radial.max(1)`), so radial 0, which
the command and the Polar Grid tool (Left arrow while dragging) accept, still drew a spoke.

Zero now draws none; the loop never divides by it.

The test checks 0, 1 and 2 radial dividers with no concentric ones give 1, 2 and 3 paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:35 +07:00
bflatastic 0497c9e46d #1007: Reverse Gradient mirrors off-centre midpoints
Reversal read the midpoints after Gradient::reverse had reordered the stops, so each stop
took the wrong segment's midpoint: red-to-blue with its midpoint at 20% reversed to a 50%
midpoint instead of 80%, moving the transition.

Gradient::reverse now mirrors each segment's midpoint with it, so a reversed gradient is the
same ramp the other way round; paint.editGradient's own (wrong) pass is gone, and the SVG
importer's reflect spread, which had a correct copy of the same mirroring, uses it too.

Tests: two- and three-stop gradients with off-centre midpoints sample the same reversed (t vs
1 - t) and come back after two reversals; paint.editGradient reverse turns a 0.2 midpoint into
0.8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:31 +07:00
bflatastic cacc9e4f36 #1006: Select > All on Active Artboard selects on the active artboard
The menu item sends no artboard, so the engine fell back to the first one even with another
active in the Artboards panel.

The frontend fills in the active artboard (the view's, as the Artboards panel and navigator
show it) for select.allOnArtboard, as it already did for Paste in Place / in Front / in Back
(#693); the two now share one block, and the index is kept within the artboards. An explicit
artboard still wins; the engine is unchanged.

The test makes artboard 2 active, then 1, and invokes the menu item each time, then passes
artboard 1 explicitly with artboard 1 active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:27 +07:00
bflatastic 148ef1870a #1004: a connected app's screenshot renders the artboard asked for
The desktop app's ui.render (MCP `screenshot` when connected) always rendered the first
artboard and ignored `artboard`, which the headless backend honours.

It now renders the 0-based `artboard` (the first without one, an error for one that doesn't
exist), as headless does. control-protocol.md and the control.rs list show the parameter.

The test drives ui.render through the control channel on a 64 x 64 and a 96 x 48 artboard:
no index and 0 give 64 x 64, 1 gives 96 x 48 (48 x 24 at scale 0.5), 2 is an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:19 +07:00
bflatastic 7f17d980d4 #1003: MCP add_text colours type made on or in a path
add_text passed `color` to text.create only, and text.createInPath built its own character
style without a colour, so type on a path and path-based area type took the current fill.

text.createInPath now uses the same new-type style as text.create (size, font, style, color,
else the current fill), and add_text sends `color` on both routes.

The MCP test draws a blue rectangle (the current fill), turns it into on-path and area type
with color #ff0000, and reads red back through inspect_document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:15 +07:00
bflatastic eeb213e5a8 #1002: new art is refused when no layer is shown and unlocked
With every layer hidden or locked, the insertion layer fell back to the top layer anyway, so
drawing a shape, typing, pasting, making a graph, a flare, a grid, trim marks or a symbol put
the art on a hidden or locked layer: invisible, or unselectable by Select All. Illustrator
refuses to draw on a locked or hidden target layer (public forum answers, e.g.
https://community.adobe.com/t5/illustrator/can-t-draw-a-new-object-in-my-document/m-p/11107182).

DocState::target_parent is insertion_parent for new art, failing with "the target layer is
locked or hidden" when it would be a layer that isn't editable; those creation paths use it.
Another layer that is shown and unlocked still takes the art, as before. The message is in the
de, es, fr, it, ja, ru and uk catalogs. File > Place is left as it is (another branch reworks
it).

The test hides, then locks, the only layer: an ellipse, a polar grid, text and a paste each
fail with the message and leave the document unchanged; a new layer then takes a rectangle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:11 +07:00
bflatastic e77d15c70d #1001: named text styles take leading, kerning, mojikumi and other attributes left out at their defaults
Character and paragraph styles checked a new attribute against the keys its base style
serializes, and the JSON leaves out Auto leading and kerning, no mojikumi and other defaults.
So charStyle.new / setAttrs refused leading or kerning, paraStyle.new refused mojikumi, and a
style captured from text with explicit leading failed to apply to Auto-leading text, to
duplicate or to come back out of a library.

The check now also accepts every field name the style type deserializes (read from serde's
derive), still refusing unknown keys and the file-only justify_auto.

The test creates and edits styles with kerning, leading, small caps, mojikumi and direction,
applies a captured leading style to fresh text with and without Clear Overrides, duplicates it,
uses it from a library, and checks `nope` and `justify_auto` are still refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	ROADMAP.md
2026-10-11 03:51:06 +07:00
bflatastic 178f99d60e #983: supersample Art Optimized edges in exports
Asset Export, File › Export, Save for Web and Object › Rasterize drew
each shape with its own smoothed edge, so where two shapes met edge to
edge (Divide, Shape Builder, abutting art) both half-covered the pixels
on the line and the background showed through as a thin ghost border.

With Art Optimized (the default) these now supersample, as the option's
name in Illustrator says: the art is drawn with hard edges at four times
the size (three or two times for large images, within a 40-megapixel
budget and the renderer's 65,535-pixel side) and each block averaged
into one pixel. Pixels shared by two shapes are fully owned by one or
the other at the finer size, so no seam is left, and the outer edges
are still smooth. The canvas keeps shape-by-shape smoothing for speed;
None and Type Optimized are unchanged.

RenderOptions::supersample turns it on (exports and Rasterize set it).
A render test checks two triangles sharing a diagonal: a seam with
shape-by-shape smoothing, none supersampled, and the factor's limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:48:03 +07:00
bflatastic 9e09d53fd9 Review #1013: the stepper test from #1015 compiles and hits the spinner
The headless test taken from #1015 set `RawInput::modifiers`, which egui
0.36 doesn't have, and clicked left of the spinner (the right-aligned
label's text ends before its box). Modifiers now come as
`ModifiersChanged` events, as the Appearance panel tests send them, and
the click is placed from the field's text. It also checks Ctrl/Cmd steps
a tenth. It fails with the spinner ignoring modifiers (15 pt for 24).
rustfmt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:14:00 +07:00
Nigel Ihza Firdausy 6d5fdc8a2a test(ui): Shift-click on the Stroke weight stepper steps 10 pt (#991)
The headless panel test of #1015, kept on top of #1013's fix (#1015's
own change to the spinner is superseded by it).
2026-10-11 02:06:41 +07:00
Nigel Ihza Firdausy 057c2ee2c1 fix(widgets): honor Shift on numeric spinner buttons (#991) 2026-10-11 02:06:20 +07:00
bflatastic 5a9c6e2e06 Review #1012: whole numbers typed in command dialogs are integers
The PR kept a field's integer kind only while its value had been an
integer: typing 2.5 in Columns and then 3 sent 3.0, and the mosaic fell
back to 10 × 10 again. A whole number typed in any command dialog is now
an integer (`typed_number`) whatever the field held before; commands
that take any number read integers as well, and a fraction stays one.

Test: Object › Create Object Mosaic… opened from its menu item, 3 typed
over Columns and Enter makes a 3 × 10 mosaic (it made 10 × 10 before,
#1000).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:06:19 +07:00
Nigel Ihza Firdausy 130bacde12 fix(dialogs): preserve integral command parameters in numeric fields (#1000) 2026-10-11 01:59:54 +07:00
bflatastic 311d75fc97 Review #988: rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:58:43 +07:00
Nigel Ihza Firdausy c845c7f9e0 fix(crop): reject malformed crop rectangles without shifting coordinates 2026-10-11 01:58:31 +07:00
bflatastic 2ff5688511 Review #999: corners and from through finite_numbers; rustfmt
Same checks in a third of the code; the corners destructure into the
four points instead of being indexed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:51:12 +07:00
Nigel Ihza Firdausy 0d697ed79d fix(distort): validate the full corner quad, source bounds and targets 2026-10-11 01:50:31 +07:00
bflatastic 57050b0987 Review #998: one strict anchor parser; build fix for path.appendAnchor
The PR added `checked_anchor_from_json` next to the lenient
`anchor_from_json` and dropped path.rs's import of the latter, which
`path.appendAnchor` still called, so the engine didn't compile.
`anchor_from_json` is now the strict parser itself (handles through
`finite_numbers`), used by path.create, path.setAnchors and
path.appendAnchor alike: the Pen's appended anchor with a malformed
handle fails too instead of losing the handle. Tests: a non-boolean
`closed` and a malformed appended handle change nothing. rustfmt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 01:50:30 +07:00
Nigel Ihza Firdausy 743fe2521e fix(path): remove unused legacy anchor parser import 2026-10-11 01:49:22 +07:00