Enter=Newline スクリプト[edit]
// ==UserScript==
// @name ChatGPT: Enter=Newline, Ctrl+Enter=Send (stable CE)
// @match https://chatgpt.com/*
// @match https://chat.openai.com/*
// @run-at document-start
// @grant none
// @version 0.0.1
// @description Enter is Newline
// ==/UserScript==
(function () {
'use strict';
console.log('[TM] ChatGPT key-remap loaded');
function isEditable(el) {
if (!el) return false;
if (el.tagName === 'TEXTAREA') return true;
if (el.tagName === 'INPUT') return false;
return el.isContentEditable === true;
}
function insertNewlineTextarea(el) {
const start = el.selectionStart ?? el.value.length;
const end = el.selectionEnd ?? el.value.length;
el.setRangeText('\n', start, end, 'end');
el.dispatchEvent(new Event('input', { bubbles: true }));
}
function insertNewlineContentEditable(el) {
// Selection/Range で <br> と ZWSP を挿入し,カーソルを次行へ
const sel = window.getSelection();
if (!sel || sel.rangeCount === 0) return;
const range = sel.getRangeAt(0);
range.deleteContents();
const br = document.createElement('br');
const zwsp = document.createTextNode('\u200B'); // 次行でタイピング可能にするおまじない
range.insertNode(br);
range.setStartAfter(br);
range.insertNode(zwsp);
// キャレットを ZWSP の後ろへ
const after = document.createRange();
after.setStartAfter(zwsp);
after.collapse(true);
sel.removeAllRanges();
sel.addRange(after);
// React/TipTap 等に変更通知
el.dispatchEvent(new InputEvent('input', { bubbles: true, data: '\n' }));
}
function insertNewline(el) {
if (el.tagName === 'TEXTAREA') return insertNewlineTextarea(el);
// 念のため focus
el.focus();
insertNewlineContentEditable(el);
}
function clickSendButton() {
const candidates = [
'button[type="submit"]',
'button[data-testid="send-button"]',
'button[aria-label*="Send"]',
'button[aria-label*="送信"]',
];
for (const sel of candidates) {
const btn = document.querySelector(sel);
if (btn && !btn.disabled) { btn.click(); return true; }
}
return false;
}
window.addEventListener('keydown', (e) => {
const el = document.activeElement;
if (!isEditable(el)) return;
// IME 変換確定中は介入しない
if (e.isComposing) return;
if (e.key !== 'Enter') return;
// Ctrl+Enter → 送信
if (e.ctrlKey) {
e.preventDefault();
e.stopImmediatePropagation();
clickSendButton();
return;
}
// Enter 単体 → 改行(誤送信防止)
if (!e.shiftKey && !e.altKey && !e.metaKey) {
e.preventDefault();
e.stopImmediatePropagation();
insertNewline(el);
return;
}
// Shift+Enter 等は既定挙動(多くは改行)
}, true);
})();