Skip to content

feat: add project export/import for cross-device transfers#766

Open
renezander030 wants to merge 2 commits into
OpenCut-app:mainfrom
renezander030:feat/751-transferable-projects
Open

feat: add project export/import for cross-device transfers#766
renezander030 wants to merge 2 commits into
OpenCut-app:mainfrom
renezander030:feat/751-transferable-projects

Conversation

@renezander030
Copy link
Copy Markdown

@renezander030 renezander030 commented Apr 13, 2026

Summary

  • Add Export Project: downloads project as .opencut JSON file from project context menu or dropdown menu
  • Add Import Project: loads .opencut file via Import button in the projects header toolbar
  • Includes formatVersion field in export format for future compatibility
  • Imported projects receive a new UUID to avoid collisions with existing projects

Test plan

  • Export a project with multiple tracks and effects
  • Import the exported file on a fresh instance
  • Verify all timeline data, tracks, and settings are preserved
  • Verify invalid files show a clear error message
  • Verify the same file can be imported multiple times (each gets a unique ID)

Fixes #751

Summary by CodeRabbit

  • New Features
    • Import projects: Add projects by selecting .opencut files; imported projects are validated, added to the top of the project list, and surface success/failure notifications.
    • Export projects: Export any project as a .opencut file via project actions or context menu; downloads are triggered with success/failure notifications.

Review Change Stack

Add the ability to export projects as .opencut JSON files and import them
on another device. Export is available from the project context menu and
dropdown menu. Import is available via a button in the projects header.

The export format includes a version field for future compatibility.
Imported projects receive a new ID to avoid collisions.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 13, 2026

@renezander030 is attempting to deploy a commit to the OpenCut OSS Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48cc2330-6ede-4204-a5a9-5f30bc9ec96a

📥 Commits

Reviewing files that changed from the base of the PR and between e35635d and 55d4e52.

📒 Files selected for processing (3)
  • apps/web/src/app/projects/page.tsx
  • apps/web/src/core/managers/project-manager.ts
  • apps/web/src/services/storage/service.ts

📝 Walkthrough

Walkthrough

This PR implements project import/export: UI components and menu items call ProjectManager methods that serialize projects to .opencut files and read/import them via the StorageService adapter.

Changes

Project import/export

Layer / File(s) Summary
UI: Projects page import/export wiring
apps/web/src/app/projects/page.tsx
Adds ImportProjectButton with hidden .opencut file input, updates React imports, adds Download04Icon/Upload04Icon, renders import button in ProjectsHeader, and wires onExportClick through ProjectItem, ProjectMenu, and ProjectContextMenuContent to call manager export methods.
ProjectManager: export/import file handling
apps/web/src/core/managers/project-manager.ts
Adds exportProjectToFile({ id }) to build { formatVersion, exportedAt, project } JSON, create .opencut blob and trigger download; adds importProjectFromFile({ file }) to read/parse/validate JSON, regenerate project id and timestamps, persist via storage service, prepend metadata to savedProjects, and return new id (or null on error).
StorageService: import/export adapters
apps/web/src/services/storage/service.ts
Adds exportProjectToJSON({ id }) to return a `SerializedProject

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as ProjectsPage
    participant PM as ProjectManager
    participant SS as StorageService
    participant Browser as BrowserAPI

    User->>UI: Click "Export Project"
    UI->>PM: exportProjectToFile({ id })
    PM->>SS: exportProjectToJSON({ id })
    SS-->>PM: SerializedProject
    PM->>PM: Wrap with formatVersion & exportedAt
    PM->>Browser: Create .opencut blob & object URL
    PM->>Browser: Trigger download
    Browser-->>User: Download .opencut file
    PM-->>UI: Show success toast
Loading
sequenceDiagram
    participant User
    participant UI as ProjectsPage
    participant PM as ProjectManager
    participant SS as StorageService
    participant Storage as LocalAdapter

    User->>UI: Select .opencut file
    UI->>PM: importProjectFromFile({ file })
    PM->>PM: Read file text
    PM->>PM: Parse JSON & validate formatVersion and project fields
    PM->>PM: Generate new UUID & update metadata.updatedAt
    PM->>SS: importProjectFromJSON({ serializedProject })
    SS->>Storage: Write to projectsAdapter[id]
    Storage-->>SS: Success
    PM->>PM: Prepend metadata to savedProjects & notify()
    PM-->>UI: Return new project ID
    UI-->>User: Show success toast
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰
I nibble bytes, pack scenes so neat,
A .opencut bundle — tidy and sweet.
Hop, carry projects from desk to lap,
Import, export — a seamless nap.
Cheers from this rabbit, feather-light and fleet.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete; it does not follow the required template which explicitly states that feature PRs will be closed and should open an issue first. The PR should either follow the template requirements or acknowledge that the template policy may have changed. Consider updating the description to align with repository guidelines.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding export/import functionality for projects to enable cross-device transfers.
Linked Issues check ✅ Passed All code changes directly address issue #751 requirements: export/import functionality is implemented, projects preserve data fidelity, and standalone export/import mechanism is used.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing export/import functionality; no unrelated modifications were introduced outside the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/projects/page.tsx`:
- Around line 557-565: The Import button becomes icon-only on small screens and
lacks an accessible name; update the Button component (the one that calls
fileInputRef.current?.click() and renders HugeiconsIcon with Upload04Icon) to
provide an accessible label—either add aria-label="Import project" to the Button
or include visually hidden text (sr-only) for "Import project" so screen readers
can announce the action.

In `@apps/web/src/core/managers/project-manager.ts`:
- Around line 653-657: The export currently only writes exportData.project as
SerializedProject and thus omits project-local media, causing missing assets on
import; modify the export/import flow around exportData (and the code paths
referenced at lines 736-748) to include bundled media by calling
loadAllMediaAssets(serializedProject) during export and adding a mediaAssets
array (with filenames, mime types, and base64 or binary blobs) to exportData,
bump formatVersion, and on import call saveMediaAsset for each entry before
restoring SerializedProject; alternatively, if bundling is undesired, explicitly
block the .opencut export path and surface a clear error message indicating that
media assets are not included and provide guidance (or a flag) to create a full
media-inclusive export.

In `@apps/web/src/services/storage/service.ts`:
- Around line 530-538: importProjectFromJSON currently writes serializedProject
directly via projectsAdapter.set and skips the migration pipeline; to fix, have
importProjectFromJSON await this.ensureMigrations() first and then run the
project's migrations against the serialized payload before persisting (i.e.,
invoke whatever migration runner you use for existing records — the same routine
used elsewhere — to produce a migratedProject) and only then call
this.projectsAdapter.set(serializedProject.metadata.id, migratedProject) so the
imported record cannot race or remain in a pre-migration shape; reference
functions: importProjectFromJSON, ensureMigrations, projectsAdapter.set and the
migration runner used elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1628ade5-fb88-4b77-a060-d45de6d674be

📥 Commits

Reviewing files that changed from the base of the PR and between cbd1b82 and e35635d.

📒 Files selected for processing (3)
  • apps/web/src/app/projects/page.tsx
  • apps/web/src/core/managers/project-manager.ts
  • apps/web/src/services/storage/service.ts

Comment thread apps/web/src/app/projects/page.tsx
Comment thread apps/web/src/core/managers/project-manager.ts
Comment thread apps/web/src/core/managers/project-manager.ts Outdated
Comment thread apps/web/src/services/storage/service.ts
- projects/page.tsx: add aria-label="Import project" to the import
  button so screen readers have a name when the visible label is
  hidden on small screens.
- project-manager.ts: tighten import schema validation — accept only
  formatVersion === SUPPORTED_FORMAT_VERSION (1), reject non-string
  metadata.id/name (previously a numeric value passed the truthy
  check), require non-empty trimmed strings, and reject arrays/null
  payloads. Constant shared with the export path.
- storage/service.ts: await ensureMigrations() at the top of
  importProjectFromJSON so an import can't race with the memoized
  migration run and land in a half-migrated store.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add transferable projects

2 participants