0.8.7: duplicate track via right-click (layer / clip / script)

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:22:32 +02:00
1 parent d60684edd1
commit 517705bf55
4 files changed
+254 -1

No files matched your search

+94
View File
@@ -1756,6 +1756,100 @@ export const useSceneStore = defineStore('scene', {
;(this.scene.trackOrder ||= []).push(track.id)
return track
},
/**
* Duplicate a top-level track by id. Handles layer / clipTrack /
* scriptTrack — clip instances and script frames belonging to the
* original are cloned too. The duplicate lands directly after the
* original in trackOrder (or in the same group's childIds if the
* original lived inside one). Groups themselves are not supported
* here; returns null for unknown ids, clip-edit / button-edit
* contexts, and groups.
*/
duplicateTrack(id: string): string | null {
if (this.activeClipId || this.activeButtonAssetId) return null
const scene = this.scene
// Grouped tracks live in TrackGroup.childIds and are absent from
// scene.trackOrder entirely (only the group's own id appears at
// top level). Duplicate stays inside whichever container held the
// original.
const placeAfter = (afterId: string, newId: string) => {
const order = (scene.trackOrder ||= [])
const idx = order.indexOf(afterId)
if (idx >= 0) {
order.splice(idx + 1, 0, newId)
return
}
for (const g of scene.groups ?? []) {
const gi = g.childIds.indexOf(afterId)
if (gi >= 0) {
g.childIds.splice(gi + 1, 0, newId)
return
}
}
order.push(newId)
}
const layer = scene.layers.find((l) => l.id === id)
if (layer) {
const cloned = structuredClone(toRaw(layer)) as Layer
cloned.id = uid()
cloned.keyframes = cloned.keyframes.map((k) => ({ ...k, id: uid() }))
if (cloned.positionKeyframes) {
cloned.positionKeyframes = cloned.positionKeyframes.map((k) => ({ ...k, id: uid() }))
}
if (cloned.rotationKeyframes) {
cloned.rotationKeyframes = cloned.rotationKeyframes.map((k) => ({ ...k, id: uid() }))
}
const origIdx = scene.layers.findIndex((l) => l.id === id)
scene.layers.splice(origIdx + 1, 0, cloned)
placeAfter(id, cloned.id)
return cloned.id
}
const clipTrack = scene.clipTracks?.find((t) => t.id === id)
if (clipTrack) {
const newTrack: ClipTrack = {
...(structuredClone(toRaw(clipTrack)) as ClipTrack),
id: uid(),
}
;(scene.clipTracks ||= []).push(newTrack)
const instances = scene.clipInstances?.filter((ci) => ci.clipTrackId === id) ?? []
for (const ci of instances) {
const clonedInst: ClipInstance = {
...(structuredClone(toRaw(ci)) as ClipInstance),
id: uid(),
clipTrackId: newTrack.id,
}
;(scene.clipInstances ||= []).push(clonedInst)
}
placeAfter(id, newTrack.id)
return newTrack.id
}
const scriptTrack = scene.scriptTracks?.find((t) => t.id === id)
if (scriptTrack) {
const newTrack: ScriptTrack = {
...(structuredClone(toRaw(scriptTrack)) as ScriptTrack),
id: uid(),
}
;(scene.scriptTracks ||= []).push(newTrack)
const frames = scene.scriptFrames?.filter((f) => f.trackId === id) ?? []
for (const f of frames) {
const clonedFrame: ScriptFrame = {
...(structuredClone(toRaw(f)) as ScriptFrame),
id: uid(),
trackId: newTrack.id,
}
;(scene.scriptFrames ||= []).push(clonedFrame)
}
if (scene.scriptFrames) scene.scriptFrames.sort((a, b) => a.time - b.time)
placeAfter(id, newTrack.id)
return newTrack.id
}
return null
},
removeClipTrack(id: string) {
if (this.scene.clipTracks) {
this.scene.clipTracks = this.scene.clipTracks.filter((t) => t.id !== id)