From 7b10464f1557899971fb9c13c565982454879990 Mon Sep 17 00:00:00 2001 From: SatyaViswas Date: Tue, 26 May 2026 10:51:39 +0530 Subject: [PATCH] feat: add draggable and resizable image overlay support in live preview --- src/components/ImageOverlay.tsx | 27 +-- src/components/ThemeToggle.tsx | 38 +++- src/components/VideoEditor.tsx | 20 +- src/components/VideoPreview.tsx | 372 ++++++++++++++++++++++---------- src/hooks/useVideoEditor.ts | 4 +- src/lib/ffmpeg.ts | 154 ++++++++----- src/lib/ffmpeg.worker.ts | 277 +++++++++++++++++------- src/lib/types.ts | 11 +- 8 files changed, 609 insertions(+), 294 deletions(-) diff --git a/src/components/ImageOverlay.tsx b/src/components/ImageOverlay.tsx index ac928206..8b772116 100644 --- a/src/components/ImageOverlay.tsx +++ b/src/components/ImageOverlay.tsx @@ -1,24 +1,23 @@ import Image from "next/image"; import { useRef, useState, useEffect } from "react"; -import { OverlayPosition } from "@/lib/types"; import { ArrowUpLeft, ArrowUpRight, ArrowDownLeft, ArrowDownRight, Upload, Trash2, FileImage } from "lucide-react"; interface ImageOverlayPanelProps { overlayFile: File | null; setOverlayFile: (file: File | null) => void; - overlayPosition: OverlayPosition; - setOverlayPosition: (p: OverlayPosition) => void; + overlayPosition: { x: number; y: number }; + setOverlayPosition: (p: { x: number; y: number }) => void; overlaySize: number; setOverlaySize: (v: number) => void; overlayOpacity: number; setOverlayOpacity: (v: number) => void; } -const POSITIONS: { value: OverlayPosition; icon: React.ReactNode; label: string }[] = [ - { value: "top-left", icon: , label: "TL" }, - { value: "top-right", icon: , label: "TR" }, - { value: "bottom-left", icon: , label: "BL" }, - { value: "bottom-right", icon: , label: "BR" }, +const POSITIONS: { value: { x: number; y: number }; icon: React.ReactNode; label: string }[] = [ + { value: { x: 5, y: 5 }, icon: , label: "TL" }, + { value: { x: 85, y: 5 }, icon: , label: "TR" }, + { value: { x: 5, y: 85 }, icon: , label: "BL" }, + { value: { x: 85, y: 85 }, icon: , label: "BR" }, ]; export default function ImageOverlayPanel({ @@ -58,6 +57,10 @@ export default function ImageOverlayPanel({ const isMediumOpacity = overlayOpacity > 35 && overlayOpacity <= 75; const isSolidOpacity = overlayOpacity > 75; + const isPositionActive = (val: { x: number; y: number }) => { + return overlayPosition.x === val.x && overlayPosition.y === val.y; + }; + return (
@@ -140,16 +143,16 @@ export default function ImageOverlayPanel({
{POSITIONS.map(({ value, icon, label }) => (
); -} +} \ No newline at end of file diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index 26464263..c96b5be2 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -11,7 +11,7 @@ export function ThemeToggle() { }, []); const isDark = theme === "dark"; - + if (!mounted) { return ( )} - {/* Grid overlay button */} + {/* Grid Overlay Toggle Button */} {recipe && !isLoading && ( )} - {/* Compare button */} {recipe && !isLoading && ( @@ -356,7 +492,7 @@ export default function VideoPreview({