Commit Graph
159 Commits
Author SHA1 Message Date
bflatastic dd2f5da98c Expand stroke: compact plain stroke spines before outlining
Squashed from #3457 onto main.
2026-10-22 02:21:59 +07:00
Louise FoxandClaude Opus 5.5 78105cc695 Plug-ins: filters read and set effects and stroke styles (G17)
A filter could reshape and repaint objects, but not set the stroke's
join, cap or miter limit, nor the effects an object's appearance holds.
A tool that prepares objects for a live effect (a sheet of samples,
each with the effect at its own settings) had to leave the user to
apply the effect to each object by hand.

- Each input stroke has its join, cap and miterLimit beside its paint
  and width (live effects get the same, through the shared encoder),
  and each object its own effect records, {id, params, visible}.
- An output stroke may set them (miter limit clamped to 1-500), and an
  output object's "effects" replaces its own effects.
- The host admits each record as effect.apply does: an effect that
  effect.list lists or an installed live-effect plug-in, parameters it
  takes (filled in to their defaults; a plug-in's checked against its
  manifest), one 3D effect that fits the object (its fills' and
  strokes' own effects counted), and the texture of an effect with
  Load Texture (Glass, Texturizer) as the effect stores it, never a
  file to read. A record the object already had stays as
  it is, so a filter that hands its input back changes nothing. One
  refused record refuses the whole output and the document stays as
  it was.

The plug-ins crate checks the records' form; which effects exist is the
engine's to say (objects::AdmitEffects, engine plugin::admit_effects).
effects::validate_three_d is the one check of whether a 3D effect fits
an object, for effect.apply, effect.setParams and the filters.

A path handed back as it was now stays the live shape it was: write()
dropped a live rectangle's or ellipse's parameters whenever the output
held a path, changed or not, so a filter that returned its input
turned a live rectangle into a plain path.

objects/tests.rs checks the round trip, stroke styles on existing and
new strokes, the host's refusal, the bad forms refused, and a live
rectangle handed back unchanged (a plain path before the fix);
tests_plugins.rs runs filters that set a built-in and a plug-in effect,
an echo that changes nothing, and six refused outputs.
CRAFT_PLUGIN_FRAME_WASM=<folder> saves a test filter to try in the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-22 01:25:50 +07:00
bflatastic eb9e064254 G2: Effect › Artistic › Fresco raster filter
Squashed from #3096 onto main.
2026-10-22 01:15:09 +07:00
bflatastic 5925e6339a G2: Effect › Sketch › Torn Edges raster filter
Squashed from #3010 onto main.
2026-10-22 01:06:25 +07:00
bflatastic a5c0c29b25 G2: Effect › Pathfinder › Soft Mix blends overlapping CMYK fills
Squashed from #3063 onto main.
2026-10-22 00:54:43 +07:00
bflatastic 03a2da13ba G2: Effect › Artistic › Colored Pencil raster filter
Squashed from #3028 onto main.
2026-10-22 00:30:39 +07:00
bflatastic 755f2b9158 G2: Effect › Artistic › Rough Pastels raster filter
Squashed from #3044 onto main.
2026-10-22 00:23:50 +07:00
bflatastic 695f7c0d4b G2: Effect › Artistic › Underpainting raster filter
Squashed from #3035 onto main.
2026-10-22 00:23:50 +07:00
bflatastic 03bd5e5a53 G2: Effect › Artistic › Paint Daubs raster filter
Squashed from #3032 onto main.
2026-10-22 00:23:49 +07:00
bflatastic f329f6271d G2: Effect › Artistic › Palette Knife raster filter
Squashed from #3038 onto main.
2026-10-22 00:23:48 +07:00
bflatastic 2cd7f156fb Add lighting presets to 3D effects
Squashed from #3442 onto main.
2026-10-22 00:01:37 +07:00
bflatastic c895f64063 Format: rustfmt after the batch 2026-10-21 23:38:03 +07:00
nrq6993-a11yandCursor dfa7bd12d9 G2: Texturizer Load Texture matches Glass and Illustrator.
Users can pick an image in the Texturizer dialog; it is stored as relief heights and survives save/open like Glass textures.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:02 +07:00
nrq6993-a11yandCursor 3f3888d040 G2: Effect › Pathfinder › Hard Mix mixes CMYK per face
Overlapping faces take the highest of each CMYK channel from every covering shape and drop strokes, matching Illustrator's Hard Mix effect.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 23:38:02 +07:00
bflatastic 0a3628f62f G2: Effect › Artistic › Dry Brush raster filter
Squashed from #3026 onto main.
2026-10-21 23:38:02 +07:00
dotCooCoo 8b67c4a943 Effects: expanded, flattened and exported art keeps the random Transform the canvas drew
Random in a Transform effect takes its seed from the object's id. When
art evaluated from an object repeats ids (the copies a Transform effect
makes, a copied group's members included, and the object under Crop
Marks), Expand Appearance and the baking in bake_document gave the
pieces new ids before expanding or baking their effects. The result
then drew with other random values than the canvas did: on main, 2,382
to 5,205 pixels of a 200 x 200 render changed after Expand Appearance,
and 7% to 14% after Flatten Transparency, for 50 pt squares with a
random Transform on their stroke, on their group or under Crop Marks.

bake_pieces now bakes each piece with the ids it has and gives new ids
after. expand_pieces expands the object's own pieces, and the other
members that repeat an id and hold a random Transform, before their
ids change. Expand Appearance now expands a node value
(expanded_node). The Crop Marks branch expands the object before the
object and the marks get new ids. The reply still lists the expanded
object first, then what was expanded inside it.
2026-10-21 23:14:34 +07:00
dotCooCoo d532e6d341 Use Preview Bounds: a clip group is measured where its clipping path's stroke paints
The renderer clips a clip group by its clipping path's geometry before
the path's geometry effects, and paints the path's strokes over the
clipped art without clipping them, with those effects. With Offset Path
on a stroked clipping path, the stroke paints along the offset shape
while the group was measured by the path's own stroke.

With Use Preview Bounds on, a clip group or layer whose clipping path
has strokes with geometry effects is now measured as the clipping
path's geometry and those strokes as drawn, in the Transform panel, the
bounding box, Align and Distribute. This holds inside groups and for a
clip group with a fill or stroke of its own. A clipping path
without a stroke still measures as the region it clips to, and a hidden
one adds no stroke.
2026-10-21 17:11:08 +07:00
dotCooCoo 1267efbcf9 Use Preview Bounds: objects are measured with their geometry effects
With Use Preview Bounds on, the bounding box, the Transform panel,
Align, Distribute, Distribute Spacing and object.setBounds measured
each object without its geometry effects. A square with Offset Path
10 measured as the square, and type with Outline Object measured by
its layout box instead of its glyphs.

The new vectorcraft_effects::preview module measures objects as they
are drawn. A path is measured by its effected shape, with the
geometry effects of each fill and stroke that paints. Type, images,
live objects and symbol instances are measured by their reshaped art.
A group or layer with geometry or Pathfinder effects, or with a fill
or stroke of its own that has geometry effects or paints members with
geometry effects, is measured by its evaluated art. A blend, envelope
or repeat whose objects have geometry effects is measured by its
evaluated art, with those objects measured after their effects. Raster
effects are not measured. In one object, the art of at most 4,096 symbol
instances is measured with its geometry effects, and the other
instances are measured without them. With Use Preview Bounds off, the
measures are unchanged.

object.setBounds sizes objects with geometry effects by measuring
them after each scale it tries, so type with Outline Object set to
100 pt wide gets a glyph box 100 pt wide.

Selection::anchors_box is split out of Selection::anchor_bounds. The
doc comment of VectorcraftApp::selection_bounds said that it includes
effects; it now says that it leaves them out.
2026-10-21 17:11:08 +07:00
bflatastic 15babbcacc Water Paper: no Adobe address in the shipped effect description, clippy (#2955) 2026-10-21 15:17:05 +07:00
bflatastic 55df023b65 Water Paper: rustfmt (#2955) 2026-10-21 15:17:04 +07:00
nrq6993-a11y 078e67a386 G2: Effect › Sketch › Water Paper raster filter (#2955) 2026-10-21 15:17:04 +07:00
Louise FoxandClaude Opus 5.5 2e4666a364 Plug-ins: live effects read their object's fills and strokes (G17)
A live-effect plug-in was given only its object's geometry. An effect
that draws as wide as the stroke it sits on, or tells its fills apart,
had to ask for them again as parameters, which then went stale when
the paint changed.

A live effect's input object now holds the object's fills and strokes
too, read only and in the form a filter gets them (one encoder,
objects::paint_lists, for both), and _context.item says where the
effect sits: "object", or {"fill": i} / {"stroke": i} for an effect in
a fill's or a stroke's own effects. effects::GeomContext carries the
object's appearance and the item, and the plug-in cache keys on the
paints, so a change of paint runs the plug-in again. Hosts from before
this give geometry only; nothing in the output changes.

Expand worked a fill's geometry effects out in the object's context, so
a plug-in effect there would be told "object" and a gradient fill was
expanded into a shape the canvas doesn't draw: fill_shape now gives them
the fill's context, as drawing does (expand.rs).

Siblings named with a declared prefix, the other half of such context,
are left out: the effected geometry is worked out from the object alone
wherever it is drawn, measured, hit-tested or expanded.

wat::when_given is a plug-in whose output depends on whether its input
or parameters hold a text. tests_plugins.rs uses it to check that an
effect sees the stroke's width and the fill, runs again for a thinner
stroke, and on a second stroke is told so; effect.rs checks the item
counting; a gradient fill under such an effect is drawn and expanded
into the same triangle. CRAFT_PLUGIN_PAINTS_WASM=<folder> saves a test
effect to try in the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhvnnnD5fTFupK8WWq35un
2026-10-20 08:39:41 +07:00
bflatastic c9d769ad4e Note Paper: grey tones on white like the other Sketch filters, rustfmt (#2954) 2026-10-20 08:30:05 +07:00
nrq6993-a11y 301783c480 G2: Effect › Sketch › Note Paper raster filter (#2954) 2026-10-20 08:28:51 +07:00
Martin Vlach f664564e97 Effects: Sketch › Bas Relief, Charcoal, Photocopy and Stamp (#2527)
Squashed from #2533 onto #2525 (Smudge Stick, Sponge, Watercolor): both sets in PIXEL_EFFECTS (45), the catalogs and the docs, whose counts now read 45/57.
2026-10-20 06:43:50 +07:00
Martin Vlach 5251246778 Effects: Artistic: Smudge Stick, Sponge and Watercolor (#2435)
Squashed from #2525 (its merges of main dropped).
2026-10-20 06:32:32 +07:00
bflatastic 8a7a1cf199 i18n: Plaster in every catalog that has the Sketch filters (#2990) 2026-10-20 04:52:37 +07:00
MarekSpodBiedry b560b97616 G2: Effect › Sketch › Plaster raster filter
Squashed from #2990 onto Chrome.
2026-10-20 04:52:26 +07:00
bflatastic 326b1c3139 i18n: Chrome in every catalog that has the Sketch filters (#2985) 2026-10-20 04:21:13 +07:00
MarekSpodBiedry 2ff683d5fe G2: Effect › Sketch › Chrome raster filter
Squashed from #2985.
2026-10-20 04:20:50 +07:00
bflatastic a5ec99d515 Effects: Graphic Pen's params doc names no vendor, as cargo xtask brands asks (#2967) 2026-10-20 03:14:27 +07:00
bflatastic 92c1657334 i18n: Chalk & Charcoal, Graphic Pen, Reticulation and Plastic Wrap in every catalog that has the Sketch filters (#2972, #2967, #2976, #3034)
Also cargo fmt over the four filters.
2026-10-20 03:14:27 +07:00
nrq6993-a11y a2c764d312 G2: Effect › Artistic › Plastic Wrap raster filter
Squashed from #3034 onto the Sketch filters; the Artistic label test lists it.
2026-10-20 02:55:29 +07:00
nrq6993-a11y 917aa29dd5 G2: Effect › Sketch › Reticulation raster filter
Squashed from #2976 onto Chalk & Charcoal and Graphic Pen.
2026-10-20 02:54:06 +07:00
nrq6993-a11y 11adb72d44 G2: Effect › Sketch › Graphic Pen raster filter
Squashed from #2967 onto #2972 (Chalk & Charcoal): both filters in the effect catalog, PIXEL_EFFECTS and the Sketch label and filter tests.
2026-10-20 02:52:33 +07:00
nrq6993-a11yandCursor 52380d683d G2: Chalk & Charcoal: fix clippy on luma type
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-20 02:50:13 +07:00
nrq6993-a11yandCursor 1f2007eb46 G2: Effect › Sketch › Chalk & Charcoal raster filter
Adds sketch.chalkCharcoal to the pixel-effect pipeline so Effect › Sketch exposes chalk highlights and charcoal strokes with public-doc-style parameters and tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-20 02:50:13 +07:00
bflatastic a52198cefa Appearance: image fills and strokes draw the shape Convert to Shape or Outline Object gives them
Squashed from #2500 (its merges of main dropped).
2026-10-20 02:06:54 +07:00
dotCooCoo ac1b380b40 3D and Materials: Extrude & Bevel and Inflate build a solid from each stroke's outline
Extrude & Bevel and Inflate built one solid from the path, painted with
the object's top fill or, without a fill, its top stroke. A filled and
stroked object lost its stroke, and a stroked open path was extruded as
the area it would enclose if it were closed.

Each visible stroke now builds a solid from its outline in its own
paint, beside the solid the top fill builds from the path, as Rotate
already turns each stroke's outline. When the fill and the strokes are
opaque with normal blending, the faces of all the solids are drawn in
one back-to-front order; otherwise each solid is drawn on its own, in
the order of the appearance. Under a light that is not white, a surface
drawn as one composite or with an opacity mask is also drawn on its
own. In that one order a flat cap turned toward the viewer is drawn
after every other face, untwisted solids included, so the stroke's
front cap covers the fill's walls inside it. The solids share one set
of bounds, and with Perspective one eye sees every inflated solid. A
stroke whose outline is past the 2,048-point limit is left out.

Inflate's cells cut by a hole now pass only the nearby contours to the
boolean. An inflated, outlined 72 pt "VectorCraft" takes about 8 ms
instead of 34 ms, and 66 ms with a 3 pt stroke.

Docs: docs/three-d.md (Fills and strokes), docs/target-app-parity.md.
2026-10-19 21:51:26 +07:00
EricKuckandClaude Fable 5.1 1b8e3d9ae1 Outer Glow grows its solid silhouette by a quarter raster pixel before the blur, as the glows AI files export measure
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 23:50:57 +07:00
dotCooCoo 22ce516e2d Appearance: the inline effect editor shows 3D, Warp, Rasterize and channel choices as dropdowns
The Appearance panel's inline effect editor still showed eight choice
parameters as text fields holding the stored value: Revolve's edge,
Extrude & Bevel's bevel, every Warp style's orientation, Rasterize's
Color Model, Background and Anti-aliasing, and the channel of Levels
and Curves. Their dialogs built these lists in their own bodies, so
effect::choices did not hold them. Nothing checked a channel's value:
a typo was stored, and Levels or Curves drew it as RGB.

effect::own_choices now holds these lists, each label as its dialog
passes it to its dropdown or radio buttons. The dialogs read the lists
from it (Rasterize's Anti-aliasing from the AntiAlias list it is built
from), and the inline editor shows each one as a dropdown with the
dialog's translated labels; Rasterize's Color Model reads CMYK or RGB
by the document's mode. effect.apply and effect.setParams refuse a
Levels or Curves channel other than rgb, red, green or blue (r, g, b
and any letter case are read too) with "`channel` must be one of rgb,
red, green, blue", and the document and its undo history stay as they
were.

Docs: docs/mcp.md (effect params), docs/target-app-parity.md.
2026-10-18 12:34:36 +07:00
dotCooCoo 26af00d108 Effects: vectorcraft-effects passes clippy on Rust 1.95, the minimum toolchain
With Rust 1.95, the workspace's rust-version, cargo clippy --workspace
--all-targets -- -D warnings stopped at vectorcraft-effects: clippy 1.95
reports nonminimal_bool on the parameter and pixel-size guards of
Cutout, Poster Edges and Neon Glow, and on the size check in
Rasterize::compose (Effect › Rasterize). Clippy 1.99 does not report
them.

Each check now has the form clippy suggests, one !(a && b && ...) in
place of !a || !(b && c) (!a || !b || !(c && d) in compose). The
operands and their order are unchanged, so the filters and compose
return early in the same cases as before.
New tests check that the three filters leave the art unchanged for a
pixel size of 0, -1, NaN or infinity and for non-finite parameters,
and that compose leaves the raster unchanged when a buffer's length
does not match its size or the image's area has no positive width or
height.
2026-10-18 01:27:13 +07:00
alpha-rock 039a467dcb M9: add multiple colored lights to 3D effects
Add opaque RGB light color and up to ten independently adjustable lights to
Revolve, Extrude & Bevel, Inflate and Rotate. Keep light selection, Add/Delete,
selected-light gizmos and preview transactions on the existing command path.

Preserve legacy white-light Inflate polygons, paint composition and seam joins,
including the 0.05 seam minimum. Use shared smooth normals for colored Inflate,
and batch solid-fill RGB channel paths behind one surface clip. Keep source
paths editable and SVG/PDF output vector artwork.

Rebase onto main f7de3d6a, retaining both sides of progress docs and all 22
catalogs. Shade freeform points once through the shared paint mapper. Document
release measurements and a reproducible CLI fixture, including the remaining
source-gradient cost and the one-light timing tradeoff.

Validation: full cargo xtask ci passed all 10 steps; 7,978 tests passed, zero
failed and 11 ignored. Four legacy white-light PNGs match unchanged main byte
for byte. Three paired release bench/perf repetitions completed without busy
warnings; all six standard perf runs passed every budget. Two-light solid
Inflate improved from 136.2 to 96.8 ms/frame at 1200x1200, default eight threads.
2026-10-18 01:11:39 +07:00
dotCooCoo 04b0be2c9f Merge main into feat/live-rasterize-effect 2026-10-17 04:09:12 -07:00
VictorandClaude Opus 5.5 d1a6e39b7e G2: Effect › Artistic › Cutout, Neon Glow and Poster Edges
Three original Artistic filters on the pixel pipeline, each with a
catalogue row, the generic live dialog, translations and tests.

- Cutout: colours softened by Edge Simplicity (less with Edge Fidelity),
  split into Number of Levels steps per channel and painted as flat
  pieces in their average colour; slivers along softened edges take the
  nearest large piece's colour.
- Poster Edges: channels posterized to Posterization + 2 levels, black
  lines on the edges of any channel and of the outline, widened by Edge
  Thickness and darkened by Edge Intensity.
- Neon Glow: dimmed greys screened with a glow colour from the light
  areas (positive Glow Size) or the dark ones (negative), as strong as
  Glow Brightness.

All keep coverage and work in document units at any Document Raster
Effects resolution. Photoshop filters 33/57; cargo xtask ci passes all
9 steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 16:17:54 +07:00
dotCooCoo 62fd793603 Expand: pattern fills expand into their tiles clipped by the shape
Object > Expand with Fill expanded gradient fills only. A pattern fill
stayed a live pattern, and object.expand.info did not report it, so the
dialog's Fill option was disabled for it. In the reference app, Expand
with Fill turns a pattern fill into the pattern's tiles, and the shape
becomes their clipping mask.

The Fill step now turns each visible pattern fill of a path or compound
path into a clip group shaped like the object, holding the tiles the
pattern lays over the object's box with fresh ids. The group isolates
the tiles' blending and takes the fill's opacity, blend mode and other
effects. Geometry effects of the fill or the object, such as Offset
Path, shape the group's outline and the box the tiles cover. A path in
the tile art with a random Transform effect is baked with its own id
first, and a path with a brush that varies at random (calligraphic or
scatter with Random variation, or bristle) is baked where the pattern
defines it first, so every tile matches the canvas. The object's other
fills and strokes stay on copies of it in paint order, and Expand stays
one undo step. Type with a pattern fill expands the same way once
Object outlines it, and object.expand.info reports pattern fills under
fill. It also reports gradient fills under a new gradient key, and the
dialog enables Expand Gradient To only when the selection has one. A
pattern fill stays as it is when it lays no tiles over the box, when a
group or type in its art has a random Transform effect, when the
pattern is scaled, rotated, reflected, stretched or sheared and its art
holds a symbol instance, and when the pattern is rotated, reflected,
stretched or sheared, or scaled while its art has geometry effects, and
its art has raster or 3D effects, or a geometry effect on a group or
type.

PatternDef gains instances_in_with, which places the tiles with a given
Scaling. Expand has stroke weights, effect distances and the tiles' own
pattern paints scale with the pattern's transform. When the pattern is
rotated, reflected, stretched or sheared, or scaled while its art has
geometry effects such as Roughen, the strokes and geometry effects of
the tiles' paths become filled art first (bake_appearance, and Outline
Stroke's conversion for brushed strokes), and their live shapes become
plain paths, in their opacity masks too. Tiles place their opacity
masks with them, unlinked ones included (place_art), which also fixes
unlinked masks in pattern tiles in PDF, EPS, EMF and WMF export and in
Distort Pattern Fills.

Faults in the shared code are fixed for every caller. assemble
dropped the effects of a sole part, so Expand lost a lone gradient
fill's own effects and Outline Stroke lost those of a stroke with a
gradient along or across it. Outline Stroke also dropped a brushed
stroke's raster effects from its brush art. Expand replaced an object
whose gradient fill could not be expanded with a plain copy; it now
leaves it as it was. Expand left the geometry effects of a gradient
fill, and with Object off those of the object, on the expanded art,
where they reshaped every strip; they now shape the clip group's
outline, and the gradient fits it, as for pattern fills.
bake_appearance dropped a stroke's raster effects from its filled
outline and filled the outline of a stroke with a gradient along or
across it with a gradient across the outline's box; it now keeps the
effects and makes the gradient meshes clipped to the outline, as
Outline Stroke does. Envelope Options > Distort Appearance uses it too.
PatternDef::offsets_covering overflowed on a region too large to count
in integers, as for a pattern scaled by 1e-20; it now gives no offsets.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
2026-10-17 15:14:42 +07:00
Marco Arnulfo 2f24495ffe Color effects recolour the points of a freeform gradient (fixes #2390) 2026-10-17 14:46:21 +07:00
dotCooCoo e37fbe3849 Effects: Smart Blur's Radius widens the edge search in Edge Only and Overlay Edge
Smart Blur's Edge Only and Overlay Edge modes marked a pixel when one
of its four nearest pixels differed from it by more than Threshold,
and Radius changed only the blur. The reference app's documentation
says that Radius sets the size of the area searched for dissimilar
pixels.

A pixel is now on an edge when one of the pixels the search reads
within Radius differs from it by more than Threshold and its
channels, alpha included, add up to at least that pixel's. The search
picks its neighbors as the Normal blur does, over the whole pixels of
Radius and within it, and always reads the four nearest pixels.
A color step gives a line about as wide as Radius on the side with
the larger sum, and a Radius of one pixel or less gives the same
edges as before. The edge modes' reach() includes the blurred copy
that a wide Radius reads.
On a 2,000 by 2,000 px image, Edge Only takes 0.49 s at Radius 10 and
0.34 s at Radius 100, and Normal takes 0.73 s and 0.39 s. Normal
draws the same pixels as before.

Docs: docs/target-app-parity.md.
2026-10-17 14:46:20 +07:00
vangel76andClaude Opus 5.5 9122ed0a7e MCP: cancel long exports, answer while one runs, report progress (#2165, #2171, #2172)
The MCP server read one line, ran it and answered it before reading the next,
so notifications/cancelled was never seen during a call and every request
(even ping) waited behind a running export.

- vectorcraft_doc::cancel: a thread-local cancel token and progress sink that
  long work polls. Checkpoints: PDF pages, PDF flattening, geometry bake,
  raster-effect flattening, drawing objects, raster artboards, and before any
  file is written. A cancelled command returns EngineError::Cancelled and
  changes nothing.
- Server::serve reads on its own thread: notifications/cancelled drops a
  queued request or stops the running one (no reply, as the MCP spec says),
  engine-free requests (ping, the list methods, prompts/get) are answered at
  once, a request that would wait behind a call running for 2 s is refused
  with -32003 naming that call, and _meta.progressToken gets
  notifications/progress (page n of m). Queue and output are bounded.
- The remote backend stops waiting for the app when the call is cancelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:49:48 +07:00
vangel76andClaude Opus 5.5 d3751cc07b Effects: Zig Zag Smooth points draw a continuous wave (#2343)
Smooth Zig Zag ran the ridge points through a Catmull-Rom spline. Its end tangents slant across the path, and where the ridges are taller than about twice their spacing the sharp-turn floor of turn_tangent lengthens the handles, so the result read as straight diagonals with rounded tips. Each peak and valley is now a smooth point whose handles run along the path, a third of the way to the neighbouring ridge, so every span follows a half cosine through the path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:20:26 +07:00