diff --git a/app/src/components/History/HistoryTable.tsx b/app/src/components/History/HistoryTable.tsx
index 914c7fcb..9a7bdcdd 100644
--- a/app/src/components/History/HistoryTable.tsx
+++ b/app/src/components/History/HistoryTable.tsx
@@ -45,6 +45,7 @@ import { apiClient } from '@/lib/api/client';
import type { EffectConfig, GenerationVersionResponse, HistoryResponse } from '@/lib/api/types';
import { BOTTOM_SAFE_AREA_PADDING } from '@/lib/constants/ui';
import {
+ useClearFailedGenerations,
useDeleteGeneration,
useExportGeneration,
useExportGenerationAudio,
@@ -124,6 +125,8 @@ export function HistoryTable() {
});
const deleteGeneration = useDeleteGeneration();
+ const clearFailed = useClearFailedGenerations();
+ const [clearFailedDialogOpen, setClearFailedDialogOpen] = useState(false);
const exportGeneration = useExportGeneration();
const exportGenerationAudio = useExportGenerationAudio();
const importGeneration = useImportGeneration();
@@ -157,11 +160,11 @@ export function HistoryTable() {
const pendingCount = useGenerationStore((state) => state.pendingGenerationIds.size);
const prevPendingCountRef = useRef(pendingCount);
useEffect(() => {
- if (deleteGeneration.isSuccess || importGeneration.isSuccess) {
+ if (deleteGeneration.isSuccess || importGeneration.isSuccess || clearFailed.isSuccess) {
setPage(0);
setAllHistory([]);
}
- }, [deleteGeneration.isSuccess, importGeneration.isSuccess]);
+ }, [deleteGeneration.isSuccess, importGeneration.isSuccess, clearFailed.isSuccess]);
useEffect(() => {
// A generation finished (pending count decreased) — scroll back to show it
@@ -415,6 +418,27 @@ export function HistoryTable() {
const history = allHistory;
const hasMore = allHistory.length < total;
+ const failedCount = history.filter((g) => g.status === 'failed').length;
+
+ const handleClearFailedConfirm = () => {
+ clearFailed.mutate(undefined, {
+ onSuccess: (data) => {
+ setClearFailedDialogOpen(false);
+ toast({
+ title: 'Cleared failed generations',
+ description: `${data.deleted} failed ${data.deleted === 1 ? 'generation' : 'generations'} removed.`,
+ });
+ },
+ onError: (error) => {
+ setClearFailedDialogOpen(false);
+ toast({
+ title: 'Failed to clear',
+ description: error instanceof Error ? error.message : 'Unknown error',
+ variant: 'destructive',
+ });
+ },
+ });
+ };
return (
@@ -424,6 +448,23 @@ export function HistoryTable() {
) : (
<>
+ {failedCount > 0 && (
+
+
+ {failedCount} failed {failedCount === 1 ? 'generation' : 'generations'}
+
+
+
+ )}
{isScrolled && (
)}
@@ -759,6 +800,31 @@ export function HistoryTable() {
+
+