Delete active chat in Claude.ai sidebar using Ctrl+Shift+Backspace
// ==UserScript==
// @name Claude.ai | Remove active chat by CTRL+SHIFT+BACKSPACE
// @namespace http://tampermonkey.net/
// @version 3.3
// @description Delete active chat in Claude.ai sidebar using Ctrl+Shift+Backspace
// @author Saymonn
// @match https://claude.ai/*
// @icon https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQO8QnSw5ArwqF8PsafiMQ3EsH0Xr9LFLgNpwutam6-FN7UhoQvXeyqIHyNvj907vU5BKU&usqp=CAU
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
const CONFIG = {
AUTO_CONFIRM: false,
WAIT_TIME: 200,
TIMEOUT: 3000
};
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const waitForElement = (selector, timeout = CONFIG.TIMEOUT) => {
return new Promise((resolve, reject) => {
const element = document.querySelector(selector);
if (element) return resolve(element);
const observer = new MutationObserver(() => {
const el = document.querySelector(selector);
if (el) {
observer.disconnect();
resolve(el);
}
});
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(() => {
observer.disconnect();
reject(new Error('Timeout'));
}, timeout);
});
};
const waitForFocusable = (element, timeout = CONFIG.TIMEOUT) => {
return new Promise((resolve, reject) => {
const start = Date.now();
const poll = () => {
const dialog = element.closest('[role="dialog"]');
const dialogPointerEvents = dialog
? window.getComputedStyle(dialog).pointerEvents
: 'auto';
const elPointerEvents = window.getComputedStyle(element).pointerEvents;
if (dialogPointerEvents !== 'none' && elPointerEvents !== 'none') {
return resolve(element);
}
if (Date.now() - start >= timeout) {
return reject(new Error('Timeout waiting for focusable'));
}
requestAnimationFrame(poll);
};
requestAnimationFrame(poll);
});
};
const simulateClick = element => {
if (!element) return false;
const rect = element.getBoundingClientRect();
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
[
new PointerEvent('pointerdown', { bubbles: true, pointerId: 1, clientX: x, clientY: y, button: 0 }),
new MouseEvent('mousedown', { bubbles: true, clientX: x, clientY: y, button: 0 }),
new PointerEvent('pointerup', { bubbles: true, pointerId: 1, clientX: x, clientY: y, button: 0 }),
new MouseEvent('mouseup', { bubbles: true, clientX: x, clientY: y, button: 0 }),
new MouseEvent('click', { bubbles: true, clientX: x, clientY: y, button: 0 })
].forEach(event => element.dispatchEvent(event));
return true;
};
const findActiveMenuButton = () => {
const selectors = [
'li a.\\!bg-bg-300',
'li a[aria-current="page"]',
'li a[class*="bg-accent"]',
'li a[class*="active"]'
];
for (const selector of selectors) {
const activeChat = document.querySelector(selector);
if (activeChat) {
const container = activeChat.closest('div.group');
const menuButton = container?.querySelector('button[aria-haspopup="menu"]');
if (menuButton) return menuButton;
}
}
const allLinks = document.querySelectorAll('nav li a[href*="/chat"]');
for (const link of allLinks) {
if (link.getAttribute('href') === window.location.pathname ||
link.classList.contains('!bg-bg-300')) {
const container = link.closest('div.group');
const menuButton = container?.querySelector('button[aria-haspopup="menu"]');
if (menuButton) return menuButton;
}
}
return null;
};
const findDeleteButton = () => {
const selectors = [
'[data-testid="delete-chat-trigger"]',
'[data-testid*="delete"]',
'button[aria-label*="Delete"]',
'button[aria-label*="delete"]',
'[role="menuitem"]'
];
for (const selector of selectors) {
const elements = document.querySelectorAll(selector);
for (const element of elements) {
const text = element.textContent?.toLowerCase() || '';
const label = element.getAttribute('aria-label')?.toLowerCase() || '';
if (text.includes('delete') || label.includes('delete')) {
return element;
}
}
}
return null;
};
const deleteActiveChat = async () => {
try {
const menuButton = findActiveMenuButton();
if (!menuButton) return;
simulateClick(menuButton);
await wait(CONFIG.WAIT_TIME);
let deleteButton = findDeleteButton();
if (!deleteButton) {
simulateClick(menuButton);
await wait(CONFIG.WAIT_TIME);
deleteButton = findDeleteButton();
}
if (!deleteButton) return;
deleteButton.click();
try {
const confirmButton = await waitForElement('[data-testid="delete-modal-confirm"]');
await waitForFocusable(confirmButton);
if (CONFIG.AUTO_CONFIRM) {
confirmButton.click();
} else {
confirmButton.focus();
}
} catch {
const fallbackButton = document.querySelector('[data-testid="delete-modal-confirm"]');
if (fallbackButton) {
if (CONFIG.AUTO_CONFIRM) {
fallbackButton.click();
} else {
fallbackButton.focus();
}
}
}
} catch (error) {
}
};
document.addEventListener('keydown', event => {
if (event.ctrlKey && event.shiftKey && event.code === 'Backspace') {
event.preventDefault();
deleteActiveChat();
}
});
})();