diff options
Diffstat (limited to 'apps/web/src/core')
| -rw-r--r-- | apps/web/src/core/managers/audio-manager.ts | 12 | ||||
| -rw-r--r-- | apps/web/src/core/managers/commands.ts | 5 | ||||
| -rw-r--r-- | apps/web/src/core/managers/project-manager.ts | 5 | ||||
| -rw-r--r-- | apps/web/src/core/managers/scenes-manager.ts | 47 | ||||
| -rw-r--r-- | apps/web/src/core/managers/timeline-manager.ts | 53 |
5 files changed, 115 insertions, 7 deletions
diff --git a/apps/web/src/core/managers/audio-manager.ts b/apps/web/src/core/managers/audio-manager.ts index 3a83be48..457e49d6 100644 --- a/apps/web/src/core/managers/audio-manager.ts +++ b/apps/web/src/core/managers/audio-manager.ts @@ -125,7 +125,8 @@ export class AudioManager { private getPlaybackTime(): number { if (!this.audioContext) return this.playbackStartTime; - const elapsed = this.audioContext.currentTime - this.playbackStartContextTime; + const elapsed = + this.audioContext.currentTime - this.playbackStartContextTime; return this.playbackStartTime + elapsed; } @@ -176,7 +177,11 @@ export class AudioManager { if (clip.startTime > windowEnd) continue; this.activeClipIds.add(clip.id); - void this.runClipIterator({ clip, startTime: currentTime, sessionId: this.playbackSessionId }); + void this.runClipIterator({ + clip, + startTime: currentTime, + sessionId: this.playbackSessionId, + }); } } @@ -239,8 +244,7 @@ export class AudioManager { node.connect(this.masterGain ?? audioContext.destination); const startTimestamp = - this.playbackStartContextTime + - (timelineTime - this.playbackStartTime); + this.playbackStartContextTime + (timelineTime - this.playbackStartTime); if (startTimestamp >= audioContext.currentTime) { node.start(startTimestamp); diff --git a/apps/web/src/core/managers/commands.ts b/apps/web/src/core/managers/commands.ts index d996a30e..46ee643a 100644 --- a/apps/web/src/core/managers/commands.ts +++ b/apps/web/src/core/managers/commands.ts @@ -11,6 +11,11 @@ export class CommandManager { return command; } + push({ command }: { command: Command }): void { + this.history.push(command); + this.redoStack = []; + } + undo(): void { if (this.history.length === 0) return; const command = this.history.pop(); diff --git a/apps/web/src/core/managers/project-manager.ts b/apps/web/src/core/managers/project-manager.ts index c7497ef0..2608b8c0 100644 --- a/apps/web/src/core/managers/project-manager.ts +++ b/apps/web/src/core/managers/project-manager.ts @@ -27,6 +27,8 @@ import { type MigrationProgress, } from "@/services/storage/migrations"; import { DEFAULT_TIMELINE_VIEW_STATE } from "@/constants/timeline-constants"; +import { loadFonts } from "@/lib/fonts/google-fonts"; +import { collectFontFamilies } from "@/lib/timeline/element-utils"; export interface MigrationState { isMigrating: boolean; @@ -146,6 +148,9 @@ export class ProjectManager { await this.editor.media.loadProjectMedia({ projectId: id }); + const allTracks = (project.scenes ?? []).flatMap((scene) => scene.tracks); + await loadFonts({ families: collectFontFamilies({ tracks: allTracks }) }); + if (!project.metadata.thumbnail) { const didUpdateThumbnail = await this.updateThumbnailFromTimeline(); if (didUpdateThumbnail) { diff --git a/apps/web/src/core/managers/scenes-manager.ts b/apps/web/src/core/managers/scenes-manager.ts index 8c761f9b..c844f448 100644 --- a/apps/web/src/core/managers/scenes-manager.ts +++ b/apps/web/src/core/managers/scenes-manager.ts @@ -7,14 +7,20 @@ import { canDeleteScene, findCurrentScene, } from "@/lib/scenes"; -import { getFrameTime, isBookmarkAtTime } from "@/lib/timeline/bookmarks"; +import { + getBookmarkAtTime, + getFrameTime, + isBookmarkAtTime, +} from "@/lib/timeline/bookmarks"; import { ensureMainTrack } from "@/lib/timeline/track-utils"; import { CreateSceneCommand, DeleteSceneCommand, + MoveBookmarkCommand, RemoveBookmarkCommand, RenameSceneCommand, ToggleBookmarkCommand, + UpdateBookmarkCommand, } from "@/lib/commands/scene"; export class ScenesManager { @@ -125,6 +131,45 @@ export class ScenesManager { this.editor.command.execute({ command }); } + async updateBookmark({ + time, + updates, + }: { + time: number; + updates: Partial<{ note: string; color: string; duration: number }>; + }): Promise<void> { + const command = new UpdateBookmarkCommand(time, updates); + this.editor.command.execute({ command }); + } + + async moveBookmark({ + fromTime, + toTime, + }: { + fromTime: number; + toTime: number; + }): Promise<void> { + const command = new MoveBookmarkCommand(fromTime, toTime); + this.editor.command.execute({ command }); + } + + getBookmarkAtTime({ time }: { time: number }) { + const activeScene = this.active; + const activeProject = this.editor.project.getActive(); + + if (!activeScene || !activeProject) return null; + + const frameTime = getFrameTime({ + time, + fps: activeProject.settings.fps, + }); + + return getBookmarkAtTime({ + bookmarks: activeScene.bookmarks, + frameTime, + }); + } + async loadProjectScenes({ projectId }: { projectId: string }): Promise<void> { try { const result = await storageService.loadProject({ id: projectId }); diff --git a/apps/web/src/core/managers/timeline-manager.ts b/apps/web/src/core/managers/timeline-manager.ts index ad9f7792..c8ac5a79 100644 --- a/apps/web/src/core/managers/timeline-manager.ts +++ b/apps/web/src/core/managers/timeline-manager.ts @@ -23,12 +23,14 @@ import { PasteCommand, UpdateElementStartTimeCommand, MoveElementCommand, + TracksSnapshotCommand, } from "@/lib/commands/timeline"; -import { BatchCommand } from "@/lib/commands"; +import { BatchCommand, PreviewTracker } from "@/lib/commands"; import type { InsertElementParams } from "@/lib/commands/timeline/element/insert-element"; export class TimelineManager { private listeners = new Set<() => void>(); + private previewTracker = new PreviewTracker<TimelineTrack[]>(); constructor(private editor: EditorCore) {} @@ -214,7 +216,8 @@ export class TimelineManager { ({ trackId, elementId, updates: elementUpdates }) => new UpdateElementCommand(trackId, elementId, elementUpdates), ); - const command = commands.length === 1 ? commands[0] : new BatchCommand(commands); + const command = + commands.length === 1 ? commands[0] : new BatchCommand(commands); if (pushHistory) { this.editor.command.execute({ command }); } else { @@ -222,6 +225,52 @@ export class TimelineManager { } } + isPreviewActive(): boolean { + return this.previewTracker.isActive(); + } + + previewElements({ + updates, + }: { + updates: Array<{ + trackId: string; + elementId: string; + updates: Partial<Record<string, unknown>>; + }>; + }): void { + const tracks = this.getTracks(); + this.previewTracker.begin({ state: tracks }); + + let updatedTracks = tracks; + for (const { trackId, elementId, updates: elementUpdates } of updates) { + updatedTracks = updatedTracks.map((track) => { + if (track.id !== trackId) return track; + const newElements = track.elements.map((element) => + element.id === elementId + ? { ...element, ...elementUpdates } + : element, + ); + return { ...track, elements: newElements } as TimelineTrack; + }); + } + this.updateTracks(updatedTracks); + } + + commitPreview(): void { + const snapshot = this.previewTracker.end(); + if (snapshot === null) return; + const currentTracks = this.getTracks(); + const command = new TracksSnapshotCommand(snapshot, currentTracks); + this.editor.command.push({ command }); + } + + discardPreview(): void { + const snapshot = this.previewTracker.end(); + if (snapshot !== null) { + this.updateTracks(snapshot); + } + } + duplicateElements({ elements, }: { |
