Claude.ai | Remove active chat by CTRL+SHIFT+BACKSPACE

Delete active chat in Claude.ai sidebar using Ctrl+Shift+Backspace

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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();
        }
    });

})();