commit 26fdcc9418d22f68a313ce6c397e668ac41c6859 Author: Samet Date: Thu Nov 28 15:34:42 2024 +0100 First Upload diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f47729 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +U need a working chatgpt with provisional chat function +Google Chrome tested +Not Settings yet +define your prompts in background.js for now + +Samet G | 11/24 \ No newline at end of file diff --git a/background.js b/background.js new file mode 100644 index 0000000..9ea1831 --- /dev/null +++ b/background.js @@ -0,0 +1,197 @@ +let originTabId = null; + +// Create context menu +chrome.runtime.onInstalled.addListener(() => { + chrome.contextMenus.create({ + id: "improveMail", + title: "Verbessere Mail", + contexts: ["selection"], // Show only when text is selected + }); + chrome.contextMenus.create({ + id: "improveText", + title: "Verbessere Text", + contexts: ["selection"], // Show only when text is selected + }); + chrome.contextMenus.create({ + id: "seoText", + title: "Erstelle ein SEO-Text", + contexts: ["selection"], // Show only when text is selected + }); + chrome.contextMenus.create({ + id: "explainMe", + title: "Erkläre Text", + contexts: ["selection"], // Show only when text is selected + }); +}); + +// Utility function to handle menu item click dynamically +function handleMenuItemClick(info, tab, prefixText) { + originTabId = tab.id; + + if (info.selectionText) { + const selectedText = prefixText + info.selectionText; + + chrome.tabs.create({ url: "https://chatgpt.com/?temporary-chat=true" }, (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], + }); + } + }); + }); + } +} +// Handle context menu clicks +chrome.contextMenus.onClicked.addListener((info, tab) => { + const menuActions = { + explainMe: 'Erkläre mir folgenden Text, fasse es kurz und gut lesbar zusammen. Erstelle keine eigenen Kommentare, zeige rein nur den text als Ausgabe: ', + improveMail: 'Verbessere folgende Mail, erstelle keine eigenen Kommentare, zeige rein nur den text als ausgabe: ', + improveText: 'Verbessere folgenden Text, mach es nicht viel länger oder kürzer als es jetzt schon ist, erstelle keine eigenen Kommentare, zeige rein nur den text als Ausgabe: ', + seoText: 'Du bist der Weltbeste SEO-Text Author. Schreibe einen SEO-optimierten Text mit 400-500 Wörtern basierend auf dem folgenden Inhalt. Identifiziere das Haupt-Keyword und integriere es mit einer Keyword-Dichte von 2-3 %. Strukturierte den Text mit HTML-Elementen (h1, h2, h3, p). Hebe das Keyword mit dem strong-Tag hervor. Gib nur den HTML-Code aus, ohne HTML-, Head- oder Body-Tags und ohne Kommentare. Die Ausgabe sollte als reiner Text erscheinen, nicht in einem Codefeld. Hier ist der relevante Text/Keyword: ', + }; + + if (menuActions[info.menuItemId]) { + handleMenuItemClick(info, tab, menuActions[info.menuItemId]); + } +}); + + +// 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 + if (originTabId) { + chrome.tabs.update(originTabId, { active: true }); + } + }); + } +}); + +// 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 = `

${text}

`; + + // 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); +} diff --git a/contentScript.css b/contentScript.css new file mode 100644 index 0000000..e69de29 diff --git a/contentScript.js b/contentScript.js new file mode 100644 index 0000000..e69de29 diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..d08db6e Binary files /dev/null and b/icon.png differ diff --git a/icon128.png b/icon128.png new file mode 100644 index 0000000..890ef9e Binary files /dev/null and b/icon128.png differ diff --git a/icon16.png b/icon16.png new file mode 100644 index 0000000..beb527c Binary files /dev/null and b/icon16.png differ diff --git a/icon48.png b/icon48.png new file mode 100644 index 0000000..5d582cc Binary files /dev/null and b/icon48.png differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b0b8b4c --- /dev/null +++ b/manifest.json @@ -0,0 +1,44 @@ +{ + "name": "Real ChatGPT Extension", + "description": "Context Menu Action. Redirect selected text to ChatGPT browser tab, copy response, close tab & switch back to origin tab", + "author": "Samet G", + "permissions": [ + "contextMenus", + "scripting", + "activeTab", + "tabs", + "clipboardWrite", + ], + "host_permissions": [ + "https://chatgpt.com/*" + ], + "version": "0.1.1", + "manifest_version": 3, + "icons": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "action": { + "default_popup": "popup.html", + "default_icon": {} + }, + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [ + "http://*/*", + "https://*/*" + ], + "run_at": "document_end", + "js": [ + "contentScript.js" + ], + "css": [ + "contentScript.css" + ] + } + ] +} \ No newline at end of file diff --git a/popup.css b/popup.css new file mode 100644 index 0000000..c7cf5f5 --- /dev/null +++ b/popup.css @@ -0,0 +1,34 @@ +@font-face { + font-family: Sora; + src: url(./dist/sora.ttf); +} + +body { + width: 500px; + height: 200px; + min-width: 500px; + min-height: 200px; + margin: 0; +} + +.container { + background-color: #F2DE62; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-evenly; + padding: 30px; + box-sizing: border-box; +} + +h1 { + margin: 0; + font-family: 'Sora'; +} + +p { + margin: 0; + font-family: 'Sora'; +} \ No newline at end of file diff --git a/popup.html b/popup.html new file mode 100644 index 0000000..f3cf1ab --- /dev/null +++ b/popup.html @@ -0,0 +1,16 @@ + + + + + + Chrome extension + + + + +
+

Great!

+

Your extension is ready
you can go to https://developer.chrome.com/docs/extensions/ to learn more about chrome extensions

+
+ + \ No newline at end of file diff --git a/popup.js b/popup.js new file mode 100644 index 0000000..e69de29