Google AI Studio - Enhanced Actions

Adds multi-select/delete (Checkbox, Ctrl+A, Shift+Click, Ctrl+Click, Delete key) and "Delete Thoughts" (Shift+F2) to Chat and Library views.

目前為 2025-10-18 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Google AI Studio - Enhanced Actions
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Adds multi-select/delete (Checkbox, Ctrl+A, Shift+Click, Ctrl+Click, Delete key) and "Delete Thoughts" (Shift+F2) to Chat and Library views.
// @author       User
// @match        https://aistudio.google.com/prompts/*
// @match        https://aistudio.google.com/library
// @icon         https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant        GM_addStyle
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration & Constants ---
    const C = {
        CLASSES: {
            CHAT_CHECKBOX: 'gm-chat-select-checkbox',
            CHAT_SELECTED: 'gm-chat-selected',
            CHAT_DELETE_BEFORE: 'gm-chat-delete-before-item',
            LIB_CHECKBOX: 'gm-lib-select-checkbox',
            LIB_HEADER_CHECKBOX: 'gm-lib-header-select-checkbox',
            LIB_SELECTED: 'gm-lib-selected',
            LIB_CHECKBOX_CELL: 'gm-lib-checkbox-cell',
            LIB_CHECKBOX_HEADER: 'gm-lib-checkbox-header',
            ENABLED: 'enabled'
        },
        IDS: {
            CHAT_MULTI_DELETE_BTN: 'gm-chat-multi-delete-button',
            LIB_MULTI_DELETE_BTN: 'gm-lib-multi-delete-button'
        },
        SELECTORS: {
            CHAT_TURN: 'ms-chat-turn',
            CHAT_SESSION: 'ms-chat-session',
            CHAT_TURN_CONTAINER: '.chat-turn-container',
            CHAT_MORE_OPTIONS_BTN: 'ms-chat-turn-options button.mat-mdc-menu-trigger',
            CHAT_MORE_ACTIONS_BTN: 'button[aria-label="View more actions"]',
            MENU_PANEL: 'div.cdk-overlay-container div.mat-mdc-menu-panel',
            MENU_ITEM: 'button.mat-mdc-menu-item',
            OVERLAY_BACKDROP: 'div.cdk-overlay-backdrop',
            LIBRARY_TABLE: 'ms-library-table table.mat-mdc-table',
            LIBRARY_HEADER_ROW: 'thead tr.mat-mdc-header-row',
            LIBRARY_ROW: 'tbody tr.mat-mdc-row',
            LIBRARY_ACTIONS_WRAPPER: 'div.lib-header div.actions-wrapper',
            LIBRARY_MORE_OPTIONS_BTN: 'button[aria-label="Show overflow"]',
            DIALOG_CONTAINER: 'mat-dialog-container',
            DIALOG_ACTIONS: 'mat-dialog-actions button'
        },
        TIMEOUTS: {
            ELEMENT_WAIT: 5000,
            ELEMENT_DISAPPEAR: 5000,
            MENU_APPEAR_DELAY: 150
        }
    };

    // --- State Variables ---
    let selectedChatTurns = new Set();
    let selectedLibraryItems = new Set();
    let isDeleting = false;
    let lastClickedTurn = null;

    // --- Styles ---
    GM_addStyle(`
        ms-chat-turn .chat-turn-container { position: relative; padding-left: 35px !important; }
        .${C.CLASSES.CHAT_CHECKBOX} { position: absolute; left: 8px; top: 12px; z-index: 10; cursor: pointer; transform: scale(1.2); }
        ms-chat-turn.${C.CLASSES.CHAT_SELECTED} > div { background-color: rgba(0, 100, 255, 0.1) !important; border: 1px solid rgba(0, 100, 255, 0.3); border-radius: 8px; }
        #${C.IDS.CHAT_MULTI_DELETE_BTN} { margin-left: 10px; background-color: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 4px; font-size: 14px; opacity: 0.5; pointer-events: none; transition: opacity 0.2s; }
        #${C.IDS.CHAT_MULTI_DELETE_BTN}.${C.CLASSES.ENABLED} { opacity: 1; pointer-events: auto; cursor: pointer; }
        #${C.IDS.CHAT_MULTI_DELETE_BTN}:hover.${C.CLASSES.ENABLED} { background-color: #c82333; }
        #${C.IDS.CHAT_MULTI_DELETE_BTN}:disabled { background-color: #a0a0a0; cursor: not-allowed; }
        .${C.CLASSES.CHAT_DELETE_BEFORE} .mat-mdc-menu-item-text { display: flex; align-items: center; }
        .${C.CLASSES.CHAT_DELETE_BEFORE} .delete-before-marker { margin-right: 8px; font-weight: bold; }
        th.${C.CLASSES.LIB_CHECKBOX_HEADER}, td.${C.CLASSES.LIB_CHECKBOX_CELL} { width: 40px !important; padding: 0 8px 0 16px !important; }
        tr.${C.CLASSES.LIB_SELECTED} { background-color: rgba(0, 100, 255, 0.08) !important; }
        #${C.IDS.LIB_MULTI_DELETE_BTN} { margin-left: 8px; background-color: #dc3545; color: white; border: none; padding: 0 12px; height: 36px; line-height: 36px; border-radius: 18px; font-weight: 500; opacity: 0.5; pointer-events: none; transition: opacity 0.2s; display: inline-flex; align-items: center; }
        #${C.IDS.LIB_MULTI_DELETE_BTN} .material-symbols-outlined { font-size: 18px; margin-right: 6px; }
        #${C.IDS.LIB_MULTI_DELETE_BTN}.${C.CLASSES.ENABLED} { opacity: 1; pointer-events: auto; cursor: pointer; }
        #${C.IDS.LIB_MULTI_DELETE_BTN}:hover.${C.CLASSES.ENABLED} { background-color: #c82333; }
        #${C.IDS.LIB_MULTI_DELETE_BTN}:disabled { background-color: #cccccc; cursor: not-allowed; opacity: 0.6; }
    `);

    // --- Utility Functions ---
    function robustClick(element) {
        if (!element) return;
        const ownerDoc = element.ownerDocument;
        const ownerWindow = ownerDoc?.defaultView;
        if (!ownerWindow) return;
        ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach(type =>
            element.dispatchEvent(new ownerWindow.MouseEvent(type, { bubbles: true, cancelable: true, view: ownerWindow }))
        );
    }

    function waitForElement(selector, timeout = C.TIMEOUTS.ELEMENT_WAIT) {
        return new Promise((resolve, reject) => {
            let el = document.querySelector(selector);
            if (el) return resolve(el);
            const observer = new MutationObserver(() => {
                el = document.querySelector(selector);
                if (el) {
                    observer.disconnect();
                    resolve(el);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Element "${selector}" not found.`));
            }, timeout);
        });
    }

    function waitForElementToDisappear(element, timeout = C.TIMEOUTS.ELEMENT_DISAPPEAR) {
        return new Promise(resolve => {
            if (!element || !document.body.contains(element)) return resolve();
            const observer = new MutationObserver(() => {
                if (!document.body.contains(element)) {
                    observer.disconnect();
                    resolve();
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            setTimeout(() => { observer.disconnect(); resolve(); }, timeout);
        });
    }

    async function withSuppressedConfirm(action) {
        const originalConfirm = window.confirm;
        window.confirm = () => true;
        try {
            await action();
        } finally {
            window.confirm = originalConfirm;
        }
    }

    // --- Chat View Functions ---
    function addChatCheckbox(turnElement) {
        if (turnElement.querySelector(`.${C.CLASSES.CHAT_CHECKBOX}`)) return;
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.className = C.CLASSES.CHAT_CHECKBOX;
        checkbox.title = 'Select this turn (Shift+Click for range, Ctrl+Click to multi-select)';
        const container = turnElement.querySelector(C.SELECTORS.CHAT_TURN_CONTAINER);
        container?.insertBefore(checkbox, container.firstChild);
    }

    function setupChatMoreOptionsListener(turnElement) {
        const moreOptionsButton = turnElement.querySelector(C.SELECTORS.CHAT_MORE_OPTIONS_BTN);
        if (!moreOptionsButton || moreOptionsButton.dataset.hasGmListener) return;
        moreOptionsButton.dataset.hasGmListener = 'true';
        moreOptionsButton.addEventListener('click', () => {
            if (isDeleting) return;
            setTimeout(() => {
                const menu = document.querySelector(C.SELECTORS.MENU_PANEL);
                if (menu) addChatDeleteBeforeMenuItem(menu, turnElement);
            }, C.TIMEOUTS.MENU_APPEAR_DELAY);
        });
    }

    function addChatDeleteBeforeMenuItem(menuPanel, targetTurnElement) {
        if (menuPanel.querySelector(`.${C.CLASSES.CHAT_DELETE_BEFORE}`)) return;
        const menuContent = menuPanel.querySelector('.mat-mdc-menu-content');
        if (!menuContent) return;
        const deleteItem = Array.from(menuPanel.querySelectorAll(C.SELECTORS.MENU_ITEM)).find(
            btn => btn.textContent.trim().toLowerCase() === 'delete'
        );
        const btn = document.createElement('button');
        btn.className = `mat-mdc-menu-item mat-focus-indicator ${C.CLASSES.CHAT_DELETE_BEFORE}`;
        btn.innerHTML = `<span class="mat-mdc-menu-item-text"><span class="delete-before-marker">[X]</span><span>Delete Before</span></span>`;
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            handleChatDeleteBefore(targetTurnElement);
        });
        if (deleteItem) menuContent.insertBefore(btn, deleteItem);
        else menuContent.appendChild(btn);
    }

    function handleChatClick(event) {
        const checkbox = event.target;
        if (!checkbox.matches(`.${C.CLASSES.CHAT_CHECKBOX}`)) return;
        const turnElement = checkbox.closest(C.SELECTORS.CHAT_TURN);
        if (!turnElement || isDeleting) {
            if (isDeleting) event.preventDefault();
            return;
        }
        if (lastClickedTurn && !document.body.contains(lastClickedTurn)) lastClickedTurn = null;
        const allTurns = Array.from(document.querySelectorAll(`${C.SELECTORS.CHAT_SESSION} ${C.SELECTORS.CHAT_TURN}`));
        if (event.shiftKey && lastClickedTurn) {
            const start = allTurns.indexOf(lastClickedTurn);
            const end = allTurns.indexOf(turnElement);
            if (start === -1 || end === -1) return;
            const range = allTurns.slice(Math.min(start, end), Math.max(start, end) + 1);
            if (!event.ctrlKey) deselectAllTurns(false);
            range.forEach(turn => selectTurn(turn, true));
        } else {
            const isSelected = selectedChatTurns.has(turnElement);
            if (!event.ctrlKey) {
                deselectAllTurns(false);
                if (!isSelected) selectTurn(turnElement, true);
            } else {
                selectTurn(turnElement, !isSelected);
            }
        }
        lastClickedTurn = turnElement;
        updateChatMultiDeleteButtonState();
    }

    function selectTurn(turnElement, shouldBeSelected) {
        const checkbox = turnElement.querySelector(`.${C.CLASSES.CHAT_CHECKBOX}`);
        if (shouldBeSelected) {
            selectedChatTurns.add(turnElement);
            turnElement.classList.add(C.CLASSES.CHAT_SELECTED);
            if (checkbox) checkbox.checked = true;
        } else {
            selectedChatTurns.delete(turnElement);
            turnElement.classList.remove(C.CLASSES.CHAT_SELECTED);
            if (checkbox) checkbox.checked = false;
        }
    }

    function deselectAllTurns(updateButtonState = true) {
        selectedChatTurns.forEach(turn => selectTurn(turn, false));
        selectedChatTurns.clear();
        if (updateButtonState) updateChatMultiDeleteButtonState();
    }

    async function deleteSingleChatTurn(turnElement) {
        const moreOptionsButton = turnElement.querySelector(C.SELECTORS.CHAT_MORE_OPTIONS_BTN);
        if (!moreOptionsButton) return false;
        robustClick(moreOptionsButton);
        try {
            const menu = await waitForElement(`${C.SELECTORS.MENU_PANEL}:has(${C.SELECTORS.MENU_ITEM})`);
            const deleteButton = Array.from(menu.querySelectorAll(C.SELECTORS.MENU_ITEM)).find(btn => {
                const text = btn.textContent.trim().toLowerCase();
                return text.includes('delete') && !text.includes('before');
            });
            if (deleteButton) {
                robustClick(deleteButton);
                await waitForElementToDisappear(turnElement);
                return true;
            }
            return false;
        } catch (error) {
            const backdrop = document.querySelector(C.SELECTORS.OVERLAY_BACKDROP);
            if (backdrop) robustClick(backdrop);
            return false;
        }
    }

    async function performChatDeletion(turnsToDelete) {
        if (isDeleting || turnsToDelete.length === 0) return;
        const turnsArray = [...turnsToDelete].sort((a, b) => b.compareDocumentPosition(a) & Node.DOCUMENT_POSITION_FOLLOWING ? 1 : -1);
        await withSuppressedConfirm(async () => {
            isDeleting = true;
            updateChatMultiDeleteButtonState();
            try {
                for (const turn of turnsArray) {
                    if (document.body.contains(turn)) await deleteSingleChatTurn(turn);
                }
            } finally {
                isDeleting = false;
                updateChatMultiDeleteButtonState();
            }
        });
    }

    async function handleChatDeleteSelected() {
        const turnsToDelete = [...selectedChatTurns];
        if (turnsToDelete.length === 0) return;
        deselectAllTurns(false);
        await performChatDeletion(turnsToDelete);
    }

    async function handleChatDeleteBefore(targetTurnElement) {
        if (!targetTurnElement?.isConnected) return;
        const allTurns = Array.from(document.querySelectorAll(`${C.SELECTORS.CHAT_SESSION} ${C.SELECTORS.CHAT_TURN}`));
        const idx = allTurns.indexOf(targetTurnElement);
        if (idx <= 0) return;
        const turnsToDelete = allTurns.slice(0, idx);
        if (turnsToDelete.length > 0) {
            deselectAllTurns(false);
            await performChatDeletion(turnsToDelete);
        }
    }

    async function handleDeleteThoughts() {
        const thoughtTurns = Array.from(document.querySelectorAll(C.SELECTORS.CHAT_TURN)).filter(turn =>
            turn.querySelector('mat-expansion-panel-header')?.textContent.trim().startsWith('Thoughts')
        );
        if (thoughtTurns.length > 0) await performChatDeletion(thoughtTurns);
    }

    function addChatMultiDeleteButton() {
        if (document.getElementById(C.IDS.CHAT_MULTI_DELETE_BTN)) return;
        const moreButton = document.querySelector(C.SELECTORS.CHAT_MORE_ACTIONS_BTN);
        if (!moreButton?.parentElement) return;
        const btn = document.createElement('button');
        btn.id = C.IDS.CHAT_MULTI_DELETE_BTN;
        btn.addEventListener('click', handleChatDeleteSelected);
        moreButton.parentElement.insertBefore(btn, moreButton);
        updateChatMultiDeleteButtonState();
    }

    function updateChatMultiDeleteButtonState() {
        const btn = document.getElementById(C.IDS.CHAT_MULTI_DELETE_BTN);
        if (!btn) return;
        const count = selectedChatTurns.size;
        btn.textContent = isDeleting ? 'Deleting...' : `Delete Selected (${count})`;
        const enabled = count > 0 && !isDeleting;
        btn.classList.toggle(C.CLASSES.ENABLED, enabled);
        btn.disabled = !enabled;
    }

    function observeChatArea() {
        const container = document.querySelector(C.SELECTORS.CHAT_SESSION);
        if (!container) return;
        container.addEventListener('click', handleChatClick);
        document.querySelectorAll(`${C.SELECTORS.CHAT_SESSION} ${C.SELECTORS.CHAT_TURN}`).forEach(turn => {
            addChatCheckbox(turn);
            setupChatMoreOptionsListener(turn);
        });
        addChatMultiDeleteButton();
        const observer = new MutationObserver(mutations => {
            for (const m of mutations) {
                m.addedNodes.forEach(n => {
                    if (n.nodeType === Node.ELEMENT_NODE) {
                        const turns = n.matches(C.SELECTORS.CHAT_TURN) ? [n] : n.querySelectorAll(C.SELECTORS.CHAT_TURN);
                        turns.forEach(turn => {
                            addChatCheckbox(turn);
                            setupChatMoreOptionsListener(turn);
                        });
                    }
                });
                m.removedNodes.forEach(n => {
                    if (n.nodeType === Node.ELEMENT_NODE && selectedChatTurns.has(n)) {
                        selectedChatTurns.delete(n);
                        updateChatMultiDeleteButtonState();
                    }
                });
            }
        });
        observer.observe(container, { childList: true, subtree: true });
    }

    // --- Library View Functions ---
    function addLibraryCheckbox(rowElement) {
        if (rowElement.querySelector(`.${C.CLASSES.LIB_CHECKBOX_CELL}`)) return;
        const cell = document.createElement('td');
        cell.className = `mat-mdc-cell mdc-data-table__cell cdk-cell ${C.CLASSES.LIB_CHECKBOX_CELL}`;
        cell.innerHTML = `<input type="checkbox" class="${C.CLASSES.LIB_CHECKBOX}">`;
        rowElement.insertBefore(cell, rowElement.firstChild);
    }

    function addLibraryHeaderCheckbox(headerRow) {
        if (headerRow.querySelector(`.${C.CLASSES.LIB_CHECKBOX_HEADER}`)) return;
        const headerCell = document.createElement('th');
        headerCell.className = `mat-mdc-header-cell mdc-data-table__header-cell cdk-header-cell ${C.CLASSES.LIB_CHECKBOX_HEADER}`;
        headerCell.innerHTML = `<input type="checkbox" class="${C.CLASSES.LIB_HEADER_CHECKBOX}" title="Select/Deselect All Visible">`;
        headerRow.insertBefore(headerCell, headerRow.firstChild);
    }

    function handleLibraryClick(event) {
        const target = event.target;
        if (target.matches(`.${C.CLASSES.LIB_HEADER_CHECKBOX}`)) {
            const isChecked = target.checked;
            document.querySelectorAll(C.SELECTORS.LIBRARY_ROW).forEach(row => {
                const rowCheckbox = row.querySelector(`input.${C.CLASSES.LIB_CHECKBOX}`);
                if (rowCheckbox) rowCheckbox.checked = isChecked;
                row.classList.toggle(C.CLASSES.LIB_SELECTED, isChecked);
                if (isChecked) selectedLibraryItems.add(row);
                else selectedLibraryItems.delete(row);
            });
        } else if (target.matches(`.${C.CLASSES.LIB_CHECKBOX}`)) {
            const row = target.closest('tr');
            if (row) {
                row.classList.toggle(C.CLASSES.LIB_SELECTED, target.checked);
                if (target.checked) selectedLibraryItems.add(row);
                else selectedLibraryItems.delete(row);
            }
        }
        updateLibraryMultiDeleteButtonState();
    }

    function addLibraryMultiDeleteButton() {
        if (document.getElementById(C.IDS.LIB_MULTI_DELETE_BTN)) return;
        const actionsWrapper = document.querySelector(C.SELECTORS.LIBRARY_ACTIONS_WRAPPER);
        if (!actionsWrapper) return;
        const button = document.createElement('button');
        button.id = C.IDS.LIB_MULTI_DELETE_BTN;
        button.innerHTML = `<span class="material-symbols-outlined">delete</span><span class="button-text">Delete Selected (0)</span>`;
        button.addEventListener('click', handleLibraryDeleteSelected);
        actionsWrapper.appendChild(button);
        updateLibraryMultiDeleteButtonState();
    }

    function updateLibraryMultiDeleteButtonState() {
        const button = document.getElementById(C.IDS.LIB_MULTI_DELETE_BTN);
        if (!button) return;
        const count = selectedLibraryItems.size;
        button.querySelector('.button-text').textContent = `Delete Selected (${count})`;
        const enabled = count > 0 && !isDeleting;
        button.classList.toggle(C.CLASSES.ENABLED, enabled);
        button.disabled = !enabled;
    }

    async function deleteSingleLibraryItem(rowElement) {
        const moreOptionsButton = rowElement.querySelector(C.SELECTORS.LIBRARY_MORE_OPTIONS_BTN);
        if (!moreOptionsButton) return false;
        robustClick(moreOptionsButton);
        try {
            const menu = await waitForElement(`${C.SELECTORS.MENU_PANEL}:has(${C.SELECTORS.MENU_ITEM})`);
            const deletePromptButton = Array.from(menu.querySelectorAll(C.SELECTORS.MENU_ITEM)).find(
                btn => btn.textContent.trim().toLowerCase().includes('delete prompt')
            );
            if (!deletePromptButton) return false;
            robustClick(deletePromptButton);
            const dialog = await waitForElement(C.SELECTORS.DIALOG_CONTAINER);
            const finalDeleteButton = Array.from(dialog.querySelectorAll(C.SELECTORS.DIALOG_ACTIONS)).find(
                btn => btn.textContent.trim().toLowerCase() === 'delete'
            );
            if (!finalDeleteButton) return false;
            robustClick(finalDeleteButton);
            await waitForElementToDisappear(rowElement);
            return true;
        } catch (error) {
            const backdrop = document.querySelector(C.SELECTORS.OVERLAY_BACKDROP);
            if (backdrop) robustClick(backdrop);
            return false;
        }
    }

    async function handleLibraryDeleteSelected() {
        if (isDeleting || selectedLibraryItems.size === 0) return;
        await withSuppressedConfirm(async () => {
            isDeleting = true;
            updateLibraryMultiDeleteButtonState();
            try {
                const itemsToDelete = [...selectedLibraryItems];
                selectedLibraryItems.clear();
                for (const row of itemsToDelete) {
                    if (row.isConnected) await deleteSingleLibraryItem(row);
                }
            } finally {
                isDeleting = false;
                updateLibraryMultiDeleteButtonState();
            }
        });
    }

    function observeLibraryTable() {
        const table = document.querySelector(C.SELECTORS.LIBRARY_TABLE);
        if (!table) return;
        table.addEventListener('click', handleLibraryClick);
        const headerRow = table.querySelector(C.SELECTORS.LIBRARY_HEADER_ROW);
        if (headerRow) addLibraryHeaderCheckbox(headerRow);
        table.querySelectorAll(C.SELECTORS.LIBRARY_ROW).forEach(addLibraryCheckbox);
        addLibraryMultiDeleteButton();
        const observer = new MutationObserver(() => {
            table.querySelectorAll(`${C.SELECTORS.LIBRARY_ROW}:not(:has(.${C.CLASSES.LIB_CHECKBOX_CELL}))`).forEach(addLibraryCheckbox);
        });
        const tbody = table.querySelector('tbody');
        if (tbody) observer.observe(tbody, { childList: true });
    }

    // --- Global Listeners & Initialization ---
    function setupGlobalListeners() {
        document.addEventListener('keydown', (event) => {
            const activeEl = document.activeElement;
            const isTyping = activeEl && (activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable || (activeEl.tagName === 'INPUT' && /^(text|search|password|tel|url)$/i.test(activeEl.type)));
            if (isTyping || isDeleting) return;

            if (window.location.pathname.startsWith('/prompts/')) {
                if (event.ctrlKey && event.key.toLowerCase() === 'a') {
                    event.preventDefault();
                    const allTurns = document.querySelectorAll(`${C.SELECTORS.CHAT_SESSION} ${C.SELECTORS.CHAT_TURN}`);
                    const allSelected = selectedChatTurns.size === allTurns.length && allTurns.length > 0;
                    deselectAllTurns(false);
                    if (!allSelected) allTurns.forEach(turn => selectTurn(turn, true));
                    updateChatMultiDeleteButtonState();
                } else if (event.key === 'Escape') {
                    if (selectedChatTurns.size > 0) {
                        event.preventDefault();
                        deselectAllTurns();
                        lastClickedTurn = null;
                    }
                } else if (event.key === 'Delete') {
                    if (selectedChatTurns.size > 0) {
                        event.preventDefault();
                        document.getElementById(C.IDS.CHAT_MULTI_DELETE_BTN)?.click();
                    }
                } else if (event.shiftKey && event.key === 'F2') {
                    event.preventDefault();
                    handleDeleteThoughts();
                }
            }
        });

        document.addEventListener('click', (event) => {
            if (window.location.pathname.startsWith('/prompts/') && selectedChatTurns.size > 0 && !event.target.closest(`${C.SELECTORS.CHAT_TURN}, #${C.IDS.CHAT_MULTI_DELETE_BTN}`)) {
                deselectAllTurns();
                lastClickedTurn = null;
            }
        });
    }

    async function initialize() {
        const currentPath = window.location.pathname;
        try {
            if (currentPath.startsWith('/prompts/')) {
                await waitForElement(C.SELECTORS.CHAT_SESSION);
                observeChatArea();
            } else if (currentPath === '/library') {
                await waitForElement(C.SELECTORS.LIBRARY_TABLE);
                observeLibraryTable();
            }
            setupGlobalListeners();
        } catch (error) {
            console.error('[Enhanced Actions] Initialization failed:', error);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();