improve lots of stuff around the editor
This commit is contained in:
@ -4,20 +4,25 @@ import Link from "next/link";
|
||||
import { Button } from "./ui/button";
|
||||
import { ChevronLeft, Download } from "lucide-react";
|
||||
import { useProjectStore } from "@/stores/project-store";
|
||||
import { useMediaStore } from "@/stores/media-store";
|
||||
import { useTimelineStore } from "@/stores/timeline-store";
|
||||
import { HeaderBase } from "./header-base";
|
||||
|
||||
export function EditorHeader() {
|
||||
const { activeProject } = useProjectStore();
|
||||
const { mediaItems } = useMediaStore();
|
||||
const { tracks } = useTimelineStore();
|
||||
const { getTotalDuration } = useTimelineStore();
|
||||
|
||||
const handleExport = () => {
|
||||
// TODO: Implement export functionality
|
||||
console.log("Export project");
|
||||
};
|
||||
|
||||
// Format duration from seconds to MM:SS format
|
||||
const formatDuration = (seconds: number): string => {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = Math.floor(seconds % 60);
|
||||
return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const leftContent = (
|
||||
<Link
|
||||
href="/"
|
||||
@ -30,9 +35,7 @@ export function EditorHeader() {
|
||||
|
||||
const centerContent = (
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{mediaItems.length} media</span>
|
||||
<span>•</span>
|
||||
<span>{tracks.length} tracks</span>
|
||||
<span>{formatDuration(getTotalDuration())}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -18,6 +18,9 @@ export function PreviewPanel() {
|
||||
? mediaItems.find((item) => item.id === firstClip.mediaId)
|
||||
: null;
|
||||
|
||||
// Calculate dynamic aspect ratio - default to 16:9 if no media
|
||||
const aspectRatio = firstMediaItem?.aspectRatio || 16 / 9;
|
||||
|
||||
const renderPreviewContent = () => {
|
||||
if (!firstMediaItem) {
|
||||
return (
|
||||
@ -32,7 +35,7 @@ export function PreviewPanel() {
|
||||
<ImageTimelineTreatment
|
||||
src={firstMediaItem.url}
|
||||
alt={firstMediaItem.name}
|
||||
targetAspectRatio={16 / 9}
|
||||
targetAspectRatio={aspectRatio}
|
||||
className="w-full h-full rounded-lg"
|
||||
backgroundType="blur"
|
||||
/>
|
||||
@ -68,8 +71,17 @@ export function PreviewPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col items-center justify-center p-4">
|
||||
<div className="aspect-video bg-black/90 w-full max-w-4xl rounded-lg shadow-lg relative group overflow-hidden">
|
||||
<div className="h-full flex flex-col items-center justify-center p-4 overflow-hidden">
|
||||
<div
|
||||
className="bg-black/90 rounded-lg shadow-lg relative group overflow-hidden flex-shrink"
|
||||
style={{
|
||||
aspectRatio: aspectRatio.toString(),
|
||||
width: aspectRatio > 1 ? "100%" : "auto",
|
||||
height: aspectRatio <= 1 ? "100%" : "auto",
|
||||
maxWidth: "100%",
|
||||
maxHeight: "100%",
|
||||
}}
|
||||
>
|
||||
{renderPreviewContent()}
|
||||
|
||||
{/* Playback Controls Overlay */}
|
||||
@ -103,6 +115,16 @@ export function PreviewPanel() {
|
||||
Preview: {firstMediaItem.name}
|
||||
{firstMediaItem.type === "image" &&
|
||||
" (with CapCut-style treatment)"}
|
||||
<br />
|
||||
<span className="text-xs text-muted-foreground/70">
|
||||
Aspect Ratio: {aspectRatio.toFixed(2)} (
|
||||
{aspectRatio > 1
|
||||
? "Landscape"
|
||||
: aspectRatio < 1
|
||||
? "Portrait"
|
||||
: "Square"}
|
||||
)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
@ -472,19 +472,12 @@ function TimelineTrackComponent({ track }: { track: TimelineTrack }) {
|
||||
|
||||
if (mediaItem.type === "image") {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center gap-2">
|
||||
<div className="w-16 h-12 flex-shrink-0">
|
||||
<ImageTimelineTreatment
|
||||
src={mediaItem.url}
|
||||
alt={mediaItem.name}
|
||||
targetAspectRatio={16 / 9}
|
||||
className="rounded-sm"
|
||||
backgroundType="mirror"
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs text-foreground/80 truncate flex-1">
|
||||
{clip.name}
|
||||
</span>
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<img
|
||||
src={mediaItem.url}
|
||||
alt={mediaItem.name}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -536,9 +529,9 @@ function TimelineTrackComponent({ track }: { track: TimelineTrack }) {
|
||||
track.clips.map((clip, index) => (
|
||||
<div
|
||||
key={clip.id}
|
||||
className={`timeline-clip h-full rounded-sm border cursor-grab active:cursor-grabbing transition-colors ${getTrackColor(track.type)} flex items-center px-2 min-w-[80px] overflow-hidden`}
|
||||
className={`timeline-clip h-full rounded-sm border cursor-grab active:cursor-grabbing transition-colors ${getTrackColor(track.type)} flex items-center py-3 min-w-[80px] overflow-hidden`}
|
||||
style={{
|
||||
width: `${Math.max(80, (clip.duration / 30) * 400)}px`,
|
||||
width: `${Math.max(80, clip.duration * 50)}px`,
|
||||
}}
|
||||
draggable={true}
|
||||
onDragStart={(e) => handleClipDragStart(e, clip)}
|
||||
|
@ -33,9 +33,12 @@ interface TimelineStore {
|
||||
clipId: string,
|
||||
newIndex: number
|
||||
) => void;
|
||||
|
||||
// Computed values
|
||||
getTotalDuration: () => number;
|
||||
}
|
||||
|
||||
export const useTimelineStore = create<TimelineStore>((set) => ({
|
||||
export const useTimelineStore = create<TimelineStore>((set, get) => ({
|
||||
tracks: [],
|
||||
|
||||
addTrack: (type) => {
|
||||
@ -134,4 +137,17 @@ export const useTimelineStore = create<TimelineStore>((set) => ({
|
||||
}),
|
||||
}));
|
||||
},
|
||||
|
||||
getTotalDuration: () => {
|
||||
const { tracks } = get();
|
||||
if (tracks.length === 0) return 0;
|
||||
|
||||
// Calculate the duration of each track (sum of all clips in that track)
|
||||
const trackDurations = tracks.map((track) =>
|
||||
track.clips.reduce((total, clip) => total + clip.duration, 0)
|
||||
);
|
||||
|
||||
// Return the maximum track duration (longest track determines project duration)
|
||||
return Math.max(...trackDurations, 0);
|
||||
},
|
||||
}));
|
||||
|
Reference in New Issue
Block a user