Commit Graph
238 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 512e96e7c8 M14.3: the dragon app icon is the brand mark
The mark left of Home (and in the About box) is now the VectorCraft app icon,
the engraved dragon from assets/app-icon/ (128 px PNG, include_bytes!), decoded
once per context into a mipmapped texture and drawn at 22 pt with the icon's own
rounded corners. The old code-drawn gradient square with a pen nib is removed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 02:00:05 +07:00
bflatasticandClaude Opus 5.5 3c856caa65 M14.2: custom title bar on Windows and Linux
The window is undecorated off macOS and the app bar is the title bar: Minimize,
Maximize/Restore and Close caption buttons (46 pt, drawn in code, Close hover red)
at the far right, empty bar space drags the window and double-click toggles
maximize, and 5 pt invisible edge/corner zones resize it (off while maximized or
fullscreen). Close runs app.quit, so modified documents are asked about first.
The right-side group now fits the space after the menus: Discord drops out and
the search box becomes an icon at narrow widths, so nothing overlaps at 800 pt.
macOS and the web build are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 02:00:04 +07:00
BFlat 8dc6396971 Merge pull request #38 from storytold/pr/m3-graphic-styles-panel
M3: Graphic Styles panel depth, Tile Edge Color, Show Swatch Bounds (M3.71, M3.72)
2026-10-05 01:59:14 +07:00
bflatasticandClaude Opus 5.5 b36fd67bf4 M3.72: Tile Edge Color dialog and Show Swatch Bounds in pattern editing
Object > Pattern > Tile Edge Color... picks the colour of the tile edge
(a layer-colour preset or a custom colour), stored in the preference
patternTileEdgeColor and passed to the renderer. Pattern Options gains
Show Swatch Bounds: a dashed outline of the part of the tiling the swatch
repeats (the period of brick and hex tilings).

Commands: prefs.set {patternTileEdgeColor}, pattern.options
{showSwatchBounds}, ui.tileEdgeColor. Model: PatternDef.show_swatch_bounds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:50:25 +07:00
bflatasticandClaude Opus 5.5 d8b942c43d M3.71: Graphic Styles panel views, previews, merge, reorder and drag and drop
Thumbnail, Small List and Large List views; thumbnails rendered on a
square or on type and cached by a hash of each style's look. Merge
Graphic Styles (named in Graphic Style Options), drag to reorder, delete
asks first (Alt-click does not), the right button held on a style shows
it large on the selected object. Styles dragged onto art apply to the
object hit; art dragged off the canvas or the Appearance thumbnail dropped
on the panel becomes a new style. Override Character Color (on by
default) clears type's character fill and stroke when a style with fills
or strokes is applied. Control bar Style picker.

Commands: graphicStyle.merge, graphicStyle.move, graphicStyle.setOptions,
ui.mergeGraphicStyles. Prefs: overrideCharColor.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:50:13 +07:00
BFlat 1c18851728 Merge pull request #35 from storytold/pr/m3-flatten-transparency
M3: Flatten Transparency engine (M3.91)
2026-10-05 01:39:03 +07:00
bflatasticandClaude Opus 5.5 f75d7628c7 M3.91: Object > Flatten Transparency turns transparent art into opaque art that looks the same
object.flattenTransparency {preset?|options, ids?} splits the targets into groups of overlapping
objects. Groups with transparency become atomic regions (faces of the arrangement of every fill,
outlined stroke and clipping path) filled with the colour the renderer composites there: blend
modes, group opacity, isolated and non-isolated groups, knockout groups and knockout shapes, over
white or (Preserve Alpha) over nothing. Regions reached by gradients, patterns, images, opacity
masks or raster effects are rendered into one image per group, under the regions, clipped to
them with Clip Complex Regions. The raster/vector balance rasterizes whole groups past its region
budget (all at 0). Options and presets live in FlattenOptions; single visible paints keep their
colour and overprint; text and strokes can be outlined everywhere. One undo step.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:36:18 +07:00
BFlat cc514957f5 Merge pull request #32 from storytold/pr/m3-freeform-gradients
M3: freeform gradients — model, rendering, commands, tool and panel (M3.81, M3.82)
2026-10-05 01:29:29 +07:00
bflatasticandClaude Opus 5.5 ed308d61f7 M3.82: freeform point and line commands, Gradient tool and Gradient panel freeform section
paint.freeform.addPoint/setPoint/deletePoint/addLine/splitLine/selectPoint
and the paint.freeform.get query edit the freeform gradient behind the active
proxy (one undo step each; errors on other paints). The session keeps the
selected point. The Gradient tool on a freeform gradient selects and drags
points, adds points on the art (joined in Lines mode) and on lines, and
deletes the selected point. The Gradient panel shows the Draw toggle and the
selected point's colour, opacity, spread and Delete Point; the Color panel
recolours the selected point.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:21:39 +07:00
bflatasticandClaude Opus 5.5 2877407111 M3.81: freeform gradients: colour points with spread, smooth lines, first points placed inside the shape
Freeform gradients carry their own points (colour, opacity, spread) and lines
(Catmull-Rom curves through points), blended by inverse-distance weighting
from each disc of pure colour. paint.editGradient {kind: "freeform", mode?}
places 4+ points inside the shape, coloured along the stops (which follow the
points' colours). The renderer samples the field on a cached colour grid
drawn as an image paint clipped by the fill or stroke. Moves, transforms,
box rebases and warps move the points; old files load unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:21:38 +07:00
BFlat 0d145986e8 Merge pull request #28 from storytold/pr/m3-stroke-on-type
M3: stroke on type, Stroke panel units, mixed values and Control bar (M3.40, M3.41)
2026-10-05 01:21:10 +07:00
BFlat 09778f236c Merge pull request #27 from storytold/pr/m3-swatch-libraries
M3: swatch libraries — library panel, generated libraries, save and load, gradient libraries (M3.56–M3.58)
2026-10-05 01:11:53 +07:00
BFlat 1ea13b54d3 Merge pull request #26 from storytold/pr/m3-container-appearance
M3: group, layer and type appearance with Contents and Characters rows (M3.68)
2026-10-05 01:10:47 +07:00
bflatasticandClaude Opus 5.5 64d366e2ae M3.41: Stroke panel follows Units > Stroke, shows mixed values blank and reaches the Control bar
- Weights and dash lengths show in Units > Stroke in the Stroke panel,
  the Control bar and the Properties panel (Session::stroke_unit).
- Values the selected objects don't share show blank (weight, miter
  limit, dashes) or light no button (cap, join, align), from a
  per-revision cached DocState::stroke_mixed.
- Align Stroke Inside/Outside are disabled for open paths and type.
- The Control bar weight is a preset spinner with a width profile
  dropdown, and its Stroke link (and the Properties panel's) opens the
  Stroke panel as an anchored popover instead of docking it.
- The panels show a group's first painted object's stroke, as the
  Stroke proxy does (Session::shown_stroke).
- stroke.set on a group skips images and symbol instances.
- document.inspect reports each object's strokeOptions (cap, join,
  miterLimit, align, dash, dashOffset, alignDashes, arrows, arrowAlign,
  profile) in stroke.set terms.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:10:14 +07:00
bflatasticandClaude Opus 5.5 323b178ea7 M3.40: stroke on type — the Stroke panel edits the characters' stroke
With type selected, stroke.set (without an item) gives every run the
weight, cap, join, miter limit and dashes instead of adding an object
stroke; while the Type tool edits text, the Stroke panel styles the
selected characters through text.setRangeStyle `strokeOptions`, and the
panel reads the first run (or the selection's style).

CharStyle gains stroke_cap, stroke_join, stroke_miter_limit and
stroke_dash (defaulted, skipped when default). Character strokes and
object strokes on type now draw through the shared stroke geometry on
the canvas, in PDF and in SVG (live text writes cap/join/miter/dash and
tspans reset what the first run sets; object strokes are drawn on the
glyph outlines), so every stroke option, opacity and blend applies.
Outlines, the Eyedropper and graphic styles keep the options.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:09:55 +07:00
bflatasticandClaude Opus 5.5 3a79e6d922 M3.58: gradient libraries; Swatch Options renames gradient swatches; Alt-drop replaces a swatch
Window > Swatch Libraries > Gradients lists five generated gradient
libraries (Spectrum, Sky, Neutral incl. fades to transparent, Duotone,
Radial Glows); the library panel's menu lists them too.
Swatch Options on a gradient swatch shows its name and the gradient; OK
renames it. Alt-dropping an edited gradient (or a colour) on a swatch of its
kind in the Swatches panel replaces it, via swatch.edit {paint}.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 01:00:00 +07:00
bflatasticandClaude Opus 5.5 786f2c05d3 M3.57: save swatch libraries as .vcswatches, .gpl or CSS and load them back
Save Swatch Library… (Swatches panel menu, libraries menu, Window menu)
opens a dialog: name, format and destination. .vcswatches is lossless JSON
(colour models, global, spot, gradients, groups), .gpl an RGB palette with
groups as comment headers, CSS custom properties with escaped names.
Libraries saved to the user library folder show under User Defined.
Other Library… (and opening a .vcswatches/.gpl file) loads a library or
another document's swatches into the library panel.

Commands: swatch.library.save {path?, format?, names?, name?, user?}
(no path returns {data}), swatch.library.load {path?|data?|dataBase64?,
name?}; UI: window.swatchLibrary.other, ui.saveSwatchLibrary (dialog
saveSwatchLibrary), window.userSwatchLibrary1..10.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:59:59 +07:00
bflatasticandClaude Opus 5.5 e7f6328188 M3.56: swatch library panel with code-generated swatch libraries
Window > Swatch Libraries and the Swatches panel's library button open a
read-only, floating library panel (thumbnail and list views, find field,
libraries menu, previous/next arrows). Clicking a swatch adds it to the
document and applies it as one undo step; Shift/Cmd-clicks select swatches
and groups for Add to Swatches. Default Swatches restores missing defaults.

Libraries are computed in code (OKLCH, rounded to 8-bit sRGB): Web Safe 216,
Grays and Neutrals, Earth Tones, Skin Tone Ramps, Pastels, Brights,
Metallic Gradients, Perceptual Scales, Harmony Sets.

Commands: swatch.library.list, swatch.library.get, swatch.library.add
{library, names?, apply?, focus?}, swatch.resetDefaults {replace?};
UI command window.swatchLibrary {library}.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:59:59 +07:00
bflatasticandClaude Opus 5.5 8a837fd4b9 M3.68: groups, layers and type have their own fills, strokes and effects, with Contents and Characters rows
A group's or layer's own fills and strokes paint every member's geometry, below or above the
members as the new contents slot (Appearance.contents_index) says; its geometry effects reshape
the members as one piece and its raster effects apply to the composite (one combined shadow),
respecting knockout groups. Type's own fills and strokes paint under or over its characters.
Render, SVG/PDF (via bake) and Expand Appearance evaluate them alike.

Commands: appearance.* / effect.* take target: "object"|"contents" and layer ids;
appearance.moveItem moves the Contents slot (from: "contents") or an item across it
(contents); appearance.targetContents selects a group's members. Graphic styles applied to a
group now style the group itself. The Appearance panel shows Contents/Characters rows that
drag, and double-click to target the members or edit the characters' paint.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:59:34 +07:00
BFlat 25c8f32fdd Merge pull request #23 from storytold/pr/ui-dialog-menu-sizing
UI: dialogs and menus size to their content
2026-10-05 00:58:01 +07:00
bflatasticandClaude Opus 5.5 d3df1bcea2 M14.1: dialogs and menus size to their content
The shared dialog frame's button row took all the height left in the window
(a right-to-left layout fills the available height), and every dialog shared
one window id, so a dialog never shrank and inherited the size of the one
before it: the save prompt showed a tall empty band around its buttons. The
row is now as tall as the buttons and each dialog kind has its own window.

Menu items forced themselves to the popup's full available width, so every
menu opened at egui's maximum menu width. The menu layout already justifies
items, so popups are now as wide as their widest label and shortcut (at least
230 pt).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:54:03 +07:00
BFlat e21ff60b6a Merge pull request #22 from storytold/pr/m3-blend-isolation
M3: blend accuracy, non-isolated groups, layer clipping masks (M3.73–M3.75)
2026-10-05 00:34:36 +07:00
bflatasticandClaude Opus 5.5 45ff33e144 M3.75: layer clipping masks from the Layers panel button
layer.clippingMask.toggle {id?} -> {clip}: the top object of the current
layer (or of the one selected group, or of `id`) becomes the clipping path,
unpainted and moved to the bottom so art added later is clipped too; pressing
the button again releases the mask and leaves the path unpainted. One undo
step. The Layers panel button runs it and underlines clipping-path names.

Model: NodeKind::Layer.clip (skipped when false). Clipped layers render, hit
test, bound, and export to SVG and PDF like clip groups (Node::clips /
set_clips); the clip-path helpers are shared with Make/Release Clipping Mask.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:27:31 +07:00
bflatasticandClaude Opus 5.5 54427d7a92 M3.74: blending inside a group reaches the art below unless the group isolates it
On screen, a group (or object) with opacity, a blend mode, an opacity mask or
knockout whose content blends is drawn offscreen over a copy of its backdrop
and mixed back in by its opacity, mask and clip (a group blend mode then
applies to the backdrop-free result, as PDF defines it). Isolate Blending keeps
the content starting from transparency. Clip groups no longer isolate their
children (blending content is clipped per drawing), and non-isolated knockout
groups composite their elements against the art below the group. Groups
without blending inside still render as plain layers.

In PDF, such groups are written as non-isolated transparency groups (a
constant-alpha soft mask instead of the writer's isolated opacity group),
knockout groups included.

doc: Node::blends_through / children_blend.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:27:31 +07:00
bflatasticandClaude Opus 5.5 a1a5c79b03 M3.73: blend modes follow the reference formulas on screen, in SVG and in PDF
color::blend gains blend_rgb (the 16 blend functions, with the 0.3/0.59/0.11
luminance of the non-separable modes), composite (source-over with blending)
and the shared opacity-mask luminance weights. Exported SVG masks take
luminance from sRGB values (color-interpolation="sRGB"), as the canvas does.

Tests: a per-mode table on the canvas (object, fill and group blend modes,
within 1/255; Exclusion 2/255 in the rasteriser's 8-bit pipeline), the same
table through SVG export rendered by resvg, clip and invert masks against the
canvas, and every mode's PDF name read back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:27:31 +07:00
BFlat bcc30ded5a Merge pull request #21 from storytold/pr/m3-color-guide
M3: Color Guide rules and options, Edit Colors dialogs, recolouring meshes, images and patterns (M3.53–M3.55)
2026-10-05 00:21:57 +07:00
bflatasticandClaude Opus 5.5 2edac7e317 M3.55: Edit Colors and Recolor Artwork also recolour gradient meshes, embedded images and pattern fills
One shared colour visitor (`colorcmds::Recolor`, used by Edit Colors, Recolor Artwork, the Blend
commands, the proxy Invert/Complement and Document Color Mode) maps solid colours, gradient stops
and text runs as before, and now also:
- gradient-mesh points (with fills); Blend grades meshes by their average and keeps their shading;
- embedded images' pixels (`includeImages?`, default on): the image becomes a recoloured copy under
  a content key, the original stays for undo and other objects; linked images are left alone;
- the tiles of pattern fills and strokes (`includePatterns?`, default on): one recoloured copy per
  pattern saved as a new pattern swatch ("Dots 2"), the original untouched.
Everything stays one undo step; only nodes that change are copied, in one walk of the tree.

- doc: `ImageBlob::map_rgb` (decode, map each distinct colour once, keep alpha, re-encode PNG),
  `ImageBlob::content_key` (now also used by image import) and `ImageBlob::png`.
- `recolor.colors` lists mesh points and pattern tile colours too.
- patterncmds: `add_pattern` adds a pattern and its swatch (shared by Make Pattern and Save a Copy).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:16:08 +07:00
bflatasticandClaude Opus 5.5 1de1c6446f M3.54: Adjust Color Balance and Saturate open live-preview dialogs, committed as one undo step
- Edit > Edit Colors > Adjust Color Balance... opens Adjust Colors (`ui.colorBalanceDialog`, kind
  `colorBalance`): colour mode Grayscale, RGB, CMYK or Global, a slider and field per channel
  (-100..100 %), Convert, Fill and Stroke, and Preview.
- Saturate... opens Saturate (`ui.saturateDialog`, kind `saturate`): Intensity -100..100 % and
  Preview.
- Both preview through the session interaction (fields changed by `ui.dialog.set` preview on the
  next frame too); OK keeps the result as one undo step, Cancel rolls it back.
- `edit.colors.adjustBalance` takes `mode` (rgb|cmyk|gray|global; default from the channels
  given). Global mode needs tints of global and spot colours and answers with a clear error until
  they exist; the dialog stays open and the colours keep their links.
- Shared dialog helpers: `form::slider`, `form::preview`, `form::commit_preview`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:14:46 +07:00
bflatasticandClaude Opus 5.5 bfebf66404 M3.53: Color Guide has 21 harmony rules, Steps and Variation options, a fixed base colour and Save Colors as Swatches
The harmony rules (Complementary 1/2, Split Complementary, Left/Right Complement, Analogous 1/2,
Monochromatic, Shades, Triad 1-3, Tetrad 1-3, Compound 1/2, High Contrast 1-3, Pentagram) live in
the color crate with our own math, base colour first. The variation grid moves there too
(`harmony::Guide`): a row per harmony colour with the colour in the centre column, shades/cool/muted
to the left and tints/warm/vivid to the right, 1-20 steps and a variation amount.

- `color.harmony {color, rule, steps?, variation?, amount?}` query returns {rule, colors, grid}.
- The panel keeps its base colour until "Set base color" takes the current one; Shift/Cmd-click
  selects several cells and Save Colors as Swatches saves them (`swatch.new {colors}`, one step).
- Color Guide Options dialog (`ui.colorGuideOptions`, kind `colorGuideOptions`) with a preview row;
  the options persist in `UiState::color_guide`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:13:36 +07:00
BFlat 6c25e54914 Merge pull request #20 from storytold/pr/m3-overprint
M3: overprint per fill and stroke, Overprint Black options (M3.49, M3.50)
2026-10-05 00:07:34 +07:00
bflatasticandClaude Opus 5.5 cb881b082f M3.50: Overprint Black dialog with fill/stroke, rich and spot black options
Edit > Edit Colors > Overprint Black... opens a parameter dialog (Remove,
Percentage, Fill, Stroke, Include Blacks with CMY, Include Spot Blacks).
edit.colors.overprintBlack takes fill?, stroke?, includeCmyBlacks? and
includeSpotBlacks?; it covers type characters and gradients whose every stop
is black, and records no undo step when nothing changes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:01:02 +07:00
bflatasticandClaude Opus 5.5 0c82a3f5c5 M3.49: each fill and stroke can overprint
Fill and stroke layers (and type characters' fill and stroke) carry an
overprint flag. object.setOverprint {fill?, stroke?, item?, ids?} sets it
(groups: their contents; type: its characters too; one undo step, none when
nothing changes) and attributes.info reads it back (null where mixed).
Overprint Preview and Separations Preview draw overprinting fills and strokes
with Multiply. The old Document.unknown["overprintBlack"] id list migrates to
the new flags on load; Overprint Black now sets the flags.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 00:01:02 +07:00
BFlat 6808564486 Merge pull request #19 from storytold/m3-paint-stage2
M3 stage 2: swatch and gradient panel depth, stroke export parity, dash alignment, appearance editing, knockout, graphic style links (M3.30–M3.48)
2026-10-04 23:55:10 +07:00
bflatasticandClaude Opus 5.5 edeb1cb548 Roadmap: M3 stage 2 (M3.30–M3.39, M3.42–M3.48)
The M3 row, the shipped list and the colour, stroke and appearance parity rows
cover Swatches panel depth, Gradient panel depth and rebased gradients, stroke
export parity and Outline Stroke fidelity, corner-fitted dashes, appearance
editing depth and effects on every object kind, knockout and page groups, and
graphic style links. Remaining estimates are recalibrated on the measured
throughput of the first two M3 waves.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:54:21 +07:00
bflatastic ca709871f2 Merge main into m3-paint-appearance (PR #15, #16) 2026-10-04 23:51:50 +07:00
bflatastic 3981acdb06 Merge wp/C3: dash alignment and variable-width dashes (M3.38, M3.39)
# Conflicts:
#	crates/pdf/src/lib.rs
#	crates/ui-egui/src/panels/mod.rs
2026-10-04 23:48:47 +07:00
bflatastic 43d7ac420d Merge wp/D2
# Conflicts:
#	crates/pdf/src/export.rs
#	crates/render/src/fx.rs
#	crates/render/src/lib.rs
#	crates/svg/src/export.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs/mod.rs
#	crates/ui-egui/src/panels/appearance.rs
#	docs/control-protocol.md
#	docs/mcp.md
2026-10-04 23:46:38 +07:00
BFlat deaeef047c Merge pull request #16 from storytold/docs/name-reference-app
Docs: name the reference app again in ROADMAP and docs/mcp.md
2026-10-04 23:41:42 +07:00
bflatasticandClaude Opus 5.5 454c5f8620 M3.39: width profiles follow the path through dashes, and their corners take the join
Each dash and dot of a profile stroke is as wide as the profile where it sits
along the whole path (before, dashes dropped the profile). Corners of profile
strokes take the stroke's miter (within the miter limit), round or bevel join on
the outer side and stay clean on the inner side, instead of a miter clamped at
4x; smooth points of curves bend round. Canvas, SVG/PDF outlines and Outline
Stroke share the geometry.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:40:52 +07:00
bflatasticandClaude Opus 5.5 42ad4540b7 M3.38: dashes fitted to corners and path ends, a flag of their own that keeps the offset
With alignDashes every run between corners and path ends holds a whole number of
periods, scaled to fit, with a dash (or dot) centred on each corner and on both
ends of an open path. It lives in the shared dash generator, so the canvas, SVG
and PDF export (as outlines) and Outline Stroke all draw it.

stroke.set: alignDashes and dashOffset edit the current pattern on their own, and
a new dash array keeps the offset and alignment it replaces. The Stroke panel
turns dashes on fitted to corners (or as last turned off), and its Exact/Fit
buttons send only the flag.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:37:58 +07:00
bflatastic 0f169b5c5d Merge wp/B2
# Conflicts:
#	crates/doc/src/lib.rs
#	crates/engine/src/cmd/style.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/swatches.rs
#	docs/control-protocol.md
2026-10-04 23:37:27 +07:00
bflatastic 818980a789 Merge wp/A2
# Conflicts:
#	crates/engine/src/lib.rs
2026-10-04 23:20:10 +07:00
bflatasticandClaude Opus 5.5 cb68229bda Docs: name the reference app again in ROADMAP and docs/mcp.md
The ROADMAP and the MCP docs say Illustrator/Adobe again where they compare
VectorCraft with it (the rewording in #15 and the create_graph row). The
vendor-name gate keeps checking what users and agents read in the app (Rust
string literals, Cargo descriptions, packaging) and the non-Markdown files
under docs/, and no longer scans Markdown pages.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:19:45 +07:00
bflatasticandClaude Opus 5.5 0358c4df95 M3.35: gradient handles snap and honour Constrain Angle; double-clicking a tool button opens its options
Gradient tool handles (a drawn vector's start and end, the end handle and the moved start) snap to anchors, edges
and smart guides, with the guide overlays; Shift constrains to 45° steps from the Constrain Angle preference
(`ToolContext::constrain_angle`, `constrain_angle_from` in geom). Double-clicking a toolbar button runs the new UI
command `tool.options {tool}`: the Gradient tool opens the Gradient panel, the Eyedropper opens Eyedropper Options
(`eyedropper.setOptions {appearance?, transparency?}`, which `appearance.copyFrom` honours and its params override).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:18:34 +07:00
bflatasticandClaude Opus 5.5 0f1565aed2 M3.43: effects render on type, placed images, symbol instances, blends, envelopes, meshes and repeats; a fill or stroke's own raster effects render and export as SVG filters on that element
Geometry effects reach objects that aren't paths through their outlines (vectorcraft_effects::
reshape: glyph outlines, evaluated live art, the symbol's art, an image clipped by its reshaped
frame), applied to the whole art as one piece; the renderer caches that art per object and
paints raster effects around it in the same filter layers paths use (shared Renderer::raster_fx,
also used per fill/stroke). Exports bake the reshaped art; SVG writes per-item filters; PDF
warns about raster effects on strokes as well as fills and objects.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:15:13 +07:00
bflatastic abd77d58b0 Merge wp/F2: knockout groups, knockout shape, page groups (M3.46-M3.48)
# Conflicts:
#	crates/pdf/src/export.rs
#	crates/svg/src/export.rs
2026-10-04 23:15:10 +07:00
bflatasticandClaude Opus 5.5 4e60eabc00 M3.30: swatches dropped back on themselves stay put
A drag released on one of the dragged swatches no longer falls through to
the list's drop zone (which moved them to the end).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:12:24 +07:00
BFlat 8008e16f57 Merge pull request #15 from storytold/m3-paint-stage1
M3 stage 1: swatches, colour picker, gradients, strokes, appearance targeting, masks (M3.7–M3.29)
2026-10-04 23:10:47 +07:00
bflatasticandClaude Opus 5.5 f48ba26380 M3.32: the document colour mode governs new colours
A CMYK document (file.new {colorMode: "cmyk"}, Document::new_with_mode)
starts with CMYK default swatches (default_swatches(model)), and RGB
colours and gradient stops applied to it are stored as CMYK (Gray stays
Gray) unless paint.setFill/setStroke get keepModel; the Color panel keeps
the model picked on its sliders. RGB documents keep colours as given.
Harmonies and Color Guide variations keep the base colour's model, Recolor
Artwork the replaced colour's, and Edit Colors blends between models take
the document's, through a shared Color::in_model / keep_model.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 23:10:15 +07:00
bflatasticandClaude Opus 5.5 039c0cbef7 M3.48: Page Isolated Blending and Page Knockout Group
New Document.page_isolate / page_knockout, set by
transparency.togglePageIsolatedBlending / togglePageKnockoutGroup {value?}
(one undo step each, saved with the document, reported by
transparency.info) and the Transparency panel menu's check items. The page
renders as an isolated group (top-level blend modes no longer blend with
what lies under the page) and/or a knockout group whose elements are the
layers (neutral layers pass their objects through). PDF and SVG wrap the
page content in an isolated group and write page knockout as soft masks.

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