// ==UserScript==
// @name Pre ChatGPT
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Handle multiple questions for ChatGPT
// @author zizhan
// @match https://chat.openai.com/*
// @grant GM_addStyle
// @license MIT
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
// 添加 CSS 样式
GM_addStyle(`
#sidebar {
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 300px;
padding: 20px;
background-color: #fafafa;
border: 1px solid #ccc;
border-radius: 10px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease-in-out;
overflow: hidden;
}
#toggleSidebar {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 30px;
height: 30px;
background: #fafafa;
border: 1px solid #ccc;
border-radius: 50%;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: left 0.3s, border 0.3s, background 0.3s;
display: flex;
align-items: center;
justify-content: center;
}
#sidebar.collapsed #sidebarContent {
display: none;
}
#sidebar.collapsed #toggleSidebar {
left: 15px;
}
#sidebarWrapper {
position: relative; /* New wrapper */
}
#toggleSidebar:hover {
border-color: #007BFF; /* Change border color on hover */
background: #e6e6e6; /* Change background color on hover */
}
#toggleSidebar svg {
height: 15px;
width: 15px;
transition: all 0.3s ease-in-out;
}
#sidebar.collapsed {
width: 60px;
}
#sidebar.collapsed #toggleSidebar {
left: 15px; /* Position it to the right when sidebar is collapsed */
}
#sidebar h2 {
text-align: center;
color: #333;
font-size: 1.4em;
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
margin-bottom: 10px;
}
#sidebar textarea {
width: 100%;
height: 100px;
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
transition: border-color 0.3s;
resize: none;
}
#sidebar textarea:focus {
border-color: #007BFF;
outline: none;
}
#sidebar button {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
transition: background-color 0.3s;
}
#submitQuestion {
background-color: #4CAF50;
}
#submitQuestion:hover {
background-color: #45a049;
}
#start {
background-color: #008CBA;
margin-bottom: 20px;
}
#start:hover {
background-color: #007B99;
}
#questionList {
max-height: 200px;
overflow-y: auto;
}
.question {
margin-bottom: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.3s;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.question:hover {
background-color: #f0f0f0;
}
.question:before {
content: '❓';
margin-right: 4px;
}
.question.answered {
color: #aaa;
}
.question.answered:before {
content: '✅';
}
.question button {
margin-left: 0px;
background: none;
border: 1px solid #000; /* 新增此行 */
box-sizing: border-box; /* 新增此行 */
cursor: pointer;
transition: color 0.3s;
display: flex;
justify-content: center;
align-items: center;
}
.question button:hover {
color: #007BFF;
}
.question .button-container {
margin-left: auto;
display: flex;
gap: 0px;
}
.button-container button {
width: 24px; /* Increase the size of the button */
height: 24px; /* Increase the size of the button */
padding: 0;
border: 2px solid red; /* Use a more visible border */
}
.question button svg {
width: 18px;
height: 18px;
margin: auto;
pointer-events: none; /* Let click events pass through the SVG to the button */
}
.button-group {
display: flex;
justify-content: space-between;
}
.question-text {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: none;
outline: none;
}
`);
// 修改 HTML
const sidebar = document.createElement('div');
sidebar.id = 'sidebar';
sidebar.innerHTML = `
`;
document.body.appendChild(sidebar);
// Toggle logic
document.getElementById('toggleSidebar').addEventListener('click', function () {
var sidebar = document.getElementById('sidebar');
var iconExpand = document.getElementById('icon-expand');
var iconCollapse = document.getElementById('icon-collapse');
sidebar.classList.toggle('collapsed');
if (sidebar.classList.contains('collapsed')) {
iconCollapse.style.display = 'none';
iconExpand.style.display = '';
} else {
iconExpand.style.display = 'none';
iconCollapse.style.display = '';
}
});
document.getElementById('submitQuestion').addEventListener('click', function () {
const input = document.getElementById('questionInput');
const questionList = document.getElementById('questionList');
const questions = input.value.split('\n').filter(question => question.trim() !== '');
for (let question of questions) {
const div = document.createElement('div');
div.className = 'question';
const questionText = document.createElement('input');
questionText.type = 'text';
questionText.className = 'question-text';
questionText.value = question;
questionText.readOnly = true;
div.appendChild(questionText);
questionText.style.border = 'none'; // Hide the textarea box border
questionText.rows = 1; // Initially show as single line
const buttonContainer = document.createElement('div');
buttonContainer.className = 'button-container';
const editButton = document.createElement('button');
editButton.innerHTML = `
`;
editButton.addEventListener('click', function () {
if (questionText.readOnly) {
questionText.readOnly = false;
questionText.style.border = '1px solid'; // Show border when editing
questionText.rows = 'auto'; // Expand the textarea box to show all text
questionText.focus();
// Move the cursor to the end of the text
questionText.selectionStart = questionText.selectionEnd = questionText.value.length;
} else {
questionText.readOnly = true;
questionText.style.border = 'none'; // Hide the textarea box border again after editing
questionText.rows = 1; // Collapse the textarea box back to single line
// Update the question text in localStorage
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
let questionToUpdate = storedQuestions.find(q => q.text === question.text);
if (questionToUpdate) {
questionToUpdate.text = questionText.value;
localStorage.setItem('questions', JSON.stringify(storedQuestions));
}
}
});
buttonContainer.appendChild(editButton);
const deleteButton = document.createElement('button');
deleteButton.innerHTML = `
`; // SVG for delete button
deleteButton.addEventListener('click', function () {
// Remove the question from the DOM
div.remove();
// Remove the question from localStorage
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
storedQuestions = storedQuestions.filter(q => q.text !== questionText.value);
localStorage.setItem('questions', JSON.stringify(storedQuestions));
});
buttonContainer.appendChild(deleteButton);
const sortButton = document.createElement('button');
sortButton.className = 'sort-handle'; // add this line
sortButton.innerHTML = `
`; // SVG for sort button
buttonContainer.appendChild(sortButton);
div.appendChild(buttonContainer);
questionList.appendChild(div);
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
storedQuestions.push({ text: question, answered: false });
localStorage.setItem('questions', JSON.stringify(storedQuestions));
}
input.value = '';
new Sortable(questionList, {
handle: '.sort-handle',
animation: 150
});
});
// Add an event listener to the question list to handle click events on the question text
questionList.addEventListener('click', function (event) {
if (event.target.classList.contains('question-text')) {
const questionText = event.target;
if (questionText.style.whiteSpace === 'nowrap') {
questionText.style.whiteSpace = 'normal';
} else {
questionText.style.whiteSpace = 'nowrap';
}
}
});
// When the page loads, load the questions from localStorage
window.addEventListener('load', function () {
const questionList = document.getElementById('questionList');
// Get the existing questions from localStorage
let storedQuestions = localStorage.getItem('questions');
// Parse the string back into an array
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
for (let question of storedQuestions) {
const div = document.createElement('div');
div.className = 'question';
const questionText = document.createElement('input');
questionText.type = 'text';
questionText.className = 'question-text';
questionText.value = question.text; // Use question.text instead of question
questionText.readOnly = true;
div.appendChild(questionText);
questionText.style.border = 'none'; // Hide the textarea box border
questionText.rows = 1; // Initially show as single line
const buttonContainer = document.createElement('div');
buttonContainer.className = 'button-container';
const editButton = document.createElement('button');
editButton.innerHTML = `
`;
editButton.addEventListener('click', function () {
if (questionText.readOnly) {
questionText.readOnly = false;
questionText.style.border = '1px solid'; // Show border when editing
questionText.rows = 'auto'; // Expand the textarea box to show all text
questionText.focus();
// Move the cursor to the end of the text
questionText.selectionStart = questionText.selectionEnd = questionText.value.length;
} else {
questionText.readOnly = true;
questionText.style.border = 'none'; // Hide the textarea box border again after editing
questionText.rows = 1; // Collapse the textarea box back to single line
// Update the question text in localStorage
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
let questionToUpdate = storedQuestions.find(q => q.text === question.text);
if (questionToUpdate) {
questionToUpdate.text = questionText.value;
localStorage.setItem('questions', JSON.stringify(storedQuestions));
}
}
});
buttonContainer.appendChild(editButton);
const deleteButton = document.createElement('button');
deleteButton.innerHTML = `
`; // SVG for delete button
deleteButton.addEventListener('click', function () {
// Remove the question from the DOM
div.remove();
// Remove the question from localStorage
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
storedQuestions = storedQuestions.filter(q => q.text !== questionText.value);
localStorage.setItem('questions', JSON.stringify(storedQuestions));
});
buttonContainer.appendChild(deleteButton);
const sortButton = document.createElement('button');
sortButton.className = 'sort-handle'; // add this line
sortButton.innerHTML = `
`; // SVG for sort button
buttonContainer.appendChild(sortButton);
div.appendChild(buttonContainer);
questionList.appendChild(div);
// Use the answered property from localStorage
if (question.answered) {
div.classList.add('answered');
}
}
});
document.getElementById('start').addEventListener('click', async function () {
const questions = document.getElementsByClassName('question');
for (let i = 0; i < questions.length; i++) {
let questionDiv = questions[i];
let questionInput = questionDiv.querySelector('input.question-text'); // Get the input element
if (!questionDiv.classList.contains('answered')) {
await askQuestion(questionInput.value);
questionDiv.classList.add('answered');
// Update the answered status in localStorage
let storedQuestions = localStorage.getItem('questions');
storedQuestions = storedQuestions ? JSON.parse(storedQuestions) : [];
let questionToUpdate = storedQuestions.find(q => q.text === questionInput.value);
if (questionToUpdate) {
questionToUpdate.answered = true;
localStorage.setItem('questions', JSON.stringify(storedQuestions));
}
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second before starting the next question
}
}
});
function findParent(el, selector, level = 5) {
let parent = el.parentNode;
let count = 1;
while (parent && count <= level) {
if (parent && parent.constructor !== HTMLDocument && parent.matches(selector)) {
return parent;
}
parent = parent.parentNode;
count++;
}
return null;
}
async function askQuestion(question) {
return new Promise((resolve, reject) => {
// Find the input box and the send button
const inputBox = document.querySelector('textarea');
const sendButton = inputBox.nextElementSibling;
// Input the question
inputBox.value = question;
const event = new Event('input', { bubbles: true });
inputBox.dispatchEvent(event);
// Click the send button after a short delay
setTimeout(() => sendButton.click(), 500);
// Create a MutationObserver to wait for the answer
const observer = new MutationObserver((mutations, observer) => {
for (let mutation of mutations) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
for (let node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE && node.textContent.includes('Regenerate response')) {
// Check if the "Continue generating" button is present
const continueSvg = document.querySelector('form.stretch .justify-center polygon[points="11 19 2 12 11 5 11 19"]');
if (continueSvg) {
const continueButton = findParent(continueSvg, 'button');
if (continueButton) {
continueButton.click();
}
} else {
// The answer has finished generating, stop observing
observer.disconnect();
resolve();
}
return;
}
}
}
}
});
const observerConfig = { childList: true, subtree: true };
observer.observe(document.body, observerConfig);
});
}
})();