Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import PrivacyBanner from "@/components/PrivacyBanner";
import VideoEditor from "@/components/VideoEditor";
import Footer from "@/components/Footer";
import Footer from "@/components/Footer";

export default function Home() {
return (
Expand All @@ -8,16 +9,17 @@ export default function Home() {
href="https://github.com/magic-peach/reframe"
target="_blank"
rel="noopener noreferrer"
className="hidden min-[300px]:flex fixed top-4 right-16 z-50 items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--surface)] text-[10px] font-heading font-semibold uppercase tracking-wider hover:bg-opacity-90 transition-all" >
className="hidden min-[300px]:flex fixed top-4 right-16 z-50 items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--surface)] text-[10px] font-heading font-semibold uppercase tracking-wider transition-all duration-200 ease-in-out hover:scale-105 hover:shadow-[0_0_10px_rgba(255,255,255,0.15)] hover:bg-white/10"
>
⭐ Star on GitHub
</a>

<main id="main-content" tabIndex={-1}>
<PrivacyBanner />
<VideoEditor />
</main>

<Footer />
</>
);
}

}
59 changes: 59 additions & 0 deletions src/components/PrivacyBanner.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="mb-4 rounded-xl border border-[var(--border)] bg-[var(--surface)] p-4 shadow-sm">
<div className="flex items-start justify-between gap-4">
<div>
<h3 className="text-sm font-semibold text-[var(--foreground)]">
Your videos never leave your device.
</h3>

<p className="mt-1 text-sm text-[var(--muted)]">
Processing is done entirely in your browser using FFmpeg.wasm.
No server, no upload, no account required.
</p>
</div>

<button
type="button"
onClick={handleClose}
aria-label="Dismiss privacy banner"
className="rounded-md p-1 text-[var(--muted)] transition-colors hover:text-[var(--accent)]"
>
</button>
</div>
</div>
);
}
Loading