// ==UserScript== // @name ChatGPT Prompt Queue // @description Queue multiple prompts for ChatGPT // @author nihaltp // @namespace https://github.com/nihaltp/uscripts // @supportURL https://github.com/nihaltp/uscripts/issues // @homepageURL https://github.com/nihaltp/uscripts // @homepage https://github.com/nihaltp/uscripts // @license MIT // @match https://chatgpt.com/* // @match https://chat.openai.com/* // @icon https://chatgpt.com/favicon.ico // @version 1.0.0 // @grant none // @downloadURL https://raw.githubusercontent.com/nihaltp/uscripts/main/AI Queue/ai_queue.user.js // @updateURL https://raw.githubusercontent.com/nihaltp/uscripts/main/AI Queue/ai_queue.user.js // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const queue = []; let running = false; let editingId = null; let draggedId = null; window.aiQueueDebug = false; // set to true to enable debug logs // ----------------------------- // MARK: UI // ----------------------------- let panel; let isPanelVisible = false; // MARK: logging helpers function log(...args) { if (!window.aiQueueDebug) return; console.log("[AI QUEUE]", ...args); } function error(...args) { console.error("[AI QUEUE]", ...args); } function throwError(...args) { error(...args); throw new Error(args.join(' ')); } // MARK: create panel function createPanel() { panel = document.createElement('div'); panel.id = 'pq-panel'; Object.assign(panel.style, { position: 'fixed', bottom: '120px', right: '20px', width: '320px', maxHeight: '70vh', overflowY: 'auto', background: '#202123', color: 'white', border: '1px solid #444', borderRadius: '16px', padding: '12px', zIndex: '2147483647', boxShadow: '0 10px 40px rgba(0,0,0,0.6)', display: 'none', outline: '2px solid #555', }); panel.innerHTML = `
Prompt Queue
Idle
    `; document.documentElement.appendChild(panel); setupPanelEvents(); } // MARK: setupPanelEvents function setupPanelEvents() { const input = panel.querySelector('#pq-input'); const addBtn = panel.querySelector('#pq-add'); const startBtn = panel.querySelector('#pq-start'); window.pqInput = input; window.pqAddBtn = addBtn; addBtn.addEventListener('click', () => { const text = input.value.trim(); if (!text) { return; } // editing existing item if (editingId !== null) { const item = queue.find(item => item.id === editingId); if (!item) { return; } item.prompt = text; editingId = null; addBtn.textContent = 'Add To Queue'; } else { // add new item queue.push({ id: crypto.randomUUID(), prompt: text, }); } updateToolbarButton(); input.value = ''; renderQueue(); }); startBtn.addEventListener('click', async () => { if (running) return; running = true; updateToolbarButton(); processQueue(); }); } // MARK: renderQueue function renderQueue() { const list = panel.querySelector('#pq-list'); list.innerHTML = ''; queue.forEach((item, index) => { const li = document.createElement('li'); li.style.marginBottom = '10px'; li.draggable = true; const row = document.createElement('div'); row.style.display = 'flex'; row.style.gap = '6px'; row.style.alignItems = 'flex-start'; if (editingId == item.id) { row.style.background = '#333'; row.style.padding = '6px'; row.style.borderRadius = '6px'; row.style.outline = '1px solid #7dd3fc'; } const text = document.createElement('div'); text.textContent = item.prompt; text.style.flex = '1'; text.style.wordBreak = 'break-word'; text.style.fontSize = '14px'; text.addEventListener('dblclick', () => { editQueueItem(item.id); }); const editBtn = document.createElement('button'); editBtn.textContent = '🖉'; editBtn.title = 'Edit'; editBtn.style.cursor = 'pointer'; editBtn.style.color = '#7dd3fc'; editBtn.addEventListener('click', () => { editQueueItem(item.id); }); const deleteBtn = document.createElement('button'); deleteBtn.textContent = '✕'; deleteBtn.title = 'Delete'; deleteBtn.style.cursor = 'pointer'; deleteBtn.style.color = '#ff6b6b'; deleteBtn.addEventListener('click', () => { const preview = item.prompt.length > 80 ? item.prompt.slice(0, 80) + '...' : item.prompt; const confirmed = confirm( `Delete this prompt?\n\n${preview}` ); if (!confirmed) { return; } deleteQueueItem(item.id); }); row.appendChild(text); row.appendChild(editBtn); row.appendChild(deleteBtn); li.appendChild(row); li.addEventListener('dragstart', () => { draggedId = item.id; li.style.opacity = '0.5'; }); li.addEventListener('dragend', () => { draggedId = null; li.style.opacity = '1'; }); li.addEventListener('dragover', e => { e.preventDefault(); li.style.borderTop = '2px solid #888'; }); li.addEventListener('dragleave', () => { li.style.borderTop = ''; }); li.addEventListener('drop', e => { e.preventDefault(); li.style.borderTop = ''; if (draggedId === item.id) { return; } moveQueueItem(draggedId, item.id); }); list.appendChild(li); }); updateToolbarButton(); } // MARK: deleteQueueItem function deleteQueueItem(id) { const index = queue.findIndex(item => item.id === id); if (index === -1) { return; } queue.splice(index, 1); renderQueue(); } // MARK: editQueueItem function editQueueItem(id) { const item = queue.find(item => item.id === id); if (!item) { return; } editingId = id; window.pqInput.value = item.prompt; window.pqAddBtn.textContent = 'Save Changes'; window.pqInput.focus(); // move cursor to end window.pqInput.selectionStart = window.pqInput.selectionEnd = window.pqInput.value.length; } // MARK: moveQueueItem function moveQueueItem(fromId, toId) { const fromIndex = queue.findIndex(item => item.id === fromId); const toIndex = queue.findIndex(item => item.id === toId); if (fromIndex === -1 || toIndex === -1) { return; } const [movedItem] = queue.splice(fromIndex, 1); queue.splice(toIndex, 0, movedItem); renderQueue(); } // MARK: setStatus function setStatus(text) { const status = panel.querySelector('#pq-status'); if (status) { status.textContent = text; } } // MARK: togglePanel function togglePanel() { isPanelVisible = !isPanelVisible; panel.style.display = isPanelVisible ? 'block' : 'none'; panel.style.visibility = 'visible'; panel.style.opacity = '1'; log('Panel element:', panel); log('Panel visible:', isPanelVisible); } // MARK: createToolbarButton function createToolbarButton() { if (document.querySelector('#pq-toolbar-button')) { return; } // specifically target dictation button const dictationButton = document.querySelector( 'button[aria-label="Start dictation"]' ); if (!dictationButton) { return; } const button = document.createElement('button'); button.id = 'pq-toolbar-button'; button.type = 'button'; button.textContent = 'Queue'; button.className = 'composer-btn h-9 min-h-9'; button.style.padding = '0 12px'; button.style.borderRadius = '9999px'; button.addEventListener('click', togglePanel); // place BEFORE dictation button dictationButton.before(button); const style = document.createElement('style'); style.textContent = ` @keyframes pq-pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.06); opacity: 0.75; } 100% { transform: scale(1); opacity: 1; } } `; document.head.appendChild(style); } function updateToolbarButton() { const button = document.querySelector('#pq-toolbar-button'); if (!button) { return; } const count = queue.length; button.textContent = count > 0 ? `Queue (${count})` : 'Queue'; // running animation if (running) { button.style.animation = 'pq-pulse 1.2s infinite'; button.style.opacity = '1'; } else { button.style.animation = ''; button.style.opacity = count > 0 ? '1' : '0.8'; } } createPanel(); // continuously reattach button because ChatGPT rerenders UI setInterval(createToolbarButton, 2000); // ----------------------------- // MARK: ChatGPT Helpers // ----------------------------- function getTextarea() { return document.querySelector('#prompt-textarea'); } function getSendButton() { return document.querySelector('button[data-testid="send-button"]'); } function isGenerating() { return !!document.querySelector('button[data-testid="stop-button"]'); } async function waitForIdle() { while (isGenerating()) { await sleep(1000); } // extra delay for stability await sleep(1500); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // MARK: sendPrompt async function sendPrompt(prompt) { const editor = document.querySelector('#prompt-textarea'); if (!editor) { throwError('Editor not found'); } editor.focus(); // clear existing content editor.innerHTML = ''; // simulate paste const dataTransfer = new DataTransfer(); dataTransfer.setData('text/plain', prompt); const pasteEvent = new ClipboardEvent('paste', { clipboardData: dataTransfer, bubbles: true, cancelable: true, }); editor.dispatchEvent(pasteEvent); await sleep(300); const sendButton = document.querySelector( 'button[data-testid="send-button"]' ); if (!sendButton) { throwError('Send button not found'); } sendButton.click(); } // MARK: processQueue async function processQueue() { setStatus('Running'); while (queue.length > 0) { await waitForIdle(); const item = queue.shift(); const prompt = item.prompt; updateToolbarButton(); renderQueue(); setStatus(`Sending: ${prompt.slice(0, 40)}...`); try { await sendPrompt(prompt); await sleep(1000); await waitForIdle(); } catch (err) { error('Error processing prompt:', err); setStatus('Error: ' + err.message); running = false; updateToolbarButton(); return; } } setStatus('Finished'); running = false; updateToolbarButton(); } })();