0.10.7: fire scripts at play-start + right-click duplicate script frame
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0bbef66192
commit
34d1a89d56
6 files changed
+313
-15
No files matched your search
+49
-2
@@ -94,6 +94,10 @@ interface State {
|
||||
selectedClipInstanceId: string | null
|
||||
playhead: number
|
||||
playing: boolean
|
||||
/** True between `play()` and the next `tick()`. Lets the first tick
|
||||
* after a play-start fire scripts whose time === current playhead
|
||||
* (e.g. a frame-0 script). Cleared by tick / pause / exitClip. */
|
||||
playStartPending: boolean
|
||||
activeClipId: string | null
|
||||
/** When set, the Stage + Timeline are inside a Button-asset edit session. */
|
||||
activeButtonAssetId: string | null
|
||||
@@ -395,6 +399,7 @@ export const useSceneStore = defineStore('scene', {
|
||||
selectedClipInstanceId: null,
|
||||
playhead: 0,
|
||||
playing: false,
|
||||
playStartPending: false,
|
||||
activeClipId: null,
|
||||
activeButtonAssetId: null,
|
||||
activeButtonEditMode: null,
|
||||
@@ -1600,6 +1605,37 @@ export const useSceneStore = defineStore('scene', {
|
||||
frame.time = Math.max(0, Math.min(this.scene.duration, time))
|
||||
this.scene.scriptFrames?.sort((a, b) => a.time - b.time)
|
||||
},
|
||||
duplicateScriptFrame(frameId: string): string | null {
|
||||
const frames = this.scene.scriptFrames ?? []
|
||||
const source = frames.find((f) => f.id === frameId)
|
||||
if (!source) return null
|
||||
const sameTrack = frames.filter((f) => f.trackId === source.trackId)
|
||||
const occupied = (t: number) =>
|
||||
sameTrack.some((f) => Math.abs(f.time - t) < KEYFRAME_EPSILON)
|
||||
const fps = this.scene.fps > 0 ? this.scene.fps : 60
|
||||
const offset = Math.max(3 / fps, 0.1)
|
||||
const duration = this.scene.duration
|
||||
// Walk forward looking for the first free slot; if the track is
|
||||
// packed to the end, fall back to stepping backward before giving up.
|
||||
let target = source.time + offset
|
||||
while (occupied(target) && target < duration) target += offset
|
||||
if (target > duration || occupied(target)) {
|
||||
target = source.time - offset
|
||||
while (occupied(target) && target > 0) target -= offset
|
||||
if (target < 0) target = 0
|
||||
if (occupied(target)) return null
|
||||
}
|
||||
const copy: ScriptFrame = {
|
||||
id: uid(),
|
||||
trackId: source.trackId,
|
||||
time: target,
|
||||
actions: source.actions.map((a) => ({ ...a })),
|
||||
}
|
||||
if (source.label) copy.label = source.label
|
||||
;(this.scene.scriptFrames ||= []).push(copy)
|
||||
this.scene.scriptFrames.sort((a, b) => a.time - b.time)
|
||||
return copy.id
|
||||
},
|
||||
|
||||
// -------- Audio tracks, clips & assets -----------------------------
|
||||
|
||||
@@ -2578,9 +2614,11 @@ export const useSceneStore = defineStore('scene', {
|
||||
play() {
|
||||
if (this.playing) return
|
||||
this.playing = true
|
||||
this.playStartPending = true
|
||||
},
|
||||
pause() {
|
||||
this.playing = false
|
||||
this.playStartPending = false
|
||||
},
|
||||
togglePlay() {
|
||||
if (this.playing) this.pause()
|
||||
@@ -2607,8 +2645,17 @@ export const useSceneStore = defineStore('scene', {
|
||||
const landed = this.playhead
|
||||
// Forward-only firing: script frames whose time sits in (prev, landed].
|
||||
// Scrubs/setPlayhead/loop-wraps never reach this path, so the rule
|
||||
// is naturally preserved.
|
||||
const frames = scriptFramesInRange(this.scene.scriptFrames, prev, landed)
|
||||
// is naturally preserved. The first tick after play() widens the
|
||||
// left bound to [prev, landed] so scripts at the starting frame
|
||||
// (e.g. the very first time slot) also fire.
|
||||
const includeStart = this.playStartPending
|
||||
this.playStartPending = false
|
||||
const frames = scriptFramesInRange(
|
||||
this.scene.scriptFrames,
|
||||
prev,
|
||||
landed,
|
||||
{ includeStart },
|
||||
)
|
||||
if (frames.length) {
|
||||
const rt = this.actionRuntime()
|
||||
for (const f of frames) {
|
||||
|
||||
Reference in new issue
Block a user