Chatbox 對話列新增快速刪除按鈕

Chatbox 快速刪除快捷鍵

// ==UserScript==
// @name         Chatbox 對話列新增快速刪除按鈕
// @namespace    http://tampermonkey.net/
// @version      1.5.1
// @description  Chatbox 快速刪除快捷鍵
// @author       shanlan(o3-mini)
// @match        https://web.chatboxai.app/*
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(() => {
  const selMenu = '.MuiPopover-root ul[role="menu"], .MuiPopover-root [role="menu"].MuiMenu-list';
  const menu = () => document.querySelector(selMenu);
  const poll = (fn, n = 25, d = 60) => { const t = () => (fn() || --n <= 0) ? 0 : setTimeout(t, d); t(); };

  // 依 data-testid 尋找
  const pickDelete = (m) => m?.querySelector('li[role="menuitem"] svg[data-testid="DeleteIcon"]')?.closest('li[role="menuitem"]');
  const pickCheck = (m) => m?.querySelector('li[role="menuitem"] svg[data-testid="CheckIcon"]')?.closest('li[role="menuitem"]');

  const tryConfirm = () => {
    const m = menu();
    const c = pickCheck(m);
    if (c) { c.click(); return true; }
    return false;
  };
  const tryDeleteThenConfirm = () => {
    const m = menu();
    const d = pickDelete(m);
    if (!d) return false;
    d.click();
    poll(tryConfirm);
    return true;
  };

  const act = (li) => {
    const more = li.querySelector('svg[data-testid="MoreHorizOutlinedIcon"]')?.closest('button');
    if (!more) return;
    more.click();
    poll(() => tryConfirm() || tryDeleteThenConfirm());
  };

  const inject = (li) => {
    if (!li || li.dataset.qd) return;
    const more = li.querySelector('svg[data-testid="MoreHorizOutlinedIcon"]')?.closest('button');
    const star = li.querySelector('svg[data-testid="StarIcon"]')?.closest('button');
    if (!more && !star) return;
    const btn = document.createElement('button');
    btn.type = 'button'; btn.title = 'Delete'; btn.dataset.qd = '1';
    btn.className = 'MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeMedium css-pbh24y';
    btn.innerHTML = 'X';
    btn.style.background = '#e53935';
    btn.style.color = '#fff';
    btn.style.border = 'none';
    btn.style.borderRadius = '0';
    btn.style.fontWeight = 'bold';
    btn.style.fontSize = '16px';
    btn.style.padding = '4px';
    btn.style.display = 'none';
    btn.onmouseover = () => btn.style.background = '#b71c1c';
    btn.onmouseout = () => btn.style.background = '#e53935';
    btn.onclick = (e) => {
      e.stopPropagation();
      e.preventDefault();
      const hideMenuStyle = document.createElement('style');
      hideMenuStyle.innerHTML = '.MuiPopover-root[role="presentation"] { display: none !important; }';
      document.head.appendChild(hideMenuStyle);
      act(li);
      setTimeout(() => {
        if (hideMenuStyle.parentNode) hideMenuStyle.parentNode.removeChild(hideMenuStyle);
      }, 500);
    };

    const targetBtn = more || star;
    targetBtn.parentElement.insertBefore(btn, targetBtn);
    targetBtn.addEventListener('mouseenter', () => { btn.style.display = ''; });
    targetBtn.addEventListener('mouseleave', () => { btn.style.display = 'none'; });
    btn.addEventListener('mouseenter', () => { btn.style.display = ''; });
    btn.addEventListener('mouseleave', () => { btn.style.display = 'none'; });
    li.dataset.qd = '1';
  };

  const scan = () => document.querySelectorAll('li[role="menuitem"][class*="session-item"]').forEach(inject);
  new MutationObserver(scan).observe(document.body, { childList: true, subtree: true });
  scan();
})();