diff --git a/src/app/page.tsx b/src/app/page.tsx index 637feae3..ce473c3f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,6 @@ import VideoEditor from "@/components/VideoEditor"; -import Footer from "@/components/Footer"; +import Footer from "@/components/Footer"; +import PrivacyBanner from "@/components/PrivacyBanner"; export default function Home() { return ( @@ -14,6 +15,7 @@ export default function Home() {
+
@@ -21,4 +23,4 @@ export default function Home() { ); } - + diff --git a/src/components/PrivacyBanner.tsx b/src/components/PrivacyBanner.tsx new file mode 100644 index 00000000..e7dd86c9 --- /dev/null +++ b/src/components/PrivacyBanner.tsx @@ -0,0 +1,59 @@ +"use client"; + +import { useEffect, useState } from "react"; + +const STORAGE_KEY = "reframe-privacy-banner-dismissed"; + +export default function PrivacyBanner() { + const [visible, setVisible] = useState(false); + + useEffect(() => { + const dismissedAt = localStorage.getItem(STORAGE_KEY); + + if (!dismissedAt) { + setVisible(true); + return; + } + + const sevenDays = 7 * 24 * 60 * 60 * 1000; + const expired = Date.now() - Number(dismissedAt) > sevenDays; + + if (expired) { + localStorage.removeItem(STORAGE_KEY); + setVisible(true); + } + }, []); + + const handleClose = () => { + localStorage.setItem(STORAGE_KEY, Date.now().toString()); + setVisible(false); + }; + + if (!visible) return null; + + return ( +
+
+
+

+ Your videos never leave your device. +

+ +

+ Processing is done entirely in your browser using FFmpeg.wasm. + No server, no upload, no account required. +

+
+ + +
+
+ ); +} diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx index 71095330..bd8cf48a 100644 --- a/src/components/VideoPreview.tsx +++ b/src/components/VideoPreview.tsx @@ -4,9 +4,11 @@ import { useEffect, useRef, useState, useCallback, RefObject } from "react"; import { EditRecipe } from "@/lib/types"; import { getPresetById } from "@/lib/presets"; + import { cn } from "@/lib/utils"; import { Camera } from "lucide-react"; + interface Props { file: File | null; recipe?: EditRecipe; @@ -113,6 +115,40 @@ export default function VideoPreview({ file, recipe, videoRef }: Props) { videoRef.current.playbackRate = recipe.speed; }, [recipe, videoRef]); + const preset = + recipe.preset !== "custom" + ? getPresetById(recipe.preset) + : null; + + const previewWidth = + recipe.preset === "custom" + ? recipe.customWidth || 1920 + : preset?.width || 1920; + + const previewHeight = + recipe.preset === "custom" + ? recipe.customHeight || 1080 + : preset?.height || 1080; + + const aspectRatio = `${previewWidth}/${previewHeight}`; + return ( +
+ +
); -} \ No newline at end of file +}