Commit Graph
13399 Commits
Author SHA1 Message Date
Don Omondi 34caacc63b Library panels: the list fills a taller panel above its footer (#2992) 2026-11-12 20:05:14 -05:00
dotCooCoo 17314c5165 Control bar: Escape in the frame that releases the gripper cancels only the drag
The release frame's shortcuts run before the bar is drawn, with the
button already up, so they missed the drag and the Escape also reached
the active tool. The drag now counts as going on in that frame too.
2026-11-12 16:57:29 -08:00
dotCooCoo 61f170a761 Mesh: a point on a mesh's outline locates on its edge on every side
A point exactly on a gradient mesh's outline located on the top and left
edges but not on the bottom and right ones, where the nonzero winding
number is 0. object.mesh.addLine and Create Gradient Mesh at a point there
reported the point outside the mesh, and the Eyedropper did not sample it,
while the hit test selects the mesh there.

locate_within now takes the nearest point of the edge for a point within
a millionth of a point of the outline, with any tolerance, and returns
nothing for a point that is not finite. A point just outside still needs
a tolerance.
2026-11-12 16:56:22 -08:00
BFlat 487ffbba4b Merge pull request #6994 from dotCooCoo/fix/paste-style-pattern-order
Paste: text styles that paint with a pattern are pasted after the patterns they use
2026-11-13 07:49:08 +07:00
BFlat 89e22cb608 Merge pull request #6987 from dotCooCoo/fix/number-param-wrong-type
MCP: a required number given as a string is refused as the wrong type, not reported as missing
2026-11-13 07:49:01 +07:00
BFlat 0d553f9a4d Merge pull request #6985 from dotCooCoo/fix/fonts-in-masks-and-patterns
Fonts: Find Font, Replace Missing Fonts and font lists include type in opacity masks and patterns
2026-11-13 07:48:52 +07:00
BFlat 24af0c146a Merge pull request #6938 from dotCooCoo/fix/pdf-large-page-export
Export As: a PDF without artboards opens Save PDF and its options
2026-11-13 07:48:44 +07:00
BFlat cd6d197c58 Merge pull request #6992 from dotCooCoo/fix/open-filter-grid-presets
File formats: File › Open has filters for graphic style libraries and perspective grid presets
2026-11-13 07:48:31 +07:00
BFlat b4bc86c45c Merge pull request #6942 from dotCooCoo/feat/cjk-insert-aki
Type: Insert Aki Left and Right add space before and after a character
2026-11-13 07:48:24 +07:00
BFlat 23825d2943 Merge pull request #6993 from dotCooCoo/fix/revolve-plugin-item-context
Effects: a fill's 3D Revolve tells the plug-in effect before it which fill it sits on
2026-11-13 07:48:14 +07:00
dotCooCoo 1ddcfb38a7 Artboards: a right-click on a panel row opens a menu with Rename
A right-click on a row of the Artboards panel opens a menu with Rename,
which opens the Rename dialog (artboard.rename) for the selected
artboards: OK names them in one undo step, numbered in panel order when
there are several, and Cancel renames nothing. A right-click on a row
that is not selected first makes its artboard the active one and the
only selected one; on a selected row the selection stays.

A drag with the right button no longer moves a row: only the primary
button starts a row drag.
2026-11-12 16:47:27 -08:00
dotCooCoo 06db8adae9 MCP: a parameter nested too deeply is refused as bad parameters
A value that nests past a command schema's 16-level limit was reported
as an internal error ("please report this bug"). It is now a parameter
error that names the value's path, like the schema's other refusals; a
schema that is wrong in itself is still an internal error.

The schema check now also refuses a list of ids or a point inside 16
lists, since every value of those types is a list and the value check
refuses a list at that depth. A color there still takes a string.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 16:45:18 -08:00
jmvergesandClaude Opus 5.5 bb50d438c8 .ai import: type from the text document takes the face of its exact PostScript name and recorded version (#6983)
Type read from a text document's font record was set in whichever installed
face first answered to the record's PostScript name (ignoring case and
punctuation), and its family was then swapped for any available family
spelled alike but for case and punctuation (`FontDb::find_family`): a record
naming `SampleInk`, the PostScript name of `Sample/Ink`, set its type in
`SAMPLE INK`. Copies of one family and style under other PostScript names or
versions were not told apart either, so the type could set at other widths
than the file's own page.

- `named_font` / `document_font` (crates/eps/src/import/text.rs) give the
  family and style of the installed face of exactly that PostScript name,
  else one that differs only in case or punctuation, and name its version
  when the family and style alone resolve to another installed copy.
- The font record's key 5, its version string (name ID 5), is read and picks
  that version among the faces of the name. Each record is resolved once.
- `FontDb::postscript_faces` lists every installed face a PostScript name
  answers to, loading their families; the import prefers among them one of
  exactly that name.
- Type a PostScript program sets (EPS import) and legacy .ai type take the
  face of their font's name the same way.
- The .ai save writes a static font's version (`/5`) when its PostScript name
  alone would give the reader another installed copy (another version, or the
  same name in another family), keeps the version in the style it compares,
  and checks that the type comes back in the same family, style and face,
  taking the record's version as the reader does (up to 256 bytes), so such
  type round-trips without a note.

Measured on 224 users' files (used locally, never committed): 16 files set
some type in another face; 12 render differently against their own page,
10 closer and 2 further by 0.03% of the page; envelopes of type that open
live go from 154 to 159, none fewer in any file; no file opens another way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 00:33:13 +00:00
dotCooCoo 9b65bf2708 3D: a fill or stroke without the effect keeps an even-odd path's holes
With a 3D effect on one fill or stroke, the object's other fills and
strokes were drawn as nonzero paths, so an even-odd compound path or
path filled its holes in them. Extrude & Bevel, Inflate, Rotate,
Revolve and their Classic kinds now draw those items with the source's
fill rule, through the group code's shape_node helper.
2026-11-12 16:28:54 -08:00
dotCooCoo 7764da6ad8 Updates: Runtime derives Default on wasm32, so wasm32 clippy passes
`cargo clippy --target wasm32-unknown-unknown -p vectorcraft-ui-egui
-- -D warnings` stopped twice. In vectorcraft-text, MAX_FILE_SEARCHES
is used only by the system font search, which is not compiled for
wasm32, so it was dead code there. In vectorcraft-ui-egui, Runtime
holds only manual_window_open and status_message on wasm32, and the
hand-written Default set them to false and None, so clippy's
derivable_impls stopped the run.

- MAX_FILE_SEARCHES is compiled only for targets other than wasm32,
  like the SysFallback struct next to it.
- Runtime derives Default on wasm32, and the hand-written Default,
  which creates the updater, is compiled only for other targets.
2026-11-12 16:27:30 -08:00
Drew Dibble d7d77dd0a5 Merge remote-tracking branch 'upstream/main' into seam-round-joins
# Conflicts:
#	docs/effects-parity.md
#	docs/target-app-parity.md
2026-11-12 17:27:14 -07:00
jmverges a8fc5275b6 Merge remote-tracking branch 'upstream/main' into fix/remaining-type-envelopes
# Conflicts:
#	docs/target-app-parity.md
2026-11-13 00:25:12 +00:00
dotCooCoo b1a35f8428 Tests: envelope ink tests set type in a test face and check its outlines
The three envelope ink tests set "Type" in the default family and
unwrapped ink_bounds(). Without bundled fonts, craft-fonts or system
fonts (vectorcraft-effects' tests on a bare Linux host), no face draws
and the effects test panicked at the unwrap.

Each test now adds the Heighttest Sans face built from the bundled
Source Sans 3 (test-fonts feature, now also on vectorcraft-effects'
dev-dependency), sets the text in it, and computes the expected box
from the layout's glyph outlines. It checks that envelope_ink gives
that box as well as the before and after registration boxes, so a
change to the registered measure fails the test.
2026-11-12 16:23:10 -08:00
Don Omondi 806ffcbded Merge remote-tracking branch 'upstream/main' into fix/graphic-styles-list-height-2992 2026-11-12 19:19:37 -05:00
Sven Lafebre 6fa569ecc3 Merge remote-tracking branch 'origin/main' into fix/6969-rasterize-gray
# Conflicts:
#	docs/target-app-parity.md
2026-11-13 01:17:36 +01:00
Don Omondi d5f0fe3a3f PDF import: glyphs in a span with an empty ActualText come in as outlines (#6971) 2026-11-12 19:15:40 -05:00
dotCooCoo b1a3ec4a97 Merge main into feat/color-rgb-percent 2026-11-12 16:15:23 -08:00
dotCooCoo a14236eca8 i18n: script errors and failed-run lines are translated in every complete-message catalog
The status bar shows a failed script run as one line: the script's
name, the line and column (and the included file), the error and the
kind of failure, with any files the run left written. No catalog had a
row for that line, so the whole line stayed in English. The errors of
File, Folder, vc.run, $.evalFile, a script's $ object, its alert,
confirm and prompt dialogs, vc.document, vc.log, the limits a run can
pass and Drop Script Engine had no row in any complete catalog either,
and several were not listed by the untranslated-strings report.

- Every complete-message catalog has rows for these errors and for the
  failed-run line at each place and for each kind (syntax, thrown,
  limit, stopped, internal). The script's name, paths, numbers, code
  names ($.flags, File, answers, true, null, UTF-8), command ids and
  the text a script throws itself are kept.
- A failure with no line and column, such as an error in code eval
  compiled, is shown with one colon after the script's name
  ("t.js: TypeError: no (thrown)" instead of "t.js:: TypeError: no
  (thrown)").
- A placeholder that occurs twice in a message template matches only
  the same text both times. Before, "{name}: the next answer in
  `answers` is for {_1}, not {name}" matched a whole failed-run line
  and dropped its line, column and kind.
- The message scan reads the script host's fail(, value( and .over(
  helpers and the script interpreter's errors (the js crate), skips
  *_tests.rs files and cfg(all(test, ...)) modules, and reads a
  constant's text from the file or crate that declares it, so the
  Scripts menu's headers are no longer listed as messages.

Tests: i18n::tests::script_errors_are_translated,
script_host_errors_are_translated,
a_repeated_placeholder_matches_the_same_text,
message_scan_reads_constants_of_the_file_and_its_crate,
message_scan_reads_script_interpreter_errors,
message_scan_skips_test_modules_with_any_test_cfg,
tests::script_run::an_error_without_a_place_names_the_script_and_kind.
2026-11-12 16:13:33 -08:00
BFlat 4b344e91ce Merge pull request #6960 from dotCooCoo/feat/3d-graphics-planar
3D and Materials: map graphics onto the planar faces of 3D objects
2026-11-13 07:10:09 +07:00
BFlat 2d22db13ff Merge pull request #6978 from dotCooCoo/fix/docinfo-brush-type-translations
Document Info: Brushes shows brush types in the interface language
2026-11-13 07:10:00 +07:00
BFlat 267555bc54 Merge pull request #6982 from jayzed80/fix/offset-inset-open-path-6427
Offset Path: an inset keeps the outline of open paths
2026-11-13 07:09:25 +07:00
BFlat b70b52836c Merge pull request #6984 from mockydevs/fix/swatches-show-recent-colors-6617
Swatches: the panel menu's Show Recent Colors shows or hides the Recent Colors row
2026-11-13 07:09:15 +07:00
dotCooCoo 4bb1df4929 Plug-ins: a filter's gradient stop plates are clamped into range like its other colors
A filter's or importer's output clamps each gradient stop's offset,
opacity, midpoint, tint and color, but kept its four process plates as
given, so a plate below 0, above 1 or infinite was stored in the
document. Each plate is now clamped to 0-1: a value that is not a
number becomes 0, and an infinite one becomes 0 or 1 by its sign.
Painted live effects still refuse such a stop.

Docs: docs/plugins.md, docs/target-app-parity.md.
2026-11-12 16:04:43 -08:00
dotCooCoo d3375c1656 Color: Toggle RGB Percent switches the RGB units from the keyboard
The double-click right of an RGB field was the only way to switch R, G and B
between percent and 0 to 255 from the UI. ui.colorRgbPercent (Toggle RGB
Percent) is listed in the command palette and in Keyboard Shortcuts without a
default key, and switches the same setting. A parameter answers an error and
changes nothing. The label has a row in every complete catalog.
2026-11-12 16:04:30 -08:00
dotCooCoo 800715b42f Actions: Select Object matches letter case per character and rejects mistyped dialog fields
select.byNote compares letter case one character at a time, so a final
sigma matches σ, and Whole Word checks its boundaries against the
note's own characters, so İ neither ends a word early nor matches half
of itself. The search runs in time proportional to the note's length.

Set Selection passes its fields to ui.action.insertSelectObject as they
are, so a note or option of another type set through ui.dialog.set is
an error and the dialog stays open.
2026-11-12 16:03:57 -08:00
Don Omondi 68ea48965b Swatches: the swatch list fills a taller panel above its footer (#1638) 2026-11-12 19:03:50 -05:00
dotCooCoo 2b975d620e Actions: Insert Select Path stores the selected path as a step
The Actions panel menu has Insert Select Path, after Insert Menu Item.
With one whole path or compound path selected, it inserts a Select
Path step into the action being recorded, or after the selected action
or step. Playing the step selects that path again.

- New engine command `select.storedPath {id, fingerprint}`. The step
  stores the path's object id and a fingerprint of its geometry and
  fill rule ("v1:" and 16 lowercase hex digits, FNV-1a 64). Playback
  selects the path only while it exists unchanged and can be selected;
  otherwise the step fails and the selection stays as it was. A path
  on a locked artboard is refused in isolation mode too.
- Capture refuses an empty selection, several objects, selected
  anchors or segments alone, groups, type, guides, a path selected
  together with ruler guides, a member of a compound path, and hidden,
  locked or template art.
- New UI command `ui.action.insertSelectPath {set?, action?, step?}`.
  Step rows show "Select Path".
- `.vcactions` files that hold a Select Path step are written as
  version 4. Reading accepts versions 1 to 4 and refuses a Select Path
  step with invalid params.
- Rows for the new labels and messages in every catalog.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 16:02:00 -08:00
dotCooCoo 3031fcf96d Cutting Tools: the weed params text keeps the spacing range in one key
The comma in "more than 0, at most 100000" split the `spacing` entry in
the params text of `addon.cutting.weed`, so the params check read
"at most 100000 (...)" as a key and every_description_parses failed.
The range now reads "more than 0 and at most 100000".
2026-11-12 16:01:28 -08:00
dotCooCoo addbcd918e Paste: text styles that paint with a pattern are pasted after the patterns they use
Paste placed character and paragraph styles before the patterns, symbols
and brushes they use, and compared each one with the document's before a
pattern it painted with had its final name. A style that painted with a
pattern added as "Dots 2" kept painting with the document's own "Dots",
or was matched to a document style of its name that paints with that
"Dots". A symbol whose art paints with such a pattern, and a brush whose
art sets type in a style renamed on the way in, were matched the same way.

Paste, and the symbol, brush, pattern and Libraries imports that add art
through it, now place each style, symbol, pattern and brush after the
ones it uses, as Load Character Styles and Load Paragraph Styles already
did, and relink the styles they add at the end. Pasted back into its own
document, each definition stays the document's of its name.
2026-11-12 15:59:36 -08:00
dotCooCoo 12c4c06fc1 Save PDF: a PDF automation asked for stays closed after its save panel answers
View PDF after Saving checked only whether automation was acting at
that moment. When automation asked for Save as PDF without a path, the
save panel's answer arrives later, so the written PDF opened in the
system viewer. The check now uses automation_asked, which also covers
the answer of a save panel automation opened, in view_file and in
Export As.
2026-11-12 15:59:19 -08:00
dotCooCoo 2a5d255217 File formats: File › Open has filters for graphic style libraries and perspective grid presets
File › Open already opened `.vcstyles` graphic style libraries in their
library panel and imported `.vcperspective` perspective grid presets, but
its dialog had no filter for either. It now lists "Graphic Style
Libraries" after "Swatch libraries" and "Perspective grid presets" after
"Print presets". "All readable files" still lists documents only.

Other Library… and Locate Library… in the swatch and graphic style
library panels list their library filter once; the swatch dialog listed
"Swatch libraries" twice.
2026-11-12 15:55:58 -08:00
dotCooCoo 632830947a Merge main into feat/recolor-theme-picker
Xf::new is test-only on main now: the Color Theme Picker's canvas layer builds its view transform with Xf::at and the canvas's interface zoom.
2026-11-12 15:54:28 -08:00
dotCooCoo e3f1609195 Tests: Intertwine keeps a third member in front of a raised overlap's margin
A red, blue and green rectangle are made into an Intertwine group, and
red is brought over blue where they overlap. The green sliver meets that
overlap's edge over red only. The test checks that green still draws in
front of red within the 2 pt margin, that red's raised area takes in the
margin where only red paints, and that it leaves out the part where
green paints too.
2026-11-12 15:53:31 -08:00
dotCooCoo c97cea8a65 Align: artboard Distribute buttons need three artboards with Align to Selection
With the Artboard tool chosen and Align to Selection, two selected artboards are the first and the last of the distribution, so the Align panel's and the Control bar's Distribute buttons are off until three are selected. With Align to Artboard the spacing places the second artboard from the active one, and two are enough.

The Distribute Spacing field takes negative values with the Artboard tool chosen, as artboard.distributeSpacing overlaps the artboards with a negative spacing. With other tools its lowest value stays 0.

Art inside two overlapping artboards now goes with the one first in artboard order when the artboard Align and Distribute commands move them, and stays when that artboard stays, as in the other artboard edits.
2026-11-12 15:49:46 -08:00
dotCooCoo 3570efa0a9 Effects: a fill's 3D Revolve tells the plug-in effect before it which fill it sits on
The per-item Revolve loop passed a copy of each fill or stroke to the
geometry context, which finds the item by identity in the object's
appearance, so a plug-in effect before the Revolve was told it sat on
the object. Each copy now keeps its original item beside it.
2026-11-12 15:48:59 -08:00
dotCooCoo 705b688f2b Type: Insert Aki keeps a widened glyph's own width and an optical pair's letter
A glyph that justification widens to fill the line keeps its Insert Aki
before it at its width, and its own width, which ligature carets stand
in, now starts where the glyph is drawn. Optical kerning measures a pair
from the letter of a cluster rather than from a combining mark whose
advance holds the cluster's Aki.

The SVG test also checks centered and right-aligned lines with Insert Aki.
2026-11-12 15:48:12 -08:00
dotCooCoo 3efdf4ab9c MCP: a required number given as a string is refused as the wrong type, not reported as missing
A command that needs a number, such as `x` of `shape.rectangle`, `intensity`
of `edit.colors.saturate` or `index` of `object.envelope.setMeshPoint`,
answered "missing number `x`" both when the key was left out and when it was
given as a value that is not a number, such as "40". A value that is given but
is not a number is now refused with "`x` must be a number, not "40"", the
message the optional number checks already use, showing the value as given.
A key left out or given as null is still a missing number. Neither error
changes the document or records an undo step.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 15:44:02 -08:00
dotCooCoo fcec08e1bb Document Info: the clear-paint test expects Gradient Objects to skip clear gradients
Gradient Objects now leaves out fills and strokes at an opacity of 0,
so the test expects no gradient count once every fill and stroke of
the spot gradient object is clear.
2026-11-12 15:41:23 -08:00
dotCooCoo 5beed58f0f PDF export: the art-bounds warning appears only with Preserve Editing on
A PDF of the art's bounds never carries Preserve Editing data, and the
export adds its warning only when Preserve Editing was on. docs/mcp.md
and the document.exportPdf params text now say so.
2026-11-12 15:41:04 -08:00
Don Omondi 7215594abc Graphic Styles: the list fills a taller panel above its footer (#2992) 2026-11-12 18:33:41 -05:00
Drew Dibble 3aede6aca2 Merge remote-tracking branch 'upstream/main' into seam-round-joins
# Conflicts:
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-12 16:30:37 -07:00
dotCooCoo 52867e7096 Fonts: Find Font, Replace Missing Fonts and font lists include type in opacity masks and patterns
Find Font and Replace Missing Fonts looked only at the type on the layers
and in symbols, and the font list behind the export warning, Package and
the Missing Fonts dialog skipped opacity masks and pattern swatches.

- text.fonts lists the type in opacity masks (on objects and layers,
  disabled masks and masks inside other art included) and in pattern
  swatches, a swatch that nothing paints with included. A pattern's type
  counts once however many objects it paints. With selectionOnly, the
  masks of the selected objects and the symbols and patterns they show.
- select.font also selects the objects whose mask holds the font and the
  objects painted with a pattern whose art uses it (visible fills and
  strokes wider than 0). A layer's mask and an unused swatch select
  nothing.
- text.replaceFont changes that type in one undo step. Type in the mask
  of a hidden or locked object, and the type of a pattern that only
  hidden or locked selected objects paint with, keeps its font and is
  counted in skipped.
- addon.cleanup.replaceFonts replaces missing fonts in masks and
  patterns, hidden or locked type included.
- text.missingFonts, File > Package and the export warning read masks
  and patterns too.
- document.info's fonts for the whole document (the Fonts and Font
  Details categories and the text report) include the same type. With
  selectionOnly the list stays the selected type's fonts.

While a mask or pattern is edited, its type is taken from the editing
layer only.
2026-11-12 15:29:22 -08:00
Drew Dibble 1032fb3979 Round seams: Classic Rotate and Revolve tests, Wireframe edge, docs
- a_faces_seam_stays_within_half_its_width_of_the_face checks Rotate
  (Classic)'s branches too: No Shading keeps its paint, a gradient keeps
  the gradient, and Diffuse shades its plane darker at some view.
- classic_revolves_seams_are_round checks each seam's reach as well as
  its join, and covers a gradient.
- The Wireframe edge takes seam_stroke (the same stroke it built by hand).
- Docs: which seams went from miter to round and which already were;
  the white-light difference from the 2026-10-17 comparison is expected,
  not measured; the 3D area rows record the change; the performance note
  says its measurement predates the Classic Rotate and Revolve paths.
2026-11-12 16:28:51 -07:00
dotCooCoo 6c280fa05a Control bar: dragging the gripper away from both docked edges floats the bar
Dragging the Control bar's gripper and releasing it within 24 points of
the top (under the menu bar) or the bottom of the window docks the bar
at that edge, as before. Released farther from both edges, the bar now
floats over the window under the pointer, 800 points wide or as wide as
the window, without taking room from the canvas. A floating bar's
gripper moves it or docks it again, and its panel menu keeps Dock to Top
and Dock to Bottom, neither checked while it floats.

- window.control.dock takes edge "float"; a floating bar floats where it
  last floated, first at 60, 80 in window points.
- Escape cancels a gripper drag, and a release outside the window leaves
  the bar where it was. In a window too short for a floating bar between
  the two zones, a release docks it at the nearer edge. A floating bar is
  kept inside a window that shrinks.
- Workspaces and preferences keep whether the bar floats and where;
  older ones dock it at the edge they name. Menus stop at the window's
  bottom while the bar floats.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-12 15:25:11 -08:00
dotCooCoo d5a02ee872 Merge main into feat/3d-graphics-planar 2026-11-12 15:19:09 -08:00