Claude.ai Bulk Delete Automation

One‑click wipe for Claude.ai Recents. Press once; script keeps looping across reloads until nothing is left.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Claude.ai Bulk Delete Automation
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  One‑click wipe for Claude.ai Recents. Press once; script keeps looping across reloads until nothing is left.
// @match        https://claude.ai/recents*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  /* ========= CONFIG ========= */
  const CLICK_DELAY  = 1500;  // ms between each UI click
  const RELOAD_DELAY = 5000;  // ms to wait after confirming delete before reload
  const START_DELAY  = 2000;  // ms after page load before auto-resume kicks in
  const BUTTON_LABEL = 'Bulk Delete';
  /* ========================== */

  // Use sessionStorage so the flag clears when the last Claude tab closes
  const storage  = sessionStorage;
  const FLAG_KEY = '__bulkDeleteActive';   // key to persist automation state within the session

  const wait = ms => new Promise(r => setTimeout(r, ms));

  /**
   * Clicks the first <button> that satisfies the predicate, polling until timeout.
   * @returns {boolean} true if clicked; false on timeout.
   */
  async function clickWhenAvailable(predicate, timeout = 10000) {
    const start = performance.now();
    while (performance.now() - start < timeout) {
      const btn = [...document.querySelectorAll('button')].find(predicate);
      if (btn) {
        btn.click();
        return true;
      }
      await wait(250);
    }
    return false;
  }

  /**
   * Runs a single delete cycle. Returns true if it executed (items were present),
   * false if it found nothing to delete (Select button missing) so we can stop.
   */
  async function runOnce(btnEl) {
    if (btnEl) btnEl.textContent = 'Running…';

    // 1) "Select" (first conversation row)
    if (!await clickWhenAvailable(b => b.textContent.trim() === 'Select')) return false;
    await wait(CLICK_DELAY);

    // 2) "Select all"
    if (!await clickWhenAvailable(b => b.textContent.trim() === 'Select all')) return false;
    await wait(CLICK_DELAY);

    // 3) "Delete Selected"
    if (!await clickWhenAvailable(b => /Delete\s+Selected/i.test(b.textContent))) return false;
    await wait(CLICK_DELAY);

    // 4) Modal confirm "Delete"
    if (!await clickWhenAvailable(b => b.dataset.testid === 'delete-modal-confirm')) return false;

    // 5) Wait for backend, then refresh to pick up next batch
    setTimeout(() => location.reload(), RELOAD_DELAY);
    return true;  // a reload is imminent; automation continues on next page load
  }

  /** Inject a floating control button (once) */
  function addControlButton() {
    if (document.getElementById('__bulkDeleteBtn')) return;

    const btn = document.createElement('button');
    btn.id = '__bulkDeleteBtn';
    btn.textContent = BUTTON_LABEL;
    Object.assign(btn.style, {
      position: 'fixed',
      top: '1rem',
      right: '1rem',
      zIndex: 9999,
      background: '#d32f2f',
      color: '#fff',
      border: 'none',
      padding: '0.5rem 1rem',
      borderRadius: '6px',
      fontSize: '14px',
      cursor: 'pointer',
      boxShadow: '0 2px 6px rgba(0,0,0,0.3)'
    });

    btn.addEventListener('click', async () => {
      if (storage.getItem(FLAG_KEY)) return;   // already running
      storage.setItem(FLAG_KEY, '1');
      btn.disabled = true;
      await runOnce(btn);  // may trigger reload
    });

    document.body.appendChild(btn);
  }

  /** Auto-resume if the flag is set (user already started automation earlier) */
  async function autoResume() {
    if (!storage.getItem(FLAG_KEY)) return;            // not active
    const btn = document.getElementById('__bulkDeleteBtn');
    if (btn) {
      btn.disabled = true;
      btn.textContent = 'Running…';
    }

    await wait(START_DELAY);
    const worked = await runOnce(btn);
    if (!worked) {
      // No more items — stop automation
      storage.removeItem(FLAG_KEY);
      if (btn) {
        btn.disabled = false;
        btn.textContent = 'Done';
      }
    }
  }

  window.addEventListener('load', () => {
    addControlButton();
    autoResume();
  });
})();