Date: Wed, 3 Jun 2026 17:55:55 +0530
Subject: [PATCH 5/8] fix: prevent unintended text preview movement
---
src/components/DraggableTextOverlays.tsx | 2 +-
src/components/VideoPreview.tsx | 24 +++++++++++-------------
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/src/components/DraggableTextOverlays.tsx b/src/components/DraggableTextOverlays.tsx
index d6a088aa..c6be44ba 100644
--- a/src/components/DraggableTextOverlays.tsx
+++ b/src/components/DraggableTextOverlays.tsx
@@ -216,7 +216,7 @@ export default function DraggableTextOverlays({
return (
{textOverlays.map((overlay) => {
diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx
index 04edaefa..34dc06f4 100644
--- a/src/components/VideoPreview.tsx
+++ b/src/components/VideoPreview.tsx
@@ -149,23 +149,21 @@ export default function VideoPreview({
videoRef.current.playbackRate = recipe.speed;
}, [recipe, videoRef]);
- /**
- * Track preview container dimensions for text overlay positioning.
- */
useEffect(() => {
- const updateDimensions = () => {
- if (previewContainerRef.current) {
- const rect = previewContainerRef.current.getBoundingClientRect();
+ const el = previewContainerRef.current;
+ if (!el) return;
+
+ const observer = new ResizeObserver((entries) => {
+ for (const entry of entries) {
setContainerDimensions({
- width: rect.width,
- height: rect.height,
+ width: entry.contentRect.width,
+ height: entry.contentRect.height,
});
}
- };
-
- updateDimensions();
- window.addEventListener("resize", updateDimensions);
- return () => window.removeEventListener("resize", updateDimensions);
+ });
+
+ observer.observe(el);
+ return () => observer.disconnect();
}, []);
useEffect(() => {
From 9009d745c63c4af1cd1b934bbd3369c5c2a2dd0a Mon Sep 17 00:00:00 2001
From: jayesh durge <179298187+jayesh-durge@users.noreply.github.com>
Date: Wed, 3 Jun 2026 18:21:12 +0530
Subject: [PATCH 6/8] implement image overlay preview
---
src/components/VideoEditor.tsx | 4 ++++
src/components/VideoPreview.tsx | 42 ++++++++++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx
index 411ca8e7..15e2d70d 100644
--- a/src/components/VideoEditor.tsx
+++ b/src/components/VideoEditor.tsx
@@ -443,6 +443,10 @@ return () => {
selectedTextId={selectedTextId}
onSelectText={setSelectedTextId}
onUpdateText={handleUpdateTextOverlay}
+ overlayFile={overlayFile}
+ overlayPosition={overlayPosition}
+ overlaySize={overlaySize}
+ overlayOpacity={overlayOpacity}
/>
diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx
index 34dc06f4..20fe0022 100644
--- a/src/components/VideoPreview.tsx
+++ b/src/components/VideoPreview.tsx
@@ -2,7 +2,7 @@
"use client";
import { useEffect, useRef, useState, useCallback, RefObject } from "react";
-import { EditRecipe, TextOverlay } from "@/lib/types";
+import { EditRecipe, TextOverlay, OverlayPosition } from "@/lib/types";
import { getPresetById } from "@/lib/presets";
import { cn } from "@/lib/utils";
import { Camera, Play, Pause, Volume2, VolumeX } from "lucide-react";
@@ -16,6 +16,10 @@ interface Props {
selectedTextId?: string | null;
onSelectText?: (id: string | null) => void;
onUpdateText?: (id: string, updates: Partial) => void;
+ overlayFile?: File | null;
+ overlayPosition?: OverlayPosition;
+ overlaySize?: number;
+ overlayOpacity?: number;
}
export default function VideoPreview({
@@ -25,6 +29,10 @@ export default function VideoPreview({
selectedTextId = null,
onSelectText,
onUpdateText,
+ overlayFile,
+ overlayPosition = "bottom-right",
+ overlaySize = 150,
+ overlayOpacity = 100,
}: Props) {
const lastId = useRef(0);
const urlRef = useRef(null);
@@ -35,6 +43,17 @@ export default function VideoPreview({
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [isMuted, setIsMuted] = useState(!recipe?.keepAudio);
+ const [overlayUrl, setOverlayUrl] = useState(null);
+
+ useEffect(() => {
+ if (!overlayFile) {
+ setOverlayUrl(null);
+ return;
+ }
+ const url = URL.createObjectURL(overlayFile);
+ setOverlayUrl(url);
+ return () => URL.revokeObjectURL(url);
+ }, [overlayFile]);
useEffect(() => {
setIsMuted(!recipe?.keepAudio);
@@ -201,6 +220,10 @@ export default function VideoPreview({
const targetH = preset ? preset.height : 9;
const targetRatio = targetW / targetH;
+
+ const scale = containerDimensions.width > 0 ? containerDimensions.width / targetW : 1;
+ const previewOverlaySize = overlaySize * scale;
+ const previewPadding = 20 * scale;
const overlay = (() => {
if (!recipe || !showOverlay || !preset) return null;
@@ -402,6 +425,23 @@ export default function VideoPreview({
)}
+ {/* IMAGE OVERLAY LAYER */}
+ {overlayUrl && containerDimensions.width > 0 && (
+

+ )}
+
{/* Draggable Text Overlays */}
{recipe && !isLoading && containerDimensions.width > 0 && (
Date: Wed, 3 Jun 2026 18:38:32 +0530
Subject: [PATCH 7/8] implemented preiview for Adjustments( Brightness,
Contrast, Saturation )
---
src/components/VideoPreview.tsx | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx
index 20fe0022..74ae075a 100644
--- a/src/components/VideoPreview.tsx
+++ b/src/components/VideoPreview.tsx
@@ -1,7 +1,7 @@
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-noninteractive-element-interactions */
"use client";
-import { useEffect, useRef, useState, useCallback, RefObject } from "react";
+import { useEffect, useRef, useState, useCallback, useMemo, RefObject } from "react";
import { EditRecipe, TextOverlay, OverlayPosition } from "@/lib/types";
import { getPresetById } from "@/lib/presets";
import { cn } from "@/lib/utils";
@@ -44,6 +44,13 @@ export default function VideoPreview({
const [currentTime, setCurrentTime] = useState(0);
const [isMuted, setIsMuted] = useState(!recipe?.keepAudio);
const [overlayUrl, setOverlayUrl] = useState(null);
+ const adjustmentFilter = useMemo(() => {
+ const brightness = 1 + (recipe?.brightness ?? 0);
+ const contrast = recipe?.contrast ?? 1;
+ const saturation = recipe?.saturation ?? 1;
+
+ return `brightness(${brightness}) contrast(${contrast}) saturate(${saturation})`;
+ }, [recipe?.brightness, recipe?.contrast, recipe?.saturation]);
useEffect(() => {
if (!overlayFile) {
@@ -312,6 +319,7 @@ export default function VideoPreview({