// ==UserScript==
// @name Perplexity helper
// @namespace Tiartyos
// @match https://www.perplexity.ai/search/*
// @match https://www.perplexity.ai/search
// @grant none
// @version 1.0
// @author Tiartyos
// @description Simple script that adds buttons to Perplexity website for repeating request using Copilot.
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @homepageURL https://www.perplexity.ai/
// @license GPL-3.0-or-later
// ==/UserScript==
var jq = $.noConflict();
let debugMode = false;
const enableDebugMode = () => {
debugMode = true;
};
const debugLog = (...args) => {
if (debugMode) {
console.log('[DEBUG]', ...args);
}
}
const button = (id, icoName, title) => `<button title="${title}" type="button" id="${id}" class="bg-super text-white hover:opacity-80 font-sans focus:outline-none outline-none transition duration-300 ease-in-out font-sans select-none items-center relative group justify-center rounded-full cursor-point active:scale-95 origin-center whitespace-nowrap inline-flex text-base aspect-square h-10">
<div class="flex items-center leading-none justify-center gap-xs">
${icoName}
</div></button>`;
const robotIco = '<svg style="width: 23px; fill: white;" viewBox="0 0 640 512" xmlns="http://www.w3.org/2000/svg"><path d="m32 224h32v192h-32a31.96166 31.96166 0 0 1 -32-32v-128a31.96166 31.96166 0 0 1 32-32zm512-48v272a64.06328 64.06328 0 0 1 -64 64h-320a64.06328 64.06328 0 0 1 -64-64v-272a79.974 79.974 0 0 1 80-80h112v-64a32 32 0 0 1 64 0v64h112a79.974 79.974 0 0 1 80 80zm-280 80a40 40 0 1 0 -40 40 39.997 39.997 0 0 0 40-40zm-8 128h-64v32h64zm96 0h-64v32h64zm104-128a40 40 0 1 0 -40 40 39.997 39.997 0 0 0 40-40zm-8 128h-64v32h64zm192-128v128a31.96166 31.96166 0 0 1 -32 32h-32v-192h32a31.96166 31.96166 0 0 1 32 32z"/></svg>';
const robotRepeatIco = '<svg style="width: 23px; fill: white;" viewBox="0 0 640 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/">' +
' <path d="M442.179,325.051L442.179,459.979C442.151,488.506 418.685,511.972 390.158,512L130.053,512C101.525,511.972 78.06,488.506 78.032,459.979L78.032,238.868C78.032,203.208 107.376,173.863 143.037,173.863L234.095,173.863L234.095,121.842C234.095,107.573 245.836,95.832 260.105,95.832C274.374,95.832 286.116,107.573 286.116,121.842L286.116,173.863L309.247,173.863C321.515,245.71 373.724,304.005 442.179,325.051ZM26.011,277.905L52.021,277.905L52.021,433.968L25.979,433.968C11.727,433.968 -0,422.241 -0,407.989L-0,303.885C-0,289.633 11.727,277.905 25.979,277.905L26.011,277.905ZM468.19,331.092C478.118,332.676 488.289,333.497 498.65,333.497C505.935,333.497 513.126,333.091 520.211,332.299L520.211,407.989C520.211,422.241 508.483,433.968 494.231,433.968L468.19,433.968L468.19,331.092ZM208.084,407.958L156.063,407.958L156.063,433.968L208.084,433.968L208.084,407.958ZM286.116,407.958L234.095,407.958L234.095,433.968L286.116,433.968L286.116,407.958ZM364.147,407.958L312.126,407.958L312.126,433.968L364.147,433.968L364.147,407.958ZM214.587,303.916C214.587,286.08 199.91,271.403 182.074,271.403C164.238,271.403 149.561,286.08 149.561,303.916C149.561,321.752 164.238,336.429 182.074,336.429C182.075,336.429 182.075,336.429 182.076,336.429C199.911,336.429 214.587,321.753 214.587,303.918C214.587,303.917 214.587,303.917 214.587,303.916ZM370.65,303.916C370.65,286.08 355.973,271.403 338.137,271.403C320.301,271.403 305.624,286.08 305.624,303.916C305.624,321.752 320.301,336.429 338.137,336.429C338.138,336.429 338.139,336.429 338.139,336.429C355.974,336.429 370.65,321.753 370.65,303.918C370.65,303.917 370.65,303.917 370.65,303.916Z" style="fill-rule:nonzero;"/>\n' +
' <g transform="matrix(14.135,0,0,14.135,329.029,-28.2701)">\n' +
' <path d="M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM17.19,15.94C17.15,16.03 17.1,16.11 17.03,16.18L15.34,17.87C15.19,18.02 15,18.09 14.81,18.09C14.62,18.09 14.43,18.02 14.28,17.87C13.99,17.58 13.99,17.1 14.28,16.81L14.69,16.4L9.1,16.4C7.8,16.4 6.75,15.34 6.75,14.05L6.75,12.28C6.75,11.87 7.09,11.53 7.5,11.53C7.91,11.53 8.25,11.87 8.25,12.28L8.25,14.05C8.25,14.52 8.63,14.9 9.1,14.9L14.69,14.9L14.28,14.49C13.99,14.2 13.99,13.72 14.28,13.43C14.57,13.14 15.05,13.14 15.34,13.43L17.03,15.12C17.1,15.19 17.15,15.27 17.19,15.36C17.27,15.55 17.27,15.76 17.19,15.94ZM17.25,11.72C17.25,12.13 16.91,12.47 16.5,12.47C16.09,12.47 15.75,12.13 15.75,11.72L15.75,9.95C15.75,9.48 15.37,9.1 14.9,9.1L9.31,9.1L9.72,9.5C10.01,9.79 10.01,10.27 9.72,10.56C9.57,10.71 9.38,10.78 9.19,10.78C9,10.78 8.81,10.71 8.66,10.56L6.97,8.87C6.9,8.8 6.85,8.72 6.81,8.63C6.73,8.45 6.73,8.24 6.81,8.06C6.85,7.97 6.9,7.88 6.97,7.81L8.66,6.12C8.95,5.83 9.43,5.83 9.72,6.12C10.01,6.41 10.01,6.89 9.72,7.18L9.31,7.59L14.9,7.59C16.2,7.59 17.25,8.65 17.25,9.94L17.25,11.72Z" style="fill-rule:nonzero;"/>\n' +
' </g>' +
'</svg>';
(function () {
'use strict';
enableDebugMode();
debugLog(jq.fn.jquery);
const getModal = () => jq(".bg-black\\/80").next();
const getBtnDotFromTextArea = (textArea) => textArea.parent().find('button .rounded-full').first();
const isCopilotOn = (el) => {
return el.hasClass('text-super');
}
const toggleBtnDot = (btnDot, value) => {
debugLog('btnDot', btnDot);
const btnDotInner = btnDot.find('.rounded-full');
if (!btnDotInner.hasClass('bg-super') && value === true) {
clickAndHold(btnDot, () => {
}, 500);
clickAndHold(btnDot, () => {
}, 500);
}
}
const checkForCopilotToggleState = (timer, checkCondition, submitWhenTrue, submitButtonSelector) => {
debugLog("checkForCopilotToggleState run", timer, checkCondition(), submitWhenTrue, submitButtonSelector);
if (checkCondition()) {
clearInterval(timer);
debugLog("checkForCopilotToggleState condition met, interval cleared");
const submitBtn = submitButtonSelector
if (submitWhenTrue) {
clickAndHold(submitBtn, () => {
}, 500);
}
}
}
const clickAndHold = (selector, callback, holdTime = 500) => {
setTimeout(() => {
let holdTimer;
selector.mousedown(() => {
holdTimer = setTimeout(() => {
callback();
}, holdTime);
}).mouseup(() => {
clearTimeout(holdTimer);
});
selector.click();
}, 500)
};
const changeValueUsingEvent = (selector, value) => {
debugLog('changeValueUsingEvent', value, selector);
const nativeTextareaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
nativeTextareaValueSetter.call(selector, value);
const inputEvent = new Event('input', {bubbles: true});
selector.dispatchEvent(inputEvent);
}
const openNewThreadModal = (lastQuery) => {
debugLog('openNewThreadModal', lastQuery)
const newThreadText = jq(".sticky div").filter(function () {
return /^New Thread$/i.test(jq(this).text());
});
newThreadText.click();
setTimeout(() => {
debugLog('newThreadText.click()');
const modal = getModal();
if (modal.length > 0) {
const textArea = modal.find('textarea');
debugLog('textArea.length should be 1', textArea.length);
const newTextArea = textArea.last();
const textareaElement = newTextArea[0];
debugLog('textareaElement', textareaElement);
changeValueUsingEvent(textareaElement, lastQuery);
const btnDot = getBtnDotFromTextArea(newTextArea);
const btn = btnDot.parent().parent().parent();
toggleBtnDot(btnDot, true);
const isCopilotOnBtn = () => isCopilotOn(btn);
const copilotCheck = () => {
const ctx = {timer: null};
ctx.timer = setInterval(() => checkForCopilotToggleState(ctx.timer, isCopilotOnBtn, true, btn.next()), 500);
}
copilotCheck()
} else {
debugLog('modal.length > 0', textArea)
}
},
2000);
}
const getLastQuery = () => {
const lastQueryBoxU = jq('svg[data-icon="arrow-down-right"]').last().parent().parent().parent().parent().parent();
const wasCopilotUsed = lastQueryBoxU.prev().find('svg[data-icon="star-christmas"]').length > 0;
return wasCopilotUsed ? lastQueryBoxU.prev().prev().text() : lastQueryBoxU.prev().text();
}
setInterval(() => {
const controlsArea = jq('#ppl-message-scroll-target ~ div textarea ~ div');
const lastQueryBoxText = getLastQuery();
const mainTextArea = controlsArea.prev()
if (lastQueryBoxText) {
const copilotNewThread = jq('#copilot_new_thread');
const copilotRepeatLast = jq('#copilot_repeat_last');
if (controlsArea.length > 0 && copilotNewThread.length < 1) {
controlsArea.append(button('copilot_new_thread', robotIco, "Starts new thread for with last query text and Copilot ON"));
}
if (controlsArea.length > 0 && copilotRepeatLast.length < 1)
controlsArea.append(button('copilot_repeat_last', robotRepeatIco, "Repeats last query with Copilot ON"));
if (!copilotNewThread.attr('data-has-custom-click-event')) {
copilotNewThread.on("click", function () {
debugLog('copilotNewThread Button clicked!');
openNewThreadModal(getLastQuery());
})
copilotNewThread.attr('data-has-custom-click-event', true);
}
if (!copilotRepeatLast.attr('data-has-custom-click-event')) {
copilotRepeatLast.on("click", function () {
const textAreaElement = mainTextArea[0];
debugLog('mainTextArea', mainTextArea);
const btnDot = getBtnDotFromTextArea(mainTextArea)
changeValueUsingEvent(textAreaElement, getLastQuery());
toggleBtnDot(btnDot, true);
const btn = btnDot.parent().parent().parent();
const isCopilotOnBtn = () => isCopilotOn(btn);
const copilotCheck = () => {
const ctx = {timer: null};
ctx.timer = setInterval(() => checkForCopilotToggleState(ctx.timer, isCopilotOnBtn, true, btn.next()), 500);
}
copilotCheck();
debugLog('copilot_repeat_last Button clicked!');
})
copilotRepeatLast.attr('data-has-custom-click-event', true);
}
}
}, 1000)
}());