Commit Graph
57 Commits
Author SHA1 Message Date
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-10-05 01:21:38 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-10-05 01:09:55 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-10-05 00:59:34 +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 <noreply@anthropic.com>
2026-10-05 00:27:31 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-10-05 00:13:36 +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 <noreply@anthropic.com>
2026-10-05 00:01:02 +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
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 <noreply@anthropic.com>
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 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 <noreply@anthropic.com>
2026-10-04 23:18:34 +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
bflatastic b85ef3de5a Merge wp/C2: stroke export parity and Outline Stroke fidelity (M3.36, M3.37) 2026-10-04 23:08:27 +07:00
bflatasticandClaude Opus 5.5 6e0af835c6 M3.46: three-state knockout groups (on, neutral, off) render, export and edit
Knockout Group is now Knockout {Neutral, Off, On} (old files' true loads as
On, false as Neutral). In a knockout group each child erases what the
children below it drew where it paints, then adds itself, so it composites
against the group's backdrop; neutral plain groups pass their children
through to the enclosing knockout group. transparency.set / object.setProps
take knockout "on"|"off"|"neutral"|bool; transparency.info reports the
state. PDF and SVG write knockout groups as nested soft masks with the same
look (PDF warns); the Transparency panel has a tri-state checkbox.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:04:20 +07:00
bflatasticandClaude Opus 5.5 10ea4d5daf M3.42: Appearance editing depth — drag effect rows to reorder them or move them into/out of fills and strokes (Alt copies), click an effect name to edit it in its prefilled dialog, choosing an applied effect asks to edit it or add another, Show All Hidden Attributes, multi-select and Alt-drag duplicate of fills/strokes, drag the thumbnail onto art to copy the appearance, stroke rows note brush/Dashed/profile, Shift-click a chip for the mixer, Properties fx opens the effect menu
Engine: effect.move {from, to, fromItem?, toItem?, copy?}; appearance.showAllHidden;
appearance.duplicateItem takes to? and indices?; appearance.removeItem takes indices?;
Appearance::has_hidden/show_all. UI: effect.dialog takes index? (edit in place via
effect.setParams) and opens the effectExists question for an effect already in the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:01:34 +07:00
bflatasticandClaude Opus 5.5 eb83efdb19 M3.44: graphic styles keep opacity and blend, capture groups and type, add on Alt-click
Styles now store the object's opacity, blend mode, isolate and knockout and apply them.
New Graphic Style from a group without its own paint captures its topmost object's
appearance, and from type its characters' fill and stroke. Names are made unique.
graphicStyle.apply takes ids and add (Alt-click in the panel adds the style on top of the
existing appearance). graphicStyle.list reports every style. Applying links objects to a
stable style id (Node.graphic_style), which the management commands build on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:01:10 +07:00
bflatasticandClaude Opus 5.5 059f623410 M3.33: Gradient panel: proxy, swatch menu, diamond location, stop popover, Alt-drag copies and swaps, swatch drops, stop eyedropper
The Gradient panel gains the Fill/Stroke proxy, a thumbnail menu of the document's gradient swatches with Save to
Swatches, Angle and Aspect Ratio fields with preset menus (the shared spin field), Show/Hide Options, and a stop
colour row whose eyedropper samples the next colour clicked on art into the selected stop (Eyedropper tool option
`stop`, `paint.sampleColor {stop}`). On the slider a selected midpoint diamond shows and sets its midpoint in
Location, double-clicking a stop opens the shared stop popover, Alt-drag copies a stop and Alt-dropping it on
another swaps their colours (`swap_stop_colors`), and a dropped colour swatch adds or recolours a stop. Clicking a
stop no longer falls through to the add-stop strip. Swatch tiles and the gradient thumbnail drag onto art, which
paints the object under the pointer through `paint.setFill`/`paint.setStroke {ids}`. The panel, the popover and
the Color panel share one stop-writing helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:00:38 +07:00
bflatasticandClaude Opus 5.5 fe258915e3 M3.30: Swatches panel multi-selection, find field and drag and drop
Clicking a colour group's folder selects the whole group. Show Find Field
filters the panel by name. Tiles and list rows drag to reorder swatches,
move them into or out of colour groups or reorder groups (new command
swatch.move, one undo step); swatches and Fill/Stroke proxies dropped on
art paint the object under the pointer (paint.setFill/setStroke with its
id), and a proxy dropped on the panel becomes a swatch. List rows show
kind and colour-mode icons and take double-clicks; spot colours get a dot
in the grid. The search field widget is shared with the Layers panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 22:54:56 +07:00
bflatasticandClaude Opus 5.5 18d870c7df M3.37: Outline Stroke and the live Outline Stroke effect outline strokes as the canvas paints them
Outline Stroke (and the stroke part of Expand) now builds each stroke from the
shared canvas geometry: dash shapes, arrowheads, width profiles and inside/outside
alignment, united into one clean path (pathops::stroke_region). Every stroke item
is outlined in paint order (runs of fills stay on copies of the path), stroke
opacity, blend mode and effects move to the new fill, and brushed strokes become
their brush art. The live Effect > Path > Outline Stroke outlines the stroke it
sits on (on a fill or the object: the top stroke) with that stroke's cap, join,
dashes, alignment, profile, arrowheads and weight: GeomContext now carries the
stroke and the fill rule, built per item by the renderer, export baking and
Expand Appearance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 22:53:58 +07:00
bflatastic 6bd27a05da Merge wp/I1
# Conflicts:
#	crates/engine/src/cmd/gradient.rs
#	crates/engine/src/cmd/paint.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/dialogs/mod.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/color.rs
#	crates/ui-egui/src/panels/mod.rs
#	crates/ui-egui/src/toolbar.rs
#	docs/control-protocol.md
2026-10-04 22:14:37 +07:00
bflatastic 96f1b6852a Merge wp/F1
# Conflicts:
#	crates/doc/src/node.rs
#	crates/engine/src/cmd/appearance.rs
#	crates/engine/src/cmd/paint.rs
#	crates/ui-egui/src/chrome.rs
#	crates/ui-egui/src/panels/transparency.rs
2026-10-04 22:07:49 +07:00
bflatastic 721bb483c3 Merge wp/D1
# Conflicts:
#	crates/engine/src/cmd/appearance.rs
#	crates/engine/src/cmd/gradient.rs
#	crates/engine/src/cmd/paint.rs
#	crates/engine/src/cmd/stroke.rs
#	crates/engine/src/lib.rs
#	crates/engine/src/tooling.rs
#	crates/render/src/lib.rs
#	crates/tools/src/lib.rs
#	crates/tools/src/xform/gradient.rs
#	crates/ui-egui/src/panels/stroke.rs
2026-10-04 22:01:05 +07:00
bflatastic 41f2af932c Merge wp/C1
# Conflicts:
#	crates/doc/src/appearance.rs
#	crates/effects/src/stroke/width.rs
#	crates/engine/src/lib.rs
#	crates/pdf/src/export.rs
#	crates/render/src/lib.rs
2026-10-04 21:47:58 +07:00
bflatastic efc0ac31d9 Merge wp/B1
# Conflicts:
#	crates/engine/src/cmd/swatch.rs
#	crates/ui-egui/src/dialogs/mod.rs
#	crates/ui-egui/src/panels/mod.rs
#	docs/control-protocol.md
2026-10-04 21:47:07 +07:00
bflatasticandClaude Opus 5.5 8a871d98a9 M3.29: proxy paint commands — paint.invert / paint.complement recolour the active proxy of each selected object keeping its colour model (the defaults when nothing is selected), paint.lastColor (,) and paint.lastGradient (.) re-apply the last solid colour or gradient (the toolbar chips draw and run them), swap and default work on type, and recent colours live in the Session, fed by every paint command (MCP, eyedropper, live previews on commit) and listed by paint.recent
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:01:58 +07:00
bflatasticandClaude Opus 5.5 d3dc4a2af3 M3.17: the stop popover survives its opening double-click, and the selected stop belongs to its gradient
The double-click that opens the popover lands outside it in the same frame, so the popover
closed as soon as it opened; only a later click elsewhere closes it now. The selected stop
remembers the document, first selected object and proxy it was picked on (Session::selected_stop),
so selecting other art or toggling Fill/Stroke no longer lets Delete or the arrows edit a stop
on a different gradient.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:52:28 +07:00
bflatasticandClaude Opus 5.5 3786cfaf36 M3.17: interactive gradient annotator — drag the start handle or the bar to move the gradient, the end handle to change length and angle, just past the end to rotate (Shift snaps to 45°); a bar click adds a stop, stops drag along the bar, off it to delete (never below two), Alt-drag copies, diamonds move midpoints; a click inside selected art applies the gradient from that point; double-clicking a stop opens a popover (Color/Swatches, Opacity, Location); the selected stop lives in the session (gradient.selectStop), shared by the tool, the Gradient and Color panels and agents, and takes Delete/Backspace and ←/→ ahead of their shortcuts (Tool::claims_key, also headless); stop chips draw as Overlay::Swatch with generated add/remove-stop cursors; the stop math moves into the color crate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:39:36 +07:00
bflatasticandClaude Opus 5.5 4f685be57a M3.26: clipping sets with any clip shape
Clip groups clip by compound paths (with their holes), even-odd paths, text
outlines and groups (the union of their members) on screen, in raster
export, in SVG and in PDF alike, through one shared region:
Node::clip_outline (the model walk) and effects::clip_outline (text
outlined, several shapes united), cached per clipping path in the
renderer. Nothing to clip by hides the clipped art everywhere, and clicks
hit a clip group only inside its clipping region. Make Clipping Mask takes
a compound path or text object on top (it loses its paint) and Release
gives it back unchanged in kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:23:57 +07:00
bflatasticandClaude Opus 5.5 09d7aaa7f4 M3.18: shared stroke-geometry module for arrowheads, width profiles and the profile preset catalogue
Arrowheads, width-profile outlines and dashed line outlines now come from
vectorcraft_effects::stroke (stroke_pieces, line_outline, width_outline), used by
the canvas and PDF export alike; the renderer caches profile outlines too. Width
profiles are a doc-level catalogue (WidthProfile::PRESETS, preset, preset_id,
id_of) shared by stroke.set and the Stroke panel; stroke.set rejects unknown
profile ids and its params doc lists the arrowhead names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:16:17 +07:00
bflatasticandClaude Opus 5.5 b117566fc2 M3.25: opacity-mask editing and transparency command reach
While a mask is edited, the Transparency panel and its commands act on the
masked object (Release leaves editing first). Mask commands and
transparency.set take ids and work without a selection; Make Opacity Mask
with one object adds an empty mask and enters editing. Opacity is 0..100 in
every command. transparency.info reports the panel's values with null for
mixed ones, shown blank. Saves and exports never include the editing layer
(Document::without_edit_modes; mask_edit is not serialized).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:10:21 +07:00
bflatasticandClaude Opus 5.5 018bed29db M3.21: the Appearance panel's active item drives paint, stroke, gradient and transparency edits
Clicking a fill or stroke row runs appearance.setActiveItem; the toolbar,
Control bar and Properties proxies and the Color, Swatches, Gradient,
Stroke and Transparency panels show and edit that item, and the gradient
tool's annotator follows it. paint.setFill/setStroke, stroke.set/
setAdvanced, paint.editGradient/setGradientGeom and transparency.set take
item (omitted: the active item, null: the top one). Item edits and the
appearance.* commands address the selected objects' own stacks, so a
group's own fills no longer leak into its contents. Any selection change
clears the active item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:28:05 +07:00
bflatasticandClaude Opus 5.5 61ddc1ce34 M3.14: lossless, validated gradient params — paint.setFill/setStroke, swatch.new and appearance.setItem keep kind (freeform too), midpoints, stop opacity, aspect, vector and swatch link; a shared parse_stops rejects fewer than two or malformed stops, clamps offsets and normalises 0..100 opacity; an aspect without a vector places the gradient on each object's (stroke-inflated) bounds; panels write gradients through one params writer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:22:49 +07:00
bflatasticandClaude Opus 5.5 00a0874d4d M3.9: swatches in colour groups are first-class — Document Color Mode converts them, they can be spot colours with their own plate and PDF Separation, and Document Info counts them
Document::swatches_iter/_mut, swatch_mut, swatch_group_of, remove_swatch and a copy-on-write
walk over linked solid paints (map_solid_paints) cover every colour group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:15:10 +07:00
bflatasticandClaude Opus 5.5 b7969a6a5d M3.8: split the paint/panel command modules and the dialogs into per-area files
No behaviour change: every command keeps its id, label, menu, shortcut and
params doc.

- Engine: swatch.* -> cmd/swatch.rs, graphicStyle.* -> cmd/style.rs,
  appearance.* (incl. copyFrom) -> cmd/appearance.rs, stroke.set/setAdvanced
  -> cmd/stroke.rs, paint.editGradient/setGradientGeom -> cmd/gradient.rs.
  Shared helpers in cmd/mod.rs: leaf_targets/paint_targets (replacing three
  copies) and unique_name (swatch and graphic style naming).
- UI: dialogs.rs -> dialogs/ with one file per dialog and a DialogSpec
  registry (one DialogKind row per dialog); shared field widgets in
  dialogs/form.rs. Headless frame tests draw and confirm every dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:22:23 +07:00
Brandon ThomasandClaude Opus 5.5 dcc5a006a2 M3.6: opacity-mask editing mode — click the mask thumbnail to edit the mask art on a temporary isolated layer with any tool; the mask updates live after every edit; the object thumbnail (or transparency.stopEditingOpacityMask) leaves the mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:40:18 -04:00
Brandon ThomasandClaude Opus 5.5 bfa46e6cb4 M9.4: raster effects in PDF export — shadows/outer glows as images under the untouched vector object, inner glow/feather/blur as images, at the document's resolution; Effect → Document Raster Effects Settings… (document.rasterEffectsSettings: screen/medium/high/ppi); engine::export_pdf used by app, CLI and MCP
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:58:10 -04:00
Brandon ThomasandClaude Opus 5.5 13d0aef57f M1.5: commands that default to the current layer validate the remembered id (reused after undo it could name a compound path: Send to Current Layer moved art into it, Delete Layer could delete it)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 00:16:23 -04:00
Brandon ThomasandClaude Opus 5.5 4745f39c6c M7.4: Object → Text Wrap (Make, Release, Text Wrap Options: offset, invert): area type flows around wrap objects above it in the same layer, re-resolved after every edit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:48:49 -04:00
Brandon ThomasandClaude Opus 5.5 4878f1c5dd Rename DrawCraft → VectorCraft: crates (vectorcraft-*), apps, binaries, types, env vars, docs, examples (.vectorcraft); old .drawcraft files and preferences still load. Fix: text bounds exact after opening a file; selection no longer marks a document modified (dirty = content differs from the saved document). Screenshots and renders regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:30:09 -04:00
Brandon ThomasandClaude Opus 5.5 d9df5b0135 M7.3/M7.7: threaded text (Type → Threaded Text: Create/Release Selection/Remove Threading; story re-flows after any edit touching its frames, deleted frames' text flows on), Fit Headline; roadmap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:00:17 -04:00
Brandon ThomasandClaude Opus 5.5 4d1eb16424 M1: fix paste into a compound path when undo made a new object reuse the active layer's id (active layer / isolation must still be a layer / group); regression test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:32:58 -04:00
Brandon ThomasandClaude Opus 5.5 c06fad7b2b M5: drawcraft-cli bench; canvas keyed by per-document uid (no stale frame from another tab); brushed strokes keep brush art under effects
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:38:38 -04:00
Brandon ThomasandClaude Opus 5.5 acb2566d89 Distortion tools (Width, Liquify, Puppet Warp, Perspective Grid), Patterns + Repeat, colour management (CMS, proofing, separations), Preferences/Shortcuts/Workspaces; original brand mark; fix filter panic in thumbnails; README demo scenes
- New original DrawCraft mark (gradient + pen nib) replaces the two-letter badge
- render: thumbnails and pattern tiles use a single-threaded context (vello's default is
  multithreaded and panics on filters); per-item raster effects detected
- Type tool state no longer leaks across documents
- ui.set {status} for clean screenshots
- README: new hero, feature shots, Made in DrawCraft gallery; examples/neon-drive, ribbons,
  feature-sheet; ASSETS.md updated; heading renamed to Crafting Apps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:44:39 -04:00
Brandon Thomas c9f38685b4 wip 2026-09-30 11:37:31 -04:00