diff --git a/app/package.json b/app/package.json index 0849a94b..07028409 100644 --- a/app/package.json +++ b/app/package.json @@ -17,6 +17,8 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^3.9.0", + "@hugeicons/core-free-icons": "^3.1.1", + "@hugeicons/react": "^1.1.4", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", @@ -44,7 +46,6 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "framer-motion": "^12.29.0", - "lucide-react": "^0.454.0", "motion": "^12.29.0", "react": "^18.3.0", "react-dom": "^18.3.0", diff --git a/app/src/App.tsx b/app/src/App.tsx index fbe29118..e54bc9dd 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -40,7 +40,7 @@ function App() { const serverStartingRef = useRef(false); // Automatically check for app updates on startup and show toast notifications - useAutoUpdater({ checkOnMount: true, showToast: true }); + useAutoUpdater(true); // Sync stored setting to Rust on startup useEffect(() => { @@ -82,8 +82,7 @@ function App() { console.log('Dev mode: Skipping auto-start of server (run it separately)'); setServerReady(true); // Mark as ready so UI doesn't show loading screen // Mark that server was not started by app (so we don't try to stop it on close) - // @ts-expect-error - adding property to window - window.__voiceboxServerStartedByApp = false; + (window as any).__voiceboxServerStartedByApp = false; return; } @@ -103,14 +102,12 @@ function App() { useServerStore.getState().setServerUrl(serverUrl); setServerReady(true); // Mark that we started the server (so we know to stop it on close) - // @ts-expect-error - adding property to window - window.__voiceboxServerStartedByApp = true; + (window as any).__voiceboxServerStartedByApp = true; }) .catch((error) => { console.error('Failed to auto-start server:', error); serverStartingRef.current = false; - // @ts-expect-error - adding property to window - window.__voiceboxServerStartedByApp = false; + (window as any).__voiceboxServerStartedByApp = false; }); // Cleanup: stop server on actual unmount (not StrictMode remount) diff --git a/app/src/components/AudioPlayer/AudioPlayer.tsx b/app/src/components/AudioPlayer/AudioPlayer.tsx index 48dd9e78..c49f7a38 100644 --- a/app/src/components/AudioPlayer/AudioPlayer.tsx +++ b/app/src/components/AudioPlayer/AudioPlayer.tsx @@ -1,5 +1,6 @@ import { useQuery } from '@tanstack/react-query'; -import { Pause, Play, Repeat, Volume2, VolumeX, X } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { PauseIcon, PlayIcon, RepeatIcon, VolumeHighIcon, VolumeMuteIcon, Cancel01Icon } from '@hugeicons/core-free-icons'; import { useEffect, useMemo, useRef, useState } from 'react'; import WaveSurfer from 'wavesurfer.js'; import { Button } from '@/components/ui/button'; @@ -832,7 +833,7 @@ export function AudioPlayer() { className="shrink-0" title={duration === 0 && !isLoading ? 'Audio not loaded' : ''} > - {isPlaying ? : } + {isPlaying ? : } {/* Waveform */} @@ -873,7 +874,7 @@ export function AudioPlayer() { className={isLooping ? 'text-primary' : ''} title="Toggle loop" > - + {/* Volume Control */} @@ -884,7 +885,7 @@ export function AudioPlayer() { onClick={() => setVolume(volume > 0 ? 0 : 1)} className="h-8 w-8" > - {volume > 0 ? : } + {volume > 0 ? : } - + diff --git a/app/src/components/AudioTab/AudioTab.tsx b/app/src/components/AudioTab/AudioTab.tsx index f76e99d7..150e7660 100644 --- a/app/src/components/AudioTab/AudioTab.tsx +++ b/app/src/components/AudioTab/AudioTab.tsx @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Check, CheckCircle2, Edit, Plus, Speaker, Trash2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { CheckmarkCircle01Icon, CheckmarkCircle02Icon, Edit01Icon, Add01Icon, SpeakerIcon, Delete01Icon } from '@hugeicons/core-free-icons'; import { useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -135,7 +136,7 @@ export function AudioTab() {

Audio Channels

@@ -150,13 +151,13 @@ export function AudioTab() { > {allChannels.length === 0 ? (
- +

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

@@ -178,7 +179,7 @@ export function AudioTab() {
- +

{channel.name}

@@ -235,7 +236,7 @@ export function AudioTab() { setEditingChannel(channel.id); }} > - +
)} @@ -325,10 +326,10 @@ export function AudioTab() { isConnected ? 'bg-accent border-accent' : 'border-muted-foreground/30', )} > - {isConnected && } + {isConnected && }
) : device.is_default ? ( - + ) : null} {device.name} @@ -339,7 +340,7 @@ export function AudioTab() {
) : (
- +

{platform.metadata.isTauri ? 'No audio devices found' : 'Audio device selection requires Tauri'}

@@ -494,7 +495,7 @@ function CreateChannelDialog({ open, onOpenChange, devices, onCreate }: CreateCh setSelectedDevices(selectedDevices.filter((id) => id !== deviceId)) } > - +
); @@ -602,7 +603,7 @@ function EditChannelDialog({ setSelectedDevices(selectedDevices.filter((id) => id !== deviceId)) } > - + ); @@ -648,7 +649,7 @@ function EditChannelDialog({ setSelectedVoices(selectedVoices.filter((id) => id !== profileId)) } > - + ); diff --git a/app/src/components/Generation/FloatingGenerateBox.tsx b/app/src/components/Generation/FloatingGenerateBox.tsx index b020a81f..dc835dfb 100644 --- a/app/src/components/Generation/FloatingGenerateBox.tsx +++ b/app/src/components/Generation/FloatingGenerateBox.tsx @@ -1,6 +1,7 @@ import { useMatchRoute } from '@tanstack/react-router'; import { AnimatePresence, motion } from 'framer-motion'; -import { Loader2, MessageSquare, Sparkles } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Loading01Icon, TextSquareIcon, SparklesIcon } from '@hugeicons/core-free-icons'; import { useEffect, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form'; @@ -301,9 +302,9 @@ export function FloatingGenerateBox({ size="icon" > {isPending ? ( - + ) : ( - + )} @@ -327,7 +328,7 @@ export function FloatingGenerateBox({ : 'bg-card border border-border hover:bg-background/50', )} > - + )} diff --git a/app/src/components/Generation/GenerationForm.tsx b/app/src/components/Generation/GenerationForm.tsx index 31b100f8..edf7b749 100644 --- a/app/src/components/Generation/GenerationForm.tsx +++ b/app/src/components/Generation/GenerationForm.tsx @@ -1,4 +1,5 @@ -import { Loader2, Mic } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Loading01Icon, Mic01Icon } from '@hugeicons/core-free-icons'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { @@ -46,7 +47,7 @@ export function GenerationForm() { Voice Profile {selectedProfile ? (
- + {selectedProfile.name} {selectedProfile.language}
@@ -177,7 +178,7 @@ export function GenerationForm() { > {isPending ? ( <> - + Generating... ) : ( diff --git a/app/src/components/History/HistoryTable.tsx b/app/src/components/History/HistoryTable.tsx index e572f69e..0c889046 100644 --- a/app/src/components/History/HistoryTable.tsx +++ b/app/src/components/History/HistoryTable.tsx @@ -1,12 +1,13 @@ +import { HugeiconsIcon } from '@hugeicons/react'; import { - AudioWaveform, - Download, - FileArchive, - Loader2, - MoreHorizontal, - Play, - Trash2, -} from 'lucide-react'; + WaveIcon, + Download01Icon, + Archive01Icon, + Loading01Icon, + MoreHorizontalIcon, + PlayIcon, + Delete01Icon, +} from '@hugeicons/core-free-icons'; import { useEffect, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { @@ -222,7 +223,7 @@ export function HistoryTable() { if (isLoading && page === 0) { return (
- +
); } @@ -268,7 +269,7 @@ export function HistoryTable() { > {/* Waveform icon */}
- +
{/* Left side - Meta information */} @@ -310,28 +311,28 @@ export function HistoryTable() { className="h-8 w-8" aria-label="Actions" > - + handlePlay(gen.id, gen.text, gen.profile_id)} > - + Play handleDownloadAudio(gen.id, gen.text)} disabled={exportGenerationAudio.isPending} > - + Export Audio handleExportPackage(gen.id, gen.text)} disabled={exportGeneration.isPending} > - + Export Package - + Delete @@ -352,7 +353,7 @@ export function HistoryTable() { {/* Load more trigger element */} {hasMore && (
- {isFetching && } + {isFetching && }
)} diff --git a/app/src/components/MainEditor/MainEditor.tsx b/app/src/components/MainEditor/MainEditor.tsx index 9d597b1e..17125893 100644 --- a/app/src/components/MainEditor/MainEditor.tsx +++ b/app/src/components/MainEditor/MainEditor.tsx @@ -1,4 +1,5 @@ -import { Sparkles, Upload } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { SparklesIcon, Upload01Icon } from '@hugeicons/core-free-icons'; import { useRef, useState } from 'react'; import { FloatingGenerateBox } from '@/components/Generation/FloatingGenerateBox'; import { HistoryTable } from '@/components/History/HistoryTable'; @@ -89,7 +90,7 @@ export function MainEditor() {

Voicebox

diff --git a/app/src/components/ServerSettings/ModelManagement.tsx b/app/src/components/ServerSettings/ModelManagement.tsx index 4a5fd439..f7e10fdb 100644 --- a/app/src/components/ServerSettings/ModelManagement.tsx +++ b/app/src/components/ServerSettings/ModelManagement.tsx @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Download, Loader2, Trash2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Download01Icon, Loading01Icon, Delete01Icon } from '@hugeicons/core-free-icons'; import { useCallback, useState } from 'react'; import { AlertDialog, @@ -67,11 +68,11 @@ export function ModelManagement() { const handleDownload = async (modelName: string) => { console.log('[Download] Button clicked for:', modelName, 'at', new Date().toISOString()); - + // Find display name const model = modelStatus?.models.find((m) => m.model_name === modelName); const displayName = model?.display_name || modelName; - + try { // IMPORTANT: Call the API FIRST before setting state // Setting state enables the SSE EventSource in useModelDownloadToast, @@ -79,11 +80,11 @@ export function ModelManagement() { console.log('[Download] Calling download API for:', modelName); const result = await apiClient.triggerModelDownload(modelName); console.log('[Download] Download API responded:', result); - + // NOW set state to enable SSE tracking (after download has started on backend) setDownloadingModel(modelName); setDownloadingDisplayName(displayName); - + // Download initiated successfully - state will be cleared when SSE reports completion // or by the polling interval detecting the model is downloaded queryClient.invalidateQueries({ queryKey: ['modelStatus'] }); @@ -117,7 +118,7 @@ export function ModelManagement() { // Invalidate AND explicitly refetch to ensure UI updates // Using refetchType: 'all' ensures we refetch even if the query is stale console.log('[Delete] Invalidating modelStatus query'); - await queryClient.invalidateQueries({ + await queryClient.invalidateQueries({ queryKey: ['modelStatus'], refetchType: 'all', }); @@ -153,7 +154,7 @@ export function ModelManagement() { {isLoading ? (
- +
) : modelStatus ? (
@@ -212,7 +213,6 @@ export function ModelManagement() { ))}
- ) : null}
@@ -246,7 +246,7 @@ export function ModelManagement() { > {deleteMutation.isPending ? ( <> - + Deleting... ) : ( @@ -265,20 +265,20 @@ interface ModelItemProps { model_name: string; display_name: string; downloaded: boolean; - downloading?: boolean; // From server - true if download in progress + downloading?: boolean; // From server - true if download in progress size_mb?: number; loaded: boolean; }; onDownload: () => void; onDelete: () => void; - isDownloading: boolean; // Local state - true if user just clicked download + isDownloading: boolean; // Local state - true if user just clicked download formatSize: (sizeMb?: number) => string; } function ModelItem({ model, onDownload, onDelete, isDownloading, formatSize }: ModelItemProps) { // Use server's downloading state OR local state (for immediate feedback before server updates) const showDownloading = model.downloading || isDownloading; - + return (
@@ -315,17 +315,17 @@ function ModelItem({ model, onDownload, onDelete, isDownloading, formatSize }: M disabled={model.loaded} title={model.loaded ? 'Unload model before deleting' : 'Delete model'} > - +
) : showDownloading ? ( ) : ( )} diff --git a/app/src/components/ServerSettings/ModelProgress.tsx b/app/src/components/ServerSettings/ModelProgress.tsx index 76aa99f1..30229c50 100644 --- a/app/src/components/ServerSettings/ModelProgress.tsx +++ b/app/src/components/ServerSettings/ModelProgress.tsx @@ -1,4 +1,5 @@ -import { Loader2, XCircle } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Loading01Icon, CancelCircleIcon } from '@hugeicons/core-free-icons'; import { useEffect, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Progress } from '@/components/ui/progress'; @@ -74,10 +75,10 @@ export function ModelProgress({ modelName, displayName, isDownloading = false }: const getStatusIcon = () => { switch (progress.status) { case 'error': - return ; + return ; case 'downloading': case 'extracting': - return ; + return ; default: return null; } diff --git a/app/src/components/ServerSettings/ProviderSettings.tsx b/app/src/components/ServerSettings/ProviderSettings.tsx index c18874a9..7ae237aa 100644 --- a/app/src/components/ServerSettings/ProviderSettings.tsx +++ b/app/src/components/ServerSettings/ProviderSettings.tsx @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { Download, Loader2, Trash2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Download01Icon, Loading01Icon, Delete01Icon } from '@hugeicons/core-free-icons'; import { useCallback, useState } from 'react'; import { AlertDialog, @@ -155,7 +156,7 @@ export function ProviderSettings() {
- +
@@ -217,10 +218,10 @@ export function ProviderSettings() { disabled={downloadingProvider === 'pytorch-cuda'} > {downloadingProvider === 'pytorch-cuda' ? ( - + ) : ( <> - + Download (2.4GB) )} @@ -241,7 +242,7 @@ export function ProviderSettings() { size="sm" variant="ghost" > - + )}
@@ -270,10 +271,10 @@ export function ProviderSettings() { disabled={downloadingProvider === 'pytorch-cpu'} > {downloadingProvider === 'pytorch-cpu' ? ( - + ) : ( <> - + Download (300MB) )} @@ -294,7 +295,7 @@ export function ProviderSettings() { size="sm" variant="ghost" > - + )} diff --git a/app/src/components/ServerSettings/ServerStatus.tsx b/app/src/components/ServerSettings/ServerStatus.tsx index 02a94ec2..8b5e5197 100644 --- a/app/src/components/ServerSettings/ServerStatus.tsx +++ b/app/src/components/ServerSettings/ServerStatus.tsx @@ -1,4 +1,5 @@ -import { Loader2, XCircle } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Loading01Icon, CancelCircleIcon } from '@hugeicons/core-free-icons'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { useServerHealth } from '@/lib/hooks/useServer'; @@ -32,12 +33,12 @@ export function ServerStatus() { {isLoading ? (
- + Checking connection...
) : error ? (
- + Connection failed: {error.message}
) : health ? ( diff --git a/app/src/components/ServerSettings/UpdateStatus.tsx b/app/src/components/ServerSettings/UpdateStatus.tsx index a3d832aa..5cab320e 100644 --- a/app/src/components/ServerSettings/UpdateStatus.tsx +++ b/app/src/components/ServerSettings/UpdateStatus.tsx @@ -1,4 +1,5 @@ -import { AlertCircle, Download, RefreshCw } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { AlertCircleIcon, Download01Icon, Refresh01Icon } from '@hugeicons/core-free-icons'; import { useEffect, useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -36,21 +37,21 @@ export function UpdateStatus() { variant="outline" size="sm" > - + Check for Updates {status.checking && (
- + Checking for updates...
)} {status.error && (
- + {status.error}
)} @@ -65,7 +66,7 @@ export function UpdateStatus() { New @@ -75,7 +76,7 @@ export function UpdateStatus() {
- + Downloading update...
{status.downloadProgress !== undefined && ( @@ -109,7 +110,7 @@ export function UpdateStatus() { your convenience.
diff --git a/app/src/components/Sidebar.tsx b/app/src/components/Sidebar.tsx index a849344f..04442db0 100644 --- a/app/src/components/Sidebar.tsx +++ b/app/src/components/Sidebar.tsx @@ -1,5 +1,6 @@ import { Link, useMatchRoute } from '@tanstack/react-router'; -import { Box, BookOpen, Loader2, Mic, Server, Speaker, Volume2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { PackageIcon, Book01Icon, Loading01Icon, Mic01Icon, McpServerIcon, SpeakerIcon, VolumeHighIcon } from '@hugeicons/core-free-icons'; import voiceboxLogo from '@/assets/voicebox-logo.png'; import { cn } from '@/lib/utils/cn'; import { useGenerationStore } from '@/stores/generationStore'; @@ -10,12 +11,12 @@ interface SidebarProps { } const tabs = [ - { id: 'main', path: '/', icon: Volume2, label: 'Generate' }, - { id: 'stories', path: '/stories', icon: BookOpen, label: 'Stories' }, - { id: 'voices', path: '/voices', icon: Mic, label: 'Voices' }, - { id: 'audio', path: '/audio', icon: Speaker, label: 'Audio' }, - { id: 'models', path: '/models', icon: Box, label: 'Models' }, - { id: 'server', path: '/server', icon: Server, label: 'Server' }, + { id: 'main', path: '/', icon: VolumeHighIcon, label: 'Generate' }, + { id: 'stories', path: '/stories', icon: Book01Icon, label: 'Stories' }, + { id: 'voices', path: '/voices', icon: Mic01Icon, label: 'Voices' }, + { id: 'audio', path: '/audio', icon: SpeakerIcon, label: 'Audio' }, + { id: 'models', path: '/models', icon: PackageIcon, label: 'Models' }, + { id: 'server', path: '/server', icon: McpServerIcon, label: 'Server' }, ]; export function Sidebar({ isMacOS }: SidebarProps) { @@ -43,7 +44,7 @@ export function Sidebar({ isMacOS }: SidebarProps) { // For index route, use exact match; for others, use default matching const isActive = tab.path === '/' - ? matchRoute({ to: '/', exact: true }) + ? matchRoute({ to: '/' }) : matchRoute({ to: tab.path }); return ( @@ -58,7 +59,7 @@ export function Sidebar({ isMacOS }: SidebarProps) { title={tab.label} aria-label={tab.label} > - + ); })} @@ -75,7 +76,7 @@ export function Sidebar({ isMacOS }: SidebarProps) { isPlayerVisible ? 'mb-[120px]' : 'mb-0', )} > - + )} diff --git a/app/src/components/StoriesTab/StoryChatItem.tsx b/app/src/components/StoriesTab/StoryChatItem.tsx index 19fa2249..c1dd793f 100644 --- a/app/src/components/StoriesTab/StoryChatItem.tsx +++ b/app/src/components/StoriesTab/StoryChatItem.tsx @@ -1,6 +1,7 @@ import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import { GripVertical, Mic, MoreHorizontal, Play, Trash2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { DragDropVerticalIcon, Mic01Icon, MoreHorizontalIcon, PlayIcon, Delete01Icon } from '@hugeicons/core-free-icons'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { @@ -74,7 +75,7 @@ export function StoryChatItem({ className="shrink-0 cursor-grab active:cursor-grabbing touch-none text-muted-foreground hover:text-foreground transition-colors" {...dragHandleProps} > - + )} @@ -92,7 +93,7 @@ export function StoryChatItem({ onError={() => setAvatarError(true)} /> ) : ( - + )} @@ -119,16 +120,16 @@ export function StoryChatItem({ - + Play from here - + Remove from Story diff --git a/app/src/components/StoriesTab/StoryContent.tsx b/app/src/components/StoriesTab/StoryContent.tsx index 483e6657..518c42a0 100644 --- a/app/src/components/StoriesTab/StoryContent.tsx +++ b/app/src/components/StoriesTab/StoryContent.tsx @@ -13,7 +13,8 @@ import { sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; -import { Download, Plus } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Download01Icon, Add01Icon } from '@hugeicons/core-free-icons'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -271,7 +272,7 @@ export function StoryContent() { @@ -316,7 +317,7 @@ export function StoryContent() { onClick={handleExportAudio} disabled={exportAudio.isPending} > - + Export Audio )} diff --git a/app/src/components/StoriesTab/StoryList.tsx b/app/src/components/StoriesTab/StoryList.tsx index ebbd6616..a283a34a 100644 --- a/app/src/components/StoriesTab/StoryList.tsx +++ b/app/src/components/StoriesTab/StoryList.tsx @@ -1,4 +1,5 @@ -import { Plus, BookOpen, MoreHorizontal, Pencil, Trash2 } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Add01Icon, Book01Icon, MoreHorizontalIcon, PencilIcon, Delete01Icon } from '@hugeicons/core-free-icons'; import { useState } from 'react'; import { AlertDialog, @@ -177,7 +178,7 @@ export function StoryList() {

Stories

@@ -186,7 +187,7 @@ export function StoryList() {
{storyList.length === 0 ? (
- +

No stories yet

Create your first story to get started

@@ -227,19 +228,19 @@ export function StoryList() { className="h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity" onClick={(e) => e.stopPropagation()} > - + handleEditClick(story)}> - + Edit handleDeleteClick(story.id)} className="text-destructive focus:text-destructive" > - + Delete diff --git a/app/src/components/StoriesTab/StoryTrackEditor.tsx b/app/src/components/StoriesTab/StoryTrackEditor.tsx index 74dbde25..95a10296 100644 --- a/app/src/components/StoriesTab/StoryTrackEditor.tsx +++ b/app/src/components/StoriesTab/StoryTrackEditor.tsx @@ -1,14 +1,15 @@ +import { HugeiconsIcon } from '@hugeicons/react'; import { - Copy, - GripHorizontal, - Minus, - Pause, - Play, - Plus, - Scissors, - Square, - Trash2, -} from 'lucide-react'; + Copy01Icon, + DragDropHorizontalIcon, + RemoveIcon, + PauseIcon, + PlayIcon, + Add01Icon, + Scissor01Icon, + SquareIcon, + Delete01Icon, +} from '@hugeicons/core-free-icons'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import WaveSurfer from 'wavesurfer.js'; import { Button } from '@/components/ui/button'; @@ -723,7 +724,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) { onMouseDown={handleResizeStart} aria-label="Resize track editor" > - + {/* Toolbar */} @@ -737,7 +738,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) { onClick={handlePlayPause} title="Play/Pause (Space)" > - {isCurrentlyPlaying ? : } + {isCurrentlyPlaying ? : } {formatTime(currentTimeMs)} / {formatTime(totalDurationMs)} @@ -763,7 +764,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) { onClick={handleSplit} title="Split at playhead (S)" > - +
)} @@ -790,10 +791,10 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) {
Zoom:
@@ -837,7 +838,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) { type="button" className="h-6 border-b bg-muted/20 sticky top-0 z-10 cursor-pointer text-left" style={{ width: `${timelineWidth}px` }} - onClick={handleTimelineClick} + onClick={(e) => handleTimelineClick(e as unknown as React.MouseEvent)} aria-label="Seek timeline" > {timeMarkers.map((ms) => ( @@ -878,7 +879,7 @@ export function StoryTrackEditor({ storyId, items }: StoryTrackEditorProps) {

@@ -122,7 +123,7 @@ export function AudioSampleRecording({ onClick={onStop} className="relative z-10 flex items-center gap-2 bg-accent text-accent-foreground hover:bg-accent/90" > - + Stop Recording

@@ -134,13 +135,13 @@ export function AudioSampleRecording({ {file && !isRecording && (

- + Recording complete

File: {file.name}

@@ -60,7 +61,7 @@ export function AudioSampleSystem({ variant="destructive" className="flex items-center gap-2" > - + Stop Capture

@@ -72,13 +73,13 @@ export function AudioSampleSystem({ {file && !isRecording && (

- + Capture complete

File: {file.name}

@@ -99,7 +100,7 @@ export function AudioSampleUpload({ ) : ( <>

- + File uploaded

File: {file.name}

@@ -111,7 +112,7 @@ export function AudioSampleUpload({ onClick={onPlayPause} disabled={isValidating} > - {isPlaying ? : } + {isPlaying ? : }
{profile.name} @@ -101,13 +102,13 @@ export function ProfileCard({ profile }: ProfileCardProps) {
} onClick={handleExport} disabled={exportProfile.isPending} aria-label="Export profile" /> } onClick={(e) => { e.stopPropagation(); handleEdit(); @@ -115,7 +116,7 @@ export function ProfileCard({ profile }: ProfileCardProps) { aria-label="Edit profile" /> } onClick={handleDeleteClick} disabled={deleteProfile.isPending} aria-label="Delete profile" diff --git a/app/src/components/VoiceProfiles/ProfileForm.tsx b/app/src/components/VoiceProfiles/ProfileForm.tsx index f4fc5711..3fcd987e 100644 --- a/app/src/components/VoiceProfiles/ProfileForm.tsx +++ b/app/src/components/VoiceProfiles/ProfileForm.tsx @@ -1,5 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { Edit2, Mic, Monitor, Upload, X } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Edit02Icon, Mic01Icon, DeskIcon, Upload01Icon, Cancel01Icon } from '@hugeicons/core-free-icons'; import { useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import * as z from 'zod'; @@ -635,7 +636,7 @@ export function ProfileForm() { setSampleMode('record'); }} > - + Discard
@@ -668,16 +669,16 @@ export function ProfileForm() { className={`grid w-full ${platform.metadata.isTauri && isSystemAudioSupported ? 'grid-cols-3' : 'grid-cols-2'}`} > - + Upload - + Record {platform.metadata.isTauri && isSystemAudioSupported && ( - + System Audio )} @@ -798,7 +799,7 @@ export function ProfileForm() { className="h-full w-full object-cover" /> ) : ( - + )}
{(avatarPreview || editingProfile?.avatar_path) && ( )}
diff --git a/app/src/components/VoiceProfiles/ProfileList.tsx b/app/src/components/VoiceProfiles/ProfileList.tsx index 8dcb06a4..4e57bfdb 100644 --- a/app/src/components/VoiceProfiles/ProfileList.tsx +++ b/app/src/components/VoiceProfiles/ProfileList.tsx @@ -1,4 +1,5 @@ -import { Mic, Sparkles } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { Mic01Icon, SparklesIcon } from '@hugeicons/core-free-icons'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { useProfiles } from '@/lib/hooks/useProfiles'; @@ -30,12 +31,12 @@ export function ProfileList() { {allProfiles.length === 0 ? ( - +

No voice profiles yet. Create your first profile to get started.

diff --git a/app/src/components/VoiceProfiles/SampleList.tsx b/app/src/components/VoiceProfiles/SampleList.tsx index 19aa1ca8..17664afd 100644 --- a/app/src/components/VoiceProfiles/SampleList.tsx +++ b/app/src/components/VoiceProfiles/SampleList.tsx @@ -1,4 +1,5 @@ -import { Check, Edit, Pause, Play, Plus, Trash2, Volume2, X } from 'lucide-react'; +import { HugeiconsIcon } from '@hugeicons/react'; +import { CheckmarkCircle01Icon, Edit01Icon, PauseIcon, PlayIcon, Add01Icon, Delete01Icon, VolumeHighIcon, Cancel01Icon } from '@hugeicons/core-free-icons'; import { useEffect, useRef, useState } from 'react'; import { Button } from '@/components/ui/button'; import { CircleButton } from '@/components/ui/circle-button'; @@ -103,7 +104,7 @@ function MiniSamplePlayer({ audioUrl }: MiniSamplePlayerProps) { onClick={handlePlayPause} disabled={isLoading} > - {isPlaying ? : } + {isPlaying ? : }
@@ -129,7 +130,7 @@ function MiniSamplePlayer({ audioUrl }: MiniSamplePlayerProps) { onClick={handleStop} title="Stop" > - +
@@ -209,7 +210,7 @@ export function SampleList({ profileId }: SampleListProps) {
{samples && samples.length === 0 ? (
- +

No samples yet

Add your first audio sample to get started @@ -232,7 +233,7 @@ export function SampleList({ profileId }: SampleListProps) { /* Edit Mode */

- + Editing transcription