diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx index a12c1f41..3ff1eec1 100644 --- a/src/components/VideoEditor.tsx +++ b/src/components/VideoEditor.tsx @@ -234,15 +234,14 @@ export default function VideoEditor() { useEffect(() => { if (!file) return; - persistOverlayState(localStorage, { - overlayPosition, - overlaySize, - overlayOpacity, - }); - }, [overlayPosition, overlaySize, overlayOpacity, file]); +persistOverlayState(localStorage, { + overlayPosition, + overlaySize, + overlayOpacity, +}); +}, [overlayPosition, overlaySize, overlayOpacity, file]); const [selectedTextId, setSelectedTextId] = useState(null); const [openSections, setOpenSections] = useState({ - resize: true, trim: false, rotation: false, text: false, @@ -250,16 +249,16 @@ export default function VideoEditor() { export: false, }); useEffect(() => { - const restored = loadOverlayState(localStorage, { - overlayPosition: initialOverlayState.current.overlayPosition, - overlaySize: initialOverlayState.current.overlaySize, - overlayOpacity: initialOverlayState.current.overlayOpacity, - }); +const restored = loadOverlayState(localStorage, { + overlayPosition: initialOverlayState.current.overlayPosition, + overlaySize: initialOverlayState.current.overlaySize, + overlayOpacity: initialOverlayState.current.overlayOpacity, +}); - if (restored.overlayPosition) setOverlayPosition(restored.overlayPosition); - if (typeof restored.overlaySize === "number") setOverlaySize(restored.overlaySize); - if (typeof restored.overlayOpacity === "number") setOverlayOpacity(restored.overlayOpacity); - }, [setOverlayOpacity, setOverlayPosition, setOverlaySize]); +if (restored.overlayPosition) setOverlayPosition(restored.overlayPosition); +if (typeof restored.overlaySize === "number") setOverlaySize(restored.overlaySize); +if (typeof restored.overlayOpacity === "number") setOverlayOpacity(restored.overlayOpacity); +}, [setOverlayOpacity, setOverlayPosition, setOverlaySize]); const toggleSection = (key: keyof typeof openSections) => setOpenSections((prev) => ({ ...prev, [key]: !prev[key] })); @@ -366,7 +365,7 @@ return () => {
-
+
{
No login. No ads. 100% private - your video never leaves your device.
-
+
-
-
+
+
{!file && ( -
+

Upload a video to get started

Supports MP4, MOV, WebM and more

)} {file && ( -
+
{ onUpdateText={handleUpdateTextOverlay} /> -
+
{

)} {file && ( -
-
- } - title="Trim" - isOpen={openSections.trim} - onToggle={() => toggleSection("trim")} - delay={50} - > - +
+
+
+
+

Core edits

+

Trim, rotate, text, and audio controls.

+
+
+ } title="Trim" isOpen={openSections.trim} onToggle={() => toggleSection("trim")} delay={50}> + - - } - title="Rotation" - isOpen={openSections.rotation} - onToggle={() => toggleSection("rotation")} - delay={100} - > + } title="Rotation" isOpen={openSections.rotation} onToggle={() => toggleSection("rotation")} delay={100}> - - } - title="Text Overlay" - isOpen={openSections.text} - onToggle={() => toggleSection("text")} - delay={110} - > - + } title="Text Overlay" isOpen={openSections.text} onToggle={() => toggleSection("text")} delay={110}> + -
-
- } - title="Audio & Speed" - isOpen={openSections.audio} - onToggle={() => toggleSection("audio")} - delay={150} - > + } title="Audio & Speed" isOpen={openSections.audio} onToggle={() => toggleSection("audio")} delay={150}> -
} - title="Adjustments" - delay={175} - > +
+ +
+
+

Output setup

+

Choose the target format, frame, overlays, and export options.

+
+
} title="Resize & Aspect Ratio" delay={50}> + {recommendedPreset && ( +
+

+ We detected a {recommendedPreset.label.replace(/\s/g, "")} video → Recommended: {(recommendedPreset.platform.split("·")[0] ?? "").trim()} ({recommendedPreset.label.replace(/\s/g, "")}) +

+
+ )} +
+ + +
+
+
} title="Adjustments" delay={175}>
- {/* Brightness */}
-
- updateRecipe({ brightness: Number(e.target.value) })} - aria-label="Adjust brightness" - className="w-full accent-film-600" - /> + updateRecipe({ brightness: Number(e.target.value) })} aria-label="Adjust brightness" className="w-full accent-film-600" />
- {/* Contrast */}
-
- updateRecipe({ contrast: Number(e.target.value) })} - aria-label="Adjust contrast" - className="w-full accent-film-600" - /> + updateRecipe({ contrast: Number(e.target.value) })} aria-label="Adjust contrast" className="w-full accent-film-600" />
- {/* Saturation */}
-
- updateRecipe({ saturation: Number(e.target.value) })} - aria-label="Adjust saturation" - className="w-full accent-film-600" - /> + updateRecipe({ saturation: Number(e.target.value) })} aria-label="Adjust saturation" className="w-full accent-film-600" />
} title="Output format" delay={190}>
- } - title="Export" - isOpen={openSections.export} - onToggle={() => toggleSection("export")} - delay={200} - > - -
} title="Image overlay" delay={120}> { setOverlayOpacity={setOverlayOpacity} />
+ } title="Export" isOpen={openSections.export} onToggle={() => toggleSection("export")} delay={200}> + +
)} @@ -668,7 +589,7 @@ return () => {
{!file && ( @@ -681,29 +602,8 @@ return () => {

)} -
- } - title="Resize & Aspect Ratio" - isOpen={openSections.resize} - onToggle={() => toggleSection("resize")} - delay={50} - > - {recommendedPreset && ( -
-

- We detected a {recommendedPreset.label.replace(/\s/g, "")} video → Recommended: {(recommendedPreset.platform.split("·")[0] ?? "").trim()} ({recommendedPreset.label.replace(/\s/g, "")}) -

-
- )} -
- - -
-
- -
+
+
- )} - - {/* Grid overlay button */} - {recipe && !isLoading && ( - - )} - - {/* Compare button */} - {recipe && !isLoading && ( - - )} - - {/* Grab frame button */} - {!isLoading && ( - +
+
+ + + +
+ +
+ + + +
+
)}