Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Huge code simplification, working for Plus and regular users ChatGPT Mar 23 Version, configurable user prompts copy, CTRL+UP to navigate through previous user prompts #12

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions Copy-for-ChatGPT.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"folders": [
{
"path": "."
}
]
}
174 changes: 109 additions & 65 deletions init.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@
const currentYear = new Date().getFullYear()
console.log(`
- Copy Button: Enabled ✅
- CMD+K Hotkey: Enabled ✅
- Up Arrow Key: Enabled ✅
- CTRL/CMD+K Hotkey: Enabled ✅
- CTRL/CMD+Up/Down Arrow Key: Enabled ✅
- ✨ AI plagiarism checker: Enabled ✅

Copyright (c) ${currentYear} Sethu Senthil
Version: 0.3.7
Copyright (c) ${currentYear} Sethu Senthil / Daniel Liedke
Version: 0.5.0
https://copygpt.sethusenthil.com
https://sethusenthil.com
`)

const CHAT_CONTAINER_SELECTOR = '.flex.flex-col.text-sm.dark\\:bg-gray-800';
const CHAT_BUBLES_SELECTOR = '.flex.flex-grow.flex-col.gap-3.max-w-full';
const CHAT_TEXT_SELECTOR = '.markdown';
const CLIPBOARD_CLASS_NAME = 'copy-to-clipboard';

const CHAT_USER_MESSAGE_SELECTOR = '#prompt-textarea';
const CHAT_SEND_MESSAGE_BUTTON_SELECTOR = '.icon-sm.m-1.md\\:m-0';

var copyUserPromptToClipboard = false;

const showSnackbar = (message, position) => {
const snack = document.createElement('div');
Expand All @@ -38,8 +42,7 @@
};

chrome.runtime.onMessage.addListener(function (response, sender, sendResponse) {
// console.log('response', response);


if (response.message.documents[0].average_generated_prob >= 0.5) {
showSnackbar('This text was flagged as AI generated by plagiarism detectors, make some changes before submitting! 🚨', 'top');
} else {
Expand All @@ -51,7 +54,7 @@
chrome.runtime.sendMessage({ message: str });

navigator.clipboard.writeText(str).then(function () {
//console.log('Async: Copying to clipboard was successful!');

}, function (err) {
console.error('Async: Could not copy text: ', err);
});
Expand All @@ -60,66 +63,74 @@


const intervalId = window.setInterval(async function () {

if (!document.querySelector('#copygpt-credits')) {
var textCenterElement = document.querySelector('.text-center');
if (textCenterElement == null) {
textCenterElement = document.querySelector('.text-center.text-xs');
var textCenterElement = document.querySelector('.text-center');
if (textCenterElement == null) {
textCenterElement = document.querySelector('.text-center.text-xs');
}
if (textCenterElement !== null) {
textCenterElement.insertAdjacentHTML('beforeend', '&nbsp; ✨ <a class="underline" id="copygpt-credits" target="_blank" href="https://copygpt.sethusenthil.com/?ref=gptFooter"> Enhanced by CopyGPT</a>');
}
textCenterElement.insertAdjacentHTML('beforeend', '&nbsp; ✨ <a class="underline" id="copygpt-credits" target="_blank" href="https://copygpt.sethusenthil.com/?ref=gptFooter"> Enhanced by CopyGPT</a>')
}

const chatContainer = document.querySelector('main');

//console.log('probing for new chat bubbles');
const chatbubbles = chatContainer.querySelectorAll('main.w-full .border-b');
//console.log('chatbubbles', chatbubbles);

//check if it is a plus user
var plusUser = (chatbubbles.length % 2 === 0) ? false : true;

chatbubbles.forEach((chatbox, i) => {
//console.log('chatbox', chatbox);
//first chat box needs to be from user, hence all the even chat bubbles are from bot
//plus users will have the first row as model selection
if ((i > 0 && (i % 2 === 0) && plusUser) ||
((i + 1) % 2 === 0 && !plusUser)) {
//it is a chat box from bot
const addAfter = chatbox.querySelector(CHAT_TEXT_SELECTOR);

if (chatbox.querySelector(`.${CLIPBOARD_CLASS_NAME}`) === null) {

//TODO: to check if ChatGPT is done typing check .result-streaming class exists in chatbox

addAfter.insertAdjacentHTML('afterend', `<div style="display: flex; align-items: center; color: lightslategray;" class="copy-to-clipboard">
<p>Copy to Clipboard</p> <img src="https://copygpt.sethusenthil.com/cdn/clipboard-emoji.webp" alt="clipboard emoji" class="emoji"/>
</div>`);
chatbox.querySelector(`.${CLIPBOARD_CLASS_NAME}`).addEventListener('click', function () {

const text = chatbox.querySelector(CHAT_TEXT_SELECTOR).innerText;
copyToClipboard(text);
// Find send message button element with class .icon-sm.m-1.md\\:m-0
const sendMessageButton = document.querySelector(CHAT_SEND_MESSAGE_BUTTON_SELECTOR);

// If sendMessageButton is not found return
if (sendMessageButton === null) {
return;
}

// If chat container is not found return
const chatContainer = document.querySelector(CHAT_CONTAINER_SELECTOR);
if (chatContainer===null) {
return;
}

});
}
const chatbubbles = document.querySelectorAll(CHAT_BUBLES_SELECTOR);

//check if copyUserPromptToClipboard is enabled
chrome.storage.sync.get('copyUserPromptToClipboard', function(settings) {
if (settings.copyUserPromptToClipboard) {
copyUserPromptToClipboard = true;
}
});

chatbubbles.forEach((chatbox, i) => {
{
var text;

if (chatbox.parentElement.querySelector('.copy-to-clipboard') === null && (!(!copyUserPromptToClipboard && i % 2 === 0))) {

chatbox.insertAdjacentHTML('afterend', `<div style="display: flex; align-items: center; color: lightslategray;" class="copy-to-clipboard">
<p>Copy to Clipboard</p> <img src="https://copygpt.sethusenthil.com/cdn/clipboard-emoji.webp" alt="clipboard emoji" class="emoji"/>
</div>`);
chatbox.parentElement.querySelector('.copy-to-clipboard').addEventListener('click', function () {

text = chatbox.innerText;
copyToClipboard(text);

});
}
}
});

if (chatContainer.getAttribute('listener-injected') !== 'true') {

//console.log('setting event listener cause its not already there')
document.addEventListener('keydown', function (event) {
const chatContainer = document.querySelector('.flex .flex-col .items-center');
const chatContainer = document.querySelector(CHAT_CONTAINER_SELECTOR);
if (chatContainer===null) {
return;
}

// check for CTRL+K on Windows or CMD+K on Mac
if ((event.ctrlKey || event.metaKey) && event.keyCode === 75) {
//console.log('Copy Shortcut Pressed');

const chatbubbles = chatContainer.querySelectorAll('main.w-full .border-b');
const chatbubbles = document.querySelectorAll(CHAT_BUBLES_SELECTOR);

//check if it is a plus user
var plusUser = (chatbubbles.length % 2 === 0) ? false : true;
if (chatbubbles.length % 2 === 0) {

if (((chatbubbles.length % 2 === 0) && !plusUser) ||
((chatbubbles.length % 2 === 1) && plusUser)) {
//if last chat is from bot
const lastChatBubble = chatbubbles[chatbubbles.length - 1];
const text = lastChatBubble.querySelector(CHAT_TEXT_SELECTOR).innerText
Expand All @@ -130,29 +141,62 @@
chatContainer.setAttribute('listener-injected', 'true');
});

const textarea = document.querySelector('textarea');
const textarea = document.querySelector(CHAT_USER_MESSAGE_SELECTOR);

if (textarea!==null && textarea.getAttribute('listener-injected') !== 'true') {
textarea.setAttribute('listener-injected', 'true');
textarea.addEventListener('keydown', function (event) {
if (event.key === 'ArrowUp') {
if (chatbubbles.length > 0) {
let lastSetIndex = chatContainer.getAttribute('last-set-index') ?? 0;
//console.log('lastSetIndex', lastSetIndex);

// CTRL+UP or CMD+UP to navigate through user previous prompts
if ((event.ctrlKey || event.metaKey) && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {

const chatContainer = document.querySelector(CHAT_CONTAINER_SELECTOR);
if (chatContainer===null) {
return;
}

const chatbubbles = document.querySelectorAll(CHAT_BUBLES_SELECTOR);

if (chatbubbles.length > 0) {

let lastSetIndex = +chatContainer.getAttribute('last-set-index') ?? chatbubbles.length;

if (event.key === 'ArrowUp') {
lastSetIndex = lastSetIndex -2;

if (lastSetIndex < 0) {
lastSetIndex = chatbubbles.length-2;
}
}

if (event.key === 'ArrowDown') {
lastSetIndex = lastSetIndex + 2;

if (lastSetIndex >= chatbubbles.length) {
lastSetIndex = 0;
}
}

lastSetIndex++;

const lastChatBubble = chatbubbles[chatbubbles.length - lastSetIndex];
const text = lastChatBubble.innerText

textarea.value = text;
chatContainer.setAttribute('last-set-index', lastSetIndex);
const lastChatBubble = chatbubbles[lastSetIndex];
var text = lastChatBubble.innerText

text = text.replace('\n\nCopy to Clipboard','');
textarea.value = text;

// Expand the textarea if prompt is multiline otherwise keeps default size
if (text.includes('\n')) {
textarea.setAttribute('style','max-height: 200px; height: 264px;');
} else {
textarea.setAttribute('style', 'max-height: 200px; height: 24px; overflow-y: hidden;');
}

chatContainer.setAttribute('last-set-index', lastSetIndex);
}
}
});
}
}, 1000);
}

}, 2000);

})();
10 changes: 7 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"manifest_version": 3,
"author": "Sethu Senthil",
"author": "Sethu Senthil/Daniel Liedke",
"name": "Copy for Chat GPT",
"icons": {
"16": "media/16.png",
Expand All @@ -10,17 +10,21 @@
"128": "media/128.png"
},
"description": "This extension allows you to copy chat GPT responses with a click of a button or CMD+K and runs it through plagiarism detectors",
"version": "0.3.8",
"version": "0.5.0",
"host_permissions": [
"https://api.gptzero.me/*"
],
"permissions": [
"storage"
],
"background": {
"service_worker": "background.js"
},
"options_page": "options.html",
"content_scripts": [
{
"js": [
"init.js"
"init.js","settings.js"
],
"css": [
"styles.css"
Expand Down
12 changes: 12 additions & 0 deletions options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Copy For ChatGPT Settings</title>
</head>
<body>
<h1>Copy For ChatGPT Settings</h1>
<div id="settings"></div>
</body>
</html>
<script src="settings.js"></script>
30 changes: 30 additions & 0 deletions settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Define the default settings
const DEFAULT_SETTINGS = {
copyUserPromptToClipboard: true
};

// Load the settings from storage
chrome.storage.sync.get(DEFAULT_SETTINGS, function(settings) {
// Apply the settings to the extension
const copyUserPromptCheckbox = document.getElementById("copyUserPromptToClipboard");
copyUserPromptCheckbox.checked = settings.copyUserPromptToClipboard;
});

// Save the settings when they are changed
function saveSettings() {
const settings = {
copyUserPromptToClipboard: document.getElementById("copyUserPromptToClipboard").checked
};
chrome.storage.sync.set(settings);
}

// Create the settings UI
const settingsDiv = document.createElement("div");
settingsDiv.innerHTML = `
<label>
<input type="checkbox" id="copyUserPromptToClipboard">
Enable User Prompt Copy
</label>
`;
settingsDiv.addEventListener("change", saveSettings);
document.body.appendChild(settingsDiv);