Commit Graph
10404 Commits
Author SHA1 Message Date
BFlat cd9dc52c79 Merge pull request #6408 from dotCooCoo/fix/outline-mode-canvas
View: Outline draws on a white canvas, with artboards as their edges
2026-11-10 15:18:48 +07:00
BFlat 8400af1065 Merge pull request #6405 from dotCooCoo/fix/escape-resets-rotation-once
View: one Escape resets a rotated view when it has nothing else to end
2026-11-10 14:47:22 +07:00
dotCooCoo a0c2dbb6da View: Outline draws on a white canvas, with artboards as their edges
In Outline the canvas kept the Preview pasteboard color and filled each
artboard with white paper. Paths and type draw as black hairlines, so
art on the pasteboard was close to invisible in the dark interface
brightnesses.

In Outline the whole canvas is now white in every interface brightness
and Canvas Color, pasteboard included. The artboards draw no paper and
show as their edges, so Simulate Colored Paper, an artboard's background
color and the paper of View > Hide Artboards no longer show in Outline.
The artboard names and the ruler origin's crosshair draw in dark gray
there. In split views and floating windows the artboard names follow
that window's own Outline setting, as the canvas already did.
2026-11-09 23:34:14 -08:00
BFlat dc456e18c7 Merge pull request #6401 from dotCooCoo/fix/svg-export-mask-regions
SVG export: mask and filter regions cover the art they apply to
2026-11-10 14:31:02 +07:00
BFlat c86c535389 Merge pull request #6402 from dotCooCoo/feat/text-glyph-bounds
Type: an optional setting measures point type by its glyph outlines
2026-11-10 14:14:00 +07:00
dotCooCoo 858e4235db View: one Escape resets a rotated view when it has nothing else to end
Escape reset a rotated view only as the second of two presses less than
a second apart, and a click or another key between them started the
count again. With the Rotate View tool one press reset it.

Now an Escape that has nothing else to end resets the rotation. An
Escape that ends something else (isolation mode, a path being drawn, a
full screen mode, a dialog and the others listed in
docs/target-app-parity.md) ends only that, and the next Escape resets
the view, however long after it comes. Tools report a path or pick that
Escape ends with `Tool::ends_on_escape`.

The rotate view tests that needed two presses now check one press, and
the screen mode test that named the old pair is renamed.

Docs: docs/target-app-parity.md.
2026-11-09 22:56:53 -08:00
BFlat f23624aeba Merge pull request #6398 from Hassan1305/feat-4936-context-menu-followups
Part of #4936: follow-ups to #6385 (only the topmost type's word; dictionary tests one at a time)
2026-11-10 13:37:41 +07:00
dotCooCoo 8ae9ca2012 SVG export: masks over type keep the full region; live art measured expanded
A mask, inverted mask or knockout group over type now keeps the region
that covers any artwork. Type is measured by its layout or, on a path,
by the path, and a reader that sets live type in its own font can draw
it wider, so a region sized from that box could cut glyphs off.

A live object (blend, envelope, repeat, mesh, placed document,
intertwine) under a mask is measured as the art the writer writes for
it, expanded on its own with the same limits.

The writer keeps its symbol boxes and symbols' art reach in plain
fields instead of cells.
2026-11-09 22:37:17 -08:00
BFlat 9761a5a514 Merge pull request #6400 from dotCooCoo/fix/revolve-clear-face-opacity
Revolve: a member whose fill or stroke is at 0% opacity stays invisible in a revolved group
2026-11-10 13:33:21 +07:00
BFlat d576dd0d39 Merge pull request #6399 from dotCooCoo/feat/api-reference
Commands: engine.apiReference returns the command API reference in Markdown
2026-11-10 13:28:44 +07:00
dotCooCoo bd0515ccc8 SVG export: mask and filter regions cover the art they apply to
Opacity masks, inverted and unclipped masks, and knockout groups were
written with mask and filter regions of 200,000 x 200,000 units around
the origin. CairoSVG draws each mask into an image of its region's size
and stops with "out of memory", so a file with any of them did not
open; Firefox drew inverted masks wrong and WebKit drew knockout groups
and inverted masks wrong.

Each region now covers what the masked or knocked-out art paints: its
visual box grown by its raster effects, brush art and members' reach,
the mask art's box, and in a scaled symbol instance its strokes and
effects at their own size. A mask whose art reaches past its object
still comes back whole on import.
2026-11-09 22:16:58 -08:00
BFlat ff6c300a9d Merge pull request #6396 from dotCooCoo/fix/export-selection-keeps-clip
Export: exported objects keep the clips, masks and effects around them
2026-11-10 13:13:14 +07:00
dotCooCoo 860149a981 Merge main into feat/text-glyph-bounds 2026-11-09 22:03:17 -08:00
dotCooCoo 156613941a Type: glyph bounds take in underline and strikethrough bars
With Measure Type by Glyph Bounds on, point type is measured by its
glyph outlines and its underline and strikethrough bars, as the canvas
draws them. A typed W or H gives what is drawn that size, and underlined
or struck-through spaces are measured by their bars instead of the em
box.

The texts now say that each point type object selected as an object
(not inside a selected group) is measured this way, that moved type
snaps by its ink box, and which Arrange Tools commands follow the
option: Swap Objects, Replace with Top Object, Set Size of Each and
Sort Objects. Arrange in Grid and Group Overlapping Objects keep
measuring visual bounds.
2026-11-09 21:53:22 -08:00
HassanKhan 0d7c6dd6e2 Spelling right-click: Intertwine members by their tier at the point (part of #4936) 2026-11-10 10:48:52 +05:00
dotCooCoo c703cc5650 Revolve: a face drawn on its own keeps its fill or stroke's opacity and blending mode
A face of a group member that paints nothing (its fill or stroke at 0% opacity) was drawn without a group, so its zero opacity was lost and the member drew fully opaque. Such a face now keeps a group with the opacity and blending mode of its fill or stroke.
2026-11-09 21:37:27 -08:00
BFlat 04edd114b1 Merge pull request #6395 from dotCooCoo/fix/revolve-group-depth-order
Revolve: a group's faces keep one back-to-front order when a member is transparent
2026-11-10 12:27:32 +07:00
dotCooCoo f41c6d3043 Merge main into feat/api-reference 2026-11-09 21:24:53 -08:00
dotCooCoo 9ccea9cfcb Commands: engine.apiReference returns the command API reference in Markdown
engine.apiReference {} -> {markdown} writes a reference from the built-in
commands' parameter schemas each time it runs: a section per command that
has a schema, in registry order, with its label, menu path, API levels,
what it writes, cost, summary and return, and a table of its parameters
(type, range with included or excluded ends, unit, JSON or worked-out
default, required, levels and description; object members as dotted
rows). Commands without a schema, plug-in commands included, are left
out. Schema text is escaped so it stays plain text in its cell, control
characters become spaces, and a reference past 1 MiB is an error rather
than a cut-short text. The command needs no document.

MCP serves the same text as the fixed resource
vectorcraft://commands/reference (text/markdown; charset=utf-8); the
other resources still read as JSON.

The command's own schema raises the schema API level to 2. Its label and
its size error have rows in every catalog that needs them.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 21:24:13 -08:00
BFlat 0ddb5b6820 Merge pull request #6387 from dotCooCoo/fix/number-wheel-panel-scroll
Number fields: a panel scrolled past a number field keeps scrolling
2026-11-10 12:17:38 +07:00
dotCooCoo 49129c565b Export: exported objects keep the clips, masks and effects around them
Export Selection, Export for Screens by selection or by layer, Asset
Export and CSS Properties' rasterized pictures put the objects on a new
plain layer. An object inside a clip group, or on a clipping layer, was
written without its clip, in SVG, PNG and the other formats, and the
opacity masks, effects, opacity and blend modes of the groups and
layers that held it were dropped too.

These exports now keep each object in the layers and groups that hold
it, and crop the file to the bounds the renderer measures for that art
(a clip group by its clip). Those layers are drawn even when they are
hidden, template or non-printing layers, as the objects were before. A
PSD of the export names its layers after the document's layers instead
of one layer named Selection or after the asset.

document.export with selectedOnly uses the same code: an object of a
blend, envelope, repeat or intertwine is now written on its own instead
of inside a blend of one object, and a Shaper group keeps its source
art. A text export of the selection still holds only the selected type,
not that of a type clipping path around it.
2026-11-09 21:10:08 -08:00
dotCooCoo a1949095c6 Type: an option fits a text object's bounds to its glyph outlines
Measure Type by Glyph Bounds (`typeGlyphBounds`, off by default) is in
Preferences > Type > Glyph Bounds and the Transform panel menu. With it
on, the bounding box, the Transform, Control bar and Properties X, Y, W
and H, `object.setBounds`, the transform tools' reference point and the
Arrange commands measure point type selected on its own by the ink of
its glyphs instead of its em box. A typed W or H scales the live type
until its ink has that size.

- The engine caches each type object's ink in text space with its
  layout bounds; the cache is not saved.
- Use Preview Bounds adds the characters' strokes, and type with
  geometry effects is measured as drawn. Skewed type is measured by its
  outlines.
- Type that draws nothing, area type, type on a path and type inside a
  selected group are measured as before. Align and Distribute keep
  following Align to Glyph Bounds.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md,
object.setBounds params text.
2026-11-09 21:07:55 -08:00
HassanKhan e33906976d Spelling right-click: only the topmost type's word; dictionary tests one at a time (part of #4936) 2026-11-10 10:00:03 +05:00
dotCooCoo af2ee68bf3 Revolve: a group's faces keep one back-to-front order when a member is transparent
When any member of a revolved group had an opacity below 100% or a
blending mode, every member's surface was drawn whole in member order,
so the opaque members lost their shared depth sort and a transparent
member drew entirely over or under the others.

Every member's faces are now sorted into one order. Opaque faces go in
that order on their own. A transparent member's faces share one group
with its opacity and blending mode, and its scopes group them further,
unless a face of another member that overlaps them on the page lies
between them in the order; only there is the member split. Faces
overlap when the convex hulls of their outlines come within the 0.75 pt
seam stroke of each other. Faces that paint nothing never split a
member. Past 4,194,304 overlap tests on one group, each further face
of such a member is drawn as a part of its own.

Docs: docs/revolve.md.
2026-11-09 20:49:13 -08:00
BFlat 520b69efd9 Merge pull request #6391 from dotCooCoo/fix/empty-contours-profile-limit
3D: a profile's empty contours count toward its point limit
2026-11-10 11:46:48 +07:00
BFlat 2e5619db48 Merge pull request #6392 from dotCooCoo/feat/command-input-schemas
MCP: command listings give each command with a schema an inputSchema
2026-11-10 11:42:18 +07:00
BFlat d6edc2a028 Merge pull request #6389 from dotCooCoo/fix/butt-cap-collapsed-handle
Transform: a butt-capped curve with a handle on its end point stops at that end
2026-11-10 11:29:40 +07:00
BFlat 958b5d533e Merge pull request #6390 from dotCooCoo/fix/hidden-revolve-profile
Revolve: a hidden Revolve's profile is measured before the effects after it
2026-11-10 11:29:30 +07:00
dotCooCoo f0dd3dc585 Merge main into fix/number-wheel-panel-scroll 2026-11-09 20:28:30 -08:00
BFlat a4a53e5ca6 Merge pull request #6328 from makesomethingshit/fix/6307-spinner-hold-pr
#6307: Keep numeric spinner arrows repeating while held
2026-11-10 11:25:06 +07:00
BFlat b82aac12ef Merge pull request #6377 from dotCooCoo/feat/new-document-popups
New Document: More Settings is a pop-up, and Save Preset a mode of Preset Details
2026-11-10 11:23:51 +07:00
BFlat 02de4ad5b6 Merge pull request #6367 from dotCooCoo/feat/lock-guides-by-default
Guides: Lock Guides by Default in Preferences locks guides when the app starts
2026-11-10 11:23:42 +07:00
dotCooCoo b62a4d901a Merge main into fix/number-wheel-panel-scroll 2026-11-09 20:07:02 -08:00
BFlat 5488e0d9e5 Merge pull request #6386 from jmverges/fix/6348-devicen-images
PDF import: DeviceN images of one ink or of process plates keep their inks (#6348)
2026-11-10 11:04:11 +07:00
dotCooCoo 9d01c0e7b5 MCP: test that list_commands gives input schemas over stdio 2026-11-09 19:59:29 -08:00
dotCooCoo fd0be1aca0 3D: a profile's empty contours count toward its point limit
revolve_curve_samples counted a profile's contours but never compared the count with the limit,
so a profile made only of empty or one-anchor subpaths passed, although each contour takes one
profile point. HoleSearch::new then built one entry for each subpath without a limit, and mesh
export skips revolve_curve_samples when curve_samples is given. Both now refuse a profile with
more than 16,384 contours as too complex.
2026-11-09 19:57:27 -08:00
BFlat d06769c362 Merge pull request #6385 from Hassan1305/feat-4936-spelling-context-menu
Part of #4936: right-click an underlined word for its suggestions, Ignore All and Add to Dictionary
2026-11-10 10:51:03 +07:00
dotCooCoo 55e6c0a5b1 Revolve: a hidden Revolve's profile is measured before the effects after it
effect.setParams checks a Revolve before the edit, so turning a hidden Revolve back on measured
its profile after every geometry effect on the object, those after the Revolve included, and could
refuse a profile that draws. The check now measures the profile after only the geometry effects
before the Revolve, hidden or shown, as drawing does. An object holds at most one Revolve, hidden
ones included, so the check finds the same one that draws.
2026-11-09 19:47:40 -08:00
makesomethingshit 7a5925359c #6307: Keep spinner evidence beside numeric field history 2026-11-10 12:47:27 +09:00
dotCooCoo 1bf6d2e793 Transform: a butt-capped curve with a handle on its end point stops at that end
With Use Preview Bounds on, a curve piece that bends more tightly than
half the stroke weight after the last split had its box grown by half
the weight on every side. A handle on an end point makes the curve's
first or last control difference zero, so such a curve always reached
that fallback and measured past its butt end: a vertical 2 pt curve
from (50,10) to (50,60) with its first handle on (50,10) measured
Rect(49, 9, 51, 60) instead of Rect(49, 10, 51, 60).

The fallback now measures the piece from the directions its tangent can
take, the nonzero control differences, when they all lie within a
quarter turn of their sum. Pieces whose tangent turns further are still
grown on every side.
2026-11-09 19:47:04 -08:00
BFlat 07148d1a59 Merge pull request #6384 from dotCooCoo/fix/font-field-overlay-disarms
Font menu: a font field covered by an overlay needs the pointer moved again
2026-11-10 10:39:12 +07:00
dotCooCoo 64da0213e4 Merge main into feat/command-input-schemas 2026-11-09 19:35:27 -08:00
dotCooCoo 9172950ad5 MCP: command listings give each command with a schema an inputSchema
engine.commands, command_list and list_commands now list an inputSchema
next to schema for each command with a parameter schema. It is the same
JSON Schema 2020-12 schema with the root type "object", the form an MCP
tool's inputSchema takes
(https://modelcontextprotocol.io/specification/2026-07-28/schema);
schema keeps the root type ["object", "null"] when no parameter is
required. A command without a schema lists neither field. tools/list is
unchanged, and the commands are still run with command_run or
run_command.

brief now leaves out schema and inputSchema as well as the params text;
a listing filtered to the command's id gives them.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 19:34:24 -08:00
BFlat 4dbaa10e0a Merge pull request #6365 from dotCooCoo/fix/issue-3617
Revolve: type, groups and layers revolve, and the profile and face limits are higher
2026-11-10 10:23:52 +07:00
dotCooCoo 2c6a0b88db Number fields: a panel scrolled past a number field keeps scrolling
A number field took every wheel turn that came while the pointer was
over it. When a panel scrolled a field under a still pointer, the field
stepped its value with each turn that reached it.

Number fields now follow the rule #6375 gave the closed font family
field. A field takes the wheel only when the wheel stream began over
it and the pointer was moved onto it, or pressed on it, after the field
last moved or was covered by a window or overlay. A stream that began
anywhere else stays there until 0.5 s pass with no wheel input or the
pointer is pressed on a field. A stream that leaves its field does not
return to it. It leaves when the pointer moves off the field or out of
the window, and when the field moves or misses a layout pass. Trackpad
momentum never starts a stream on a field. When some of a frame's wheel
turns land on the field and some land elsewhere, the field takes none
of them and the panel scrolls with all of them. The rule and its 0.5 s
are VectorCraft's own.

Moving the pointer from one number field to another within 0.5 s of
the last wheel turn now scrolls the panel until the wheel pauses or the
pointer is pressed on the new field.

The stream state and the 0.5 s gap move to widgets/wheel_stream.rs,
which both kinds of field use.
2026-11-09 19:18:09 -08:00
jmvergesandClaude Opus 5.5 ae9ab1bc06 PDF import: DeviceN images of one ink or of process plates keep their inks (#6348)
An image in a DeviceN color space opened as an RGB image, losing its inks.

One of one colorant now opens as a Separation image does since #6347: a
grayscale image of its tints colorized by its ink. One of two to 32
colorants that are all process plates (the ink None may be among them)
opens as a CMYK image, each colorant's tint in its plate, its Decode array
applied (8 or 16 bits a sample). A DeviceN image that mixes a spot ink
with other colorants, such as a duotone, still opens as RGB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 03:09:42 +00:00
BFlat ef81d550e4 Merge pull request #6371 from dotCooCoo/fix/issue-6361
Part of #6361: Cut takes direct-selected anchors out of the path
2026-11-10 10:07:20 +07:00
BFlat 59cecd3d28 Merge pull request #6381 from dotCooCoo/fix/mcp-schema-keywords
MCP: docs list every JSON Schema keyword in a command's schema
2026-11-10 09:59:14 +07:00
BFlat 695721f9df Merge pull request #6380 from dotCooCoo/fix/preview-bounds-butt-line
Transform: butt-capped lines measure to their end points with Use Preview Bounds
2026-11-10 09:59:06 +07:00
dotCooCoo 4f4a447b05 Font menu: a font field covered by an overlay needs the pointer moved again
A closed font family field stayed ready for the wheel as long as it kept
its place and drew in each pass. Coverage was checked only when a pointer
move or wheel turn arrived. When a window, modal or popup covered a still
pointer and went away with no input in between, the next plain wheel
turn changed the selected type's font.

The field now stops being ready, and a wheel stream it held ends, in any
pass where the last pointer position is over the field but on another
layer. The pointer has to move onto the field again before the wheel
steps the font.
2026-11-09 18:47:38 -08:00