From 0fd6563d0f373837ee228e514ca3164bd88f372f Mon Sep 17 00:00:00 2001 From: Akanksha Trehun Date: Tue, 3 Nov 2026 14:48:33 +0530 Subject: [PATCH] docs: rewrite README; deployment, usage and contributing guides; credits - README: about 120 lines. Working badges for reframe-oss/reframe (stars, issues, license, CI on main, Netlify status, Snyk), the live demo link, a demo placeholder, features, how it works, quick start, privacy, contributing, GSSoC, "Supported by" with self-hosted light/dark logos, and contrib.rocks. Removes the fork badge, the duplicated Vercel sections and the "works offline after first load" claim (there is no service worker). - docs/DEPLOYMENT.md (Netlify first, cross-origin isolation explained), docs/USAGE.md (flow, keyboard shortcuts as the code defines them, what to do when an export is slow). - CONTRIBUTING.md rewritten: Bun setup, claiming with /assign, branch names, Conventional Commits, PR checklist (lint, typecheck, test, build), review, project rules, GSSoC notes. Folds in CONTRIUBUTION.md, which is removed. - CODE_OF_CONDUCT.md: the [INSERT CONTACT METHOD] placeholder becomes a visible TODO with a working interim route. - "MIT LICENSE" renamed to LICENSE (so GitHub detects it); PHASE_1_MVP.md moved to docs/history/. - all-contributors config and .github/ALL_CONTRIBUTORS.md with instructions; nobody is added automatically. Co-Authored-By: Claude Opus 5.5 --- .all-contributorsrc | 14 + .github/ALL_CONTRIBUTORS.md | 48 ++ CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 616 +++++------------- CONTRIUBUTION.md | 113 ---- MIT LICENSE => LICENSE | 0 README.md | 558 +++------------- docs/ARCHITECTURE.md | 2 +- docs/DEPLOYMENT.md | 82 +++ docs/PROJECTS.md | 2 +- docs/USAGE.md | 71 ++ docs/assets/brand/chromatic-dark.svg | 5 + docs/assets/brand/chromatic-light.svg | 5 + docs/assets/brand/datadog-dark.png | Bin 0 -> 40549 bytes docs/assets/brand/datadog-light.png | Bin 0 -> 40549 bytes docs/assets/brand/gssoc-dark.svg | 5 + docs/assets/brand/gssoc-light.svg | 5 + docs/assets/brand/netlify-dark.svg | 1 + docs/assets/brand/netlify-light.svg | 1 + docs/assets/brand/snyk-dark.svg | 5 + docs/assets/brand/snyk-light.svg | 5 + docs/assets/demo-placeholder.svg | 5 + PHASE_1_MVP.md => docs/history/PHASE_1_MVP.md | 0 23 files changed, 520 insertions(+), 1025 deletions(-) create mode 100644 .all-contributorsrc create mode 100644 .github/ALL_CONTRIBUTORS.md delete mode 100644 CONTRIUBUTION.md rename MIT LICENSE => LICENSE (100%) create mode 100644 docs/DEPLOYMENT.md create mode 100644 docs/USAGE.md create mode 100644 docs/assets/brand/chromatic-dark.svg create mode 100644 docs/assets/brand/chromatic-light.svg create mode 100644 docs/assets/brand/datadog-dark.png create mode 100644 docs/assets/brand/datadog-light.png create mode 100644 docs/assets/brand/gssoc-dark.svg create mode 100644 docs/assets/brand/gssoc-light.svg create mode 100644 docs/assets/brand/netlify-dark.svg create mode 100644 docs/assets/brand/netlify-light.svg create mode 100644 docs/assets/brand/snyk-dark.svg create mode 100644 docs/assets/brand/snyk-light.svg create mode 100644 docs/assets/demo-placeholder.svg rename PHASE_1_MVP.md => docs/history/PHASE_1_MVP.md (100%) diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 000000000..ec24d532d --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,14 @@ +{ + "projectName": "reframe", + "projectOwner": "reframe-oss", + "repoType": "github", + "repoHost": "https://github.com", + "files": [".github/ALL_CONTRIBUTORS.md"], + "imageSize": 80, + "commit": false, + "commitConvention": "angular", + "contributorsPerLine": 7, + "linkToUsage": false, + "skipCi": true, + "contributors": [] +} diff --git a/.github/ALL_CONTRIBUTORS.md b/.github/ALL_CONTRIBUTORS.md new file mode 100644 index 000000000..f1eef0279 --- /dev/null +++ b/.github/ALL_CONTRIBUTORS.md @@ -0,0 +1,48 @@ +# Everyone who helps Reframe + +Code contributors appear automatically in the +[README](../README.md#contributors) through contrib.rocks. This page also +credits work that doesn't show up as commits: reviews, mentoring, design, +testing, translations, docs feedback, ideas and community support. It follows +the [all-contributors](https://allcontributors.org) specification. + + + + + + + + + +## Adding someone (maintainers) + +Nobody is added automatically. A maintainer adds each person, with their +agreement, using one of these: + +**With the all-contributors bot** (install the +[GitHub App](https://allcontributors.org/docs/en/bot/installation) on +reframe-oss/reframe once). Then comment on any issue or pull request: + +``` +@all-contributors please add @username for review, mentoring +``` + +The bot opens a pull request that updates `.all-contributorsrc` and the list +above. Review and merge it like any other PR. + +**With the CLI**, from a branch: + +```bash +bunx all-contributors-cli add username review,mentoring +bunx all-contributors-cli generate +``` + +Commit `.all-contributorsrc` and this file, and open a pull request. + +Common contribution types: `review`, `mentoring`, `design`, `ideas`, `bug`, +`userTesting`, `doc`, `translation`, `question` (answering questions), +`eventOrganizing`, `maintenance`, `security`, `a11y`. The full list is in the +[emoji key](https://allcontributors.org/docs/en/emoji-key). + +GSSoC points are separate: they come from the `gssoc:approved` label on merged +pull requests, not from this list. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 737de08aa..54a0ddafb 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -36,7 +36,7 @@ This Code of Conduct applies within all community spaces, and also applies when ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at **TODO: a private contact address will be added here.** Until then, use GitHub's [report abuse](https://docs.github.com/en/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam) form, or tag a maintainer ([@magic-peach](https://github.com/magic-peach)) on an issue asking them to get in touch. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2534f84d0..5ede0b948 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,469 +1,209 @@ -> ⭐ **If Reframe helped you, [star the repo](https://github.com/reframe-oss/reframe)** — it helps more people discover it! -> -> 💬 **Have a question or idea?** Head to [Discussions](https://github.com/reframe-oss/reframe/discussions) instead of opening an issue. -> -> 🟢 **Ready to contribute?** Check out our [Good First Issues](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22) — perfect for first-time contributors! - -*** - # Contributing to Reframe -First off, **thank you for considering contributing to Reframe**! 🎉 - -Whether you're fixing a typo, adding a feature, improving accessibility, or writing a test — every contribution makes Reframe better for everyone. - ---- - -## 👋 Want to contribute to reframe-oss/reframe? - -If you have a bug or an idea, read this guide before opening an issue. - -If you're ready to tackle some open issues, **[we've collected some good first issues for you](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22)**. - ---- +Thank you for helping. Fixes, features, tests, docs, design and accessibility +work are all welcome, from a typo to a new editing tool. -## Table of Contents +Questions and ideas go to +[Discussions](https://github.com/reframe-oss/reframe/discussions); bugs and +concrete tasks go to [issues](https://github.com/reframe-oss/reframe/issues). -- [Getting Started](#getting-started) -- [Development Setup](#development-setup) -- [Project Structure](#project-structure) -- [Finding Issues](#finding-issues) -- [Claiming Issues](#-claiming-issues) -- [Making Changes](#making-changes) -- [Submitting a Pull Request](#submitting-a-pull-request) -- [Visual Review (Chromatic)](#visual-review-chromatic) -- [Code Style](#code-style) -- [Development Tips](#development-tips) -- [GSSoC'26 Participants](#gssoc26-participants) -- [Code of Conduct](#code-of-conduct) +- [Set up](#set-up) +- [Claim an issue](#claim-an-issue) +- [Branches and commits](#branches-and-commits) +- [Before you open a pull request](#before-you-open-a-pull-request) +- [Pull requests and review](#pull-requests-and-review) +- [Project rules](#project-rules) +- [Development tips](#development-tips) +- [Reporting bugs and suggesting features](#reporting-bugs-and-suggesting-features) +- [GSSoC'26 participants](#gssoc26-participants) ---- +## Set up -## Getting Started - -### Prerequisites - -- [Git](https://git-scm.com) -- [Bun](https://bun.sh) (recommended) — or Node.js 18+ -- A modern browser (Chrome, Firefox, or Safari) - -### Fork and Clone +You need [Git](https://git-scm.com), [Bun](https://bun.sh) 1.x and a recent +Chrome, Firefox or Safari. This project uses **Bun and `bun.lock` only**: don't +use npm or yarn, and never commit `package-lock.json` or `yarn.lock`. ```bash -# 1. Fork the repo by clicking 'Fork' on GitHub - -# 2. Clone your fork +# Fork on GitHub, then: git clone https://github.com//reframe.git cd reframe - -# 3. Add the upstream remote git remote add upstream https://github.com/reframe-oss/reframe.git -``` - ---- - -## Development Setup - -```bash -# Install dependencies bun install - -# Start the development server -bun run dev -``` - -Open [http://localhost:3000](http://localhost:3000). Changes to components reflect instantly with [Next.js Fast Refresh](https://nextjs.org/docs/architecture/fast-refresh). - -### Other Commands - -```bash -bun run build # Build for production (outputs to out/) -bun run lint # Run ESLint -bunx tsc --noEmit # Run TypeScript type checking +bun run dev # http://localhost:3000 ``` ---- - -## Project Structure - -``` -reframe/ -├── src/ -│ ├── app/ -│ │ ├── layout.tsx # Root layout, metadata, fonts -│ │ ├── page.tsx # Home page -│ │ └── globals.css # Global styles -│ ├── components/ -│ │ ├── VideoEditor.tsx # Root editor component -│ │ ├── FileUpload.tsx # Drag-drop file upload -│ │ ├── VideoPreview.tsx # HTML5 video player -│ │ ├── PresetSelector.tsx # 11 preset formats + custom -│ │ ├── FramingControl.tsx # Fit/Fill framing toggle -│ │ ├── TrimControl.tsx # Start/end time inputs -│ │ ├── RotateControl.tsx # Rotation buttons -│ │ ├── AudioSpeedControl.tsx # Audio + speed -│ │ ├── ExportSettings.tsx # Quality CRF slider -│ │ ├── ExportOverlay.tsx # Export progress modal -│ │ ├── DownloadResult.tsx # Success + download -│ │ └── LottiePlayer.tsx # Lottie animation wrapper -│ ├── hooks/ -│ │ └── useVideoEditor.ts # Main state management hook -│ └── lib/ -│ ├── ffmpeg.ts # FFmpeg wrapper & filter builders -│ ├── presets.ts # Preset definitions -│ └── types.ts # TypeScript types -├── public/ # Static assets -├── next.config.ts -├── tailwind.config.ts -└── tsconfig.json -``` ---- - -## Development Tips - -### 1. Next.js Fast Refresh -This project uses Next.js Fast Refresh in development mode. Most changes to React components, hooks, and styles are reflected instantly in the browser without restarting the dev server. - -- Component updates appear immediately -- State is often preserved during edits -- Restarting `npm run dev` is usually unnecessary for UI changes - -Learn more: https://nextjs.org/docs/architecture/fast-refresh - ---- - -### 2. FFmpeg Module Changes -Changes to `ffmpeg.ts` may not hot-reload correctly because FFmpeg initialization and WebAssembly modules can persist in memory. - -If updates are not reflected: - -- Perform a full browser page reload -- Clear cached worker instances if necessary -- Restart the development server only when required - -FFmpeg WASM reference: https://ffmpegwasm.netlify.app/docs/overview - ---- - -### 3. Monitor FFmpeg Downloads -FFmpeg WebAssembly assets can be large and may take time to download during development. - -Use the browser DevTools **Network** tab to: - -- Verify FFmpeg assets are loading correctly -- Inspect caching behavior -- Detect failed `.wasm` or worker requests -- Measure initialization performance - -Chrome DevTools: https://developer.chrome.com/docs/devtools/network - ---- - -### 4. Use React DevTools -Install React DevTools for easier component inspection and debugging. - -Helpful for: - -- Inspecting component props and state -- Tracing re-renders -- Debugging hooks -- Monitoring React component trees - -React DevTools: https://react.dev/learn/react-developer-tools - ---- - -### 5. Keep Console Open During Development -The browser console provides important runtime diagnostics for: - -- FFmpeg initialization issues -- Hydration warnings -- API request failures -- WebAssembly loading errors - -Filtering logs by warnings/errors can speed up debugging significantly. - ---- - -### 6. Use Source Maps for Easier Debugging -Development builds include source maps, allowing you to debug original TypeScript/React source files directly from DevTools. - -Tips: - -- Set breakpoints in source files -- Use async stack traces -- Inspect runtime variables during rendering - -JavaScript debugging guide: https://developer.chrome.com/docs/devtools/javascript - ---- - -### 7. Watch for Memory Usage -FFmpeg WebAssembly processing can consume significant browser memory during video operations. - -Recommendations: - -- Close unused tabs while testing -- Refresh the page after heavy processing tasks -- Monitor memory usage in browser performance tools - -Performance tools: https://developer.chrome.com/docs/devtools/performance - ---- - -### 8. Verify Environment Variables -After modifying `.env.local`, restart the Next.js development server because environment variables are loaded only during server startup. +| Command | What it does | +|---|---| +| `bun run dev` | Development server with Fast Refresh | +| `bun run lint` | ESLint | +| `bunx tsc --noEmit` | Type check | +| `bun run test` | Unit tests (Vitest) | +| `bun run build` | Static production build into `out/` | +| `bunx playwright test` | End-to-end export tests (see below) | +| `bun run storybook` | Component stories | -Example: +The end-to-end tests export real videos. Before the first run: ```bash -npm run dev +brew install ffmpeg # or: sudo apt-get install ffmpeg +scripts/make-fixtures.sh --small +bunx playwright install chromium +NEXT_PUBLIC_DATADOG_ENV=off bun run build +bunx playwright test ``` -Environment variables guide: https://nextjs.org/docs/app/guides/environment-variables - ---- - -## Finding Issues - -We have **300+ open issues** across all skill levels: - -| Level | Where to look | -|-------|--------------| -| 🟢 **Beginner** | [`good first issue`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22) label — 100+ beginner tasks | -| 🟡 **Intermediate** | [`enhancement`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aenhancement) label | -| 🔴 **Advanced** | [`feature`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Afeature) label | -| 🐛 **Bug Fixes** | [`bug`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Abug) label | -| ♿ **Accessibility** | [`accessibility`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aaccessibility) label | -| 📝 **Documentation** | [`documentation`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Adocumentation) label | -| 🔒 **Security** | [`security`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Asecurity) label | -| ⚡ **Performance** | [`performance`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aperformance) label | - -**Before claiming an issue:** -1. Check if it already has an assignee — if so, pick a different one -2. Comment `/assign` on the issue to claim it instantly via our bot -3. If an issue has been idle and unassigned for a while, it's fair game - ---- +Keep your fork current with `git fetch upstream && git merge upstream/main`. -## 🙋 Claiming Issues +## Claim an issue -We use a bot to automatically manage issue assignments. Here's how it works: +Pick an issue from +[good first issue](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22), +[bug](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Abug), +[enhancement](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aenhancement) +or [documentation](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Adocumentation). -### Claiming an Issue -Comment `/assign` on any open issue to claim it: -- The bot will assign it to you instantly -- You have **5 days** to make progress before it is automatically unassigned -- You can only hold **5 issues** at a time across the repo +- Comment **`/assign`** on an open, unassigned issue. The bot assigns it to the + first person who asks. +- One person per issue, and at most **5 issues** at a time. +- No activity for 2 days gets a reminder, 4 days a final warning, and after + **5 days** the issue is unassigned. Any comment on the issue resets the timer, + so say if you're stuck. +- Comment **`/unassign`** if you can't finish. That's fine. -### Unassigning Yourself -If you can no longer work on an issue, comment `/unassign` to release it: -- This frees it up for other contributors immediately -- No hard feelings — we appreciate the honesty! +Please don't open a pull request for an issue assigned to someone else. -### Assignment Rules -- ✅ First person to comment `/assign` gets the issue -- ✅ Only **one contributor** can be assigned per issue at a time -- ✅ Maximum **5 issues** per contributor at once -- ⚠️ You will get a warning after **2 days** of no activity -- ⚠️ You will get a final warning after **4 days** of no activity -- ❌ Issue is automatically unassigned after **5 days** of no activity +## Branches and commits -### Inactivity Warnings -The bot will ping you if your assigned issue has no activity: -- **Day 2** — friendly reminder to update your progress -- **Day 4** — final warning, 24 hours left before unassignment -- **Day 5** — automatic unassignment, issue reopens for others +Branch from an up-to-date `main` and name the branch by type: -### Tips -- Leave a comment on your issue if you are stuck or need help — any comment resets the inactivity timer -- Link your PR to the issue using `Fixes #issue_number` in your PR description -- If you see an issue already assigned, please pick a different one - ---- - -## Making Changes +| Prefix | For | +|---|---| +| `feat/` | New features, e.g. `feat/crop-tool` | +| `fix/` | Bug fixes, e.g. `fix/trim-end-validation` | +| `docs/` | Documentation, e.g. `docs/deployment-guide` | +| `refactor/` | Restructuring with no behaviour change | +| `test/` | Tests only | +| `chore/` | Build, config, dependencies | -### 1. Create a branch +Commits follow [Conventional Commits](https://www.conventionalcommits.org): +`type(scope): what changed`, in the imperative. -```bash -git checkout -b feat/your-feature-name -# or -git checkout -b fix/bug-description -# or -git checkout -b docs/what-you-documented ``` - -### 2. Make your changes - -- Keep changes focused — one issue per PR -- Test your changes in the browser (test file upload, export, and download) -- Make sure `bun run lint` passes -- Make sure `bunx tsc --noEmit` passes (no TypeScript errors) - -### 3. Commit your changes - -```bash -git add -git commit -m "feat: add aria-label to export button" +feat(trim): set the trim end from the playhead with O +fix(export): stop the progress bar going backwards on retry +docs: explain cross-origin isolation in DEPLOYMENT.md ``` -**Commit message format:** -- `feat:` — new feature -- `fix:` — bug fix -- `docs:` — documentation changes -- `refactor:` — code refactoring (no behavior change) -- `test:` — adding or fixing tests -- `style:` — formatting, whitespace changes -- `chore:` — build config, dependencies - ---- - -## Submitting a Pull Request - -1. Push your branch: `git push origin feat/your-feature-name` -2. Go to your fork on GitHub and click **"Compare & pull request"** -3. Fill in the PR template: - - Describe what you changed and why - - Reference the issue: `Closes #` - - **Attach a screen recording** (required for UI/feature changes — see below) -4. Submit the PR — maintainers will review within a few days - -### Screen Recording Requirement - -**Any PR that adds or modifies a UI element, a user-facing feature, or any visual behaviour must include a screen recording of the working change running on your local machine.** - -This is a hard requirement — PRs without a recording will not be merged until one is added. - -**What to record:** -- Run `bun run dev` and open `http://localhost:3000` -- Demonstrate the full working flow of your change (e.g. upload a video → use the new control → export → see the result) -- Show any edge cases your implementation handles (empty state, error state, etc.) - -**How to record:** -- **macOS**: `Cmd + Shift + 5` → Record Selected Portion, or use QuickTime Player -- **Windows**: `Win + G` → Xbox Game Bar → Capture -- **Linux**: OBS Studio, GNOME Screenshot tool, or `kazam` -- **Any OS**: [Loom](https://loom.com) (free, great for sharing) - -Attach the recording directly to the PR by dragging the file into the GitHub comment box, or paste a Loom/shareable link. - -### PR Checklist - -- [ ] Code works in Chrome, Firefox, and Safari -- [ ] No new TypeScript errors (`bunx tsc --noEmit`) -- [ ] ESLint passes (`bun run lint`) -- [ ] UI changes tested on mobile (use browser DevTools) -- [ ] Accessibility: new interactive elements have ARIA labels -- [ ] Issue number referenced in PR description -- [ ] **Screen recording attached** (required for all UI/feature PRs) - ---- - -## Visual Review (Chromatic) - -Every PR gets its Storybook screenshotted and diffed against `main` by -[Chromatic](https://www.chromatic.com/), so UI changes are reviewed visually -rather than guessed at from a diff. - -You'll see up to three checks: - -| Check | What it means | -|---|---| -| `Storybook Publish` | Your Storybook built and uploaded | -| `UI Tests` | Snapshots compared against the baseline | -| `Visual regression` / `Visual regression (fork)` | The workflow that ran it | - -**Visual changes do not fail your PR.** If `UI Tests` says changes need -accepting, that is a maintainer's call, not a problem with your branch — a -maintainer accepts or rejects each change in the Chromatic UI. - -**Maintainers:** to re-run visual review on a fork PR without asking the -contributor to push again, apply the `visual-review` label. Fork PRs are built -automatically on open and on every push; the label is for re-running on demand. - -If your PR touches a component, please **add or update its story** in -`src/components/.stories.tsx`. Stories are what Chromatic can see; -a change with no story gets no visual review. - -Avoid anything time-dependent inside a story — live clocks, carousels, -autoplaying animations. A screenshot taken at an arbitrary moment will differ -between runs and report a change that isn't real. Existing examples: -`ExportOverlay` marks its spinner and tip carousel `data-chromatic="ignore"`, -and `TrimControl` passes `file={null}` to avoid async audio decoding. - ---- - -## Code Style - -- **TypeScript**: Strict types, no `any` unless absolutely necessary -- **React**: Functional components only, use hooks -- **Tailwind**: Use utility classes; avoid inline `style={}` for static styles -- **No comments**: Code should be self-documenting via good naming; only add comments for non-obvious behavior -- **No console.log**: Remove debug logs before submitting -- **Imports**: Use relative imports within `src/` - ---- - -## Development Tips - -- **Next.js Fast Refresh**: Changes to React components update immediately in dev mode without restarting the server. Learn more in the [Next.js Fast Refresh docs](https://nextjs.org/docs/architecture/fast-refresh). -- **FFmpeg changes**: Changes to `src/lib/ffmpeg.ts` may require a full page reload because FFmpeg WASM state can stay cached in the browser. -- **FFmpeg downloads**: Use the [browser DevTools Network tab](https://developer.chrome.com/docs/devtools/network/) to monitor FFmpeg WASM downloads, cache behavior, and failed CDN requests. -- **Testing exports**: Keep a few small test videos (~5-10 MB) for quick export testing. -- **React DevTools**: Install the [React DevTools browser extension](https://react.dev/learn/react-developer-tools) for component inspection. -- **Responsive checks**: Use the [Chrome DevTools Device Toolbar](https://developer.chrome.com/docs/devtools/device-mode/) to test mobile layouts before opening a PR. -- **Accessibility checks**: Use the [axe DevTools browser extension](https://www.deque.com/axe/devtools/) to catch common accessibility issues. - ---- - -## GSSoC'26 Participants - -Reframe is an **official GirlScript Summer of Code 2026 project**! - -### Getting Started as a GSSoC Contributor - -1. Browse issues labeled [`gssoc'26`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22gssoc%2726%22) -2. Start with [`good first issue`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22+label%3A%22gssoc%2726%22) if you're new to open source -3. Comment `/assign` on the issue — our bot will assign it to you instantly, no maintainer needed -4. Submit your PR within **5 days** and remember to link it with `Fixes #issue_number` - -### Tips for GSSoC Success - -- **Read the issue fully** before starting — ask questions in comments if anything is unclear -- **Small, focused PRs** are merged faster than large ones -- **Quality over quantity** — one well-implemented feature beats five half-done ones -- **Be communicative** — comment on your progress, especially if you're stuck -- **Test thoroughly** — check your changes work before submitting - ---- - -## Questions? - -- **Found a bug?** → [Open a bug report](https://github.com/reframe-oss/reframe/issues/new?labels=bug) -- **Have a feature idea?** → [Open a feature request](https://github.com/reframe-oss/reframe/issues/new?labels=feature) -- **Stuck on an issue?** → Comment on the issue and tag `@magic-peach` - ---- - -## Code of Conduct - -We expect all contributors to follow our Code of Conduct to create a safe, welcoming, and inclusive community. -- **Be respectful and welcoming.** -- **Harassment and discrimination are strictly prohibited.** -- **Constructive feedback is encouraged.** - -Thank you for making Reframe better! 🎬 - -## Cross-Origin Isolation & GitHub Pages - -This project uses `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` -headers to enable cross-origin isolation, which allows FFmpeg.wasm to run in -multi-threaded mode for significantly faster video exports. - -**GitHub Pages does not support custom HTTP headers.** -If you are deploying to GitHub Pages, cross-origin isolation cannot be enabled -and FFmpeg will automatically fall back to single-threaded mode. - -For full multi-threading support, deploy to **Vercel**, **Netlify**, or -**Cloudflare Pages** where custom headers are supported. +Avoid messages like "update", "changes" or "fixed stuff". Pull request titles +use the same format; the PR validator checks it. + +## Before you open a pull request + +- [ ] `bun run lint` passes +- [ ] `bunx tsc --noEmit` passes +- [ ] `bun run test` passes, and new logic has tests +- [ ] `bun run build` succeeds +- [ ] For export or FFmpeg changes: `bunx playwright test` passes +- [ ] Tried in the browser: upload, edit, export and download +- [ ] UI changes checked at phone width, with keyboard only, and in dark mode +- [ ] New interactive elements have accessible names (`aria-label` or a label) +- [ ] The description says `Closes #` +- [ ] **UI or feature changes include a screen recording** (below) + +### Screen recording + +Any PR that adds or changes something visible must include a short recording +of the change working on your machine: run `bun run dev`, then show the whole +flow (upload → use the change → export → result) and any edge cases. On macOS +use Cmd+Shift+5, on Windows Win+G, on Linux OBS, or [Loom](https://loom.com) +anywhere. Drag the file into the PR, or paste a link. PRs without one aren't +merged until it's added. + +## Pull requests and review + +1. Push your branch and open a pull request against `main`. Fill in the + template: what changed, why, and `Closes #`. +2. Automated checks run: lint, type check, tests, the build, a Netlify deploy + preview, Snyk, and Chromatic visual review. +3. A maintainer reviews, usually within a few days. Reply to comments and push + fixes to the same branch; don't open a new PR. +4. **A maintainer merges every PR by hand.** Bots comment and label, but never + merge. + +**Visual review (Chromatic).** Every PR's Storybook is screenshotted and +compared with `main`. Visual changes don't fail your PR; a maintainer accepts +or rejects them. If you change a component, add or update its story in +`src/components/.stories.tsx`, and keep stories free of anything +time-dependent (clocks, carousels, autoplay). Maintainers can re-run visual +review on a fork PR with the `visual-review` label. + +## Project rules + +These come from how Reframe works; PRs that break them are sent back. + +- **Static export.** `output: "export"` means no API routes, middleware, server + actions or `not-found.tsx`. Headers belong in `netlify.toml` and + `public/_headers`, not `next.config.ts`. +- **FFmpeg.wasm is the only video engine.** Don't add other video-processing + libraries. FFmpeg command lines are built only in `src/lib/ffmpegArgs.ts`. +- **Design tokens.** Never rename the CSS variables `--bg`, `--surface`, + `--border`, `--text`, `--muted`. +- **`cn()`** (clsx + tailwind-merge) for conditional classes; don't replace it + with template strings. +- **React hooks** go before any early `return`. +- **Privacy.** Video content, file names, captions and paths never leave the + device, telemetry included. + +Code style: strict TypeScript (avoid `any`), function components and hooks, +Tailwind utility classes, the `@/` import alias, comments only where the code +can't explain itself, and no leftover `console.log`. + +## Development tips + +- **Fast Refresh** updates components instantly; you rarely need to restart. +- **FFmpeg changes** (`src/lib/ffmpeg*.ts`) often need a full page reload, + because the worker and WebAssembly module stay loaded. +- **Watch the engine download** in the DevTools Network tab: failed `.wasm` or + worker requests show up there. +- **Multi-threaded engine.** `bun run dev` isn't cross-origin isolated, so it + uses the single-threaded engine. To test the multi-threaded one, build and + run `node scripts/serve-isolated.mjs`. +- **Memory.** Exports can use a lot of memory; close other tabs and keep a few + small test clips around (`scripts/make-fixtures.sh` makes some). +- **Environment variables** are read at startup: restart `bun run dev` after + editing `.env.local` (see `.env.example`). +- Useful tools: [React DevTools](https://react.dev/learn/react-developer-tools), + the Chrome [device toolbar](https://developer.chrome.com/docs/devtools/device-mode/), + and [axe DevTools](https://www.deque.com/axe/devtools/) for accessibility. + +Project layout: components in `src/components/`, hooks in `src/hooks/`, FFmpeg +code in `src/lib/` (`ffmpeg.ts` client, `ffmpeg.worker.ts` worker, +`ffmpegArgs.ts` command builder). See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). + +## Reporting bugs and suggesting features + +**Bugs:** open an issue with the steps to reproduce, what you expected, what +happened, your browser and OS, and screenshots or the console error if you +have them. Please don't attach private videos. + +**Features:** open an issue with a clear title, what the feature does, and why +it would be useful. Check for an existing issue first. + +**Security problems:** don't open a public issue; see [SECURITY.md](SECURITY.md). + +## GSSoC'26 participants + +Reframe is an official [GirlScript Summer of Code 2026](https://gssoc.girlscript.tech) +project. + +- Look for issues labelled + [`gssoc'26`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22gssoc%2726%22), + starting with `good first issue` if you're new. +- Claim with `/assign` and link your PR with `Closes #`. +- **Points count only after a maintainer adds `gssoc:approved`** to your merged + PR. Labels for difficulty (`level:*`), type (`type:*`) and quality are added + by maintainers by hand. +- Small, focused PRs are reviewed and merged faster. Quality beats quantity. + +Everyone taking part agrees to the [Code of Conduct](CODE_OF_CONDUCT.md). +Thank you for making Reframe better. diff --git a/CONTRIUBUTION.md b/CONTRIUBUTION.md deleted file mode 100644 index ad7001f1a..000000000 --- a/CONTRIUBUTION.md +++ /dev/null @@ -1,113 +0,0 @@ - # Contribution Guide - -Thank you for contributing to this project! 🚀 - -We welcome all kinds of contributions including bug fixes, features, documentation improvements, and design enhancements. - ---- - -## Getting Started - -1. Fork the repository -2. Clone your fork locally: git clone https://github.com/your-username/project-name.git -3. Navigate to the project directory : cd project-name -4. Install dependencies : npm install -5. Start the development server : npm start - ---- - -## Contribution Workflow -- Check existing issues before creating a new one -- Comment on the issue you want to work on -- Create a new branch -- Make your changes -- Test your changes properly -- Commit and push your changes -- Open a Pull Request -- Branch Naming Convention - ---- - -Use meaningful branch names. - -## Examples -- feature/add-login-page -- fix/navbar-overflow -- docs/update-readme - -## Prefixes -- feature/ → New features -- fix/ → Bug fixes -- docs/ → Documentation updates -- refactor/ → Code improvements -- Commit Message Guidelines - -## Use short and meaningful commit messages. - -Examples -- Add responsive navbar -- Fix login validation bug -- Update CONTRIBUTION.md - -## Avoid vague messages like: - -- update -- changes -- fixed stuff -- Pull Request Guidelines - -## Before submitting a PR: - -- Ensure the project builds successfully -- Test your changes properly -- Keep PRs focused on a single issue -- Write a clear PR title and description -- Link related issues properly - -Example -- Fixes #12 -- Coding Style Expectations -- Write clean and readable code -- Use meaningful variable and function names -- Keep functions modular and focused -- Add comments where necessary -- Follow the existing project structure and formatting - ---- - -## Reporting Bugs - - When reporting a bug, include: - -- Steps to reproduce -- Expected behavior -- Screenshots (if applicable) -- Environment details -- Suggesting Features - ---- - -## Feature suggestions are welcome. - -## Please include: - -- A clear title -- Feature description -- Why the feature would be useful -- Code of Conduct - -By contributing, you agree to follow the project's CODE_OF_CONDUCT.md. - -Please be respectful and supportive toward other contributors. - ---- - -## Need Help? - -Feel free to: - -1. Open an issue -2. Start a discussion -3. Ask maintainers for guidance - -Happy Contributing! 🎉 diff --git a/MIT LICENSE b/LICENSE similarity index 100% rename from MIT LICENSE rename to LICENSE diff --git a/README.md b/README.md index e2f74b595..7cdc839eb 100644 --- a/README.md +++ b/README.md @@ -1,510 +1,126 @@
-# Reframe - -### Free, open-source video editor that runs entirely in your browser. - -### No login. No uploads. No ads. 100% private. - -

- - - - - - - - - -

- -

- - - - - - - - - -

- -

- - - - - - - - - - - - -

- -

- - Deploys by Netlify - -

+ -
+# Reframe ---- +**Resize, trim and convert videos in your browser. Nothing is uploaded.** -## Built With +[![Stars](https://img.shields.io/github/stars/reframe-oss/reframe?style=flat-square&logo=github)](https://github.com/reframe-oss/reframe/stargazers) +[![Issues](https://img.shields.io/github/issues/reframe-oss/reframe?style=flat-square)](https://github.com/reframe-oss/reframe/issues) +[![License](https://img.shields.io/github/license/reframe-oss/reframe?style=flat-square)](LICENSE) +[![CI](https://github.com/reframe-oss/reframe/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/reframe-oss/reframe/actions/workflows/main.yml?query=branch%3Amain) +[![Netlify Status](https://api.netlify.com/api/v1/badges/fc902da9-9876-4298-9aea-f930075f8d95/deploy-status)](https://app.netlify.com/sites/reframe-os/deploys) +[![Known Vulnerabilities](https://snyk.io/test/github/reframe-oss/reframe/badge.svg)](https://snyk.io/test/github/reframe-oss/reframe) -
+### [Open Reframe → reframe-os.netlify.app](https://reframe-os.netlify.app) -![Next.js](https://img.shields.io/badge/Next.js-15-black?style=flat-square&logo=next.js) -![TypeScript](https://img.shields.io/badge/TypeScript-5-blue?style=flat-square&logo=typescript) -![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-3-06B6D4?style=flat-square&logo=tailwindcss&logoColor=white) -![FFmpeg](https://img.shields.io/badge/FFmpeg.wasm-0.12.10-green?style=flat-square&logo=ffmpeg) -![Lucide](https://img.shields.io/badge/Lucide_React-latest-orange?style=flat-square) -![Lottie](https://img.shields.io/badge/Lottie_Web-latest-purple?style=flat-square) + +Demo coming soon
---- - -## What is Reframe? - -Reframe is a **browser-based video editor** — everything happens on your device. Your videos are never sent to any server. No account needed. No fees. Just open and edit. - -> Built for everyone — whether you're a creator resizing videos for social media, or just someone who wants to quickly trim and convert without installing bulky software. - ## Features -- **Instant Resizing** — 11 preset formats (Reels, TikTok, YouTube, Instagram, etc.) + custom dimensions -- **Flexible Framing** — Fit (letterbox) or Fill (crop) to your target aspect ratio -- **Precise Trimming** — Cut start and end times with real-time duration validation -- **Rotation** — 0°, 90°, 180°, 270° rotation support -- **Audio Control** — Keep or mute audio independently -- **Speed Control** — 0.25x to 4x playback speed with smooth audio adjustment -- **Quality Settings** — CRF slider for quality vs. file size trade-offs -- **Undo / Redo** — every edit can be undone; a slider drag counts as one step -- **Project recovery** — edits survive a refresh, and project files (`.reframe.json`) save and reopen your edits without the video. See [docs/PROJECTS.md](docs/PROJECTS.md) -- **Smooth UX** — Lottie animations, live export progress, instant download - -Everything stays on your device. No servers. No tracking. No login. - ---- -## Keyboard Shortcuts - -| Shortcut | Action | -|----------|--------| -| Ctrl+Enter / Cmd+Enter | Export video | -| Ctrl+Z / Cmd+Z | Undo the last edit | -| Ctrl+Y or Ctrl+Shift+Z / Shift+Cmd+Z | Redo | -| Space | Play/pause video preview | -| M | Toggle audio mute | -| Escape | Cancel export | - -> On macOS, use `Cmd` instead of `Ctrl` for keyboard shortcuts. +- Resize for Reels, TikTok, Shorts, Instagram, YouTube and more, or any custom size; fit or fill +- Trim, rotate, change speed (0.25× to 4×), mute or adjust audio, add background music +- Text and image overlays; brightness, contrast, saturation and sharpness +- Export to MP4, WebM, MKV or GIF with a quality slider +- Undo and redo, recovery after a refresh, and project files you can reopen +- Keyboard shortcuts for everything common: see [docs/USAGE.md](docs/USAGE.md) -## Getting Started +## How it works -### Prerequisites +Reframe runs [FFmpeg](https://ffmpeg.org) compiled to WebAssembly +([FFmpeg.wasm](https://ffmpegwasm.netlify.app)) in a Web Worker, so your video +is processed on your own device and the page stays responsive. When the site is +cross-origin isolated (the `Cross-Origin-Opener-Policy` and +`Cross-Origin-Embedder-Policy` headers), browsers allow `SharedArrayBuffer` and +Reframe uses the faster multi-threaded FFmpeg; otherwise it falls back to the +single-threaded build. The details are in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). -- [Bun](https://bun.sh) (recommended) or Node.js 18+ - -### Installation +## Quick start ```bash -git clone https://github.com/reframe-oss/reframe.git -cd reframe +git clone https://github.com/reframe-oss/reframe.git && cd reframe bun install -``` - -### Development - -```bash bun run dev ``` -Open [http://localhost:3000](http://localhost:3000) — component changes reflect instantly with [Next.js Fast Refresh](https://nextjs.org/docs/architecture/fast-refresh), so you usually do not need to restart the dev server. For FFmpeg reload notes and debugging tools, see the [Development Tips](CONTRIBUTING.md#development-tips). - -### Production Build - -```bash -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. - ---- - -## Deploying - -Reframe uses static export (`output: 'export'`), so it can be deployed to any static hosting provider. - -### Deploying to Vercel - -Reframe uses static export (`output: 'export'`) and can be deployed easily on Vercel. - -#### Option 1 — Vercel Dashboard (Recommended) - -1. Fork this repository -2. Go to https://vercel.com/new -3. Import your forked repository -4. Configure: - - Framework Preset: Next.js - - Build Command: `bun run build` - - Output Directory: leave blank (Vercel auto-detects `out` for static exports) -5. Click **Deploy** - -Vercel will automatically build and host the static output. - -#### Option 2 — Vercel CLI - -```bash -# Install Vercel CLI globally -npm i -g vercel - -# Login to Vercel -vercel login - -# Deploy from project root -vercel --prod -``` - -#### FFmpeg.wasm Configuration - -FFmpeg.wasm requires COOP/COEP headers for SharedArrayBuffer support. - -Add the following to `vercel.json`: - -```json -{ - "headers": [ - { - "source": "/(.*)", - "headers": [ - { - "key": "Cross-Origin-Opener-Policy", - "value": "same-origin" - }, - { - "key": "Cross-Origin-Embedder-Policy", - "value": "require-corp" - } - ] - } - ] -} -``` - -### Alternative Static Hosts - -You can also deploy Reframe on other static hosting providers: - -| Platform | Deployment Method | -| -------------------- | ----------------------------------------------------------- | -| **Netlify** | Connect your fork at https://app.netlify.com/start | -| **GitHub Pages** | Deploy the generated `out/` folder to the `gh-pages` branch | -| **Cloudflare Pages** | Connect your fork in Cloudflare Pages | - -### Deploying to Vercel - -The quickest way to get Reframe live: - -**Option 1 — Vercel Dashboard (Recommended)** - -1. Fork this repository on GitHub -2. Go to [vercel.com/new](https://vercel.com/new) and import your fork -3. Vercel auto-detects Next.js settings: - - **Framework Preset:** Next.js - - **Build Command:** `bun run build` - - **Output Directory:** leave blank (Vercel auto-detects `out` for static exports) -4. Click **Deploy** — your site will be live in ~2 minutes - -**Option 2 — Vercel CLI** - -```bash -# Install Vercel CLI globally -npm i -g vercel - -# Login to Vercel -vercel login - -# Deploy from project root -vercel --prod -``` - -> **Note:** FFmpeg.wasm requires COOP/COEP headers for SharedArrayBuffer support. On Vercel, add a `vercel.json` in your project root: -> -> ```json -> { -> "headers": [ -> { -> "source": "/(.*)", -> "headers": [ -> { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }, -> { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" } -> ] -> } -> ] -> } -> ``` - -### Deploying to Netlify - -1. Push your fork to GitHub -2. Open Netlify and import the repository -3. Configure: - - Build command: `bun run build` - - Publish directory: `out` -4. Deploy the site - -> Note: FFmpeg browser features may require proper CORS headers depending on hosting setup. - -### Deploying to GitHub Pages - -Build the static export: - -```bash -bun run build -``` - -The production files will be generated in the `out/` directory. +Then open http://localhost:3000. Deploying your own copy: [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md). -You can deploy the `out/` folder using: -- GitHub Pages -- `gh-pages` branch -- GitHub Actions workflow - ---- - -## Architecture - -For detailed technical information about Reframe's architecture, design choices, and implementation details, see the [Architecture Documentation](docs/ARCHITECTURE.md). - -> Reframe requires WebAssembly (WASM) support to process videos in the browser. ---- - -## Development Tips - -### 1. Next.js Fast Refresh -This project uses Next.js Fast Refresh in development mode. Most changes to React components, hooks, and styles are reflected instantly in the browser without restarting the dev server. - -- Component updates appear immediately -- State is often preserved during edits -- Restarting `npm run dev` is usually unnecessary for UI changes - -Learn more: https://nextjs.org/docs/architecture/fast-refresh - ---- - -### 2. FFmpeg Module Changes -Changes to `ffmpeg.ts` may not hot-reload correctly because FFmpeg initialization and WebAssembly modules can persist in memory. - -If updates are not reflected: - -- Perform a full browser page reload -- Clear cached worker instances if necessary -- Restart the development server only when required - -FFmpeg WASM reference: https://ffmpegwasm.netlify.app/docs/overview - ---- - -### 3. Monitor FFmpeg Downloads -FFmpeg WebAssembly assets can be large and may take time to download during development. - -Use the browser DevTools **Network** tab to: - -- Verify FFmpeg assets are loading correctly -- Inspect caching behavior -- Detect failed `.wasm` or worker requests -- Measure initialization performance - -Chrome DevTools: https://developer.chrome.com/docs/devtools/network - ---- - -### 4. Use React DevTools -Install React DevTools for easier component inspection and debugging. - -Helpful for: - -- Inspecting component props and state -- Tracing re-renders -- Debugging hooks -- Monitoring React component trees - -React DevTools: https://react.dev/learn/react-developer-tools - ---- - -### 5. Keep Console Open During Development -The browser console provides important runtime diagnostics for: - -- FFmpeg initialization issues -- Hydration warnings -- API request failures -- WebAssembly loading errors - -Filtering logs by warnings/errors can speed up debugging significantly. - ---- - -### 6. Use Source Maps for Easier Debugging -Development builds include source maps, allowing you to debug original TypeScript/React source files directly from DevTools. - -Tips: - -- Set breakpoints in source files -- Use async stack traces -- Inspect runtime variables during rendering - -JavaScript debugging guide: https://developer.chrome.com/docs/devtools/javascript - ---- - -### 7. Watch for Memory Usage -FFmpeg WebAssembly processing can consume significant browser memory during video operations. - -Recommendations: - -- Close unused tabs while testing -- Refresh the page after heavy processing tasks -- Monitor memory usage in browser performance tools - -Performance tools: https://developer.chrome.com/docs/devtools/performance - ---- - -### 8. Verify Environment Variables -After modifying `.env.local`, restart the Next.js development server because environment variables are loaded only during server startup. - -Example: - -```bash -npm run dev -``` - -Environment variables guide: https://nextjs.org/docs/app/guides/environment-variables +## Privacy ---- +Your videos never leave your device. Reframe collects anonymous performance and +error data to improve export reliability; you can opt out in Settings. +Exactly what is sent, and what never is, is listed on the +[privacy page](https://reframe-os.netlify.app/privacy) and in +[docs/DATADOG.md](docs/DATADOG.md). ## Contributing -### ⭐ Star this repo — it helps more people find Reframe! +Contributions of every size are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) +to set up, claim an issue with `/assign`, and open a pull request. -**Reframe is an open-source project and we welcome contributions of all kinds** — from fixing a typo in the README to implementing a brand new feature. Every contribution matters. +## Part of GirlScript Summer of Code 2026 ---- + + + + GirlScript Summer of Code 2026 + + -### 🌸 GirlScript Summer of Code 2026 +Reframe is an official GSSoC'26 project. Start with the +[`gssoc'26` issues](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22gssoc%2726%22). -Reframe is an **official project in GirlScript Summer of Code (GSSoC) 2026**! We have **300+ open issues** across all difficulty levels — from beginner-friendly tasks to advanced features. +## Supported by -> **If you're a GSSoC participant**, add the `gssoc'26` label to any issue you want to work on, and mention your GitHub username in a comment to claim it. - -#### Find issues to work on: - -| Level | Label | Description | -| ------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| 🟢 **Beginner** | [`good first issue`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22good+first+issue%22) | Small, well-defined tasks — perfect if this is your first open source contribution | -| 🟡 **Intermediate** | [`enhancement`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aenhancement) | Feature improvements and UX enhancements | -| 🔴 **Advanced** | [`feature`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Afeature) | New features requiring deeper understanding of FFmpeg/WASM | -| 🔵 **Any Level** | [`documentation`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Adocumentation) | Docs, guides, and README improvements | -| ♿ **Any Level** | [`accessibility`](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3Aaccessibility) | Making Reframe usable for everyone | - -**[→ Browse all GSSoC'26 issues](https://github.com/reframe-oss/reframe/issues?q=is%3Aopen+label%3A%22gssoc%2726%22)** - ---- - -### How to Contribute - -1. **Find an issue** — Browse [open issues](https://github.com/reframe-oss/reframe/issues) or pick one from the table above -2. **Comment on the issue** — Say you'd like to work on it so we don't duplicate effort -3. **Fork the repo** — Click the Fork button at the top right -4. **Create a branch** — `git checkout -b feat/your-feature-name` -5. **Make your changes** — Code, test, and commit -6. **Open a Pull Request** — Reference the issue number in your PR description -7. **Get reviewed** — We'll review and merge your contribution! - -See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide including development setup, code style, and PR checklist. - - - -## Privacy +

+ + + + Deploys by Netlify + + +    + + + + Snyk + + +    + + + + Datadog + + +    + + + + Chromatic + + +

-Reframe processes all videos **100% client-side**. Your video files are never uploaded to any server. You can even use Reframe offline (after first load). Edit settings and a copy of the open video are kept in your own browser so a refresh doesn't lose work; **Delete saved data** in the editor removes them. The source code is fully open for inspection. ---- +[Netlify](https://www.netlify.com) hosts the site and its deploy previews. +[Snyk](https://snyk.io) scans dependencies for vulnerabilities. +[Datadog](https://www.datadoghq.com) Real User Monitoring tracks export +reliability, through the Datadog Open Source Program. +[Chromatic](https://www.chromatic.com) runs visual review of UI changes. ## Contributors -Thanks to all the amazing people who have contributed to Reframe! - -[![Contributors](https://contrib.rocks/image?repo=reframe-oss/reframe)](https://github.com/reframe-oss/reframe/graphs/contributors) - -We welcome contributions of all kinds — code, documentation, design, and feedback. Check out our [Contributing Guide](CONTRIBUTING.md) to get started. - ---- - -## Thanks - -Chromatic - -Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions. + + Contributors to Reframe + -Datadog - -Reframe is part of the [Datadog Open Source Program](https://www.datadoghq.com/partners/open-source/). Thank you to [Datadog](https://www.datadoghq.com/) for supporting the project and for the Real User Monitoring that helps us understand FFmpeg.wasm load performance and export reliability. - ---- +Reviewers, mentors, designers and others who don't commit code are credited +with [all-contributors](.github/ALL_CONTRIBUTORS.md). ## License -MIT License — See [LICENSE](LICENSE) for details. - ---- - -
- -**If Reframe saved you time, please [⭐ star the repo](https://github.com/reframe-oss/reframe) — it helps others discover it!** - -Made with ❤️ for everyone who just wants to edit a video without the hassle. - -
- ---- - -## Troubleshooting - -### Installation fails -- Ensure you're on [Bun](https://bun.sh) (this project uses `bun.lock`, not npm/yarn) with Node.js 20.9+. -- Run `bun install`. -- Delete `node_modules` and `bun.lock`, then reinstall if issues persist. - -### Environment variables not loading -- Verify that a `.env.local` file exists (see `.env.example` for the required keys). -- Restart the development server after making changes — env vars are only read at startup. - -## FAQ - -### How do I start the project? -Run: - -```bash -bun install -bun run dev -``` - -### How do I report a bug? -Please open a GitHub issue with reproduction steps and relevant logs. - +[MIT](LICENSE) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 8a5782538..b3c6b4595 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -97,7 +97,7 @@ Reframe uses FFmpeg.wasm to process videos entirely in the browser. The processi - **Zero backend** — Entire app is static HTML/CSS/JS - **Fast deployment** — Can be hosted on any static file server -- **Offline-capable** — Works offline after initial load (with service worker) +- **Could work offline** — There is no service worker yet, so Reframe needs a connection to load. Offline support (a PWA that keeps the page cross-origin isolated) is planned - **SEO-friendly** — Static pages are easily crawlable ### Why Single-threaded FFmpeg? diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 000000000..0e62b3d90 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,82 @@ +# Deploying Reframe + +Reframe is a static site: `bun run build` exports it to `out/` +(`output: "export"` in `next.config.ts`). There is no server to run. + +The live site is on **Netlify** (https://reframe-os.netlify.app). Netlify is +the only host the project maintains; the other options below work for your +own fork. + +## Cross-origin isolation: the one requirement + +FFmpeg's fast multi-threaded engine needs `SharedArrayBuffer`, which browsers +only allow on a page that is cross-origin isolated. The host must send these +headers on every page: + +``` +Cross-Origin-Opener-Policy: same-origin +Cross-Origin-Embedder-Policy: require-corp +``` + +Without them Reframe still works, but every export uses the single-threaded +engine and is several times slower. With them, any image, script or font +loaded from another origin must allow it (CORP or CORS); that's why the +Netlify badge and brand logos are served from this site instead of hotlinked. + +Check a deployment with: + +```bash +curl -sI https://your-site.example | grep -i cross-origin +``` + +and, in the browser console on the site, `crossOriginIsolated` should be `true`. + +## Netlify (recommended) + +`netlify.toml` in the repository already sets the build, the publish +directory and the headers. + +1. Fork the repository. +2. In Netlify, **Add new site → Import an existing project** and pick your fork. +3. Netlify reads `netlify.toml`: + - build command: `bun install && bun run build` + - publish directory: `out` + - headers: COOP/COEP and the security headers +4. Deploy. Pull requests get deploy previews automatically. + +Optional environment variables (Site settings → Environment variables): + +| Variable | Purpose | +|---|---| +| `NEXT_PUBLIC_DATADOG_APPLICATION_ID`, `NEXT_PUBLIC_DATADOG_CLIENT_TOKEN` | Datadog RUM. Leave unset for no telemetry. See [DATADOG.md](DATADOG.md) | +| `NEXT_PUBLIC_DATADOG_ENV` | Override the telemetry environment, or `off` | + +Netlify's own `CONTEXT` variable tells the build whether it is production or a +preview; `next.config.ts` passes it to the app so previews never report as +production. + +### Netlify CLI + +```bash +bun install +NEXT_PUBLIC_DATADOG_ENV=off bun run build +npx netlify-cli deploy --dir out # draft URL +npx netlify-cli deploy --dir out --prod # production (maintainers only) +``` + +## Other static hosts + +| Host | Headers | Notes | +|---|---|---| +| Cloudflare Pages | Already handled: `public/_headers` is copied to `out/_headers`, which Cloudflare Pages reads too | Build: `bun run build`, output: `out` | +| Vercel | `vercel.json` in the repository sets the same headers | Framework preset Next.js, build `bun run build`. Not maintained by the project | +| GitHub Pages | **Not possible**: GitHub Pages can't send custom headers | Works, single-threaded only | +| Any static server | Configure COOP/COEP on the server | `scripts/serve-isolated.mjs` is a small example used by the tests | + +## Checking a build locally + +```bash +NEXT_PUBLIC_DATADOG_ENV=off bun run build +node scripts/serve-isolated.mjs # http://127.0.0.1:4173, isolated +ISOLATED=0 node scripts/serve-isolated.mjs 4174 # without isolation +``` diff --git a/docs/PROJECTS.md b/docs/PROJECTS.md index 9a35af1dd..f194610f5 100644 --- a/docs/PROJECTS.md +++ b/docs/PROJECTS.md @@ -19,7 +19,7 @@ Not included, on purpose: - **The completion-sound preference.** It's a setting, not an edit, so undo never flips it. - **Files** (the source video, the image overlay file, background music). They are binary assets, not edit values, so adding or removing the image file is not an undo step. - **Transient UI state.** Hover, open panels, the selected text overlay, export progress, object URLs and worker state. -- **The multi-track timeline state.** It isn't connected to the UI or the export yet (see `PHASE_1_MVP.md`). +- **The multi-track timeline state.** It isn't connected to the UI or the export yet (see [history/PHASE_1_MVP.md](history/PHASE_1_MVP.md)). `useVideoEditor` exposes `recipe` derived from `EditState`. `handleExport` passes that same object, plus the image overlay settings, to `exportVideo`. So after an undo the preview and the export can't disagree. diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 000000000..8f2881657 --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,71 @@ +# Using Reframe + +Reframe edits a video in your browser. Nothing is uploaded: the file is +processed on your device by FFmpeg running as WebAssembly. + +## Basic flow + +1. Open https://reframe-os.netlify.app and drop a video on the page, or click + to choose one. MP4, MOV, WebM, MKV and most other formats work. +2. Pick an output size. There are presets for Reels/TikTok/Shorts (9:16), + Instagram (4:5), square, YouTube (16:9) and more, or set custom dimensions. + Choose **Fit** to letterbox or **Fill** to crop to the new shape. +3. Edit: trim the start and end, rotate, change the speed, mute or adjust the + audio, add text or an image overlay, and tune brightness, contrast, + saturation and sharpness. +4. Choose a format (MP4, WebM, MKV or GIF) and quality, then **Export**. +5. When it finishes, **Download** the file. + +The first export downloads the video engine (about 30 MB), so it takes longer. +Later exports reuse it. + +## Undo, recovery and project files + +- Every edit can be undone and redone. A slider drag counts as one step. +- Your edits and a copy of the open video are kept in this browser, so a + refresh doesn't lose work. **Delete saved data** in the editor removes them. +- **Save project file** writes your edits (not the video) to a + `.reframe.json` file you can reopen later. See [PROJECTS.md](PROJECTS.md). + +## Keyboard shortcuts + +On macOS use **Cmd** where this says Ctrl. + +| Shortcut | Action | +|---|---| +| Ctrl+Enter | Export the video | +| Ctrl+Shift+E | Export the video (when no export is running) | +| Escape | Cancel the export in progress | +| Ctrl+Z | Undo the last edit | +| Ctrl+Y, or Ctrl+Shift+Z | Redo | +| Space | Play or pause the preview | +| I | Set the trim start to the current position | +| O | Set the trim end to the current position | +| M | Mute or unmute the video's audio | +| R | Reset all settings | +| 1 – 9 | Choose one of the first nine output presets | +| ? | Show the shortcut list | + +Shortcuts other than undo and redo are ignored while you're typing in a text +field. + +## Exports that are slow or fail + +- **Multi-threaded and single-threaded engines.** On a site served with + cross-origin isolation (as the live site is), Reframe uses the faster + multi-threaded engine. If an export stops making progress, Reframe restarts + it once on the single-threaded engine automatically; if that also stalls, + you'll see a message with a **Retry** button. +- **Large files** need a lot of memory. Close other tabs, or trim the clip or + lower the output resolution first. +- **Screen recordings** made in a browser often have no length in the file. + Reframe works it out when the file loads; this can take a moment for long + recordings. + +## Privacy + +Your videos never leave your device. Reframe collects anonymous performance +and error data (no file names, captions or video content) to improve export +reliability, and you can turn it off under **Settings** in the page footer. +See the [privacy page](https://reframe-os.netlify.app/privacy) and +[DATADOG.md](DATADOG.md) for exactly what is sent. diff --git a/docs/assets/brand/chromatic-dark.svg b/docs/assets/brand/chromatic-dark.svg new file mode 100644 index 000000000..627f8286d --- /dev/null +++ b/docs/assets/brand/chromatic-dark.svg @@ -0,0 +1,5 @@ + + + + Chromatic + diff --git a/docs/assets/brand/chromatic-light.svg b/docs/assets/brand/chromatic-light.svg new file mode 100644 index 000000000..89ca69a54 --- /dev/null +++ b/docs/assets/brand/chromatic-light.svg @@ -0,0 +1,5 @@ + + + + Chromatic + diff --git a/docs/assets/brand/datadog-dark.png b/docs/assets/brand/datadog-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ba795eed30be790b6909b6fcac2d14c11836863b GIT binary patch literal 40549 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 + + + GSSoC 2026 + diff --git a/docs/assets/brand/gssoc-light.svg b/docs/assets/brand/gssoc-light.svg new file mode 100644 index 000000000..a6d42e840 --- /dev/null +++ b/docs/assets/brand/gssoc-light.svg @@ -0,0 +1,5 @@ + + + + GSSoC 2026 + diff --git a/docs/assets/brand/netlify-dark.svg b/docs/assets/brand/netlify-dark.svg new file mode 100644 index 000000000..2edda2250 --- /dev/null +++ b/docs/assets/brand/netlify-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/brand/netlify-light.svg b/docs/assets/brand/netlify-light.svg new file mode 100644 index 000000000..2edda2250 --- /dev/null +++ b/docs/assets/brand/netlify-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/brand/snyk-dark.svg b/docs/assets/brand/snyk-dark.svg new file mode 100644 index 000000000..b97801084 --- /dev/null +++ b/docs/assets/brand/snyk-dark.svg @@ -0,0 +1,5 @@ + + + + Snyk + diff --git a/docs/assets/brand/snyk-light.svg b/docs/assets/brand/snyk-light.svg new file mode 100644 index 000000000..650056bd6 --- /dev/null +++ b/docs/assets/brand/snyk-light.svg @@ -0,0 +1,5 @@ + + + + Snyk + diff --git a/docs/assets/demo-placeholder.svg b/docs/assets/demo-placeholder.svg new file mode 100644 index 000000000..ef15e800c --- /dev/null +++ b/docs/assets/demo-placeholder.svg @@ -0,0 +1,5 @@ + + + + Demo recording coming soon + diff --git a/PHASE_1_MVP.md b/docs/history/PHASE_1_MVP.md similarity index 100% rename from PHASE_1_MVP.md rename to docs/history/PHASE_1_MVP.md