* feat(svgcanvas): Trigger 'changed' event on text attribute modifications
This update adds a call to the 'changed' event for various text attribute methods, ensuring that changes to text elements are properly tracked and reflected in the SVG canvas. The methods updated include setBold, setItalic, setTextAnchor, setLetterSpacing, setWordSpacing, setTextLength, setLengthAdjust, setFontFamily, setFontColor, and setFontSize.
* refactor(svgcanvas): Simplify text attribute methods and optimize 'changed' event emission
This update introduces helper functions to streamline the handling of selected text elements and their attribute changes. The methods setBold, setItalic, setTextAnchor, setLetterSpacing, setWordSpacing, and setTextLength now utilize these helpers to filter and notify only modified text elements, improving performance and clarity in event emissions.
* Fix rotation recalculation corrupting compound transforms
Two bugs in the rotation center recalculation triggered by attribute
changes on rotated elements:
1. The recalculation fires for ALL attribute changes (stroke-width,
fill, opacity, etc.) even though only geometric attributes (x, y,
width, height, d, points, etc.) can change the bbox center. This
causes unnecessary and incorrect rotation center updates.
2. In undo.js, the center is computed through ALL remaining transforms
via transformListToTransform(tlist).matrix, but for compound
transform lists like [translate(tx,ty), rotate(angle)], the
pre-rotation translate leaks into the center calculation, producing
rotate(angle, bcx+tx, bcy+ty) instead of the correct
rotate(angle, bcx, bcy).
3. In history.js (undo/redo), the code replaces the ENTIRE transform
attribute with just rotate(...), completely destroying any other
transforms (translate, scale, etc.) in the list.
Fixes:
- Guard recalculation with a BBOX_AFFECTING_ATTRS set so non-geometric
attribute changes skip the rotation block entirely.
- In undo.js, use transformListToTransform(tlist, n, max) to compute
the center through only post-rotation transforms.
- In history.js, replace string-based transform replacement with
transform list API that finds and updates only the rotation entry,
preserving all other transforms. Extract shared logic into
relocateRotationCenter() helper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add tests for rotation recalculation with compound transforms
Tests demonstrate and verify fixes for three bugs:
1. Non-geometric attributes (stroke-width, fill, opacity) no longer
trigger rotation center recalculation — the transform list is
left untouched.
2. Geometric attribute changes on elements with compound transforms
(e.g. translate + rotate) compute the rotation center using only
post-rotation transforms, preventing the translate from leaking
into the center calculation.
3. ChangeElementCommand.apply/unapply preserve compound transform
structure instead of replacing the entire transform attribute
with just rotate(...).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add visual demo and screenshot for rotation recalculation bug
Includes an HTML demo showing both bugs side-by-side:
- Bug 1: Non-geometric attribute changes (stroke-width) on compound
transforms corrupt character positions (e.g., word art on curves)
- Bug 2: Rotation center computed through ALL transforms instead of
only post-rotation transforms, causing translate to leak into center
Each panel shows Original / Buggy / Fixed comparisons with the actual
SVG transform math applied. A Playwright script generates screenshots.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add sample SVG demonstrating compound transform corruption bug
A pinwheel of 6 colored rectangles using translate(200,200) rotate(N).
Loading this into SVG-Edit and changing stroke-width on the group
triggers the rotation recalculation bug on unfixed builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix standard linting: object properties on separate lines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Centralize BBOX_AFFECTING_ATTRS and fix bbox-before-remove safety
Address two code review comments:
1. Export BBOX_AFFECTING_ATTRS from history.js and import it in undo.js
so the attribute list is defined in one place.
2. Compute getBBox() before calling tlist.removeItem() so that a falsy
bbox causes an early return/break without having already destroyed
the rotation transform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When editing text inside a group element, the text cursor was appearing
in the wrong position because only the text element's own transform was
being considered, ignoring transforms from parent groups.
This change:
- Adds a new #getAccumulatedMatrix() method that traverses up the DOM tree
from the text element to the SVG content element, collecting and multiplying
all transform matrices along the way
- Updates the init() method to use this accumulated matrix instead of just
the text element's transform
- Updates test mock to include getSvgContent() method
Fixes the issue where editing text inside a transformed group would show
the cursor at the wrong position.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>