Refactor App and Sidebar components to support macOS, add TitleBarDragRegion for improved window dragging, and enhance model management with delete functionality and download progress tracking. Update audio player to handle audio resets more effectively and improve generation form with model download notifications.

This commit is contained in:
Jamie Pine
2026-01-25 23:25:21 -08:00
parent 090b1f6dde
commit b2659e6a6d
19 changed files with 919 additions and 154 deletions
@@ -9,6 +9,7 @@ import { usePlayerStore } from '@/stores/playerStore';
export function AudioPlayer() {
const {
audioUrl,
audioId,
title,
isPlaying,
currentTime,
@@ -25,6 +26,9 @@ export function AudioPlayer() {
const waveformRef = useRef<HTMLDivElement>(null);
const wavesurferRef = useRef<WaveSurfer | null>(null);
const loadingRef = useRef(false);
const previousAudioIdRef = useRef<string | null>(null);
const previousCurrentTimeRef = useRef<number>(0);
const hasInitializedRef = useRef(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -354,6 +358,61 @@ export function AudioPlayer() {
}
}, [volume]);
// Mark as initialized when audio is ready, reset when audioId changes
useEffect(() => {
if (duration > 0 && audioId) {
hasInitializedRef.current = true;
}
// Reset initialization flag when audioId changes to a new audio
if (audioId !== previousAudioIdRef.current && previousAudioIdRef.current !== null) {
hasInitializedRef.current = false;
}
}, [duration, audioId]);
// Handle clicking the same audio again - always restart from beginning
// When setAudio is called with the same audioId, it sets currentTime to 0 in the store
// but WaveSurfer's actual position is still wherever it was. We detect this mismatch and reset.
useEffect(() => {
const wavesurfer = wavesurferRef.current;
if (!wavesurfer || !audioId || duration === 0 || !hasInitializedRef.current) {
// Update the refs even if we don't process
if (audioId !== null) {
previousAudioIdRef.current = audioId;
}
previousCurrentTimeRef.current = currentTime;
return;
}
const previousAudioId = previousAudioIdRef.current;
const previousCurrentTime = previousCurrentTimeRef.current;
// Check if the same audio was clicked again
// This happens when:
// 1. audioId matches the previous one (same audio)
// 2. currentTime was reset from a non-zero value to 0 (setAudio was called)
// 3. WaveSurfer is not at the beginning (needs reset)
const wasResetToZero = previousCurrentTime > 0.1 && currentTime < 0.1;
const isSameAudio = audioId === previousAudioId;
const wavesurferPosition = wavesurfer.getCurrentTime();
const wavesurferNotAtStart = wavesurferPosition > 0.1;
// Update refs for next time
previousAudioIdRef.current = audioId;
previousCurrentTimeRef.current = currentTime;
// If same audio was clicked (reset to 0) and WaveSurfer is not at start, reset it
if (isSameAudio && wasResetToZero && wavesurferNotAtStart) {
// Reset to beginning and play
console.log('Same audio clicked again, resetting to beginning');
wavesurfer.seekTo(0);
wavesurfer.play().catch((error) => {
console.error('Failed to play after reset:', error);
setIsPlaying(false);
setError(`Playback error: ${error instanceof Error ? error.message : String(error)}`);
});
}
}, [audioId, duration, currentTime, setIsPlaying]);
// Handle loop - WaveSurfer handles this via the 'finish' event
const handlePlayPause = () => {