diff --git a/.gitignore b/.gitignore index 56acdbe8..3ad53aaf 100644 --- a/.gitignore +++ b/.gitignore @@ -88,8 +88,8 @@ dist/ downloads/ eggs/ .eggs/ -lib/ -lib64/ +/lib/ +/lib64/ parts/ sdist/ var/ diff --git a/src/lib/taskCancellation.ts b/src/lib/taskCancellation.ts new file mode 100644 index 00000000..9c138416 --- /dev/null +++ b/src/lib/taskCancellation.ts @@ -0,0 +1,114 @@ +/** + * Task cancellation utilities for long-running generation processes. + */ + +import { toast } from 'sonner'; + +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'; + +export interface TaskCancellationResponse { + success: boolean; + message?: string; + error?: string; +} + +/** + * Cancel a task by its ID + */ +export async function cancelTask(taskId: string): Promise { + try { + const url = `${API_BASE_URL}/tasks/${taskId}`; + + const response = await fetch(url, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + if (response.status === 404) { + return { + success: false, + error: 'Task not found or already completed', + }; + } + + const errorData = await response.json().catch(() => ({})); + return { + success: false, + error: errorData.error || `Failed to cancel task: ${response.statusText}`, + }; + } + + const data = await response.json(); + return { + success: true, + message: data.message || 'Task cancelled successfully', + }; + } catch (error) { + console.error('Error cancelling task:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Network error while cancelling task', + }; + } +} + +/** + * Cancel a task with user feedback + */ +export async function cancelTaskWithFeedback(taskId: string, taskDescription: string = 'task'): Promise { + try { + const result = await cancelTask(taskId); + + if (result.success) { + toast.success(result.message || `${taskDescription} cancelled successfully`); + return true; + } else { + toast.error(result.error || `Failed to cancel ${taskDescription}`); + return false; + } + } catch (error) { + console.error('Error cancelling task with feedback:', error); + toast.error(`Failed to cancel ${taskDescription}`); + return false; + } +} + +/** + * Hook-style function for creating a cancellation handler + */ +export function useCancellationHandler(taskId: string | null, onCancel?: () => void) { + return async () => { + if (!taskId) { + console.warn('No task ID available for cancellation'); + return false; + } + + const success = await cancelTaskWithFeedback(taskId, 'generation'); + + if (success && onCancel) { + onCancel(); + } + + return success; + }; +} + +/** + * Utility for handling WebSocket cancellation events + */ +export function handleWebSocketCancellation( + taskId: string, + onCancelled: () => void, + taskDescription: string = 'task' +) { + // This will be implemented when we add WebSocket support + return (event: any) => { + if (event.task_id === taskId && event.type === 'task_cancelled') { + toast.info(`${taskDescription} was cancelled`); + onCancelled(); + } + }; +} \ No newline at end of file