@@ -51,7 +51,7 @@ export function PlaybackControls({ project, playerRef }: PlaybackControlsProps)
size="sm"
variant="default"
onClick={togglePlay}
- className="w-12 h-12 rounded-full"
+ className="w-12 h-12 rounded-full bg-blue-600 hover:bg-blue-700"
>
{isPlaying ? (
@@ -65,20 +65,20 @@ export function PlaybackControls({ project, playerRef }: PlaybackControlsProps)
variant="ghost"
onClick={() => skipForward(30)}
title="Go forward 1 second"
- className="h-10 w-10"
+ className="h-10 w-10 text-white hover:bg-slate-800"
>
- {formatTime(currentFrame)}
- /
- {formatTime(totalFrames)}
+ {formatTime(currentFrame)}
+ /
+ {formatTime(totalFrames)}
-
- Frame:
{currentFrame} / {totalFrames}
+
+ Frame: {currentFrame} / {totalFrames}
);
diff --git a/MyApp.Client/components/editor/canvas/preview-canvas.tsx b/MyApp.Client/components/editor/canvas/preview-canvas.tsx
index 7a10162..3878cfa 100644
--- a/MyApp.Client/components/editor/canvas/preview-canvas.tsx
+++ b/MyApp.Client/components/editor/canvas/preview-canvas.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useCallback, useEffect, useRef } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
import { Player, PlayerRef } from '@remotion/player';
import { Project } from '@/lib/types/project';
import { useAssetStore } from '@/lib/stores/asset-store';
@@ -19,37 +19,94 @@ export function PreviewCanvas({ project }: PreviewCanvasProps) {
const { isPlaying, pause } = usePlaybackStore();
const { getTimeline, setCurrentFrame } = useTimelineStore();
const timeline = getTimeline();
+ const [isPlayerReady, setIsPlayerReady] = useState(false);
- // Sync player playback state
+ // Wait for player to be ready with better detection
useEffect(() => {
- if (!playerRef.current) return;
+ let attempts = 0;
+ const maxAttempts = 20;
+ let timeoutId: NodeJS.Timeout;
+
+ const checkPlayerReady = () => {
+ if (playerRef.current) {
+ try {
+ // Try to get current frame as a test of readiness
+ playerRef.current.getCurrentFrame();
+ console.log('Player is ready!');
+ setIsPlayerReady(true);
+ return;
+ } catch (error) {
+ console.log('Player not ready yet, attempt:', attempts + 1);
+ }
+ }
+
+ attempts++;
+ if (attempts < maxAttempts) {
+ timeoutId = setTimeout(checkPlayerReady, 100);
+ } else {
+ console.error('Player failed to initialize after', maxAttempts, 'attempts');
+ }
+ };
- if (isPlaying) {
- playerRef.current.play();
- } else {
- playerRef.current.pause();
+ timeoutId = setTimeout(checkPlayerReady, 100);
+ return () => clearTimeout(timeoutId);
+ }, []);
+
+ // Sync current frame to player when timeline changes
+ useEffect(() => {
+ if (!playerRef.current || !isPlayerReady) {
+ return;
}
- }, [isPlaying]);
- // Handle frame updates from player
- const handleFrameUpdate = useCallback(
- (frame: number) => {
- if (timeline && frame !== timeline.currentFrame) {
- setCurrentFrame(frame);
+ try {
+ const currentPlayerFrame = playerRef.current.getCurrentFrame();
+ if (timeline && currentPlayerFrame !== timeline.currentFrame) {
+ playerRef.current.seekTo(timeline.currentFrame);
}
- },
- [timeline, setCurrentFrame]
- );
+ } catch (error) {
+ console.error('Error seeking player:', error);
+ }
+ }, [timeline?.currentFrame, isPlayerReady]);
- // Handle playback end
- const handleEnded = useCallback(() => {
- pause();
- }, [pause]);
+ // Sync player playback state
+ useEffect(() => {
+ if (!playerRef.current || !isPlayerReady) {
+ console.log('Player not ready for playback control');
+ return;
+ }
+
+ try {
+ const player = playerRef.current;
+ console.log('Current player state:', {
+ isPlaying,
+ currentFrame: player.getCurrentFrame(),
+ isPlayerPlaying: player.isPlaying(),
+ });
+
+ if (isPlaying) {
+ console.log('Calling play() on player');
+ player.play();
+
+ // Verify playback started
+ setTimeout(() => {
+ console.log('Playback verification:', {
+ isPlayerPlaying: player.isPlaying(),
+ currentFrame: player.getCurrentFrame(),
+ });
+ }, 100);
+ } else {
+ console.log('Calling pause() on player');
+ player.pause();
+ }
+ } catch (error) {
+ console.error('Error controlling playback:', error);
+ }
+ }, [isPlaying, isPlayerReady]);
if (!timeline) return null;
return (
-
+
(
+
+ )}
+ errorFallback={({ error }) => (
+
+
Error: {error.message}
+
+ )}
/>
diff --git a/MyApp.Client/components/editor/editor-layout.tsx b/MyApp.Client/components/editor/editor-layout.tsx
index 209f629..0d1f8fd 100644
--- a/MyApp.Client/components/editor/editor-layout.tsx
+++ b/MyApp.Client/components/editor/editor-layout.tsx
@@ -12,12 +12,12 @@ interface EditorLayoutProps {
export function EditorLayout({ project }: EditorLayoutProps) {
return (
-
+
{/* Header */}
-
-
{project.name}
+
+
{project.name}
-
+
{project.settings.width}x{project.settings.height} • {project.settings.fps}fps
@@ -26,7 +26,7 @@ export function EditorLayout({ project }: EditorLayoutProps) {
{/* Main Content */}
{/* Left Sidebar - Asset Library */}
-
+
@@ -36,10 +36,10 @@ export function EditorLayout({ project }: EditorLayoutProps) {
-
+
{/* Timeline */}
-
diff --git a/MyApp.Client/components/editor/timeline/timeline-container.tsx b/MyApp.Client/components/editor/timeline/timeline-container.tsx
index f9bafc0..a7054f5 100644
--- a/MyApp.Client/components/editor/timeline/timeline-container.tsx
+++ b/MyApp.Client/components/editor/timeline/timeline-container.tsx
@@ -50,18 +50,18 @@ export function TimelineContainer({ project }: TimelineContainerProps) {
};
return (
-
+
{/* Toolbar */}
-
-