以下の内容はhttps://turgenev.hatenablog.com/entry/2025/07/17/223434より取得しました。


TampermonkeyでEnter送信の入力欄をCtrl+Enter送信&Enter改行に変える(Discord、Google Chat、ChatGPT、Twitter(X)、Grok、Geminiに対応)

概要

世界は英語を中心に回っているので、各種ソフトウェアやWebサイトの中には他言語での利用にあまり適していない設計になっているものもよくあります。その典型例の一つがEnterの扱いです。

英語ではEnterを「変換」に使うことがないため、入力フォームのようなものをEnterキーで「決定」「送信」されるようにして、改行はShift+Enterで入力することにしてもそれほど使いづらくありません。一方日本語では「変換」の操作が必要で、かつこれはShift+Enterではできない操作なので、このようなフォームで改行を入力しようとするとEnterで確定Shift+Enterで改行という操作をミスなく行う必要があります。あるいは、EnterとShiftに関して押し間違いがなくても、意図せず日本語入力ではなく英数字入力になっていた場合にはひらがなを打って確定のつもりでEnterを押したところで送信されてしまうということもあります。

従って日本語ユーザーの使い勝手を考えれば、Enterを送信に割り当てるのをやめて改行用にし、送信のためにはEnterでないキー(Ctrl+EnterまたはAlt+Enterの2択だが、おそらく前者のほうがメジャー)を使うほうがベターです。

このような改変は日本語入力が伴う任意のPC用ソフトウェアに対して有意味ですが、この記事ではブラウザ上で動作するWebアプリケーションのみを対象にします。

現代の主要なブラウザでは拡張機能(アドオン)が利用可能で、大抵はWebExtensions APIがベースになっているので、ChromeFirefox両方で利用できるように開発されている拡張機能なども多くあります。その中で今回の記事でやりたいようなキーイベントの改変に使えるのがTampermonkeyです。これは一般にWebページの動作を好き勝手に書き換えるために使えるものです。リダイレクトなどもできます。書いたスクリプトGoogle Driveで同期とかもできるので便利です。

ブラウザ上で動作するので、当然Windows/Mac/Linuxなどクロスプラットフォームで動作します。ただしMacでの動作検証はしていません。

以下ではこのTampermonkey用として、6つのサイトをCtrl+Enter送信&Enter改行に変えるためのスクリプトを2種類紹介します。

先例・経緯

Tampermonkey用のスクリプトではないのですが、ChatGPTやGrokはじめ各種AIモデルのサイトをCtrl+Enter送信に変えてくれるChatGPT Ctrl+Enter Sender - Chrome ウェブストアFirefox版はChatGPT Ctrl+Enter Sender – 🦊 Firefox (ja) 向け拡張機能を入手)(ソースコードGitHub - masachika-kamada/ChatGPT-Ctrl-Enter-Sender: ChatGPT用Chrome拡張機能)という名前まんまの拡張機能が既にあります。

この記事の経緯としては、この拡張機能がそもそも対応していないDiscord向けに同じことをするためのスクリプトを書き(というかChatGPTに書かせ)、さらにこの拡張機能も参考にしつつChatGPT・Grok・Twitter(ダイレクトメッセージ)・Google Chat・Geminiにも対応してみた(再びChatGPTを使って)、という感じです。

Discord・Grok・Gemini用

まずDiscord・Grok・Gemini用です。これはChatGPT・TwitterGoogle Chatだとそのままでは動きません。textarea要素が使われている場合に適しているようです。

// ==UserScript==
// @name         Ctrl+Enter Sender (Discord, Grok, Gemini)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Enter for line breaks, Ctrl+Enter for sending
// @author       You
// @match        https://discord.com/*
// @match        https://grok.com/*
// @match        https://gemini.google.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var url = window.location.href;
    let selector;
    if ( url.match(new RegExp(/https:\/\/discord.com/)) != null ) {
        selector = 'div[role="textbox"][data-slate-editor="true"]';
    }
    else if ( url.match(new RegExp(/https:\/\/grok.com/)) != null ) {
        selector = 'main form textarea'
    }
    else if ( url.match(new RegExp(/https:\/\/gemini.google.com/)) != null ) {
        selector = 'main chat-window :is(user-query mat-form-field textarea, input-container rich-textarea)'
    }
    function setupTextarea(textarea) {
        textarea.addEventListener('keydown', function(e) {
            // Ctrl + Enter
            if (e.ctrlKey && e.key === 'Enter') {
                e.preventDefault();
                textarea.dispatchEvent(new KeyboardEvent('keydown', {
                    key: 'Enter',
                    code: 'Enter',
                    bubbles: true,
                    cancelable: true
                }));
            }
            // Enter
            else if (!e.shiftKey && !e.ctrlKey && e.key === 'Enter') {
                e.stopPropagation();
            }
        }, true);
    }

    const observer = new MutationObserver(() => {
        const textareas = document.querySelectorAll(selector);
        textareas.forEach(textarea => {
            setupTextarea(textarea);
        });
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();

最初でサイトに応じてセレクタを分けていますが、あとの実装は完全に共通です。

querySelectorAllにしているのは、Discordの「転送」機能に対応するためです。

ちなみにDiscordは、もともとの仕様として、コードブロック(バッククォート3つで囲まれている)の編集中にはEnter(あるいはCtrl+Enterなど)を押しても送信されず、改行されるようになっています。このため、上記スクリプトでも同じ条件でCtrl+Enterを押した場合は送信が行われません。

カーソルを先頭に持って行ってからCtrl+Enterするなどの方法も考えましたが、イベントがうまく発動しないようです。また、送信ボタンにclickイベントを送る方法もありますが、Discordには送信ボタンがないのでこれもできません(ちなみに既存メッセージの編集のUIにはあるのでこっちでは動く)。

Twitter・ChatGPT・Google Chat用

次にTwitter・ChatGPT・Google Chat用です。先ほどよりも長くなっています。こちらもDiscord・Grok・Geminiでは動きません。このへんの仕組みはあまり理解できていないのですが、div要素などでcontenteditable=trueが使われているタイプの場合はこちらのスクリプトが良さそうです。

ちなみにTwitter(DMだけでなくツイートも)では、複数行のテキストを選択した状態から日本語を入力し始めるとHTMLの構造がおかしくなって文字が消えたりするバグがあるので使用時は気をつけると良いでしょう。

// ==UserScript==
// @name         Ctrl+Enter Sender (ChatGPT, X, Google Chat)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Enter for line breaks, Ctrl+Enter for sending
// @author       You
// @match        https://x.com/messages/*
// @match        https://chatgpt.com/*
// @match        https://chat.google.com/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    var url = window.location.href;
    let useMeta=false
    let selector;
    if ( url.match(new RegExp(/https:\/\/chatgpt.com/)) != null ) {
        selector = 'div.ProseMirror#prompt-textarea[contenteditable="true"]';
        useMeta = true
    }
    else if ( url.match(new RegExp(/https:\/\/x.com/)) != null ) {
        selector = 'div[contenteditable="true"][data-testid="dmComposerTextInput"]'
    }
    else if ( url.match(new RegExp(/https:\/\/chat.google.com/)) != null ) {
        selector = 'div[contenteditable="true"].editable'
    }

    function setupEditor(editor) {
        if (editor.dataset.ctrlEnterInstalled) return; // avoid duplicate registration
        editor.dataset.ctrlEnterInstalled = "true";

        editor.addEventListener('keydown', (e) => {
            // avoid loop
            if (e.__fromTamperMonkey) return;

            // Ctrl+Enter
            if (e.ctrlKey && e.key === 'Enter') {

                e.preventDefault();
                e.stopImmediatePropagation();

                const submitEvent = new KeyboardEvent('keydown', {
                    key: 'Enter',
                    code: 'Enter',
                    bubbles: true,
                    cancelable: true,
                    metaKey: useMeta, // Meta+Enter for some sites
                    shiftKey: false,
                    keyCode: 13,
                });
                Object.defineProperty(submitEvent, '__fromTamperMonkey', {
                    value: true
                });
                editor.dispatchEvent(submitEvent);
            }

            // Enter
            else if (!e.ctrlKey && e.key === 'Enter') {
                e.preventDefault();
                e.stopImmediatePropagation();

                const lineBreakEvent = new KeyboardEvent('keydown', {
                    key: 'Enter',
                    code: 'Enter',
                    bubbles: true,
                    cancelable: true,
                    shiftKey: true, // Shift+Enter
                    metaKey: false,
                    ctrlKey: false,
                    keyCode: 13,
                });
                Object.defineProperty(lineBreakEvent, '__fromTamperMonkey', {
                    value: true
                });
                editor.dispatchEvent(lineBreakEvent);
            }
        }, true);
    }

    const observer = new MutationObserver(() => {
        const editor = document.querySelector(selector);
        if (editor) {
            setupEditor(editor);
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();

__fromTamperMonkeyというフィールドをセットしておくことによって自前で生成したキーイベントかどうかを判別し、無限ループになることを防いでいます。

Metaキーに関しては、前述のChatGPT Ctrl+Enter Senderのソースコードを見た感じ、一部環境では付けておいたほうが動きやすくなるようだったので付けました。しかしTwitterGoogle Chatの場合は付けると動かないのでfalseにしてあります。

あとは先ほどと同じようにサイトごとにセレクタを切り替えているだけで、ほかの部分の動作は共通です。先ほどとの大きな違いとしてはEnterが押されたときにわざわざShift+Enterのキーイベントを作成しています。

まとめ

スクリプトは2種類(計6サイト対応)しか作っていませんが、ChatGPT Ctrl+Enter Senderでもサイト間での動作の違いはそこまで大きくないようなので、この2つのどっちかをちょっと変えれば他サイトでも使える可能性は高いのではないでしょうか。

また他の記事ですが、Google Chatについては株式会社NXTED|【Chrome用】Google Chatの改行設定を変更してみた、Teamsに関してはキー操作の混乱にさよなら!TeamsのEnterキーで誤送信しない操作方法 🚀 #JavaScript - Qiitaで似たようなことをやっている方がいました。

余談

Webアプリケーションでない場合は拡張機能などは使えないので各OS向けのキーイベント改変ツール(AutoHotkeyとか)を使うことになります。

 



以上の内容はhttps://turgenev.hatenablog.com/entry/2025/07/17/223434より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14