您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
一个帮助用户在ChatGPT原生网页添加可移动且大小可调的便签,用于快速选择和添加prompt的脚本。
// ==UserScript== // @name 😍ChatGPT - Prompt便签 // @version 6.0 // @description 一个帮助用户在ChatGPT原生网页添加可移动且大小可调的便签,用于快速选择和添加prompt的脚本。 // @author futureo0 // @require https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.4.min.js // @match https://chat.openai.com/* // @match https://chat.openai.com/c/* // @match https://chat.openai.com/?* // @match https://neu.learnwithgpt.beauty/ // @match https://neu.learnwithgpt.beauty/c // @match https://neu.learnwithgpt.beauty/c/* // @match *://chatgpt.com/* // @run-at document-start // @grant GM_setValue // @grant GM_getValue // @namespace https://greasyforks.org/users/1242018 // ==/UserScript== (function () { 'use strict'; $(document).ready(function() { const stickyNoteHtml = ` <div id="stickyNoteContainer" style="position: fixed; top: 20px; right: 20px; width: 300px; min-height: 300px; background-color: lightyellow; border: 1px solid black; padding-bottom: 10px; box-shadow: 3px 3px 5px rgba(0,0,0,0.2); z-index: 1000; resize: both; overflow: auto; color: black;"> <div id="stickyNoteHeader" style="cursor: move; background-color: #ddd; height: 10px; width: 100%;"></div> <input type="text" id="newPromptInput" placeholder="输入新prompt" style="width: calc(100% - 20px); margin: 10px;"> <button id="savePromptButton" style="width: calc(100% - 20px); margin: 0 10px;">保存</button> <div id="promptsList" style="margin: 5px 10px;"></div> </div> `; $('body').append(stickyNoteHtml); dragElement(document.getElementById("stickyNoteContainer"), document.getElementById("stickyNoteHeader")); loadPrompts(); $('#savePromptButton').click(function() { const newPrompt = $('#newPromptInput').val().trim(); if(newPrompt) { addPromptToStickyNote(newPrompt); savePrompt(newPrompt); $('#newPromptInput').val(''); } }); // MutationObserver to monitor DOM changes and re-insert the sticky note if it’s removed const observer = new MutationObserver(() => { if (!document.getElementById("stickyNoteContainer")) { $('body').append(stickyNoteHtml); // Re-append the sticky note dragElement(document.getElementById("stickyNoteContainer"), document.getElementById("stickyNoteHeader")); loadPrompts(); } }); // Start observing the body for changes observer.observe(document.body, { childList: true, subtree: true }); }); function loadPrompts() { const prompts = JSON.parse(GM_getValue('prompts', '[]')); prompts.forEach(prompt => { addPromptToStickyNote(prompt); }); } function addPromptToStickyNote(promptText) { const promptHtml = ` <div style="display: flex; align-items: center; margin-bottom: 10px;"> <button class="deletePromptButton" style="cursor: pointer; background: none; border: none; color: grey; margin-right: 5px;">×</button> <div style="cursor: pointer; flex-grow: 1;">${promptText}</div> </div> `; $('#promptsList').prepend(promptHtml); $('#promptsList .deletePromptButton:first').click(function() { const promptText = $(this).siblings('div').text(); // 获取被点击的Prompt的文本内容 removePromptByText(promptText); // 用文本内容删除Prompt $(this).parent().remove(); // 从UI中删除 }); // Use mousedown event to prevent focus loss $('#promptsList div:first').on('mousedown', function(e) { e.preventDefault(); // Prevent the div from stealing focus const inputField = getActiveInputField(); if (inputField) { insertAtCursor(inputField, promptText); } else { console.error('No input field found to insert text into.'); } }); } function removePromptByText(promptText) { let prompts = JSON.parse(GM_getValue('prompts', '[]')); const promptIndex = prompts.indexOf(promptText); // 根据文本内容找到Prompt的索引 if (promptIndex !== -1) { prompts.splice(promptIndex, 1); // 删除匹配的Prompt GM_setValue('prompts', JSON.stringify(prompts)); // 更新存储 } } function savePrompt(promptText) { let prompts = JSON.parse(GM_getValue('prompts', '[]')); prompts.push(promptText); GM_setValue('prompts', JSON.stringify(prompts)); } function dragElement(elmnt, header) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; header.onmousedown = dragMouseDown; function dragMouseDown(e) { if (e.target === header) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } function getActiveInputField() { // Get the currently focused element let activeElement = document.activeElement; // If it's a textarea or contenteditable div, return it if ((activeElement.tagName.toLowerCase() === 'textarea') || activeElement.isContentEditable) { return activeElement; } // No suitable input field found return null; } function insertAtCursor(myField, myValue) { // Ensure the field is focused myField.focus(); if (myField.isContentEditable) { // For contenteditable elements var sel = window.getSelection(); if (sel.rangeCount > 0) { var range = sel.getRangeAt(0); // Ensure the selection is within myField if (myField.contains(range.commonAncestorContainer)) { range.deleteContents(); var textNode = document.createTextNode(myValue); range.insertNode(textNode); // Move the cursor after the inserted text node range.setStartAfter(textNode); range.setEndAfter(textNode); sel.removeAllRanges(); sel.addRange(range); } else { // If selection is not in myField, place cursor at the end var newRange = document.createRange(); newRange.selectNodeContents(myField); newRange.collapse(false); // collapse to end var textNode = document.createTextNode(myValue); newRange.insertNode(textNode); // Move the cursor after the inserted text node newRange.setStartAfter(textNode); newRange.setEndAfter(textNode); sel.removeAllRanges(); sel.addRange(newRange); } } else { // No selection, insert at end var newRange = document.createRange(); newRange.selectNodeContents(myField); newRange.collapse(false); // collapse to end var textNode = document.createTextNode(myValue); newRange.insertNode(textNode); // Move the cursor after the inserted text node newRange.setStartAfter(textNode); newRange.setEndAfter(textNode); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(newRange); } } else if (myField.tagName.toLowerCase() === 'textarea' || myField.tagName.toLowerCase() === 'input') { // For input or textarea elements if (myField.selectionStart || myField.selectionStart == '0') { var startPos = myField.selectionStart; var endPos = myField.selectionEnd; myField.value = myField.value.substring(0, startPos) + myValue + myField.value.substring(endPos, myField.value.length); myField.selectionStart = myField.selectionEnd = startPos + myValue.length; } else { myField.value += myValue; } } else { console.error('Unsupported input field.'); } // Trigger input event to ensure changes are recognized var event = new Event('input', { bubbles: true }); myField.dispatchEvent(event); } })();