您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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.3
- // @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&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);
- })();