Commit Graph
10333 Commits
Author SHA1 Message Date
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
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
BFlat 008595aa69 Merge pull request #6375 from dotCooCoo/fix/font-wheel-panel-scroll
Font menu: a panel scrolled past a closed font field keeps scrolling
2026-11-10 09:21:14 +07:00
BFlat a9a8b1e79c Merge pull request #6370 from dotCooCoo/fix/glow-pan-test-background-doc
Render: the glow pan test checks fractional pans, and background docs say straight alpha
2026-11-10 09:07:56 +07:00
BFlat 7a9c8fe860 Merge pull request #6372 from dotCooCoo/fix/wheel-turn-end-phase
Canvas: a touchpad's End or Cancel ends its wheel turn with Zoom with Mouse Wheel on
2026-11-10 09:02:49 +07:00
BFlat 51498975b4 Merge pull request #6368 from dotCooCoo/fix/measure-snap-to-glyph
Measure: both ends land on glyph guides with Snap to Glyph on
2026-11-10 08:58:14 +07:00
BFlat 27a64bf090 Merge pull request #6363 from dotCooCoo/fix/issue-6316
Fix #6316: a Transform effect's copies of a group are whole groups below the original
2026-11-10 08:53:49 +07:00
BFlat 15f34f5083 Merge pull request #6357 from dotCooCoo/fix/pathops-vc-unite-sweeps-twice
Path ops: Unite sweeps a single path once when its own sweep leaves its segments as they are
2026-11-10 08:35:40 +07:00
dotCooCoo 138bdd77e4 Font menu: a panel scrolled past a closed font field keeps scrolling
Since #5331, a closed font family field in Character, Properties or the
Control bar took every plain vertical wheel turn while the pointer was
over it. When a panel scrolled the field under a still pointer, the
field took the rest of the turns: the panel stopped scrolling and each
turn changed the selected type's font.

The field now takes the wheel only when the pointer was moved onto it
after the field last moved, and the wheel stream began there. A stream
that began anywhere else, a sideways scroll included, stays there until
0.5 s pass with no wheel input. 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, when the field moves or misses a layout pass, and when a
turn arrives while an overlay covers the field. Trackpad momentum stays
with the stream it follows and never starts one on a field. The rule and
its 0.5 s are VectorCraft's own.

Wheel turns the field does not take stay in the input in their order.
Moving the pointer onto the field and turning the wheel still steps the
font, as #5253 asks, in floating panel windows too. When the pointer was
moved onto the field while a window or overlay covered it, the pointer
has to move again after the cover goes away before the wheel steps the
font.
2026-11-09 17:32:58 -08:00
dotCooCoo 2989954301 Canvas: a touchpad's End or Cancel ends its wheel turn with Zoom with Mouse Wheel on
With Zoom with Mouse Wheel on, a wheel turn that a touchpad began did not
end on the End or Cancel phase, so a whole Cmd/Ctrl notch within 150 ms,
or in the same frame, still zoomed instead of scrolling sideways. The
turn now ends on those phases, as it does in the Scroll Sideways branch.

Tests cover End and Cancel in the frame of the next notch, a Start phase,
the scroll timeout with no frame in between, a table of fine_steps cases,
and exact zoom and scroll amounts.

Follows #6360.
2026-11-09 17:24:42 -08:00
dotCooCoo aca44ae14e Render: the glow pan test checks fractional pans, and background docs say straight alpha
a_coarser_glow_kept_whole_draws_after_a_pan_as_made_again compared only
whole-pixel pans with a fresh render. At a fractional pan it now compares
the pixels right of the rectangle, where only the glow draws, with a fresh
render at the whole pixel nearest the pan (either neighbor on a half pixel).

RenderOptions::background and render_node_thumbnail's background are
straight-alpha colors, as over_background reads them; their docs said
premultiplied.
2026-11-09 17:15:41 -08:00
dotCooCoo f91f16d993 Docs: list the groups whose Transform copies stay per member
A clipping group, a group with a fill or stroke of its own and a group
whose type is kept as type still copy each member in place, and a
group's copies leave out its guides.
2026-11-09 17:01:16 -08:00
dotCooCoo 28fc436ef3 Measure: both ends land on glyph guides with Snap to Glyph on
The Measure and Measure & Inspect tools ignored View › Snap to Glyph:
their press and end points snapped only to targets that hold no glyph
guides.

With Snap to Glyph, Smart Guides and Alignment Guides on, and Snap to
Grid and Snap to Pixel off, a drag's press and end land within the
snapping tolerance on the guides of live type that the Character
panel's options allow, except Anchor Point. A Shift-drag's end slides
along its 45° step to them, and the guide shows during the drag. With
Snap to Glyph off, the tools snap to the same targets as before and
show no guide. The targets are kept while the document, the window and
the snapping options stay the same, so a drag lays out the type once.
2026-11-09 16:59:54 -08:00
BFlat bca98461c4 Merge pull request #6366 from dotCooCoo/fix/issue-5632
Part of #5632: Convert to Grayscale stores an image with one gray channel
2026-11-10 07:50:43 +07:00
BFlat c7c6c12ca8 Merge pull request #6364 from dotCooCoo/feat/command-param-schemas
Commands: the View commands check their params against machine-readable schemas
2026-11-10 07:42:57 +07:00
BFlat e64a5f1996 Merge pull request #6290 from addibble/type-3d-redraw
Canvas: a 3D effect changed on type or a group redraws where its solid was
2026-11-10 07:27:46 +07:00
dotCooCoo 35db931810 Fix #6316: a group copies each member in place when a member makes more art
A group with a visible member that carries a geometry, Pathfinder or 3D effect, also in an opacity mask, keeps its previous copies. Copies stop at the budget with what each copy's own effects make, and a group's copies count the art of opacity masks.

The PDF and SVG tests for a group's image copies now expect the original on top.
2026-11-09 16:24:13 -08:00
dotCooCoo 4060f14ce0 Path ops: Unite sweeps a single path again when two segments lie within twice the sweep tolerance
The second sweep of `unite_all` joins contours that lie within the sweep
tolerance of each other, and the first sweep can leave such contours
apart. `solid::tests::meets_within_twice_the_boolean_tolerance` needs
that join. A single input now skips the second sweep only when no two
of its segments lie within twice the tolerance. The check compares at
most 512 pairs per segment and runs the second sweep past that.

`unite_parts` tests the single-input case before it builds the combined
path, and it takes `parts` and `inputs` by value so that both are freed
before the second sweep.
2026-11-09 16:19:01 -08:00
dotCooCoo 7ff3d25697 Part of #5632: Convert to Grayscale stores an image with one gray channel
Edit Colors > Convert to Grayscale wrote the new image as an RGBA PNG, so
a PDF wrote it as DeviceRGB, in a CMYK document too. The image is now
stored as a gray PNG, with alpha only when some pixel is not opaque. Adjust
Colors with mode gray and convert does the same.

An RGB image whose pixels are already neutral now converts, and an image
already stored as gray with no visible change is left as it is.
2026-11-09 16:15:12 -08:00
dotCooCoo cf8b6cb3b8 Docs: the params text equals the text the schema writes
The params literals are kept in the source, and a test checks that each equals the text its schema writes.
2026-11-09 16:06:46 -08:00
Drew Dibble b242f5acf5 Fit to Artwork Bounds: a change in place is measured again; the fix's evidence names EPS export
A new test changes the document in place (Arc::make_mut, with no other
holder and no change of revision) and checks the answer is measured
again: keyed on the document's address alone, it would keep the old
answer. The kept answer's doc says what it holds after the measure,
and that the fonts set the bounds of all type. Should measuring ever
ask again while it runs, it is measured without being kept, rather
than borrowed twice.

The stack overflow fixed in d5d04f868 also hit EPS export, and the
parity rows now say so. Two test docs are corrected: the fill-only
clipping is an older bug on main, and the outline-view difference runs
both ways on a few dozen hairline pixels.
2026-11-09 16:50:36 -07:00
Drew Dibble a9a061c8a9 Merge main into type-3d-redraw
# Conflicts:
#	docs/target-app-parity.md
2026-11-09 16:36:25 -07:00
dotCooCoo 27c9fc8dd2 Merge main into feat/command-param-schemas 2026-11-09 15:16:23 -08:00
BFlat 25cfa9d3cf Merge pull request #6355 from dotCooCoo/fix/issue-6125
Fix #6125: raster exports keep a Screen glow on the bare page
2026-11-10 06:05:32 +07:00
dotCooCoo d186d73c42 Commands: a schema with no required parameter allows null params
The engine reads null params as {}, but engine.commands listed every
schema with the root type "object", so a client that validated a call
against the schema refused one the engine accepts. A schema with no
required parameter now has the root type ["object", "null"]. A schema
with a required parameter keeps "object", because the engine refuses
null params for it as a missing parameter. Nested objects are unchanged.
2026-11-09 14:56:42 -08:00
dotCooCoo 93f75def04 Merge main into feat/command-param-schemas 2026-11-09 14:38:20 -08:00
dotCooCoo 4ec99c7cd9 Commands: view.transparencyGrid lists session as what it writes
The grid is view state of each open document, with no undo step, so its schema's x-writes is session rather than the default document. A test pins x-writes for all six View commands, and the level 1 schema hash is updated.
2026-11-09 14:37:28 -08:00
BFlat 8591637999 Merge pull request #6359 from ruchirguitar/g2-effect-gallery-preview
G2: Effect Gallery thumbnails and an in-dialog preview
2026-11-10 05:33:33 +07:00
BFlat 39c1f0283f Merge pull request #6360 from dotCooCoo/fix/touchpad-ctrl-zoom
Canvas: Cmd/Ctrl-wheel input finer than a whole notch zooms; only whole notches scroll sideways
2026-11-10 05:28:23 +07:00
dotCooCoo 59304f9964 Fix #6316: a Transform effect's copies of a group are whole groups below the original
A Transform effect with copies on a group copied each member on its own,
so one member's copies lay over the fills of the next member. Each copy
is now a whole group, the original on top and each copy below the one
before. A path's copies follow the same order. Guides are not copied,
and the copies stop at the node limit. A group with its own fill or
stroke, a clipping group and live type keep their previous copies.
2026-11-09 14:27:25 -08:00
dotCooCoo 2e6ba25598 Commands: parameter schemas cover every route, bound hostile values and translate their errors
Check a new call's params against the schema in execute_spec, after the
key check and before the parser, and check every command.batch step
before the first one runs. menucmds::run_raw and the two other direct
runs go through the same check. Saved steps keep keys the schema does
not name and have the values it names checked.

Limit nesting to 16 levels, cut error text and paths to a fixed length,
and copy nothing before it has passed. Treat a whole number written
with a fraction as an integer. Check declared defaults and duplicate
names once at first use.

Give the five schema errors rows in the 22 complete-message catalogs.
2026-11-09 14:19:22 -08:00
Drew Dibble 59cc303912 Canvas redraw: the stack overflow fix's evidence, and two tests' known differences
- The parity rows say what the fix covers: a 3D effect on a fill or
  stroke that builds nothing overflowed the stack with no effect of the
  object's own too, on the canvas, in Object › Rasterize and in raster
  exports.
- The builds-nothing test says the fill-only difference (a raster
  effect clipped to the path, cutting Roughen's spikes, when drawn
  without the 3D effect) is an older bug, not the design.
- The traced-shape test says why its area is bounded rather than drawn
  again: a partial render in outline view differs from the whole by one
  in alpha on 37 pixels, older than the test and unrelated to the edit.
- A doc comment rewrapped.
2026-11-09 15:13:42 -07:00
Drew Dibble 78b31831c2 Artboards: Fit to Artwork Bounds' kept answer kept by the document, with the plug-ins and fonts it reads
The answer whether Fit to Artwork Bounds has art to fit moves from a
function-local thread_local to DocState, beside any_empty_artboards, and
drops with the document. It is kept for that version of the document
(held weakly, as any_empty_artboards holds its own) and the artboards
asked about, and measured again when the installed plug-ins, whose
effects draw art, or the installed fonts, which set type drawn by its
effects' art, change: their generations are an explicit input of the
measure. A change of the selection or the view keeps it.

The test counts the measures, as the Delete Empty Artboards check's
does: a later frame, select.set and a view change keep the answer; an
edit, other artboards, and each generation measure again. Taking any
part out of the key fails it.
2026-11-09 15:13:41 -07:00
dotCooCoo 5eb6b62632 Flatten: blend modes do not read the white under the art
Flatten Transparency composited each region over white, so a blend mode
on the bare page read that white. Raster renders now draw their
background under the finished art, and the flattened color no longer
matched them: a Color Burn object on the bare page flattened to white
and rendered in its own color. The region now composites the art alone
and puts the white under it.

- Tests that relied on the white under the art now draw art under the
  style (`every_builtin_style_paints_visibly_on_a_rectangle`) or compare
  only whole-pixel pans with a fresh render
  (`a_coarser_glow_kept_whole_draws_after_a_pan_as_made_again`).
- New tests cover every blend mode on white and blue backgrounds, a
  thumbnail and the ink planes with a Screen glow, and
  `over_background` at background and pixel alpha 0, 128 and 255.
2026-11-09 14:13:08 -08:00
dotCooCoo 39d538dadf Canvas: Cmd/Ctrl-wheel input finer than a whole notch zooms
With Scroll Sideways with Cmd/Ctrl + Wheel on, wheel() turned all
Cmd/Ctrl wheel motion into a sideways scroll unless it arrived as a
pinch event. A wheel turn now zooms about the pointer once it holds an
event from a trackpad source, point deltas from an unnamed source in the
desktop app, or line deltas that are not whole notches. Whole notches
keep scrolling sideways, and the option's default is unchanged. The same
rule applies with Zoom with Mouse Wheel on. egui-winit reports a wheel
source only on macOS, so the delta decides elsewhere.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 13:48:07 -08:00
ruchirguitarandCursor e1b73f38bc G2: Effect Gallery thumbnails and an in-dialog preview
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-10 03:12:54 +05:30
dotCooCoo 1796c77765 Merge main into feat/command-param-schemas 2026-11-09 13:31:22 -08:00
BFlat e6680d6979 Merge pull request #6327 from thisisgomaa/fix/pdf-import-actualtext
PDF import: preserve authored text in ActualText spans
2026-11-10 04:23:23 +07:00
dotCooCoo 10909656d9 Path ops: Unite sweeps a single path once when its own sweep leaves its segments as they are
`unite_all` and `try_unite_all` swept each input under its own fill rule
and then swept all the resulting contours again under NonZero. With one
input whose sweep split and dropped none of its segments, the second
sweep ran over the same simple contours and doubled the time. Such an
input is now refit from the first sweep's contours. Inputs that cross
themselves and unions of several inputs are still swept again, and the
input paths are converted once instead of twice.
2026-11-09 13:20:20 -08:00
Drew Dibble 758a31890d Artboards: Fit to Artwork Bounds' kept answer follows the document's version and the plug-ins
fit_has_art kept its answer for the document's uid and revision, which
don't follow every change of what it measures: a document's tab coming
to the front replaces it with its composite fonts synced, and a plug-in
installed or removed changes what its effects draw, with no revision
moving either time. The answer is now kept for that document Arc, held
weakly as DocState::any_empty_artboards keeps its own, and the artboards
asked about, until the plug-ins' revision moves. A selection or view
change keeps it.

The test replaces the document with art on an artboard, its revision as
it was, and asks again: kept by revision, the answer was stale.
2026-11-09 14:18:05 -07:00
Drew Dibble bd9f6a04ef Canvas redraw: review follow-ups to the strengthened tests
- A 3D effect that builds nothing: af754bcb2 said the object draws exactly
  as without it under a Drop Shadow or Gaussian Blur. That holds only for
  a stroked object. With only a fill, the raster effect draws from the
  art made for the object, where without the 3D effect it is clipped to
  the path, so Roughen's spikes differ: the earlier wording was right.
  The test keeps the stroked cases exact and restores fill-only cases
  with a Drop Shadow and a Gaussian Blur, which differ in under 4% of
  the pixels and nowhere inside the path. Its reach check compares with
  the object drawn without its raster effect, since Roughen alone
  reaches past the path.
- The Vector Halftone test's bounds are a consistency check, and only
  holding no art tests the branch: its doc no longer says no solid is
  built (the free measure's halftone arm builds one). The overprint
  test's area check is labeled likewise.
- Recoloring a traced shape bounds the area the renderer that drew the
  trace object measures too, inflated by half the stroke and a pixel.
- The let-go test pins the nothing-to-draw branch: no frame, whole or
  part, began since the measure.
- The parity doc's feature row names the stack overflow fix and its
  test; a group test's doc says what its fixture holds.
2026-11-09 14:18:05 -07:00
dotCooCoo faea85f772 Fix #6125: raster exports keep a Screen glow on the bare page
A raster export filled its background into the page before the art, so
blend modes read it as art underneath: an Outer Glow in Screen mode, the
default, turned white on a white background and was missing from PNG and
JPEG files while the canvas showed it. Blending against nothing also
differed from the canvas for Difference and the other modes.

The background now goes under the finished art, in frames and in object
thumbnails. A blend mode reads only the art below it. A white object
under a Difference object still inverts.

Page Isolated Blending no longer changes a raster export on a bare page.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-09 13:02:28 -08:00
BFlat a90d7bcec1 Merge pull request #6346 from slafebre/fix/6140-extrude-refusal-reason
3D effect dialogs say why a selection is refused and turn OK off (#6140)
2026-11-10 03:47:25 +07:00
Ahmed Gomaa 323b3385bc Merge remote-tracking branch 'upstream/main' into fix/pdf-import-actualtext 2026-11-09 22:42:50 +02:00
dotCooCoo f688129ef2 Merge main into feat/command-param-schemas 2026-11-09 12:34:02 -08:00
Drew Dibble 2fcaaedb2d Merge main into type-3d-redraw 2026-11-09 13:20:30 -07:00
Drew Dibble d0b26c0772 Canvas redraw: the let-go test receives the worker's job; the fit cache's key documented
Review follow-ups:
- The test that art measured for an edit not drawn on this thread is let
  go first checks that measuring the edit through the canvas's renderer
  holds art, so a measure that held nothing can't pass it. Its worker is
  a test channel whose job the test receives, in place of a real thread's
  pending job, which a finished frame could clear. Taking out either
  let_go_of_measured fails it.
- fit_has_art's comment says why its one kept answer can't be stale: a
  document's uid is never reused in the process and its revision only
  moves forward.
2026-11-09 13:19:55 -07:00
BFlat 9394a43e58 Merge pull request #6347 from jmverges/fix/6314-pdf-separation-images
PDF import: Separation images keep their ink (#6314)
2026-11-10 03:18:58 +07:00
Drew Dibble af754bcb23 Canvas redraw: the renderer's measure tests check what their names say
Review follow-ups:
- The tests that a group with effects of its own is measured once a pass
  plant its kept bounds after the first artboard and read them back over
  the next two, so a measure made again fails. Its solid is built by the
  free measure, which the renderer's three_d_built doesn't count.
- The budget test checks that every object, past the budget too, is
  measured as the free measure does and draws as a new renderer draws it.
- A 3D effect that builds nothing draws the object exactly as without it,
  with a Drop Shadow or Gaussian Blur too, which reach past the path. The
  doc comment said those differ past the path; they don't.
- The brush widened in view first checks that the wider brush draws
  otherwise.
- Recoloring a traced shape changes no more than the shape, its stroke
  and a pixel's anti-aliasing.
2026-11-09 13:13:17 -07:00