diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx
index 35bb9460..209f706b 100644
--- a/src/components/DownloadResult.tsx
+++ b/src/components/DownloadResult.tsx
@@ -13,6 +13,7 @@ const SHARE_TWEET_TEXT =
interface Props {
result: ExportResult;
+ exportHistory: ExportHistoryItem[]; // NEW: Added history array prop
onReset: () => void;
soundOnCompletion: boolean;
}
@@ -41,6 +42,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion }: P
return (
+ {/* Current Export Success Header */}
@@ -51,6 +53,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion }: P
+ {/* Current Export Details */}
Resolution
@@ -143,6 +146,39 @@ export default function DownloadResult({ result, onReset, soundOnCompletion }: P
Share on X
+
+ {/* 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
+
+
+ ))}
+
+
+ )}
);
-}
+}
\ No newline at end of file
diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx
index d43f5d18..a921018a 100644
--- a/src/components/VideoEditor.tsx
+++ b/src/components/VideoEditor.tsx
@@ -74,6 +74,7 @@ export default function VideoEditor() {
}
}, [status]);
+
const isProcessing = status === "loading-engine" || status === "exporting";
const videoSrc = useMemo(
diff --git a/src/hooks/useVideoEditor.ts b/src/hooks/useVideoEditor.ts
index a2283128..981ed859 100644
--- a/src/hooks/useVideoEditor.ts
+++ b/src/hooks/useVideoEditor.ts
@@ -147,6 +147,39 @@ export function useVideoEditor() {
const [overlaySize, setOverlaySize] = useState(150);
const [overlayOpacity, setOverlayOpacity] = useState(100);
+ // --- LocalStorage Persistence Logic ---
+
+ // 1. Load saved settings on mount (Client-side only)
+ useEffect(() => {
+ const savedToggle = localStorage.getItem('rememberSettings') === 'true';
+ setRememberSettings(savedToggle);
+
+ if (savedToggle) {
+ const savedRecipe = localStorage.getItem('videoEditorRecipe');
+ if (savedRecipe) {
+ try {
+ const parsedRecipe = JSON.parse(savedRecipe);
+ setRecipe(parsedRecipe);
+ } catch (error) {
+ console.error("Failed to parse saved video recipe", error);
+ }
+ }
+ }
+ }, []);
+
+ // 2. Save settings when recipe or toggle changes
+ useEffect(() => {
+ localStorage.setItem('rememberSettings', String(rememberSettings));
+
+ if (rememberSettings) {
+ localStorage.setItem('videoEditorRecipe', JSON.stringify(recipe));
+ } else {
+ localStorage.removeItem('videoEditorRecipe');
+ }
+ }, [rememberSettings, recipe]);
+
+ // --- End LocalStorage Logic ---
+
const updateRecipe = useCallback((patch: Partial
) => {
setRecipe((prev) => ({ ...prev, ...patch }));
}, []);
@@ -304,6 +337,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;
diff --git a/src/lib/types.ts b/src/lib/types.ts
index bf167094..8df7b392 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -86,4 +86,4 @@ export const MAX_FILE_SIZE =
2 * 1024 * 1024 * 1024;
export const WARNING_FILE_SIZE =
- 500 * 1024 * 1024; // 500MB
\ No newline at end of file
+ 500 * 1024 * 1024; // 500MB