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
11 changes: 0 additions & 11 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,8 @@ on:
# - "src/**/*.js"
# - "src/**/*.jsx"

permissions:
contents: read
pull-requests: read
issues: read
id-token: write

jobs:
claude-review:
if: |
github.event_name == 'pull_request' &&
(github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR')
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/claude-manager.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ on:

jobs:
claude-pr-review:
if: |
github.event_name == 'pull_request' &&
(github.event.pull_request.author_association == 'OWNER' ||
github.event.pull_request.author_association == 'MEMBER' ||
github.event.pull_request.author_association == 'COLLABORATOR')
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,6 @@ Open [http://localhost:3000](http://localhost:3000) — component changes reflec
bun run build
```

## Theme (Dark Mode)

Reframe supports a theme toggle in the header that switches between light, dark, and a high-contrast mode. The app remembers your choice in localStorage and will respect your system preference when no explicit choice is saved.

- Use the theme button in the top-right to switch modes.
- The selected theme is persisted across sessions.
- You can override the theme manually in the browser console by setting `localStorage.setItem('theme', 'dark')` (values: `light`, `dark`, `high-contrast`) and reloading the page.


Outputs a static site to `out/` — deploy to Vercel, Netlify, GitHub Pages, or any static host.

---
Expand Down
215 changes: 138 additions & 77 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"dependencies": {
"@ffmpeg/ffmpeg": "^0.12.10",
"@ffmpeg/util": "^0.12.2",
"@mantine/core": "7",
"@mantine/hooks": "7",
"clsx": "^2.1.1",
"focus-trap-react": "^12.0.1",
"jszip": "^3.10.1",
Expand Down
49 changes: 42 additions & 7 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,52 @@ body {
transition: background-color 0.3s ease, color 0.3s ease;
}

/* Smooth transitions for all themed elements */
*,
*::before,
*::after {
transition-property: background-color, border-color, color, fill, stroke;
transition-timing-function: ease;
transition-duration: 200ms;
/* ── Animations ── */
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}

<<<<<<< HEAD
.animate-shimmer {
animation: shimmer 2s infinite linear;
}

@keyframes fade-in {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.animate-fade-in {
animation: fade-in 0.4s ease-out forwards;
}

@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.animate-spin {
animation: spin 1s linear infinite;
}
=======
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 4px;
}
>>>>>>> upstream/main
75 changes: 56 additions & 19 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import type { Metadata } from "next";
import { Bebas_Neue, Syne, DM_Sans } from "next/font/google";
import { ColorSchemeScript, MantineProvider, createTheme } from "@mantine/core";
import "@mantine/core/styles.css";
import ErrorBoundary from "@/components/ErrorBoundary";
import "./globals.css";
import { ThemeProvider } from "@/components/ThemeProvider";
import { ThemeToggle } from "@/components/ThemeToggle";
import ScrollToTop from "@/components/ScrollToTop";
import BrandLogo from "@/components/BrandLogo";

const bebasNeue = Bebas_Neue({
weight: "400",
subsets: ["latin"],
variable: "--font-bebas",
});

const syne = Syne({
subsets: ["latin"],
variable: "--font-syne",
});

const dmSans = DM_Sans({
subsets: ["latin"],
variable: "--font-dm-sans",
});

export const metadata: Metadata = {
title: "Reframe — Resize, trim, and export videos in your browser",
description: "Free, open-source video editor that runs entirely in your browser. No login, no uploads, no ads. Resize for any platform, trim, rotate, adjust speed, and export.",
Expand Down Expand Up @@ -41,14 +59,31 @@ export const metadata: Metadata = {
},
};

const theme = createTheme({
fontFamily: "var(--font-dm-sans), sans-serif",
headings: {
fontFamily: "var(--font-syne), sans-serif",
},
colors: {
// Customizing Mantine colors to match Reframe's brand if needed
// For now, using default dark/light handling
},
});

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<html lang="en" suppressHydrationWarning className={`${bebasNeue.variable} ${syne.variable} ${dmSans.variable}`}>
<head>
<<<<<<< HEAD
<ColorSchemeScript defaultColorScheme="dark" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
=======
>>>>>>> upstream/main
<script
dangerouslySetInnerHTML={{
__html: `(function() {
Expand All @@ -74,24 +109,26 @@ export default function RootLayout({
>
Skip to main content
</a>
<ThemeProvider>
<ErrorBoundary>
<header
role="banner"
className="sticky top-0 z-50 flex items-center justify-between px-6 py-3 border-b border-[var(--border)] bg-[var(--bg)]"
>
<div className="flex items-center gap-2">
<BrandLogo size={24} />
<h1 className="text-lg font-semibold">Reframe</h1>
</div>
<ThemeToggle />
</header>
<main id="main-content" tabIndex={-1}>
{children}
</main>
<ScrollToTop />
</ErrorBoundary>
</ThemeProvider>
<MantineProvider theme={theme} defaultColorScheme="dark">
<ThemeProvider>
<ErrorBoundary>
<header
role="banner"
className="sticky top-0 z-50 flex items-center justify-between px-6 py-3 border-b border-[var(--border)] bg-[var(--bg)]"
>
<div className="flex items-center gap-2">
<BrandLogo size={24} />
<h1 className="text-lg font-semibold">Reframe</h1>
</div>
<ThemeToggle />
</header>
<main id="main-content" tabIndex={-1}>
{children}
</main>
<ScrollToTop />
</ErrorBoundary>
</ThemeProvider>
</MantineProvider>
</body>
</html>
);
Expand Down
3 changes: 2 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import VideoEditor from "@/components/VideoEditor";
import Footer from "@/components/Footer";
import Footer from "@/components/Footer";

export default function Home() {
return (
Expand All @@ -21,3 +21,4 @@ export default function Home() {
</>
);
}

2 changes: 1 addition & 1 deletion src/components/AudioSpeedControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default function AudioSpeedControl({ recipe, onChange }: Props) {
htmlFor="speed-control"
className="text-sm font-heading font-semibold uppercase tracking-wider text-[var(--muted)] flex items-center gap-2"
>
<Gauge size={10} aria-hidden="true" /> Speed
<Gauge size={10} aria-hidden="true" /> Speed
</label>

<div className="text-right">
Expand Down
2 changes: 1 addition & 1 deletion src/components/BrandLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export default function BrandLogo({ className, size = 32 }: BrandLogoProps) {
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
className={cn("shrink-0 text-film-600", className)}
>
<rect x="3" y="3" width="18" height="18" rx="3" stroke="currentColor" strokeWidth="2" />
<path d="M10 8L16 12L10 16V8Z" fill="currentColor" />
{/* Notches */}
<circle cx="3" cy="7" r="1.5" fill="var(--surface)" />
<circle cx="3" cy="12" r="1.5" fill="var(--surface)" />
<circle cx="3" cy="17" r="1.5" fill="var(--surface)" />
Expand Down
6 changes: 3 additions & 3 deletions src/components/DownloadResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
useEffect(() => {
if (soundOnCompletion) {
const audio = new Audio("/sounds/export-complete.mp3");
audio.play().catch((err) => console.error(err));
audio.play().catch(console.error);
}
}, [soundOnCompletion]);
const handleReset = () => {
Expand Down Expand Up @@ -123,7 +123,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
if (!isValid) e.preventDefault();
}}
>
<Download size={15} aria-hidden="true" />
<Download size={15} aria-hidden="true" />
Download {result.format.toUpperCase()}
</a>
<NativeShareButton
Expand All @@ -147,7 +147,7 @@ export default function DownloadResult({ result, onReset, soundOnCompletion, onT
onClick={handleReset}
className="flex items-center gap-2 px-4 py-3 border border-[var(--border)] text-[var(--muted)] text-sm rounded-lg hover:bg-[var(--bg)] transition-colors"
>
<RotateCcw size={14} aria-hidden="true" />
<RotateCcw size={14} aria-hidden="true" />
New
</button>
<a
Expand Down
Loading