// ==UserScript==
// @name Claude Project Delete Button
// @namespace Violentmonkey Scripts
// @match https://claude.ai/project/*
// @grant none
// @version 1.0
// @author Elias Benbourenane
// @description Add a delete button to Claude projects
// @license MIT
// ==/UserScript==
(function() {
'use strict';
function getProjectId() {
const path = window.location.pathname;
const match = path.match(/\/project\/([^\/]+)/);
return match ? match[1] : null;
}
function getOrgId() {
return window.intercomSettings?.lastActiveOrgUUID;
}
function createDeleteButton() {
const deleteButton = document.createElement('button');
deleteButton.className = `inline-flex
items-center
justify-center
relative
shrink-0
can-focus
select-none
disabled:pointer-events-none
disabled:opacity-50
disabled:shadow-none
disabled:drop-shadow-none
text-text-300
border-transparent
transition
font-styrene
duration-300
ease-[cubic-bezier(0.165,0.85,0.45,1)]
hover:bg-bg-400
aria-pressed:bg-bg-400
aria-checked:bg-bg-400
aria-expanded:bg-bg-300
hover:text-text-100
aria-pressed:text-text-100
aria-checked:text-text-100
aria-expanded:text-text-100
h-8 w-8 rounded-md active:scale-95`.replace(/\s+/g, ' ');
deleteButton.type = 'button';
deleteButton.title = 'Delete Project';
// Trash icon SVG
deleteButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 256 256" class="-translate-y-[0.5px]">
<path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path>
</svg>
`;
deleteButton.addEventListener('click', handleDeleteClick);
return deleteButton;
}
async function handleDeleteClick(event) {
event.preventDefault();
const projectId = getProjectId();
const orgId = getOrgId();
if (!projectId || !orgId) {
alert('Unable to determine project or organization ID');
return;
}
if (!confirm('Are you sure you want to delete this project? This action cannot be undone.')) {
return;
}
try {
const response = await fetch(`https://claude.ai/api/organizations/${orgId}/projects/${projectId}`, {
method: 'DELETE',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
alert('Project deleted successfully');
window.location.href = 'https://claude.ai/';
} else {
throw new Error(`Delete failed: ${response.status} ${response.statusText}`);
}
} catch (error) {
console.error('Error deleting project:', error);
alert('Failed to delete project. Please try again.');
}
}
function addDeleteButton() {
// Look for the container with the star and menu buttons
const buttonContainer = document.querySelector('.flex.items-center.gap-1.ml-auto');
if (buttonContainer && !buttonContainer.querySelector('[title="Delete Project"]')) {
const deleteButton = createDeleteButton();
// Insert the delete button before the menu button (last button)
const menuButton = buttonContainer.lastElementChild;
buttonContainer.insertBefore(deleteButton, menuButton);
}
}
let currentUrl = window.location.href;
function isProjectPage() {
return window.location.pathname.startsWith('/project/');
}
function handleUrlChange() {
const newUrl = window.location.href;
if (newUrl !== currentUrl) {
currentUrl = newUrl;
if (isProjectPage()) {
// Delay to allow page content to load
setTimeout(addDeleteButton, 500);
setTimeout(addDeleteButton, 1500);
setTimeout(addDeleteButton, 3000);
}
}
}
// Override history methods to detect programmatic navigation
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function(...args) {
originalPushState.apply(history, args);
setTimeout(handleUrlChange, 0);
};
history.replaceState = function(...args) {
originalReplaceState.apply(history, args);
setTimeout(handleUrlChange, 0);
};
function init() {
if (isProjectPage()) {
// Try to add the button immediately
addDeleteButton();
// Also try after delays in case the page is still loading
setTimeout(addDeleteButton, 500);
setTimeout(addDeleteButton, 1500);
setTimeout(addDeleteButton, 3000);
}
// Set up a mutation observer to handle dynamic content loading
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
// Only try to add button if we're on a project page
if (isProjectPage()) {
addDeleteButton();
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Listen for browser back/forward navigation
window.addEventListener('popstate', handleUrlChange);
// Periodic check to ensure button is present (fallback)
setInterval(() => {
if (isProjectPage()) {
addDeleteButton();
}
}, 2000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();