improvements

This commit is contained in:
samex 2025-04-04 14:02:41 +02:00
parent 13c7ab1d08
commit 655e788b7a
7 changed files with 657 additions and 69 deletions

View File

@ -1,37 +1,215 @@
let originTabId = null;
// Create context menu
// 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(() => {
chrome.contextMenus.create({
id: "improveMail",
title: "Verbessere Mail",
contexts: ["selection"], // Show only when text is selected
// 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);
});
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
// 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) {
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/?temporary-chat=true" }, (newTab) => {
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);
@ -45,19 +223,7 @@ function handleMenuItemClick(info, tab, prefixText) {
});
}
}
// 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
@ -69,9 +235,14 @@ chrome.runtime.onMessage.addListener((message, sender) => {
chrome.tabs.remove(currentTabId, () => {
console.log("Closed current tab:", currentTabId);
// Switch back to the original tab
// Switch back to the original tab and show completion message
if (originTabId) {
chrome.tabs.update(originTabId, { active: true });
chrome.tabs.update(originTabId, { active: true }, () => {
// Show completion message in the original tab
setTimeout(() => {
showCompletionMessage(originTabId);
}, 100); // Small delay to ensure tab is active
});
}
});
}

View File

@ -0,0 +1,43 @@
#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); }
}

View File

@ -0,0 +1,94 @@
// Create and manage overlay elements for loading and completion states
let overlay = null;
let messageElement = null;
// Flag to indicate content script is ready
let isInitialized = false;
// Initialize the content script
function initialize() {
if (isInitialized) return;
isInitialized = true;
// Let the background script know we're ready
chrome.runtime.sendMessage({ action: 'contentScriptReady' });
console.log('Real ChatGPT Extension: Content script initialized');
}
// Initialize immediately
initialize();
// Create the overlay element with loading spinner
function createOverlay() {
// Remove any existing overlay
removeOverlay();
// Create main overlay container
overlay = document.createElement('div');
overlay.id = 'real-chatgpt-overlay';
// Create message container
messageElement = document.createElement('div');
messageElement.id = 'real-chatgpt-message';
// Create loading spinner
const spinner = document.createElement('div');
spinner.id = 'real-chatgpt-spinner';
// Add loading message
messageElement.textContent = 'Processing with ChatGPT...';
// Assemble the overlay
overlay.appendChild(spinner);
overlay.appendChild(messageElement);
document.body.appendChild(overlay);
}
// Remove the overlay from the DOM
function removeOverlay() {
const existingOverlay = document.getElementById('real-chatgpt-overlay');
if (existingOverlay) {
existingOverlay.remove();
}
}
// Show completion message and remove after delay
function showCompletionMessage() {
if (!overlay) {
createOverlay();
}
// Update the overlay to show completion message
const spinner = document.getElementById('real-chatgpt-spinner');
if (spinner) {
spinner.style.display = 'none';
}
// Update message
if (messageElement) {
messageElement.textContent = 'Finished, please paste from clipboard';
messageElement.classList.add('completion');
}
// Remove after 1 second
setTimeout(removeOverlay, 1000);
}
// Listen for messages from the background script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Make sure we're initialized
initialize();
if (message.action === 'showLoading') {
createOverlay();
sendResponse({ status: 'overlay_created' });
return true;
} else if (message.action === 'showCompletion') {
showCompletionMessage();
sendResponse({ status: 'completion_shown' });
return true;
} else if (message.action === 'removeOverlay') {
removeOverlay();
sendResponse({ status: 'overlay_removed' });
return true;
}
});

View File

@ -7,7 +7,8 @@
"scripting",
"activeTab",
"tabs",
"clipboardWrite"
"clipboardWrite",
"storage"
],
"host_permissions": [
"https://chatgpt.com/*"
@ -25,20 +26,5 @@
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"run_at": "document_end",
"js": [
"contentScript.js"
],
"css": [
"contentScript.css"
]
}
]
}

100
popup.css
View File

@ -4,31 +4,103 @@
}
body {
width: 500px;
height: 200px;
background: #fff9d8;
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;
padding: 15px;
box-sizing: border-box;
border-radius: 10px;
}
.btn-primary {
background-color: #2196F3;
margin-right: 1rem;
}
.btn {
color: white;
font-weight: bold;
}
h1 {
font-size: 20px;
}
.menu-items {
margin-bottom: 10px;
width: 100%;
}
.menu-item {
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
margin-bottom: 10px;
background-color: #f9f9f9;
}
.menu-item-header {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
font-weight: bold;
}
.menu-item-content {
display: none;
margin-top: 1rem;
}
.menu-item.expanded .menu-item-content {
display: block;
}
input[type="text"], textarea, select {
width: 100%;
padding: 8px;
margin: 5px 0;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
h1 {
margin: 0;
font-family: 'Sora';
textarea {
height: 80px;
resize: vertical;
}
p {
margin: 0;
font-family: 'Sora';
.btn {
padding: 8px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
}
.btn-primary {
background-color: #4285f4;
color: white;
}
.btn-danger {
background-color: #ea4335;
color: white;
}
.btn-secondary {
background-color: #f1f1f1;
color: #333;
}
.action-buttons {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.toggle-btn {
background: none;
border: none;
cursor: pointer;
}
.form-group {
margin-bottom: 10px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

View File

@ -3,14 +3,59 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chrome extension</title>
<title>Real ChatGPT Extension Settings</title>
<link rel="stylesheet" href="popup.css">
<script defer src="popup.js"></script>
</head>
<body>
<div class="container">
<h1>Great!</h1>
<p>Your extension is ready<br>you can go to <a target="_blank" href="https://developer.chrome.com/docs/extensions/">https://developer.chrome.com/docs/extensions/</a> to learn more about chrome extensions</p>
<h1>Real ChatGPT Extension Settings</h1>
<div class="menu-items" id="menuItems">
<!-- Menu items will be added here dynamically -->
</div>
<button id="addMenuItem" class="btn btn-primary">+ New</button>
<div class="action-buttons">
<button id="saveSettings" class="btn btn-primary">Save Settings</button>
<button id="resetSettings" class="btn btn-danger">Reset to Default</button>
</div>
</div>
<!-- Template for menu item -->
<template id="menuItemTemplate">
<div class="menu-item">
<div class="menu-item-header">
<span class="menu-item-title">Menu Item</span>
<button class="toggle-btn"></button>
</div>
<div class="menu-item-content">
<div class="form-group">
<label for="menuId">ID:</label>
<input type="text" class="menu-id" placeholder="Unique identifier (e.g., improveMail)">
</div>
<div class="form-group">
<label for="menuTitle">Title:</label>
<input type="text" class="menu-title" placeholder="Display text in context menu">
</div>
<div class="form-group">
<label for="menuPrompt">Prompt Template:</label>
<textarea class="menu-prompt" placeholder="Prompt text to send to ChatGPT"></textarea>
</div>
<div class="form-group">
<label for="menuModel">Model:</label>
<select class="menu-model">
<option value="gpt-4o">GPT-4o</option>
<option value="gpt-4-5">GPT-4.5</option>
<option value="o1">o1</option>
<option value="o3-mini">o3-mini</option>
<option value="o3-mini-high">o3-mini-high</option>
</select>
</div>
<button class="btn btn-danger delete-menu-item">Delete</button>
</div>
</div>
</template>
</body>
</html>

177
popup.js
View File

@ -0,0 +1,177 @@
document.addEventListener("DOMContentLoaded", () => {
// Default menu items
const defaultMenuItems = [
{
id: "improveMail",
title: "Improve Email",
prompt:
"Improve the following email. Do not add any personal comments, only show the text as output:",
model: "gpt-4o",
},
{
id: "improveText",
title: "Improve Text",
prompt:
"Improve the following text without making it much longer or shorter than it already is. Do not add any personal comments, only show the text as output:",
model: "gpt-4o",
},
{
id: "seoText",
title: "Create an SEO Text",
prompt:
"You are the worlds best SEO text author. Write an SEO-optimized text of 400500 words based on the following content. Identify the main keyword and integrate it with a keyword density of 23%. Structure the text with HTML elements (h1, h2, h3, p). Highlight the keyword with the strong tag. Only output the HTML code, without HTML, head, or body tags, and without comments. The output should appear as plain text, not in a code block. Here is the relevant text/keyword:",
model: "gpt-4o",
},
{
id: "explainMe",
title: "Explain Text",
prompt:
"Explain the following text to me, summarize it briefly and in a readable way. Do not add any personal comments, only show the text as output:",
model: "gpt-4o",
},
{
id: "fixCode",
title: "Fix Code",
prompt:
"Improve or fix the following code snippet. Provide the corrected code without adding any personal comments. Only output the corrected code:",
model: "gpt-4o",
},
];
// DOM Elements
const menuItemsContainer = document.getElementById("menuItems");
const addMenuItemBtn = document.getElementById("addMenuItem");
const saveSettingsBtn = document.getElementById("saveSettings");
const resetSettingsBtn = document.getElementById("resetSettings");
const menuItemTemplate = document.getElementById("menuItemTemplate");
// Load settings from storage
function loadSettings() {
chrome.storage.sync.get(["menuItems"], (result) => {
let menuItems = result.menuItems || defaultMenuItems;
renderMenuItems(menuItems);
});
}
// Save settings to storage
function saveSettings() {
const menuItems = [];
const menuItemElements = document.querySelectorAll(".menu-item");
menuItemElements.forEach((item) => {
const id = item.querySelector(".menu-id").value.trim();
const title = item.querySelector(".menu-title").value.trim();
const prompt = item.querySelector(".menu-prompt").value.trim();
const model = item.querySelector(".menu-model").value;
if (id && title && prompt) {
menuItems.push({
id,
title,
prompt,
model,
});
}
});
chrome.storage.sync.set({ menuItems }, () => {
// Update context menus
chrome.runtime.sendMessage({ action: "updateContextMenus" });
// Show save confirmation
const saveBtn = document.getElementById("saveSettings");
const originalText = saveBtn.textContent;
saveBtn.textContent = "Saved!";
saveBtn.disabled = true;
setTimeout(() => {
saveBtn.textContent = originalText;
saveBtn.disabled = false;
}, 1500);
});
}
// Reset settings to default
function resetSettings() {
if (confirm("Are you sure you want to reset all settings to default?")) {
chrome.storage.sync.set({ menuItems: defaultMenuItems }, () => {
renderMenuItems(defaultMenuItems);
chrome.runtime.sendMessage({ action: "updateContextMenus" });
});
}
}
// Render menu items in the UI
function renderMenuItems(menuItems) {
menuItemsContainer.innerHTML = "";
menuItems.forEach((item, index) => {
const menuItemElement = createMenuItemElement(item);
menuItemsContainer.appendChild(menuItemElement);
});
}
// Create a menu item element
function createMenuItemElement(item = {}) {
const fragment = document.importNode(menuItemTemplate.content, true);
const menuItem = fragment.querySelector(".menu-item");
// Set values if provided
const titleElement = menuItem.querySelector(".menu-item-title");
const idInput = menuItem.querySelector(".menu-id");
const titleInput = menuItem.querySelector(".menu-title");
const promptInput = menuItem.querySelector(".menu-prompt");
const modelSelect = menuItem.querySelector(".menu-model");
if (item.id) {
idInput.value = item.id;
titleElement.textContent = item.title || "Menu Item";
}
if (item.title) titleInput.value = item.title;
if (item.prompt) promptInput.value = item.prompt;
if (item.model) modelSelect.value = item.model;
// Toggle expand/collapse
const toggleBtn = menuItem.querySelector(".menu-item-header");
toggleBtn.addEventListener("click", () => {
menuItem.classList.toggle("expanded");
toggleBtn.querySelector(".toggle-btn").textContent =
menuItem.classList.contains("expanded") ? "▲" : "▼";
});
// Delete button
const deleteBtn = menuItem.querySelector(".delete-menu-item");
deleteBtn.addEventListener("click", () => {
if (confirm("Are you sure you want to delete this menu item?")) {
menuItem.remove();
}
});
// Update title when changed
titleInput.addEventListener("input", () => {
titleElement.textContent = titleInput.value || "Menu Item";
});
return menuItem;
}
// Add new menu item
function addMenuItem() {
const newItem = createMenuItemElement();
menuItemsContainer.appendChild(newItem);
// Expand the new item and scroll to it
newItem.classList.add("expanded");
newItem.querySelector(".toggle-btn").textContent = "▲";
newItem.scrollIntoView({ behavior: "smooth" });
}
// Event listeners
addMenuItemBtn.addEventListener("click", addMenuItem);
saveSettingsBtn.addEventListener("click", saveSettings);
resetSettingsBtn.addEventListener("click", resetSettings);
// Initialize
loadSettings();
});