Commit Graph
3958 Commits
Author SHA1 Message Date
BFlat 36d751b72f Merge pull request #2843 from dotCooCoo/feat/history-clear-history
History: the panel menu clears the history
2026-10-18 13:24:18 +07:00
BFlat c40718fafa Merge pull request #2842 from dotCooCoo/feat/ai-import-warns-unread-variables
.ai import: opening a file with a variable library warns that its variables weren't read
2026-10-18 13:24:02 +07:00
BFlat d9d1a1aba3 Merge pull request #2839 from dotCooCoo/fix/export-for-screens-example-name
Export for Screens: the dialog's example names the file the export writes
2026-10-18 13:23:47 +07:00
BFlat 2c4f059444 Merge pull request #2838 from dotCooCoo/fix/pdf-export-keeps-created-date
PDF export: PDF and .ai files keep the document's creation date
2026-10-18 13:23:33 +07:00
BFlat dba668240c Merge pull request #2829 from dotCooCoo/fix/freeform-gradients-keep-cmyk
Color: freeform gradients keep CMYK inks when expanded, meshed or edited
2026-10-18 13:23:18 +07:00
BFlat a0657c9a22 Merge pull request #2836 from dotCooCoo/fix/drag-budget-test-threads
Tests: the drag-budget test sets its thread count, and no render test changes the default
2026-10-18 13:23:04 +07:00
BFlat 5f20ef194b Merge pull request #2783 from dotCooCoo/fix/linux-save-picks-chosen-type
Save As: on Linux the file type picked in the save dialog decides the format
2026-10-18 13:22:50 +07:00
BFlat eb8eaa7aa0 Merge pull request #2825 from dotCooCoo/fix/pattern-rename-refuses-swatch-names
Patterns: Pattern Options refuses a name another swatch has
2026-10-18 13:22:35 +07:00
BFlat 1d6daaefcc Merge pull request #2819 from dotCooCoo/fix/template-refuses-legacy-ai
Save as Template: aiVersion 7 is an error for a .ait, and the dialog shows no Version
2026-10-18 13:22:20 +07:00
BFlat 78b7ffe1fb Merge pull request #2813 from dotCooCoo/fix/export-ait-names-save-as-template
Export: a .ait export names file.saveAsTemplate, and the CLI's convert writes the template
2026-10-18 13:22:06 +07:00
BFlat 31df7aee9a Merge pull request #2812 from dotCooCoo/fix/cmyk-document-converts-rgb-everywhere
Color: type, gradient, mesh and swatch commands store RGB input as color-managed CMYK in a CMYK document
2026-10-18 13:21:51 +07:00
BFlat 2909972140 Merge pull request #2803 from dotCooCoo/fix/history-template-step-names-translated
i18n: the History panel translates the step names that contain a style's name
2026-10-18 13:21:36 +07:00
BFlat cfe77f1706 Merge pull request #2805 from dotCooCoo/fix/marquee-intertwine-by-members
Selection: a marquee selects an Intertwine group or a repeat only where it meets their art
2026-10-18 13:21:22 +07:00
BFlat 69dad83148 Merge pull request #2782 from dotCooCoo/fix/tools-skip-shaper-sources
Shaper: the Lasso, Smart Guides and the anchor tools skip a Shaper Group's original shapes
2026-10-18 13:21:08 +07:00
BFlat 5646fa7253 Merge pull request #2796 from dotCooCoo/fix/appearance-inline-3d-and-channel-dropdowns
Appearance: the inline effect editor shows 3D, Warp, Rasterize and channel choices as dropdowns
2026-10-18 13:20:53 +07:00
BFlat 545e935e0c Merge pull request #2794 from dotCooCoo/fix/color-sliders-report-values
Accessibility: color and channel sliders report their values to screen readers
2026-10-18 13:20:39 +07:00
BFlat 46a1d9f175 Merge pull request #2785 from dotCooCoo/fix/panel-fields-shift-enter
Fields: Shift+Enter applies a pattern, layer, artboard or asset name and keeps the field active
2026-10-18 13:20:24 +07:00
BFlat f794069fbd Merge pull request #2797 from dotCooCoo/fix/whole-selected-group-anchors-drawn-selected
Direct Selection: a group selected whole shows its anchors as selected
2026-10-18 13:20:10 +07:00
BFlat c2bd5b4a52 Merge pull request #2795 from dotCooCoo/fix/caret-steps-past-zero-width-clusters
Type: Left and Right reach both sides of a character drawn with no width
2026-10-18 13:19:55 +07:00
BFlat 2623e95a13 Merge pull request #2809 from dotCooCoo/fix/repeat-tab-from-left-indent
Tabs: Repeat Tab measures the first stop from the left indent
2026-10-18 13:19:40 +07:00
BFlat ced933af47 Merge pull request #2815 from dotCooCoo/fix/rtf-rtl-justify
Place: justified RTF paragraphs marked right to left end their last line on the right
2026-10-18 13:19:24 +07:00
BFlat c29701f65b Merge pull request #2806 from dotCooCoo/fix/place-bounding-skips-hidden-art
Place: Bounding Box measures only the art that shows
2026-10-18 13:19:13 +07:00
BFlat 44e7be8b04 Merge pull request #2781 from dotCooCoo/fix/grid-images-count-decode-failures
Render: raster exports warn about an undecodable image drawn from a kept Rasterize image
2026-10-18 13:18:57 +07:00
BFlat 2c93a290e8 Merge pull request #2790 from dotCooCoo/fix/pdf-export-skips-undecodable-images
PDF export: an image that can't be decoded is left out with a note instead of failing the export
2026-10-18 13:18:43 +07:00
BFlat 646a5cc9a3 Merge pull request #2801 from dotCooCoo/fix/issue-2692
Legacy .ai export: images are written in the form the version 7 specification shows
2026-10-18 13:18:28 +07:00
BFlat 20c8296b5a Merge pull request #2807 from dotCooCoo/fix/issue-2687
Legacy .ai export: radial gradients are written with their highlight (Bh)
2026-10-18 13:18:14 +07:00
BFlat eb5bf7b828 Merge pull request #2817 from dotCooCoo/fix/legacy-ai-warns-undecodable-image
Legacy .ai export: warns when an image in art it flattens or expands can't be decoded
2026-10-18 13:18:03 +07:00
BFlat 36b85472d2 Merge pull request #2808 from dotCooCoo/fix/ai-import-covered-type-spells-text
.ai import: page type over a text object is left out only when that object's text has its letters
2026-10-18 13:17:47 +07:00
BFlat e69a08c1a6 Merge pull request #2799 from dotCooCoo/fix/issue-2034
EPS and .ai import: the note for editing data of another page size gives both sizes and the scale
2026-10-18 13:17:31 +07:00
BFlat 602b3b34bc Merge pull request #2800 from dotCooCoo/fix/ai-undecodable-editing-data-note
.ai import: a file whose editing data can't be decompressed says why its layers weren't read
2026-10-18 13:17:16 +07:00
BFlat 9361a3b3f1 Merge pull request #2810 from dotCooCoo/fix/pdf-layer-names-pdfdocencoding
PDF import: PDF and .ai layer names without a byte order mark are read as PDFDocEncoding
2026-10-18 13:17:02 +07:00
BFlat dfb1a86a23 Merge pull request #2816 from dotCooCoo/fix/xmp-nesting-limit
PDF import: an XMP packet nested more than 256 deep is skipped instead of overflowing the stack
2026-10-18 13:16:47 +07:00
BFlat 3f673c35eb Merge pull request #2776 from dotCooCoo/fix/mcp-canned-replies-respect-queue-limit
MCP: parse errors and overlong lines wait for room in the 64-line queue
2026-10-18 13:16:30 +07:00
BFlat 2d2698e5fb Merge pull request #2837 from dotCooCoo/fix/svg-import-paths-on-windows
SVG import: hrefs that need no folder stay as written, and images in a non-absolute folder are read
2026-10-18 13:16:15 +07:00
dotCooCoo 1caa957531 History: the panel menu clears the history
The History panel menu had no Clear History, and VectorCraft had no
command that removes the undo and redo steps. In the reference app,
Clear History in the History panel menu removes the states after the
selected state, and it removes the states before it while their
changes remain in the document.

edit.clearHistory (Clear History) removes every step to undo and to
redo in the active document and returns how many it removed. The
document and the selection stay as they are, and clearing is not an
undo step. Typing or a drag still in progress becomes a step first, as
Undo makes it one, so its change stays in the document. The command is
disabled when there is no step to clear, and it waits while an input
method composes, as Undo does.

The History panel menu has Clear History after Step Forward. It runs
the command and is disabled when there is no step to clear.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:39:07 +07:00
dotCooCoo 4803573940 .ai import: opening a file with a variable library warns that its variables weren't read
A .ai can keep its variables and data sets in a variable library in
its editing data. VectorCraft doesn't read that library, and such a
file opened with an empty Variables panel and no warning about it.

The reader now counts the library's variables and data sets in the
editing data's /Document dictionary, and opening the file adds a
warning: "the file's variables and data sets weren't read (2
variables, 3 data sets): the Variables panel doesn't list them". The
same warning appears when the layers can't be read and the file opens
from its PDF part. A PDF that opens from the editing data it carries
gets the warning too. A library with no variables and no data sets
gives no warning.

Docs: docs/target-app-parity.md.
2026-10-18 12:38:06 +07:00
dotCooCoo 3c0c112fbd Export for Screens: the dialog's example names the file the export writes
The Export for Screens dialog built its Example line from its fields as
typed, so it often showed a name the export does not write. It used the
artboard's name with its spaces (Artboard 1.png where the export writes
Artboard-1.png) and the first artboard whatever the range, kept a
prefix, a suffix or a device name such as CON as typed, used the scale
text as the row folder instead of the row's own folder or its size,
kept a vector row's @2x suffix, left out the folder that Create
Sub-folders adds, and read a counter set as text as 1.

The line now comes from the export's own naming. The naming part of
document.exportForScreens moves into a Naming type that the export
uses for every file, and fileio::screen_first_file returns the path of
the first file the export writes for the given params, without encoding
or writing anything. The dialog passes it the params it exports with,
without the destination, so the line shows before a folder is chosen
and is left out while the settings choose nothing to export. The Start
at field shows the counter the export uses when it was set as text.

Docs: docs/target-app-parity.md.
2026-10-18 12:37:29 +07:00
dotCooCoo aa9cf83fd2 PDF export: PDF and .ai files keep the document's creation date
The engine built the PDF options without a creation date, so the
writer dated every PDF and PDF-compatible .ai file with the time it
was written. A document created on 2020-01-01 (File › File Info
shows the date) exported as a PDF with today's date in /CreationDate
and xmp:CreateDate. SVG export with the metadata option already
writes the document's own date.

The options now carry the document's File Info created date and the
time of writing as the modification date. A document without a
creation date is dated with the time of writing, as before, so PDF/X
files still get their required date. The PDF writer writes one date
for both, so the export then sets /ModDate, xmp:ModifyDate and the
PDF/A history dates to the time of writing; /CreationDate and
xmp:CreateDate keep the document's date. The page information mark
and the embedded editing data's modification date keep the time of
writing.

Docs: docs/mcp.md (File Info), docs/target-app-parity.md.
2026-10-18 12:36:27 +07:00
dotCooCoo 24d4777d1a Color: freeform gradients keep CMYK inks when expanded, meshed or edited
Object > Expand and Object > Create Gradient Mesh mixed a freeform
gradient's points in display RGB and turned the result back into the
points' model with the profile-free formula. CMYK inks changed: points
of C50 M20 Y0 K10 gave mesh points of C45 M24 Y0 K22. RGB points were
stored as display sRGB, so with a working RGB space other than sRGB
the mesh showed other colors. Points colored along CMYK stops, a point
paint.freeform.splitLine added between CMYK points and a point
paint.freeform.addPoint added without a color came out as RGB.

Points that share a color model now mix in it: the C, M, Y and K
values mix as inks, and Gray, RGB and Lab components mix as they are.
Points in different models still mix in display RGB. Object > Expand
and Object > Create Gradient Mesh store the result in the working RGB
space, and in a CMYK document they store it as CMYK through the color
settings, as paint.setFill stores an RGB color. Freeform::recolor and
split_line mix with the new Color::lerp_keep_model, which
vectorcraft_doc::blend::lerp_color now calls.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:36:25 +07:00
dotCooCoo 739b0b7335 Tests: the drag-budget test sets its thread count, and no render test changes the default
The FreeBSD job fails
fx::tests::a_drag_renders_raster_effects_within_the_drag_budget at
fx.rs:2822 with 1 previewed instead of 2.

pixels_do_not_depend_on_the_thread_count called set_default_threads
with 0, 1, 4 and 8. The override is process-wide, so a Renderer::new()
in a test running at the same time took that count. With 0 threads a
drag renders the drop shadow at full resolution, as
RenderOptions::dragging documents, so stats.previewed counts only the
filtered content (1 instead of 2).

The thread-count test now sets each renderer's threads. The drag test
gives its renderers 3 threads, so it also passes where default_threads()
is 0 (one CPU, or VECTORCRAFT_RENDER_THREADS=0).
2026-10-18 12:36:24 +07:00
dotCooCoo 468d7b21e9 Save As: on Linux the file type picked in the save dialog decides the format
On Linux the save dialog is the XDG desktop portal's, shown through
rfd. rfd sends no `current_filter` and reads only the file name from
the portal's answer, and the GTK file chooser keeps the typed name
when another file type is picked. With "PDF-compatible .ai" picked,
`poster` typed for a new document was saved as a native
`poster.vectorcraft`, and so was the suggested
`Untitled-1.vectorcraft` left as it was.

When a save offers two or more file types (Save As, Save a Copy, Save
as Template and the first Save of a new document), the app now calls
the portal's SaveFile itself over D-Bus. The dialog starts on the
suggested file's type, and its answer names the type picked. A name
typed without an extension, or the suggested name left as it was,
gets that type's extension. A name typed with another save format's
extension, other than the suggested file's (for Save as Template, a
template format's), is saved in that format, and any other extension
gets the picked type's added. The dialog keeps the window as its
parent on X11 and Wayland, and rfd shows the dialog when the portal
can't. The same code runs on the BSDs; Windows, macOS and the web
build keep their dialogs.

Docs: docs/target-app-parity.md.
2026-10-18 12:36:23 +07:00
dotCooCoo db3092ae2e Patterns: Pattern Options refuses a name another swatch has
pattern.options refused a new name that another pattern had, but not
one that a color swatch, a gradient swatch, a second swatch of the
pattern or a color group had. The rename then gave the pattern's swatch
that name, so two swatches, or a swatch and a color group, had one
name. The rename also gave the new name to every swatch that had the
old one, a color swatch included, and art linked to that color swatch
kept a link to a name no swatch had. Make and Save a Copy numbered a
pattern's or a swatch's name, but not a color group's.

pattern.options now refuses a name that a swatch or a color group has,
with "a swatch or color group named `<name>` exists". It takes a name
that only the pattern's own swatches have, such as a name Swatch
Options gave the pattern's swatch, when none of them still has the old
name. A rename gives the new name only to the pattern's swatches that
had the old one. Make and Save a Copy number a color group's name as
they number a swatch's. The new message has a row in the 17 catalogs
that translate every message (COMPLETE_MESSAGES) and in bg, da, id and
nl.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:36:22 +07:00
dotCooCoo 0cf12a1611 Save as Template: aiVersion 7 is an error for a .ait, and the dialog shows no Version
file.saveAsTemplate with format ait and aiVersion: "7" wrote the
legacy PostScript .ai of version 7 under the .ait name: for a
one-artboard test document, a 7,556-byte file starting %!PS-Adobe-3.0.
The web's Save as Template dialog showed the .ai Version menu for
.ait, whose Legacy (version 7) choice set aiVersion 7.

aiVersion 7 for a .ait is now an error, "aiVersion 7 writes legacy .ai
files; a .ait template is always PDF-compatible", and nothing is
written. The 17 catalogs that translate every message have a row for
it. The dialog shows no Version row for .ait, and aiVersion "pdf"
still writes the PDF-compatible template.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/file-format-parity.md
(two stale rows for .ai and .ait removed).
2026-10-18 12:35:18 +07:00
dotCooCoo d23030ce2b Export: a .ait export names file.saveAsTemplate, and the CLI's convert writes the template
document.export, document.serialize, document.exportSelection and
Export for Screens rows answered "PDF-compatible .ait template files
can be opened but not written" for a .ait, although
file.saveAsTemplate writes them. The CLI's convert and run --export
call document.export, so vectorcraft-cli convert in.pdf out.ait
failed with that error.

The error now reads "PDF-compatible .ait template files are written
by file.saveAsTemplate", with a row in the 17 catalogs that translate
status messages and in bg, da, id and nl. convert and run --export
write a .ait with file.saveAsTemplate, which writes every artboard
with the .ai options. convert passes --replace-lossy and its .ai flags
on, and refuses --artboard and --range for a .ait.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:34:41 +07:00
dotCooCoo 2643f57b1f Color: type, gradient, mesh and swatch commands store RGB input as color-managed CMYK in a CMYK document
In a CMYK document, paint.setFill stores an RGB color as CMYK through
the color settings, but the other commands that take a color stored it
as RGB: text.create, text.createInPath, text.setStyle and
text.setRangeStyle colors, a shapeBuilder.merge hex fill,
paint.editGradient stops, freeform gradient points, gradient mesh
points, the Symbol Stainer's color, and swatch.new {colors},
swatch.newGroup, swatch.editGroup and swatch.edit. The Mesh tool sent
the fill as a hex value, so a CMYK fill came in as RGB.

These commands now store such a color through paint::new_color, as
paint.setFill does, and take keepModel to keep RGB (shapeBuilder.merge
inside its fill object). A color the swatch
or gradient already has keeps its model, so stops sent back with a
moved offset keep their colors (the Gradient panel's ramp and the
Gradient tool send keepModel), and a stop that names a process
swatch takes the swatch's color as it is, in paint.setFill gradients
too. Where the app copies an existing color, it keeps the color's
model: the Color panel's sliders for gradient stops and freeform
points, a swatch put on a stop or a point (the stop popover, Alt-click
in the Swatches panel, a drop on the Gradient panel's ramp), the fill
the Mesh tool gives a new point or the Symbol Stainer applies, and the
colors the Color Guide, Color Themes and Recolor Artwork save as
swatches.

swatch.new {color, mode} converted RGB into CMYK before converting it
into mode, so the New Swatch dialog in RGB mode stored #00ff00 as
R101 G176 B62 in a CMYK document. With mode, the color is now
converted once.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-18 12:34:40 +07:00
dotCooCoo ef9e554dd2 i18n: the History panel translates the step names that contain a style's name
The History panel and Edit › Undo and Redo did not translate the steps
that apply or redefine a character or paragraph style, or redefine a
graphic style. The engine built their names with format! ("Apply Body",
"Redefine Body", "Redefine Graphic Style “S”"), and the panel looks a
name up as a whole, so no catalog row matched a name with a style's
name in it. A name cannot be matched against template rows instead: a
character style named Brush gives "Apply Brush", which is also the name
of the brush step.

The engine now names these steps with Session::edit_named, and each
such step keeps its template and the style's name
(HistoryEntry::named) through undo and redo. The History panel and
Edit › Undo and Redo show the template's row with the name in it
(panels::history::entry_name). The name stays as the document holds
it, and the Normal styles' names are shown in the interface language.
Every complete catalog has rows for Apply {name} and Redefine {name},
and Redefine Graphic Style “{name}” had one.
every_undo_label_is_translated also requires a row for each template,
and the only step name it allows to be built with format! is the
Liquify tools' "{} Tool".

Docs: docs/target-app-parity.md.
2026-10-18 12:34:39 +07:00
dotCooCoo 438d27ffbe Selection: a marquee selects an Intertwine group or a repeat only where it meets their art
A marquee measured an Intertwine group and a repeat by their bounding
box. A marquee in the gap between two members of an Intertwine group,
or between two copies of a repeat, selected the object, although a
click there misses and a plain group needs a marquee to meet one of its
members. Direct and Group Selection's marquees also selected the
Intertwine group whole beside the anchor points of its members.

The marquee now touches an Intertwine group by its members, as it
touches a plain group, and it touches a repeat by the copies the repeat
draws, as clicks hit them. Direct and Group Selection's marquees select
the anchor points of an Intertwine group's members, and Group
Selection's E marquee selects the members inside it. Blends, and
envelopes outside Edit Contents, keep their box, as clicks on them do.

Docs: docs/target-app-parity.md.
2026-10-18 12:34:38 +07:00
dotCooCoo 7d56260b47 Shaper: the Lasso, Smart Guides and the anchor tools skip a Shaper Group's original shapes
A Shaper Group keeps its original shapes as its first child, at no
opacity, and clicks do not pick them outside Construction mode. The
Lasso, the Add Anchor Point, Delete Anchor Point, Anchor Point and
Scissors tools still picked their anchor points and segments, also
where the group erased the face, and Smart Guides, Snap to Point and
the transform tools' reference point snapped to them. The Lasso and
the anchor tools also picked art outside the isolated group or object
in isolation mode.

The Lasso and the anchor tools now visit only the art a click can pick:
the isolated object in isolation mode, and a Shaper Group without its
original shapes unless they are isolated (Construction mode). Smart
Guides and Snap to Point leave the original shapes out of their
targets, and Spacing Guides leave out their bounds when a face moves.
The transform tools' reference point snaps to an original shape only
when that shape is selected, or under the pointer in Construction
mode. The Shaper Group test document moves into the shared test
helpers.

Docs: docs/shaper.md, docs/target-app-parity.md.
2026-10-18 12:34:37 +07:00
dotCooCoo 22ce516e2d Appearance: the inline effect editor shows 3D, Warp, Rasterize and channel choices as dropdowns
The Appearance panel's inline effect editor still showed eight choice
parameters as text fields holding the stored value: Revolve's edge,
Extrude & Bevel's bevel, every Warp style's orientation, Rasterize's
Color Model, Background and Anti-aliasing, and the channel of Levels
and Curves. Their dialogs built these lists in their own bodies, so
effect::choices did not hold them. Nothing checked a channel's value:
a typo was stored, and Levels or Curves drew it as RGB.

effect::own_choices now holds these lists, each label as its dialog
passes it to its dropdown or radio buttons. The dialogs read the lists
from it (Rasterize's Anti-aliasing from the AntiAlias list it is built
from), and the inline editor shows each one as a dropdown with the
dialog's translated labels; Rasterize's Color Model reads CMYK or RGB
by the document's mode. effect.apply and effect.setParams refuse a
Levels or Curves channel other than rgb, red, green or blue (r, g, b
and any letter case are read too) with "`channel` must be one of rgb,
red, green, blue", and the document and its undo history stay as they
were.

Docs: docs/mcp.md (effect params), docs/target-app-parity.md.
2026-10-18 12:34:36 +07:00
dotCooCoo 30e54dedcf Accessibility: color and channel sliders report their values to screen readers
The color and channel sliders gave screen readers a name but no value.
Each caller maps its own range onto the thumb's position, so a screen
reader announced the Color panel's G slider, Transform Each's Scale or
the Navigator's Zoom without the setting the field beside it shows.

color_slider_valued and channel_slider_valued take an optional
SliderValue: the number and text of the field beside the slider and
the slider's range. The accessibility node gets the number, the text
and the range, which extends to a number past the slider's end.
color_slider_named and channel_slider_named keep their arguments and
report no value. Every caller outside the tests uses the new variants:
the Color panel, Swatch Options, the Color Picker, the Navigator, the
Character panel's variable font axes and the dialog sliders.
form::slider_rail_named reports the number it edits, and the harmony
wheel's Brightness slider, which has no field, reports its brightness
in percent.

Docs: docs/target-app-parity.md.
2026-10-18 12:34:35 +07:00