@@ -183,9 +209,18 @@ function KeyboardShortcutsPanel() {
height="12"
viewBox="0 0 12 12"
fill="none"
- className={cn("text-[var(--muted)] transition-transform duration-200", open && "rotate-180")}
+ className={cn(
+ "text-[var(--muted)] transition-transform duration-200",
+ open && "rotate-180"
+ )}
>
-
+
@@ -208,15 +243,31 @@ function KeyboardShortcutsPanel() {
export default function VideoEditor() {
const {
- file, duration, recipe, status, progress,
- result, error, exportStartedAt, updateRecipe,
- handleFileSelect, fileError, handleExport, cancelExport, reset, resetSettings,
+ file,
+ duration,
+ recipe,
+ status,
+ progress,
+ result,
+ error,
+ exportStartedAt,
+ updateRecipe,
+ handleFileSelect,
+ fileError,
+ handleExport,
+ cancelExport,
+ reset,
+ resetSettings,
videoRef,
seekTo,
- overlayFile, setOverlayFile,
- overlayPosition, setOverlayPosition,
- overlaySize, setOverlaySize,
- overlayOpacity, setOverlayOpacity,
+ overlayFile,
+ setOverlayFile,
+ overlayPosition,
+ setOverlayPosition,
+ overlaySize,
+ setOverlaySize,
+ overlayOpacity,
+ setOverlayOpacity,
recommendedPreset,
currentTime,
toggleSound,
@@ -288,7 +339,7 @@ export default function VideoEditor() {
const handleCopyLink = () => {
if (typeof window === "undefined") return;
- const encoded = btoa(JSON.stringify(recipe));
+ const encoded = encodeURIComponent(JSON.stringify(recipe));
const url = new URL(window.location.href);
url.searchParams.set("settings", encoded);
history.replaceState(null, "", url.toString());
@@ -300,7 +351,6 @@ export default function VideoEditor() {
useEffect(() => {
if (status === "done" && downloadRef.current) {
-
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
downloadRef.current.scrollIntoView({
behavior: prefersReducedMotion ? "instant" : "smooth",
@@ -309,7 +359,6 @@ export default function VideoEditor() {
}
}, [status]);
-
const isProcessing = status === "loading-engine" || status === "exporting";
const [isMac, setIsMac] = useState(false);
@@ -324,23 +373,19 @@ export default function VideoEditor() {
return 30;
}, [duration]);
- const videoSrc = useMemo(
- () => (file ? URL.createObjectURL(file) : null),
- [file]
- );
+ const videoSrc = useMemo(() => (file ? URL.createObjectURL(file) : null), [file]);
const exportSummary = useMemo(() => {
const preset = getPresetById(recipe.preset);
- const width = recipe.preset === "custom" ? recipe.customWidth : (preset?.width ?? recipe.customWidth);
- const height = recipe.preset === "custom" ? recipe.customHeight : (preset?.height ?? recipe.customHeight);
+ const width =
+ recipe.preset === "custom" ? recipe.customWidth : (preset?.width ?? recipe.customWidth);
+ const height =
+ recipe.preset === "custom" ? recipe.customHeight : (preset?.height ?? recipe.customHeight);
const framingLabel = recipe.framing === "fit" ? "Fit" : "Fill";
const speedLabel = `${recipe.speed}× speed`;
- const qualityLabel = recipe.quality <= 21
- ? "High"
- : recipe.quality <= 25
- ? "Balanced"
- : "Small file";
+ const qualityLabel =
+ recipe.quality <= 21 ? "High" : recipe.quality <= 25 ? "Balanced" : "Small file";
return `Exporting to ${width}×${height} ${recipe.format.toUpperCase()} • ${framingLabel} • ${speedLabel} • Quality: ${qualityLabel}`;
}, [recipe]);
@@ -367,58 +412,67 @@ export default function VideoEditor() {
{status === "error" && `Export failed: ${error}`}
-