您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Deletes the current Gemini conversation on both mobile and desktop layouts. Uses simplified polling and timing; focuses and highlights the confirm button without automatic click on both layouts. On desktop, after confirm is clicked, conditionally opens side nav if its content width ≤72px, without adding duplicate listeners. The confirm button is now scoped under message-dialog.
// ==UserScript== // @name Gemini Conversation Delete Shortcut // @namespace https://gf.qytechs.cn/ja/scripts/533285-gemini-conversation-delete-shortcut // @version 1.7.7 // @description Deletes the current Gemini conversation on both mobile and desktop layouts. Uses simplified polling and timing; focuses and highlights the confirm button without automatic click on both layouts. On desktop, after confirm is clicked, conditionally opens side nav if its content width ≤72px, without adding duplicate listeners. The confirm button is now scoped under message-dialog. // @author Takashi Sasasaki // @license MIT // @homepageURL https://x.com/TakashiSasaki // @supportURL https://gf.qytechs.cn/ja/scripts/533285-gemini-conversation-delete-shortcut // @match https://gemini.google.com/app/* // @match https://gemini.google.com/app // @icon https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png // @grant GM_registerMenuCommand // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // --- Utility to check if an element is visible (only for mobile) --- function isElementVisible(el) { if (!el) return false; const style = window.getComputedStyle(el); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return false; } return el.offsetParent !== null; } // --- Show script status dialog --- function showStatusDialog() { const mobilePresent = !!document.querySelector(SELECTOR_MOBILE_MENU_BUTTON); const mobileVisible = isElementVisible(document.querySelector(SELECTOR_MOBILE_MENU_BUTTON)); const desktopPresent = !!document.querySelector(SELECTOR_DESKTOP_MENU_BUTTON); alert( `Gemini Conversation Delete Shortcut is active (version 1.7.7).\n\n` + `Mobile menu button (${SELECTOR_MOBILE_MENU_BUTTON}): In DOM=${mobilePresent}, Visible=${mobileVisible}\n` + `Desktop menu button (${SELECTOR_DESKTOP_MENU_BUTTON}): In DOM=${desktopPresent}` ); } // --- Show help dialog --- function showHelp() { alert( 'Gemini Conversation Delete Shortcut Help:\n' + 'Ctrl+Shift+Backspace → Start deletion sequence (open menu, click Delete, focus/confirm)\n' + 'Ctrl+Shift+S → Click final action button (e.g., New Chat)\n' + 'Ctrl+Shift+? → Show script status\n' + '🗑️ Button next to menu → Manual deletion trigger (mobile only)' ); } GM_registerMenuCommand('Show delete shortcut status', showStatusDialog); GM_registerMenuCommand('Show shortcuts help', showHelp, 'H'); // --- Configuration --- const SHORTCUT_KEY_CODE = 'Backspace'; const USE_CTRL_KEY = true; const USE_SHIFT_KEY = true; const USE_ALT_KEY = false; const USE_META_KEY = false; const SELECTOR_MOBILE_MENU_BUTTON = '[data-test-id="conversation-actions-button"]'; const SELECTOR_DESKTOP_MENU_BUTTON = 'conversations-list div.selected button'; const SELECTOR_DELETE_BUTTON = '[data-test-id="delete-button"]'; const SELECTOR_CONFIRM_BUTTON = 'message-dialog button[data-test-id="confirm-button"]'; const SELECTOR_FINAL_BUTTON = '#app-root > main > div > button'; const SELECTOR_SIDE_NAV = 'bard-sidenav'; const SELECTOR_SIDE_NAV_TOGGLE = "button[data-test-id='side-nav-menu-button']"; const POLLING_INTERVAL = 100; // ms const MAX_POLLING_TIME = 1000; // ms const POST_CONFIRM_DELAY = 200; // ms // --- Utility functions --- function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function pollForElement(selector, maxTime) { const start = Date.now(); while (Date.now() - start < maxTime) { const el = document.querySelector(selector); if (el) return el; await sleep(POLLING_INTERVAL); } return null; } // --- Clear dataset flag when dialog closes (so new listeners can be added next time) --- function clearConfirmDatasetOnClose() { const confirmBtn = document.querySelector(SELECTOR_CONFIRM_BUTTON); if (confirmBtn) { delete confirmBtn.dataset.sideNavListenerAdded; } } // Listen for dialog-close clicks (e.g., mat-dialog-close attribute) document.body.addEventListener('click', event => { if (event.target.closest('[mat-dialog-close]')) { clearConfirmDatasetOnClose(); } }); // --- Main deletion sequence --- async function performDeletion() { try { // 1) Try mobile layout const mobileBtn = await pollForElement(SELECTOR_MOBILE_MENU_BUTTON, MAX_POLLING_TIME); if (mobileBtn && isElementVisible(mobileBtn)) { mobileBtn.click(); const deleteBtn = await pollForElement(SELECTOR_DELETE_BUTTON, MAX_POLLING_TIME); if (!deleteBtn || !isElementVisible(deleteBtn)) throw new Error('Delete not found (mobile)'); deleteBtn.click(); const confirmBtn = await pollForElement(SELECTOR_CONFIRM_BUTTON, MAX_POLLING_TIME); if (!confirmBtn || !isElementVisible(confirmBtn)) throw new Error('Confirm not found (mobile)'); // Wait for Angular animation to finish await sleep(150); // Focus and highlight (use !important to override Angular styles) confirmBtn.focus({ preventScroll: false }); confirmBtn.style.setProperty('background-color', 'lightgreen', 'important'); confirmBtn.style.setProperty('border', '3px solid green', 'important'); confirmBtn.style.setProperty('color', 'black', 'important'); confirmBtn.style.setProperty('outline', '2px dashed darkgreen', 'important'); // No side-nav logic for mobile return; } // 2) Try desktop layout const desktopBtn = await pollForElement(SELECTOR_DESKTOP_MENU_BUTTON, MAX_POLLING_TIME); if (desktopBtn) { desktopBtn.click(); const deleteBtn = await pollForElement(`div[role="menu"] button${SELECTOR_DELETE_BUTTON}`, MAX_POLLING_TIME); if (!deleteBtn) throw new Error('Delete not found (desktop)'); deleteBtn.click(); const confirmBtn = await pollForElement(SELECTOR_CONFIRM_BUTTON, MAX_POLLING_TIME); if (!confirmBtn) throw new Error('Confirm not found (desktop)'); // Wait for Angular animation to finish await sleep(150); // Focus and highlight (use !important) confirmBtn.focus({ preventScroll: false }); confirmBtn.style.setProperty('background-color', 'lightgreen', 'important'); confirmBtn.style.setProperty('border', '3px solid green', 'important'); confirmBtn.style.setProperty('color', 'black', 'important'); confirmBtn.style.setProperty('outline', '2px dashed darkgreen', 'important'); // Add side-nav listener only once per dialog instance if (!confirmBtn.dataset.sideNavListenerAdded) { confirmBtn.dataset.sideNavListenerAdded = 'true'; confirmBtn.addEventListener('click', async () => { // After user clicks confirm, wait and then toggle side-nav if narrow await sleep(POST_CONFIRM_DELAY); const sideNav = document.querySelector(SELECTOR_SIDE_NAV); if (sideNav) { const style = window.getComputedStyle(sideNav); const paddingLeft = parseFloat(style.paddingLeft) || 0; const paddingRight = parseFloat(style.paddingRight) || 0; const contentWidth = sideNav.clientWidth - paddingLeft - paddingRight; if (contentWidth <= 72) { const toggleBtn = document.querySelector(SELECTOR_SIDE_NAV_TOGGLE); if (toggleBtn && toggleBtn.offsetParent !== null) { toggleBtn.click(); } } } }, { once: true }); } return; } alert('Conversation menu button not found. Cannot delete.'); } catch (err) { console.error('Deletion error:', err.message); } } // --- Keyboard shortcut listener --- document.addEventListener('keydown', event => { if ( event.code === SHORTCUT_KEY_CODE && event.ctrlKey === USE_CTRL_KEY && event.shiftKey === USE_SHIFT_KEY && event.altKey === USE_ALT_KEY && event.metaKey === USE_META_KEY ) { event.preventDefault(); event.stopPropagation(); performDeletion(); } else if (event.ctrlKey && event.shiftKey && event.key === '?') { event.preventDefault(); event.stopPropagation(); showStatusDialog(); } else if (event.ctrlKey && event.shiftKey && (event.key === 'S' || event.key === 's')) { event.preventDefault(); event.stopPropagation(); const finalBtn = document.querySelector(SELECTOR_FINAL_BUTTON); if (finalBtn && isElementVisible(finalBtn)) { finalBtn.click(); } else { alert('Final action button not found.'); } } }, true); // --- Manual delete button insertion for mobile layout only --- function insertMobileButton() { const mobileBtn = document.querySelector(SELECTOR_MOBILE_MENU_BUTTON); const isVisible = isElementVisible(mobileBtn); document.querySelectorAll('.delete-shortcut-button').forEach(b => b.remove()); if (isVisible) { let wrapper = mobileBtn.closest('div.menu-button-wrapper') || mobileBtn.parentElement; if (!wrapper || !wrapper.classList.contains('menu-button-wrapper')) return; const btn = document.createElement('button'); btn.className = 'delete-shortcut-button'; btn.title = 'Delete conversation (Ctrl+Shift+Backspace)'; btn.textContent = '🗑️'; btn.style.marginLeft = '8px'; btn.style.padding = '4px'; btn.style.border = '1px solid red'; btn.style.background = 'yellow'; btn.style.cursor = 'pointer'; btn.style.zIndex = '9999'; btn.addEventListener('click', e => { e.preventDefault(); e.stopPropagation(); performDeletion(); }); wrapper.parentNode.insertBefore(btn, wrapper.nextSibling); } } let debounce; const observer = new MutationObserver(() => { clearTimeout(debounce); debounce = setTimeout(insertMobileButton, 150); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); setTimeout(insertMobileButton, 500); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址