🏠 Home 

Chat UI Ctrl+Enter Sender

Send with Ctrl+Enter in ChatGPT, Claude, Gemini, Perplexity, Copilot, DuckDuckGo AI, and others.


Installer dette script?
// ==UserScript==
// @name         Chat UI Ctrl+Enter Sender
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Send with Ctrl+Enter in ChatGPT, Claude, Gemini, Perplexity, Copilot, DuckDuckGo AI, and others.
// @author       Chippppp
// @license      MIT
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&pattern=chatgpt.com
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// ==/UserScript==
(() => {
"use strict";
const defaultPatterns = [
"https://chatgpt.com/*",
"https://claude.ai/*",
"https://gemini.google.com/*",
"https://www.perplexity.ai/*",
"https://www.bing.com/chat*",
"https://duckduckgo.com/*ia=chat*"
];
let patterns = GM_getValue("patterns", defaultPatterns.slice());
GM_registerMenuCommand("Manage URL patterns", () => {
showPatternSettingsUI(patterns);
});
function showPatternSettingsUI(patterns) {
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;">URL Pattern Settings</h2>
<div id="pattern-list" style="margin-bottom: 10px; flex-grow: 1; overflow-y: auto; border: 1px solid #ccc; padding: 10px;"></div>
<div style="margin-bottom: 10px; display: flex;">
<input id="new-pattern-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-pattern" type="button" style="padding: 5px;">Add URL pattern</button>
</div>
<div style="margin-bottom: 10px;">
<button id="reset-patterns" 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-pattern").addEventListener("click", () => {
const newPatternInput = dialog.querySelector("#new-pattern-input").value;
if (!newPatternInput) {
alert("Invalid input")
} else if (!patterns.includes(newPatternInput)) {
patterns.push(newPatternInput);
GM_setValue("patterns", patterns);
alert(`URL pattern added: ${newPatternInput}`);
updatePatternList(dialog, patterns);
dialog.querySelector("#new-pattern-input").value = location.origin + "/*";
} else {
alert(`URL pattern already exists: ${newPatternInput}`);
}
});
dialog.querySelector("#reset-patterns").addEventListener("click", () => {
if (confirm("Are you sure you want to reset URL patterns to default?")) {
patterns = defaultPatterns.slice();
GM_setValue("patterns", patterns);
alert("URL patterns have been reset to default.");
updatePatternList(dialog, patterns);
dialog.querySelector("#new-pattern-input").value = location.origin + "/*";
}
});
dialog.addEventListener("close", () => {
dialog.remove();
});
updatePatternList(dialog, patterns);
}
function createPatternItemHTML(pattern) {
return `
<div class="pattern-item" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
<span>${pattern}</span>
<div>
<button class="edit-pattern" data-pattern="${pattern}" style="margin-right: 5px;">Edit</button>
<button class="remove-pattern" data-pattern="${pattern}">Remove</button>
</div>
</div>
`;
}
function updatePatternList(dialog, patterns) {
const patternList = dialog.querySelector("#pattern-list");
patternList.innerHTML = patterns.map(pattern => createPatternItemHTML(pattern)).join("");
patternList.querySelectorAll(".remove-pattern").forEach(button => {
button.addEventListener("click", (event) => {
const patternToRemove = event.target.getAttribute("data-pattern");
if (confirm(`Are you sure you want to remove ${patternToRemove}?`)) {
const index = patterns.indexOf(patternToRemove);
if (index !== -1) {
patterns.splice(index, 1);
GM_setValue("patterns", patterns);
alert(`URL pattern removed: ${patternToRemove}`);
} else {
alert(`URL pattern not found: ${patternToRemove}`);
}
}
updatePatternList(dialog, patterns);
});
});
patternList.querySelectorAll(".edit-pattern").forEach(button => {
button.addEventListener("click", (event) => {
const patternToEdit = event.target.getAttribute("data-pattern");
const newPatternName = prompt(`Edit URL pattern: ${patternToEdit}`, patternToEdit);
if (newPatternName === null) {
updatePatternList(dialog, patterns);
return;
}
if (!newPatternName) {
alert("Invalid input");
} else if (!patterns.includes(newPatternName)) {
const index = patterns.indexOf(patternToEdit);
if (index !== -1) {
patterns[index] = newPatternName;
GM_setValue("patterns", patterns);
alert(`URL pattern edited: ${patternToEdit} to ${newPatternName}`);
} else {
alert(`URL pattern not found: ${patternToEdit}`);
}
} else {
alert(`URL pattern already exists: ${newPatternName}`);
}
updatePatternList(dialog, patterns);
});
});
}
function matchPattern(url, pattern) {
let regexPattern = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".")
.replace(/\|/g, "|");
return new RegExp(`^${regexPattern}$`).test(url);
}
if (!patterns.some(pattern => matchPattern(location.href, pattern))) return;
console.log("Chat UI Ctrl+Enter Sender Enabled");
window.addEventListener("keydown", e => {
if (e.key !== "Enter" || e.ctrlKey || e.shiftKey) 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") {
e.stopPropagation();
}
}, true);
window.addEventListener("keypress", e => {
if (e.key !== "Enter" || e.ctrlKey || e.shiftKey) 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") {
e.stopPropagation();
}
}, true);
})();