369 lines
15 KiB
JavaScript
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);
|
|
}
|