Files

334 lines
7.2 KiB
TypeScript

import type {
Asset,
AssetTake,
AssetBins,
GenerationParams,
SubtitleClip,
SubtitleStyle,
Timeline,
TimelineClip,
Track,
} from '../../types/project-model'
import {
DEFAULT_LAYOUT,
type EditorLayout,
type ToolType,
} from './video-editor-utils'
export interface EditorModel {
assets: Asset[]
bins: AssetBins
timelines: Timeline[]
activeTimelineId: string | null
}
export interface TimelineGapSelection {
trackIndex: number
startTime: number
endTime: number
}
export interface TimelineInOutRange {
inPoint: number | null
outPoint: number | null
}
export interface EditorUndoSnapshot {
assets: Asset[]
bins: AssetBins
timelines: Timeline[]
}
export interface EditorClipboardState {
kind: 'clips' | null
clips: TimelineClip[]
copiedFromTimelineId: string | null
}
export interface EditorHistoryState {
undoStack: EditorUndoSnapshot[]
redoStack: EditorUndoSnapshot[]
}
export interface EditorProjectSyncState {
dirty: boolean
}
export interface EditorSelectionState {
clipIds: Set<string>
subtitleId: string | null
editingSubtitleId: string | null
gap: TimelineGapSelection | null
}
export interface EditorTransportState {
currentTime: number
isPlaying: boolean
shuttleSpeed: number
playingInOut: boolean
timelineInOutMap: Record<string, TimelineInOutRange>
}
export interface EditorToolsState {
zoom: number
snapEnabled: boolean
activeTool: ToolType
lastTrimTool: ToolType
}
export interface EditorUiState {
showImportTimelineModal: boolean
showExportModal: boolean
showSourceMonitor: boolean
showPropertiesPanel: boolean
showEffectsBrowser: boolean
activeFocusArea: 'source' | 'timeline'
sourceSplitPercent: number
hasSourceAsset: boolean
openTimelineIds: Set<string>
renamingTimelineId: string | null
renameValue: string
renameSource: 'tab' | 'panel'
layout: EditorLayout
subtitleTrackStyleIdx: number | null
gapGenerateMode: 'text-to-video' | 'image-to-video' | 'text-to-image' | null
}
export interface EditorRegenerationState {
regeneratingAssetId: string | null
regeneratingClipId: string | null
preError: string | null
}
export interface EditorSessionState {
selection: EditorSelectionState
transport: EditorTransportState
tools: EditorToolsState
ui: EditorUiState
regeneration: EditorRegenerationState
clipboard: EditorClipboardState
}
export interface EditorState {
editorModel: EditorModel
session: EditorSessionState
history: EditorHistoryState
projectSync: EditorProjectSyncState
}
export interface TimelineListItem {
timeline: Timeline
isActive: boolean
isOpen: boolean
isRenaming: boolean
clipCount: number
duration: number
}
export interface ClipDimensions {
width: number
height: number
}
export interface ClipResolutionInfo {
label: string
color: string
height: number
displayName: string
}
export interface OrderedTrackEntry {
track: Track
realIndex: number
displayRow: number
}
export interface TimelineCutPoint {
leftClip: TimelineClip
rightClip: TimelineClip
time: number
trackIndex: number
hasDissolve: boolean
}
export interface ClipMetadata {
liveAsset: Asset | null | undefined
dimensions: ClipDimensions | null
resolution: ClipResolutionInfo | null
generationParams: GenerationParams | undefined
totalTakes: number
currentTakeIdx: number
displayTakeNum: number
filePath: string
isUpscaled: boolean
}
export interface ClipCapabilities {
isVideo: boolean
isImage: boolean
isAudio: boolean
isAdjustment: boolean
isText: boolean
canCreateVideoFromImage: boolean
canCreateVideoFromAudio: boolean
canRegenerate: boolean
canRetake: boolean
canUseIcLora: boolean
}
export interface SelectedClipPropertiesModel {
clip: TimelineClip
metadata: ClipMetadata
capabilities: ClipCapabilities
}
export interface SelectedSubtitleEditorModel {
subtitle: SubtitleClip
track: Track | undefined
effectiveStyle: Partial<SubtitleStyle>
}
export interface SubtitleTrackStyleEditorModel {
trackIndex: number
track: Track
style: SubtitleStyle
}
export interface KeyboardCommandContext {
clips: TimelineClip[]
selectedClipIds: Set<string>
totalDuration: number
currentTime: number
inPoint: number | null
outPoint: number | null
}
export interface MenuState {
selectedClip: TimelineClip | null
selectedClipIds: Set<string>
clips: TimelineClip[]
tracks: Track[]
subtitles: SubtitleClip[]
snapEnabled: boolean
showEffectsBrowser: boolean
showSourceMonitor: boolean
showPropertiesPanel: boolean
hasSourceAsset: boolean
activeTool: ToolType
activeTimeline: Timeline | null
timelines: Timeline[]
}
export interface AssetListFilters {
assetFilter?: Asset['type'] | 'all'
selectedBinId?: string | null
assetViewMode?: 'grid' | 'list'
listSortCol?: 'name' | 'type' | 'duration' | 'resolution' | 'date' | 'color'
listSortDir?: 'asc' | 'desc'
}
export interface AssetTakeView {
asset: Asset | undefined
takes: AssetTake[]
activeTakeIndex: number
}
export const EMPTY_IN_OUT_RANGE: TimelineInOutRange = {
inPoint: null,
outPoint: null,
}
export function createInitialEditorState(
editorModel: EditorModel,
layout: EditorLayout = DEFAULT_LAYOUT,
): EditorState {
return {
editorModel,
session: {
selection: {
clipIds: new Set(),
subtitleId: null,
editingSubtitleId: null,
gap: null,
},
transport: {
currentTime: 0,
isPlaying: false,
shuttleSpeed: 0,
playingInOut: false,
timelineInOutMap: {},
},
tools: {
zoom: 1,
snapEnabled: true,
activeTool: 'select',
lastTrimTool: 'ripple',
},
ui: {
showImportTimelineModal: false,
showExportModal: false,
showSourceMonitor: false,
showPropertiesPanel: false,
showEffectsBrowser: false,
activeFocusArea: 'timeline',
sourceSplitPercent: 50,
hasSourceAsset: false,
openTimelineIds: editorModel.activeTimelineId ? new Set([editorModel.activeTimelineId]) : new Set(),
renamingTimelineId: null,
renameValue: '',
renameSource: 'tab',
layout,
subtitleTrackStyleIdx: null,
gapGenerateMode: null,
},
regeneration: {
regeneratingAssetId: null,
regeneratingClipId: null,
preError: null,
},
clipboard: {
kind: null,
clips: [],
copiedFromTimelineId: null,
},
},
history: {
undoStack: [],
redoStack: [],
},
projectSync: {
dirty: false,
},
}
}
export function getUndoSnapshot(state: EditorState): EditorUndoSnapshot {
return {
assets: state.editorModel.assets,
bins: state.editorModel.bins,
timelines: state.editorModel.timelines,
}
}
export function applyUndoSnapshot(
state: EditorState,
snapshot: EditorUndoSnapshot,
): EditorState {
return {
...state,
editorModel: {
...state.editorModel,
assets: snapshot.assets,
bins: snapshot.bins,
timelines: snapshot.timelines,
},
}
}
export function equalUndoSnapshot(
left: EditorUndoSnapshot,
right: EditorUndoSnapshot,
): boolean {
return left.assets === right.assets && left.bins === right.bins && left.timelines === right.timelines
}