From 85ad295970eb82c5c8f5d84699b75053696523fc Mon Sep 17 00:00:00 2001 From: Lathika226 Date: Tue, 19 May 2026 17:14:55 +0530 Subject: [PATCH 1/5] feat: batch / multi-preset export (#668) --- src/components/DownloadResult.tsx | 6 +- src/components/ExportOverlay.tsx | 8 ++- src/components/PresetSelector.tsx | 82 +++++++++++++++------ src/components/VideoEditor.tsx | 3 +- src/hooks/useVideoEditor.ts | 116 ++++++++++++++++++++++++------ src/lib/types.ts | 10 ++- 6 files changed, 175 insertions(+), 50 deletions(-) diff --git a/src/components/DownloadResult.tsx b/src/components/DownloadResult.tsx index 35bb9460..556686d4 100644 --- a/src/components/DownloadResult.tsx +++ b/src/components/DownloadResult.tsx @@ -18,7 +18,7 @@ interface Props { } export default function DownloadResult({ result, onReset, soundOnCompletion }: Props) { - const defaultName = `reframe_${result.width}x${result.height}`; + const defaultName = result.format === "zip" ? "reframe_batch" : `reframe_${result.width}x${result.height}`; const [name, setName] = useState(defaultName); const invalidCharRegex = /[<>:"/\\|?*]/; @@ -54,7 +54,9 @@ export default function DownloadResult({ result, onReset, soundOnCompletion }: P

Resolution

-

{result.width} × {result.height}

+

+ {result.format === "zip" ? "Multiple files" : `${result.width} × ${result.height}`} +

File size

diff --git a/src/components/ExportOverlay.tsx b/src/components/ExportOverlay.tsx index 62bbbd6f..36dad1fe 100644 --- a/src/components/ExportOverlay.tsx +++ b/src/components/ExportOverlay.tsx @@ -10,10 +10,11 @@ import TipCarousel from "./TipCarousel"; interface Props { status: ExportStatus; progress: number; + progressText?: string; onCancel?: () => void; } -export default function ExportOverlay({ status, progress, onCancel }: Props) { +export default function ExportOverlay({ status, progress, progressText, onCancel }: Props) { const visible = status === "loading-engine" || status === "exporting"; const previousFocusRef = useRef(null); const focusAnchorRef = useRef(null); @@ -92,6 +93,11 @@ export default function ExportOverlay({ status, progress, onCancel }: Props) {

{isLoading ? "Loading engine" : "Exporting"}

+ {progressText && ( +

+ {progressText} +

+ )}

{isLoading ? "Downloading the video engine. This only happens once." diff --git a/src/components/PresetSelector.tsx b/src/components/PresetSelector.tsx index 53821604..93996189 100644 --- a/src/components/PresetSelector.tsx +++ b/src/components/PresetSelector.tsx @@ -61,10 +61,19 @@ export default function PresetSelector({ recipe, onChange }: Props) { const handlePresetSelect = useCallback( (presetId: string) => { - onChange({ preset: presetId }); - setSearch(""); + if (recipe.isBatchExport) { + if (presetId === "custom") return; + const currentBatch = recipe.batchPresets || []; + const newBatch = currentBatch.includes(presetId) + ? currentBatch.filter((id) => id !== presetId) + : [...currentBatch, presetId]; + onChange({ batchPresets: newBatch }); + } else { + onChange({ preset: presetId }); + setSearch(""); + } }, - [onChange], + [onChange, recipe.isBatchExport, recipe.batchPresets], ); const handleWidthChange = useCallback( @@ -83,17 +92,35 @@ export default function PresetSelector({ recipe, onChange }: Props) { return (

-
-
- +
+
+
+ +
+ setSearch(e.target.value)} + className="w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] py-2 pl-9 pr-3 text-sm font-heading text-[var(--text)] transition-shadow focus:outline-none focus:ring-2 focus:ring-film-400" + />
- setSearch(e.target.value)} - className="w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] py-2 pl-9 pr-3 text-sm font-heading text-[var(--text)] transition-shadow focus:outline-none focus:ring-2 focus:ring-film-400" - /> +
@@ -103,7 +130,9 @@ export default function PresetSelector({ recipe, onChange }: Props) {
) : ( filteredPresets.map((preset) => { - const active = recipe.preset === preset.id; + const active = recipe.isBatchExport + ? (recipe.batchPresets || []).includes(preset.id) + : recipe.preset === preset.id; return (
- {recipe.preset === "custom" && ( + {recipe.preset === "custom" && !recipe.isBatchExport && (