diff --git a/background.js b/background.js
index 3488114..740784f 100644
--- a/background.js
+++ b/background.js
@@ -35,63 +35,81 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
});
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
- if (info.menuItemId === "translateToHinglish" && info.selectionText) {
+ if (info.menuItemId === "translateToHinglish") {
try {
- // Show loading popup
- chrome.scripting.executeScript({
+ // 1. Show loading popup
+ await chrome.scripting.executeScript({
target: { tabId: tab.id },
- func: showLoadingPopup,
- args: []
+ func: showLoadingPopup
});
- const translatedText = await translateText(info.selectionText);
-
- // Remove loading popup and show translation
- chrome.scripting.executeScript({
+ // 2. Enhanced selection extraction (handles
elements gracefully)
+ const [{ result: selectedText }] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
- func: showTranslationPopup,
- args: [info.selectionText, translatedText]
- });
- } catch (error) {
- console.error("Context menu translation error:", error);
- // Show error in popup
- chrome.scripting.executeScript({
- target: { tabId: tab.id },
- func: showErrorPopup,
- args: [error.message]
- });
- }
- } else if (info.menuItemId === "explainInHinglish" && info.selectionText) {
- try {
- // Show loading popup
- chrome.scripting.executeScript({
- target: { tabId: tab.id },
- func: showLoadingPopup,
- args: []
+ func: () => {
+ const selection = window.getSelection();
+ if (!selection || selection.rangeCount === 0) return '';
+
+ const range = selection.getRangeAt(0);
+ const fragment = range.cloneContents();
+ const container = document.createElement('div');
+ container.appendChild(fragment);
+
+ // Gather text with bullets if it's a list
+ const items = container.querySelectorAll('li');
+ if (items.length > 0) {
+ return Array.from(items).map(li => '• ' + li.innerText.trim()).join('\n');
+ }
+
+ // Fallback to plain text with preserved line breaks
+ return container.innerText || selection.toString();
+ }
});
- const explanation = await explainText(info.selectionText);
-
- // Remove loading popup and show explanation
- chrome.scripting.executeScript({
+ if (!selectedText || selectedText.trim() === '') {
+ throw new Error("No text selected");
+ }
+
+ // 3. Format newline characters
+ const formattedText = selectedText.replace(/\n/g, '\n\n');
+
+ // 4. Translate the structured list text
+ const translatedText = await translateText(formattedText);
+
+ // 5. Show the translation popup
+ await chrome.scripting.executeScript({
target: { tabId: tab.id },
- func: showExplanationPopup,
- args: [info.selectionText, explanation]
+ func: showTranslationPopup,
+ args: [selectedText, translatedText]
});
+
} catch (error) {
- console.error("Context menu explanation error:", error);
- // Show error in popup
- chrome.scripting.executeScript({
+ console.error("Translation error:", error);
+ await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: showErrorPopup,
- args: [error.message]
+ args: [error.message || "Something went wrong"]
});
}
}
});
+const promptCache={};
+
// Function to get translation prompt based on style and level
function getTranslationPrompt(style, level) {
+
+
+ const structureInstruction = `
+ Keep the original structure, including:
+- Bullet points
+- Line breaks
+- Paragraphs
+- Indentation (if any)
+
+Do not remove or reorder items.
+Only respond with the translated text — no comments or explanations.
+`;
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.",
@@ -120,7 +138,8 @@ function getTranslationPrompt(style, level) {
}
};
- return prompts[style][level] || prompts.hinglish.balanced;
+ return basePrompt=prompts[style]?.[level] || prompts.hinglish.balanced;
+
}
// Function to translate text using Groq API
@@ -133,7 +152,13 @@ async function translateText(text) {
const style = translationSettings?.style || 'hinglish';
const level = translationSettings?.level || 'balanced';
- const prompt = getTranslationPrompt(style, level);
+ const baseprompt = getTranslationPrompt(style, level);
+
+ const prompt = `
+${basePrompt}
+
+If multiple sentences are provided separated by "--SPLIT--", translate each one individually in the same order. Separate translations using "---SPLIT---".
+ `.trim()
try {
const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {
@@ -286,6 +311,121 @@ function showLoadingPopup() {
}
// Function to show translation popup
+function showTranslationPopup(originalText, translatedText) {
+ // Remove loading popup if it exists
+ const loadingPopup = document.getElementById('translationLoadingPopup');
+ if (loadingPopup) {
+ document.body.removeChild(loadingPopup);
+ }
+
+ // Remove existing popup if any
+ const oldPopup = document.querySelector('.hinglish-popup');
+ if (oldPopup) {
+ document.body.removeChild(oldPopup);
+ }
+
+ // Create popup container
+ 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 isDarkMode = window.matchMedia &&
+ window.matchMedia('(prefers-color-scheme: dark)').matches;
+
+ popup.style.backgroundColor = isDarkMode ? '#2d2d2d' : '#ffffff';
+ popup.style.color = isDarkMode ? '#ffffff' : '#333333';
+ popup.style.border = isDarkMode ? '1px solid #444' : '1px solid #ddd';
+
+ // === Content container
+ const content = document.createElement('div');
+
+ const originalLabel = document.createElement('div');
+ originalLabel.style.fontWeight = 'bold';
+ originalLabel.style.marginBottom = '8px';
+ originalLabel.style.color = isDarkMode ? '#aaa' : '#666';
+ originalLabel.textContent = 'Original Text:';
+
+ const originalBox = document.createElement('div');
+ originalBox.style.background = isDarkMode ? '#3a3a3a' : '#f5f5f5';
+ originalBox.style.padding = '12px';
+ originalBox.style.borderRadius = '6px';
+ originalBox.style.marginBottom = '15px';
+ originalBox.style.lineHeight = '1.5';
+ originalBox.style.whiteSpace = 'pre-wrap';
+ originalBox.textContent = originalText;
+
+ const translatedLabel = document.createElement('div');
+ translatedLabel.style.fontWeight = 'bold';
+ translatedLabel.style.marginBottom = '8px';
+ translatedLabel.style.color = isDarkMode ? '#aaa' : '#666';
+ translatedLabel.textContent = 'Translation:';
+
+ const translatedBox = document.createElement('div');
+ translatedBox.style.background = isDarkMode ? '#1a3d6d' : '#e8f0fe';
+ translatedBox.style.padding = '12px';
+ translatedBox.style.borderRadius = '6px';
+ translatedBox.style.marginBottom = '15px';
+ translatedBox.style.lineHeight = '1.5';
+ translatedBox.style.whiteSpace = 'pre-wrap';
+ translatedBox.textContent = translatedText;
+
+ // Append to content
+ content.appendChild(originalLabel);
+ content.appendChild(originalBox);
+ content.appendChild(translatedLabel);
+ content.appendChild(translatedBox);
+
+ // === Close button
+ const buttonWrapper = document.createElement('div');
+ buttonWrapper.style.textAlign = 'right';
+
+ const closeButton = document.createElement('button');
+ closeButton.textContent = 'Close';
+ closeButton.style.cursor = 'pointer';
+ closeButton.style.padding = '8px 16px';
+ closeButton.style.background = '#1a73e8';
+ closeButton.style.color = 'white';
+ closeButton.style.border = 'none';
+ closeButton.style.borderRadius = '4px';
+ closeButton.style.fontSize = '14px';
+ closeButton.style.transition = 'background 0.2s';
+
+ closeButton.addEventListener('mouseenter', () => {
+ closeButton.style.background = '#0d5bc1';
+ });
+ closeButton.addEventListener('mouseleave', () => {
+ closeButton.style.background = '#1a73e8';
+ });
+ closeButton.addEventListener('click', () => {
+ document.body.removeChild(popup);
+ });
+
+ buttonWrapper.appendChild(closeButton);
+
+ // Final assembly
+ popup.appendChild(content);
+ popup.appendChild(buttonWrapper);
+ document.body.appendChild(popup);
+
+ // Optional: click outside to close
+ document.addEventListener('click', function outsideClick(e) {
+ if (!popup.contains(e.target)) {
+ document.body.removeChild(popup);
+ document.removeEventListener('click', outsideClick);
+ }
+ });
+}
+
function showTranslationPopup(originalText, translatedText) {
// Remove loading popup if it exists
const loadingPopup = document.getElementById('translationLoadingPopup');
diff --git a/content.js b/content.js
index cf158c9..14c24e7 100644
--- a/content.js
+++ b/content.js
@@ -75,49 +75,64 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
return translatedCount;
}
-
+
+ //translate all text
// Translate all text nodes (more aggressive approach)
- async function translateAllText() {
- const walker = document.createTreeWalker(
- document.body,
- NodeFilter.SHOW_TEXT,
- null,
- false
- );
-
- let node;
- const textNodes = [];
-
- while (node = walker.nextNode()) {
- if (node.textContent.trim() &&
- node.parentElement &&
- !node.parentElement.classList.contains('hinglish-translated')) {
- textNodes.push(node);
- }
+async function translateAllText(batchSize = 3) {
+ console.time('translateAllText');
+ const walker = document.createTreeWalker(
+ document.body,
+ NodeFilter.SHOW_TEXT,
+ null,
+ false
+ );
+
+ let node;
+ const textNodes = [];
+
+ // Collect text nodes that should be translated
+ while (node = walker.nextNode()) {
+ if (
+ node.textContent.trim() &&
+ node.parentElement &&
+ !node.parentElement.classList.contains('hinglish-translated')
+ ) {
+ textNodes.push(node);
}
-
- let translatedCount = 0;
-
- for (const node of textNodes) {
- const originalText = node.textContent;
-
- try {
- const response = await chrome.runtime.sendMessage({
- action: "translateText",
- text: originalText
- });
-
- if (response && response !== "Please configure your API key first") {
- node.textContent = response;
- if (node.parentElement) {
- node.parentElement.classList.add('hinglish-translated');
+ }
+
+ let translatedCount = 0;
+
+ // Process in batches
+ for (let i = 0; i < textNodes.length; i += batchSize) {
+ const batch = textNodes.slice(i, i + batchSize);
+ const originalText = batch.map(n => n.textContent.trim());
+ const combineText = originalText.join("\n--SPLIT--\n");
+
+ try {
+ const response = await chrome.runtime.sendMessage({
+ action: "translateText",
+ text: combineText
+ });
+
+ if (response && response !== "Please configure your API key first") {
+ const translatedParts = response.split("\n---SPLIT---\n");
+
+ batch.forEach((node, index) => {
+ const translated = translatedParts[index]?.trim();
+ if (translated) {
+ node.textContent = translated;
+ node.parentElement?.classList.add("hinglish-translated");
+ translatedCount++;
}
- translatedCount++;
- }
- } catch (error) {
- console.error('Translation error:', error);
+ });
}
+ } catch (error) {
+ console.error("Batch translation error:", error);
}
-
- return translatedCount;
- }
\ No newline at end of file
+ }
+ console.timeEnd("translateAllText");
+ return translatedCount;
+}
+
+
\ No newline at end of file