From 3e89d2998588f8e35c524e523ba2ee853f0f6475 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 20 Apr 2026 11:31:17 +0200 Subject: refactor: restructure files to their domains, new preview overlay system, and dep graph --- apps/web/src/core/managers/scenes-manager.ts | 640 +++++++++++++-------------- 1 file changed, 320 insertions(+), 320 deletions(-) (limited to 'apps/web/src/core/managers/scenes-manager.ts') diff --git a/apps/web/src/core/managers/scenes-manager.ts b/apps/web/src/core/managers/scenes-manager.ts index 3508a066..13f251a1 100644 --- a/apps/web/src/core/managers/scenes-manager.ts +++ b/apps/web/src/core/managers/scenes-manager.ts @@ -1,320 +1,320 @@ -import type { EditorCore } from "@/core"; -import type { SceneTracks, TScene } from "@/lib/timeline"; -import { storageService } from "@/services/storage/service"; -import { - getMainScene, - ensureMainScene, - canDeleteScene, - findCurrentScene, -} from "@/lib/scenes"; -import { - getBookmarkAtTime, - getFrameTime, - isBookmarkAtTime, -} from "@/lib/timeline/bookmarks"; -import { - CreateSceneCommand, - DeleteSceneCommand, - MoveBookmarkCommand, - RemoveBookmarkCommand, - RenameSceneCommand, - ToggleBookmarkCommand, - UpdateBookmarkCommand, -} from "@/lib/commands/scene"; - -export class ScenesManager { - private active: TScene | null = null; - private list: TScene[] = []; - private listeners = new Set<() => void>(); - - constructor(private editor: EditorCore) {} - - async createScene({ - name, - isMain = false, - }: { - name: string; - isMain: boolean; - }): Promise { - if (!this.editor.project.getActive()) { - throw new Error("No active project"); - } - - const command = new CreateSceneCommand(name, isMain); - this.editor.command.execute({ command }); - return command.getSceneId(); - } - - async deleteScene({ sceneId }: { sceneId: string }): Promise { - const sceneToDelete = this.list.find((s) => s.id === sceneId); - - if (!sceneToDelete) { - throw new Error("Scene not found"); - } - - const { canDelete, reason } = canDeleteScene({ scene: sceneToDelete }); - if (!canDelete) { - throw new Error(reason); - } - - if (!this.editor.project.getActive()) { - throw new Error("No active project"); - } - - const command = new DeleteSceneCommand(sceneId); - this.editor.command.execute({ command }); - } - - async renameScene({ - sceneId, - name, - }: { - sceneId: string; - name: string; - }): Promise { - if (!this.editor.project.getActive()) { - throw new Error("No active project"); - } - - const command = new RenameSceneCommand(sceneId, name); - this.editor.command.execute({ command }); - } - - async switchToScene({ sceneId }: { sceneId: string }): Promise { - const targetScene = this.list.find((s) => s.id === sceneId); - - if (!targetScene) { - throw new Error("Scene not found"); - } - - const activeProject = this.editor.project.getActive(); - - if (activeProject) { - const updatedProject = { - ...activeProject, - currentSceneId: sceneId, - metadata: { - ...activeProject.metadata, - updatedAt: new Date(), - }, - }; - - this.editor.project.setActiveProject({ project: updatedProject }); - } - - this.active = targetScene; - this.notify(); - } - - async toggleBookmark({ time }: { time: number }): Promise { - const command = new ToggleBookmarkCommand(time); - this.editor.command.execute({ command }); - } - - isBookmarked({ time }: { time: number }): boolean { - const activeScene = this.getActiveScene(); - const activeProject = this.editor.project.getActive(); - - if (!activeScene || !this.active || !activeProject) return false; - - const frameTime = getFrameTime({ - time, - fps: activeProject.settings.fps, - }); - - return isBookmarkAtTime({ bookmarks: activeScene.bookmarks, frameTime }); - } - - async removeBookmark({ time }: { time: number }): Promise { - const command = new RemoveBookmarkCommand(time); - this.editor.command.execute({ command }); - } - - async updateBookmark({ - time, - updates, - }: { - time: number; - updates: Partial<{ note: string; color: string; duration: number }>; - }): Promise { - const command = new UpdateBookmarkCommand(time, updates); - this.editor.command.execute({ command }); - } - - async moveBookmark({ - fromTime, - toTime, - }: { - fromTime: number; - toTime: number; - }): Promise { - 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 { - try { - const result = await storageService.loadProject({ id: projectId }); - if (result?.project.scenes) { - const ensuredScenes = result.project.scenes ?? []; - const currentScene = findCurrentScene({ - scenes: ensuredScenes, - currentSceneId: result.project.currentSceneId, - }); - - this.list = ensuredScenes; - this.active = currentScene; - this.notify(); - } - } catch (error) { - console.error("Failed to load project scenes:", error); - this.list = []; - this.active = null; - this.notify(); - } - } - - initializeScenes({ - scenes, - currentSceneId, - }: { - scenes: TScene[]; - currentSceneId?: string; - }): void { - const ensuredScenes = ensureMainScene({ scenes }); - const currentScene = currentSceneId - ? ensuredScenes.find((s) => s.id === currentSceneId) - : null; - - const fallbackScene = getMainScene({ scenes: ensuredScenes }); - - this.list = ensuredScenes; - this.active = currentScene || fallbackScene; - this.notify(); - - const hasAddedMainScene = ensuredScenes.length > scenes.length; - if (hasAddedMainScene) { - 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 }); - } - } - } - - clearScenes(): void { - this.list = []; - this.active = null; - this.notify(); - } - - getActiveScene(): TScene { - if (!this.active) { - throw new Error("No active scene."); - } - return this.active; - } - - getActiveSceneOrNull(): TScene | null { - return this.active; - } - - getScenes(): TScene[] { - return this.list; - } - - setScenes({ - scenes, - activeSceneId, - }: { - scenes: TScene[]; - activeSceneId?: string; - }): void { - this.list = scenes; - const nextActiveSceneId = activeSceneId ?? this.active?.id ?? null; - this.active = nextActiveSceneId - ? (scenes.find((scene) => scene.id === nextActiveSceneId) ?? null) - : null; - this.notify(); - - const activeProject = this.editor.project.getActive(); - if (activeProject) { - const updatedProject = { - ...activeProject, - scenes, - metadata: { - ...activeProject.metadata, - updatedAt: new Date(), - }, - }; - this.editor.project.setActiveProject({ project: updatedProject }); - } - } - - subscribe(listener: () => void): () => void { - this.listeners.add(listener); - return () => this.listeners.delete(listener); - } - - private notify(): void { - this.listeners.forEach((fn) => { - fn(); - }); - } - - updateSceneTracks({ tracks }: { tracks: SceneTracks }): void { - if (!this.active) return; - - const updatedScene: TScene = { - ...this.active, - tracks, - updatedAt: new Date(), - }; - - this.list = this.list.map((s) => - s.id === this.active?.id ? updatedScene : s, - ); - this.active = updatedScene; - this.notify(); - - const activeProject = this.editor.project.getActive(); - if (activeProject) { - const updatedProject = { - ...activeProject, - scenes: this.list, - metadata: { - ...activeProject.metadata, - updatedAt: new Date(), - }, - }; - this.editor.project.setActiveProject({ project: updatedProject }); - } - } -} +import type { EditorCore } from "@/core"; +import type { SceneTracks, TScene } from "@/timeline"; +import { storageService } from "@/services/storage/service"; +import { + getMainScene, + ensureMainScene, + canDeleteScene, + findCurrentScene, +} from "@/timeline/scenes"; +import { + getBookmarkAtTime, + getFrameTime, + isBookmarkAtTime, +} from "@/timeline/bookmarks/index"; +import { + CreateSceneCommand, + DeleteSceneCommand, + MoveBookmarkCommand, + RemoveBookmarkCommand, + RenameSceneCommand, + ToggleBookmarkCommand, + UpdateBookmarkCommand, +} from "@/commands/scene"; + +export class ScenesManager { + private active: TScene | null = null; + private list: TScene[] = []; + private listeners = new Set<() => void>(); + + constructor(private editor: EditorCore) {} + + async createScene({ + name, + isMain = false, + }: { + name: string; + isMain: boolean; + }): Promise { + if (!this.editor.project.getActive()) { + throw new Error("No active project"); + } + + const command = new CreateSceneCommand(name, isMain); + this.editor.command.execute({ command }); + return command.getSceneId(); + } + + async deleteScene({ sceneId }: { sceneId: string }): Promise { + const sceneToDelete = this.list.find((s) => s.id === sceneId); + + if (!sceneToDelete) { + throw new Error("Scene not found"); + } + + const { canDelete, reason } = canDeleteScene({ scene: sceneToDelete }); + if (!canDelete) { + throw new Error(reason); + } + + if (!this.editor.project.getActive()) { + throw new Error("No active project"); + } + + const command = new DeleteSceneCommand(sceneId); + this.editor.command.execute({ command }); + } + + async renameScene({ + sceneId, + name, + }: { + sceneId: string; + name: string; + }): Promise { + if (!this.editor.project.getActive()) { + throw new Error("No active project"); + } + + const command = new RenameSceneCommand(sceneId, name); + this.editor.command.execute({ command }); + } + + async switchToScene({ sceneId }: { sceneId: string }): Promise { + const targetScene = this.list.find((s) => s.id === sceneId); + + if (!targetScene) { + throw new Error("Scene not found"); + } + + const activeProject = this.editor.project.getActive(); + + if (activeProject) { + const updatedProject = { + ...activeProject, + currentSceneId: sceneId, + metadata: { + ...activeProject.metadata, + updatedAt: new Date(), + }, + }; + + this.editor.project.setActiveProject({ project: updatedProject }); + } + + this.active = targetScene; + this.notify(); + } + + async toggleBookmark({ time }: { time: number }): Promise { + const command = new ToggleBookmarkCommand(time); + this.editor.command.execute({ command }); + } + + isBookmarked({ time }: { time: number }): boolean { + const activeScene = this.getActiveScene(); + const activeProject = this.editor.project.getActive(); + + if (!activeScene || !this.active || !activeProject) return false; + + const frameTime = getFrameTime({ + time, + fps: activeProject.settings.fps, + }); + + return isBookmarkAtTime({ bookmarks: activeScene.bookmarks, frameTime }); + } + + async removeBookmark({ time }: { time: number }): Promise { + const command = new RemoveBookmarkCommand(time); + this.editor.command.execute({ command }); + } + + async updateBookmark({ + time, + updates, + }: { + time: number; + updates: Partial<{ note: string; color: string; duration: number }>; + }): Promise { + const command = new UpdateBookmarkCommand(time, updates); + this.editor.command.execute({ command }); + } + + async moveBookmark({ + fromTime, + toTime, + }: { + fromTime: number; + toTime: number; + }): Promise { + 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 { + try { + const result = await storageService.loadProject({ id: projectId }); + if (result?.project.scenes) { + const ensuredScenes = result.project.scenes ?? []; + const currentScene = findCurrentScene({ + scenes: ensuredScenes, + currentSceneId: result.project.currentSceneId, + }); + + this.list = ensuredScenes; + this.active = currentScene; + this.notify(); + } + } catch (error) { + console.error("Failed to load project scenes:", error); + this.list = []; + this.active = null; + this.notify(); + } + } + + initializeScenes({ + scenes, + currentSceneId, + }: { + scenes: TScene[]; + currentSceneId?: string; + }): void { + const ensuredScenes = ensureMainScene({ scenes }); + const currentScene = currentSceneId + ? ensuredScenes.find((s) => s.id === currentSceneId) + : null; + + const fallbackScene = getMainScene({ scenes: ensuredScenes }); + + this.list = ensuredScenes; + this.active = currentScene || fallbackScene; + this.notify(); + + const hasAddedMainScene = ensuredScenes.length > scenes.length; + if (hasAddedMainScene) { + 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 }); + } + } + } + + clearScenes(): void { + this.list = []; + this.active = null; + this.notify(); + } + + getActiveScene(): TScene { + if (!this.active) { + throw new Error("No active scene."); + } + return this.active; + } + + getActiveSceneOrNull(): TScene | null { + return this.active; + } + + getScenes(): TScene[] { + return this.list; + } + + setScenes({ + scenes, + activeSceneId, + }: { + scenes: TScene[]; + activeSceneId?: string; + }): void { + this.list = scenes; + const nextActiveSceneId = activeSceneId ?? this.active?.id ?? null; + this.active = nextActiveSceneId + ? (scenes.find((scene) => scene.id === nextActiveSceneId) ?? null) + : null; + this.notify(); + + const activeProject = this.editor.project.getActive(); + if (activeProject) { + const updatedProject = { + ...activeProject, + scenes, + metadata: { + ...activeProject.metadata, + updatedAt: new Date(), + }, + }; + this.editor.project.setActiveProject({ project: updatedProject }); + } + } + + subscribe(listener: () => void): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + private notify(): void { + this.listeners.forEach((fn) => { + fn(); + }); + } + + updateSceneTracks({ tracks }: { tracks: SceneTracks }): void { + if (!this.active) return; + + const updatedScene: TScene = { + ...this.active, + tracks, + updatedAt: new Date(), + }; + + this.list = this.list.map((s) => + s.id === this.active?.id ? updatedScene : s, + ); + this.active = updatedScene; + this.notify(); + + const activeProject = this.editor.project.getActive(); + if (activeProject) { + const updatedProject = { + ...activeProject, + scenes: this.list, + metadata: { + ...activeProject.metadata, + updatedAt: new Date(), + }, + }; + this.editor.project.setActiveProject({ project: updatedProject }); + } + } +} -- cgit v1.3.1