ChatGPT Command+Enter Send

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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