Commit Graph
3098 Commits
Author SHA1 Message Date
BFlat bf10fd64f6 Merge pull request #2065 from dotCooCoo/feat/auto-size-type-fewer-layouts
Type: Auto Size area type in columns usually lays the columns out twice an edit
2026-10-16 04:22:45 +07:00
BFlat 2346a4acfb Merge pull request #2067 from dotCooCoo/feat/alt-drag-copies-once-more-tools
Part of #1429: Alt-drags of objects and plane widgets in perspective copy the objects once per drag
2026-10-16 04:22:29 +07:00
BFlat 5b9a9b6d6c Merge pull request #2068 from dotCooCoo/fix/active-artboard-outline
Artboards: the canvas outlines the active artboard in black
2026-10-16 04:22:13 +07:00
BFlat 82d9143502 Merge pull request #2087 from dotCooCoo/feat/layers-panel-filter
Layers: the panel filters its rows by object kind, lock and visibility
2026-10-16 04:21:55 +07:00
BFlat cebc4ed93e Merge pull request #2093 from dotCooCoo/feat/presentation-mode-artboard-keys
View: in Presentation Mode, the left and right arrow keys and a click go between artboards
2026-10-16 04:21:32 +07:00
BFlat 29bf7b7e06 Merge pull request #2099 from dotCooCoo/feat/artboard-navigator-list
Status bar: the artboard navigator lists the artboards by number and name
2026-10-16 04:21:15 +07:00
BFlat e965c0daa2 Merge pull request #2102 from dotCooCoo/feat/properties-artboard-name-options
Properties: Edit Artboards renames the active artboard and opens Artboard Options
2026-10-16 04:21:00 +07:00
BFlat aaeb658292 Merge pull request #2106 from dotCooCoo/feat/rotate-view-menu
View: Rotate View turns the view to a preset angle, and `view.rotate` sets any angle
2026-10-16 04:20:42 +07:00
BFlat d541eb9476 Merge pull request #2116 from dotCooCoo/feat/export-screens-selected-artboards
Export for Screens: the dialog opens with the Artboard tool's selected artboards checked
2026-10-16 04:20:26 +07:00
BFlat cf0be249ba Merge pull request #2134 from dotCooCoo/feat/free-distort-dialog-corners
Effects: Free Distort's corners are dragged in its dialog
2026-10-16 04:20:09 +07:00
BFlat 0594f04e30 Merge pull request #2136 from dotCooCoo/feat/color-panel-options-command
Color panel: Hide Options comes back after a restart, and `ui.colorPanel` sets the mode and options
2026-10-16 04:19:47 +07:00
BFlat b15c28b7c0 Merge pull request #2138 from dotCooCoo/fix/glowing-edges-raster-stylize
Effect menu: Glowing Edges moves to a Stylize submenu under Raster Effects
2026-10-16 04:19:23 +07:00
BFlat fc51d66659 Merge pull request #2140 from dotCooCoo/feat/save-template-ait
Templates: Save as Template can also write a PDF-compatible .ait
2026-10-16 04:19:04 +07:00
dotCooCoo 867e72b085 Type: Auto Size area type in columns usually lays the columns out twice an edit
For Auto Size area type in columns, every text edit, each keystroke of
the Type tool included, laid the text out in one column and then laid
the columns out at that height and at each step of a bisection for the
least height that holds the text. In the release-build timing test
auto_height_in_columns_typing_timing (stories of 5,000 to 50,000
characters at 9 pt in a frame 500 pt wide), a keystroke laid the
columns out 18 to 22 times, and typing at the start of the story of
50,000 characters in 2 columns took 1,670 ms a keystroke.

The bisection now runs on the lines of the one-column layout.
columns_hold computes where those lines would sit in the columns, with
each column's first line at its first baseline below the column's top
(LineInfo::first_baseline, new in vectorcraft-text). The columns are
then laid out at the upper height the bisection ends on and, when they
hold the text there, at the lower one, unless that is the 1 pt it starts
from. When they hold the text at the upper height and not at the lower
one, Auto Size takes the upper height. When taller columns always hold
the text, the old search ends between the same two heights. When the
text wraps around other objects or does not fit the one-column layout,
or when that check fails, the columns are laid out at every step as
before. The same keystroke now takes 292 ms.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 0ffae0c3fa Part of #1429: Alt-drags of objects and plane widgets in perspective copy the objects once per drag
On each pointer move of an Alt-drag with the Perspective Selection
tool, Session::preview put the drag's snapshot back and ran
perspective.move with copy again, and persp_edit copied every selected
object again. An Alt-drag of a plane widget with the Perspective Grid
or Perspective Selection tool ran perspective.plane.move with objects
"copy" again, and plane_move copied every object on the plane again.

Both commands now take their copies from preview_copies, as
apply_transform does for the Selection and transform tools: the first
preview copies the objects into a clone of the snapshot, and each later
preview of the same objects on the same snapshot maps only those
copies. The copies keep their ids, and the document, the selection,
the undo step and the journal are the same as before. When the stored
grid still lists attachments of its own, as grids saved before
attachments were kept on the objects do, both commands copy on every
preview as before. With 25,000 rectangles attached to the left plane,
a release build of vectorcraft-cli took a median of 424.8 ms for each
Alt-drag move of the objects after the first preview and takes
328.9 ms with this change. An Alt-drag of the plane's widget took
340.3 ms a move and takes 256.9 ms.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 9e832d32da Artboards: the canvas outlines the active artboard in black
The canvas drew artboard 1's edge in black, 1 px wide, whichever
artboard was active, and drew every other artboard's edge in gray,
0.6 px wide.

It now draws the active artboard's edge in black and draws the other
artboards' edges in gray, at the same widths. The active artboard is
the artboard that the status bar's navigator shows and that Align to
Artboard and Paste in Place use. With several artboards selected by
the Artboard tool, it is the one the tool draws with handles and its
name.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 29eb490381 Layers: the panel filters its rows by object kind, lock and visibility
The Layers panel narrowed its rows by name (Search All) or to layers
alone (Show Layers Only).

The panel now has a Filter button at the right of Search All. Its
popup holds Clear All and a check box for each object kind under By
Object (Paths, Shapes, Text, Images, Effects, Groups, Linked Files,
Guides) and for each row state under By Layer (Locked, Hidden). A row
stays when it is of a ticked kind, is in a ticked state and has the
search text in its name, and a section with nothing ticked keeps every
row. The rows that hold a kept row stay too, as they do for the
search. The button is lit while a filter is on. The filter lasts the
session and does not change the document, its open rows or the
selection. ui.layersFilter {objects?, layers?} sets it. The button
shows a new icon, assets/icons/dc-filter.svg, with its row in
ASSETS.md.

The panel lists its rows again when the filter changes, as it does
when the search changes. On a layer of 5,000 groups of 10 paths, a
release build lists the rows for a filter in 2.9 to 3.6 ms.

Docs: docs/mcp.md (Layers panel), docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 697f238b60 View: in Presentation Mode, the left and right arrow keys and a click go between artboards
In Presentation Mode the arrow keys moved the selection, and the
active tool received a click and selected or drew as it does in
Normal Screen Mode. No key or click moved the view to another
artboard.

Left Arrow and Right Arrow now go to the previous and the next
artboard, and a click anywhere goes to the next one, through
view.goToArtboard, as the status bar navigator's Previous and Next
buttons do. On the first artboard Left Arrow leaves the view as it
is, and on the last artboard Right Arrow and a click leave it as it
is. No arrow key moves the selection in the mode, and the tool gets
no press, drag or hover, so a click selects and draws nothing. A drag
with the middle button or with Space held no longer pans the view
there; the mouse wheel still zooms and scrolls it.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 8ca25c955e Status bar: the artboard navigator lists the artboards by number and name
The status bar's artboard navigator showed only the active artboard's
number between its first, previous, next and last buttons, and it had
no list of the artboards.

The number is now a drop-down field that shows the active artboard's
number and name. Its list shows every artboard by number and name, as
the Artboards panel lists them. Choosing one runs view.goToArtboard,
the command the buttons run, which makes that artboard the active one
(and the Artboard tool's, while that tool is in use) and fits it in
the window. The list lays out only the rows in view and at most one
more, and opens with the active artboard's row in view. In a test
build with 1,000 artboards, the two frames that open the list take
0.12 ms and 0.04 ms, and a frame with the list open takes 0.03 ms. The
timed frames are headless and draw only the status bar.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 5a5f1a0995 Properties: Edit Artboards renames the active artboard and opens Artboard Options
With the Artboard tool, the Properties panel showed the active
artboard's name as a label, and no control in the panel opened
Artboard Options.

The name is now a text field. Typing a new name and pressing Enter or
clicking elsewhere renames the active artboard in one undo step
through artboard.setProps, as the Artboards panel's rename does;
pressing Escape or leaving the field empty keeps the old name. A click
on the Artboard Options icon beside the field runs tool.options with
tool "artboard", which opens Artboard Options for the active artboard,
as a double-click on the Artboard tool's button does.

Docs: docs/target-app-parity.md.
2026-10-16 04:04:50 +07:00
dotCooCoo 799155cdd0 View: Rotate View turns the view to a preset angle, and view.rotate sets any angle
In the View menu, Reset Rotate View was the only rotation item. The
status bar's angle menu set its twelve preset angles by writing the
view's rotation itself, and no command took an angle to turn the view
to.

View › Rotate View now lists the status bar's angles (0°, 15°, 30°,
45°, 60°, 90°, 180°, -15°, -30°, -45°, -60° and -90°) and checks the
one the view is turned to. The new UI command `view.rotate {angle}`
turns the view to any angle about the center of the canvas and
returns the angle it set, normalized to (-180, 180] as a Rotate View
drag normalizes it. The menu's items and the status bar's angle menu
run it from one list of angles; the status bar sets the same angles as
before. Without a document the command returns "no document" and the
items are disabled. Run from the command palette, which passes no
angle, Rotate View opens a dialog that asks for the angle, with the
view's current angle filled in.

The de, it, ja, ko, pl, pt-br, ru, uk, vi and zh-hant catalogs have a
new "Rotate View" row. The strings the catalogs cover leave out the
angle labels, as they leave out Type › Size's point sizes.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-10-16 04:04:49 +07:00
dotCooCoo 1f6ef9dcf3 Export for Screens: the dialog opens with the Artboard tool's selected artboards checked
Export for Screens opened on the artboard choice the document last
exported with (all artboards by default), whichever artboards were
selected with the Artboard tool or in the Artboards panel.

While the Artboard tool is in use, File › Export › Export for Screens…
now opens with the artboards the tool has selected checked, and Export
Artboard writes only those. They are the artboards the Artboards panel
highlights, and they are checked even after a Full Document export.
With another tool in use, the dialog still opens on the saved artboard
choice. The other fields show the saved settings in both cases, and the
params text of file.exportForScreens describes the rule.

Docs: docs/control-protocol.md, docs/target-app-parity.md.
2026-10-16 04:04:49 +07:00
dotCooCoo fc0e926cda Effects: Free Distort's corners are dragged in its dialog
Free Distort's dialog showed the four corners as read-only text, and
so did the Appearance panel's inline editor. Only effect.apply and
effect.setParams could set them.

The dialog now shows a box in the proportions of the first selected
object's bounds with a handle on each corner. Dragging a handle moves
that corner, and the preview follows. A handle focused by a click or
Tab moves 1% of the box with each arrow key, and 10% with Shift. Reset
puts the four corners back on the box. Each handle has a
screen-reader name. On the corners row of the Appearance panel's
inline editor, Edit… opens the dialog.

Docs: docs/effects-parity.md, docs/target-app-parity.md.
2026-10-16 04:04:49 +07:00
dotCooCoo 940f425a01 Color panel: Hide Options comes back after a restart, and ui.colorPanel sets the mode and options
Show Options and Hide Options were kept in egui's temporary memory, so
the Color panel showed its options again after every restart. No
command set them or the panel's mode.

- `UiState::color_hide_options` holds Hide Options and is saved with the
  preferences. Its reader reads `true` as hidden and any other value as
  shown, so a bad value leaves the other preferences as they are.
- `ui.colorPanel {mode?, options?}` picks a mode as the panel menu does
  (the active color converts to the mode's model as one undo step) and
  shows or hides the options. It answers `{mode, options, shown}`. A mode
  the menu does not list, `lab` included, answers an error. The panel
  menu's Show Options and Hide Options call the command's function.
- `ui.inspect` reports the setting as `ui.color_hide_options`.

Docs: docs/control-protocol.md, docs/target-app-parity.md.
2026-10-16 04:04:49 +07:00
dotCooCoo ea59b066d4 Effect menu: Glowing Edges moves to a Stylize submenu under Raster Effects
On main, Glowing Edges… is listed in the Effect menu's vector Stylize
submenu, after Drop Shadow, Inner Glow, Outer Glow and Feather. The
reference app lists it in the Stylize submenu of its Photoshop effects,
below the vector effects.

The Effect menu now has a Stylize submenu under Raster Effects, between
Sharpen and Texture, and Glowing Edges is listed there. The menu sends
the effects at the Stylize path that PIXEL_EFFECTS names to that
submenu, and the vector Stylize submenu keeps its six effects. The
effect's id, its catalog entry, its effect.list entry and Apply Last
Effect do not change.
2026-10-16 04:04:49 +07:00
dotCooCoo 427cd65f30 Templates: Save as Template can also write a PDF-compatible .ait
File › Save as Template always wrote a .vctemplate. The save panel
offered only that type, file.saveAsTemplate ignored its format param,
and a name typed with .ait got .vctemplate added. New from Template
already opened a .ait as an untitled document.

Save as Template now also writes a .ait: the PDF-compatible file Save
As writes for .ai, carrying the document. The desktop save panel lists
VectorCraft Template first and the .ait type beside it, and the web
build's Save as Template dialog gets a Format menu with the same two
types. file.saveAsTemplate writes .ait for format: "ait", or for a path
ending in .ait when no format is given, and takes the .ai options;
file.formatOptions answers for ait. .vctemplate stays the default.
Exports and Export As still do not write .ait.

Docs: docs/mcp.md, docs/file-format-parity.md (.ait row),
docs/target-app-parity.md.
2026-10-16 04:04:49 +07:00
BFlat ed96a12196 Merge pull request #2201 from dotCooCoo/fix/paste-copied-svgz
Clipboard: a .svgz file copied in a file manager pastes as its art
2026-10-16 04:02:01 +07:00
BFlat be97caf90b Merge pull request #2148 from dotCooCoo/fix/paste-ai-file
Clipboard: a .ai or .ait file copied in a file manager pastes as its art
2026-10-16 04:01:44 +07:00
BFlat c48aba77e3 Merge pull request #2149 from dotCooCoo/fix/postscript-message-and-params
PDF/.ai import: the PostScript file error names File › Open and File › Place, which read the file
2026-10-16 04:01:28 +07:00
BFlat 6a56c7a5f6 Merge pull request #2202 from EricKuck/fix/pdf-shading-curve-stops
PDF import: an axial or radial shading keeps its function's curve when sampled into stops
2026-10-16 04:01:11 +07:00
BFlat 4a6d52e7ea Merge pull request #2218 from dotCooCoo/fix/same-value-spot-inks
PDF/.ai import: spot inks with the same values and different names keep their own swatches
2026-10-16 04:00:54 +07:00
BFlat fda7c9fae3 Merge pull request #2129 from dotCooCoo/fix/pdf-page-user-unit
PDF/.ai import: a .ai whose page has a UserUnit opens with its layers at the page's size
2026-10-16 04:00:36 +07:00
BFlat 0b10aa67a8 Merge pull request #2199 from dotCooCoo/fix/ai-cmyk-images-keep-samples
.ai import: CMYK images in the editing data keep their ink amounts
2026-10-16 04:00:18 +07:00
BFlat 2133a21096 Merge pull request #2051 from EricKuck/fix/ai-mesh-edge-points
AI import: a gradient mesh patch's edges bend through the anchors listed between its corners
2026-10-16 03:59:59 +07:00
BFlat ca8fb9a543 Merge pull request #2058 from EricKuck/fix/ai-pattern-gradient-canvas
AI import: a gradient in a pattern's definition is placed from the canvas, as the file's PDF part draws it
2026-10-16 03:59:49 +07:00
BFlat faba555c63 Merge pull request #2052 from EricKuck/fix/ai-styled-object-keeps-look-transparency
AI import: a styled object read in place of its drawn look keeps the transparency written on the look
2026-10-16 03:59:31 +07:00
BFlat 97d7e7488a Merge pull request #2050 from EricKuck/fix/ai-blurred-art-drawn-look
AI import: a drawn look that is an image stands for its object, as one of paths does
2026-10-16 03:59:15 +07:00
BFlat 029ab098a3 Merge pull request #2046 from EricKuck/fix/ai-clip-path-in-group
AI import: a clipping path alone in a group clips the clipping group around it
2026-10-16 03:58:57 +07:00
BFlat 2422d0ea5b Merge pull request #2030 from midasmatterhorn/fix/issue-2004-ai-opacity-mask
eps: an opacity mask in the editing data is named, not drawn whole (#2004)
2026-10-16 03:58:38 +07:00
BFlat e24edbab41 Merge pull request #2029 from midasmatterhorn/fix/issue-2003-ai-layer-opacity
eps: a layer's own transparency in the editing data is read (#2003)
2026-10-16 03:58:21 +07:00
BFlat 16af375873 Merge pull request #2060 from Silvertree2010/fix/2005-pdf-ai-import-a-document-with-on
Open PDFs and .ai files with an image over 256 MB decoded
2026-10-16 03:58:01 +07:00
dotCooCoo c246fc2f0e Clipboard: a .svgz file copied in a file manager pastes as its art
Paste takes a file copied in a file manager by its flavor, which
file_flavour tells from the file's first 4,096 bytes through
fileio::detect. detect names gzip data SVGZ unless it is a compressed
native file, and file_flavour gave no flavor for the format id svgz.
So a copied .svgz file did not paste as its art, although
clipboard.importSvg, the SVG flavor's command, reads SVGZ data.

A copied file whose gzip data unpacks to SVG markup now pastes through
the SVG flavor. file_flavour unpacks the head it is given with the
gzip decoder the SVGZ reader uses, up to where the head ends, and
checks the first 4,096 unpacked bytes with looks_like_svg, as detect
checks an uncompressed file, reading them as UTF-16 after a UTF-16
byte order mark. Other gzip files get no flavor, as before.

Docs: docs/target-app-parity.md.
2026-10-16 03:36:20 +07:00
dotCooCoo c5a1504e99 Clipboard: a .ai or .ait file copied in a file manager pastes as its art
Paste takes a file copied in a file manager by its flavor, which
file_flavour tells from the file's first 4,096 bytes through
fileio::detect. detect names PDF or PostScript bytes in a file called
.ai or .ait by that extension, and file_flavour gave no flavor for
the format ids ai and ait. So a copied .ai or .ait file did not paste
as its art, while the same bytes in a .pdf or .eps file did.

A copied .ai or .ait file whose bytes are a PDF now pastes through the
PDF flavor (clipboard.importPdf), and one whose bytes are PostScript
pastes through the EPS flavor (clipboard.importEps). Other files
with those extensions paste as before. A .ai saved without its PDF
part also gets the PDF flavor, and Paste answers it with
clipboard.importPdf's error, as it does for the same file named .pdf.

Docs: docs/control-protocol.md, docs/development.md,
docs/target-app-parity.md.
2026-10-16 03:36:20 +07:00
bflatastic b9341d3b3b i18n(bg): the PostScript file error names File › Open and File › Place 2026-10-16 03:36:20 +07:00
EricKuckandClaude Opus 5.5 f3b4c3ec78 PDF import: an axial or radial shading keeps its function's curve when sampled into stops
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 03:36:20 +07:00
bflatastic 136810bee2 PDF/.ai import: the tint transform test passes no ink names to space_of 2026-10-16 03:36:20 +07:00
bflatastic 90e6ff3de1 PDF/.ai import: the tests added on main pass the page's unit to layered_ai 2026-10-16 03:36:20 +07:00
dotCooCoo dc179c3671 .ai import: CMYK images in the editing data keep their ink amounts
When a .ai file, or an EPS with editing data, opened from its editing
data, the reader converted each embedded 8-bit CMYK image to RGB and
stored it as a PNG, so the document lost the image's ink amounts. When
the same .ai opens from its PDF part, the PDF importer keeps them in a
CMYK TIFF.

The reader now stores those samples with ImageBlob::cmyk_tiff, as the
PDF importer does (crates/pdf/src/import_image.rs). An alpha channel
that hides part of the image becomes the image's opacity mask, a
grayscale image over it, as in PDF import. Grayscale, RGB and 1-bit
images still open as RGBA PNGs.

Docs: docs/file-format-parity.md, docs/target-app-parity.md.
2026-10-16 03:36:20 +07:00
bflatastic 8af0459232 AI import: a gradient mesh keeps at most 65,536 points, however its patches are cut 2026-10-16 03:36:20 +07:00
EricKuck e93ba4c83f AI import: a gradient in a pattern's definition is placed from the canvas, as the file's PDF part draws it 2026-10-16 03:36:20 +07:00