您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Deletes the current Gemini conversation with a keyboard shortcut or button, provides a Tampermonkey menu command and a Ctrl+Shift+? shortcut to show script status, Ctrl+Shift+S to click the final action button, and a help menu. Uses MutationObserver for UI tracking.
当前为
// ==UserScript== // @name Gemini Conversation Delete Shortcut // @namespace https://greasyfork.org/ja/scripts/533285-gemini-conversation-delete-shortcut // @version 1.6.9 // @description Deletes the current Gemini conversation with a keyboard shortcut or button, provides a Tampermonkey menu command and a Ctrl+Shift+? shortcut to show script status, Ctrl+Shift+S to click the final action button, and a help menu. Uses MutationObserver for UI tracking. // @author Takashi Sasasaki // @license MIT // @homepageURL https://x.com/TakashiSasaki // @supportURL https://greasyfork.org/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 // ==/UserScript== (function() { 'use strict'; // --- Utility to check if an element is visible --- function isElementVisible(el) { if (!el) return false; const style = window.getComputedStyle(el); if (style.display === 'none') return false; if (style.visibility === 'hidden') return false; if (style.opacity === '0') return false; return el.offsetParent !== null; } // --- Function to show script status (for menu command and shortcut) --- function showStatusDialog() { const headers = document.querySelectorAll('div.response-container-header'); const currentUrl = window.location.href; const menuButtonElement = document.querySelector(SELECTOR_MENU_BUTTON); const menuButtonIsDomPresent = !!menuButtonElement; const menuButtonIsCurrentlyVisible = isElementVisible(menuButtonElement); alert( `Gemini Conversation Delete Shortcut is active (version 1.6.9).\n` + `URL: ${currentUrl}\n` + `Found ${headers.length} elements matching div.response-container-header.\n` + `Conversation actions menu button (${SELECTOR_MENU_BUTTON}):\n` + ` - In DOM: ${menuButtonIsDomPresent}\n` + ` - Visible: ${menuButtonIsCurrentlyVisible}\n` + `Using MutationObserver for UI changes.` ); console.log(`Delete Shortcut Status: URL=${currentUrl}, Found ${headers.length} headers. Menu button DOM present: ${menuButtonIsDomPresent}, Visible: ${menuButtonIsCurrentlyVisible}. Using MutationObserver.`); } /** * showHelp - Display a simple help of available shortcuts. */ function showHelp() { const helpText = [ 'Gemini Conversation Delete Shortcut Help:', 'Ctrl+Shift+Backspace → Start deletion sequence (open menu, click Delete, focus Confirm)', 'Ctrl+Shift+S → Click final action button (e.g., New Chat)', 'Ctrl+Shift+? → Show script status', '🗑️ Yellow button → Manual deletion trigger next to the More menu' ].join('\n'); alert(helpText); } // --- Register Tampermonkey menu commands --- 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_MENU_BUTTON = '[data-test-id="conversation-actions-button"]'; const SELECTOR_DELETE_BUTTON_IN_MENU = '[data-test-id="delete-button"]'; const SELECTOR_CONFIRM_BUTTON_IN_DIALOG = '[data-test-id="confirm-button"]'; const SELECTOR_FINAL_BUTTON = '#app-root > main > div > button'; const WAIT_AFTER_MENU_CLICK = 100; const WAIT_AFTER_DELETE_CLICK = 100; const POLLING_INTERVAL = 50; const MAX_POLLING_TIME = 3000; const MAX_WIDTH_FOR_AUTOMATION = 960; // --- Utility functions --- function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function pollForElement(selector, maxTime, interval) { const startTime = Date.now(); while (Date.now() - startTime < maxTime) { const element = document.querySelector(selector); if (element) { return element; } await sleep(interval); } return null; } // --- Main automation sequence to delete conversation up to confirmation --- async function performAutomationSequence() { if (window.innerWidth > MAX_WIDTH_FOR_AUTOMATION) { console.warn(`Automation sequence aborted: window width (${window.innerWidth}px) exceeds limit (${MAX_WIDTH_FOR_AUTOMATION}px). Intended for mobile-like views.`); return; } try { const menuButton = document.querySelector(SELECTOR_MENU_BUTTON); if (!menuButton || !isElementVisible(menuButton)) { console.error(`Menu button (${SELECTOR_MENU_BUTTON}) not found or not visible.`); alert('Conversation actions menu not found or hidden. Cannot proceed with deletion.'); throw new Error('Menu button not found or not visible'); } menuButton.click(); await sleep(WAIT_AFTER_MENU_CLICK); const deleteBtn = await pollForElement(SELECTOR_DELETE_BUTTON_IN_MENU, MAX_POLLING_TIME, POLLING_INTERVAL); if (!deleteBtn || !isElementVisible(deleteBtn)) { throw new Error('Delete button not found or not visible'); } deleteBtn.click(); await sleep(WAIT_AFTER_DELETE_CLICK); const confirmBtn = await pollForElement(SELECTOR_CONFIRM_BUTTON_IN_DIALOG, MAX_POLLING_TIME, POLLING_INTERVAL); if (!confirmBtn || !isElementVisible(confirmBtn)) { throw new Error('Confirm button not found or not visible'); } console.log(`Confirm button found. Focusing and highlighting.`); confirmBtn.focus({ preventScroll: false }); confirmBtn.style.backgroundColor = 'lightgreen'; confirmBtn.style.border = '3px solid green'; confirmBtn.style.color = 'black'; confirmBtn.style.outline = '2px dashed darkgreen'; console.log('Confirmation button is highlighted. Press Enter or click to confirm deletion. Use Ctrl+Shift+S to click any subsequent final button.'); } catch (err) { console.error('Automation 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(); const menuButtonElement = document.querySelector(SELECTOR_MENU_BUTTON); if (!isElementVisible(menuButtonElement)) { console.log('Shortcut used but menu button not visible. Aborting.'); alert('Conversation actions menu (︙) not found or not displayed. Cannot delete.'); return; } if (window.innerWidth > MAX_WIDTH_FOR_AUTOMATION) { console.warn('Shortcut used but window width exceeds limit.'); } performAutomationSequence(); } 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 finalButton = document.querySelector(SELECTOR_FINAL_BUTTON); if (finalButton && isElementVisible(finalButton)) { finalButton.click(); } else { console.warn('Final action button not found or not visible.'); alert('Final action button not found or not displayed.'); } } }, true); // --- Manual trigger button insertion --- function insertManualTriggerButton() { const menuButtonElement = document.querySelector(SELECTOR_MENU_BUTTON); const isMenuVisible = isElementVisible(menuButtonElement); const existingButtons = document.querySelectorAll('.delete-shortcut-button'); if (isMenuVisible) { const wrapperSelector = 'div.menu-button-wrapper'; let targetWrapper = null; if (menuButtonElement.closest(wrapperSelector)) { targetWrapper = menuButtonElement.closest(wrapperSelector); } else if (menuButtonElement.parentElement && menuButtonElement.parentElement.classList.contains('menu-button-wrapper')) { targetWrapper = menuButtonElement.parentElement; } if (targetWrapper) { const existing = targetWrapper.parentNode.querySelector('.delete-shortcut-button'); if (existing) { existing.remove(); } 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(); performAutomationSequence(); }); targetWrapper.parentNode.insertBefore(btn, targetWrapper.nextSibling); } else { existingButtons.forEach(btn => btn.remove()); } } else { existingButtons.forEach(btn => btn.remove()); } } // --- Observe DOM changes and debounce --- let observerDebounceTimeout; const observer = new MutationObserver(() => { clearTimeout(observerDebounceTimeout); observerDebounceTimeout = setTimeout(insertManualTriggerButton, 150); }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); // --- Initial manual button insertion --- setTimeout(insertManualTriggerButton, 500); })();