From 4e52cab88e3ad1fc6361d41722d789144add2edb Mon Sep 17 00:00:00 2001 From: Antigravity Date: Mon, 5 Oct 2026 11:47:07 +0300 Subject: [PATCH] fix(reader): fix reader word popup translation key and sanitize token lookup --- app/dictionary.py | 3 +++ app/static/index.html | 4 ++-- app/static/js/app.js | 17 +++++++++++------ app/static/sw.js | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/dictionary.py b/app/dictionary.py index 324cb4b..61472cb 100644 --- a/app/dictionary.py +++ b/app/dictionary.py @@ -273,6 +273,9 @@ async def lookup_term(query: str) -> Dict[str, Any]: result["translation"] = mm_data.get("translation", "") result["translations_list"] = [result["translation"]] if result["translation"] else [] + # Alias translation_ru for client compatibility + result["translation_ru"] = result["translation"] + # Store into SQLite cache with get_db() as conn: conn.execute( diff --git a/app/static/index.html b/app/static/index.html index 41dd552..5343b89 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -5,7 +5,7 @@ Notes — Персональный тренажер и словарь английского языка - + @@ -922,6 +922,6 @@ - + diff --git a/app/static/js/app.js b/app/static/js/app.js index 267c9b0..e2a73e2 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -1897,7 +1897,8 @@ document.addEventListener('DOMContentLoaded', () => { // Tokenize words while preserving spaces & punctuation const tokens = paraText.split(/([A-Za-z0-9'-]+)/); tokens.forEach(token => { - if (/^[A-Za-z0-9'-]+$/.test(token) && token.length > 1) { + const isWord = /^[A-Za-z0-9'-]+$/.test(token) && token.replace(/[^A-Za-z0-9]/g, '').length >= 1; + if (isWord) { const span = document.createElement('span'); span.className = 'reader-word'; span.dataset.word = token; @@ -1951,13 +1952,16 @@ document.addEventListener('DOMContentLoaded', () => { } async function openWordPopup(term, contextSentence, targetElement) { + const cleanTerm = (term || '').replace(/^['"“«`\s\-_.,;:!?()[\]{}]+|['"”»`\s\-_.,;:!?()[\]{}]+$/g, '').trim(); + if (!cleanTerm) return; + popupCurrentData = { - term: term, + term: cleanTerm, contextSentence: contextSentence, targetElement: targetElement, }; - popupTerm.textContent = term; + popupTerm.textContent = cleanTerm; popupPhonetic.textContent = ''; popupTranslation.textContent = ''; popupContext.textContent = contextSentence ? `“${contextSentence}”` : ''; @@ -1985,13 +1989,14 @@ document.addEventListener('DOMContentLoaded', () => { } try { - const resp = await fetch(`/api/lookup?q=${encodeURIComponent(term)}`); + const resp = await fetch(`/api/lookup?q=${encodeURIComponent(cleanTerm)}`); if (resp.ok) { const data = await resp.json(); popupCurrentData.lookup = data; + const trans = data.translation || data.translation_ru || (data.translations_list && data.translations_list.length > 0 ? data.translations_list.join(', ') : ''); popupPhonetic.textContent = data.phonetic || ''; - popupTranslation.textContent = data.translation_ru || 'Перевод не найден'; + popupTranslation.textContent = trans || 'Перевод не найден'; popupLoading.classList.add('hidden'); popupContent.classList.remove('hidden'); @@ -2035,7 +2040,7 @@ document.addEventListener('DOMContentLoaded', () => { const payload = { term: d.term, - translation: lookup.translation_ru || popupTranslation.textContent, + translation: lookup.translation || lookup.translation_ru || popupTranslation.textContent, context_sentence: d.contextSentence || '', phonetic: lookup.phonetic || '', audio_url: lookup.audio_url || '', diff --git a/app/static/sw.js b/app/static/sw.js index f741828..4faeda8 100644 --- a/app/static/sw.js +++ b/app/static/sw.js @@ -1,7 +1,7 @@ // Service Worker for Notes English PWA (Stage 2) // Strategy: Cache-first for static assets, Network-first for API calls. -const CACHE_NAME = 'notes-v2.0'; +const CACHE_NAME = 'notes-v2.2'; const OFFLINE_URL = '/'; const PRECACHE_URLS = [