Fix gitignore to allow src/lib and add missing taskCancellation utility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
michael 2025-12-09 11:38:15 -06:00
parent 25a2cd122b
commit 76cfc96df1
2 changed files with 116 additions and 2 deletions

4
.gitignore vendored
View file

@ -88,8 +88,8 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
/lib/
/lib64/
parts/
sdist/
var/

114
src/lib/taskCancellation.ts Normal file
View file

@ -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<TaskCancellationResponse> {
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<boolean> {
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();
}
};
}