Commit Graph
2754 Commits
Author SHA1 Message Date
arhammusheerandClaude Opus 5.5 7d79a32748 #1172: look nodes up by id through a checked cache of their tree paths
A lookup by id walked the tree, so every edit, click and pointer move of a single object cost
time in proportion to the whole document. At 100,000 paths:

- hit test, per click: 1.85 → 0.35 ms
- handle drag, 5,000-anchor path, per move: 6.5 → 2.6 ms
- segment drag, per move: 5.8 → 2.7 ms

Document keeps every node's index path in a cache (id_index, never saved). The tree is edited
through public fields in many places, so the cache isn't kept in step with it; it is checked
instead. A cached path is used only while the node at that path still has the id, a check as deep
as the tree. A path that doesn't check out rebuilds the cache, and an id it doesn't have walks,
as before. A rebuild costs a few walks, so it happens only after a few such misses, which keeps
any edit pattern within a small factor of the walk. Ids that appear more than once are never
cached, so they keep their first place in tree order. index_paths uses the cache for small
batches and one walk for large ones.

The one case that answers differently from a walk: a second node with an existing id put in the
tree after the cache was built, earlier in tree order. The lookup then returns the original node,
also one with that id. The app doesn't make such nodes; damaged files with duplicates are cached
afresh when opened.

cached_lookups_follow_any_edit_of_the_tree checks index_path, node and index_paths against a
walk after inserting at the front, removing, swapping, moving between layers, deleting a layer,
and in a clone sharing the cache. Using cached paths without the check fails it. The doc and
engine suites (2,002 tests) pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
arhammusheerandClaude Opus 5.5 f8064ba3ce #1172: rasterize with up to 8 threads instead of 4
The automatic rasterizer thread count was one per core up to 4. On a 10-core Apple M2 Pro, 8
workers are faster (9 are slower again), and vello_cpu draws the same pixels with any number:

- fit page, 50,000 paths: 66.7 → 56.6 ms; after a pan 66.4 → 51.2 ms
- 400 % zoom, 50,000 paths: 17.8 → 15.7 ms (back under its 16 ms budget)
- fit page, 100,000 paths: 137 → 105 ms; 400 % zoom 33.0 → 25.8 ms

Machines with 5 cores or fewer keep their count, and Preferences → Performance and
VECTORCRAFT_RENDER_THREADS still override it. pixels_do_not_depend_on_the_thread_count renders
a page of live effects and 400 overlapping translucent objects with 0, 1, 4 and 8 workers and
gets the same pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
arhammusheerandClaude Opus 5.5 add27cb8fc #1172: keep live-effect geometry across frames
A node with live geometry effects (Offset Path, Outline Stroke, Warp, Transform, Roughen…) had
them computed again on every frame: Offset Path's curve offsetting and boolean sweep for every
object, every time the canvas redrew. Rendering 2,000 paths that each have Offset Path, warm:
175 → 11.8 ms.

The renderer keeps each node's effected geometry, and each appearance item's own geometry
effects on it, keyed by the node's Arc identity like its other per-node caches. The entry keeps
the Arc alive, so a hit is exact and an edited object (a new node) gets new geometry. Only the
node draw_arc is drawing uses the cache: its path comes from the node alone, so its effects give
the same result every frame. Entries not drawn for three frames are swept like the rest.

cached_effect_geometry_draws_the_same_pixels renders a page of Offset Path, Roughen + Drop
Shadow, Warp + Outline Stroke and Transform objects (with item-level Offset Path) cold, warm,
after a pan and with a fresh renderer, and gets the same pixels each time; after an edit, the
warm renderer draws what a fresh one does. Keying the cache by node id instead of identity fails
it. The render suite (216 tests, golden images included) passes unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
arhammusheerandClaude Opus 5.5 4b260c364d #1172: edit a large selection in time linear in the document, not quadratic
Every lookup by id walked the whole tree, and the edit path did one per selected id, so editing
a selection of n objects cost O(n²). Measured with the new `perf --deep` rows at 25,000 paths:

- Select All: 458 → 3.5 ms
- nudge the selection: 3,517 → 34 ms
- drag it with the Selection tool, per pointer move: 3,413 → 35 ms
- undo / redo: 407 → 3.1 ms

- vectorcraft_doc: Document::index_paths (every id's index path, in one walk; the first in tree
  order for a duplicated id, as index_path), node_at, node_mut_at (copy-on-write, as node_mut)
  and ancestry_at.
- apply_transform, roots_of, transform_patterns, doc_sane, Selection::prune and
  Document::paint_order look their ids up with one walk instead of one each.
- Selection::set dedups with a hash set instead of Vec::contains per id.
- vectorcraft-cli perf --deep: tracked rows for editing a whole selection (select, nudge, drag,
  undo, redo), live effects, every Pathfinder operation, typing in a long story, and memory.

tests_lookups checks the batched lookups against the one-id ones on nested trees with missing and
duplicated ids; storing the last duplicate instead of the first fails it. The doc and engine
suites (2,000 tests) pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmMmc1J8XeNLxhRihWv9cc
2026-10-11 23:26:53 +07:00
bflatastic cfe0a779f0 Review: Czech rows for Digits (#1201)
Czech is a complete catalog now (#1189), so it needs the new labels.
2026-10-11 22:18:06 +07:00
zakariayacineandClaude Fable 5.1 16af3cc3fa Type: Digits — Default, Arabic, Hindi and Farsi numeral systems
Adds the Character panel menu's Digits option (with the Middle Eastern /
Indic options, beside Paragraph Direction in the Paragraph panel) as a
character attribute: CharStyle::digits, saved only when set, with
text.setFormat {digits} and text.setRangeStyle {id, start, end, digits}
("default" | "arabic" | "hindi" | "farsi"; a bad value is an error).

Shaping draws every digit of the Western, Arabic-Indic (U+0660–U+0669)
and Eastern Arabic-Indic (U+06F0–U+06F9) sets in the chosen system,
whatever was typed, before font fallback, so a font without the
Arabic-Indic digits gets a fallback for them. The text, its clusters,
the caret, selection and search stay as typed, as with All Caps; SVG
export writes the digits as drawn.

Catalog rows for Digits, Arabic, Hindi and Farsi in the ten translated
languages. Docs: docs/mcp.md (Digits), type-parity.md (digit types
done; Character Direction, kashidas, composers and the split caret
still missing), target-app-parity.md and gaps.md.

Behaviour from the public documentation (a character-level option with
the values Arabic, Hindi and Farsi in the Character panel menu):
https://helpx.adobe.com/indesign/desktop/language-and-proofing/arabic-and-hebrew/choose-digit-types.html

Tests: doc (the mapping and serialization), text (glyphs of "page 3" in
Hindi/Farsi equal the typed Arabic-Indic digits' on the typed clusters;
skips without a font covering the three sets), engine (setFormat,
setRangeStyle, undo, error), svg (digits written as drawn), ui-egui
(the menu shows with the Indic options).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 22:18:01 +07:00
bflatastic d19fd80390 Review: progress log for the complete Czech catalog (#1189) 2026-10-11 22:18:01 +07:00
LosminekandClaude Opus 5.5 15beddbb74 i18n (cs): keep the language name "English", translate "List Folder"
The language switcher shows every language in its own name in every UI
language (language_and_appearance_are_in_the_app_menu), so "English" stays
"English" like in the other catalogs. "List Folder" is the file.list command
that arrived with #1181 and is a menu string the complete catalogs translate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:56 +07:00
LosminekandClaude Opus 5.5 4a2b2bb36a i18n (cs): terminology review fixes
Changes from an independent review pass over the Czech catalog. Short keys
shared between a menu and a dialog got the wording that fits both places
(All = "Vše", Add = "Přidat", Type: = "Typ:", Center = "Střed", Edge =
"Okraj", Advanced = "Rozšířené"); sentences that built a plural from a bare
count now read "Počet ...: {count}"; "Offset Path" is "Odsadit cestu", the
New... button is named consistently in the preset hints, "resolve" is not
translated as "rozlišit", and the rendering intent is "Záměr vykreslení".

Strings that need a context in the code to be right everywhere (Scale as a
dialog title, Random/Uniform as check boxes, Subset fonts below) are left as
they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:55 +07:00
LosminekandClaude Opus 5.5 d906a59287 i18n: Czech Expand reads "rozložit" in every label
Object › Expand, Expand Appearance and the rest of the catalog say
"Rozložit"; three older rows said "rozšířit" (widen) for the same
command: Expand Shaper Group and the two Revolve notes about expanding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:55 +07:00
LosminekandClaude Opus 5.5 a59d8533b7 i18n: Czech joins the complete-message languages; Czech reads as Czech
Czech is in COMPLETE_MESSAGES, so a new status or error message needs a
cs.tsv row too. czech_reads_as_czech checks the vocabulary (Nástroj
Pracovní plocha, Vzorník, Operace s cestami, Tah, Výplň), the three
plural forms of a layer count, the axis context and a nested message;
czech_plurals_have_three_forms now also runs every @plural row through
trn for one, few and other counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:55 +07:00
LosminekandClaude Opus 5.5 b9f4d9bd24 i18n: complete the Czech (cs) catalog: panels, dialogs, plurals and messages
The Czech catalog covered every menu label (726 rows); panels, dialogs,
tooltips and status messages fell back to English. It now has a row for
every key of the complete catalogs: every menu string and tl! literal,
the 13 plurals in one/few/other form, the axis/effect/lighting/theme
contexts and every @msg status and error message (3,404 rows added,
4,130 in all). The existing rows are kept as they were; the header says
how the new ones were written and lists the vocabulary.

With the catalog complete, Czech sets complete_menus, so the coverage
tests now hold it to every menu string and tl! literal.

Written clean-room from the meaning of the English text, drafted with AI
assistance; not yet reviewed by a native speaker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:55 +07:00
Wladmir Júnior e60487755b pt-BR: translate the remaining UI labels and engine messages 2026-10-11 22:17:50 +07:00
bflatastic d3f8ee14cc Review: leading of wrapped area type keeps the line distance (#1206)
Lines that wrap don't pair up with the text's breaks, so taking a
paragraph's spacing off the first pair could shrink ordinary leading.
2026-10-11 22:17:50 +07:00
Matthew FarrowandClaude Sonnet 5.5 77b247dea5 Part of #1102: type from the text document keeps its line spacing across paragraphs
The leading of a text object made from the file's text document was the
distance between the first two lines of the file's own layout. Those lines
are where the paragraph spacing has put them, and the text object applies
that spacing again, so paragraphs with a space after (or before) came in with
their lines too close (negative space) or too far apart (positive).

The leading is now that distance less the space after the paragraph before
and the space before the paragraph after; two lines of one paragraph have
none between them.

Tests on synthetic stories: negative and positive spacing, a frame scale,
line breaks inside a paragraph, a single line.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:17:45 +07:00
Hiroshi InukaiandClaude Opus 5.5 0e5e83cf5c EPS/.ai import: a font is found by its exact PostScript name before the name is split
The private data a .ai file opens with names fonts by PostScript name, and
the importer split the name at its first hyphen: rounded-x-mplus-1c-black
became the family "rounded", style "x-mplus-1c-black", which no font has,
so the type fell back to Source Sans 3 although Rounded-X M+ 1c is
installed. An installed or bundled face of that exact PostScript name now
gives its own family and style; other names split as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:40 +07:00
bflatastic 66fd1b78a2 Review: reuse the importer's find and number helpers (#1208) 2026-10-11 22:17:39 +07:00
Hiroshi InukaiandClaude Opus 5.5 996111b179 .ai import: copied area type and type on a path keep their own place
Read through its editing data, a .ai put the copies of area type and type
on a path on top of their original: the text document's frame of a copied
object keeps the original's path (with a matrix of its own that isn't
understood yet). The object's `/Art` path, which the layers' data keeps
for each text object, is where the file places it: area type and type on
a path now take it as their frame or path, by story and frame.

On a file of cards made by copying one, the type laid out from the text
document now lands on each card (515 text objects where its PDF part has
them, against 467, many on one card, before).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:17:32 +07:00
bflatastic 30ddaa78ff #955: a move grabbed by an anchor lands it on another anchor
With Smart Guides on, a selection moved by the Selection tool only lined
its bounds up with the other art: grabbed by an anchor and dropped near
another object's anchor, it could stop a fraction of a point beside it
where an edge lined up instead. When the grabbed point comes within the
snapping tolerance of an anchor or a centre, it now lands on it, as
Smart Guides' anchor snapping does.
2026-10-11 22:08:30 +07:00
bflatastic 06d3e52c24 #1180: Align aligns the selected anchor points
With anchor points selected by Direct Selection, the Align buttons moved
the whole path, so two points couldn't be lined up. As in Illustrator,
they now align the selected points: to the points' own bounds (Align To
Selection) or to the artboard. A path selected whole still aligns as an
object.
2026-10-11 22:08:01 +07:00
bflatastic 6f4049494d Test: the idle place test waits for the link scan in real time
tests_place::a_file_placed_while_idle_follows_its_changes failed at
random in CI (twice on 2026-10-11): while the link scan ran on its
worker thread, the test's clock moved on by whole simulated seconds per
5 ms of real time, so on a busy machine the 10 simulated seconds ran out
before the scan had read the changed file. The clock now waits for the
scan (up to 20 real seconds).
2026-10-11 22:02:50 +07:00
bflatastic 7f35522b15 #1197: Make Compound Path cuts holes where paths overlap
Make Compound Path kept each path's direction under the non-zero fill
rule, so a shape drawn inside or across another, running the same way,
left the overlap filled instead of making a hole. The paths in front of
the backmost one now run against it, so where they overlap it they cut
holes, as users expect from a compound path.
2026-10-11 21:32:23 +07:00
bflatasticandClaude Opus 5.5 947b294f87 Release: embed the current craft-fonts (Thai, Arabic, Chinese)
Releases pinned craft-fonts at its first commit, with the Japanese fonts
only. The pin moves to its current main, as PdfCraft, LightCraft and
WordCraft have moved theirs: desktop builds embed Noto Sans Thai, Noto
Sans Arabic and Noto Sans CJK SC too (the web build still embeds only
the UI font and fetches Thai and Arabic beside the wasm). Packages
already copy every embedded font's licence.

The Thai loanword test no longer assumes the bundled fonts' widths: it
checks that no line ends inside โปรแกรม, whichever Thai font sets it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:47:46 +07:00
bflatasticandClaude Opus 5.5 a08ca07c69 Type: the caret and double-click take Thai word by word
Ctrl/Alt+arrows and double-click treated a run of Thai (Lao, Khmer,
Myanmar) without spaces as one word, and stopped at a word's tone and
vowel marks, which didn't count as part of a word. They now stop at the
word boundaries the line segmenter finds in those scripts (shared with
line breaking, so no more data ships), and combining marks count as
part of a word, also for combining accents in other scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:06:39 +07:00
bflatasticandClaude Opus 5.5 70e99987e9 Type: Noto Sans Thai from craft-fonts is the Thai fallback, on the web too
The web build has no installed fonts, so Thai there showed as empty
boxes. With a craft-fonts checkout that has Noto Sans Thai, its Thai
font joins the document font database after the Arabic ones (the
fallback for Thai where no Thai font is installed) and the web build
fetches it before the app starts, as it does Noto Sans Arabic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:52:08 +07:00
bflatasticandClaude Opus 5.5 cc2f45b129 Font menu: a Thai script filter
The font menu's script filter offered Japanese and Latin only, so Thai
fonts had to be found by name among every installed family. It now has
Thai too: the families whose character map has Thai. Latin lists the
families with neither Japanese nor Thai.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:51:32 +07:00
bflatasticandClaude Opus 5.5 5a855971aa Type: Thai, Lao, Khmer and Myanmar lines break between words
These scripts don't separate words with spaces, and lines only broke at
spaces, hyphens and CJK characters, so a Thai paragraph in area type ran
on to the next space (or was cut wherever it overflowed). Each
paragraph's glyphs now carry the word ends ICU4X's line segmenter finds
in text of those scripts (its dictionaries on the desktop, its far
smaller LSTM models on the web), and a line may break after them as
after a space. Other text keeps its own breaks, and clusters (a
consonant with its vowel and tone marks) still never split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:31:37 +07:00
bflatasticandClaude Opus 5.5 3fb51869ac Type: the caret steps over whole clusters (Thai marks, accents, emoji)
The Type tool's Left and Right arrows and Delete moved one code point at
a time, so the caret could stop between a Thai consonant and its vowel
or tone marks (or inside an accented letter or emoji sequence), where
typing or deleting splits the cluster. They now step over extended
grapheme clusters. Backspace still takes back one character at a time,
as Thai typists expect for a mistyped tone mark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:29:16 +07:00
bflatasticandClaude Opus 5.5 fba3702103 Type: Thai falls back to a font made for Thai
Thai typed in a font without Thai glyphs (Source Sans 3, a Latin font)
took the first loaded face that had them, or the first installed file
that did, in path order: on one Windows machine a display face (Kanit)
rather than Leelawadee UI. Characters of Thai, Lao, Khmer and Myanmar
now try fonts made for their script first, installed ones loaded on
demand (Leelawadee UI, Tahoma, Thonburi, Sukhumvit Set, Noto Sans Thai,
the TLWG fonts for Thai), then the fallback as before. Latin beside
them keeps the font chosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:31 +07:00
bflatasticandClaude Opus 5.5 339b01f48b #1163: file.list lists a folder for agents, held to the read root
An agent driving VectorCraft over the confined MCP server had no way to
see which files it was given, so it guessed names. `file.list {path}`
lists a folder's files (with their sizes) and folders by name, held to
the automation read root like every read; links are listed as links and
not followed. At most 5000 entries, with `truncated` saying when there
were more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:35:23 +07:00
bflatasticandClaude Opus 5.5 ed80181d70 #1163: command_batch can be one undo step
An agent's build over MCP left one undo step per batch step (about 280
for one job). `command_batch` takes `atomic: true` (and an optional
`label` naming the step) to make the whole batch one undo step. It runs
the batch in an undo group, opened and closed by the new commands
`edit.undoGroup.begin {label?}` and `edit.undoGroup.end {cancel?}`, so
every backend (headless or the connected app) supports it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:22:45 +07:00
bflatasticandClaude Opus 5.5 4603ebec7a Review: report module beside rasterfx, ids checked without a tree walk, docs trimmed (#1147)
The report module moves to cmd/rasterfx/report.rs (the crate's foo.rs +
foo/ layout instead of #[path]). An object is the document's when its id
is below the document's next id, so building the report no longer walks
the whole document (and its symbols and patterns) on every PDF, SVG,
print and clipboard export. The parity docs say what changed in the
status line and one evidence bullet instead of a separate paragraph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:17:25 +07:00
KnottyDyes 8d881cc0bc PDF export: warn when raster effects render below the requested resolution
The 64 million pixel cap can silently lower raster-effect resolution. Report each original object's id and name with requested and achieved ppi through the existing export warnings, retaining the lowest resolution across its images.

Keep the cap and rendering unchanged, cover capped and uncapped exports, and record the evidence without changing parity scores.
2026-10-11 18:17:25 +07:00
bflatasticandClaude Opus 5.5 805d30e284 Review: DXF outlineText note and new-art wording in the MCP docs (#1148)
DXF already outlines type unless preserve: editability, so say so next to
the outlineText flag; reword the new-art sentence and point to paint.default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:14:25 +07:00
KnottyDyes 4505ccb20d MCP: drawing tools describe inherited paint and export text options
Omitted paint fields keep the current appearance, so agents can get a black outline when they only set a fill. Document the initial white fill and 1 pt black stroke and recommend explicit paint for outline-free art.

Clarify SVG/SVGZ outlineText versus PDF options.advanced.outlineText and PDF's live-text default, noting DXF also accepts the top-level flag. Descriptions and documentation only; drawing and export behavior are unchanged.
2026-10-11 18:14:23 +07:00
bflatasticandClaude Opus 5.5 c3719b2e79 #1163: MCP render_preview takes an artboard index
render_preview only rendered the first artboard, so an agent couldn't
look at pages 2 and on of a multi-artboard document without switching
to screenshot. It now takes an optional 0-based `artboard` (as
screenshot does) and refuses an index the document doesn't have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:13:26 +07:00
bflatasticandClaude Opus 5.5 9b12fff215 #1163: Release splits a path of several subpaths into one path each
A path of several subpaths (an SVG `d` with several `M`s, PDF paths, a
path drawn in one go over MCP) is a compound path in Illustrator, and
Object › Compound Path › Release splits it into separate paths. Here
Release only acted on compound path objects, so such a path couldn't be
broken apart. Release now gives each subpath a path of its own, with the
path's paint, in stacking order; the first keeps the path's id. One undo
step, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:58:13 +07:00
bflatasticandClaude Opus 5.5 da735914b6 #1158: Alt-click on a layer's eye or lock isolates that layer
Hide Others, Lock Others and Outline Others left the layer they keep as
it was, so Alt-clicking the eye of a hidden layer hid everything and
Alt-clicking a locked layer's lock left it locked. As Illustrator's
Layers panel does, the layer clicked now shows (unlocks, previews) while
the others hide (lock, outline), in the same undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:50:35 +07:00
bflatasticandClaude Opus 5.5 d3c997329d #1111: a blend's spine bends into a curve
The Anchor Point tool drags any segment of a selected blend's spine into
a curve, straight ones too, and Direct Selection reshapes a curved spine
segment as it does a path's (a straight one it leaves, as on paths). Both
run the new `object.blend.spine.reshapeSegment`, which bends the segment
with the same math as `path.reshapeSegment`, now shared as
`SubPath::bend_segment`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:49:41 +07:00
bflatasticandClaude Opus 5.5 1f5073e4d1 #1111: a selected gradient mesh shows its mesh lines and points
Only the Mesh tool drew a gradient mesh's grid, so with the Selection
or Direct Selection tool a selected mesh showed no outline and no
points, and its points couldn't be seen to be clicked. The selection
highlight now draws a gradient mesh's lines and points, as it already
did for mesh envelopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:36:23 +07:00
bflatasticandClaude Opus 5.5 a809f270eb #1151: Preferences › Performance › Linux display server opens the window through Xwayland, where file drops work
winit 0.30 delivers no file drops on native Wayland. The new Linux Display
Server preference (Automatic / X11 (XWayland), shown on Linux only, read at
startup) has winit open the window on X11 when the session is Wayland,
DISPLAY is set and the X11 libraries winit loads are installed (ldconfig
check ported from PhotoCraft); otherwise the window opens on Wayland and
the log says why. README, development notes, parity docs, ROADMAP and the
complete catalogs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:29:23 +07:00
bflatasticandClaude Opus 5.5 171c382618 #1103: a marquee selects only the objects whose paths it touches
A marquee drawn wholly inside a filled shape counted as touching it, so
a marquee over a background (the usual way to pick small objects off it
in Outline view) selected the background too. As in Illustrator, the
marquee now needs the path itself: an anchor inside it or a segment
crossing its edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:16:33 +07:00
bflatasticandClaude Opus 5.5 c7522b7f0c #1152: Offset Path opens on the values it was last applied with
Object › Path › Offset Path (and `ui.menuDialog` for it) opens on the
offset, joins and miter limit of the last time OK was pressed, as in
Illustrator, instead of 10 pt and Miter every time. The values are kept
with the rest of the remembered dialog options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:16:16 +07:00
bflatasticandClaude Opus 5.5 f68f66a579 #1133: Zig Zag's dialog has sliders and radio buttons, as in Illustrator
Effect › Distort & Transform › Zig Zag… lays out its options the way
Illustrator's dialog does: Size and Ridges per segment each have a
slider beside their field, Relative / Absolute are radio buttons under
Size, and Points offers Smooth and Corner as radio buttons instead of a
dropdown. The label reads "Ridges per segment".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:46:14 +07:00
bflatasticandClaude Opus 5.5 fbc1c494b2 #1144: Live Corners round corners between curved segments too
A corner is now any anchor that isn't smooth where two sides meet at an
angle, straight or curved, as in Illustrator. The cut meets each side at
its setback measured along the curve, leaves both sides along their
tangents there, and each curved side is trimmed to what the cuts at its
ends leave of it. The largest radius is still the one taking the cut
halfway along the shorter side, measured along the curve. Corners
between straight sides draw exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:14:08 +07:00
bflatasticandClaude Opus 5.5 255fefe71f Render: a shape far beyond any page is left undrawn, never crashing the app
A property test of the importers (import_fuzz) crashed CI with
STATUS_STACK_BUFFER_OVERRUN: a shape with coordinates in the 1e20s from a
mutated file made vello_cpu either panic in flatten_simd on a rayon
worker (rayon then aborts the process, so the engine's panic guard never
sees it) or ask for a 132 GB allocation, which aborts too. A malformed
file could end the app and lose the user's work.

draw_shape now leaves a shape undrawn when its control points, through
the view, aren't finite or reach past 1e10 pixels, or a visible stroke
is that wide (MAX_DEVICE). A document's art stays within 4 million
points, 2.6e9 pixels at the closest zoom, and vello was checked safe
with 90,000 random shapes within 1e9 pixels and 30,000 within 1e12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:30 +07:00
bflatasticandClaude Opus 5.5 423cb18f40 #1139: Shift-click selects several artboards, which move and delete together
The Artboard tool and the Artboards panel only ever had one artboard
selected, so Shift-clicking another didn't add it. As in Illustrator:

- Artboard tool: Shift-click adds an artboard to the selection (it
  becomes active) or takes one out; a plain click on a selected one keeps
  the selection, so a drag moves them all (Alt copies them all, the
  copies becoming the selection), and Delete removes them all, one
  artboard always staying. The other selected artboards show dashed
  bounds; `selected` is a tool option.
- Artboards panel: with the Artboard tool, Shift- or Ctrl/Cmd-click on a
  row adds or removes it, every selected row is highlighted, and Delete
  Artboard removes them all.
- `artboard.move` and `artboard.delete` take `indices` besides `index`:
  several artboards in one undo step, each with its art (an object moved
  once where artboards overlap).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:04:30 +07:00
Biromedic f4035c98d1 Fix #1141: SVG import keeps a mask whose art is all hidden, so its object stays hidden
Mask art that is all visibility:hidden imports as nothing, and opacity_mask
then returned no mask: the object was drawn whole. An empty mask is what the
file means (black all over); the renderer and the PDF and SVG exports already
draw empty mask art by its clip and invert options.
2026-10-11 16:00:38 +07:00
Biromedic 1726c600d2 Fix #1140: gradient stops blend colour and opacity apart on the canvas, as the exports do
The canvas built its peniko gradients premultiplied (peniko's default), while
Gradient::sample, the PDF export (shading plus SMask) and SVG viewers blend
colour and opacity separately: a transparent white stop into black drew grey
127 halfway instead of 191. Opaque gradients are unchanged.
2026-10-11 16:00:36 +07:00
dotCooCoo d79aa607cc Lint: clippy -D warnings passes for the wasm32 web build
On main, cargo xtask wasm printed two dead-code warnings in
vectorcraft-ui-egui, and this clippy run reported the same two as
errors:

    cargo clippy --target wasm32-unknown-unknown -p vectorcraft-web -- -D warnings

ask_update_changed (dialogs/missing_links.rs) is called only from
tick_link_updates, and is_c_locale (i18n/mod.rs) is called only from
detect_system_lang. Both callers are compiled only for native targets.

Each function now has the same #[cfg(not(target_arch = "wasm32"))]
attribute as its caller. Native builds compile both functions as
before.
2026-10-11 16:00:33 +07:00