summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/web/src/components/editor/preview-panel.tsx12
-rw-r--r--apps/web/src/stores/editor-store.ts65
-rw-r--r--apps/web/src/stores/timeline-store.ts29
-rw-r--r--apps/web/src/types/editor.ts11
4 files changed, 107 insertions, 10 deletions
diff --git a/apps/web/src/components/editor/preview-panel.tsx b/apps/web/src/components/editor/preview-panel.tsx
index 0eee6002..06692926 100644
--- a/apps/web/src/components/editor/preview-panel.tsx
+++ b/apps/web/src/components/editor/preview-panel.tsx
@@ -7,6 +7,7 @@ import {
} from "@/stores/timeline-store";
import { useMediaStore, type MediaItem } from "@/stores/media-store";
import { usePlaybackStore } from "@/stores/playback-store";
+import { useEditorStore } from "@/stores/editor-store";
import { VideoPlayer } from "@/components/ui/video-player";
import { Button } from "@/components/ui/button";
import { Play, Pause, Volume2, VolumeX, Plus } from "lucide-react";
@@ -22,7 +23,7 @@ export function PreviewPanel() {
const { tracks } = useTimelineStore();
const { mediaItems } = useMediaStore();
const { currentTime, muted, toggleMute, volume } = usePlaybackStore();
- const [canvasSize, setCanvasSize] = useState({ width: 1920, height: 1080 });
+ const { canvasSize, canvasPresets, setCanvasSize } = useEditorStore();
const previewRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [previewDimensions, setPreviewDimensions] = useState({
@@ -183,15 +184,6 @@ export function PreviewPanel() {
return null;
};
- // Canvas presets
- const canvasPresets = [
- { name: "16:9 HD", width: 1920, height: 1080 },
- { name: "16:9 4K", width: 3840, height: 2160 },
- { name: "9:16 Mobile", width: 1080, height: 1920 },
- { name: "1:1 Square", width: 1080, height: 1080 },
- { name: "4:3 Standard", width: 1440, height: 1080 },
- ];
-
return (
<div className="h-full w-full flex flex-col min-h-0 min-w-0">
{/* Controls */}
diff --git a/apps/web/src/stores/editor-store.ts b/apps/web/src/stores/editor-store.ts
index e8dd8e21..1ce870c4 100644
--- a/apps/web/src/stores/editor-store.ts
+++ b/apps/web/src/stores/editor-store.ts
@@ -1,20 +1,72 @@
import { create } from "zustand";
+import { CanvasSize, CanvasPreset } from "@/types/editor";
interface EditorState {
// Loading states
isInitializing: boolean;
isPanelsReady: boolean;
+ // Canvas/Project settings
+ canvasSize: CanvasSize;
+ canvasPresets: CanvasPreset[];
+
// Actions
setInitializing: (loading: boolean) => void;
setPanelsReady: (ready: boolean) => void;
initializeApp: () => Promise<void>;
+ setCanvasSize: (size: CanvasSize) => void;
+ setCanvasSizeFromAspectRatio: (aspectRatio: number) => void;
}
+const DEFAULT_CANVAS_PRESETS: CanvasPreset[] = [
+ { name: "16:9 HD", width: 1920, height: 1080 },
+ { name: "16:9 4K", width: 3840, height: 2160 },
+ { name: "9:16 Mobile", width: 1080, height: 1920 },
+ { name: "1:1 Square", width: 1080, height: 1080 },
+ { name: "4:3 Standard", width: 1440, height: 1080 },
+];
+
+// Helper function to find the best matching canvas preset for an aspect ratio
+const findBestCanvasPreset = (aspectRatio: number): CanvasSize => {
+ // Calculate aspect ratio for each preset and find the closest match
+ let bestMatch = DEFAULT_CANVAS_PRESETS[0]; // Default to 16:9 HD
+ let smallestDifference = Math.abs(
+ aspectRatio - bestMatch.width / bestMatch.height
+ );
+
+ for (const preset of DEFAULT_CANVAS_PRESETS) {
+ const presetAspectRatio = preset.width / preset.height;
+ const difference = Math.abs(aspectRatio - presetAspectRatio);
+
+ if (difference < smallestDifference) {
+ smallestDifference = difference;
+ bestMatch = preset;
+ }
+ }
+
+ // If the difference is still significant (> 0.1), create a custom size
+ // based on the media aspect ratio with a reasonable resolution
+ const bestAspectRatio = bestMatch.width / bestMatch.height;
+ if (Math.abs(aspectRatio - bestAspectRatio) > 0.1) {
+ // Create custom dimensions based on the aspect ratio
+ if (aspectRatio > 1) {
+ // Landscape - use 1920 width
+ return { width: 1920, height: Math.round(1920 / aspectRatio) };
+ } else {
+ // Portrait or square - use 1080 height
+ return { width: Math.round(1080 * aspectRatio), height: 1080 };
+ }
+ }
+
+ return { width: bestMatch.width, height: bestMatch.height };
+};
+
export const useEditorStore = create<EditorState>((set, get) => ({
// Initial states
isInitializing: true,
isPanelsReady: false,
+ canvasSize: { width: 1920, height: 1080 }, // Default 16:9 HD
+ canvasPresets: DEFAULT_CANVAS_PRESETS,
// Actions
setInitializing: (loading) => {
@@ -32,4 +84,17 @@ export const useEditorStore = create<EditorState>((set, get) => ({
set({ isPanelsReady: true, isInitializing: false });
console.log("Video editor ready");
},
+
+ setCanvasSize: (size) => {
+ set({ canvasSize: size });
+ },
+
+ setCanvasSizeFromAspectRatio: (aspectRatio) => {
+ const newCanvasSize = findBestCanvasPreset(aspectRatio);
+ console.log(
+ `Setting canvas size based on aspect ratio ${aspectRatio}:`,
+ newCanvasSize
+ );
+ set({ canvasSize: newCanvasSize });
+ },
}));
diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts
index 73628497..9f98b320 100644
--- a/apps/web/src/stores/timeline-store.ts
+++ b/apps/web/src/stores/timeline-store.ts
@@ -1,5 +1,8 @@
import { create } from "zustand";
import type { TrackType } from "@/types/timeline";
+import { useEditorStore } from "./editor-store";
+import { useMediaStore } from "./media-store";
+import { toast } from "sonner";
// Helper function to manage clip naming with suffixes
const getClipNameWithSuffix = (
@@ -199,6 +202,15 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
addClipToTrack: (trackId, clipData) => {
get().pushHistory();
+
+ // Check if this is the first clip being added to the timeline
+ const currentState = get();
+ const totalClipsInTimeline = currentState.tracks.reduce(
+ (total, track) => total + track.clips.length,
+ 0
+ );
+ const isFirstClip = totalClipsInTimeline === 0;
+
const newClip: TimelineClip = {
...clipData,
id: crypto.randomUUID(),
@@ -207,6 +219,23 @@ export const useTimelineStore = create<TimelineStore>((set, get) => ({
trimEnd: 0,
};
+ // If this is the first clip, automatically set the project canvas size
+ // to match the media's aspect ratio
+ if (isFirstClip && clipData.mediaId) {
+ const mediaStore = useMediaStore.getState();
+ const mediaItem = mediaStore.mediaItems.find(
+ (item) => item.id === clipData.mediaId
+ );
+
+ if (
+ mediaItem &&
+ (mediaItem.type === "image" || mediaItem.type === "video")
+ ) {
+ const editorStore = useEditorStore.getState();
+ editorStore.setCanvasSizeFromAspectRatio(mediaItem.aspectRatio);
+ }
+ }
+
set((state) => ({
tracks: state.tracks.map((track) =>
track.id === trackId
diff --git a/apps/web/src/types/editor.ts b/apps/web/src/types/editor.ts
index bf1dae2f..dce7ac50 100644
--- a/apps/web/src/types/editor.ts
+++ b/apps/web/src/types/editor.ts
@@ -1 +1,12 @@
export type BackgroundType = "blur" | "mirror" | "color";
+
+export interface CanvasSize {
+ width: number;
+ height: number;
+}
+
+export interface CanvasPreset {
+ name: string;
+ width: number;
+ height: number;
+}