NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Translate Selection Sidebar and Tooltip // @namespace https://aveusaid.wordpress.com // @version 0.9082024 // @description Translate selected text, show in a tooltip, add to a sidebar list, and store in local storage // @author Usaid Bin Khalid Khan // @match *://*/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; // Add CSS for the tooltip, sidebar, buttons, and dark mode text const styleElement = document.createElement('style'); styleElement.type = 'text/css'; styleElement.innerHTML = ` .translator-tooltip { font-weight: 700; color: #000000; position: absolute; z-index: 10000; padding: 8px 12px; max-width: 300px; border-radius: 0.3em; background-color: #ffffdb; border: 1px solid #ccc; box-shadow: 0 2px 4px rgba(0,0,0,0.2); text-align: center; font-size: 18px; line-height: 1.4; visibility: hidden; opacity: 0; transition: visibility 0s linear 300ms, opacity 300ms; } .translator-tooltip.visible { visibility: visible; opacity: 1; } .translator-sidebar { position: fixed; top: 0; right: 0; width: 280px; height: 100%; background-color: #f7f7f7; overflow-y: auto; border-left: 2px solid #3388CC; padding: 20px; z-index: 10000; font-size: 16px; overflow: auto; box-shadow: -2px 0 5px rgba(0,0,0,0.1); box-sizing: border-box; resize: horizontal; min-width: 200px; max-width: 500px; display: none; } .translator-sidebar > * { margin-bottom: 20px; } .translator-entry { display: flex; flex-direction: column; padding: 10px 0; margin-bottom: 10px; border-bottom: 1px solid #ccc; } .translator-entry span:first-child { margin-bottom: 6px; font-weight: bold; color: #333; } .translator-entry span:last-child { color: #666; } .close-sidebar { position: absolute; top: 10px; right: 10px; cursor: pointer; font-weight: bold; color: #555; font-size: 20px; background-color: transparent; border: none; z-index: 10001; } .attractive-text { display: block; font-size: 14px; font-style: italic; text-decoration: none; color: #ff6666; margin-top: 20px; text-align: center; } .attractive-text:hover { color: #ff3333; } .clear-button, .copy-all-button, .mode-toggle { cursor: pointer; padding: 10px 15px; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); font-size: 16px; margin: 10px 0; width: calc(100% - 20px); box-sizing: border-box; } .clear-button { margin-top: 20px; } .mode-toggle { display: flex; align-items: center; margin-top: 20px; } .mode-toggle span { margin-right: 10px; } .close-sidebar { position: absolute; top: 10px; right: 10px; cursor: pointer; font-weight: bold; color: #555; font-size: 20px; background-color: transparent; border: none; z-index: 10001; } .attractive-text { display: block; font-size: 14px; font-style: italic; text-decoration: none; color: #ff6666; margin-top: 20px; text-align: center; } .attractive-text:hover { color: #ff3333; } .error-message { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; border-radius: 5px; z-index: 10002; font-size: 18px; text-align: center; visibility: hidden; opacity: 0; transition: visibility 0s linear 300ms, opacity 300ms; } .error-message.visible { visibility: visible; opacity: 1; } .open-sidebar-button { position: fixed; bottom: 140px; right: 20px; z-index: 10001; cursor: pointer; padding: 10px 15px; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); font-size: 16px; display: block; } .empty-sidebar { font-style: italic; color: grey; margin-top: 20px; text-align: center; } .dark-mode .translator-sidebar { background-color: #333333; /* Dark background color */ color: #ffffff; /* White text */ } .dark-mode .translator-entry span:first-child { color: #FFD700; /* Sunset yellow */ } .dark-mode .translator-entry span:last-child { color: #ffffff; /* White */ } .dark-mode .clear-button, .dark-mode .open-sidebar-button, .dark-mode .copy-all-button { background-color: #444; color: #FFD700; /* Sunset yellow */ border-color: #666; } `; document.head.appendChild(styleElement); // Create tooltip element const tooltip = document.createElement('div'); tooltip.className = 'translator-tooltip'; document.body.appendChild(tooltip); // Create sidebar element const sidebar = document.createElement('div'); sidebar.className = 'translator-sidebar resizable'; document.body.appendChild(sidebar); // Create close button for sidebar const closeButton = document.createElement('button'); closeButton.innerHTML = '×'; closeButton.className = 'close-sidebar'; closeButton.setAttribute('aria-label', 'Close sidebar'); sidebar.appendChild(closeButton); // Create "Cogito, Ergo Sum" text const attractText = document.createElement('a'); attractText.innerHTML = 'Cogito, Ergo Sum'; attractText.href = 'https://aveusaid.wordpress.com'; attractText.className = 'attractive-text'; attractText.setAttribute('role', 'link'); attractText.setAttribute('aria-label', 'Cogito, Ergo Sum'); sidebar.appendChild(attractText); // Add two italic lines for empty sidebar const emptyLines = document.createElement('div'); emptyLines.className = 'empty-sidebar'; emptyLines.innerHTML = ` <i>The Archives are empty.</i> <br> <i>Select some text to add it to the Translation Archives.</i> `; sidebar.appendChild(emptyLines); // Create clear button const clearButton = document.createElement('button'); clearButton.textContent = 'Tabula Rasa'; clearButton.className = 'clear-button'; clearButton.setAttribute('aria-label', 'Clear translations'); sidebar.appendChild(clearButton); // Create copy all button const copyAllButton = document.createElement('button'); copyAllButton.textContent = 'Copy All'; copyAllButton.className = 'copy-all-button'; copyAllButton.setAttribute('aria-label', 'Copy all translations'); sidebar.appendChild(copyAllButton); // Create mode toggle button const modeToggleButton = document.createElement('button'); modeToggleButton.className = 'mode-toggle'; modeToggleButton.textContent = 'Dim the Lights'; sidebar.appendChild(modeToggleButton); // Create open sidebar button const openSidebarButton = document.createElement('button'); openSidebarButton.textContent = 'The Archive'; openSidebarButton.className = 'open-sidebar-button'; openSidebarButton.setAttribute('aria-label', 'Open translation archive'); document.body.appendChild(openSidebarButton); // Error message element const errorMessage = document.createElement('div'); errorMessage.className = 'error-message'; document.body.appendChild(errorMessage); let translations = []; function showError(message) { errorMessage.textContent = message; errorMessage.classList.add('visible'); setTimeout(() => { errorMessage.classList.remove('visible'); }, 3000); } function translateText(text) { return fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q=${encodeURIComponent(text)}`) .then(response => response.json()) .then(result => result[0][0][0]) .catch(error => { showError('ERROR: The Network Towers Have Fallen!'); console.error(error); }); } function updateSidebar() { sidebar.innerHTML = ''; sidebar.appendChild(closeButton); sidebar.appendChild(attractText); if (translations.length === 0) { sidebar.appendChild(emptyLines); } else { translations.forEach(({ original, translated }) => { const entry = document.createElement('div'); entry.className = 'translator-entry'; entry.innerHTML = ` <span>${original}</span> <span>${translated}</span> `; sidebar.appendChild(entry); }); } sidebar.appendChild(clearButton); sidebar.appendChild(copyAllButton); sidebar.appendChild(modeToggleButton); } function toggleSidebar() { if (sidebar.style.display === 'block') { sidebar.style.display = 'none'; openSidebarButton.style.display = 'block'; // Show "The Archive" button } else { updateSidebar(); sidebar.style.display = 'block'; openSidebarButton.style.display = 'none'; // Hide "The Archive" button } } function clearTranslations() { translations = []; updateSidebar(); } function copyAllTranslations() { const allTranslations = translations.map(({ original, translated }) => `${original}: ${translated}`).join('\n'); navigator.clipboard.writeText(allTranslations).then(() => { showError('Translations copied to clipboard!'); }).catch(() => { showError('Failed to copy translations.'); }); } function toggleDarkMode() { document.body.classList.toggle('dark-mode'); const isDarkMode = document.body.classList.contains('dark-mode'); modeToggleButton.textContent = isDarkMode ? 'Light the Way' : 'Dim the Lights'; } document.addEventListener('mouseup', async (e) => { if (window.getSelection().toString()) { const selectedText = window.getSelection().toString(); const translatedText = await translateText(selectedText); translations.push({ original: selectedText, translated: translatedText }); updateSidebar(); tooltip.textContent = translatedText; tooltip.style.left = `${e.pageX}px`; tooltip.style.top = `${e.pageY + 10}px`; tooltip.classList.add('visible'); setTimeout(() => { tooltip.classList.remove('visible'); }, 3000); } }); openSidebarButton.addEventListener('click', toggleSidebar); closeButton.addEventListener('click', toggleSidebar); clearButton.addEventListener('click', clearTranslations); copyAllButton.addEventListener('click', copyAllTranslations); modeToggleButton.addEventListener('click', toggleDarkMode); })();