diff --git a/frontend/src/components/BuiltInModelManager.tsx b/frontend/src/components/BuiltInModelManager.tsx index e1cb8ba..251f2e5 100644 --- a/frontend/src/components/BuiltInModelManager.tsx +++ b/frontend/src/components/BuiltInModelManager.tsx @@ -9,6 +9,7 @@ import { cn } from '@/lib/utils'; import { Download, RefreshCw, BadgeAlert, Trash2 } from 'lucide-react'; import { toast } from 'sonner'; import { formatSummaryModelSizeLabelFromMb } from '@/lib/onboarding-summary-model'; +import { ConfirmationModal } from '@/components/ConfirmationModel/confirmation-modal'; interface ModelInfo { name: string; @@ -46,6 +47,8 @@ export function BuiltInModelManager({ const [downloadProgress, setDownloadProgress] = useState>({}); const [downloadProgressInfo, setDownloadProgressInfo] = useState>({}); const [downloadingModels, setDownloadingModels] = useState>(new Set()); + const [pendingDelete, setPendingDelete] = useState(null); + const [isDeleting, setIsDeleting] = useState(false); const fetchModels = async () => { try { @@ -254,6 +257,17 @@ export function BuiltInModelManager({ } }; + const confirmDelete = async () => { + if (!pendingDelete) return; + setIsDeleting(true); + try { + await deleteModel(pendingDelete.name); + } finally { + setIsDeleting(false); + setPendingDelete(null); + } + }; + // Don't show loading spinner if we have downloads in progress - show the model list instead if (isLoading && downloadingModels.size === 0) { return ( @@ -410,7 +424,7 @@ export function BuiltInModelManager({ size="sm" onClick={(e) => { e.stopPropagation(); - deleteModel(model.name); + setPendingDelete(model); }} > @@ -424,7 +438,7 @@ export function BuiltInModelManager({ className="p-2 rounded hover:bg-gray-100 transition-colors text-gray-500 hover:text-red-600" onClick={(e) => { e.stopPropagation(); - deleteModel(model.name); + setPendingDelete(model); }} title="Delete model" > @@ -487,6 +501,15 @@ export function BuiltInModelManager({ ); })} + { if (!isDeleting) setPendingDelete(null); }} + /> ); } diff --git a/frontend/src/components/ParakeetModelManager.tsx b/frontend/src/components/ParakeetModelManager.tsx index 28749c5..a4b9a5a 100644 --- a/frontend/src/components/ParakeetModelManager.tsx +++ b/frontend/src/components/ParakeetModelManager.tsx @@ -11,6 +11,7 @@ import { getModelDisplayName, formatFileSize } from '../lib/parakeet'; +import { ConfirmationModal } from '@/components/ConfirmationModel/confirmation-modal'; interface ParakeetModelManagerProps { selectedModel?: string; @@ -30,6 +31,8 @@ export function ParakeetModelManager({ const [error, setError] = useState(null); const [initialized, setInitialized] = useState(false); const [downloadingModels, setDownloadingModels] = useState>(new Set()); + const [pendingDelete, setPendingDelete] = useState(null); + const [isDeleting, setIsDeleting] = useState(false); // Refs for stable callbacks const onModelSelectRef = useRef(onModelSelect); @@ -328,6 +331,17 @@ export function ParakeetModelManager({ } }; + const confirmDelete = async () => { + if (!pendingDelete) return; + setIsDeleting(true); + try { + await deleteModel(pendingDelete.name); + } finally { + setIsDeleting(false); + setPendingDelete(null); + } + }; + if (loading) { return (
@@ -370,7 +384,7 @@ export function ParakeetModelManager({ }} onDownload={() => downloadModel(recommendedModel.name)} onCancel={() => cancelDownload(recommendedModel.name)} - onDelete={() => deleteModel(recommendedModel.name)} + onDelete={() => setPendingDelete(recommendedModel)} isDownloading={downloadingModels.has(recommendedModel.name)} /> )} @@ -391,7 +405,7 @@ export function ParakeetModelManager({ }} onDownload={() => downloadModel(model.name)} onCancel={() => cancelDownload(model.name)} - onDelete={() => deleteModel(model.name)} + onDelete={() => setPendingDelete(model)} isDownloading={downloadingModels.has(model.name)} /> ))} @@ -408,6 +422,15 @@ export function ParakeetModelManager({ Using {getModelDisplayName(selectedModel)} for transcription )} + { if (!isDeleting) setPendingDelete(null); }} + />
); } diff --git a/frontend/src/components/WhisperModelManager.tsx b/frontend/src/components/WhisperModelManager.tsx index a52c3f3..5f48493 100644 --- a/frontend/src/components/WhisperModelManager.tsx +++ b/frontend/src/components/WhisperModelManager.tsx @@ -14,6 +14,7 @@ import { WhisperAPI } from '../lib/whisper'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; +import { ConfirmationModal } from '@/components/ConfirmationModel/confirmation-modal'; interface ModelManagerProps { selectedModel?: string; @@ -34,6 +35,8 @@ export function ModelManager({ const [initialized, setInitialized] = useState(false); const [downloadingModels, setDownloadingModels] = useState>(new Set()); const [hasUserSelection, setHasUserSelection] = useState(false); + const [pendingDelete, setPendingDelete] = useState(null); + const [isDeleting, setIsDeleting] = useState(false); // Refs for stable callbacks const onModelSelectRef = useRef(onModelSelect); @@ -373,6 +376,17 @@ export function ModelManager({ } }; + const confirmDelete = async () => { + if (!pendingDelete) return; + setIsDeleting(true); + try { + await deleteModel(pendingDelete.name); + } finally { + setIsDeleting(false); + setPendingDelete(null); + } + }; + const getDisplayName = (modelName: string): string => { const modelNameMapping: { [key: string]: string } = { "small": "Small", @@ -434,7 +448,7 @@ export function ModelManager({ }} onDownload={() => downloadModel(model.name)} onCancel={() => cancelDownload(model.name)} - onDelete={() => deleteModel(model.name)} + onDelete={() => setPendingDelete(model)} isDownloading={downloadingModels.has(model.name)} displayName={getDisplayName(model.name)} /> @@ -464,7 +478,7 @@ export function ModelManager({ }} onDownload={() => downloadModel(model.name)} onCancel={() => cancelDownload(model.name)} - onDelete={() => deleteModel(model.name)} + onDelete={() => setPendingDelete(model)} isDownloading={downloadingModels.has(model.name)} displayName={getDisplayName(model.name)} /> @@ -485,6 +499,15 @@ export function ModelManager({ Using {getDisplayName(selectedModel)} for transcription )} + { if (!isDeleting) setPendingDelete(null); }} + /> ); }