Last active
April 21, 2025 23:29
-
-
Save mitry/8d1c40f7b9c7a246b9de6973fbf5a126 to your computer and use it in GitHub Desktop.
Умный Переводчик (Via)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Умный Переводчик (Via) | |
| // @namespace http://tampermonkey.net/ | |
| // @version 6.2.1 | |
| // @description Фикс позиционирования над панелью браузера | |
| // @match *://*/* | |
| // @grant GM_xmlhttpRequest | |
| // @connect * | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| const id = 'via-trans-popup'; | |
| const config = { | |
| pressDuration: 600, | |
| maxWordLength: 25, | |
| source: { | |
| url: (word, fromLang) => `https://api.mymemory.translated.net/get?q=${encodeURIComponent(word)}&langpair=${fromLang}|${fromLang === 'en' ? 'ru' : 'en'}`, | |
| parser: res => { | |
| try { | |
| const data = JSON.parse(res); | |
| return data.responseData.translatedText || ''; | |
| } catch (e) { | |
| return ''; | |
| } | |
| } | |
| } | |
| }; | |
| // Обновленные стили с фиксом позиционирования | |
| const css = ` | |
| #${id} { | |
| display: none; | |
| position: fixed; | |
| top: 20px; /* Изменено с bottom на top */ | |
| left: 10px; | |
| right: 10px; | |
| background: #424242; | |
| border-radius: 15px; | |
| padding: 15px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.2); | |
| z-index: 999; | |
| font-size: 16px; | |
| font-family: system-ui; | |
| max-height: 70vh; | |
| overflow-y: auto; | |
| } | |
| .via-trans-close { | |
| position: absolute; | |
| top: 5px; | |
| right: 10px; | |
| font-size: 24px; | |
| color: #666; | |
| cursor: pointer; | |
| } | |
| .via-trans-content { | |
| padding-right: 30px; | |
| } | |
| .lang-direction { | |
| color: #888; | |
| font-size: 12px; | |
| margin-bottom: 5px; | |
| }`; | |
| // Создаем элементы | |
| document.head.insertAdjacentHTML('beforeend', `<style>${css}</style>`); | |
| document.body.insertAdjacentHTML('beforeend', ` | |
| <div id="${id}"> | |
| <div class="via-trans-close">×</div> | |
| <div class="via-trans-content"></div> | |
| </div> | |
| `); | |
| const popup = document.getElementById(id); | |
| const content = popup.querySelector('.via-trans-content'); | |
| const closeBtn = popup.querySelector('.via-trans-close'); | |
| // Обработчики событий | |
| let longPressTimer; | |
| document.addEventListener('touchstart', e => { | |
| longPressTimer = setTimeout(() => { | |
| const text = window.getSelection() | |
| .toString() | |
| .trim(); | |
| if (text && isValidWord(text)) { | |
| showPopup(); | |
| translateWord(text); | |
| } | |
| }, config.pressDuration); | |
| }, { | |
| passive: true | |
| }); | |
| document.addEventListener('touchend', () => clearTimeout(longPressTimer)); | |
| document.addEventListener('touchmove', () => clearTimeout(longPressTimer)); | |
| closeBtn.addEventListener('click', () => popup.style.display = 'none'); | |
| // Основные функции | |
| function detectLanguage(word) { | |
| const hasCyrillic = /[а-яё]/i.test(word); | |
| const hasLatin = /[a-z]/i.test(word); | |
| if (hasCyrillic && !hasLatin) return 'ru'; | |
| if (hasLatin && !hasCyrillic) return 'en'; | |
| return 'auto'; | |
| } | |
| function isValidWord(text) { | |
| return text.length <= config.maxWordLength && | |
| (/^[a-zA-Z'-]+$/.test(text) || /^[а-яёА-ЯЁ'-]+$/.test(text)); | |
| } | |
| async function translateWord(word) { | |
| const fromLang = detectLanguage(word); | |
| if (fromLang === 'auto') { | |
| content.textContent = 'Смешанный язык не поддерживается'; | |
| return; | |
| } | |
| content.textContent = 'Загрузка...'; | |
| try { | |
| const translation = await fetchTranslation(word, fromLang); | |
| const langPair = `${fromLang}→${fromLang === 'en' ? 'ru' : 'en'}`; | |
| content.innerHTML = ` | |
| <div class="lang-direction">Направление: ${langPair}</div> | |
| <div style="color: #1a73e8; margin-bottom: 8px">${word}</div> | |
| <div>${translation}</div> | |
| `; | |
| } catch (e) { | |
| content.textContent = 'Ошибка перевода'; | |
| } | |
| } | |
| function fetchTranslation(word, fromLang) { | |
| return new Promise((resolve, reject) => { | |
| GM_xmlhttpRequest({ | |
| method: "GET", | |
| url: config.source.url(word, fromLang), | |
| timeout: 5000, | |
| onload: res => { | |
| const result = config.source.parser(res.responseText); | |
| result ? resolve(result) : reject(); | |
| }, | |
| onerror: reject | |
| }); | |
| }); | |
| } | |
| function showPopup() { | |
| popup.style.display = 'block'; | |
| // Прокрутка к началу попапа | |
| popup.scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'start' | |
| }); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment