From d8b48d9f481873cf30f7fd0539a1db242767a389 Mon Sep 17 00:00:00 2001 From: Akshar Sawhney Date: Sat, 16 May 2026 11:05:21 +0530 Subject: [PATCH 1/3] Update VideoEditor.tsx --- src/components/VideoEditor.tsx | 98 ++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx index 917c9611..e7fc5212 100644 --- a/src/components/VideoEditor.tsx +++ b/src/components/VideoEditor.tsx @@ -49,6 +49,7 @@ export default function VideoEditor() { handleFileSelect, handleExport, cancelExport, reset, } = useVideoEditor(); + const isProcessing = status === "loading-engine" || status === "exporting"; return ( @@ -92,6 +93,11 @@ export default function VideoEditor() { )} + {file && file.size > 100 * 1024 * 1024 && ( +

+ ⚠️ Large file — processing may take several minutes +

+ )} {file && (
} title="Audio & Speed" delay={150}> + +
} title="Adjustments" delay={175}> +
+ {/* Brightness */} +
+
+ Brightness + +
+ updateRecipe({ brightness: Number(e.target.value) })} + className="w-full" + /> +
+ + {/* Contrast */} +
+
+ Contrast + +
+ updateRecipe({ contrast: Number(e.target.value) })} + className="w-full" + /> +
+ + {/* Saturation */} +
+
+ Saturation + +
+ updateRecipe({ saturation: Number(e.target.value) })} + className="w-full" + /> +
+
+
+
} title="Export quality" delay={200}> - +
@@ -122,10 +206,18 @@ export default function VideoEditor() { className="flex items-start gap-3 p-4 bg-film-50 border border-film-200 rounded-xl text-film-800 text-sm animate-fade-in" > -
+

Error

{error}

+ {!error.includes("Validation Failed") && ( + + )}
)} @@ -181,7 +273,7 @@ export default function VideoEditor() { href="https://github.com/magic-peach/reframe" target="_blank" rel="noopener noreferrer" - className="flex items-center gap-1.5 text-[11px] font-heading font-medium text-[var(--muted)] hover:text-film-600 transition-colors" + className="min-h-[44px] min-w-[44px] flex items-center gap-1.5 px-2 text-[11px] font-heading font-medium text-[var(--muted)] hover:text-film-600 transition-colors" > Source on GitHub From baf8a544d4f4fd744da857278fdea13a4083a551 Mon Sep 17 00:00:00 2001 From: aksharsawhney74-rgb Date: Tue, 19 May 2026 23:09:22 +0530 Subject: [PATCH 2/3] feat: add export history tracking with safe memory management --- src/components/DownloadResult.tsx | 46 ++++++++++++++++++++++++++-- src/hooks/useVideoEditor.ts | 50 ++++++++++++++++++++++++++++++- src/lib/types.ts | 8 +++++ 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx index 4103cc3f..cee242bb 100644 --- a/src/components/DownloadResult.tsx +++ b/src/components/DownloadResult.tsx @@ -1,8 +1,8 @@ "use client"; -import { ExportResult } from "@/lib/types"; +import { ExportResult, ExportHistoryItem } from "@/lib/types"; import { formatBytes } from "@/lib/ffmpeg"; -import { Download, RotateCcw, Share2 } from "lucide-react"; +import { Download, RotateCcw, Share2, History } from "lucide-react"; import LottiePlayer from "./LottiePlayer"; import successAnim from "@/lib/lottie/success.json"; @@ -11,15 +11,17 @@ const SHARE_TWEET_TEXT = interface Props { result: ExportResult; + exportHistory: ExportHistoryItem[]; // NEW: Added history array prop onReset: () => void; } -export default function DownloadResult({ result, onReset }: Props) { +export default function DownloadResult({ result, exportHistory, onReset }: Props) { const filename = `reframe_${result.width}x${result.height}.${result.format}`; const shareHref = `https://x.com/intent/tweet?text=${encodeURIComponent(SHARE_TWEET_TEXT)}`; return (
+ {/* Current Export Success Header */}
@@ -33,6 +35,7 @@ export default function DownloadResult({ result, onReset }: Props) {
+ {/* Current Export Details */}

Resolution

@@ -44,6 +47,7 @@ export default function DownloadResult({ result, onReset }: Props) {
+ {/* Primary Actions */} + + {/* NEW: Export History Panel */} + {exportHistory && exportHistory.length > 0 && ( +
+ + + + Recent Exports ({exportHistory.length}) + + Click to view + +
+ {exportHistory.map((item) => ( +
+
+ + {new Date(item.timestamp).toLocaleTimeString()} + + {item.result.width}x{item.result.height} • {formatBytes(item.result.size)} +
+ + + Save + +
+ ))} +
+
+ )}
); } diff --git a/src/hooks/useVideoEditor.ts b/src/hooks/useVideoEditor.ts index 0e0413a3..736f7789 100644 --- a/src/hooks/useVideoEditor.ts +++ b/src/hooks/useVideoEditor.ts @@ -1,7 +1,8 @@ "use client"; import { useState, useCallback, useEffect, useRef } from "react"; -import { EditRecipe, ExportResult, ExportStatus, DEFAULT_RECIPE } from "@/lib/types"; +// NEW: Make sure to import ExportHistoryItem here! +import { EditRecipe, ExportResult, ExportStatus, DEFAULT_RECIPE, ExportHistoryItem } from "@/lib/types"; import { loadFFmpeg, exportVideo, terminateFFmpeg } from "@/lib/ffmpeg"; const DEFAULT_TITLE = "Reframe — Resize, trim, and export videos in your browser"; @@ -52,6 +53,16 @@ export function useVideoEditor() { const [file, setFile] = useState(null); const [duration, setDuration] = useState(0); const [recipe, setRecipe] = useState(DEFAULT_RECIPE); +<<<<<<< Updated upstream +======= + + // New state for persisting settings + const [rememberSettings, setRememberSettings] = useState(false); + + // NEW: State for tracking the session's export history + const [exportHistory, setExportHistory] = useState([]); + +>>>>>>> Stashed changes const [status, setStatus] = useState("idle"); const [progress, setProgress] = useState(0); const [result, setResult] = useState(null); @@ -133,6 +144,27 @@ export function useVideoEditor() { if (exportCancelledRef.current) return; setResult(exportResult); + + // NEW: Update export history and safely manage memory limits + setExportHistory((prevHistory) => { + const newItem: ExportHistoryItem = { + id: Date.now().toString(), + timestamp: Date.now(), + result: exportResult, + recipe: recipe, // Captures the exact settings used + }; + + const updatedHistory = [newItem, ...prevHistory]; + + // MEMORY LEAK MANAGER: Revoke URL of the 6th item before throwing it away + if (updatedHistory.length > 5) { + URL.revokeObjectURL(updatedHistory[5].result.blobUrl); + console.log("Memory Released: Oldest history blob revoked."); + } + + return updatedHistory.slice(0, 5); // Ensure only 5 are kept in state + }); + setStatus("done"); } catch (err) { if (exportCancelledRef.current) return; @@ -195,6 +227,7 @@ export function useVideoEditor() { setProgress(0); setResult(null); setError(null); + // Note: We deliberately do NOT clear exportHistory here so it persists across "New" clicks in the same session! }, []); // Development-only memory monitoring during export @@ -212,6 +245,15 @@ export function useVideoEditor() { return () => clearInterval(interval); }, [status]); + // Clean up ALL blobs when the editor unmounts (closes session) to be extra safe + useEffect(() => { + return () => { + exportHistory.forEach((item) => { + URL.revokeObjectURL(item.result.blobUrl); + }); + }; + }, [exportHistory]); + return { file, duration, @@ -220,6 +262,12 @@ export function useVideoEditor() { progress, result, error, +<<<<<<< Updated upstream +======= + rememberSettings, + exportHistory, // NEW: Expose history to the UI + setRememberSettings, +>>>>>>> Stashed changes updateRecipe, handleFileSelect, handleExport, diff --git a/src/lib/types.ts b/src/lib/types.ts index 726d1120..91a5a393 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -40,3 +40,11 @@ export const DEFAULT_RECIPE: EditRecipe = { speed: 1, quality: 23, }; + +// NEW: Added for Issue #105 +export interface ExportHistoryItem { + id: string; + timestamp: number; + result: ExportResult; + recipe: EditRecipe; +} \ No newline at end of file From cb721b0fddd6449d3c889244b4fd5b82d97adde7 Mon Sep 17 00:00:00 2001 From: aksharsawhney74-rgb Date: Tue, 19 May 2026 23:14:39 +0530 Subject: [PATCH 3/3] fix: resolve stash pop merge conflicts --- src/components/DownloadResult.tsx | 16 +++++++----- src/hooks/useVideoEditor.ts | 42 +++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx index cee242bb..4cd6785a 100644 --- a/src/components/DownloadResult.tsx +++ b/src/components/DownloadResult.tsx @@ -57,13 +57,17 @@ export default function DownloadResult({ result, exportHistory, onReset }: Props Download {result.format.toUpperCase()} + + Preview +