diff --git a/.releaserc.json b/.releaserc.json index fb91eaa..1ad671e 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -1,16 +1,21 @@ { - "branches": ["main"], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - "@semantic-release/changelog", - ["@semantic-release/npm", { - "pkgRoot": "." - }], - ["@semantic-release/git", { - "assets": ["package.json", "CHANGELOG.md"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" - }], - "@semantic-release/github" - ] + "branches": ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/npm", + { + "pkgRoot": "." + } + ], + [ + "@semantic-release/git", + { + "assets": ["package.json"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] } diff --git a/src/core/ui/selection-handles.ts b/src/core/ui/selection-handles.ts index 55c6825..aee6597 100644 --- a/src/core/ui/selection-handles.ts +++ b/src/core/ui/selection-handles.ts @@ -652,6 +652,7 @@ export class SelectionHandles implements CanvasOverlayRegistration { const rawRotation = this.initialRotation + deltaAngle; const { angle: snappedRotation } = snapRotation(rawRotation); + const normalizedRotation = snappedRotation % 360; // Get current transform to preserve other properties (scale, etc.) const currentTransform = this.selectedPlayer.clipConfiguration.transform ?? {}; @@ -660,7 +661,7 @@ export class SelectionHandles implements CanvasOverlayRegistration { this.finalDragState = { transform: { ...currentTransform, - rotate: { angle: snappedRotation } + rotate: { angle: normalizedRotation } } }; @@ -668,7 +669,7 @@ export class SelectionHandles implements CanvasOverlayRegistration { this.edit.updateClipInDocument(this.selectedClipId, { transform: { ...currentTransform, - rotate: { angle: snappedRotation } + rotate: { angle: normalizedRotation } } }); this.edit.resolveClip(this.selectedClipId); diff --git a/tests/snap-system.test.ts b/tests/snap-system.test.ts index 3d60014..83d2809 100644 --- a/tests/snap-system.test.ts +++ b/tests/snap-system.test.ts @@ -653,6 +653,50 @@ describe("SnapSystem", () => { }); }); + // ─── Rotation Normalization (schema boundary) ─────────────────────────── + + describe("rotation normalization for document storage", () => { + it("normalizes multi-rotation snap result to within ±360", () => { + // snapRotation(723) returns 720, but schema requires [-360, 360] + const { angle } = snapRotation(723); + expect(angle).toBe(720); + expect(angle % 360).toBe(0); + }); + + it("normalizes large negative rotations", () => { + // snapRotation(-723) returns -720 + const { angle } = snapRotation(-723); + expect(angle).toBe(-720); + expect(angle % 360).toBe(-0); + }); + + it("normalizes 4+ full rotations", () => { + const { angle } = snapRotation(1443); + expect(angle).toBe(1440); + expect(angle % 360).toBe(0); + }); + + it("preserves non-snapped values after normalization", () => { + // 380 doesn't snap (20° is not near any snap angle) + const { angle, snapped } = snapRotation(380); + expect(snapped).toBe(false); + expect(angle % 360).toBe(20); + }); + + it("normalizes 360 boundary snap to 0", () => { + // 358 snaps to 360, normalized to 0 + const { angle } = snapRotation(358); + expect(angle).toBe(360); + expect(angle % 360).toBe(0); + }); + + it("keeps values already within range unchanged", () => { + const { angle } = snapRotation(92); + expect(angle).toBe(90); + expect(angle % 360).toBe(90); + }); + }); + // ─── Constants Tests ────────────────────────────────────────────────────── describe("constants", () => {