Send with Ctrl+Enter in ChatGPT, Claude, Gemini, Copilot, Perplexity, and others.
当前为
// ==UserScript==
// @name Chat UI Ctrl+Enter Sender
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Send with Ctrl+Enter in ChatGPT, Claude, Gemini, Copilot, Perplexity, and others.
// @author Chippppp
// @license MIT
// @match *://*/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// ==/UserScript==
(() => {
"use strict";
const defaultDomains = [
"https://chatgpt.com",
"https://claude.ai",
"https://gemini.google.com",
"https://www.bing.com",
"https://www.perplexity.ai"
];
let domains = GM_getValue("domains", defaultDomains.slice());
GM_registerMenuCommand("Manage domains", () => {
showDomainSettingsUI(domains);
});
function showDomainSettingsUI(domains) {
const dialog = document.createElement("dialog");
dialog.style.width = "400px";
dialog.style.height = "500px";
dialog.style.overflowY = "auto";
dialog.innerHTML = `
<form method="dialog" style="display: flex; flex-direction: column; height: 100%; padding: 10px;">
<h2 style="margin: 0 0 10px 0;">Domain Settings</h2>
<div id="domain-list" style="margin-bottom: 10px; flex-grow: 1; overflow-y: auto; border: 1px solid #ccc; padding: 10px;">
${domains.map(domain => createDomainItemHTML(domain)).join("")}
</div>
<div style="margin-bottom: 10px; display: flex;">
<input id="new-domain-input" type="text" value="${location.origin}" style="flex-grow: 1; margin-right: 5px; padding: 5px; color: black; background-color: white; border: 1px solid #ccc;">
<button id="add-domain" type="button" style="padding: 5px;">Add domain</button>
</div>
<div style="margin-bottom: 10px;">
<button id="reset-domains" type="button" style="padding: 5px;">Reset to default</button>
</div>
<div style="text-align: right;">
<button id="close-dialog" type="submit" value="close" style="padding: 5px;">Close</button>
</div>
</form>
`;
document.body.appendChild(dialog);
dialog.showModal();
dialog.querySelector("#add-domain").addEventListener("click", () => {
const newDomainInput = dialog.querySelector("#new-domain-input").value;
if (!newDomainInput) {
alert("Invalid input")
} else if (!domains.includes(newDomainInput)) {
domains.push(newDomainInput);
GM_setValue("domains", domains);
alert(`Domain added: ${newDomainInput}`);
updateDomainList(dialog, domains);
dialog.querySelector("#new-domain-input").value = location.origin;
} else {
alert(`Domain already exists: ${newDomainInput}`);
}
});
dialog.querySelector("#reset-domains").addEventListener("click", () => {
if (confirm("Are you sure you want to reset domains to default?")) {
domains = defaultDomains.slice();
GM_setValue("domains", domains);
alert("Domains have been reset to default.");
updateDomainList(dialog, domains);
dialog.querySelector("#new-domain-input").value = location.origin;
}
});
dialog.addEventListener("close", () => {
dialog.remove();
});
updateDomainList(dialog, domains);
}
function createDomainItemHTML(domain) {
return `
<div class="domain-item" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
<span>${domain}</span>
<div>
<button class="edit-domain" data-domain="${domain}" style="margin-right: 5px;">Edit</button>
<button class="remove-domain" data-domain="${domain}">Remove</button>
</div>
</div>
`;
}
function updateDomainList(dialog, domains) {
const domainList = dialog.querySelector("#domain-list");
domainList.innerHTML = domains.map(domain => createDomainItemHTML(domain)).join("");
domainList.querySelectorAll(".remove-domain").forEach(button => {
button.addEventListener("click", (event) => {
const domainToRemove = event.target.getAttribute("data-domain");
if (confirm(`Are you sure you want to remove ${domainToRemove}?`)) {
const index = domains.indexOf(domainToRemove);
if (index !== -1) {
domains.splice(index, 1);
GM_setValue("domains", domains);
alert(`Domain removed: ${domainToRemove}`);
} else {
alert(`Domain not found: ${domainToRemove}`);
}
}
updateDomainList(dialog, domains);
});
});
domainList.querySelectorAll(".edit-domain").forEach(button => {
button.addEventListener("click", (event) => {
const domainToEdit = event.target.getAttribute("data-domain");
const newDomainName = prompt(`Edit domain: ${domainToEdit}`, domainToEdit);
if (newDomainName === null) {
updateDomainList(dialog, domains);
return;
}
if (!newDomainName) {
alert("Invalid input");
} else if (!domains.includes(newDomainName)) {
const index = domains.indexOf(domainToEdit);
if (index !== -1) {
domains[index] = newDomainName;
GM_setValue("domains", domains);
alert(`Domain edited: ${domainToEdit} to ${newDomainName}`);
} else {
alert(`Domain not found: ${domainToEdit}`);
}
} else {
alert(`Domain already exists: ${newDomainName}`);
}
updateDomainList(dialog, domains);
});
});
}
if (!domains.some(domain => location.origin === domain)) return;
console.log("Chat UI Ctrl+Enter Sender Enabled");
window.addEventListener("keydown", e => {
if (!e.key === "Enter" || e.ctrlKey) return;
let target = e.composedPath ? e.composedPath()[0] || e.target : e.target;
if (/INPUT|TEXTAREA|SELECT|LABEL/.test(target.tagName) || target.getAttribute && target.getAttribute("contenteditable") === "true") {
event.stopPropagation();
}
}, true);
})();