feat: implement Phase 12.3 - Project Export/Import
Added full export/import functionality for projects:
Export Features:
- Export button for each project in Projects dialog
- Downloads project as JSON file with all data
- Includes tracks, audio buffers, effects, automation, settings
- Filename format: {project_name}_{timestamp}.json
Import Features:
- Import button in Projects dialog header
- File picker for .json files
- Automatically generates new project ID to avoid conflicts
- Appends "(Imported)" to project name
- Preserves all project data
This enables:
- Backup of projects outside the browser
- Sharing projects with collaborators
- Migration between computers/browsers
- Version snapshots at different stages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -210,3 +210,64 @@ export async function duplicateProject(sourceProjectId: string, newName: string)
|
||||
await saveProject(newProject);
|
||||
return newId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export project as JSON file
|
||||
*/
|
||||
export async function exportProjectAsJSON(projectId: string): Promise<void> {
|
||||
const project = await loadProject(projectId);
|
||||
if (!project) throw new Error('Project not found');
|
||||
|
||||
// Convert the project to JSON
|
||||
const json = JSON.stringify(project, null, 2);
|
||||
|
||||
// Create blob and download
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${project.metadata.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_${Date.now()}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import project from JSON file
|
||||
*/
|
||||
export async function importProjectFromJSON(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = async (e) => {
|
||||
try {
|
||||
const json = e.target?.result as string;
|
||||
const project = JSON.parse(json) as ProjectData;
|
||||
|
||||
// Generate new ID to avoid conflicts
|
||||
const newId = generateProjectId();
|
||||
const now = Date.now();
|
||||
|
||||
const importedProject: ProjectData = {
|
||||
...project,
|
||||
metadata: {
|
||||
...project.metadata,
|
||||
id: newId,
|
||||
name: `${project.metadata.name} (Imported)`,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
};
|
||||
|
||||
await saveProject(importedProject);
|
||||
resolve(newId);
|
||||
} catch (error) {
|
||||
reject(new Error('Failed to parse project file'));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = () => reject(new Error('Failed to read file'));
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user