diff options
Diffstat (limited to 'apps/web/src/core/managers')
| -rw-r--r-- | apps/web/src/core/managers/audio-manager.ts | 2 | ||||
| -rw-r--r-- | apps/web/src/core/managers/commands.ts | 17 | ||||
| -rw-r--r-- | apps/web/src/core/managers/project-manager.ts | 11 | ||||
| -rw-r--r-- | apps/web/src/core/managers/renderer-manager.ts | 2 | ||||
| -rw-r--r-- | apps/web/src/core/managers/scenes-manager.ts | 67 | ||||
| -rw-r--r-- | apps/web/src/core/managers/timeline-manager.ts | 106 |
6 files changed, 112 insertions, 93 deletions
diff --git a/apps/web/src/core/managers/audio-manager.ts b/apps/web/src/core/managers/audio-manager.ts index aaeb7ade..076b998d 100644 --- a/apps/web/src/core/managers/audio-manager.ts +++ b/apps/web/src/core/managers/audio-manager.ts @@ -163,7 +163,7 @@ export class AudioManager { this.playbackSessionId++; this.playbackLatencyCompensationSeconds = 0; - const tracks = this.editor.timeline.getTracks(); + const tracks = this.editor.scenes.getActiveScene().tracks; const mediaAssets = this.editor.media.getAssets(); const duration = this.editor.timeline.getTotalDuration(); diff --git a/apps/web/src/core/managers/commands.ts b/apps/web/src/core/managers/commands.ts index a3665852..2f652a85 100644 --- a/apps/web/src/core/managers/commands.ts +++ b/apps/web/src/core/managers/commands.ts @@ -1,7 +1,7 @@ import type { EditorCore } from "@/core"; import type { Command, CommandResult } from "@/lib/commands"; import { applyRippleAdjustments, computeRippleAdjustments } from "@/lib/ripple"; -import type { TimelineTrack, ElementRef } from "@/lib/timeline/types"; +import type { ElementRef, SceneTracks } from "@/lib/timeline/types"; interface CommandHistoryEntry { command: Command; @@ -18,7 +18,9 @@ export class CommandManager { constructor(private editor: EditorCore) {} execute({ command }: { command: Command }): Command { - const beforeTracks = this.isRippleEnabled ? this.editor.timeline.getTracks() : null; + const beforeTracks = this.isRippleEnabled + ? this.editor.scenes.getActiveSceneOrNull()?.tracks ?? null + : null; const previousSelection = this.getSelectionSnapshot(); const result = command.execute(); this.applyRippleIfEnabled({ beforeTracks }); @@ -71,7 +73,9 @@ export class CommandManager { return; } - const beforeTracks = this.isRippleEnabled ? this.editor.timeline.getTracks() : null; + const beforeTracks = this.isRippleEnabled + ? this.editor.scenes.getActiveSceneOrNull()?.tracks ?? null + : null; const previousSelection = this.getSelectionSnapshot(); const result = entry.command.redo(); this.applyRippleIfEnabled({ beforeTracks }); @@ -123,13 +127,16 @@ export class CommandManager { private applyRippleIfEnabled({ beforeTracks, }: { - beforeTracks: TimelineTrack[] | null; + beforeTracks: SceneTracks | null; }): void { if (!this.isRippleEnabled || !beforeTracks) { return; } - const afterTracks = this.editor.timeline.getTracks(); + const afterTracks = this.editor.scenes.getActiveSceneOrNull()?.tracks; + if (!afterTracks) { + return; + } const adjustments = computeRippleAdjustments({ beforeTracks, afterTracks, diff --git a/apps/web/src/core/managers/project-manager.ts b/apps/web/src/core/managers/project-manager.ts index 513eab94..6324b988 100644 --- a/apps/web/src/core/managers/project-manager.ts +++ b/apps/web/src/core/managers/project-manager.ts @@ -156,9 +156,14 @@ export class ProjectManager { await this.editor.media.loadProjectMedia({ projectId: id }); - const allTracks = (project.scenes ?? []).flatMap((scene) => scene.tracks); await loadFonts({ - families: getElementFontFamilies({ tracks: allTracks }), + families: [ + ...new Set( + (project.scenes ?? []).flatMap((scene) => + getElementFontFamilies({ tracks: scene.tracks }), + ), + ), + ], }); if (!project.metadata.thumbnail) { @@ -643,7 +648,7 @@ export class ProjectManager { private async updateThumbnailFromTimeline(): Promise<boolean> { if (!this.active) return false; - const tracks = this.editor.timeline.getTracks(); + const tracks = this.editor.scenes.getActiveScene().tracks; const mediaAssets = this.editor.media.getAssets(); const duration = this.editor.timeline.getTotalDuration(); const { canvasSize, background } = this.active.settings; diff --git a/apps/web/src/core/managers/renderer-manager.ts b/apps/web/src/core/managers/renderer-manager.ts index e0d5b895..eec4bf37 100644 --- a/apps/web/src/core/managers/renderer-manager.ts +++ b/apps/web/src/core/managers/renderer-manager.ts @@ -139,7 +139,7 @@ export class RendererManager { const { format, quality, fps, includeAudio } = options; try { - const tracks = this.editor.timeline.getTracks(); + const tracks = this.editor.scenes.getActiveScene().tracks; const mediaAssets = this.editor.media.getAssets(); const activeProject = this.editor.project.getActive(); diff --git a/apps/web/src/core/managers/scenes-manager.ts b/apps/web/src/core/managers/scenes-manager.ts index 43938f96..3508a066 100644 --- a/apps/web/src/core/managers/scenes-manager.ts +++ b/apps/web/src/core/managers/scenes-manager.ts @@ -1,5 +1,5 @@ import type { EditorCore } from "@/core";
-import type { TimelineTrack, TScene } from "@/lib/timeline";
+import type { SceneTracks, TScene } from "@/lib/timeline";
import { storageService } from "@/services/storage/service";
import {
getMainScene,
@@ -12,7 +12,6 @@ import { getFrameTime,
isBookmarkAtTime,
} from "@/lib/timeline/bookmarks";
-import { ensureMainTrack } from "@/lib/timeline/placement";
import {
CreateSceneCommand,
DeleteSceneCommand,
@@ -174,10 +173,7 @@ export class ScenesManager { try {
const result = await storageService.loadProject({ id: projectId });
if (result?.project.scenes) {
- const { scenes: ensuredScenes, hasAddedMainTrack } =
- this.ensureScenesHaveMainTrack({
- scenes: result.project.scenes ?? [],
- });
+ const ensuredScenes = result.project.scenes ?? [];
const currentScene = findCurrentScene({
scenes: ensuredScenes,
currentSceneId: result.project.currentSceneId,
@@ -186,22 +182,6 @@ export class ScenesManager { this.list = ensuredScenes;
this.active = currentScene;
this.notify();
-
- if (hasAddedMainTrack) {
- const activeProject = this.editor.project.getActive();
- if (activeProject) {
- const updatedProject = {
- ...activeProject,
- scenes: ensuredScenes,
- metadata: {
- ...activeProject.metadata,
- updatedAt: new Date(),
- },
- };
- this.editor.project.setActiveProject({ project: updatedProject });
- this.editor.save.markDirty({ force: true });
- }
- }
}
} catch (error) {
console.error("Failed to load project scenes:", error);
@@ -219,26 +199,24 @@ export class ScenesManager { currentSceneId?: string;
}): void {
const ensuredScenes = ensureMainScene({ scenes });
- const { scenes: scenesWithMainTracks, hasAddedMainTrack } =
- this.ensureScenesHaveMainTrack({ scenes: ensuredScenes });
const currentScene = currentSceneId
- ? scenesWithMainTracks.find((s) => s.id === currentSceneId)
+ ? ensuredScenes.find((s) => s.id === currentSceneId)
: null;
- const fallbackScene = getMainScene({ scenes: scenesWithMainTracks });
+ const fallbackScene = getMainScene({ scenes: ensuredScenes });
- this.list = scenesWithMainTracks;
+ this.list = ensuredScenes;
this.active = currentScene || fallbackScene;
this.notify();
const hasAddedMainScene = ensuredScenes.length > scenes.length;
- if (hasAddedMainScene || hasAddedMainTrack) {
+ if (hasAddedMainScene) {
const activeProject = this.editor.project.getActive();
if (activeProject) {
const updatedProject = {
...activeProject,
- scenes: scenesWithMainTracks,
+ scenes: ensuredScenes,
metadata: {
...activeProject.metadata,
updatedAt: new Date(),
@@ -264,6 +242,10 @@ export class ScenesManager { return this.active;
}
+ getActiveSceneOrNull(): TScene | null {
+ return this.active;
+ }
+
getScenes(): TScene[] {
return this.list;
}
@@ -307,7 +289,7 @@ export class ScenesManager { });
}
- updateSceneTracks({ tracks }: { tracks: TimelineTrack[] }): void {
+ updateSceneTracks({ tracks }: { tracks: SceneTracks }): void {
if (!this.active) return;
const updatedScene: TScene = {
@@ -335,29 +317,4 @@ export class ScenesManager { this.editor.project.setActiveProject({ project: updatedProject });
}
}
-
- private ensureScenesHaveMainTrack({ scenes }: { scenes: TScene[] }): {
- scenes: TScene[];
- hasAddedMainTrack: boolean;
- } {
- let hasAddedMainTrack = false;
- const ensuredScenes: TScene[] = [];
-
- for (const scene of scenes) {
- const existingTracks = scene.tracks ?? [];
- const updatedTracks = ensureMainTrack({ tracks: existingTracks });
- if (updatedTracks !== existingTracks) {
- hasAddedMainTrack = true;
- ensuredScenes.push({
- ...scene,
- tracks: updatedTracks,
- updatedAt: new Date(),
- });
- } else {
- ensuredScenes.push(scene);
- }
- }
-
- return { scenes: ensuredScenes, hasAddedMainTrack };
- }
}
diff --git a/apps/web/src/core/managers/timeline-manager.ts b/apps/web/src/core/managers/timeline-manager.ts index a5cb428e..74870a95 100644 --- a/apps/web/src/core/managers/timeline-manager.ts +++ b/apps/web/src/core/managers/timeline-manager.ts @@ -1,6 +1,7 @@ import type { EditorCore } from "@/core"; import type { ParamValues } from "@/lib/params"; import type { + SceneTracks, TrackType, TimelineTrack, TimelineElement, @@ -9,6 +10,10 @@ import type { } from "@/lib/timeline"; import { calculateTotalDuration } from "@/lib/timeline"; import { + findTrackInSceneTracks, + updateElementInSceneTracks, +} from "@/lib/timeline/track-element-update"; +import { canElementBeHidden, canElementHaveAudio, } from "@/lib/timeline/element-utils"; @@ -53,7 +58,7 @@ import type { InsertElementParams } from "@/lib/commands/timeline/element/insert export class TimelineManager { private listeners = new Set<() => void>(); private previewOverlay = new Map<string, Partial<TimelineElement>>(); - private previewTracks: TimelineTrack[] | null = null; + private previewTracks: SceneTracks | null = null; constructor(private editor: EditorCore) {} @@ -193,7 +198,12 @@ export class TimelineManager { } getTotalDuration(): number { - return calculateTotalDuration({ tracks: this.getTracks() }); + const activeScene = this.editor.scenes.getActiveSceneOrNull(); + if (!activeScene) { + return 0; + } + + return calculateTotalDuration({ tracks: activeScene.tracks }); } getLastFrameTime(): number { @@ -204,7 +214,12 @@ export class TimelineManager { } getTrackById({ trackId }: { trackId: string }): TimelineTrack | null { - return this.getTracks().find((track) => track.id === trackId) ?? null; + const activeScene = this.editor.scenes.getActiveSceneOrNull(); + if (!activeScene) { + return null; + } + + return findTrackInSceneTracks({ tracks: activeScene.tracks, trackId }); } getElementsWithTracks({ @@ -629,14 +644,20 @@ export class TimelineManager { } as Partial<TimelineElement>; this.previewOverlay.set(elementId, mergedOverlay); } - const committedTracks = this.editor.scenes.getActiveScene()?.tracks ?? []; + const committedTracks = this.editor.scenes.getActiveSceneOrNull()?.tracks; + if (!committedTracks) { + return; + } this.previewTracks = this.applyPreviewOverlay(committedTracks); this.notify(); } commitPreview(): void { if (this.previewOverlay.size === 0) return; - const committedTracks = this.editor.scenes.getActiveScene()?.tracks ?? []; + const committedTracks = this.editor.scenes.getActiveSceneOrNull()?.tracks; + if (!committedTracks) { + return; + } const afterTracks = this.previewTracks ?? this.applyPreviewOverlay(committedTracks); const command = new TracksSnapshotCommand(committedTracks, afterTracks); @@ -653,19 +674,32 @@ export class TimelineManager { this.notify(); } - private applyPreviewOverlay(tracks: TimelineTrack[]): TimelineTrack[] { + private applyPreviewOverlay(tracks: SceneTracks): SceneTracks { if (this.previewOverlay.size === 0) return tracks; - return tracks.map((track) => { - const hasOverlay = track.elements.some((el) => - this.previewOverlay.has(el.id), + + const applyTrackOverlay = <TTrack extends TimelineTrack>(track: TTrack): TTrack => { + const hasOverlay = track.elements.some((element) => + this.previewOverlay.has(element.id), ); - if (!hasOverlay) return track; - const newElements = track.elements.map((el) => { - const overlay = this.previewOverlay.get(el.id); - return overlay ? ({ ...el, ...overlay } as TimelineElement) : el; + if (!hasOverlay) { + return track; + } + + const nextElements = track.elements.map((element) => { + const overlay = this.previewOverlay.get(element.id); + return overlay + ? ({ ...element, ...overlay } as TimelineElement) + : element; }); - return { ...track, elements: newElements } as TimelineTrack; - }); + + return { ...track, elements: nextElements } as TTrack; + }; + + return { + overlay: tracks.overlay.map((track) => applyTrackOverlay(track)), + main: applyTrackOverlay(tracks.main), + audio: tracks.audio.map((track) => applyTrackOverlay(track)), + }; } duplicateElements({ @@ -734,13 +768,8 @@ export class TimelineManager { this.updateElements({ updates: nextUpdates }); } - getTracks(): TimelineTrack[] { - return this.editor.scenes.getActiveScene()?.tracks ?? []; - } - - getRenderTracks(): TimelineTrack[] { - if (this.previewTracks !== null) return this.previewTracks; - return this.getTracks(); + getPreviewTracks(): SceneTracks | null { + return this.previewTracks ?? this.editor.scenes.getActiveSceneOrNull()?.tracks ?? null; } subscribe(listener: () => void): () => void { @@ -771,14 +800,35 @@ export class TimelineManager { }: { elementId: string; }): string | null { - return ( - this.getTracks().find((track) => - track.elements.some((element) => element.id === elementId), - )?.id ?? null - ); + const activeScene = this.editor.scenes.getActiveSceneOrNull(); + if (!activeScene) { + return null; + } + + if ( + activeScene.tracks.main.elements.some( + (element) => element.id === elementId, + ) + ) { + return activeScene.tracks.main.id; + } + + for (const track of activeScene.tracks.overlay) { + if (track.elements.some((element) => element.id === elementId)) { + return track.id; + } + } + + for (const track of activeScene.tracks.audio) { + if (track.elements.some((element) => element.id === elementId)) { + return track.id; + } + } + + return null; } - updateTracks(newTracks: TimelineTrack[]): void { + updateTracks(newTracks: SceneTracks): void { this.previewOverlay.clear(); this.previewTracks = null; this.editor.scenes.updateSceneTracks({ tracks: newTracks }); |
