ChatGPT Command+Enter Send

Send messages with Command+Enter or Ctrl+Enter. Enter and Shift+Enter will create new lines.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

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

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         ChatGPT Command+Enter Send
// @description  Send messages with Command+Enter or Ctrl+Enter. Enter and Shift+Enter will create new lines.
// @author       schweigen
// @license      MIT
// @version      1.2
// @namespace    ChatGPT.CommandEnterSend
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @match        https://chatgpt.com/*
// @match        https://chat.openai.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    function getPromptElementFrom(target) {
        if (!(target instanceof Element)) return null;

        const byId = target.closest('#prompt-textarea');
        if (byId) return byId;

        const byProseMirror = target.closest('.ProseMirror[contenteditable="true"]');
        if (byProseMirror && byProseMirror.closest('form')?.querySelector('textarea[name="prompt-textarea"]')) {
            return byProseMirror;
        }

        const byName = target.closest('textarea[name="prompt-textarea"]');
        if (byName) return byName;

        return null;
    }

    function dispatchEnter(target, { shiftKey }) {
        const event = new KeyboardEvent('keydown', {
            key: 'Enter',
            code: 'Enter',
            keyCode: 13,
            which: 13,
            shiftKey,
            ctrlKey: false,
            altKey: false,
            metaKey: false,
            bubbles: true,
            cancelable: true,
            composed: true
        });
        target.dispatchEvent(event);
    }

    function insertNewlineIntoTextarea(textarea) {
        const value = textarea.value ?? '';
        const start = Number.isFinite(textarea.selectionStart) ? textarea.selectionStart : value.length;
        const end = Number.isFinite(textarea.selectionEnd) ? textarea.selectionEnd : value.length;
        const nextValue = value.slice(0, start) + '\n' + value.slice(end);
        textarea.value = nextValue;

        const nextCursor = start + 1;
        textarea.selectionStart = nextCursor;
        textarea.selectionEnd = nextCursor;

        textarea.dispatchEvent(new Event('input', { bubbles: true }));
    }

    function insertNewline(promptEl) {
        if (promptEl instanceof HTMLTextAreaElement) {
            insertNewlineIntoTextarea(promptEl);
            return;
        }
        dispatchEnter(promptEl, { shiftKey: true });
    }

    function isStopButton(button) {
        if (!(button instanceof HTMLButtonElement)) return false;
        const testId = button.getAttribute('data-testid');
        if (testId && testId.toLowerCase().includes('stop')) return true;
        const aria = button.getAttribute('aria-label') || '';
        if (/stop/i.test(aria)) return true;
        if (button.querySelector('svg[aria-label*="Stop"]')) return true;
        return false;
    }

    function isGenerating(form) {
        if (!form) return false;
        const stopButton = form.querySelector('button[data-testid="stop-button"]');
        if (stopButton) return true;
        const submitButton = form.querySelector('#composer-submit-button');
        return submitButton ? isStopButton(submitButton) : false;
    }

    function clickSendButtonNear(promptEl) {
        const form = promptEl.closest('form');
        const button = form?.querySelector('button[data-testid="send-button"], button#composer-submit-button');
        if (!button || !(button instanceof HTMLButtonElement) || button.disabled) return false;
        if (isStopButton(button)) return false;
        button.click();
        return true;
    }

    function sendMessage(promptEl) {
        const form = promptEl.closest('form');
        if (isGenerating(form)) return;
        if (clickSendButtonNear(promptEl)) return;
        dispatchEnter(promptEl, { shiftKey: false });
    }

    function handleKeyDown(event) {
        if (event.key !== 'Enter') return;
        if (!event.isTrusted) return;
        if (event.isComposing || event.keyCode === 229) return;

        const promptEl = getPromptElementFrom(event.target) || getPromptElementFrom(document.activeElement);
        if (!promptEl) return;

        const wantsSend = event.metaKey || event.ctrlKey;
        if (wantsSend && event.repeat) {
            event.preventDefault();
            event.stopImmediatePropagation();
            return;
        }

        event.preventDefault();
        event.stopImmediatePropagation();

        if (wantsSend) {
            sendMessage(promptEl);
        } else {
            insertNewline(promptEl);
        }
    }

    window.addEventListener('keydown', handleKeyDown, true);
})();