From 0ff4465d34f2f0140fa102500ea6b0a2615d4e02 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Tue, 6 Oct 2026 17:03:38 +0300 Subject: [PATCH] fix(reader): preserve full context sentence with abbreviations (Mr., Mrs., etc.) and log bug fix --- TODO.md | 8 ++++++++ app/static/js/modules/reader.js | 34 ++++++++++++++++++++++++++++++--- app/static/sw.js | 2 +- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index a444e42..78fcaef 100644 --- a/TODO.md +++ b/TODO.md @@ -369,6 +369,14 @@ --- +### πŸ› УстранённыС Π΄Π΅Ρ„Π΅ΠΊΡ‚Ρ‹ ΠΈ хотфиксы (Bug Tracker & Hotfixes) +- [x] **Π‘Π°Π³ Ρ‡ΠΈΡ‚Π°Π»ΠΊΠΈ: ΠŸΡ€Π΅ΠΆΠ΄Π΅Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ€Ρ‹Π² контСкстного прСдлоТСния Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ°Ρ… сокращСний (`Mr.`, `Mrs.`, `Dr.`, `Prof.`, `St.`, `etc.`, `e.g.`, `i.e.`, `1.5`)**: + - **Π‘ΠΈΠΌΠΏΡ‚ΠΎΠΌ**: ΠŸΡ€ΠΈ ΠΊΠ»ΠΈΠΊΠ΅ Π½Π° слово Π² ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠΈ с ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ΠΌ ΠΈΠ»ΠΈ сокращСниСм (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, *Β«Mr. and Mrs. Dursley were proud...Β»*) ΠΊΠ½ΠΎΠΏΠΊΠ° ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Π° прСдлоТСния («🌐 ΠŸΠ΅Ρ€Π΅Π²Π΅ΡΡ‚ΠΈ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅Β») отправляла Π½Π° ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ лишь ΠΎΠ±Ρ€Π΅Π·ΠΎΠΊ Π΄ΠΎ ΠΈΠ»ΠΈ послС Ρ‚ΠΎΡ‡ΠΊΠΈ (*Β«Mr.Β»*, *Β«and Mrs.Β»* ΠΈΠ»ΠΈ *Β«Dursley were proud...Β»*). Если ΠΏΠ΅Ρ€Π΅Π΄ словом Π±Ρ‹Π»Π° Ρ‚ΠΎΡ‡ΠΊΠ° Π² ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΈ, тСкст дальшС Π½Π΅ пСрСводился. + - **ΠŸΡ€ΠΈΡ‡ΠΈΠ½Π°**: Π’ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ `extractContextSentence(fullText, word)` Π² [`app/static/js/modules/reader.js`](file:///home/ai-projects/services/study.qpid.pro/app/static/js/modules/reader.js) использовалось Π½Π°ΠΈΠ²Π½ΠΎΠ΅ рСгулярноС Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ `[^.!?\n]*`, ΡΡ‡ΠΈΡ‚Π°Π²ΡˆΠ΅Π΅ Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ Π»ΡŽΠ±ΡƒΡŽ Ρ‚ΠΎΡ‡ΠΊΡƒ ΠΊΠΎΠ½Ρ†ΠΎΠΌ прСдлоТСния. + - **РСшСниС**: Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π° бСзопасная Π·Π°Ρ‰ΠΈΡ‚Π° списка английских Ρ‚ΠΈΡ‚ΡƒΠ»ΠΎΠ² ΠΈ сокращСний (`READER_HONORIFICS`) ΠΈ Π΄Ρ€ΠΎΠ±Π½Ρ‹Ρ… чисСл ΠΏΠ΅Ρ€Π΅Π΄ сСгмСнтациСй. ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ дСлятся ΠΏΠΎ Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΌ Π·Π½Π°ΠΊΠ°ΠΌ прСпинания с Π·Π°ΠΊΡ€Ρ‹Π²Π°ΡŽΡ‰ΠΈΠΌΠΈ ΠΊΠ°Π²Ρ‹Ρ‡ΠΊΠ°ΠΌΠΈ/скобками (`[.!?]['"”’»)]*`), гарантируя ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ, связного контСкста Π² ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄Ρ‡ΠΈΠΊ ΠΈ ΡΠ»ΠΎΠ²Π°Ρ€ΡŒ. + +--- + ### 17. πŸ” Π Π°Π·Π³Ρ€ΡƒΠ·ΠΊΠ° Ρ…Π΅Π΄Π΄Π΅Ρ€Π° ΠΈ Сдиная навигация Ρ‡Π΅Ρ€Π΅Π· Π±ΠΎΠΊΠΎΠ²ΠΎΠ΅ мСню / Drawer (Unified Navigation & Header Redesign) - [ ] **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° ΠΏΠ΅Ρ€Π΅Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ Ρ…Π΅Π΄Π΄Π΅Ρ€Π°**: - Π’ шапкС скопилось 8+ ΠΊΠ½ΠΎΠΏΠΎΠΊ (`πŸ“š Π§ΠΈΡ‚Π°Π»ΠΊΠ°`, `🎯 ВСсты CEFR`, `πŸ“– Π“Ρ€Π°ΠΌΠΌΠ°Ρ‚ΠΈΠΊΠ°`, `πŸŽ“ Π’Ρ€Π΅Π½Π°ΠΆΡ‘Ρ€`, `πŸ“Š Бтатистика`, `πŸ“€ Экспорт`, Ρ‚Π΅ΠΌΠ°, ΡˆΠ΅ΡΡ‚Π΅Ρ€Π΅Π½ΠΊΠ°, ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ), Π½Π° экранах ΠΏΠ»Π°Π½ΡˆΠ΅Ρ‚ΠΎΠ² ΠΈ Π½ΠΎΡƒΡ‚Π±ΡƒΠΊΠΎΠ² Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΈ Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΡˆΡƒΠΌ. diff --git a/app/static/js/modules/reader.js b/app/static/js/modules/reader.js index 168f910..666c393 100644 --- a/app/static/js/modules/reader.js +++ b/app/static/js/modules/reader.js @@ -1070,11 +1070,39 @@ export function renderInteractiveSpans(container, text, contextText, knownWordsM }); } +const READER_HONORIFICS = [ + 'Mr.', 'Mrs.', 'Ms.', 'Dr.', 'Prof.', 'St.', 'vs.', 'etc.', 'e.g.', 'i.e.', 'Jr.', 'Sr.', + 'Capt.', 'Col.', 'Gen.', 'Lt.', 'Sgt.', 'Rev.', 'Hon.' +]; + export function extractContextSentence(fullText, word) { if (!fullText) return ''; - const re = new RegExp(`([^.!?\\n]*?\\b${escapeRegex(word)}\\b[^.!?\\n]*[.!?]?)`, 'i'); - const m = fullText.match(re); - if (m && m[1]) return m[1].trim(); + let protectedText = fullText; + for (const h of READER_HONORIFICS) { + if (protectedText.toLowerCase().includes(h.toLowerCase())) { + const safe = h.replace(/\./g, 'Β§DOTΒ§'); + const reH = new RegExp(escapeRegex(h), 'gi'); + protectedText = protectedText.replace(reH, safe); + } + } + // Protect decimal numbers (e.g. 3.14, 1.5) + protectedText = protectedText.replace(/(\d+)\.(\d+)/g, '$1Β§DOTΒ§$2'); + + // Split sentences on terminal punctuation (.!?) followed by optional quotes/brackets and whitespace + const marked = protectedText.replace(/([.!?]['"”’»)]*)\s+/g, '$1Β§SEPΒ§'); + const rawSentences = marked.split('Β§SEPΒ§'); + + const cleanWord = (word || '').trim(); + if (cleanWord) { + const escaped = escapeRegex(cleanWord); + const wordRe = new RegExp(`(?:^|\\W)${escaped}(?:$|\\W)`, 'i'); + for (const s of rawSentences) { + if (wordRe.test(s)) { + return s.replace(/Β§DOTΒ§/g, '.').trim(); + } + } + } + return fullText.slice(0, 160).trim(); } diff --git a/app/static/sw.js b/app/static/sw.js index f516b6c..4abd4bb 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-v5.5'; +const CACHE_NAME = 'notes-v5.6'; const OFFLINE_URL = '/'; const PRECACHE_URLS = [