diff --git a/app/src/App.tsx b/app/src/App.tsx index 96f75043..deeb6c17 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import voiceboxLogo from '@/assets/voicebox-logo.png'; import { AudioPlayer } from '@/components/AudioPlayer/AudioPlayer'; import { GenerationForm } from '@/components/Generation/GenerationForm'; diff --git a/app/src/components/Generation/GenerationForm.tsx b/app/src/components/Generation/GenerationForm.tsx index 0106684d..c029f05b 100644 --- a/app/src/components/Generation/GenerationForm.tsx +++ b/app/src/components/Generation/GenerationForm.tsx @@ -26,6 +26,7 @@ import { useToast } from '@/components/ui/use-toast'; import { apiClient } from '@/lib/api/client'; import { useGeneration } from '@/lib/hooks/useGeneration'; import { useProfile } from '@/lib/hooks/useProfiles'; +import { useGenerationStore } from '@/stores/generationStore'; import { usePlayerStore } from '@/stores/playerStore'; import { useUIStore } from '@/stores/uiStore'; @@ -45,6 +46,7 @@ export function GenerationForm() { const generation = useGeneration(); const { toast } = useToast(); const setAudio = usePlayerStore((state) => state.setAudio); + const setIsGenerating = useGenerationStore((state) => state.setIsGenerating); const form = useForm({ resolver: zodResolver(generationSchema), @@ -68,6 +70,7 @@ export function GenerationForm() { } try { + setIsGenerating(true); const result = await generation.mutateAsync({ profile_id: selectedProfileId, text: data.text, @@ -93,6 +96,8 @@ export function GenerationForm() { description: error instanceof Error ? error.message : 'Failed to generate audio', variant: 'destructive', }); + } finally { + setIsGenerating(false); } } diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx index 9b690c24..4526f1a5 100644 --- a/app/src/components/Sidebar.tsx +++ b/app/src/components/Sidebar.tsx @@ -1,6 +1,8 @@ -import { Home, Settings } from 'lucide-react'; -import { cn } from '@/lib/utils/cn'; +import { Home, Loader2, Settings } from 'lucide-react'; import voiceboxLogo from '@/assets/voicebox-logo.png'; +import { cn } from '@/lib/utils/cn'; +import { useGenerationStore } from '@/stores/generationStore'; +import { usePlayerStore } from '@/stores/playerStore'; interface SidebarProps { activeTab: string; @@ -13,15 +15,15 @@ const tabs = [ ]; export function Sidebar({ activeTab, onTabChange }: SidebarProps) { + const isGenerating = useGenerationStore((state) => state.isGenerating); + const audioUrl = usePlayerStore((state) => state.audioUrl); + const isPlayerVisible = !!audioUrl; + return (
{/* Logo */}
- Voicebox + Voicebox
{/* Navigation Buttons */} @@ -48,6 +50,21 @@ export function Sidebar({ activeTab, onTabChange }: SidebarProps) { ); })}
+ + {/* Spacer to push loader to bottom */} +
+ + {/* Generation Loader */} + {isGenerating && ( +
+ +
+ )}
); } diff --git a/app/src/components/VoiceProfiles/ProfileList.tsx b/app/src/components/VoiceProfiles/ProfileList.tsx index a415f933..0aea78ad 100644 --- a/app/src/components/VoiceProfiles/ProfileList.tsx +++ b/app/src/components/VoiceProfiles/ProfileList.tsx @@ -10,7 +10,7 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog'; -import { useProfiles, useImportProfile } from '@/lib/hooks/useProfiles'; +import { useImportProfile, useProfiles } from '@/lib/hooks/useProfiles'; import { useUIStore } from '@/stores/uiStore'; import { ProfileCard } from './ProfileCard'; import { ProfileForm } from './ProfileForm'; @@ -128,7 +128,8 @@ export function ProfileList() { Import Profile - Import the profile from "{selectedFile?.name}". This will create a new profile with all samples. + Import the profile from "{selectedFile?.name}". This will create a new profile with + all samples. diff --git a/app/src/stores/generationStore.ts b/app/src/stores/generationStore.ts new file mode 100644 index 00000000..c507f5f3 --- /dev/null +++ b/app/src/stores/generationStore.ts @@ -0,0 +1,11 @@ +import { create } from 'zustand'; + +interface GenerationState { + isGenerating: boolean; + setIsGenerating: (generating: boolean) => void; +} + +export const useGenerationStore = create((set) => ({ + isGenerating: false, + setIsGenerating: (generating) => set({ isGenerating: generating }), +})); diff --git a/tauri/src-tauri/gen/Assets.car b/tauri/src-tauri/gen/Assets.car index 67fa90c8..0795abd4 100644 Binary files a/tauri/src-tauri/gen/Assets.car and b/tauri/src-tauri/gen/Assets.car differ