feat: Add comprehensive timeline editor with frame editing and regeneration capabilities
This commit is contained in:
122
frontend/src/stores/editor-store.ts
Normal file
122
frontend/src/stores/editor-store.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export interface StoryboardFrame {
|
||||
id: string
|
||||
index: number
|
||||
narration: string
|
||||
imagePrompt: string
|
||||
imagePath?: string
|
||||
audioPath?: string
|
||||
duration: number
|
||||
order: number
|
||||
}
|
||||
|
||||
export interface Storyboard {
|
||||
id: string
|
||||
title: string
|
||||
frames: StoryboardFrame[]
|
||||
totalDuration: number
|
||||
}
|
||||
|
||||
interface EditorState {
|
||||
storyboard: Storyboard | null
|
||||
selectedFrameId: string | null
|
||||
isPlaying: boolean
|
||||
currentTime: number
|
||||
|
||||
// Actions
|
||||
setStoryboard: (storyboard: Storyboard) => void
|
||||
selectFrame: (frameId: string | null) => void
|
||||
setSelectedFrameId: (frameId: string | null) => void // Alias for selectFrame
|
||||
reorderFrames: (fromIndex: number, toIndex: number) => void
|
||||
updateFrameDuration: (frameId: string, duration: number) => void
|
||||
updateFrame: (frameId: string, updates: Partial<StoryboardFrame>) => void
|
||||
setPlaying: (playing: boolean) => void
|
||||
setCurrentTime: (time: number | ((prev: number) => number)) => void
|
||||
}
|
||||
|
||||
export const useEditorStore = create<EditorState>((set, get) => ({
|
||||
storyboard: null,
|
||||
selectedFrameId: null,
|
||||
isPlaying: false,
|
||||
currentTime: 0,
|
||||
|
||||
setStoryboard: (storyboard) => set({ storyboard }),
|
||||
|
||||
selectFrame: (frameId) => set({ selectedFrameId: frameId }),
|
||||
|
||||
setSelectedFrameId: (frameId) => set({ selectedFrameId: frameId }),
|
||||
|
||||
reorderFrames: (fromIndex, toIndex) => {
|
||||
const { storyboard } = get()
|
||||
if (!storyboard) return
|
||||
|
||||
const frames = [...storyboard.frames]
|
||||
const [removed] = frames.splice(fromIndex, 1)
|
||||
frames.splice(toIndex, 0, removed)
|
||||
|
||||
// Update order values
|
||||
const reorderedFrames = frames.map((frame, idx) => ({
|
||||
...frame,
|
||||
order: idx,
|
||||
}))
|
||||
|
||||
set({
|
||||
storyboard: {
|
||||
...storyboard,
|
||||
frames: reorderedFrames,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
updateFrameDuration: (frameId, duration) => {
|
||||
const { storyboard } = get()
|
||||
if (!storyboard) return
|
||||
|
||||
const frames = storyboard.frames.map((frame) =>
|
||||
frame.id === frameId ? { ...frame, duration } : frame
|
||||
)
|
||||
|
||||
const totalDuration = frames.reduce((sum, f) => sum + f.duration, 0)
|
||||
|
||||
set({
|
||||
storyboard: {
|
||||
...storyboard,
|
||||
frames,
|
||||
totalDuration,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
updateFrame: (frameId, updates) => {
|
||||
const { storyboard } = get()
|
||||
if (!storyboard) return
|
||||
|
||||
const frames = storyboard.frames.map((frame) =>
|
||||
frame.id === frameId ? { ...frame, ...updates } : frame
|
||||
)
|
||||
|
||||
const totalDuration = frames.reduce((sum, f) => sum + f.duration, 0)
|
||||
|
||||
set({
|
||||
storyboard: {
|
||||
...storyboard,
|
||||
frames,
|
||||
totalDuration,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
setPlaying: (playing) => set({ isPlaying: playing }),
|
||||
|
||||
setCurrentTime: (time) => {
|
||||
if (typeof time === 'function') {
|
||||
const { currentTime } = get()
|
||||
set({ currentTime: time(currentTime) })
|
||||
} else {
|
||||
set({ currentTime: time })
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user