real-chatgpt-extension/background.js
2025-04-04 14:02:41 +02:00

369 lines
15 KiB
JavaScript

let originTabId = null;
// Function to show loading overlay directly in the page
function showLoadingOverlay(tabId) {
chrome.scripting.executeScript({
target: { tabId: tabId },
function: () => {
// Create overlay elements if they don't exist
let overlay = document.getElementById('real-chatgpt-overlay');
if (!overlay) {
// Create styles
const style = document.createElement('style');
style.textContent = `
#real-chatgpt-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
font-family: Arial, sans-serif;
}
#real-chatgpt-message {
color: white;
font-size: 18px;
margin-top: 20px;
padding: 10px 20px;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 5px;
text-align: center;
max-width: 80%;
}
#real-chatgpt-message.completion {
background-color: #28a745;
font-weight: bold;
}
#real-chatgpt-spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
// Create overlay
overlay = document.createElement('div');
overlay.id = 'real-chatgpt-overlay';
// Create spinner
const spinner = document.createElement('div');
spinner.id = 'real-chatgpt-spinner';
// Create message
const message = document.createElement('div');
message.id = 'real-chatgpt-message';
message.textContent = 'Processing with ChatGPT...';
// Assemble overlay
overlay.appendChild(spinner);
overlay.appendChild(message);
document.body.appendChild(overlay);
}
}
});
}
// Function to show completion message
function showCompletionMessage(tabId) {
chrome.scripting.executeScript({
target: { tabId: tabId },
function: () => {
// Update existing overlay or create a new one
let overlay = document.getElementById('real-chatgpt-overlay');
let spinner = document.getElementById('real-chatgpt-spinner');
let message = document.getElementById('real-chatgpt-message');
if (!overlay) {
// If overlay doesn't exist, create it
const style = document.createElement('style');
style.textContent = `
#real-chatgpt-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
font-family: Arial, sans-serif;
}
#real-chatgpt-message {
color: white;
font-size: 18px;
margin-top: 20px;
padding: 10px 20px;
background-color: #28a745;
border-radius: 5px;
text-align: center;
max-width: 80%;
font-weight: bold;
}
`;
document.head.appendChild(style);
overlay = document.createElement('div');
overlay.id = 'real-chatgpt-overlay';
message = document.createElement('div');
message.id = 'real-chatgpt-message';
overlay.appendChild(message);
document.body.appendChild(overlay);
} else if (spinner) {
// Hide spinner if it exists
spinner.style.display = 'none';
}
// Update message
if (message) {
message.textContent = 'Finished, please paste from clipboard';
message.classList.add('completion');
}
// Remove overlay after 1 second
setTimeout(() => {
const overlay = document.getElementById('real-chatgpt-overlay');
if (overlay) {
overlay.remove();
}
}, 1000);
}
});
}
// Create context menus from settings or defaults
function createContextMenus(menuItems) {
// Remove all existing context menus
chrome.contextMenus.removeAll();
// Create new context menus from settings
menuItems.forEach(item => {
chrome.contextMenus.create({
id: item.id,
title: item.title,
contexts: ["selection"], // Show only when text is selected
});
});
}
// Initialize context menus on installation
chrome.runtime.onInstalled.addListener(() => {
// Initialize storage with default values if not already set
chrome.storage.sync.get(['menuItems'], (result) => {
const menuItems = result.menuItems || defaultMenuItems;
// Save to storage if not already set
if (!result.menuItems) {
chrome.storage.sync.set({ menuItems: defaultMenuItems });
}
// Create context menus
createContextMenus(menuItems);
});
});
// Handle context menu clicks
chrome.contextMenus.onClicked.addListener((info, tab) => {
// Get menu items from storage
chrome.storage.sync.get(['menuItems'], (result) => {
const menuItems = result.menuItems || defaultMenuItems;
const selectedItem = menuItems.find(item => item.id === info.menuItemId);
if (selectedItem) {
handleMenuItemClick(info, tab, selectedItem.prompt, selectedItem.model);
}
});
});
// Listen for messages from popup.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'updateContextMenus') {
chrome.storage.sync.get(['menuItems'], (result) => {
const menuItems = result.menuItems || defaultMenuItems;
createContextMenus(menuItems);
});
}
});
// Utility function to handle menu item click dynamically
function handleMenuItemClick(info, tab, prefixText, model = 'gpt-4o') {
originTabId = tab.id;
if (info.selectionText) {
// Show loading indicator in the original tab
showLoadingOverlay(originTabId);
const selectedText = prefixText + info.selectionText;
chrome.tabs.create({ url: `https://chatgpt.com/?model=${model}&temporary-chat=true`, active: false }, (newTab) => {
chrome.tabs.onUpdated.addListener(function listener(tabId, info) {
if (tabId === newTab.id && info.status === "complete") {
chrome.tabs.onUpdated.removeListener(listener);
chrome.scripting.executeScript({
target: { tabId: newTab.id },
func: pasteToChatGPT,
args: [selectedText, newTab.id],
});
}
});
});
}
}
// Listen for a message to close the current tab and switch back
chrome.runtime.onMessage.addListener((message, sender) => {
if (message.action === "closeGPTAndSwitchBack") {
const currentTabId = sender.tab.id;
// Close the current tab
chrome.tabs.remove(currentTabId, () => {
console.log("Closed current tab:", currentTabId);
// Switch back to the original tab and show completion message
if (originTabId) {
chrome.tabs.update(originTabId, { active: true }, () => {
// Show completion message in the original tab
setTimeout(() => {
showCompletionMessage(originTabId);
}, 100); // Small delay to ensure tab is active
});
}
});
}
});
// Function to paste text into ChatGPT input and send it
function pasteToChatGPT(text, gptTab) {
const newChatButton = document.querySelector('a[href="/"]');
if (newChatButton) newChatButton.click();
let sendInterval;
const interval = setInterval(() => {
const contentEditableDiv = document.querySelector('div[contenteditable="true"]');
if (contentEditableDiv) {
clearInterval(interval); // Stop retrying once the div is found
// Insert text into the contenteditable div
contentEditableDiv.innerHTML = `<p>${text}</p>`;
// Dispatch input events to trigger the mirroring mechanism
contentEditableDiv.dispatchEvent(new Event('input', { bubbles: true }));
sendInterval = setInterval(() => {
// Simulate clicking the Send button
const sendButton = document.querySelector('button[data-testid="send-button"]');
if (sendButton) {
clearInterval(sendInterval);
sendButton.click();
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed"; // Prevent scrolling
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
console.log(successful ? "Fallback: Copied to clipboard!" : "Fallback: Failed to copy!");
} catch (err) {
console.error("Fallback: Could not copy text:", err);
}
document.body.removeChild(textArea);
}
function waitForElement(selector, checkForDisappearance = false) {
return new Promise((resolve) => {
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if ((element && !checkForDisappearance) || (!element && checkForDisappearance)) {
observer.disconnect();
resolve(element);
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
}
// Function to copy text to the clipboard with fallback
function copyTextToClipboard(text) {
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text.trim(); // Ensure plain text without leading/trailing whitespace
textArea.style.position = "fixed"; // Prevent scrolling
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand("copy");
console.log(successful ? "Fallback: Copied to clipboard!" : "Fallback: Failed to copy!");
} catch (err) {
console.error("Fallback: Could not copy text:", err);
}
document.body.removeChild(textArea);
}
if (navigator.clipboard && document.hasFocus()) {
navigator.clipboard.writeText(text.trim())
.then(() => console.log("Copied to clipboard!"))
.catch((err) => {
console.error("Clipboard API failed, falling back:", err);
fallbackCopyTextToClipboard(text);
});
} else {
fallbackCopyTextToClipboard(text);
}
}
waitForElement('button[data-testid="stop-button"]').then(() => {
return new Promise((resolve) => {
const observer = new MutationObserver(() => {
const stopButton = document.querySelector('button[data-testid="stop-button"]');
if (!stopButton) {
observer.disconnect();
setTimeout(() => { resolve(); }, 1500);
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
}).then(() => {
const assistantMessages = document.querySelectorAll('div[data-message-author-role="assistant"]');
const latestResponse = assistantMessages[assistantMessages.length - 1]?.innerText || "";
if (latestResponse) {
setTimeout(() => {
copyTextToClipboard(latestResponse)
console.log("Action complete. Closing tab...");
chrome.runtime.sendMessage({ action: "closeGPTAndSwitchBack" });
}, 1000);
}
});
} else {
console.error("Send button not found!");
}
}, 100); // Check every 100ms
}
}, 100); // Check every 100ms
let timeout = setTimeout(() => {
clearInterval(interval);
clearInterval(sendInterval);
}, 5000);
}