🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

Gemini Keyboard Shortcuts (Power Tweaks)

Power-user hotkeys for Gemini with Win11 fixes, URL param prefill, and a centralized config for selectors & shortcuts. New Chat = ⌘/Ctrl+Shift+O, Sidebar Toggle = ⌘/Ctrl+B.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         Gemini Keyboard Shortcuts (Power Tweaks)
// @namespace    http://tampermonkey.net/
// @version      1.3.1
// @description  Power-user hotkeys for Gemini with Win11 fixes, URL param prefill, and a centralized config for selectors & shortcuts. New Chat = ⌘/Ctrl+Shift+O, Sidebar Toggle = ⌘/Ctrl+B.
// @license      MIT
// @author       Henry Getz
// @match        https://gemini.google.com/*
// @match        https://gemini.google.com/u/*
// @match        https://gemini.google.com/app*
// @supportURL   https://github.com/HenryGetz/GeminiPilot/issues
// @grant        none
// @run-at       document-start
// ==/UserScript==
/*

# What’s new (maintainability + fixes)

- **Centralized config**: All shortcut preferences and selectors live in a single `CFG` object at the top.
- **Reliable New Chat selector**: Uses `button[aria-label*='New chat']`.
- **Shortcut updates**:
  - New Chat: **⌘/Ctrl + Shift + O**
  - Sidebar Toggle: **⌘/Ctrl + B** (like VS Code)
  - Help Window: **⌘/Ctrl + Shift + ?**
- **Windows 11 key handling**: Normalizes `event.key` to lowercase so Shift no longer breaks letter detection.
- **URL param prefill**: Open Gemini with a pre-populated prompt via `?q=...` (e.g. `https://gemini.google.com/app?q=Your+Prompt`).
- **onload resiliency**: Rebinds `window.onload = onLoad` inside an IIFE to survive sites where `onload` stops firing.
- **No forced UI repositioning**: Removed custom `bard-mode-switcher` fixed positioning.

# Included Keyboard Shortcuts

## Chat Management
| Shortcut (Mac/Windows)      | Action            |
|:---------------------------:|:------------------|
| ⌘/Ctrl + **Shift + O**      | Open new chat     |
| ⌘/Ctrl + Shift + Backspace  | Delete chat       |
| ⌘/Ctrl + **B**              | Toggle sidebar    |
| ⌥/Alt + 1–9                 | Go to nth chat    |
| ⌘/Ctrl + Shift + =          | Next chat         |
| ⌘/Ctrl + Shift + –          | Previous chat     |

## Text Input and Editing
| Shortcut (Mac/Windows) | Action                           |
|:----------------------:|:----------------------------------|
| Shift + Esc            | Focus chat input                  |
| ⌘/Ctrl + Shift + E     | Edit text                         |
| ⌘/Ctrl + Shift + ;     | Copy last code block              |
| ⌘/Ctrl + Shift + '     | Copy second-to-last code block    |
| ⌘/Ctrl + Shift + C     | Copy response                     |
| ⌘/Ctrl + Shift + K     | Stop/start generation             |

## Draft Navigation
| Shortcut (Mac/Windows) | Action                |
|:----------------------:|:----------------------|
| ⌘/Ctrl + Shift + D     | Generate more drafts  |
| ⌘/Ctrl + Shift + ,     | Next draft            |
| ⌘/Ctrl + Shift + .     | Previous draft        |

## Sharing and Linking
| Shortcut (Mac/Windows) | Action                      |
|:----------------------:|:----------------------------|
| ⌘/Ctrl + Shift + L     | Copy prompt/response link   |
| ⌘/Ctrl + Shift + M     | Copy chat link              |

## Audio and File Shortcuts
| Shortcut (Mac/Windows) | Action            |
|:----------------------:|:------------------|
| ⌘/Ctrl + Shift + Y     | Play/pause audio  |
| ⌘/Ctrl + Shift + S     | Voice to text     |
| ⌘/Ctrl + O             | Open file         |

## Help
| Shortcut (Mac/Windows) | Action             |
|:----------------------:|:-------------------|
| ⌘/Ctrl + Shift + ?     | Open help window   |

*/


(function () {
    'use strict';

    // ====== CENTRAL CONFIG (shortcuts + selectors) ======
    const CFG = {
        behavior: {
            assumeLastResponse: false,
            goToNextChatOnDelete: true,
        },
        timings: { rapidClickDelayMS: 100 },
        hotkeys: {
            newChat:      { key: 'o', shift: true,  cmdOrCtrl: true },
            sidebarToggle:{ key: 'b', shift: false, cmdOrCtrl: true },
            openFile:     { key: 'o', shift: false, cmdOrCtrl: true },
            // Help accepts '?' OR '/' with Shift OR code 'Slash'
            showHelp:     { key: '?', shift: true,  cmdOrCtrl: true, altKeys: ['/', '?'], codes: ['Slash'] },
        },
        selectors: {
            showMoreButton: '[data-test-id="show-more-button"]',
            loadMoreButton: '[data-test-id="load-more-button"]',
            conversation: '[data-test-id="conversation"]',
            selectedConversation: '.selected[data-test-id="conversation"]',
            conversationTitle: '.conversation-title',
            actionsMenuButton: '[data-test-id="actions-menu-button"]',
            deleteButton: '[data-test-id="delete-button"]',
            confirmButton: '[data-test-id="confirm-button"]',
            textInputField: '.text-input-field',
            enterPrompt: '[aria-label="Enter a prompt here"]',
            sendMessageButton: '[aria-label="Send message"]',
            micButton: '[mattooltip="Use microphone"]',
            draftPreviewButton: '.draft-preview-button',
            generateMoreDraftsButton: '[data-test-id="generate-more-drafts-button"]',
            redoButton: '[aria-label=Redo]',
            regenerateDraftsTooltip: '[mattooltip="Regenerate drafts"]',
            editTextTooltip: '[mattooltip="Edit text"]',
            shareButton: '[aria-label*="Share"]',
            shareResponseButton: '[aria-label*="Share response"]',
            shareModeFull: '[data-test-id="share-mode-radio-button-full"] label',
            createButton: '[data-test-id="create-button"]',
            copyPublicLinkButton: '[aria-label="Copy public link"]',
            closeDialogButton: '[aria-label="Close"]',
            uploadButtonPrimary: '.upload-button button',
            uploadButtonAlt: '[aria-label*="Upload files"]',
            sidebarHide: '[aria-label*="Hide side panel"]',
            sidebarShow: '[aria-label*="Show side panel"]',
            mainMenu: '[aria-label*="Main menu"]',
            menuToggleButton: '[data-test-id="menu-toggle-button"]',
            modelSwitcherButton: '[data-test-id="bard-mode-menu-button"]',
            modelMenuPanel: '.mat-mdc-menu-panel',
            modelMenuContent: '.mat-mdc-menu-content',
            modelMenuItem: 'button.mat-mdc-menu-item',
            newChatButton: "button[aria-label*='New chat']",
            modelResponseText: '.model-response-text',
            codeTTSButton: '.response-tts-container button',
            editorCancel: '[aria-label*="Cancel"]',
            hideDuringCopy:
            '.code-block-decoration.footer, .code-block-decoration.header, .table-footer',
            hideDuringCopyRestore:
            '.code-block-decoration.footer, .code-block-decoration.header',
        },
    };

    const { assumeLastResponse, goToNextChatOnDelete } = CFG.behavior;
    const { rapidClickDelayMS } = CFG.timings;

    const hasQuery = window.location.href.includes('?q=');
    let url = new URL(window.location.href);
    let params = new URLSearchParams(url.search);
    let query = unescape(params.get('q') || '');

    window.onload = onLoad;

    function onLoad() {
        // ----- CSS tweaks (minimal & intentional) -----
        const s = document.createElement('style');
        document.head.append(s);
        s.textContent = `
      .conversation-container, .input-area-container, .bottom-container, user-query {
        max-width: -webkit-fill-available !important;
        max-width: fill-available !important;
      }
      #gbwa, .cdk-overlay-backdrop { display: none !important; }
      .code-block-decoration.footer, .code-block-decoration.header {
        user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none;
      }
      .mat-mdc-focus-indicator::before { border: none !important; }
    `;
        // NOTE: Per feedback, do NOT force-position the model switcher.

        const nums = ['first','second','third','fourth','fifth','sixth','seventh','eighth','ninth','tenth'];

        // Ensure "Show more" expanded & support ?q=
        let showMoreClicked = false;
        let inputBarClicked = false;
        const initialObserver = new MutationObserver(() => {
            const showMore = document.querySelector(CFG.selectors.showMoreButton);
            const inputBar = document.querySelector(CFG.selectors.textInputField);
            const textInput = document.querySelector(CFG.selectors.enterPrompt);

            if (showMore && !showMoreClicked) {
                showMoreClicked = true;
                simulateClick(showMore);
            }

            if (hasQuery && inputBar && !inputBarClicked) {
                if (textInput) {
                    inputBarClicked = true;
                    params.delete('q');
                    window.history.pushState(null, '', url.origin + url.pathname);

                    setTimeout(() => {
                        inputBar.click();
                        setTimeout(() => {
                            if (textInput.firstChild) textInput.firstChild.remove();
                            for (const line of query.split('\n')) {
                                const p = document.createElement('p');
                                p.innerText = line;
                                textInput.append(p);
                            }
                            const draftsObs = new MutationObserver(() => {
                                const showDrafts = document.querySelector(CFG.selectors.generateMoreDraftsButton);
                                if (showDrafts) {
                                    draftsObs.disconnect();
                                    setTimeout(() => {
                                        url = new URL(window.location.href);
                                        params = new URLSearchParams(url.search);
                                        window.history.pushState(null, '', url.origin + url.pathname);
                                    }, 2000);
                                }
                            });
                            draftsObs.observe(document.body, { childList: true, subtree: true });

                            setTimeout(() => {
                                const sendBtn = document.querySelector(CFG.selectors.sendMessageButton);
                                if (sendBtn) sendBtn.click();
                            }, rapidClickDelayMS);
                        }, rapidClickDelayMS);
                    }, rapidClickDelayMS);
                }
            } else if (inputBar && !inputBarClicked) {
                inputBarClicked = true;
                setTimeout(() => inputBar.click(), rapidClickDelayMS);
            }

            if (showMoreClicked && inputBarClicked) initialObserver.disconnect();
        });
        initialObserver.observe(document.body, { childList: true, subtree: true });

        // ====== Helpers ======
        let c = null;
        function getLastElement(querySelector) {
            const containers = document.querySelectorAll('.conversation-container');
            c = containers[containers.length - 1];
            if (!assumeLastResponse) {
                let mostVisibleElement = null;
                let maxVisibleArea = 0;
                containers.forEach((container) => {
                    const rect = container.getBoundingClientRect();
                    const vh = window.innerHeight;
                    const visibleArea = Math.max(0, Math.min(rect.bottom, vh) - Math.max(rect.top, 0));
                    if (visibleArea > maxVisibleArea && visibleArea !== 0) {
                        maxVisibleArea = visibleArea;
                        mostVisibleElement = container;
                    }
                });
                c = mostVisibleElement || c;
            }
            const list = c ? c.querySelectorAll(querySelector) : [];
            return list[list.length - 1] || null;
        }

        function copyRichTextFromDiv(element) {
            if (!element) return;
            document.querySelectorAll(CFG.selectors.hideDuringCopy)
                .forEach((el) => (el.style.display = 'none'));

            const selection = window.getSelection();
            const range = document.createRange();
            range.selectNodeContents(element);
            selection.removeAllRanges();
            selection.addRange(range);
            try { document.execCommand('copy'); } catch (_) {}
            selection.removeAllRanges();

            setTimeout(() => {
                document.querySelectorAll(CFG.selectors.hideDuringCopyRestore)
                    .forEach((el) => (el.style.display = ''));
            }, rapidClickDelayMS);
        }

        function clearNotifications() {
            for (const ele of document.querySelectorAll('.gemini-key-notification')) ele.remove();
        }

        function notify(text) {
            clearNotifications();
            const div = document.createElement('div');
            div.className = 'gemini-key-notification';
            const tDuration = 125, nDuration = 3000, tLeft = nDuration - tDuration;
            div.textContent = text;
            div.style.cssText = `
        position:fixed; bottom:26px; left:26px; font-family:sans-serif; font-size:.875rem;
        color:#fff; border-radius:4px; background:#333; z-index:2147483647; padding:14px 16px;
        box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12);
        transition:opacity ${tDuration}ms ease-in-out, transform ${tDuration}ms ease-in-out;
        transform-origin:center bottom; transform:scale(.8); opacity:0; max-width:calc(100% - 52px); box-sizing:border-box;
      `;
          document.body.appendChild(div);
          setTimeout(() => { div.style.opacity = '1'; div.style.transform = 'scale(1)'; }, 10);
          setTimeout(() => {
              div.style.opacity = '0'; div.style.transform = 'scale(.8)';
              setTimeout(() => { if (div.parentNode) div.remove(); }, tDuration);
          }, tLeft);
      }

        function simulateClick(el) { if (el) el.click(); else throw new Error('Element not found for click().'); }

        // ====== Drafts ======
        let draftIndex = 0;
        let googleDraftCount = 3;
        let waitOnGeneration = false;

        function changeDraft(direction) {
            let draftButtons = document.querySelectorAll(CFG.selectors.draftPreviewButton);
            if (!waitOnGeneration) draftIndex = (draftIndex + direction + googleDraftCount) % googleDraftCount;

            if (!waitOnGeneration && draftButtons[draftIndex]) {
                simulateClick(draftButtons[draftIndex]);
            } else if (!waitOnGeneration) {
                simulateClick(getLastElement(CFG.selectors.generateMoreDraftsButton));
                notify(`Generating ${nums[draftIndex]} draft`);
                waitOnGeneration = true;

                const obs = new MutationObserver(() => {
                    draftButtons = document.querySelectorAll(CFG.selectors.draftPreviewButton);
                    if (draftButtons[draftIndex]) {
                        obs.disconnect();
                        setTimeout(() => {
                            waitOnGeneration = false;
                            simulateClick(draftButtons[draftIndex]);
                        }, rapidClickDelayMS * 2);
                    }
                });
                obs.observe(document.body, { childList: true, subtree: true });
            } else {
                notify('Waiting on generation');
            }
        }
        const nextDraft = () => changeDraft(1);
        const previousDraft = () => changeDraft(-1);

        // ====== Chats ======
        let chatIndex = 0;
        let waitOnLoadingMore = false;

        function changeChat(direction) {
            chatIndex = Array.from(document.querySelectorAll(CFG.selectors.conversation))
                .indexOf(document.querySelector(CFG.selectors.selectedConversation));
            let chatButtons = document.querySelectorAll(CFG.selectors.conversation);

            if (!waitOnLoadingMore) chatIndex = Math.max(0, chatIndex + direction);

            if (!waitOnLoadingMore && chatButtons[chatIndex]) {
                simulateClick(chatButtons[chatIndex]);
                notify(`"${chatButtons[chatIndex].querySelector(CFG.selectors.conversationTitle).innerText.trim()}"`);
            } else if (!waitOnLoadingMore) {
                simulateClick(document.querySelector(CFG.selectors.loadMoreButton));
                notify('Loading chats');
                waitOnLoadingMore = true;

                const obs = new MutationObserver(() => {
                    chatButtons = document.querySelectorAll(CFG.selectors.conversation);
                    if (chatButtons[chatIndex]) {
                        obs.disconnect();
                        setTimeout(() => {
                            waitOnLoadingMore = false;
                            simulateClick(chatButtons[chatIndex]);
                            notify(`"${chatButtons[chatIndex].querySelector(CFG.selectors.conversationTitle).innerText.trim()}"`);
                        }, rapidClickDelayMS * 2);
                    }
                });
                obs.observe(document.body, { childList: true, subtree: true });
            } else {
                notify('Chats loading');
            }
        }
        const nextChat = () => changeChat(1);
        const previousChat = () => changeChat(-1);

        // ====== Model Switching ======
        function switchModel(modelNumber) {
            const modelIndex = modelNumber - 1;
            const switcher = document.querySelector(CFG.selectors.modelSwitcherButton);
            if (!switcher) { notify('Model switcher button not found.'); return; }
            simulateClick(switcher);
            setTimeout(() => {
                const panel = document.body.querySelector(CFG.selectors.modelMenuPanel);
                const content = panel ? panel.querySelector(CFG.selectors.modelMenuContent) : null;
                const buttons = content ? content.querySelectorAll(CFG.selectors.modelMenuItem) : null;
                if (buttons && buttons.length && modelIndex >= 0 && modelIndex < buttons.length) {
                    const btn = buttons[modelIndex];
                    const name = (btn.textContent || '').trim().replace(/\s+/g, ' ') || `Model ${modelNumber}`;
                    simulateClick(btn);
                    notify(`Switched to ${name}`);
                } else {
                    notify(`Model number ${modelNumber} is invalid or not available.`);
                }
            }, 10);
        }

        // ====== Sidebar Toggle ======
        function toggleSidebar() {
            const toggle =
                  document.querySelector(CFG.selectors.sidebarHide) ||
                  document.querySelector(CFG.selectors.sidebarShow) ||
                  document.querySelector(CFG.selectors.mainMenu) ||
                  document.querySelector(CFG.selectors.menuToggleButton);

            if (toggle) simulateClick(toggle);
            else {
                const side = document.querySelector('bard-sidenav-container');
                if (side) side.toggleAttribute('collapsed');
                else notify('Sidebar toggle not found.');
            }
        }

        // ====== New Chat (final selector) ======
        function openNewChat() {
            const btn = document.querySelector(CFG.selectors.newChatButton);
            if (btn) {
                simulateClick(btn);
                setTimeout(() => {
                    const inputBar = document.querySelector(CFG.selectors.textInputField);
                    if (inputBar) inputBar.click();
                }, rapidClickDelayMS);
            } else {
                notify('New Chat button not found.');
            }
        }

        // ====== Key Handling ======
        const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);

        function handleKeydown(event) {
            const key = (event.key || '').toLowerCase();
            const code = event.code;
            const isCmdOrCtrl = (isMac && event.metaKey) || (!isMac && event.ctrlKey);
            const activeEl = document.activeElement;

            // --- Robust Help shortcut: Cmd/Ctrl + Shift + ? ---
            if (
                isCmdOrCtrl &&
                event.shiftKey &&
                (
                    key === (CFG.hotkeys.showHelp.key || '?') ||
                    (CFG.hotkeys.showHelp.altKeys || []).includes(key) ||
                    (CFG.hotkeys.showHelp.codes || []).includes(code)
                )
            ) {
                event.preventDefault();
                showHelpPopup();
                return;
            }

            // --- Standalone Shortcuts ---

            // Alt+Number jump (1-9/0=10)
            let keyNumber = parseInt(event.code.replace('Digit', ''), 10);
            keyNumber = keyNumber === 0 ? 10 : keyNumber;
            if (event.altKey && keyNumber) {
                const items = document.querySelectorAll(CFG.selectors.conversation);
                if (items[keyNumber - 1]) {
                    items[keyNumber - 1].click();
                    const title = items[keyNumber - 1].querySelector(CFG.selectors.conversationTitle).innerHTML.trim();
                    notify(`"${title}"`);
                }
                event.preventDefault();
                return;
            }

            // Cancel edit (Esc while editing)
            if (key === 'escape' && !event.shiftKey && activeEl?.getAttribute('aria-label')?.includes('Edit prompt')) {
                const cancel = getLastElement(CFG.selectors.editorCancel);
                if (cancel) simulateClick(cancel);
                event.preventDefault();
                return;
            }

            // Model switch: Ctrl + [1-9/0]
            if (event.ctrlKey && keyNumber) {
                switchModel(keyNumber);
                event.preventDefault();
                return;
            }

            // --- Cmd/Ctrl Shortcuts ---

            // Open file: Cmd/Ctrl + O
            if (isCmdOrCtrl && !event.shiftKey && key === CFG.hotkeys.openFile.key) {
                event.preventDefault();
                const upBtn = document.querySelector(CFG.selectors.uploadButtonPrimary)
                || document.querySelector(CFG.selectors.uploadButtonAlt);
                if (upBtn) simulateClick(upBtn);
                return;
            }

            // Sidebar: Cmd/Ctrl + B
            if (isCmdOrCtrl && !event.shiftKey && key === CFG.hotkeys.sidebarToggle.key) {
                event.preventDefault();
                toggleSidebar();
                return;
            }

            // --- Cmd/Ctrl + Shift Shortcuts ---

            // This block now correctly requires BOTH Cmd/Ctrl and Shift to be pressed.
            if (isCmdOrCtrl && event.shiftKey) {
                switch (key) {
                        // New Chat: Cmd/Ctrl + Shift + O
                    case CFG.hotkeys.newChat.key: {
                        openNewChat();
                        event.preventDefault();
                        break;
                    }
                    case 'c': {
                        getLastElement();
                        const resp = c ? c.querySelector(CFG.selectors.modelResponseText) : null;
                        copyRichTextFromDiv(resp);
                        notify('Copied response');
                        event.preventDefault();
                        break;
                    }
                    case 'f': {
                        const mainMenu = document.querySelector(CFG.selectors.mainMenu);
                        simulateClick(mainMenu);
                        event.preventDefault();
                        break;
                    }
                    case 'backspace': {
                        event.preventDefault();
                        chatIndex = Array.from(document.querySelectorAll(CFG.selectors.conversation))
                            .indexOf(document.querySelector(CFG.selectors.selectedConversation));
                        const actions = document.querySelector('.conversation.selected')
                        ?.parentElement?.querySelector(CFG.selectors.actionsMenuButton);
                        simulateClick(actions);
                        setTimeout(() => {
                            simulateClick(document.body.querySelector(CFG.selectors.deleteButton));
                        }, rapidClickDelayMS);
                        setTimeout(() => {
                            simulateClick(document.body.querySelector(CFG.selectors.confirmButton));
                            setTimeout(() => {
                                if (goToNextChatOnDelete) {
                                    const next = document.querySelectorAll(CFG.selectors.conversation)[chatIndex];
                                    if (next) simulateClick(next);
                                }
                            }, rapidClickDelayMS);
                        }, rapidClickDelayMS * 2); // Increased delay to ensure menu is open
                        break;
                    }
                    case 'd': {
                        let el = getLastElement(CFG.selectors.redoButton) || getLastElement(CFG.selectors.regenerateDraftsTooltip);
                        simulateClick(el);
                        event.preventDefault();
                        break;
                    }
                    case 'e': {
                        simulateClick(getLastElement(CFG.selectors.editTextTooltip));
                        event.preventDefault();
                        break;
                    }
                    case ';': {
                        event.preventDefault();
                        getLastElement();
                        const blocks = c ? c.querySelectorAll('code-block') : [];
                        copyRichTextFromDiv(blocks[blocks.length - 1]);
                        notify('Copied last code block to clipboard');
                        break;
                    }
                    case "'": {
                        event.preventDefault();
                        getLastElement();
                        const blocks = c ? c.querySelectorAll('code-block') : [];
                        copyRichTextFromDiv(blocks[blocks.length - 2]);
                        notify('Copied second-last code block to clipboard');
                        break;
                    }
                    case 'm': {
                        simulateClick(getLastElement(CFG.selectors.shareButton));
                        setTimeout(() => simulateClick(document.querySelector(CFG.selectors.shareResponseButton)), rapidClickDelayMS);
                        setTimeout(() => simulateClick(document.querySelector(CFG.selectors.shareModeFull)), rapidClickDelayMS * 2);
                        setTimeout(() => simulateClick(document.querySelector(CFG.selectors.createButton)), rapidClickDelayMS * 3);

                        const obs = new MutationObserver(() => {
                            const element = document.querySelector(CFG.selectors.copyPublicLinkButton);
                            if (element) {
                                obs.disconnect();
                                simulateClick(element);
                                setTimeout(() => {
                                    simulateClick(document.querySelector(CFG.selectors.closeDialogButton));
                                    notify('Chat link copied');
                                }, rapidClickDelayMS);
                            }
                        });
                        obs.observe(document.body, { childList: true, subtree: true });
                        clearNotifications();
                        event.preventDefault();
                        break;
                    }
                    case 'l': {
                        simulateClick(getLastElement(CFG.selectors.shareButton));
                        setTimeout(() => simulateClick(document.querySelector(CFG.selectors.shareResponseButton)), rapidClickDelayMS);
                        setTimeout(() => simulateClick(document.querySelector(CFG.selectors.createButton)), rapidClickDelayMS * 2);

                        const obs = new MutationObserver(() => {
                            const element = document.querySelector(CFG.selectors.copyPublicLinkButton);
                            if (element) {
                                obs.disconnect();
                                simulateClick(element);
                                setTimeout(() => {
                                    simulateClick(document.querySelector(CFG.selectors.closeDialogButton));
                                    notify('Prompt/response link copied');
                                }, rapidClickDelayMS);
                            }
                        });
                        obs.observe(document.body, { childList: true, subtree: true });
                        event.preventDefault();
                        break;
                    }
                    case ',': { previousDraft(); break; }
                    case '.': { nextDraft(); break; }
                    case '-': { event.preventDefault(); previousChat(); break; }
                    case '=': { event.preventDefault(); nextChat(); break; }
                    case 'k': {
                        event.preventDefault();
                        simulateClick(document.querySelector(CFG.selectors.sendMessageButton));
                        break;
                    }
                    case 'y': {
                        simulateClick(getLastElement(CFG.selectors.codeTTSButton));
                        event.preventDefault();
                        break;
                    }
                    case 's': {
                        simulateClick(document.querySelector(CFG.selectors.micButton));
                        event.preventDefault();
                        break;
                    }
                }
            }
        }

        document.addEventListener('keydown', (event) => {
            try { handleKeydown(event); }
            catch (error) { notify(`Failed to execute shortcut: ${error}`); }
        }, { capture: true });

        // ====== Help Popup ======
        function showHelpPopup() {
            const existing = document.getElementById('gemini-help-popup');
            if (existing) {
                existing.remove();
                const overlay0 = document.getElementById('gemini-help-overlay');
                if (overlay0) overlay0.remove();
            }

            const overlay = document.createElement('div'); overlay.id = 'gemini-help-overlay';
            const popup = document.createElement('div'); popup.id = 'gemini-help-popup';

            const style = document.createElement('style');
            style.textContent = `
        #gemini-help-popup{background:#202124;color:#e8eaed;padding:25px;border-radius:8px;box-shadow:0 4px 15px rgba(0,0,0,.2);max-width:700px;max-height:80vh;overflow-y:auto;z-index:2147483647;font-family:sans-serif;line-height:1.6;position:relative}
        #gemini-help-popup h2{color:#8ab4f8;margin:0 0 15px;font-size:1.4em}
        #gemini-help-popup h3{color:#bdc1c6;margin:20px 0 10px;font-size:1.1em;border-bottom:1px solid #5f6368;padding-bottom:5px}
        #gemini-help-popup table{width:100%;border-collapse:collapse;margin-bottom:15px;font-size:.95em}
        #gemini-help-popup th,#gemini-help-popup td{border:1px solid #5f6368;padding:8px 12px;text-align:left}
        #gemini-help-popup th{background:#3c4043}
        #gemini-help-popup code{background:#3c4043;padding:2px 5px;border-radius:4px;font-family:monospace}
        #gemini-help-popup .close-button{position:absolute;top:10px;right:15px;background:none;border:none;font-size:1.8em;color:#9aa0a6;cursor:pointer;line-height:1}
        #gemini-help-popup .close-button:hover{color:#e8eaed}
        #gemini-help-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:2147483646;display:flex;justify-content:center;align-items:center}
      `;

          const close = document.createElement('button'); close.className = 'close-button'; close.title = 'Close (Esc)'; close.textContent = '×';
          const title = document.createElement('h2'); title.textContent = 'Gemini Keyboard Shortcuts Help';
          const intro = document.createElement('p'); intro.appendChild(document.createTextNode('Press '));
          const esc = document.createElement('code'); esc.textContent = 'Esc';
          intro.appendChild(esc); intro.appendChild(document.createTextNode(" or click the 'X' to close this window."));

          const section = (label, rows) => {
              const h3 = document.createElement('h3'); h3.textContent = label; popup.appendChild(h3);
              const table = document.createElement('table');
              const thead = table.createTHead(); const tr = thead.insertRow();
              const th1 = document.createElement('th'); th1.textContent = 'Shortcut (Mac/Windows)'; tr.appendChild(th1);
              const th2 = document.createElement('th'); th2.textContent = 'Action'; tr.appendChild(th2);
              const tbody = table.createTBody();
              rows.forEach(r => { const row = tbody.insertRow(); const c1 = row.insertCell(); const c2 = row.insertCell();
                                 const code = document.createElement('code'); code.textContent = r.key; c1.appendChild(code); c2.textContent = r.action; });
              popup.appendChild(table);
          };

          popup.appendChild(style); popup.appendChild(close); popup.appendChild(title); popup.appendChild(intro);

          section('Chat Management', [
              { key: '⌘/Ctrl + Shift + O', action: 'Open new chat' },        // UPDATED
              { key: '⌘/Ctrl + Shift + Backspace', action: 'Delete chat' },
              { key: '⌘/Ctrl + B', action: 'Hide/Show sidebar' },            // stays
              { key: '⌥/Alt + 1–9', action: 'Go to nth chat' },
              { key: '⌘/Ctrl + Shift + =', action: 'Next chat' },
              { key: '⌘/Ctrl + Shift + –', action: 'Previous chat' },
          ]);

          section('Text Input and Editing', [
              { key: 'Shift + Esc', action: 'Focus chat input' },
              { key: '⌘/Ctrl + Shift + E', action: 'Edit text' },
              { key: '⌘/Ctrl + Shift + ;', action: 'Copy last code block' },
              { key: "⌘/Ctrl + Shift + '", action: 'Copy second-to-last code block' },
              { key: '⌘/Ctrl + Shift + C', action: 'Copy response' },
              { key: '⌘/Ctrl + Shift + K', action: 'Stop/start generation' },
          ]);

          section('Draft Navigation', [
              { key: '⌘/Ctrl + Shift + D', action: 'Generate more drafts' },
              { key: '⌘/Ctrl + Shift + ,', action: 'Next draft' },
              { key: '⌘/Ctrl + Shift + .', action: 'Previous draft' },
          ]);

          section('Sharing and Linking', [
              { key: '⌘/Ctrl + Shift + L', action: 'Copy prompt/response link' },
              { key: '⌘/Ctrl + Shift + M', action: 'Copy chat link' },
          ]);

          section('Audio and File Shortcuts', [
              { key: '⌘/Ctrl + Shift + Y', action: 'Play/pause audio' },
              { key: '⌘/Ctrl + Shift + S', action: 'Voice to text' },
              { key: '⌘/Ctrl + O', action: 'Open file' },
          ]);

          section('Other', [
              { key: '⌘/Ctrl + Shift + ?', action: 'Show this help' },
              { key: 'Ctrl + 1–9/0', action: 'Switch Model (if available)' },
          ]);

          const h3 = document.createElement('h3'); h3.textContent = 'Disabling the Script'; popup.appendChild(h3);
          const p = document.createElement('p'); p.textContent = 'To temporarily or permanently disable these shortcuts:'; popup.appendChild(p);
          const ol = document.createElement('ol');
          [
              'Open the Tampermonkey extension menu in your browser.',
              'Go to the "Dashboard".',
              'Find the script named "Gemini Keyboard Shortcuts".',
              'Toggle the switch next to the script name to disable it.',
          ].forEach(t => { const li = document.createElement('li'); li.textContent = t; ol.appendChild(li); });
          popup.appendChild(ol);

          const closePopup = () => { overlay.remove(); document.removeEventListener('keydown', escListener); };
          const escListener = (e) => { if ((e.key || '').toLowerCase() === 'escape') closePopup(); };

          overlay.addEventListener('click', (e) => { if (e.target === overlay) closePopup(); });
          close.addEventListener('click', closePopup);
          document.addEventListener('keydown', escListener);

          const style2 = document.createElement('style'); style2.textContent = `#gemini-help-popup{background:#202124}`;
          overlay.appendChild(style2);
          overlay.appendChild(popup);
          document.body.appendChild(overlay);
      }
        // ===== End Help Popup =====
    }
})();