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.

// ==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 =====
    }
})();