fix(reader): fix reader word popup translation key and sanitize token lookup

This commit is contained in:
Antigravity committed 2026-10-05 11:47:07 +03:00
1 parent d8a2c6e34f
commit 4e52cab88e
4 files changed
+17 -9

No files matched your search

+3
View File
@@ -273,6 +273,9 @@ async def lookup_term(query: str) -> Dict[str, Any]:
result["translation"] = mm_data.get("translation", "") result["translation"] = mm_data.get("translation", "")
result["translations_list"] = [result["translation"]] if result["translation"] else [] result["translations_list"] = [result["translation"]] if result["translation"] else []
# Alias translation_ru for client compatibility
result["translation_ru"] = result["translation"]
# Store into SQLite cache # Store into SQLite cache
with get_db() as conn: with get_db() as conn:
conn.execute( conn.execute(
+2 -2
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Notes — Персональный тренажер и словарь английского языка</title> <title>Notes — Персональный тренажер и словарь английского языка</title>
<meta name="description" content="Защищённый персональный тренажёр и словарь английского языка с интервальными повторениями SRS SM-2, контекстным поиском и озвучкой"> <meta name="description" content="Защищённый персональный тренажёр и словарь английского языка с интервальными повторениями SRS SM-2, контекстным поиском и озвучкой">
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css?v=2.2">
<!-- PWA meta and icons --> <!-- PWA meta and icons -->
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
@@ -922,6 +922,6 @@
<!-- Audio Element for native streaming --> <!-- Audio Element for native streaming -->
<audio id="global-audio-player" preload="none"></audio> <audio id="global-audio-player" preload="none"></audio>
<script src="/static/js/app.js"></script> <script src="/static/js/app.js?v=2.2"></script>
</body> </body>
</html> </html>
+11 -6
View File
@@ -1897,7 +1897,8 @@ document.addEventListener('DOMContentLoaded', () => {
// Tokenize words while preserving spaces & punctuation // Tokenize words while preserving spaces & punctuation
const tokens = paraText.split(/([A-Za-z0-9'-]+)/); const tokens = paraText.split(/([A-Za-z0-9'-]+)/);
tokens.forEach(token => { 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'); const span = document.createElement('span');
span.className = 'reader-word'; span.className = 'reader-word';
span.dataset.word = token; span.dataset.word = token;
@@ -1951,13 +1952,16 @@ document.addEventListener('DOMContentLoaded', () => {
} }
async function openWordPopup(term, contextSentence, targetElement) { async function openWordPopup(term, contextSentence, targetElement) {
const cleanTerm = (term || '').replace(/^['"“«`\s\-_.,;:!?()[\]{}]+|['"”»`\s\-_.,;:!?()[\]{}]+$/g, '').trim();
if (!cleanTerm) return;
popupCurrentData = { popupCurrentData = {
term: term, term: cleanTerm,
contextSentence: contextSentence, contextSentence: contextSentence,
targetElement: targetElement, targetElement: targetElement,
}; };
popupTerm.textContent = term; popupTerm.textContent = cleanTerm;
popupPhonetic.textContent = ''; popupPhonetic.textContent = '';
popupTranslation.textContent = ''; popupTranslation.textContent = '';
popupContext.textContent = contextSentence ? `“${contextSentence}”` : ''; popupContext.textContent = contextSentence ? `“${contextSentence}”` : '';
@@ -1985,13 +1989,14 @@ document.addEventListener('DOMContentLoaded', () => {
} }
try { try {
const resp = await fetch(`/api/lookup?q=${encodeURIComponent(term)}`); const resp = await fetch(`/api/lookup?q=${encodeURIComponent(cleanTerm)}`);
if (resp.ok) { if (resp.ok) {
const data = await resp.json(); const data = await resp.json();
popupCurrentData.lookup = data; 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 || ''; popupPhonetic.textContent = data.phonetic || '';
popupTranslation.textContent = data.translation_ru || 'Перевод не найден'; popupTranslation.textContent = trans || 'Перевод не найден';
popupLoading.classList.add('hidden'); popupLoading.classList.add('hidden');
popupContent.classList.remove('hidden'); popupContent.classList.remove('hidden');
@@ -2035,7 +2040,7 @@ document.addEventListener('DOMContentLoaded', () => {
const payload = { const payload = {
term: d.term, term: d.term,
translation: lookup.translation_ru || popupTranslation.textContent, translation: lookup.translation || lookup.translation_ru || popupTranslation.textContent,
context_sentence: d.contextSentence || '', context_sentence: d.contextSentence || '',
phonetic: lookup.phonetic || '', phonetic: lookup.phonetic || '',
audio_url: lookup.audio_url || '', audio_url: lookup.audio_url || '',
+1 -1
View File
@@ -1,7 +1,7 @@
// Service Worker for Notes English PWA (Stage 2) // Service Worker for Notes English PWA (Stage 2)
// Strategy: Cache-first for static assets, Network-first for API calls. // 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 OFFLINE_URL = '/';
const PRECACHE_URLS = [ const PRECACHE_URLS = [