EnterIt

Support Enter for new line and Ctrl+Enter to send in various AI assistant web input boxes

Versione datata 05/02/2026. Vedi la nuova versione l'ultima versione.

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               EnterIt
// @name:zh-CN         EnterIt
// @name:zh-TW         EnterIt
// @namespace          http://tampermonkey.net/
// @version            1.2.0
// @description        Support Enter for new line and Ctrl+Enter to send in various AI assistant web input boxes
// @description:zh-CN  支持在各种 AI 助手网页端输入框按回车换行,Ctrl+回车发送
// @description:zh-TW  支援在各種 AI 助手網頁端輸入框,以 Enter 譜寫換行的詩篇,以 Ctrl+Enter 傳送命運的覺悟。
// @author             Clover Yan
// @homepage           https://www.khyan.top/
// @match              https://chatgpt.com/*
// @match              https://claude.ai/*
// @match              https://gemini.google.com/*
// @match              https://www.perplexity.ai/*
// @match              https://chat.deepseek.com/*
// @match              https://grok.com/*
// @match              https://github.com/*
// @match              https://notebooklm.google.com/*
// @match              https://www.phind.com/*
// @match              https://poe.com/*
// @match              https://chat.mistral.ai/*
// @match              https://you.com/*
// @match              https://v0.dev/*
// @match              https://copilot.microsoft.com/*
// @match              https://m365.cloud.microsoft/*
// @match              https://yuanbao.tencent.com/*
// @match              https://www.doubao.com/*
// @match              https://www.qianwen.com/*
// @match              https://chat.qwen.ai/*
// @match              https://yiyan.baidu.com/*
// @icon               https://www.khyan.top/favicon.png
// @grant              none
// @run-at             document-start
// @license            AGPLv3
// ==/UserScript==

(function () {
	"use strict";

	function handleChatGPT(event) {
		const isOnlyEnter =
			event.code === "Enter" && !(event.ctrlKey || event.metaKey);
		const isCtrlEnter = event.code === "Enter" && event.ctrlKey;
		const isPromptTextarea = event.target.id === "prompt-textarea";

		if (!event.isTrusted) return false;

		if (isPromptTextarea && isOnlyEnter) {
			event.preventDefault();
			const newEvent = new KeyboardEvent("keydown", {
				key: "Enter",
				code: "Enter",
				bubbles: true,
				cancelable: true,
				ctrlKey: false,
				metaKey: false,
				shiftKey: true,
			});
			event.target.dispatchEvent(newEvent);
			return true;
		} else if (isPromptTextarea && isCtrlEnter) {
			event.preventDefault();
			const newEvent = new KeyboardEvent("keydown", {
				key: "Enter",
				code: "Enter",
				bubbles: true,
				cancelable: true,
				ctrlKey: false,
				metaKey: true,
				shiftKey: false,
			});
			event.target.dispatchEvent(newEvent);
			return true;
		} else if (event.target.tagName === "TEXTAREA" && isCtrlEnter) {
			event.preventDefault();
			const newEvent = new KeyboardEvent("keydown", {
				key: "Enter",
				code: "Enter",
				bubbles: true,
				cancelable: true,
				ctrlKey: false,
				metaKey: true,
				shiftKey: false,
			});
			event.target.dispatchEvent(newEvent);
			return true;
		}

		return false;
	}

	function shouldHandleCtrlEnter(url, event) {
		if (url.startsWith("https://claude.ai")) {
			return (
				(event.target.tagName === "DIV" &&
					event.target.contentEditable === "true") ||
				event.target.tagName === "TEXTAREA"
			);
		} else if (url.startsWith("https://notebooklm.google.com")) {
			return (
				event.target.tagName === "TEXTAREA" &&
				event.target.classList.contains("query-box-input")
			);
		} else if (url.startsWith("https://gemini.google.com")) {
			return (
				((event.target.tagName === "DIV" &&
					event.target.classList.contains("ql-editor") &&
					event.target.contentEditable === "true") ||
					event.target.tagName === "TEXTAREA") &&
				!(event.shiftKey && event.code === "Enter")
			);
		} else if (url.startsWith("https://www.phind.com")) {
			return (
				event.target.tagName === "DIV" &&
				event.target.classList.contains("public-DraftEditor-content") &&
				event.target.contentEditable === "true"
			);
		} else if (url.startsWith("https://chat.deepseek.com")) {
			return event.target.tagName === "TEXTAREA";
		} else if (url.startsWith("https://grok.com")) {
			return (
				event.target.tagName === "TEXTAREA" ||
				(event.target.tagName === "DIV" &&
					event.target.contentEditable === "true")
			);
		} else if (url.startsWith("https://github.com")) {
			return (
				event.target.getAttribute("placeholder") === "Ask Copilot" ||
				event.target.getAttribute("placeholder") === "Ask anything" ||
				event.target.getAttribute("id") === "copilot-chat-textarea"
			);
		} else if (url.startsWith("https://m365.cloud.microsoft/chat")) {
			return event.target.id === "m365-chat-editor-target-element";
		} else if (url.startsWith("https://www.perplexity.ai")) {
			return (
				event.target.tagName === "DIV" &&
				event.target.contentEditable === "true" &&
				event.target.id === "ask-input"
			);
		} else if (url.startsWith("https://yuanbao.tencent.com")) {
			return (
				event.target.tagName === "DIV" &&
				event.target.classList.contains("ql-editor") &&
				event.target.contentEditable === "true"
			);
		} else if (url.startsWith("https://yiyan.baidu.com")) {
			return (
				event.target.tagName === "DIV" &&
				event.target.getAttribute("role") === "textbox" &&
				event.target.contentEditable === "true"
			);
		}

		return false;
	}

	function findNBLMSendButton() {
		const submitButton = document.querySelector(
			'query-box form button[type="submit"]',
		);
		if (submitButton) return submitButton;
		return null;
	}

	function handleCustomInputs(event) {
		const url = window.location.href;

		if (event.isComposing) {
			return false;
		}

		if (!shouldHandleCtrlEnter(url, event) || !event.isTrusted) {
			return false;
		}

		const isOnlyEnter =
			event.code === "Enter" && !(event.ctrlKey || event.metaKey);
		const isCtrlEnter =
			event.code === "Enter" && (event.ctrlKey || event.metaKey);

		if (isOnlyEnter || isCtrlEnter) {
			const preventDefaultSites = [
				"https://claude.ai",
				"https://www.phind.com",
				"https://www.perplexity.ai",
				"https://yuanbao.tencent.com",
			];
			if (preventDefaultSites.some((site) => url.startsWith(site))) {
				event.preventDefault();
			}
			event.stopImmediatePropagation();

			let eventConfig = {
				key: "Enter",
				code: "Enter",
				bubbles: true,
				cancelable: true,
				shiftKey: isOnlyEnter,
			};

			if (url.startsWith("https://www.phind.com")) {
				eventConfig.keyCode = 13;
			}

			if (url.startsWith("https://m365.cloud.microsoft/chat") && isCtrlEnter) {
				eventConfig.keyCode = 13;
			}

			if (url.startsWith("https://chat.deepseek.com")) {
				eventConfig.keyCode = 13;
				eventConfig.composed = true;
			}

			if (url.startsWith("https://yiyan.baidu.com")) {
				eventConfig.keyCode = 13;
			}

			const newEvent = new KeyboardEvent("keydown", eventConfig);
			event.target.dispatchEvent(newEvent);

			if (isCtrlEnter && url.startsWith("https://notebooklm.google.com")) {
				const sendButton = findNBLMSendButton();
				if (sendButton) {
					sendButton.click();
				}
			}

			if (
				url.startsWith("https://claude.ai") &&
				event.target.tagName === "TEXTAREA"
			) {
				if (isOnlyEnter) {
					const textarea = event.target;
					const start = textarea.selectionStart;
					const end = textarea.selectionEnd;
					const value = textarea.value;
					textarea.value =
						value.substring(0, start) + "\n" + value.substring(end);
					textarea.selectionStart = textarea.selectionEnd = start + 1;
					textarea.dispatchEvent(new Event("input", { bubbles: true }));
				} else if (isCtrlEnter) {
					const saveButton = document.querySelector('button[type="submit"]');
					if (saveButton) {
						saveButton.click();
					}
				}
			}

			return true;
		}

		return false;
	}

	function handleTextarea(event) {
		if (event.target.tagName !== "TEXTAREA" || !event.isTrusted) {
			return false;
		}

		const isOnlyEnter =
			event.code === "Enter" && !(event.ctrlKey || event.metaKey);

		if (isOnlyEnter) {
			event.stopPropagation();
			return true;
		} else {
			const isCtrlEnter =
				event.code === "Enter" && (event.ctrlKey || event.metaKey);
			if (isCtrlEnter) {
				const preventCtrlEnterSites = ["https://www.qianwen.com"];
				if (
					preventCtrlEnterSites.some((site) =>
						window.location.href.startsWith(site),
					)
				) {
					event.preventDefault();
					event.stopImmediatePropagation();
					const newEvent = new KeyboardEvent("keydown", {
						key: "Enter",
						code: "Enter",
						bubbles: true,
						cancelable: true,
						shiftKey: false,
					});
					event.target.dispatchEvent(newEvent);
					return true;
				}
			}
		}

		return false;
	}

	function handleKeyDown(event) {
		const url = window.location.href;

		if (url.startsWith("https://chatgpt.com")) {
			if (handleChatGPT(event)) return;
		} else if (
			url.startsWith("https://claude.ai") ||
			url.startsWith("https://notebooklm.google.com") ||
			url.startsWith("https://gemini.google.com") ||
			url.startsWith("https://www.phind.com") ||
			url.startsWith("https://chat.deepseek.com") ||
			url.startsWith("https://github.com") ||
			url.startsWith("https://grok.com") ||
			url.startsWith("https://m365.cloud.microsoft") ||
			url.startsWith("https://www.perplexity.ai") ||
			url.startsWith("https://yuanbao.tencent.com") ||
			url.startsWith("https://yiyan.baidu.com")
		) {
			if (handleCustomInputs(event)) return;
		} else if (
			url.startsWith("https://poe.com") ||
			url.startsWith("https://chat.mistral.ai") ||
			url.startsWith("https://you.com") ||
			url.startsWith("https://v0.dev") ||
			url.startsWith("https://copilot.microsoft.com") ||
			url.startsWith("https://www.doubao.com") ||
			url.startsWith("https://www.qianwen.com") ||
			url.startsWith("https://chat.qwen.ai")
		) {
			if (handleTextarea(event)) return;
		}
	}

	function fixYuanbaoPlaceholder() {
		const editor = document.querySelector('.ql-editor[contenteditable="true"]');
		if (editor) {
			const placeholder = editor.getAttribute("data-placeholder");
			if (placeholder && placeholder.includes("shift+enter")) {
				editor.setAttribute(
					"data-placeholder",
					placeholder.replace(/shift\+enter/gi, "Ctrl+Enter"),
				);
			}
		}
	}

	function observeYuanbaoPlaceholder() {
		if (!window.location.href.startsWith("https://yuanbao.tencent.com")) {
			return;
		}

		fixYuanbaoPlaceholder();

		const observer = new MutationObserver(() => {
			fixYuanbaoPlaceholder();
		});

		observer.observe(document.body, {
			childList: true,
			subtree: true,
			attributes: true,
			attributeFilter: ["data-placeholder"],
		});
	}

	document.addEventListener("keydown", handleKeyDown, { capture: true });

	if (document.readyState === "loading") {
		document.addEventListener("DOMContentLoaded", observeYuanbaoPlaceholder);
	} else {
		observeYuanbaoPlaceholder();
	}

	console.log("✅ EnterIt 已加载");
})();