feat: add project export/import for cross-device transfers#766
feat: add project export/import for cross-device transfers#766renezander030 wants to merge 2 commits into
Conversation
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.
|
@renezander030 is attempting to deploy a commit to the OpenCut OSS Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis 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. ChangesProject import/export
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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
apps/web/src/app/projects/page.tsxapps/web/src/core/managers/project-manager.tsapps/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>
Summary
.opencutJSON file from project context menu or dropdown menu.opencutfile via Import button in the projects header toolbarformatVersionfield in export format for future compatibilityTest plan
Fixes #751
Summary by CodeRabbit