⌘-Enter to Submit in AI Chats [MonkeyScript]

3 min read Original article ↗
// ==UserScript== // @name Chat Cmd+Enter to Submit // @namespace https://intellectronica.net/ // @version 1.0.0 // @description Makes Enter add a newline and Cmd+Enter (or Ctrl+Enter) submit in Claude, ChatGPT, Gemini, and Google AI Mode // @author Eleanor Berger <eleanor@intellectronica.net> // @license Public Domain // @match https://claude.ai/* // @match https://chat.openai.com/* // @match https://chatgpt.com/* // @match https://gemini.google.com/* // @match https://www.google.com/search* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; const INPUT_SELECTORS = { claude: '.ProseMirror[contenteditable="true"], div[data-placeholder][contenteditable="true"]', chatgpt: '#prompt-textarea, textarea[data-id="root"], div[contenteditable="true"][data-id]', gemini: '.ql-editor[contenteditable="true"], div[contenteditable="true"][aria-label*="prompt"], rich-textarea [contenteditable="true"]', googleai: 'textarea[aria-label="Ask anything"], textarea.ITIRGe' }; const SUBMIT_SELECTORS = { claude: 'button[data-testid="send-button"], button[aria-label*="Send message" i], button[aria-label="Send" i]', chatgpt: 'button[data-testid="send-button"], button[aria-label*="Send"], form button[type="submit"]', gemini: 'button[aria-label*="Send"], button.send-button, button[mattooltip*="Send"]', googleai: 'button[aria-label*="Send" i], button[aria-label*="Submit" i], button[type="submit"]' }; function getCurrentSite() { const host = location.hostname; if (host.includes('claude.ai')) return 'claude'; if (host.includes('openai.com') || host.includes('chatgpt.com')) return 'chatgpt'; if (host.includes('gemini.google.com')) return 'gemini'; if (host.includes('google.com') && (location.search.includes('udm=50') || document.querySelector('textarea[aria-label="Ask anything"]'))) { return 'googleai'; } return null; } function getInputElement(element, site) { if (!element) return null; const selector = INPUT_SELECTORS[site]; if (!selector) return null; if (element.matches(selector)) return element; return element.closest(selector); } function findSubmitButton(site, inputEl) { const selector = SUBMIT_SELECTORS[site]; if (!selector) return null; // Try direct selector match const buttons = document.querySelectorAll(selector); for (const btn of buttons) { if (!btn.disabled && btn.offsetParent !== null) { return btn; } } // Fallback: walk up from input to find a suitable button if ((site === 'claude' || site === 'googleai') && inputEl) { let container = inputEl.parentElement; for (let i = 0; i < 10 && container; i++) { const candidates = Array.from(container.querySelectorAll('button')).filter(btn => { if (btn.disabled || btn.offsetParent === null) return false; const label = (btn.getAttribute('aria-label') || '').toLowerCase(); if (label.includes('menu') || label.includes('attachment') || label.includes('voice') || label.includes('microphone')) return false; if (site === 'claude' && !btn.querySelector('svg')) return false; return true; }); if (candidates.length > 0) { return candidates[candidates.length - 1]; } container = container.parentElement; } } return buttons[0] || null; } function triggerSubmit(site, inputEl) { let submitBtn = findSubmitButton(site, inputEl); if (submitBtn) { if (submitBtn.tagName.toLowerCase() !== 'button') { submitBtn = submitBtn.closest('button'); } if (submitBtn) { submitBtn.click(); return true; } } // Fallback: dispatch Enter key event for sites that submit via Enter if (inputEl) { inputEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true })); return true; } return false; } function handleKeyDown(event) { const site = getCurrentSite(); if (!site) return; const inputEl = getInputElement(event.target, site); if (!inputEl) return; if (event.key !== 'Enter') return; const hasModifier = event.metaKey || event.ctrlKey; const hasShift = event.shiftKey; if (hasModifier && !hasShift) { // Cmd+Enter or Ctrl+Enter: Submit event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); setTimeout(() => triggerSubmit(site, inputEl), 10); } else if (!hasModifier && !hasShift) { // Plain Enter: Transform into Shift+Enter to insert newline Object.defineProperty(event, 'shiftKey', { get: () => true }); } // Shift+Enter: Default behavior (newline) } document.addEventListener('keydown', handleKeyDown, true); })();