Commit Graph
1450 Commits
Author SHA1 Message Date
Louise FoxandClaude Opus 5.5 2f893566ee Export As: a held Enter doesn't confirm the options; exporters get their file
Follow-ups to the review of the exporter plug-ins PR (#3171):

- Export As opens its format's options once the save panel has picked
  the file. The Enter that closed the panel, still held, confirmed
  them at once: Save As marks such a dialog (dialogs::AFTER_PANEL) so
  it waits for Enter to be let go, Export As didn't. Every options
  dialog it opens after the panel is marked now, an exporter plug-in's
  included, through one helper (dialogs::opened_by_panel) that Save As
  uses too.
- io::export with options that aren't a JSON object dropped the path
  and format it adds for the encoder: an exporter plug-in was told the
  document's name and its first extension instead of the file it
  writes. Such options now count as none.
- The exporter UI tests remove the plug-ins they install, so no other
  test's Export As lists them.

tests_export.rs reproduces the held Enter for every options dialog
(PNG, SVG, Save PDF, Text, DXF, EPS and an exporter's), and
tests_plugin_export.rs the options that aren't an object; both failed
before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-22 06:40:28 +07:00
Cursor Agentandnrq6993-a11y 6066b4b8c7 Properties: regression test for ellipse pie in Transform section (#3451)
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 06:29:30 +07:00
Cursor Agentandnrq6993-a11y 718c65c855 Ellipse pie angles in Transform and Properties (#3451)
## Summary

Live ellipses already support pie start/end angles, canvas widgets and Invert Pie
like Illustrator. Label the Properties panel Transform section with Ellipse
Properties so the pie fields match the Transform panel and are easier to find.

## Tests

- `an_ellipse_shows_its_pie_properties` in `panels/transform` tests.

Closes storytold/vectorcraft#3451

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 06:29:30 +07:00
dotCooCoo 4e4723699d Export: EPS, legacy .ai, EMF/WMF and DXF give one note for images that can't be decoded
EPS, EMF, WMF and DXF exports gave each image they left out because it
couldn't be decoded its own warning, "image '<key>' could not be
decoded and was left out", which names the internal key the document
stores the image under. A version 7 .ai said "an image that can't be
decoded or is too large was left out".

The four writers now give the note raster exports, PDF and the legacy
.ai flattening give, "an image couldn't be decoded and is missing from
the picture", once per export. In a version 7 .ai it is one line with
the note for flattened art. The other image notes in the same
functions no longer name the key: EPS's note for an image too large to
write, and DXF's notes for an image with no pixels (its rows in 23
catalogs are reworded) and one that could not be written. Legacy .ai
gives a separate note for an image that is empty or too large.

Docs: docs/mcp.md (EPS, DXF export, EMF and WMF, aiVersion "7").
2026-10-22 06:13:32 +07:00
dotCooCoo 3bb0f07a4b Tests: the data set export test fails when its folder cannot be emptied (#3389)
variables_panel_data_sets_ask_off_the_ui_thread empties its folder
before Export Data Sets… writes out.csv, then checks that the file is
exactly the header and the row for data set B. It ignored the result
of remove_dir_all, and create_dir_all succeeds on a folder that is
still there. When the folder could not be emptied, an out.csv left by
an earlier run passed the check even when the export wrote nothing.

The test now fails when emptying the folder fails with an error other
than NotFound.
2026-10-22 06:07:55 +07:00
Cursor Agentandnrq6993-a11y c8082facb1 G11: Align panel moves ruler guides to artboard (#3463)
## Summary
object.align now moves selected ruler guides when aligning to the artboard,
matching Illustrator and the existing Distribute behaviour. Guide-only
selection can run align; the Align panel enables its buttons when only
guides are selected. Guides still stay put when objects are in the selection
and Align To is Selection; key-guide behaviour is unchanged.

## Tests
- engine tests_keyguide: guides_selected_alone_align_to_artboard; updated
  selected_guides_align_to_a_clicked_key_object for artboard align
- ui-egui align panel: the_align_buttons_align_guides_selected_alone_to_artboard

Closes storytold/vectorcraft#3463

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 05:20:10 +07:00
dotCooCoo bd9a508d49 Save As: the suggested extension typed in another case gets the picked type's
On Linux and the BSDs, where the save dialog reports the file type
picked in it, a name that still ends in the suggested name's extension
gets the picked type's extension. The comparison was case-sensitive,
so Untitled-1.VECTORCRAFT with "PDF-compatible .ai" picked was saved
as a native file named Untitled-1.VECTORCRAFT instead of Untitled-1.ai.

with_picked_type now compares the lower-case extensions that
fileio::extension gives. A name typed with the picked type's own
extension in another case (a.AI with .ai picked) stays as typed.

This follows up #3372.

Docs: docs/target-app-parity.md.
2026-10-22 05:09:55 +07:00
BFlat b15230bfb5 Merge pull request #3461 from lehni/update/canvas-partial-redraw
Canvas: an edit redraws only the area it changed
2026-10-22 05:09:38 +07:00
Jürg Lehni ede08590a0 Canvas: an edit redraws only the area it changed
Wide effect rasters cropped to the frame also serve parts of it.
2026-10-22 04:45:43 +07:00
bflatastic 92f295d2a2 Blend: Ease In / Ease Out and separate Color Ease in Blend Options (#1124)
Squashed from #2539 onto main.
2026-10-22 04:40:10 +07:00
bflatastic 34cde7c3db Character: Character Direction sets the selected characters left to right or right to left
Squashed from #3436 onto main.
2026-10-22 04:18:35 +07:00
bflatastic b3268a829d UI.8: collapse dock groups to their tabs
Squashed from #3281 onto main.
2026-10-22 04:07:25 +07:00
bflatastic 9cda8452ab UI.6: dock panels into any dock group
Squashed from #3280 onto main.
2026-10-22 03:40:36 +07:00
gorkematlynandClaude Opus 5.5 d6d91c0954 UI.7: match workspace names whatever their case
find_saved matched names case-insensitively but save_as, remember and
switch compared them exactly: "Mine" saved over "mine" made a second
workspace, and changes kept as "Mine" didn't come back when switching
to "mine". save_as now replaces a user workspace of the name in any
case, remembered layouts are kept under the saved spelling and looked
up whatever the case, and sanitized maps remembered layouts to the
saved spelling and keeps one per workspace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KcHisW1vt6pkVPYbzL6Cpg
2026-10-22 03:40:18 +07:00
gorkematlynandClaude Opus 5.5 e3e761fe32 UI.7: doc comments on the workspace tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KcHisW1vt6pkVPYbzL6Cpg
2026-10-22 03:40:18 +07:00
gorkematlynandClaude Opus 5.5 81de0f6fbf UI.7: keep the newest remembered layout when the list is full
remember() pushed the layout being left and then truncated the list to
64, so with 64 workspaces already remembered it dropped the entry it
had just pushed and the changes were lost. Drop the oldest entries
instead; a test fills the list and checks the last one left survives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KcHisW1vt6pkVPYbzL6Cpg
2026-10-22 03:40:18 +07:00
gorkematlynandClaude Opus 5.5 4308821285 UI.7: workspaces keep their layout changes
A workspace remembers the changes made in it: switching away keeps them
(UiState::workspace_changes, kept with the preferences), switching back
restores them, and Reset <name> returns to the saved layout. Saving a
workspace starts it with no changes; renaming keeps them, deleting drops
them, and deleting the current workspace shows Essentials as it was
left. Saved preferences recover a damaged remembered layout instead of
losing the whole file.

Split from #2965 as a reviewer asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KcHisW1vt6pkVPYbzL6Cpg
2026-10-22 03:40:18 +07:00
Don Omondi a3b2b18cb0 M3: preserve highlighted Eyedropper ranges and reject incompatible transparency 2026-10-22 02:58:48 +07:00
bflatastic 10a178f64a Shape Builder: Gap Detection and Tool Options (#2523)
Squashed from #2526 onto main.
2026-10-22 02:53:17 +07:00
bflatastic 62776d5f46 Recolor: recolor.apply takes a model option for the new colour's model (#2170)
Squashed from #2520 onto main.
2026-10-22 02:01:57 +07:00
Cursor Agentandnrq6993-a11y 59f4b753dd G4: mark Snap to Glyph done in parity docs and test the menu toggle
## Summary
Aligns parity checklists with shipped View › Snap to Glyph behaviour (#3124):
when Smart Guides are on, drawing tools snap to live-text baselines and glyph
bounds via `view.snapToGlyph`. Adds a headless test that `view.snapToGlyph`
toggles the UI view flag like Illustrator’s menu item.

## Tests
- `vectorcraft-ui-egui`: `snap_to_glyph_menu_command_toggles_the_view_flag`
- Existing `tools::text_tests::snap_to_glyph_lines_up_new_point_type_with_a_baseline`

Part of gap G4

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-22 01:25:51 +07:00
bflatastic eb9e064254 G2: Effect › Artistic › Fresco raster filter
Squashed from #3096 onto main.
2026-10-22 01:15:09 +07:00
bflatastic 5925e6339a G2: Effect › Sketch › Torn Edges raster filter
Squashed from #3010 onto main.
2026-10-22 01:06:25 +07:00
bflatastic b45bb3dd54 Character panel Tate-chu-yoko marks vertical type as a horizontal block
Squashed from #3399 onto main.
2026-10-22 01:06:24 +07:00
Jürg Lehni a92f0264c2 Canvas: each update copies the rendered frame whole instead of pixel by pixel
The renderer's pixels and egui's are both premultiplied RGBA8, so color_image copies the bytes in one go. At 3456×2234 that takes 0.9 ms instead of 3.4 ms per update, at 5120×2880 1.6 ms instead of 6.8 ms. Every rendered image the UI shows (canvas, thumbnails, previews) goes through it.
2026-10-22 00:54:44 +07:00
bflatastic a5c0c29b25 G2: Effect › Pathfinder › Soft Mix blends overlapping CMYK fills
Squashed from #3063 onto main.
2026-10-22 00:54:43 +07:00
bflatastic adbe0566a6 G4: System Layout spaces every text object in whole points
Squashed from #3412 onto main.
2026-10-22 00:54:43 +07:00
dotCooCoo a3e7dc9e41 i18n: the message scan reads errors made with bad() and error variants, and new ones need rows
complete_languages_translate_every_message read a message literal only
right after a marker such as Err( or Other(. It did not read the
message given to the engine's bad(cmd, ...), so this test did not
require a row for a parameter error. It also did not read the messages
given to the engine's err() and error(), to the PDF writer's bad() and
failed(), or to an error enum's variant such as PsError::Limit(...).
1,122 of these messages have no row in any complete catalog.

The scan now reads those messages, a literal or a format! string, in
the same template form as the catalogs' rows. It skips PsError::Ps and
EngineError::UnknownCommand, whose first argument is a PostScript error
name or a command id. The 1,122 messages are listed in
untranslated_messages.txt next to the tests, together with the 71 that
pull requests open at 009384b add without rows, and the test lets a
listed message through whether or not it has rows. Every other message
needs a row in each complete catalog, so a new message made this way
fails the test until it has its rows.
VECTORCRAFT_I18N_DUMP_UNTRANSLATED=<file> writes the messages in the
sources that lack a row. No catalog rows change.

Docs: docs/development.md, docs/localization-parity.md.
2026-10-22 00:40:10 +07:00
bflatastic 03a2da13ba G2: Effect › Artistic › Colored Pencil raster filter
Squashed from #3028 onto main.
2026-10-22 00:30:39 +07:00
bflatastic 755f2b9158 G2: Effect › Artistic › Rough Pastels raster filter
Squashed from #3044 onto main.
2026-10-22 00:23:50 +07:00
bflatastic 695f7c0d4b G2: Effect › Artistic › Underpainting raster filter
Squashed from #3035 onto main.
2026-10-22 00:23:50 +07:00
bflatastic 03bd5e5a53 G2: Effect › Artistic › Paint Daubs raster filter
Squashed from #3032 onto main.
2026-10-22 00:23:49 +07:00
bflatastic f329f6271d G2: Effect › Artistic › Palette Knife raster filter
Squashed from #3038 onto main.
2026-10-22 00:23:48 +07:00
bflatastic bbe026607f Variables: Load Variable Library… reads a variable library's text variables and data sets
Squashed from #2853 onto main.
2026-10-22 00:12:39 +07:00
bflatastic 14f804cdee Type: ruby (ふりがな) for live text
Squashed from #3448 onto main.
2026-10-22 00:10:56 +07:00
bflatastic 2cd7f156fb Add lighting presets to 3D effects
Squashed from #3442 onto main.
2026-10-22 00:01:37 +07:00
dotCooCoo 4d1581f3da Document Info: a Repeat Objects category lists the document's repeats
Document Info had no Repeat Objects category, and document.info
refused category "repeats". The reference app's Document Info panel
menu lists Repeat Objects after Font Details.

Document Info now has a Repeat Objects category after Embedded Images.
It lists each radial, grid and mirror Repeat object by name with its
kind and the number of instances it makes. For a grid it gives the
rows and columns instead, followed by the number of instances it
draws when that is fewer. The count comes from the new
RepeatSpec::instances, which gives the number of instances a repeat
draws. That number is smaller than its options give when repeats
nested in its source would pass the instance budget. With Selection
Only the category lists the Repeat objects in the selection.
document.info gives them as repeatObjects, the panel's flyout offers
the category, and the text report that Document Info › Save… and
File › Package write has a REPEAT OBJECTS section. The title has a row
in every catalog.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-22 00:01:36 +07:00
Drew DibbleandClaude Opus 5.5 4f5f573ded Properties: doc comments for the panel's entry point and its test helpers
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:01:36 +07:00
Drew DibbleandClaude Opus 5.5 f19b33c219 G1: Properties: Edit Clipping Path and Edit Contents for one clip group
With one clip group selected, the Properties panel has Edit Clipping
Path and Edit Contents at its top, under the Clip Group heading, as the
reference app does: "With the clip group selected, click the Edit
Clipping Path button at the top of the Properties panel to select and
edit the mask, or click the Edit Contents button to select and edit the
masked content"
(https://www.adobe.com/learn/illustrator/web/crop-using-masks). They
run the existing object.clippingMask.editMask and editContents, which
select the clipping path or the clipped art for the selection tools, as
Object > Clipping Mask does. Edit Contents shows only when the group
clips something. With several objects selected they don't show (the
tutorial describes one selected clip group); the Contextual Task Bar
does the same. Both labels are already in every catalog.

Test: panels::properties::tests::one_clip_group_has_edit_clipping_path_and_edit_contents_at_the_top
(first under the heading, each selecting its part; Edit Clipping Path
alone once the clipped art is deleted; neither for a clip group
selected with another object in either order, or for a plain group).
Documented in docs/target-app-parity.md, docs/ui-parity.md and
docs/gaps.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:01:36 +07:00
bflatastic c895f64063 Format: rustfmt after the batch 2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 4e5f5e608b G12: translate Touch workspace for complete i18n catalogs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 1c7b02a6fe G12: honor Enable Touch Workspace preference
Switch to the Touch built-in workspace when the preference is on and a touch
screen is present; restore the prior workspace when it is turned off.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor cc4836abb7 G4: reuse Character panel Shared/Metric for mixed paragraph fields.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 45cabc1c3c G4: blank Paragraph panel indents when paragraphs differ
Mixed left/right/first-line indents and space before/after now follow the same shared-value rules as alignment buttons.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 56cfb79d24 G4: add i18n rows for Scale Text Attributes command and error
Complete menu catalogs need plain and @msg entries so i18n tests pass for the new type command.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 07a49d447d G4: Scale Text Attributes bakes transform scale into type sizes
After scaling groups, type.scaleTextAttributes matches panel sizes to what draws on canvas without leaving scale in the affine.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor ab0ddf0eae G1: Live Corners relative rounding (#1144)
Store corner radii as fractions of each corner's max so rounding scales with the shape; expose via Corners dialog and object.setLiveShape relativeRadii.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
Cursor Agentandnrq6993-a11y b13072b77c M3: rustfmt eyedropper and brush key-color UI
Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 3f45472e15 i18n: add Key Color eyedropper tooltip to complete catalogs
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor 6433b6ca3a G5: Pattern brush Key Color eyedropper in Brush Options
Hue Shift colorization can sample key color from canvas art via the
eyedropper next to the swatches, matching Illustrator's brush workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:03 +07:00