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 --- .../src/components/editor/panels/preview/index.tsx | 294 --------------------- 1 file changed, 294 deletions(-) delete mode 100644 apps/web/src/components/editor/panels/preview/index.tsx (limited to 'apps/web/src/components/editor/panels/preview/index.tsx') diff --git a/apps/web/src/components/editor/panels/preview/index.tsx b/apps/web/src/components/editor/panels/preview/index.tsx deleted file mode 100644 index 369d854c..00000000 --- a/apps/web/src/components/editor/panels/preview/index.tsx +++ /dev/null @@ -1,294 +0,0 @@ -"use client"; - -import { useCallback, useEffect, useMemo, useRef } from "react"; -import useDeepCompareEffect from "use-deep-compare-effect"; -import { useEditor } from "@/hooks/use-editor"; -import { useRafLoop } from "@/hooks/use-raf-loop"; -import { useContainerSize } from "@/hooks/use-container-size"; -import { useFullscreen } from "@/hooks/use-fullscreen"; -import { CanvasRenderer } from "@/services/renderer/canvas-renderer"; -import { TICKS_PER_SECOND } from "@/lib/wasm"; -import type { RootNode } from "@/services/renderer/nodes/root-node"; -import { buildScene } from "@/services/renderer/scene-builder"; -import { PreviewInteractionOverlay } from "./preview-interaction-overlay"; -import { BookmarkNoteOverlay } from "./bookmark-note-overlay"; -import { GuideOverlay } from "./guide-overlay"; -import { ContextMenu, ContextMenuTrigger } from "@/components/ui/context-menu"; -import { usePreviewStore } from "@/stores/preview-store"; -import { PreviewContextMenu } from "./context-menu"; -import { PreviewToolbar } from "./toolbar"; -import { - PreviewViewportProvider, - usePreviewViewportState, -} from "./preview-viewport"; - -function usePreviewSize() { - const canvasSize = useEditor( - (e) => e.project.getActive()?.settings.canvasSize, - ); - - return { - width: canvasSize?.width, - height: canvasSize?.height, - }; -} - -function normalizeWheelDelta({ - delta, - deltaMode, - pageSize, -}: { - delta: number; - deltaMode: number; - pageSize: number; -}): number { - if (deltaMode === WheelEvent.DOM_DELTA_LINE) { - return delta * 16; - } - - if (deltaMode === WheelEvent.DOM_DELTA_PAGE) { - return delta * pageSize; - } - - return delta; -} - -export function PreviewPanel() { - const containerRef = useRef(null); - const { toggleFullscreen } = useFullscreen({ containerRef }); - - return ( -
- - -
- ); -} - -function RenderTreeController() { - const editor = useEditor(); - const tracks = useEditor( - (e) => e.timeline.getPreviewTracks() ?? e.scenes.getActiveScene().tracks, - ); - const mediaAssets = useEditor((e) => e.media.getAssets()); - const activeProject = useEditor((e) => e.project.getActive()); - - const { width, height } = usePreviewSize(); - - useDeepCompareEffect(() => { - if (!activeProject) return; - - const duration = editor.timeline.getTotalDuration(); - const renderTree = buildScene({ - tracks, - mediaAssets, - duration, - canvasSize: { width, height }, - background: activeProject.settings.background, - isPreview: true, - }); - - editor.renderer.setRenderTree({ renderTree }); - }, [tracks, mediaAssets, activeProject?.settings.background, width, height]); - - return null; -} - -function PreviewCanvas({ - containerRef, - onToggleFullscreen, -}: { - containerRef: React.RefObject; - onToggleFullscreen: () => void; -}) { - const canvasRef = useRef(null); - const viewportRef = useRef(null); - const lastFrameRef = useRef(-1); - const lastSceneRef = useRef(null); - const renderingRef = useRef(false); - const { width: nativeWidth, height: nativeHeight } = usePreviewSize(); - const viewportSize = useContainerSize({ containerRef: viewportRef }); - const editor = useEditor(); - const activeProject = useEditor((e) => e.project.getActive()); - const renderTree = useEditor((e) => e.renderer.getRenderTree()); - const { overlays } = usePreviewStore(); - const viewport = usePreviewViewportState({ - canvasHeight: nativeHeight, - canvasWidth: nativeWidth, - viewportHeight: viewportSize.height, - viewportRef, - viewportWidth: viewportSize.width, - }); - - const renderer = useMemo(() => { - return new CanvasRenderer({ - width: nativeWidth, - height: nativeHeight, - fps: activeProject.settings.fps, - }); - }, [nativeWidth, nativeHeight, activeProject.settings.fps.numerator, activeProject.settings.fps.denominator]); - - const render = useCallback(() => { - if (canvasRef.current && renderTree && !renderingRef.current) { - const renderTime = Math.min( - editor.playback.getCurrentTime(), - editor.timeline.getLastFrameTime(), - ); - const ticksPerFrame = Math.round(TICKS_PER_SECOND * renderer.fps.denominator / renderer.fps.numerator); - const frame = Math.floor(renderTime / ticksPerFrame); - - if ( - frame !== lastFrameRef.current || - renderTree !== lastSceneRef.current - ) { - renderingRef.current = true; - lastSceneRef.current = renderTree; - lastFrameRef.current = frame; - renderer - .renderToCanvas({ - node: renderTree, - time: renderTime, - targetCanvas: canvasRef.current, - }) - .then(() => { - renderingRef.current = false; - }); - } - } - }, [renderer, renderTree, editor.playback]); - - useRafLoop(render); - - useEffect(() => { - const container = viewportRef.current; - if (!container) return; - - let pendingZoomDelta = 0; - let pendingPanDeltaX = 0; - let pendingPanDeltaY = 0; - let zoomRafId: ReturnType | null = null; - let panRafId: ReturnType | null = null; - - const onWheel = (event: WheelEvent) => { - const normalizedDeltaX = normalizeWheelDelta({ - delta: event.deltaX, - deltaMode: event.deltaMode, - pageSize: container.clientWidth, - }); - const normalizedDeltaY = normalizeWheelDelta({ - delta: event.deltaY, - deltaMode: event.deltaMode, - pageSize: container.clientHeight, - }); - const isZoomGesture = event.ctrlKey || event.metaKey; - if (isZoomGesture) { - event.preventDefault(); - pendingZoomDelta += normalizedDeltaY; - - if (zoomRafId === null) { - zoomRafId = requestAnimationFrame(() => { - const cappedDelta = - Math.sign(pendingZoomDelta) * - Math.min(Math.abs(pendingZoomDelta), 30); - const zoomFactor = Math.exp(-cappedDelta / 300); - - viewport.scaleZoom({ factor: zoomFactor }); - pendingZoomDelta = 0; - zoomRafId = null; - }); - } - - return; - } - - if (!viewport.canPan) { - return; - } - - if (normalizedDeltaX === 0 && normalizedDeltaY === 0) { - return; - } - - event.preventDefault(); - pendingPanDeltaX += normalizedDeltaX; - pendingPanDeltaY += normalizedDeltaY; - - if (panRafId === null) { - panRafId = requestAnimationFrame(() => { - viewport.panByScreenDelta({ - deltaX: pendingPanDeltaX, - deltaY: pendingPanDeltaY, - }); - pendingPanDeltaX = 0; - pendingPanDeltaY = 0; - panRafId = null; - }); - } - }; - - container.addEventListener("wheel", onWheel, { - capture: true, - passive: false, - }); - - return () => { - container.removeEventListener("wheel", onWheel, { - capture: true, - }); - if (zoomRafId !== null) { - cancelAnimationFrame(zoomRafId); - } - if (panRafId !== null) { - cancelAnimationFrame(panRafId); - } - }; - }, [viewport.canPan, viewport.panByScreenDelta, viewport.scaleZoom]); - - return ( - -
-
- - -
- - - - {overlays.bookmarks && } -
-
- -
-
- -
-
- ); -} -- cgit v1.3.1