Files
AI-Video/frontend/src/stores/editor-store.ts
empty 79a6c2ef3e feat: Add inpainting (局部重绘) feature for timeline editor
- Add canvas-based mask drawing tools (brush, eraser, rect, lasso)
- Add undo/redo history support for mask editing
- Integrate inpainting UI into preview player
- Add backend API endpoint for inpainting requests
- Add MediaService.inpaint method with ComfyUI workflow support
- Add Flux inpainting workflows for selfhost and RunningHub

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 23:44:51 +08:00

162 lines
4.0 KiB
TypeScript

import { create } from 'zustand'
// Inpainting types
export type InpaintingTool = 'brush' | 'eraser' | 'rect' | 'lasso'
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
// Inpainting state
isInpaintingMode: boolean
inpaintingTool: InpaintingTool
brushSize: number
currentMask: string | null
inpaintPrompt: string
// 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
// Inpainting actions
setInpaintingMode: (mode: boolean) => void
setInpaintingTool: (tool: InpaintingTool) => void
setBrushSize: (size: number) => void
setCurrentMask: (mask: string | null) => void
setInpaintPrompt: (prompt: string) => void
resetInpainting: () => void
}
export const useEditorStore = create<EditorState>((set, get) => ({
storyboard: null,
selectedFrameId: null,
isPlaying: false,
currentTime: 0,
// Inpainting initial state
isInpaintingMode: false,
inpaintingTool: 'brush' as InpaintingTool,
brushSize: 20,
currentMask: null,
inpaintPrompt: '',
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 })
}
},
// Inpainting actions
setInpaintingMode: (mode) => set({ isInpaintingMode: mode }),
setInpaintingTool: (tool) => set({ inpaintingTool: tool }),
setBrushSize: (size) => set({ brushSize: size }),
setCurrentMask: (mask) => set({ currentMask: mask }),
setInpaintPrompt: (prompt) => set({ inpaintPrompt: prompt }),
resetInpainting: () => set({
isInpaintingMode: false,
inpaintingTool: 'brush',
brushSize: 20,
currentMask: null,
inpaintPrompt: '',
}),
}))