The eyedropper (`appearance.copyFrom`), `graphicStyle.new` and `graphicStyle.apply` move placed gradients so they
sit at the same place relative to each target's bounds (type: its layout box in text space) instead of where the
source had them; the paint defaults take the gradient unplaced, so new art fits it. Model: `GradientGeom::rebase`
/ `GradientPaint::rebase` (color), `Appearance::rebase_gradients` (strokes relative to their inflated boxes) and
`GraphicStyle::unit_box`: new styles store placed gradients in unit-box space; styles from older files keep
document coordinates and apply as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>