少し前のTampermonkeyでEnter送信の入力欄をCtrl+Enter送信&Enter改行に変える(Discord、Google Chat、ChatGPT、Twitter(X)、Grokに対応) - turgenev’s blogの続きのような感じなのですが、ChatGPTで他にも不自然な挙動があったので同じくTampermonkeyを使ったスクリプトで修正します。
今回修正(・改善)する挙動は以下の4つです。
- ChatGPTによる応答メッセージをコピーする際、コードブロックが含まれていると、その手前までしかコピーされない。
- ユーザーの過去のメッセージを編集している際に、Ctrl+Cや右クリックメニューからテキストをコピーしようとしてもコピーされない。
- ユーザーの過去のメッセージを(編集せず)コピーした際、改行が全て削除されている。
- (改善)ユーザーの過去のメッセージを編集した後、キーボード入力での送信ができない(新規入力欄と異なり、Enterは改行になる)。
普通に使っていても結構不便で、酷いものです。
対処の方法としては、まず1と2は全く同じで、stopImmediatePropagation()というのを呼んでChatGPTの独自動作?(詳しくは不明)みたいなのが呼ばれないように普通のcopyイベントが普通に処理されるような感じにします。3については、<div>の中に改行文字\nが直接書かれているのが原因ぽかったので、これを全て<br>に置換します。ただ、何も工夫しないものすごい呼び出し回数になるみたいで重くなってしまったので、CSSセレクタを少し真面目にして、ChatGPTに言われた通りにdataset.processedというのを書きました。4については冒頭の過去記事に挙動をあわせて、Ctrl+Enterを押したときに送信ボタンにclickイベントが発生するようにします。
- 2025-10-10追記(修正)…click()と書くべきところ括弧が抜けていて動作しない状態になっていました。一度は動作したはずなのですがそのあと間違えて消してしまったのかもしれません。修正しました。
コードは以下です。
// ==UserScript==
// @name ChatGPT Fixes
// @namespace http://tampermonkey.net/
// @version 2025-08-31
// @description try to take over the world!
// @author You
// @match https://chatgpt.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
function setupTextarea(textarea) {
textarea.addEventListener('keydown', function(e) {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
let edit_submit = document.querySelector("button.btn.btn-primary")
if (edit_submit) edit_submit.click()
}
}, true);
textarea.addEventListener('copy', function(e) {
e.stopImmediatePropagation();
}, true);
}
function setupMydiv(textarea) {
textarea.addEventListener('copy', function(e) {
e.stopImmediatePropagation();
}, true);
}
const observer = new MutationObserver(() => {
const textareas = document.querySelectorAll('main article textarea');
textareas.forEach(textarea => {
setupTextarea(textarea);
});
const mydivs = document.querySelectorAll('main article div');
mydivs.forEach(mydiv => {
setupMydiv(mydiv);
});
const els = document.querySelectorAll("main article div.user-message-bubble-color > div.whitespace-pre-wrap");
els.forEach(el => {
if (!el.dataset.processed) {
el.innerHTML = el.innerText.replace(/\n/g, "<br>");
el.dataset.processed = "true";
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
})();
短いですが、以上です。