Commit Graph
306 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 8ac84dde20 M14.4: robustness docs name the library fuzzing and main's brands gate
docs/development.md lists the library files import_fuzz now covers, and the
PR template's CI checklist includes `cargo xtask brands`, which main added
after the no-panics branch was cut.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:40:34 +07:00
bflatasticandClaude Opus 5.5 0744347065 M14.4: tests main gained follow the no-panic lint rules
As the branch does for older tests: `panic!("…")` instead of
`unreachable!()` in test code (clippy.toml only allows unwrap, expect and
panic there), and the integration tests main added (CLI file I/O, render
gradient text and stroke geometry, SVG export suites) opt out of the
unwrap/expect/panic lints. No test logic changed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:40:34 +07:00
bflatasticandClaude Opus 5.5 8d0fae610d M14.4: fuzz the library loaders and main's new SVG import paths
import_fuzz now also feeds garbage and mutated `.vcswatches`, `.gpl`,
`.vcstyles` and `.vcflattener` files through `swatch.library.load` /
`graphicStyle.loadLibrary` / `flattener.presets.import`, uses what loads
(adds and applies the swatches and styles, previews and flattens with the
presets) and renders and exports the result. The SVG generator gains `<a>`,
Lab colours, mask options (`mask-type`, `data-vectorcraft-mask`) and paint
attributes. `PROPTEST_CASES` now raises the case count as the file's docs
say (an explicit `cases: 64` used to override it).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:40:34 +07:00
bflatasticandClaude Opus 5.5 7fb07bb3db M14.4: as_chunks for the pixel loops main gained since the no-panics branch
Same treatment as the branch's `as_chunks` commit (Rust 1.99's
`chunks_exact_to_as_chunks` lint) for the flattener's coverage pass, the
CMYK ink compositor, the Flattener Preview highlight and four test helpers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:40:34 +07:00
bflatasticandClaude Opus 5.5 1a5f7f6441 M14.4: no panics in the shipped code main gained since the no-panics branch
The M3/M4 work added ten panic sites the new workspace lints deny:
- doc: the lens/taper width profiles fall back to a uniform profile
  instead of `expect` (the presets are a constant table, covered by tests).
- engine: `FlattenOptions::default` builds the medium preset directly
  (`builtin`), `apply` skips an empty group instead of `expect`, the
  writable-format fallback reports an error instead of `expect`, and the
  new-art template's stroke comes back as an `Option` (it always has one)
  instead of `expect`.
- tools: the freeform gradient drag returns no actions for a grab it never
  starts (was `unreachable!`), and the gradient tool's release takes the
  gesture with `if let` instead of `expect`.

Behaviour on the success paths is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:39:18 +07:00
Brandon ThomasandClaude Opus 5.5 3e323bfa2a Robustness: VectorCraft never crashes — no panics in shipped code
Shipped code no longer unwraps, expects or panics: every one of the 124
unwrap()/expect()/panic!/unreachable! sites outside tests now handles the
failure (Result/Option, ?, let-else, defaults) and reports errors to the user.

- Lints: workspace clippy denies unwrap_used, expect_used, panic, todo,
  unimplemented and unreachable; clippy.toml allows them in tests, and
  integration tests, examples and testkit opt out explicitly.
- Safety net (vectorcraft_engine::guard): a panic in a top-level command rolls
  the document back and returns EngineError::Internal; a panicking tool is
  reset; the MCP server answers with an internal error and keeps serving; the
  UI frame and each control request are guarded.
- APIs that hid failures now return them: Rendered::to_png/to_jpeg/to_webp
  return Result (JPEG/WebP errors used to write empty files),
  FontDb::face returns Option, Region::top returns Option.
- Fuzzing: engine/tests/import_fuzz.rs feeds garbage, hostile and mutated SVG
  and PDF through import → render → export (30k cases locally, no panics).
- Docs: AGENTS.md/CLAUDE.md rule, docs/development.md "Robustness" section,
  README pointer and a PR template checklist.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:28:59 +07:00
Brandon ThomasandClaude Opus 5.5 0f241aac9f clippy: as_chunks for fixed-size pixel and pair chunks (Rust 1.99 lint was red on main)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:24:06 +07:00
BFlat c4236e1bb2 Merge pull request #64 from storytold/pr/scale-strokes-off-by-default
M3: Scale Strokes & Effects is off by default
2026-10-05 06:17:50 +07:00
bflatasticandClaude Opus 5.5 a0b694dcd8 M3.66: Scale Strokes & Effects is off by default
New preferences start with Scale Strokes & Effects off, as in Illustrator:
scaling an object keeps its stroke weights, dashes and effect distances unless
the option (or `strokes: true`) is on. Preference files that already store the
option keep their value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 06:17:36 +07:00
BFlat 5750786e63 Merge pull request #63 from storytold/docs/roadmap-m3-complete
Roadmap: M3 Paint & appearance complete
2026-10-05 05:57:37 +07:00
bflatasticandClaude Opus 5.5 b9fa2d27fd Roadmap: M3 complete (M3.7–M3.98)
M3 is done: the M3 row, the shipped list (swatches, colour, gradients, strokes,
appearance, masks, transparency, graphic styles, and the title bar) and the
colour, stroke and appearance parity rows cover everything that landed through
#62, with the few known gaps named. M6 notes the Boolean precision fix and M14
the custom title bar and content-sized dialogs. The totals are summed from the
rows again (they had drifted), and the method note records M3's measured pace.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:57:21 +07:00
BFlat bd7ed58376 Merge pull request #62 from storytold/pr/m3-gradient-on-strokes
M3: gradient within, along or across a stroke (M3.83)
2026-10-05 05:51:11 +07:00
bflatasticandClaude Opus 5.5 025d758abe M3.83: gradients within, along or across a stroke
The Gradient panel's Stroke buttons now work: with the Stroke proxy on a
linear or radial gradient they lay it within the stroke (placed on the
page, as before), along the path (start to end of each subpath) or across
it (left edge to right edge; inside/outside strokes span their visible
side). `paint.editGradient {stroke: true, strokeMode}` sets it (new art
too with nothing selected); `document.inspect` reports
strokeOptions.gradientMode. Model: StrokeLayer.gradient_mode (default
Within, not written).

Shared geometry in effects::stroke::gradient: ribs on the bisectors of
the flattened path (fans round sharp corners, widened under arrowheads,
following width profiles) cut slices each painted with a linear piece of
the gradient (Gradient::span). The canvas and raster export clip them to
the stroke's outline and arrowheads, grown a pixel and composited with
copy so there are no seams and translucent stops paint once. SVG and PDF
write the same slices clipped to the outline, with a warning (new
vectorcraft_svg::export_with_report). Outline Stroke and Expand make
gradient meshes clipped to the outline. Mesh quads now grow along their
edges so thin quads show no seams. Dashes, profiles and arrowheads keep
working in both modes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:47:54 +07:00
BFlat ccdb1b30f6 Merge pull request #61 from storytold/pr/eyedropper-test-flake
Tests: the eyedropper image-sampling test no longer flakes (colour settings changed by parallel tests)
2026-10-05 05:37:48 +07:00
bflatasticandClaude Opus 5.5 5a0e6be852 M3.52: stop the eyedropper image-sampling test from flaking
The engine unit tests share one process, and two colour-management tests
switched the process-wide Color Settings (the working RGB space to the wide
gamut profile, Black Point Compensation off) for a moment. Any test that
showed or rendered a colour at that moment saw it through the wrong profile:
clicking_an_image_samples_its_pixel read #008181 instead of #008080.

- Move the two tests that change the Color Settings to their own test
  binary (crates/engine/tests/color_settings.rs), so no unit test can see
  non-default settings.
- The image-sampling test compares the stored colour, not its display hex.
- every_command_survives_empty_params toggles the process-wide proof view
  (Proof Colors, Overprint Preview, Separations), so it now takes the
  colour-management test lock like the tests that read that view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:36:57 +07:00
BFlat 0038e00382 Merge pull request #60 from storytold/pr/m3-color-guide-limit-themes
M3: Color Guide Limit to Library, Edit or Apply without art, Color Themes panel (M3.95, M3.96)
2026-10-05 05:11:50 +07:00
bflatasticandClaude Opus 5.5 be648089be M3.96: Color Themes panel with a local theme library
Window → Color Themes: the Create tab makes a five-colour theme on a
harmony wheel (a harmony rule from the selected colour, colours moving
together; or Custom, each on its own), with a brightness slider, hex
field, current-colour picker and name; the My Themes tab lists saved
themes to edit, add to Swatches as a colour group, or delete. Themes are
kept in the preferences (Prefs::color_themes; no online service).

Commands: colorTheme.list, colorTheme.save {colors | color + rule, name?,
replace?}, colorTheme.delete, colorTheme.addToSwatches. Harmony::theme
and move_on_wheel live in the colour crate; the wheel is now a shared
widget (widgets::harmony_wheel) used by Recolor Artwork's Edit tab too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:08:28 +07:00
bflatasticandClaude Opus 5.5 81582c8d4f M3.95: Color Guide Limit to Library and Edit or Apply Colors without art
The Color Guide's library button opens a Limit to Library popup (None,
Document Swatches, the colour libraries): every harmony colour and
variation snaps to the nearest colour there (CIEDE2000), cached per
inputs so the grid isn't re-snapped every frame. `color.harmony` takes
`limitTo`, `ui.colorGuideLimit` sets the panel's limit, and Recolor
Artwork's Limit to Library also offers the document's swatches (one
shared engine helper, swatchlib::limit_palette).

Edit or Apply Colors opens Recolor with the harmony colours limited to
the same library; with no art selected the colours are the rows and OK
saves them as a new colour group.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 05:08:15 +07:00
BFlat 96a1a02436 Merge pull request #59 from storytold/pr/m3-freeform-canvas-focal
M3: freeform editing on the canvas; radial extent, aspect and focal point (M3.97, M3.98)
2026-10-05 04:48:55 +07:00
bflatasticandClaude Opus 5.5 d8eff17b9f M3.98: radial annotator extent ellipse, aspect handle and off-centre focal point
Radial gradients show a dashed extent ellipse: its handle across the bar
sets the aspect ratio, the ellipse elsewhere rotates it, and the dot inside
the centre ring drags the focal point (GradientGeom.focal) for an off-centre
radial. paint.setGradientGeom takes aspect (%) and focal ([x,y] | null), and
start/end may be left out; the focal point keeps its place in the ellipse as
the vector, aspect and object transform. It renders as a two-point radial,
exports and imports as SVG fx/fy and PDF two-point radial shadings, expands
to off-centre rings, and travels in gradient params and the native format.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:43:43 +07:00
bflatasticandClaude Opus 5.5 b8793eeaef M3.97: freeform points drag out to delete, spread ring drags, Lines mode draws lines
With the Gradient tool on a freeform gradient the selected point shows its
spread as a dashed ring with a handle; dragging either sets the spread. A
point dragged out of the object is removed on release, and double-clicking a
point opens the gradientStop popover, which now edits the selected freeform
point (colour, opacity, spread). In Lines mode successive clicks draw one
smooth line (with a rubber band to the pointer); clicking an existing point
continues the line from it, and Escape or a click off the art ends it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:43:43 +07:00
BFlat 37acaebf95 Merge pull request #58 from storytold/pr/m3-width-tool-new-art
M3: Width tool depth and the new-art appearance template (M3.64, M3.65)
2026-10-05 04:43:28 +07:00
bflatasticandClaude Opus 5.5 faf006e135 M3.65: new-art appearance template (stroke options and graphic styles with nothing selected, New Art Has Basic Appearance)
With nothing selected, Stroke panel edits (stroke.set / stroke.setAdvanced: cap, join, dashes,
arrowheads, profile...) and a graphic style clicked in the Graphic Styles panel
(graphicStyle.apply, add too) set up the next object drawn: its stroke options, fills, strokes,
effects, opacity and blend mode, linked to the style. The Appearance panel's New Art Has Basic
Appearance (appearance.setNewArtBasic, preference newArtBasic, on by default) now works: off,
new art takes the whole appearance of the last selection. paint.default resets the template,
appearance.newArt reports it, and with nothing selected the Stroke panel shows its stroke and
the Appearance panel its rows. PaintDefaults gains the template appearance, opacity, blend
mode and style.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:38:09 +07:00
bflatasticandClaude Opus 5.5 b79262d4f4 M3.64: Width tool depth: Width Point Edit, multi-select, Alt-copy and discontinuous points
Double-clicking a width point opens Width Point Edit (Side 1, Side 2, Total Width, a link
keeping the sides equal, Adjust Adjoining Width Points, Delete; dialog `widthPoint`, also
`ui.widthPointEdit`). Shift-click selects several width points, which drag and widen together
as one undo step; Alt-dragging a point's centre copies it (`stroke.widthPoint.copy`); dropping a
point onto another makes a discontinuous point (two points at the same t), and the stroke's
width steps there. `stroke.widthPoint.set` takes `adjustAdjoining`, keeps a discontinuous point
when only its widths change, and `stroke.widthPoint.remove` takes `indices`. Flipping a profile
along the path swaps the sides of its steps; `document.inspect` reports `widthPoints`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:38:09 +07:00
BFlat d9740931a5 Merge pull request #57 from storytold/pr/m3-cmyk-blending
M3: blend modes and opacity composite inks in CMYK documents (M3.90)
2026-10-05 04:37:52 +07:00
bflatasticandClaude Opus 5.5 9537eca5f7 M3.90: blend modes and opacity composite inks in CMYK documents
A CMYK document that shows transparency is drawn once per ink plane
(complemented C, M, Y; complemented K as a grey) so every blend mode,
opacity, group, knockout and effect composites inks, PDF-style; the
planes' inks are shown through the working CMYK profile (cached 4-D XYZ
table). Cyan multiplied over magenta now shows C100 M100. Opacity masks
keep screen luminance; RGB colours and placed images are separated
first. RGB documents and opaque CMYK documents keep the screen-colour
path.

PDF export of CMYK documents writes its transparency groups with
/CS /DeviceCMYK (luminosity mask groups stay RGB) and puts top-level
transparency in a non-isolated page-wide group so it blends in CMYK too.
Flatten Transparency composites CMYK documents ink by ink and makes CMYK
regions. Non-separable modes no longer give NaN for greys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:30:06 +07:00
BFlat 6ee994239d Merge pull request #56 from storytold/pr/m3-recolor
M3: Recolor Artwork reduction, methods, presets and Edit tab; Edit Color Group (M3.78, M3.79)
2026-10-05 04:28:14 +07:00
bflatasticandClaude Opus 5.5 fcc0f4ca0f M3.79: Edit or Apply Color Group opens Recolor Artwork on a colour group
Double-clicking a colour group's folder in the Swatches panel, or the options
button (Edit or Apply Color Group) while a group is selected, opens Recolor
Artwork on the group: without art the rows are the group's colours and OK
rewrites the group; with art selected the art is reduced to the group's size,
takes its colours, and art and group change in one undo step. New
swatch.editGroup {group, colors, rename?} rewrites a group in order (art linked
to its global swatches follows); recolor.apply takes group, groupColors and
rename.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:20:27 +07:00
bflatasticandClaude Opus 5.5 fca068a0a7 M3.78: Recolor Artwork with colour reduction, methods, presets and Limit to Library
Recolor Artwork keys colours by identity (model and exact values), so CMYK
colours stay CMYK and lookalikes stay apart. recolor.reduce groups the
selection's colours into rows (k-means in Lab, weighted by use; tints of a
global swatch share its row) with Preserve White/Black/Grays and Limit to
Library; recolor.apply maps rows by Exact, Preserve Tints, Scale Tints, Tints
and Shades or Hue Shift; recolor.randomize shuffles or varies new colours.
The dialog has Assign (rows with merge, separate, exclude, randomize, method,
library) and Edit (hue/saturation wheel with linked or unlinked handles,
brightness, harmony rules) tabs, and Edit Colors gains Recolor with Preset
(1/2/3 Color Job, Color Library). Colour parameters accept colour keys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:20:27 +07:00
BFlat 0c950aae0e Merge pull request #55 from storytold/pr/ui-dialog-stretch-test
UI: test that no dialog stretches with the screen
2026-10-05 04:13:34 +07:00
bflatasticandClaude Opus 5.5 1aac138b4f M14.1: a test that no shared-frame dialog stretches with the screen
Fourteen dialog openers (swatch, colour, style, flattener, expand, effect and
parameter dialogs) are drawn on a 1000 pt and a 2000 pt tall screen; each
dialog must be the same height on both, so nothing inside one fills the
available height again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 04:10:34 +07:00
BFlat 321d8e78cf Merge pull request #54 from storytold/pr/m3-scale-strokes-preview-bounds
M3: Scale Strokes & Effects complete, Scale Corners, Use Preview Bounds (M3.66, M3.67)
2026-10-05 04:10:17 +07:00
bflatasticandClaude Opus 5.5 65f0f34cd3 M3.67: Use Preview Bounds measures strokes in transforms, the bounding box and Align
With Use Preview Bounds on, the Transform panel, Properties and Control bar
X/Y/W/H, the Selection tool's bounding box, Align, Distribute and
Distribute Spacing measure visual bounds (the whole stroke). setBounds sets
the visual box, scaling the path so that kept or scaled strokes land on
the typed size. object.align/distribute/distributeSpacing take
bounds?: "preview"|"geometric"; the Align flyout item shows and toggles the
preference through prefs.set.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:59:37 +07:00
bflatasticandClaude Opus 5.5 5afc78a100 M3.66: Scale Strokes & Effects scales dashes and effect distances; Scale Corners works
Scaling with Scale Strokes & Effects on now scales stroke weights, dash
lengths and dash offsets and every distance parameter of object, fill and
stroke effects (groups and layers too), from new length metadata in the
effects catalogue. Off, nothing painted changes size and type keeps its
character strokes' weight. Scale Corners off keeps live corner radii.
object.scale/transform/transformEach take strokes?/corners? (default: the
preferences) and journal the values they used. The Scale dialog and a new
Transform Each dialog get both checkboxes, kept as the preferences on OK;
the panels' toggles go through prefs.set.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:59:37 +07:00
BFlat f3397fadc6 Merge pull request #53 from storytold/pr/m3-lab-spots
M3: spot colours defined in Lab, Spot Colors options (M3.85)
2026-10-05 03:55:29 +07:00
BFlat b32284aeae Merge pull request #52 from storytold/pr/m3-attributes-registration
M3: Attributes panel; Registration colour, trim marks and live Crop Marks (M3.76, M3.77)
2026-10-05 03:54:53 +07:00
bflatasticandClaude Opus 5.5 ea61d34a93 M3.85: spot colours defined in Lab, with Spot Colors options for Lab values or CMYK equivalents
Colours gain a CIE Lab model (`Color::Lab {l,a,b}`, serde tag `lab`), handled through the
CMS (display, separation, conversions, gamut), tints (toward paper white), keep_model, harmonies,
recolouring, blends, Info and the Color panel. Swatch Options gets a Lab mode
(`swatch.edit {mode: "lab"}` / `{color: {l,a,b}}`), and colour params accept `{l,a,b}`.

Swatches panel → Spot Colors… (`ui.spotColors`, dialog `spotColors`) runs `swatch.spotOptions
{useLab}` (Document.spot_use_lab, default on): Lab spot colours show and separate from their Lab
values, or art linked to them takes their working-CMYK equivalents, as one undo step. PDF export
writes Lab spot inks as Separation spaces with a Lab alternate (tint function from paper white),
or a DeviceCMYK one with CMYK equivalents.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:46:57 +07:00
bflatasticandClaude Opus 5.5 c1df6f0621 M3.77: [Registration] swatch on every plate, trim marks on every artboard, Japanese marks and live Crop Marks
[Registration] is a built-in swatch that prints 100% on every plate: Separations
Preview shows it on each process and spot plate and PDF export writes it as
/Separation /All. It can't be edited, moved, duplicated, merged or deleted;
swatch.list shows it after None and the Swatches panel applies it as a swatch.
Object > Create Trim Marks strokes the marks in Registration and marks every
artboard when nothing is selected ({style?, allArtboards?}). The preference
japaneseCropMarks (General) switches to Japanese marks: double lines at the
trim and bleed edges plus centre marks. Effect > Crop Marks (cropMarks) is a
live effect whose marks follow the object; rendering, SVG/PDF export and
Expand Appearance draw them. The mark geometry lives in doc::marks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:46:04 +07:00
bflatasticandClaude Opus 5.5 4591c85845 M3.76: Attributes panel with overprint, centre point, path direction, fill rule, image map, URL and note
Window > Attributes (Cmd+F11) shows Overprint Fill/Stroke, Show/Don't Show
Center, Reverse Path Direction Off/On, the non-zero / even-odd fill rule,
Image Map, a URL field with recent URLs and a Browser button, and a note.
New commands attributes.set and path.setFillRule; path.reverse takes ids and
reversed; attributes.info reports every value. Objects keep the attributes in
Node.attrs. SVG export wraps linked objects in <a>, and import reads links
back. The canvas draws the centre point of selected objects that show it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:46:04 +07:00
BFlat 9fcefde3f9 Merge pull request #51 from storytold/pr/pathops-near-horizontal-edges
Pathops: Booleans keep their corners on almost-horizontal edges; deterministic property tests (M6.10)
2026-10-05 03:45:49 +07:00
bflatasticandClaude Opus 5.5 be7006ccf5 M6.10: Boolean operations hold their area identities on almost-horizontal edges
The sweep compares a curve with another segment over a y-span shorter than
its tolerance by bounding boxes only, so an edge rising by less than that
tolerance (e.g. a rectangle edge cut through a circle's cap, whose computed
intersection end sits ~1e-12 off the horizontal) was mis-ordered and the
result lost a corner: xor/difference/area disagreed by up to 10%. Every
sweep input now snaps on-curve y values within the tolerance to one level
(handles follow and are levelled with their anchor), so such edges become
exact horizontals, which the sweep handles exactly.

- tests/regressions.rs: the six minimal failures found by the properties,
  the root cause in isolation, and the self-crossing "star" polygon.
- The star-polygon strategy rejected no angular step of half a turn or
  more, so it sometimes drew self-crossing polygons (the shoelace failure).
- Property suites run from a fixed seed (PROPTEST_CASES / PROPTEST_RNG_SEED
  still override), so CI cannot fail at random.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:36:30 +07:00
BFlat bc59c53b5a Merge pull request #50 from storytold/pr/m3-object-expand
M3: Object > Expand dialog; gradient fills expand to objects or a mesh (M3.84)
2026-10-05 03:32:51 +07:00
bflatasticandClaude Opus 5.5 705976fd96 M3.84: Object > Expand dialog; gradient fills expand to a mesh or N objects
Object > Expand… opens a purpose-built dialog (Object, Fill and Stroke
checkboxes, disabled when the selection has nothing for them; Expand
Gradient To: Gradient Mesh or Specify N Objects, default 255). Every
option now does something:
- Object: type to outlines, live shapes to paths, effects baked.
- Stroke: strokes outlined into filled paths.
- Fill: linear/radial gradient fills become N solid rectangles or
  concentric ellipses, or a gradient mesh with lines where the colour
  changes, inside a clip group shaped like the object; freeform
  gradients become a mesh shaped like the object. Colours keep their
  stops' colour model. With Fill off gradient fills stay live.
object.expand {} defaults match the dialog (stroke now defaults on);
new query object.expand.info and UI command ui.expandDialog.
Create Gradient Mesh on a gradient-filled object takes the colours the
gradient paints at each point (GradientMesh::for_path_with, from_grid,
highlight).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:25:33 +07:00
BFlat 4757dd265c Merge pull request #49 from storytold/pr/m3-mask-view-clip-paint
M3: view only the mask, lossless SVG mask options, per-document transparency grid, paint on clipping paths (M3.86–M3.89)
2026-10-05 03:24:19 +07:00
bflatasticandClaude Opus 5.5 3b2f46137a M3.89: a clipping path's fill paints behind the clipped art, its stroke over it
Paint given to a clipping path (paint commands with its id) now shows: its
fill paints inside the clip behind the clipped art and its stroke over the
art, unclipped, on the canvas and in SVG and PDF. Node::clip_paint splits the
clipping path into its fills and its strokes (type: its characters' too) for
all three outputs; the renderer caches the parts per clipping path. Clip group
visual bounds take in the stroke. Make Clipping Mask still leaves the clipping
path unpainted, as the reference app does; Ungroup no longer wipes a painted
clipping path's appearance (Release already kept it).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:11:04 +07:00
bflatasticandClaude Opus 5.5 799b507564 M3.88: Show Transparency Grid is per-document engine view state
view.transparencyGrid {on?} -> {on} is now an engine command (DocState.
transparency_grid; not undoable, doesn't dirty the document), so agents can
turn the grid on headless and each open document keeps its own setting. The
menu item, its Show/Hide label, the shortcut and the Properties panel button
use the same id; the canvas draws the grid for the active document only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:11:04 +07:00
bflatasticandClaude Opus 5.5 1603729ec3 M3.87: SVG opacity masks keep their Clip and Invert options
Exported masks carry data-vectorcraft-mask="noclip invert" (the options that
differ from clipping, not inverted). Import restores the flags and the original
mask art, dropping the backdrop rectangle and inverting filter group that draw
the options in SVG; a single mask object comes back as itself, not wrapped in a
group. Masks from other apps import as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:11:04 +07:00
bflatasticandClaude Opus 5.5 03571affbd M3.86: Alt-click the mask thumbnail to view only the mask
transparency.viewOpacityMask {on?, id?} shows only the opacity mask on the
canvas as greyscale coverage (luminance, clip and invert applied; the rest of
the art hidden) and enters mask editing; called again it shows the artwork and
keeps editing. The Transparency panel's mask thumbnail runs it on Alt-click and
is outlined in the accent colour while the mask is viewed. The state is
DocState.mask_view (shown while that mask is edited: DocState::shown_mask) and
reaches the renderer as RenderOptions.mask_view.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:11:04 +07:00
BFlat cbdef4ea1f Merge pull request #48 from storytold/pr/m3-graphic-style-libraries
M3: graphic style libraries with .vcstyles save and load (M3.80)
2026-10-05 03:07:34 +07:00
bflatasticandClaude Opus 5.5 874a287958 M3.80: graphic style libraries in the library panel, with .vcstyles save and load
Window > Graphic Style Libraries and the Graphic Styles panel's library button open
read-only libraries of original styles generated in code (Shadows and Glows, Outlines
and Rules, Hand-Drawn, Gradient Finishes, Shape Effects, Blends and Transparency) in
the shared library panel. Clicking a style adds it and applies it to the selection as
one undo step (Alt: on top). Save Graphic Style Library writes .vcstyles JSON (styles
unlinked from swatches, with the patterns they paint with) to the user library folder
(User Defined) or a file; Other Library loads one, or another document's styles.

Commands: graphicStyle.libraries, graphicStyle.library, graphicStyle.addFromLibrary,
graphicStyle.saveLibrary, graphicStyle.loadLibrary; UI: window.graphicStyleLibrary
(.other), ui.saveGraphicStyleLibrary, window.userGraphicStyleLibrary1-10. The user and
loaded library bookkeeping is shared with swatch libraries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 03:06:13 +07:00