fix: project loading and add editable project name

Fixed project loading to restore all track properties and added inline
project name editing in header.

**AudioEditor.tsx:**
- Added `loadTracks` from useMultiTrack hook
- Fixed `handleLoadProject` to use `loadTracks()` instead of recreating tracks
  - Now properly restores all track properties (effects, automation, volume, pan, etc.)
  - Shows track count in success toast message
- Added editable project name input in header
  - Positioned between logo and track actions
  - Auto-sizes based on text length
  - Saves on blur (triggers auto-save)
  - Smooth hover/focus transitions
  - Muted color that brightens on interaction

**useMultiTrack.ts:**
- Added `loadTracks()` method to replace all tracks at once
- Enables proper project loading with full state restoration
- Maintains all track properties during load

**Fixes:**
- Projects now load correctly with all tracks and their audio buffers
- Track properties (effects, automation, volume, pan, etc.) fully restored
- Project name can be edited inline in header
- Auto-save triggers when project name changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 09:32:34 +01:00
parent d3a5961131
commit b743f97276
2 changed files with 23 additions and 11 deletions

View File

@@ -95,6 +95,7 @@ export function AudioEditor() {
removeTrack,
updateTrack,
clearTracks,
loadTracks,
} = useMultiTrack();
// Track whether we should auto-select on next add (when project is empty)
@@ -955,15 +956,8 @@ export function AudioEditor() {
throw new Error('Project not found');
}
// Clear current state
clearTracks();
// Load tracks
for (const track of projectData.tracks) {
if (track.audioBuffer) {
addTrackFromBufferOriginal(track.audioBuffer, track.name);
}
}
// Load tracks with all their properties restored
loadTracks(projectData.tracks);
// Restore settings
setZoom(projectData.settings.zoom);
@@ -976,7 +970,7 @@ export function AudioEditor() {
addToast({
title: 'Project Loaded',
description: `"${projectData.metadata.name}" loaded successfully`,
description: `"${projectData.metadata.name}" loaded successfully (${projectData.tracks.length} track${projectData.tracks.length !== 1 ? 's' : ''})`,
variant: 'success',
duration: 2000,
});
@@ -989,7 +983,7 @@ export function AudioEditor() {
duration: 3000,
});
}
}, [clearTracks, addTrackFromBufferOriginal, addToast]);
}, [loadTracks, addToast]);
// Delete project
const handleDeleteProject = React.useCallback(async (projectId: string) => {
@@ -1252,6 +1246,19 @@ export function AudioEditor() {
<h1 className="text-lg font-bold text-foreground">Audio UI</h1>
</div>
{/* Project Name */}
<div className="flex items-center gap-2 border-l border-border pl-4">
<input
type="text"
value={currentProjectName}
onChange={(e) => setCurrentProjectName(e.target.value)}
onBlur={handleSaveProject}
className="bg-transparent border-none outline-none text-sm font-medium text-muted-foreground hover:text-foreground focus:text-foreground transition-colors px-2 py-1 rounded hover:bg-accent/50 focus:bg-accent"
placeholder="Untitled Project"
style={{ width: `${Math.max(12, currentProjectName.length)}ch` }}
/>
</div>
{/* Track Actions */}
<div className="flex items-center gap-2 border-l border-border pl-4">
<Button variant="outline" size="sm" onClick={handleOpenProjectsDialog}>

View File

@@ -120,6 +120,10 @@ export function useMultiTrack() {
);
}, []);
const loadTracks = useCallback((tracksToLoad: Track[]) => {
setTracks(tracksToLoad);
}, []);
return {
tracks,
addTrack,
@@ -129,5 +133,6 @@ export function useMultiTrack() {
clearTracks,
reorderTracks,
setTrackBuffer,
loadTracks,
};
}