0.8.6: clip instances get stage transforms (x/y/scale/rotation)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-22 06:13:09 +02:00
1 parent 53c0410824
commit d60684edd1
7 files changed
+512 -41

No files matched your search

+57
View File
@@ -73,6 +73,11 @@ interface State {
* anchor. Used by group-selected / delete-selected and any
* operation that needs the whole set. */
selectedLayerIds: string[]
/** Selected clip-instance id — drives the canvas transformer when a
* packed clip is clicked on the stage. Mutually exclusive with
* `selectedLayerId`: selecting a clip clears the layer selection
* and vice versa. */
selectedClipInstanceId: string | null
playhead: number
playing: boolean
activeClipId: string | null
@@ -268,6 +273,7 @@ export const useSceneStore = defineStore('scene', {
scene: createBlankScene(),
selectedLayerId: null,
selectedLayerIds: [],
selectedClipInstanceId: null,
playhead: 0,
playing: false,
activeClipId: null,
@@ -454,6 +460,14 @@ export const useSceneStore = defineStore('scene', {
selectLayer(id: string | null) {
this.selectedLayerId = id
this.selectedLayerIds = id ? [id] : []
if (id) this.selectedClipInstanceId = null
},
selectClipInstance(id: string | null) {
this.selectedClipInstanceId = id
if (id) {
this.selectedLayerId = null
this.selectedLayerIds = []
}
},
/** Shift-click behaviour: add the layer to the selection if it's
* not already there; otherwise remove it. The anchor moves to
@@ -1808,6 +1822,49 @@ export const useSceneStore = defineStore('scene', {
removeClipInstance(id: string) {
if (!this.scene.clipInstances) return
this.scene.clipInstances = this.scene.clipInstances.filter((ci) => ci.id !== id)
if (this.selectedClipInstanceId === id) this.selectedClipInstanceId = null
},
/** Update the per-instance x / y / scaleX / scaleY. Missing fields
* on the patch are left alone; clearing a field (setting it to
* the default 0 / 1) prunes it from the stored object so the
* JSON stays clean. The clip's own keyframes are untouched. */
setClipInstanceTransform(
id: string,
patch: Partial<{
x: number
y: number
scaleX: number
scaleY: number
rotation: number
}>,
) {
const inst = this.scene.clipInstances?.find((ci) => ci.id === id)
if (!inst) return
if (patch.x !== undefined) {
if (patch.x === 0) delete inst.x
else inst.x = patch.x
}
if (patch.y !== undefined) {
if (patch.y === 0) delete inst.y
else inst.y = patch.y
}
if (patch.scaleX !== undefined) {
if (patch.scaleX === 1) delete inst.scaleX
else inst.scaleX = patch.scaleX
}
if (patch.scaleY !== undefined) {
if (patch.scaleY === 1) delete inst.scaleY
else inst.scaleY = patch.scaleY
}
if (patch.rotation !== undefined) {
// Normalise to (-180, 180] so the stored value stays human-
// readable and idempotent across multiple transform sessions.
let r = patch.rotation % 360
if (r > 180) r -= 360
if (r <= -180) r += 360
if (r === 0) delete inst.rotation
else inst.rotation = r
}
},
addTrackGroup(name?: string): TrackGroup {
const group: TrackGroup = {