diff --git a/app/src/App.tsx b/app/src/App.tsx index 89afc281..a832aa48 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -11,6 +11,7 @@ import { Sidebar } from '@/components/Sidebar'; import { TitleBarDragRegion } from '@/components/TitleBarDragRegion'; import { Toaster } from '@/components/ui/toaster'; import { VoicesTab } from '@/components/VoicesTab/VoicesTab'; +import { TOP_SAFE_AREA_PADDING } from '@/lib/constants/ui'; import { useModelDownloadToast } from '@/lib/hooks/useModelDownloadToast'; import { MODEL_DISPLAY_NAMES, useRestoreActiveTasks } from '@/lib/hooks/useRestoreActiveTasks'; import { @@ -20,9 +21,8 @@ import { setupWindowCloseHandler, startServer, } from '@/lib/tauri'; -import { useServerStore } from '@/stores/serverStore'; -import { TOP_SAFE_AREA_PADDING } from '@/lib/constants/ui'; import { cn } from '@/lib/utils/cn'; +import { useServerStore } from '@/stores/serverStore'; // Track if server is starting to prevent duplicate starts let serverStarting = false; @@ -140,7 +140,12 @@ function App() { // Show loading screen while server is starting in Tauri if (isTauri() && !serverReady) { return ( -
+
diff --git a/app/src/components/AudioTab/AudioTab.tsx b/app/src/components/AudioTab/AudioTab.tsx index 059bc836..9d802467 100644 --- a/app/src/components/AudioTab/AudioTab.tsx +++ b/app/src/components/AudioTab/AudioTab.tsx @@ -1,5 +1,8 @@ -import { Edit, Plus, Trash2, Speaker, CheckCircle2, Check } from 'lucide-react'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { invoke } from '@tauri-apps/api/core'; +import { Check, CheckCircle2, Edit, Plus, Speaker, Trash2 } from 'lucide-react'; import { useState } from 'react'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Dialog, @@ -18,14 +21,11 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Badge } from '@/components/ui/badge'; -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { apiClient } from '@/lib/api/client'; -import { isTauri } from '@/lib/tauri'; -import { invoke } from '@tauri-apps/api/core'; -import { usePlayerStore } from '@/stores/playerStore'; -import { cn } from '@/lib/utils/cn'; import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui'; +import { isTauri } from '@/lib/tauri'; +import { cn } from '@/lib/utils/cn'; +import { usePlayerStore } from '@/stores/playerStore'; interface AudioDevice { id: string; @@ -69,8 +69,7 @@ export function AudioTab() { }); const createChannel = useMutation({ - mutationFn: (data: { name: string; device_ids: string[] }) => - apiClient.createChannel(data), + mutationFn: (data: { name: string; device_ids: string[] }) => apiClient.createChannel(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['channels'] }); setCreateDialogOpen(false); @@ -144,12 +143,18 @@ export function AudioTab() {
{/* Left Column - Channels */} -
+
{allChannels.length === 0 ? (

- No audio channels yet. Create your first channel to route voices to specific devices. + No audio channels yet. Create your first channel to route voices to specific + devices.

- {channel.device_ids.length > 0 ? ( - channel.device_ids.map((deviceId) => { - const device = allDevices.find((d) => d.id === deviceId); - return ( - - {device?.name || deviceId} - - ); - }) - ) : ( - (() => { - const defaultDevice = allDevices.find((d) => d.is_default); - return defaultDevice ? ( - - {defaultDevice.name} - - ) : null; - })() - )} + {channel.device_ids.length > 0 + ? channel.device_ids.map((deviceId) => { + const device = allDevices.find((d) => d.id === deviceId); + return ( + + {device?.name || deviceId} + + ); + }) + : (() => { + const defaultDevice = allDevices.find((d) => d.is_default); + return defaultDevice ? ( + + {defaultDevice.name} + + ) : null; + })()}
@@ -259,7 +262,12 @@ export function AudioTab() {
{/* Right Column - Available Devices */} -
+

Available Devices

@@ -279,22 +287,23 @@ export function AudioTab() { (selectedChannel.device_ids.length === 0 ? device.is_default : selectedChannel.device_ids.includes(device.id)); - const canToggle = selectedChannelId && selectedChannel && !selectedChannel.is_default; - + const canToggle = + selectedChannelId && selectedChannel && !selectedChannel.is_default; + const handleDeviceClick = () => { if (!canToggle || !selectedChannel) return; - + const currentDeviceIds = selectedChannel.device_ids; const newDeviceIds = isConnected ? currentDeviceIds.filter((id) => id !== device.id) : [...currentDeviceIds, device.id]; - + updateChannel.mutate({ channelId: selectedChannelId, data: { device_ids: newDeviceIds }, }); }; - + return (

); } @@ -396,9 +402,7 @@ function ChannelVoicesList({ channelId }: { channelId: string }) { }); const voiceNames = - voices?.profile_ids - .map((id) => profiles?.find((p) => p.id === id)?.name) - .filter(Boolean) || []; + voices?.profile_ids.map((id) => profiles?.find((p) => p.id === id)?.name).filter(Boolean) || []; return (
@@ -422,12 +426,7 @@ interface CreateChannelDialogProps { onCreate: (name: string, deviceIds: string[]) => void; } -function CreateChannelDialog({ - open, - onOpenChange, - devices, - onCreate, -}: CreateChannelDialogProps) { +function CreateChannelDialog({ open, onOpenChange, devices, onCreate }: CreateChannelDialogProps) { const [name, setName] = useState(''); const [selectedDevices, setSelectedDevices] = useState([]); @@ -492,7 +491,9 @@ function CreateChannelDialog({ @@ -562,11 +563,7 @@ function EditChannelDialog({
- setName(e.target.value)} - /> + setName(e.target.value)} />
@@ -602,7 +599,9 @@ function EditChannelDialog({ @@ -646,7 +645,9 @@ function EditChannelDialog({ diff --git a/app/src/components/History/HistoryTable.tsx b/app/src/components/History/HistoryTable.tsx index 198bf937..77380dd6 100644 --- a/app/src/components/History/HistoryTable.tsx +++ b/app/src/components/History/HistoryTable.tsx @@ -17,6 +17,7 @@ import { } from '@/components/ui/dropdown-menu'; import { Textarea } from '@/components/ui/textarea'; import { apiClient } from '@/lib/api/client'; +import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui'; import { useDeleteGeneration, useExportGeneration, @@ -27,7 +28,6 @@ import { import { cn } from '@/lib/utils/cn'; import { formatDate, formatDuration } from '@/lib/utils/format'; import { usePlayerStore } from '@/stores/playerStore'; -import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui'; // OLD TABLE-BASED COMPONENT - REMOVED (can be found in git history) // This is the new alternate history view with fixed height rows diff --git a/app/src/components/ServerSettings/ModelManagement.tsx b/app/src/components/ServerSettings/ModelManagement.tsx index 41d2143b..2b6f4e67 100644 --- a/app/src/components/ServerSettings/ModelManagement.tsx +++ b/app/src/components/ServerSettings/ModelManagement.tsx @@ -1,13 +1,6 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { CheckCircle2, Download, Loader2, Trash2 } from 'lucide-react'; import { useState } from 'react'; -import { apiClient } from '@/lib/api/client'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Loader2, Download, CheckCircle2, Trash2 } from 'lucide-react'; -import { ModelProgress } from './ModelProgress'; -import { useToast } from '@/components/ui/use-toast'; -import { useModelDownloadToast } from '@/lib/hooks/useModelDownloadToast'; import { AlertDialog, AlertDialogAction, @@ -18,6 +11,13 @@ import { AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { useToast } from '@/components/ui/use-toast'; +import { apiClient } from '@/lib/api/client'; +import { useModelDownloadToast } from '@/lib/hooks/useModelDownloadToast'; +import { ModelProgress } from './ModelProgress'; export function ModelManagement() { const { toast } = useToast(); @@ -197,8 +197,8 @@ export function ModelManagement() { {modelToDelete?.sizeMb && ( <> {' '} - This will free up {formatSize(modelToDelete.sizeMb)} of disk space. The model - will need to be re-downloaded if you want to use it again. + This will free up {formatSize(modelToDelete.sizeMb)} of disk space. The model will + need to be re-downloaded if you want to use it again. )} @@ -244,13 +244,7 @@ interface ModelItemProps { formatSize: (sizeMb?: number) => string; } -function ModelItem({ - model, - onDownload, - onDelete, - isDownloading, - formatSize, -}: ModelItemProps) { +function ModelItem({ model, onDownload, onDelete, isDownloading, formatSize }: ModelItemProps) { return (
diff --git a/app/src/components/VoicesTab/VoicesTab.tsx b/app/src/components/VoicesTab/VoicesTab.tsx index 4c2b8acd..12fedef5 100644 --- a/app/src/components/VoicesTab/VoicesTab.tsx +++ b/app/src/components/VoicesTab/VoicesTab.tsx @@ -17,15 +17,15 @@ import { TableHeader, TableRow, } from '@/components/ui/table'; +import { ProfileForm } from '@/components/VoiceProfiles/ProfileForm'; import { apiClient } from '@/lib/api/client'; import type { VoiceProfileResponse } from '@/lib/api/types'; +import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui'; import { useHistory } from '@/lib/hooks/useHistory'; import { useDeleteProfile, useProfileSamples, useProfiles } from '@/lib/hooks/useProfiles'; -import { useUIStore } from '@/stores/uiStore'; -import { ProfileForm } from '@/components/VoiceProfiles/ProfileForm'; -import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui'; import { cn } from '@/lib/utils/cn'; import { usePlayerStore } from '@/stores/playerStore'; +import { useUIStore } from '@/stores/uiStore'; export function VoicesTab() { const { data: profiles, isLoading } = useProfiles();