Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
5 changes: 3 additions & 2 deletions src/core/ui/selection-handles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?? {};
Expand All @@ -660,15 +661,15 @@ export class SelectionHandles implements CanvasOverlayRegistration {
this.finalDragState = {
transform: {
...currentTransform,
rotate: { angle: snappedRotation }
rotate: { angle: normalizedRotation }
}
};

// Document-first: Update document, then resolve
this.edit.updateClipInDocument(this.selectedClipId, {
transform: {
...currentTransform,
rotate: { angle: snappedRotation }
rotate: { angle: normalizedRotation }
}
});
this.edit.resolveClip(this.selectedClipId);
Expand Down
44 changes: 44 additions & 0 deletions tests/snap-system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
Loading