diff --git a/background.js b/background.js index 3488114..8c0a63a 100644 --- a/background.js +++ b/background.js @@ -3,12 +3,12 @@ chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ id: "translateToHinglish", title: "Translate to Hinglish", - contexts: ["selection"] + contexts: ["selection"], }); chrome.contextMenus.create({ id: "explainInHinglish", title: "Explain in Hinglish", - contexts: ["selection"] + contexts: ["selection"], }); }); @@ -17,7 +17,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "translateText") { translateText(request.text) .then(sendResponse) - .catch(error => { + .catch((error) => { console.error("Translation error:", error); sendResponse("Translation error: " + error.message); }); @@ -26,7 +26,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "explainText") { explainText(request.text) .then(sendResponse) - .catch(error => { + .catch((error) => { console.error("Explanation error:", error); sendResponse("Explanation error: " + error.message); }); @@ -41,16 +41,16 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showLoadingPopup, - args: [] + args: [], }); const translatedText = await translateText(info.selectionText); - + // Remove loading popup and show translation chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showTranslationPopup, - args: [info.selectionText, translatedText] + args: [info.selectionText, translatedText], }); } catch (error) { console.error("Context menu translation error:", error); @@ -58,7 +58,7 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showErrorPopup, - args: [error.message] + args: [error.message], }); } } else if (info.menuItemId === "explainInHinglish" && info.selectionText) { @@ -67,16 +67,16 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showLoadingPopup, - args: [] + args: [], }); const explanation = await explainText(info.selectionText); - + // Remove loading popup and show explanation chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showExplanationPopup, - args: [info.selectionText, explanation] + args: [info.selectionText, explanation], }); } catch (error) { console.error("Context menu explanation error:", error); @@ -84,7 +84,7 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: showErrorPopup, - args: [error.message] + args: [error.message], }); } } @@ -94,30 +94,45 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { function getTranslationPrompt(style, level) { const prompts = { hinglish: { - balanced: "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", - moreHindi: "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use more Hindi words than English. Only respond with the translated text, no explanations.", - moreEnglish: "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use more English words than Hindi. Only respond with the translated text, no explanations." + balanced: + "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", + moreHindi: + "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use more Hindi words than English. Only respond with the translated text, no explanations.", + moreEnglish: + "You are a translator that converts English text to Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural in Hinglish. Use more English words than Hindi. Only respond with the translated text, no explanations.", }, hindi: { - balanced: "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use a balanced mix of formal and colloquial Hindi. Only respond with the translated text, no explanations.", - moreHindi: "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use more formal Hindi words. Only respond with the translated text, no explanations.", - moreEnglish: "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use more colloquial Hindi words. Only respond with the translated text, no explanations." + balanced: + "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use a balanced mix of formal and colloquial Hindi. Only respond with the translated text, no explanations.", + moreHindi: + "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use more formal Hindi words. Only respond with the translated text, no explanations.", + moreEnglish: + "You are a translator that converts English text to Hindi (Devanagari script). Keep the meaning exactly the same but make it sound natural in Hindi. Use more colloquial Hindi words. Only respond with the translated text, no explanations.", }, roman: { - balanced: "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use a balanced mix of formal and colloquial words. Only respond with the translated text, no explanations.", - moreHindi: "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use more formal words. Only respond with the translated text, no explanations.", - moreEnglish: "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use more colloquial words. Only respond with the translated text, no explanations." + balanced: + "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use a balanced mix of formal and colloquial words. Only respond with the translated text, no explanations.", + moreHindi: + "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use more formal words. Only respond with the translated text, no explanations.", + moreEnglish: + "You are a translator that converts Hindi text to Romanized Hindi (Hindi written in English letters). Keep the meaning exactly the same but make it sound natural. Use more colloquial words. Only respond with the translated text, no explanations.", }, formal: { - balanced: "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", - moreHindi: "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use more Hindi words than English. Only respond with the translated text, no explanations.", - moreEnglish: "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use more English words than Hindi. Only respond with the translated text, no explanations." + balanced: + "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", + moreHindi: + "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use more Hindi words than English. Only respond with the translated text, no explanations.", + moreEnglish: + "You are a translator that converts English text to formal Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound professional and formal. Use more English words than Hindi. Only respond with the translated text, no explanations.", }, casual: { - balanced: "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", - moreHindi: "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use more Hindi words than English. Only respond with the translated text, no explanations.", - moreEnglish: "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use more English words than Hindi. Only respond with the translated text, no explanations." - } + balanced: + "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use a balanced mix of Hindi and English words. Only respond with the translated text, no explanations.", + moreHindi: + "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use more Hindi words than English. Only respond with the translated text, no explanations.", + moreEnglish: + "You are a translator that converts English text to casual Hinglish (Hindi written in English letters). Keep the meaning exactly the same but make it sound casual and conversational. Use more English words than Hindi. Only respond with the translated text, no explanations.", + }, }; return prompts[style][level] || prompts.hinglish.balanced; @@ -125,49 +140,60 @@ function getTranslationPrompt(style, level) { // Function to translate text using Groq API async function translateText(text) { - const { groqApiKey, translationSettings } = await chrome.storage.local.get(['groqApiKey', 'translationSettings']); - + const { groqApiKey, translationSettings } = await chrome.storage.local.get([ + "groqApiKey", + "translationSettings", + ]); + if (!groqApiKey) { throw new Error("Please configure your API key first"); } - const style = translationSettings?.style || 'hinglish'; - const level = translationSettings?.level || 'balanced'; + const style = translationSettings?.style || "hinglish"; + const level = translationSettings?.level || "balanced"; const prompt = getTranslationPrompt(style, level); try { - const response = await fetch('https://api.groq.com/openai/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${groqApiKey}` - }, - body: JSON.stringify({ - messages: [{ - role: "system", - content: prompt - }, { - role: "user", - content: text - }], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000 - }) - }); - + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify({ + messages: [ + { + role: "system", + content: prompt, + }, + { + role: "user", + content: text, + }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }), + } + ); + if (!response.ok) { const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error?.message || `API error: ${response.status}`); + throw new Error( + errorData.error?.message || `API error: ${response.status}` + ); } - + const data = await response.json(); const translatedText = data.choices[0].message.content.trim(); - + if (!translatedText) { throw new Error("Empty translation received"); } - + return translatedText; } catch (error) { console.error("Translation error:", error); @@ -177,53 +203,72 @@ async function translateText(text) { // Function to explain text using Groq API async function explainText(text) { - const { groqApiKey, translationSettings } = await chrome.storage.local.get(['groqApiKey', 'translationSettings']); - + const { groqApiKey, translationSettings } = await chrome.storage.local.get([ + "groqApiKey", + "translationSettings", + ]); + if (!groqApiKey) { throw new Error("Please configure your API key first"); } - const style = translationSettings?.style || 'hinglish'; - const level = translationSettings?.level || 'balanced'; - const prompt = `You are an AI assistant that explains concepts in ${style === 'hindi' ? 'Hindi' : 'Hinglish'}. + const style = translationSettings?.style || "hinglish"; + const level = translationSettings?.level || "balanced"; + const prompt = `You are an AI assistant that explains concepts in ${ + style === "hindi" ? "Hindi" : "Hinglish" + }. Provide a clear and detailed explanation of the given text. - Make it easy to understand and use ${level === 'moreHindi' ? 'more Hindi words' : level === 'moreEnglish' ? 'more English words' : 'a balanced mix of Hindi and English words'}. + Make it easy to understand and use ${ + level === "moreHindi" + ? "more Hindi words" + : level === "moreEnglish" + ? "more English words" + : "a balanced mix of Hindi and English words" + }. Format your response in a clear, structured way with bullet points or short paragraphs. Only respond with the explanation, no additional text.`; try { - const response = await fetch('https://api.groq.com/openai/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${groqApiKey}` - }, - body: JSON.stringify({ - messages: [{ - role: "system", - content: prompt - }, { - role: "user", - content: text - }], - model: "meta-llama/llama-4-scout-17b-16e-instruct", - temperature: 0.7, - max_tokens: 1000 - }) - }); - + const response = await fetch( + "https://api.groq.com/openai/v1/chat/completions", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${groqApiKey}`, + }, + body: JSON.stringify({ + messages: [ + { + role: "system", + content: prompt, + }, + { + role: "user", + content: text, + }, + ], + model: "meta-llama/llama-4-scout-17b-16e-instruct", + temperature: 0.7, + max_tokens: 1000, + }), + } + ); + if (!response.ok) { const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error?.message || `API error: ${response.status}`); + throw new Error( + errorData.error?.message || `API error: ${response.status}` + ); } - + const data = await response.json(); const explanation = data.choices[0].message.content.trim(); - + if (!explanation) { throw new Error("Empty explanation received"); } - + return explanation; } catch (error) { console.error("Explanation error:", error); @@ -233,32 +278,35 @@ async function explainText(text) { // Function to show loading popup function showLoadingPopup() { - const popup = document.createElement('div'); - popup.id = 'translationLoadingPopup'; - popup.style.position = 'fixed'; - popup.style.zIndex = '9999'; - popup.style.borderRadius = '8px'; - popup.style.padding = '20px'; - popup.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; - popup.style.maxWidth = '300px'; - popup.style.fontFamily = 'Arial, sans-serif'; - popup.style.fontSize = '14px'; - popup.style.top = '50%'; - popup.style.left = '50%'; - popup.style.transform = 'translate(-50%, -50%)'; - popup.style.textAlign = 'center'; - + const popup = document.createElement("div"); + popup.id = "translationLoadingPopup"; + popup.style.position = "fixed"; + popup.style.zIndex = "9999"; + popup.style.borderRadius = "8px"; + popup.style.padding = "20px"; + popup.style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)"; + popup.style.maxWidth = "300px"; + popup.style.fontFamily = "Arial, sans-serif"; + popup.style.fontSize = "14px"; + popup.style.top = "50%"; + popup.style.left = "50%"; + popup.style.transform = "translate(-50%, -50%)"; + popup.style.textAlign = "center"; + // Dark mode detection and styling - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - popup.style.backgroundColor = '#2d2d2d'; - popup.style.color = '#ffffff'; - popup.style.border = '1px solid #444'; + if ( + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches + ) { + popup.style.backgroundColor = "#2d2d2d"; + popup.style.color = "#ffffff"; + popup.style.border = "1px solid #444"; } else { - popup.style.backgroundColor = '#ffffff'; - popup.style.color = '#333333'; - popup.style.border = '1px solid #ddd'; + popup.style.backgroundColor = "#ffffff"; + popup.style.color = "#333333"; + popup.style.border = "1px solid #ddd"; } - + popup.innerHTML = `
Processing...
`; - + // Add the animation - const style = document.createElement('style'); + const style = document.createElement("style"); style.textContent = ` @keyframes spin { 0% { transform: rotate(0deg); } @@ -281,106 +329,85 @@ function showLoadingPopup() { } `; document.head.appendChild(style); - + document.body.appendChild(popup); } // Function to show translation popup function showTranslationPopup(originalText, translatedText) { // Remove loading popup if it exists - const loadingPopup = document.getElementById('translationLoadingPopup'); + const loadingPopup = document.getElementById("translationLoadingPopup"); if (loadingPopup) { document.body.removeChild(loadingPopup); } - const popup = document.createElement('div'); - popup.className = 'hinglish-popup'; - popup.style.position = 'fixed'; - popup.style.zIndex = '9999'; - popup.style.borderRadius = '8px'; - popup.style.padding = '20px'; - popup.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; - popup.style.maxWidth = '400px'; - popup.style.fontFamily = 'Arial, sans-serif'; - popup.style.fontSize = '14px'; - popup.style.top = '50%'; - popup.style.left = '50%'; - popup.style.transform = 'translate(-50%, -50%)'; - - // Dark mode detection and styling - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - popup.style.backgroundColor = '#2d2d2d'; - popup.style.color = '#ffffff'; - popup.style.border = '1px solid #444'; - - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
Translation:
-
${translatedText}
-
-
- -
- `; - } else { - popup.style.backgroundColor = '#ffffff'; - popup.style.color = '#333333'; - popup.style.border = '1px solid #ddd'; - - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
Translation:
-
${translatedText}
-
-
- -
- `; - } - + const popup = document.createElement("div"); + popup.className = "hinglish-popup"; + popup.style.position = "fixed"; + popup.style.zIndex = "9999"; + popup.style.borderRadius = "12px"; + popup.style.padding = "24px"; + popup.style.boxShadow = "0 8px 30px rgba(0, 0, 0, 0.15)"; + popup.style.maxWidth = "420px"; + popup.style.width = "90%"; + popup.style.fontFamily = "Inter, Arial, sans-serif"; + popup.style.fontSize = "15px"; + popup.style.top = "50%"; + popup.style.left = "50%"; + popup.style.transform = "translate(-50%, -50%)"; + popup.style.transition = "all 0.3s ease"; + + // Unified light/dark mode UI + popup.style.backgroundColor = "#ffffff"; + popup.style.color = "#000000"; + popup.style.border = "1px solid #e0e0e0"; + + // Apply modern minimal HTML + popup.innerHTML = ` +
+
Original
+
${originalText}
+
+
+
Translation
+
${translatedText}
+
+
+ +
+`; + document.body.appendChild(popup); - + // Close button functionality - const closeButton = popup.querySelector('#closePopup'); - closeButton.addEventListener('click', () => { + const closeButton = popup.querySelector("#closePopup"); + closeButton.addEventListener("click", () => { document.body.removeChild(popup); }); - + // Hover effect for close button - closeButton.addEventListener('mouseenter', () => { - closeButton.style.background = '#0d5bc1'; + closeButton.addEventListener("mouseenter", () => { + closeButton.style.background = "#0d5bc1"; }); - closeButton.addEventListener('mouseleave', () => { - closeButton.style.background = '#1a73e8'; + closeButton.addEventListener("mouseleave", () => { + closeButton.style.background = "#1a73e8"; }); - + // Close when clicking outside - document.addEventListener('click', function outsideClick(e) { + document.addEventListener("click", function outsideClick(e) { if (!popup.contains(e.target)) { document.body.removeChild(popup); - document.removeEventListener('click', outsideClick); + document.removeEventListener("click", outsideClick); } }); } @@ -388,99 +415,87 @@ function showTranslationPopup(originalText, translatedText) { // Function to show explanation popup function showExplanationPopup(originalText, explanation) { // Remove loading popup if it exists - const loadingPopup = document.getElementById('translationLoadingPopup'); + const loadingPopup = document.getElementById("translationLoadingPopup"); if (loadingPopup) { document.body.removeChild(loadingPopup); } - const popup = document.createElement('div'); - popup.className = 'hinglish-popup'; - popup.style.position = 'fixed'; - popup.style.zIndex = '9999'; - popup.style.borderRadius = '8px'; - popup.style.padding = '20px'; - popup.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; - popup.style.maxWidth = '500px'; - popup.style.fontFamily = 'Arial, sans-serif'; - popup.style.fontSize = '14px'; - popup.style.top = '50%'; - popup.style.left = '50%'; - popup.style.transform = 'translate(-50%, -50%)'; - - // Dark mode detection and styling - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - popup.style.backgroundColor = '#2d2d2d'; - popup.style.color = '#ffffff'; - popup.style.border = '1px solid #444'; - - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
AI Explanation:
-
${explanation}
-
-
- -
- `; - } else { - popup.style.backgroundColor = '#ffffff'; - popup.style.color = '#333333'; - popup.style.border = '1px solid #ddd'; - - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
AI Explanation:
-
${explanation}
-
-
- -
- `; - } - + const popup = document.createElement("div"); + popup.className = "hinglish-popup"; + popup.style.position = "fixed"; + popup.style.zIndex = "9999"; + popup.style.borderRadius = "10px"; + popup.style.padding = "20px"; + popup.style.boxShadow = "0 4px 20px rgba(0,0,0,0.15)"; + popup.style.maxWidth = "500px"; + popup.style.width = "90%"; + popup.style.fontFamily = "Arial, sans-serif"; + popup.style.fontSize = "14px"; + popup.style.top = "50%"; + popup.style.left = "50%"; + popup.style.transform = "translate(-50%, -50%)"; + + // πŸŒ™ Light/Dark Theme Detection + const isDarkMode = + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches; + + popup.style.backgroundColor = isDarkMode ? "#1e1e1e" : "#ffffff"; + popup.style.color = isDarkMode ? "#f1f1f1" : "#000000"; + popup.style.border = isDarkMode ? "1px solid #333333" : "1px solid #e0e0e0"; + + popup.innerHTML = ` +
+
Original Text:
+
${originalText}
+ +
AI Explanation:
+
${explanation}
+
+ +
+ +
+`; + document.body.appendChild(popup); - + // Close button functionality - const closeButton = popup.querySelector('#closePopup'); - closeButton.addEventListener('click', () => { + const closeButton = popup.querySelector("#closePopup"); + closeButton.addEventListener("click", () => { document.body.removeChild(popup); }); - + // Hover effect for close button - closeButton.addEventListener('mouseenter', () => { - closeButton.style.background = '#0d5bc1'; + closeButton.addEventListener("mouseenter", () => { + closeButton.style.background = "#0d5bc1"; }); - closeButton.addEventListener('mouseleave', () => { - closeButton.style.background = '#1a73e8'; + closeButton.addEventListener("mouseleave", () => { + closeButton.style.background = "#1a73e8"; }); - + // Close when clicking outside - document.addEventListener('click', function outsideClick(e) { + document.addEventListener("click", function outsideClick(e) { if (!popup.contains(e.target)) { document.body.removeChild(popup); - document.removeEventListener('click', outsideClick); + document.removeEventListener("click", outsideClick); } }); } @@ -488,31 +503,34 @@ function showExplanationPopup(originalText, explanation) { // Function to show error popup function showErrorPopup(errorMessage) { // Remove loading popup if it exists - const loadingPopup = document.getElementById('translationLoadingPopup'); + const loadingPopup = document.getElementById("translationLoadingPopup"); if (loadingPopup) { document.body.removeChild(loadingPopup); } - const popup = document.createElement('div'); - popup.className = 'hinglish-popup'; - popup.style.position = 'fixed'; - popup.style.zIndex = '9999'; - popup.style.borderRadius = '8px'; - popup.style.padding = '20px'; - popup.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)'; - popup.style.maxWidth = '300px'; - popup.style.fontFamily = 'Arial, sans-serif'; - popup.style.fontSize = '14px'; - popup.style.top = '50%'; - popup.style.left = '50%'; - popup.style.transform = 'translate(-50%, -50%)'; - + const popup = document.createElement("div"); + popup.className = "hinglish-popup"; + popup.style.position = "fixed"; + popup.style.zIndex = "9999"; + popup.style.borderRadius = "8px"; + popup.style.padding = "20px"; + popup.style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)"; + popup.style.maxWidth = "300px"; + popup.style.fontFamily = "Arial, sans-serif"; + popup.style.fontSize = "14px"; + popup.style.top = "50%"; + popup.style.left = "50%"; + popup.style.transform = "translate(-50%, -50%)"; + // Dark mode detection and styling - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - popup.style.backgroundColor = '#2d2d2d'; - popup.style.color = '#ffffff'; - popup.style.border = '1px solid #444'; - + if ( + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches + ) { + popup.style.backgroundColor = "#2d2d2d"; + popup.style.color = "#ffffff"; + popup.style.border = "1px solid #444"; + popup.innerHTML = `
Error:
@@ -532,10 +550,10 @@ function showErrorPopup(errorMessage) {
`; } else { - popup.style.backgroundColor = '#ffffff'; - popup.style.color = '#333333'; - popup.style.border = '1px solid #ddd'; - + popup.style.backgroundColor = "#ffffff"; + popup.style.color = "#333333"; + popup.style.border = "1px solid #ddd"; + popup.innerHTML = `
Error:
@@ -555,27 +573,27 @@ function showErrorPopup(errorMessage) {
`; } - + document.body.appendChild(popup); - + // Close button functionality - const closeButton = popup.querySelector('#closePopup'); - closeButton.addEventListener('click', () => { + const closeButton = popup.querySelector("#closePopup"); + closeButton.addEventListener("click", () => { document.body.removeChild(popup); }); - + // Hover effect for close button - closeButton.addEventListener('mouseenter', () => { - closeButton.style.background = '#c5221f'; + closeButton.addEventListener("mouseenter", () => { + closeButton.style.background = "#c5221f"; }); - closeButton.addEventListener('mouseleave', () => { - closeButton.style.background = '#d93025'; + closeButton.addEventListener("mouseleave", () => { + closeButton.style.background = "#d93025"; }); - + // Auto close after 5 seconds setTimeout(() => { if (document.body.contains(popup)) { document.body.removeChild(popup); } }, 5000); -} \ No newline at end of file +} diff --git a/popup/popup.css b/popup/popup.css index 36a70a5..99d348d 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -1,225 +1,210 @@ :root { - --text-color: #202124; + --text-color: #1e1e1e; --bg-color: #ffffff; - --border-color: #e0e0e0; - --section-bg: #f8f9fa; - --input-border: #dadce0; - --button-bg: #1a73e8; - --button-hover: #1557b0; - --button-active: #174ea6; - --success-bg: #e6f4ea; - --success-color: #0b8043; - --error-bg: #fce8e6; - --error-color: #d93025; - --label-color: #5f6368; + --border-color: #e2e2e2; + --section-bg: #f4f5f7; + --input-border: #d0d7de; + --button-bg: #4f46e5; + --button-hover: #4338ca; + --button-active: #3730a3; + --success-bg: #e6fffa; + --success-color: #047857; + --error-bg: #fff1f2; + --error-color: #be123c; + --label-color: #4b5563; + --font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + --popup-width: 400px; + --popup-height: 500px; } @media (prefers-color-scheme: dark) { :root { - --text-color: #e8eaed; - --bg-color: #202124; - --border-color: #3c4043; - --section-bg: #2d2e31; - --input-border: #3c4043; - --button-bg: #1a73e8; - --button-hover: #1557b0; - --button-active: #174ea6; + --text-color: #f5f5f5; + --bg-color: #1f1f1f; + --border-color: #3f3f3f; + --section-bg: #292929; + --input-border: #4b5563; + --button-bg: #6366f1; + --button-hover: #4f46e5; + --button-active: #4338ca; --success-bg: #1e3a2f; - --success-color: #81c995; + --success-color: #6ee7b7; --error-bg: #3c1a1a; - --error-color: #f28b82; - --label-color: #9aa0a6; + --error-color: #f87171; + --label-color: #d1d5db; } } +/* --- Base Layout --- */ body { - width: 400px; - padding: 20px; - font-family: Arial, sans-serif; - background: var(--bg-color); - color: var(--text-color); -} - -.container { - display: flex; - flex-direction: column; - gap: 20px; -} - -h1 { margin: 0; - color: var(--button-bg); - font-size: 24px; - text-align: center; -} - -h2 { - margin: 0 0 15px 0; + font-family: var(--font-family); + background-color: var(--bg-color); color: var(--text-color); - font-size: 18px; -} - -.section { - background: var(--section-bg); - padding: 15px; - border-radius: 8px; + width: var(--popup-width); + height: var(--popup-height); + overflow-y: auto; + padding: 16px; + animation: fadeIn 0.4s ease-in-out; +} + +/* --- Sections --- */ +.popup-header, +/* .settings-section, */ +.instructions, +.api-key-form { + background-color: var(--section-bg); + padding: 12px 16px; + margin-bottom: 12px; + border-radius: 10px; + border: 1px solid var(--border-color); + animation: fadeSlideUp 0.3s ease-in-out; +} +.settings-selection { + background-color: var(--bg-color); + color: #282828; + padding: 12px 16px; + margin-bottom: 12px; + border-radius: 10px; border: 1px solid var(--border-color); + animation: fadeSlideUp 0.3s ease-in-out; } -.input-group { - display: flex; - gap: 8px; - margin-bottom: 10px; +.title { + font-size: 20px; + margin-bottom: 6px; + font-weight: bold; } -.input-group input { - flex-grow: 1; - padding: 8px; - border: 1px solid var(--input-border); - border-radius: 4px; - font-family: monospace; - background: var(--bg-color); - color: var(--text-color); -} - -.button-group { - display: flex; - gap: 8px; - margin-top: 10px; +.api-key-status { + font-size: 14px; + margin-bottom: 8px; + color: var(--label-color); } -button { - padding: 8px 16px; - background: var(--button-bg); - color: white; +/* --- Buttons --- */ +.button { + padding: 8px 14px; border: none; - border-radius: 4px; + border-radius: 8px; + background-color: var(--button-bg); + color: #fff; cursor: pointer; + transition: background-color 0.2s ease, transform 0.1s ease; font-size: 14px; - transition: background-color 0.2s; -} - -button:hover { - background: var(--button-hover); -} - -button:active { - background: var(--button-active); + margin: 4px; } -#saveApiKey { - background: var(--success-color); +.button:hover { + background-color: var(--button-hover); + transform: scale(1.03); } -#saveApiKey:hover { - background: var(--success-color); - opacity: 0.9; +.button:active { + background-color: var(--button-active); + transform: scale(0.98); } -#changeApiKey { - background: var(--button-bg); -} - -#removeApiKey { - background: var(--error-color); +.button.secondary { + background-color: transparent; + border: 1px solid var(--button-bg); + color: var(--button-bg); } -#removeApiKey:hover { - background: var(--error-color); - opacity: 0.9; +.button.secondary:hover { + background-color: var(--button-hover); + color: white; } -.setting-group { - margin-bottom: 15px; +.primary { + margin-top: 10px; + width: 100%; } -.setting-group label { - display: block; - margin-bottom: 5px; - color: var(--label-color); - font-size: 14px; +/* --- Inputs & Selects --- */ +.input-wrapper { + display: flex; + gap: 8px; + margin-bottom: 8px; } +input[type="password"], select { - width: 100%; padding: 8px; + font-size: 14px; + width: 100%; border: 1px solid var(--input-border); - border-radius: 4px; - background: var(--bg-color); + border-radius: 6px; + background-color: var(--bg-color); color: var(--text-color); - font-size: 14px; + transition: border 0.2s ease, box-shadow 0.2s ease; } -.status { - font-size: 14px; - margin-top: 8px; - color: var(--label-color); +input:focus, +select:focus { + outline: none; + border-color: var(--button-bg); + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); } -#statusMessage { - text-align: center; - padding: 10px; - border-radius: 4px; -} - -#statusMessage.success { - background: var(--success-bg); - color: var(--success-color); +.icon-button { + background: none; + border: none; + font-size: 16px; + cursor: pointer; } -#statusMessage.error { - background: var(--error-bg); - color: var(--error-color); +/* --- Labels and Grouping --- */ +label { + display: block; + margin-bottom: 4px; + color: var(--label-color); } -ol { - margin: 0; - padding-left: 20px; +.form-group { + margin-bottom: 12px; } -li { - margin-bottom: 8px; - color: var(--label-color); - font-size: 14px; +.button-group { + display: flex; + gap: 8px; + flex-wrap: wrap; } -li:last-child { - margin-bottom: 0; +.hidden { + display: none !important; } -/* Success and Error Messages */ -.success-message, -.error-message { - position: fixed; - top: 20px; - left: 50%; - transform: translateX(-50%); - padding: 12px 24px; - border-radius: 4px; +/* --- Instructions --- */ +.instructions ol, +.instructions ul { + padding-left: 18px; font-size: 14px; - font-weight: 500; - z-index: 1000; - animation: slideDown 0.3s ease-out; - box-shadow: 0 2px 8px var(--shadow-color); } -.success-message { - background-color: var(--success-color); - color: white; +.instructions li { + margin-bottom: 6px; } -.error-message { - background-color: var(--error-color); - color: white; +/* --- Animations --- */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } } -@keyframes slideDown { +@keyframes fadeSlideUp { from { - transform: translate(-50%, -100%); opacity: 0; + transform: translateY(10px); } to { - transform: translate(-50%, 0); opacity: 1; + transform: translateY(0px); } -} \ No newline at end of file +} diff --git a/popup/popup.html b/popup/popup.html index fdc02d7..729c372 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -1,67 +1,75 @@ - - - Hinglish Translator - - - -
-
-

Hinglish Translator

-
-
No API key configured
-
+ + + + + Hinglish Translator + + + + -
+ - + -
-

Translation Settings

-
- - -
-
- - -
- -
+
+

βš™οΈ Translation Settings

+
+ + +
+
+ + +
+ +
-
-

How to Use

-
    -
  1. Select text on any webpage
  2. -
  3. Right-click and choose: -
      -
    • "Translate to Hinglish" for translation
    • -
    • "Explain in Hinglish" for AI explanation
    • -
    -
  4. -
  5. The translation/explanation will appear in a popup
  6. -
+
+

πŸ“˜ How to Use

+
    +
  1. Select text on any webpage
  2. +
  3. + Right-click and choose: +
      +
    • Translate to Hinglish
    • +
    • Explain in Hinglish
    • +
    +
  4. +
  5. See the output in a popup
  6. +
+
-
- - - \ No newline at end of file + + + + diff --git a/popup/welcome.css b/popup/welcome.css new file mode 100644 index 0000000..94a625f --- /dev/null +++ b/popup/welcome.css @@ -0,0 +1,148 @@ +/* Reset & Base styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body { + font-family: "Segoe UI", Roboto, sans-serif; + background-color: #f9f9f9; + color: #333; + height: 100%; + line-height: 1.6; +} + +/* Dark mode support */ +@media (prefers-color-scheme: dark) { + html, + body { + background-color: #121212; + color: #eee; + } + + input, + .primary-button { + background-color: #1e1e1e; + color: #fff; + border: 1px solid #333; + } + + .setup-box { + background-color: #1a1a1a; + box-shadow: 0 4px 16px rgba(255, 255, 255, 0.05); + } + + a { + color: #4ea1f2; + } + + a:hover { + color: #7dbbff; + } +} + + +/* Container */ +.container { + width: 100%; + max-width: 100%; + padding: 16px; + min-width: 360px; /* Ensure it looks good in extension popup */ +} + + + +.setup{ + background: #fff; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); +} + +/* Header */ +h1 { + font-size: 24px; + margin-bottom: 6px; + font-weight: 600; + text-align: center; + +} + +.subtitle { + font-size: 15px; + text-align: center; + color: #666; + margin-bottom: 30px; +} + +.setup-intro{ + color: #121212; +} + + +.input-label { + display: block; + margin-bottom: 8px; + font-weight: 500; + font-size: 14px; + color: #555; +} + +input[type="password"] { + width: 100%; + padding: 10px 12px; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 15px; + margin-bottom: 20px; + transition: border-color 0.2s; +} + +input[type="password"]:focus { + outline: none; + border-color: #1a73e8; +} + +/* Button */ +.primary-button { + display: block; + width: 100%; + padding: 10px 16px; + background-color: #1a73e8; + color: white; + font-size: 15px; + font-weight: 500; + border: none; + border-radius: 6px; + cursor: pointer; + transition: background 0.3s; + margin-bottom: 20px; +} + +.primary-button:hover { + background-color: #1664cc; +} + +/* Notes & Links */ +.note { + font-size: 13px; + color: #777; + margin-bottom: 8px; + text-align: center; +} + +.note a { + color: #1a73e8; + text-decoration: none; + font-weight: 500; +} + +.note a:hover { + text-decoration: underline; +} + +.small { + font-size: 12px; +} diff --git a/popup/welcome.html b/popup/welcome.html index 6b94e00..12b1ee7 100644 --- a/popup/welcome.html +++ b/popup/welcome.html @@ -1,20 +1,73 @@ + + + + Setup Groq API Key – Hinglish Translator + + + + +
+

Welcome to Hinglish Translator

+ +
+

+ Please enter your Groq API key to enable + translations: +

+ + + + + +

+ πŸ” Your key will be stored locally in Chrome and only + used for translation requests. +

+ +

+ Don’t have a key? + Get one from Groq +

+
+
+ + + + + + +