您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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 ===== } })();