Commit Graph
10785 Commits
Author SHA1 Message Date
Julius van der Vaart c8d7f2cce0 Stroke: Line Arrow's arms take the head's weight, at least 0.25 pt (#5421) 2026-11-10 06:42:19 +01: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
Julius van der Vaart fe487d8226 Merge branch 'main' of github.com:storytold/vectorcraft into 5421-line-arrowhead
# Conflicts:
#	docs/target-app-parity.md
2026-11-10 06:17:42 +01: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
Ahmed KhanandClaude Opus 5.5 f68338482f A full flush refused with ENOTTY falls back to a plain fsync (#1620)
A folder shared into a macOS VM with virtiofs (a Tart VM's --dir share)
refuses F_FULLFSYNC with ENOTTY (os error 25) rather than ENOTSUP, so
full_flush_unsupported did not match it and every save there failed. It
now matches ENOTSUP or ENOTTY, as craft-libs' atomic crate already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VE6j3WRdUkYkmdJG6Q3Me5
2026-11-09 21:13:07 -08: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
dotCooCoo 0a7b48503e Merge main into feat/script-run 2026-11-09 20:30:53 -08:00
dotCooCoo 62ed510c1e Scripts: a failed run puts the Caps Lock state back
view.capsLock joins the state a run puts back when it fails or is
stopped. A finished run keeps the state it set, and keeps the state the
app reported during the run when the script did not change it. The
pointer over the canvas is refreshed when a run's end changes Caps Lock.
2026-11-09 20:30:33 -08: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
Julius van der Vaart b6e49fa4c0 Merge branch 'main' of github.com:storytold/vectorcraft into 5421-line-arrowhead
# Conflicts:
#	docs/target-app-parity.md
2026-11-10 04:40:09 +01: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
dotCooCoo 16da351e5d Merge main into feat/script-run 2026-11-09 19:29:33 -08:00
dotCooCoo 2d5fc74cbf Scripts: a document a script opens counts toward the run's growth limit
A document that document.open or file.new adds during a run counts whole
toward the growth allowance: every object in its layers, symbols and
patterns, opacity masks included, and its document data.
2026-11-09 19:29:02 -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 b5f62c75b9 .ai import: the envelope type note doesn't promise the type is kept
A text slot the text document gives nothing is left out, with a note of
its own, so the note for type in an envelope no longer says the type is
kept hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 03:15:38 +00:00
jmverges 14b2a60e73 Merge main into fix/3179-envelope-type-note
# Conflicts:
#	docs/file-format-parity.md
2026-11-10 03:15:38 +00: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
BFlat 57f596126f Merge pull request #6379 from jmverges/fix/ai-separation-rasters
.ai import: images in a spot ink's Separation space keep their ink (#6350)
2026-11-10 09:46:21 +07:00
HassanKhan 9560b7cfe9 Spelling in the canvas context menu: suggestions, Ignore All and Add to Dictionary on a word Auto Spell Check underlined (part of #4936) 2026-11-10 07:46:05 +05:00
jmvergesandClaude Opus 5.5 c67a624128 .ai import: a note for type in an envelope (part of #3179)
An envelope of type keeps its drawn art with its type hidden, but nothing
in the import notes said so, while the other envelopes that keep their
drawn art do. Opening such a file now notes that the type in an envelope
comes in as the envelope's drawn art, not as editable type, and that the
type is kept hidden. The type is still not editable in the envelope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 02:40:38 +00:00
dotCooCoo dd55adb701 Merge main into feat/script-run 2026-11-09 18:39:42 -08:00
dotCooCoo 2c0d09e079 Scripts: File › Scripts › Other Script… runs JavaScript files
File › Scripts › Other Script… picks a .js or .jsx file, shows a warning
that can be turned off in Preferences › General, and runs the script
against the open documents through the `vc` object. The new
`script.run` and `script.stop` commands run and stop a script over MCP.

- A run is one transaction. Each document it changed gets one undo step
  named Run Script. An uncaught error, a limit or a stop puts every
  document, the paint, the clipboard, menu state and preferences back.
- A command that fails inside a run, or whose result is too large to
  reach the script, is undone and throws an error the script can catch.
- Files a command wrote stay on disk and the report names them, also
  when the run is stopped from the quit question.
- While a script runs, other commands that change anything fail with a
  busy error; MCP reports it as JSON-RPC error -32003.
- In the app the run advances one slice per frame with a Stop button in
  the status bar. A run that adds a great many objects asks whether to
  continue; Stop and Escape end it.
- Commands that change state outside the documents, such as prefs.*,
  plugin.* and file.place.queue, are refused to scripts.

Docs: docs/mcp.md, docs/gaps.md, docs/roadmap.md,
docs/target-app-parity.md.
2026-11-09 18:35:41 -08:00