From f80def071de1679e1a60f765f686b1b47787055c Mon Sep 17 00:00:00 2001 From: ronitrai27 <108011380+ronitrai27@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:55:51 +0530 Subject: [PATCH 1/2] WIP: Initial popup and welcome UI tweaks --- background.js | 495 +++++++++++++++++++++++++-------------------- popup/popup.css | 297 +++++++++++++-------------- popup/popup.html | 128 ++++++------ popup/welcome.css | 148 ++++++++++++++ popup/welcome.html | 87 ++++++-- 5 files changed, 703 insertions(+), 452 deletions(-) create mode 100644 popup/welcome.css diff --git a/background.js b/background.js index 3488114..817c8c5 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,38 +329,41 @@ 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%)'; - + 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'; - + 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:
@@ -334,10 +385,10 @@ function showTranslationPopup(originalText, translatedText) {
`; } 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 = `
Original Text:
@@ -359,28 +410,28 @@ function showTranslationPopup(originalText, 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,31 +439,34 @@ 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%)'; - + 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'; - + 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:
@@ -434,10 +488,10 @@ function showExplanationPopup(originalText, explanation) {
`; } 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 = `
Original Text:
@@ -459,28 +513,28 @@ function showExplanationPopup(originalText, 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 +542,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 +589,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 +612,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 +

+
+
+ + + + + + + From b04d5a8d43792652e20007ecb0d7b05c85beae3e Mon Sep 17 00:00:00 2001 From: ronitrai27 <108011380+ronitrai27@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:18:13 +0530 Subject: [PATCH 2/2] Enhance translation and explanation popups with modern minimal UI and theme support --- background.js | 189 ++++++++++++++++++++------------------------------ 1 file changed, 75 insertions(+), 114 deletions(-) diff --git a/background.js b/background.js index 817c8c5..8c0a63a 100644 --- a/background.js +++ b/background.js @@ -345,71 +345,47 @@ function showTranslationPopup(originalText, translatedText) { 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.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"; - // 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"; + // Unified light/dark mode UI + popup.style.backgroundColor = "#ffffff"; + popup.style.color = "#000000"; + popup.style.border = "1px solid #e0e0e0"; - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
Translation:
-
${translatedText}
-
-
- -
- `; - } + // Apply modern minimal HTML + popup.innerHTML = ` +
+
Original
+
${originalText}
+
+
+
Translation
+
${translatedText}
+
+
+ +
+`; document.body.appendChild(popup); @@ -448,71 +424,56 @@ function showExplanationPopup(originalText, explanation) { popup.className = "hinglish-popup"; popup.style.position = "fixed"; popup.style.zIndex = "9999"; - popup.style.borderRadius = "8px"; + popup.style.borderRadius = "10px"; popup.style.padding = "20px"; - popup.style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)"; + 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%)"; - // Dark mode detection and styling - if ( + // πŸŒ™ Light/Dark Theme Detection + const isDarkMode = window.matchMedia && - window.matchMedia("(prefers-color-scheme: dark)").matches - ) { - popup.style.backgroundColor = "#2d2d2d"; - popup.style.color = "#ffffff"; - popup.style.border = "1px solid #444"; + window.matchMedia("(prefers-color-scheme: dark)").matches; - popup.innerHTML = ` -
-
Original Text:
-
${originalText}
-
AI Explanation:
-
${explanation}
-
-
- -
- `; - } else { - popup.style.backgroundColor = "#ffffff"; - popup.style.color = "#333333"; - popup.style.border = "1px solid #ddd"; + 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}
-
-
- -
- `; - } + popup.innerHTML = ` +
+
Original Text:
+
${originalText}
+ +
AI Explanation:
+
${explanation}
+
+ +
+ +
+`; document.body.appendChild(popup);