|
| 1 | +# Feature: 22 Multi-project Management |
| 2 | + |
| 3 | +## Status |
| 4 | +Planned |
| 5 | + |
| 6 | +## Goal |
| 7 | + |
| 8 | +Allow users to manage more than one browser-local project instead of being limited to a single hard-coded active project. |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +The current app persists one active project in IndexedDB using a fixed `ACTIVE_PROJECT_ID`. The transport bar displays `Project 1`, and there is no project-level UI for creating, switching, renaming, or deleting separate songs. |
| 13 | + |
| 14 | +Multiple local projects should stay browser-first and lightweight. This feature is not a cloud account system or a file browser. It should extend the existing IndexedDB persistence model so users can keep separate sketches in the same browser profile. |
| 15 | + |
| 16 | +## Scope |
| 17 | + |
| 18 | +Included: |
| 19 | + |
| 20 | +- Store multiple serializable project documents in IndexedDB. |
| 21 | +- Store the active project ID separately from project documents. |
| 22 | +- Restore the last active project on app startup. |
| 23 | +- Add a transport bar project menu that shows the active project name. |
| 24 | +- Support creating a new blank project. |
| 25 | +- Support switching between existing projects. |
| 26 | +- Support renaming the active project. |
| 27 | +- Support deleting a project with confirmation. |
| 28 | +- Stop playback and audio preview before switching projects. |
| 29 | +- Keep imported sample blobs scoped to the owning project so projects cannot collide on sample IDs. |
| 30 | +- Migrate the existing single active project into the multi-project shape where practical. |
| 31 | + |
| 32 | +Excluded: |
| 33 | + |
| 34 | +- Cloud sync or user accounts. |
| 35 | +- Cross-device collaboration. |
| 36 | +- File-system project export/import. |
| 37 | +- Project templates beyond the current default blank project. |
| 38 | +- Project duplication unless it is trivial after the base workflow exists. |
| 39 | +- A full project dashboard page. |
| 40 | +- Sharing projects between browsers or devices. |
| 41 | + |
| 42 | +## Constraints |
| 43 | + |
| 44 | +- Project data must remain serializable. |
| 45 | +- Runtime objects such as `AudioContext`, `AudioBuffer`, `File`, object URLs, active source nodes, and mixer nodes must not be stored in project JSON. |
| 46 | +- Switching projects must not let pending autosave write the old project into the new project ID. |
| 47 | +- Persistence code should stay separate from React rendering and audio scheduling. |
| 48 | +- The audio engine must not depend on IndexedDB or project menu UI. |
| 49 | +- Use CSS Modules and semantic design tokens for project menu UI. |
| 50 | +- Do not introduce a runtime dependency for IndexedDB or menu behavior unless the implementation clearly justifies it. |
| 51 | + |
| 52 | +## Proposed Model |
| 53 | + |
| 54 | +Use a stable project ID per project instead of one fixed `ACTIVE_PROJECT_ID`. |
| 55 | + |
| 56 | +Illustrative shape: |
| 57 | + |
| 58 | +```ts |
| 59 | +export interface ProjectSummary { |
| 60 | + id: string; |
| 61 | + name: string; |
| 62 | + createdAt: number; |
| 63 | + updatedAt: number; |
| 64 | +} |
| 65 | + |
| 66 | +export interface ProjectCollectionState { |
| 67 | + activeProjectId: string; |
| 68 | + projects: ProjectSummary[]; |
| 69 | +} |
| 70 | +``` |
| 71 | + |
| 72 | +`PersistedProjectDocument.id` should become the actual project ID. Existing project document fields such as clips, arrangement tracks, clip instances, sample metadata, tempo, mixer state, arrangement length, and loop range remain serializable project data. |
| 73 | + |
| 74 | +Imported sample blob storage should include project ownership. Acceptable first-pass strategies: |
| 75 | + |
| 76 | +- Use a composite blob key such as `${projectId}:${sampleId}`. |
| 77 | +- Or add `projectId` to each imported sample blob record and query/delete by project ID if IndexedDB indexes are added. |
| 78 | + |
| 79 | +The implementation should choose the smaller safe migration path and document it in the PR. |
| 80 | + |
| 81 | +## UI Direction |
| 82 | + |
| 83 | +The project menu belongs in the transport bar near the current project name/save status area. |
| 84 | + |
| 85 | +The first project menu should include: |
| 86 | + |
| 87 | +- Current project name. |
| 88 | +- List of local projects. |
| 89 | +- `New Project`. |
| 90 | +- `Rename Project`. |
| 91 | +- `Delete Project`. |
| 92 | + |
| 93 | +Switching projects should close the menu, stop playback/preview, save or flush the current project state if practical, load the selected project, and reset selection to valid clip/instrument defaults. |
| 94 | + |
| 95 | +## Done when |
| 96 | + |
| 97 | +- Users can create, select, rename, and delete browser-local projects from the transport bar. |
| 98 | +- Refreshing the browser restores the last active project. |
| 99 | +- Each project restores its own clips, arrangement, mixer state, sample metadata, and imported sample blobs. |
| 100 | +- Deleting a project requires confirmation and does not delete another project's sample blobs. |
| 101 | +- Existing single-project IndexedDB data is preserved as an initial project after migration where practical. |
| 102 | +- Autosave writes to the intended project after creating or switching projects. |
| 103 | +- The transport bar displays the active project name from project state, not a hard-coded value. |
| 104 | +- The PR documents any migration limitations. |
| 105 | + |
| 106 | +## Verification |
| 107 | + |
| 108 | +Run: |
| 109 | + |
| 110 | +- `npm run typecheck --if-present` |
| 111 | +- `npm run lint --if-present` |
| 112 | +- `npm run test --if-present` |
| 113 | +- `npm run build --if-present` |
| 114 | + |
| 115 | +Manual check: |
| 116 | + |
| 117 | +- Start from an existing single-project browser profile if possible. |
| 118 | +- Confirm the old project appears as a local project after migration. |
| 119 | +- Create a second project and verify it starts blank. |
| 120 | +- Rename a project and refresh the browser. |
| 121 | +- Switch between projects and confirm clips, arrangement placements, mixer state, and tempo remain separate. |
| 122 | +- Import a WAV file in one project, refresh, and verify the imported audio belongs only to that project. |
| 123 | +- Delete a project and confirm another project still loads correctly. |
| 124 | + |
| 125 | +## PR notes |
| 126 | + |
| 127 | +- Summarize IndexedDB store/key changes. |
| 128 | +- Explain active project ID persistence. |
| 129 | +- Explain imported sample blob scoping and deletion behavior. |
| 130 | +- Mention any unsupported project operations, such as duplication or file export/import. |
0 commit comments