Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions MyApp.Client/components/editor/canvas/playback-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,23 @@ export function PlaybackControls({ project, playerRef }: PlaybackControlsProps)
};

return (
<div className="h-16 bg-gray-900 border-t border-gray-800 px-4 flex items-center gap-4">
<div className="h-16 bg-white dark:bg-slate-900 border-t border-border px-4 flex items-center gap-4 shadow-sm">
<div className="flex items-center gap-2">
<Button
size="sm"
variant="ghost"
onClick={() => skipBackward(30)}
title="Go back 1 second"
className="h-10 w-10"
>
<SkipBack className="w-4 h-4" />
<SkipBack className="w-5 h-5" />
</Button>

<Button
size="sm"
variant="default"
onClick={togglePlay}
className="w-12 h-12"
className="w-12 h-12 rounded-full"
>
{isPlaying ? (
<Pause className="w-5 h-5" />
Expand All @@ -64,19 +65,20 @@ export function PlaybackControls({ project, playerRef }: PlaybackControlsProps)
variant="ghost"
onClick={() => skipForward(30)}
title="Go forward 1 second"
className="h-10 w-10"
>
<SkipForward className="w-4 h-4" />
<SkipForward className="w-5 h-5" />
</Button>
</div>

<div className="flex items-center gap-3 text-sm font-mono">
<span>{formatTime(currentFrame)}</span>
<span className="text-gray-600">/</span>
<span className="text-gray-400">{formatTime(totalFrames)}</span>
<div className="flex items-center gap-3 text-sm font-mono font-semibold">
<span className="text-foreground">{formatTime(currentFrame)}</span>
<span className="text-muted-foreground">/</span>
<span className="text-muted-foreground">{formatTime(totalFrames)}</span>
</div>

<div className="ml-auto text-sm text-gray-400">
Frame: {currentFrame} / {totalFrames}
<div className="ml-auto text-sm text-muted-foreground font-medium">
Frame: <span className="text-foreground font-semibold">{currentFrame}</span> / {totalFrames}
</div>
</div>
);
Expand Down
31 changes: 22 additions & 9 deletions MyApp.Client/components/editor/canvas/preview-canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useEffect, useRef } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { Player, PlayerRef } from '@remotion/player';
import { Project } from '@/lib/types/project';
import { useAssetStore } from '@/lib/stores/asset-store';
Expand All @@ -16,10 +16,11 @@ interface PreviewCanvasProps {
export function PreviewCanvas({ project }: PreviewCanvasProps) {
const playerRef = useRef<PlayerRef>(null);
const { assets } = useAssetStore();
const { isPlaying, fps, pause, play } = usePlaybackStore();
const { isPlaying, pause } = usePlaybackStore();
const { getTimeline, setCurrentFrame } = useTimelineStore();
const timeline = getTimeline();

// Sync player playback state
useEffect(() => {
if (!playerRef.current) return;

Expand All @@ -30,19 +31,28 @@ export function PreviewCanvas({ project }: PreviewCanvasProps) {
}
}, [isPlaying]);

useEffect(() => {
if (playerRef.current && timeline) {
playerRef.current.seekTo(timeline.currentFrame);
}
}, [timeline?.currentFrame]);
// Handle frame updates from player
const handleFrameUpdate = useCallback(
(frame: number) => {
if (timeline && frame !== timeline.currentFrame) {
setCurrentFrame(frame);
}
},
[timeline, setCurrentFrame]
);

// Handle playback end
const handleEnded = useCallback(() => {
pause();
}, [pause]);

if (!timeline) return null;

return (
<div className="h-full flex flex-col bg-gray-950">
<div className="h-full flex flex-col bg-slate-200 dark:bg-slate-900">
<div className="flex-1 flex items-center justify-center p-4">
<div
className="relative bg-black"
className="relative bg-black shadow-2xl rounded-lg overflow-hidden"
style={{
aspectRatio: `${project.settings.width} / ${project.settings.height}`,
maxWidth: '100%',
Expand All @@ -66,6 +76,9 @@ export function PreviewCanvas({ project }: PreviewCanvasProps) {
compositionHeight={project.settings.height}
style={{ width: '100%', height: '100%' }}
controls={false}
clickToPlay={false}
doubleClickToFullscreen={false}
spaceKeyToPlayOrPause={false}
/>
</div>
</div>
Expand Down
12 changes: 6 additions & 6 deletions MyApp.Client/components/editor/editor-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ interface EditorLayoutProps {

export function EditorLayout({ project }: EditorLayoutProps) {
return (
<div className="h-screen flex flex-col bg-black text-white">
<div className="h-screen flex flex-col bg-background text-foreground">
{/* Header */}
<div className="h-14 bg-gray-900 border-b border-gray-800 flex items-center px-4">
<h1 className="text-lg font-semibold">{project.name}</h1>
<div className="h-14 bg-slate-100 dark:bg-slate-900 border-b border-border flex items-center px-4 shadow-sm">
<h1 className="text-lg font-semibold text-foreground">{project.name}</h1>
<div className="ml-auto flex items-center gap-2">
<span className="text-sm text-gray-400">
<span className="text-sm text-muted-foreground font-medium">
{project.settings.width}x{project.settings.height} • {project.settings.fps}fps
</span>
</div>
Expand All @@ -26,7 +26,7 @@ export function EditorLayout({ project }: EditorLayoutProps) {
{/* Main Content */}
<div className="flex-1 flex overflow-hidden">
{/* Left Sidebar - Asset Library */}
<div className="w-80 bg-gray-900 border-r border-gray-800 overflow-y-auto">
<div className="w-80 bg-slate-50 dark:bg-slate-900 border-r border-border overflow-y-auto">
<AssetLibrary projectId={project.id} />
</div>

Expand All @@ -39,7 +39,7 @@ export function EditorLayout({ project }: EditorLayoutProps) {
<Separator />

{/* Timeline */}
<div className="h-80 bg-gray-950">
<div className="h-80 bg-slate-100 dark:bg-slate-950">
<TimelineContainer project={project} />
</div>
</div>
Expand Down
18 changes: 9 additions & 9 deletions MyApp.Client/components/editor/timeline/timeline-container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ export function TimelineContainer({ project }: TimelineContainerProps) {
};

return (
<div className="h-full flex flex-col">
<div className="h-full flex flex-col bg-background">
{/* Toolbar */}
<div className="h-12 bg-gray-900 border-b border-gray-800 px-4 flex items-center gap-2">
<Button size="sm" variant="ghost" onClick={() => addTrack('video')}>
<div className="h-12 bg-slate-50 dark:bg-slate-900 border-b border-border px-4 flex items-center gap-2 shadow-sm">
<Button size="sm" variant="ghost" onClick={() => addTrack('video')} className="font-medium">
<Video className="w-4 h-4 mr-1" />
Add Video Track
</Button>
<Button size="sm" variant="ghost" onClick={() => addTrack('audio')}>
<Button size="sm" variant="ghost" onClick={() => addTrack('audio')} className="font-medium">
<Music className="w-4 h-4 mr-1" />
Add Audio Track
</Button>
<Button size="sm" variant="ghost" onClick={() => addTrack('text')}>
<Button size="sm" variant="ghost" onClick={() => addTrack('text')} className="font-medium">
<Type className="w-4 h-4 mr-1" />
Add Text Track
</Button>
Expand All @@ -71,13 +71,13 @@ export function TimelineContainer({ project }: TimelineContainerProps) {
<TimelineRuler fps={project.settings.fps} totalFrames={project.settings.durationInFrames} />

{/* Tracks */}
<div className="flex-1 overflow-y-auto">
<div className="flex-1 overflow-y-auto bg-slate-50 dark:bg-slate-950">
{timeline.tracks.length === 0 ? (
<div className="h-full flex items-center justify-center text-gray-500">
<div className="h-full flex items-center justify-center text-muted-foreground">
<div className="text-center">
<Plus className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p className="text-sm">No tracks yet</p>
<p className="text-xs">Add a track to get started</p>
<p className="text-sm font-medium">No tracks yet</p>
<p className="text-xs mt-1">Add a track to get started</p>
</div>
</div>
) : (
Expand Down
22 changes: 11 additions & 11 deletions MyApp.Client/components/editor/timeline/timeline-element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ export function TimelineElement({ element, fps }: TimelineElementProps) {
const getElementColor = () => {
switch (element.type) {
case 'video':
return 'bg-blue-600 border-blue-500';
return 'bg-blue-600 dark:bg-blue-700 border-blue-500 dark:border-blue-600';
case 'audio':
return 'bg-green-600 border-green-500';
return 'bg-green-600 dark:bg-green-700 border-green-500 dark:border-green-600';
case 'image':
return 'bg-purple-600 border-purple-500';
return 'bg-purple-600 dark:bg-purple-700 border-purple-500 dark:border-purple-600';
case 'text':
return 'bg-yellow-600 border-yellow-500';
return 'bg-yellow-600 dark:bg-yellow-700 border-yellow-500 dark:border-yellow-600';
default:
return 'bg-gray-600 border-gray-500';
return 'bg-gray-600 dark:bg-gray-700 border-gray-500 dark:border-gray-600';
}
};

Expand Down Expand Up @@ -90,30 +90,30 @@ export function TimelineElement({ element, fps }: TimelineElementProps) {

return (
<div
className={`absolute top-1 bottom-1 rounded border-2 ${getElementColor()} ${
isSelected ? 'ring-2 ring-white' : ''
} cursor-move overflow-hidden`}
className={`absolute top-1 bottom-1 rounded-md border-2 ${getElementColor()} ${
isSelected ? 'ring-2 ring-primary shadow-lg' : 'shadow-md'
} cursor-move overflow-hidden transition-shadow hover:shadow-lg`}
style={{
left: `${left}px`,
width: `${width}px`,
}}
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
>
<div className="px-2 py-1 text-xs font-medium truncate text-white">
<div className="px-2 py-1 text-xs font-semibold truncate text-white drop-shadow-sm">
{asset?.name || element.name}
</div>

{/* Resize handles */}
<div
className="absolute left-0 top-0 bottom-0 w-1 bg-white/50 cursor-ew-resize hover:bg-white"
className="absolute left-0 top-0 bottom-0 w-1.5 bg-white/30 cursor-ew-resize hover:bg-white/60 transition-colors"
onMouseDown={(e) => {
e.stopPropagation();
setIsResizing(true);
}}
/>
<div
className="absolute right-0 top-0 bottom-0 w-1 bg-white/50 cursor-ew-resize hover:bg-white"
className="absolute right-0 top-0 bottom-0 w-1.5 bg-white/30 cursor-ew-resize hover:bg-white/60 transition-colors"
onMouseDown={(e) => {
e.stopPropagation();
setIsResizing(true);
Expand Down
6 changes: 3 additions & 3 deletions MyApp.Client/components/editor/timeline/timeline-ruler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function TimelineRuler({ fps, totalFrames }: TimelineRulerProps) {

return (
<div
className="h-8 bg-gray-900 border-b border-gray-800 relative cursor-pointer overflow-x-auto"
className="h-8 bg-white dark:bg-slate-900 border-b border-border relative cursor-pointer overflow-x-auto shadow-sm"
onClick={handleClick}
style={{ width: '100%' }}
>
Expand All @@ -53,10 +53,10 @@ export function TimelineRuler({ fps, totalFrames }: TimelineRulerProps) {

{/* Current frame cursor */}
<div
className="absolute top-0 bottom-0 w-0.5 bg-blue-500 pointer-events-none z-10"
className="absolute top-0 bottom-0 w-0.5 bg-primary pointer-events-none z-10 shadow-md"
style={{ left: `${cursorPosition}px` }}
>
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-3 h-3 bg-blue-500 rounded-sm" />
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-3 h-3 bg-primary rounded-sm shadow-md" />
</div>
</div>
</div>
Expand Down
28 changes: 14 additions & 14 deletions MyApp.Client/components/editor/timeline/timeline-track.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,41 @@ export function TimelineTrack({ track, fps, onDrop, onDragOver }: TimelineTrackP
const getTrackColor = () => {
switch (track.type) {
case 'video':
return 'bg-blue-500/20 border-blue-500/30';
return 'bg-blue-50 dark:bg-blue-950/20 border-blue-200 dark:border-blue-800/30';
case 'audio':
return 'bg-green-500/20 border-green-500/30';
return 'bg-green-50 dark:bg-green-950/20 border-green-200 dark:border-green-800/30';
case 'text':
return 'bg-yellow-500/20 border-yellow-500/30';
return 'bg-yellow-50 dark:bg-yellow-950/20 border-yellow-200 dark:border-yellow-800/30';
default:
return 'bg-purple-500/20 border-purple-500/30';
return 'bg-purple-50 dark:bg-purple-950/20 border-purple-200 dark:border-purple-800/30';
}
};

return (
<div
className={`flex border-b border-gray-800 ${getTrackColor()}`}
className={`flex border-b border-border ${getTrackColor()}`}
style={{ height: `${track.height}px` }}
>
{/* Track Header */}
<div className="w-48 bg-gray-900 border-r border-gray-800 p-2 flex flex-col justify-between">
<div className="w-48 bg-slate-100 dark:bg-slate-900 border-r border-border p-2 flex flex-col justify-between shadow-sm">
<div>
<div className="font-medium text-sm mb-1 truncate">{track.name}</div>
<div className="text-xs text-gray-500 capitalize">{track.type}</div>
<div className="font-semibold text-sm mb-1 truncate text-foreground">{track.name}</div>
<div className="text-xs text-muted-foreground capitalize font-medium">{track.type}</div>
</div>
<div className="flex gap-1">
<Button size="sm" variant="ghost" className="h-6 w-6 p-0">
{track.isLocked ? <Lock className="w-3 h-3" /> : <Unlock className="w-3 h-3" />}
<Button size="sm" variant="ghost" className="h-7 w-7 p-0 hover:bg-accent">
{track.isLocked ? <Lock className="w-3.5 h-3.5" /> : <Unlock className="w-3.5 h-3.5" />}
</Button>
<Button size="sm" variant="ghost" className="h-6 w-6 p-0">
{track.isMuted ? <VolumeX className="w-3 h-3" /> : <Volume2 className="w-3 h-3" />}
<Button size="sm" variant="ghost" className="h-7 w-7 p-0 hover:bg-accent">
{track.isMuted ? <VolumeX className="w-3.5 h-3.5" /> : <Volume2 className="w-3.5 h-3.5" />}
</Button>
<Button
size="sm"
variant="ghost"
className="h-6 w-6 p-0"
className="h-7 w-7 p-0 hover:bg-destructive/90 hover:text-destructive-foreground"
onClick={() => removeTrack(track.id)}
>
<Trash2 className="w-3 h-3" />
<Trash2 className="w-3.5 h-3.5" />
</Button>
</div>
</div>
Expand Down
22 changes: 11 additions & 11 deletions MyApp.Client/components/editor/toolbar/asset-library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function AssetLibrary({ projectId }: AssetLibraryProps) {

return (
<div className="p-4">
<h2 className="text-lg font-semibold mb-4">Assets</h2>
<h2 className="text-lg font-semibold mb-4 text-foreground">Assets</h2>

<input
ref={fileInputRef}
Expand All @@ -77,33 +77,33 @@ export function AssetLibrary({ projectId }: AssetLibraryProps) {

<div className="space-y-2">
{assets.length === 0 ? (
<div className="text-center py-8 text-gray-500">
<div className="text-center py-8 text-muted-foreground">
<Upload className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p className="text-sm">No assets yet</p>
<p className="text-xs">Upload videos, images, or audio</p>
<p className="text-sm font-medium">No assets yet</p>
<p className="text-xs mt-1">Upload videos, images, or audio</p>
</div>
) : (
assets.map((asset) => (
<div
key={asset.id}
className="group relative bg-gray-800 rounded-lg overflow-hidden hover:bg-gray-700 transition-colors"
className="group relative bg-white dark:bg-slate-800 border border-border rounded-lg overflow-hidden hover:shadow-md hover:border-primary/50 transition-all"
>
<div className="aspect-video bg-black flex items-center justify-center">
<div className="aspect-video bg-slate-100 dark:bg-slate-950 flex items-center justify-center border-b border-border">
{asset.metadata.thumbnail ? (
<img
src={asset.metadata.thumbnail}
alt={asset.name}
className="w-full h-full object-cover"
/>
) : (
<div className="text-gray-600">{getAssetIcon(asset.type)}</div>
<div className="text-muted-foreground">{getAssetIcon(asset.type)}</div>
)}
</div>
<div className="p-2">
<div className="p-2 bg-card">
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">{asset.name}</p>
<div className="flex items-center gap-2 text-xs text-gray-400">
<p className="text-sm font-medium truncate text-foreground">{asset.name}</p>
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-0.5">
{getAssetIcon(asset.type)}
<span className="capitalize">{asset.type}</span>
{asset.metadata.duration && (
Expand All @@ -118,7 +118,7 @@ export function AssetLibrary({ projectId }: AssetLibraryProps) {
size="sm"
variant="ghost"
onClick={() => handleDeleteAsset(asset.id)}
className="opacity-0 group-hover:opacity-100 transition-opacity"
className="opacity-0 group-hover:opacity-100 transition-opacity h-8 w-8"
>
<Trash2 className="w-4 h-4" />
</Button>
Expand Down
Loading