Files
notes-qpid-pro/TODO.md
T

532 lines
70 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📋 Задачи проекта: study.qpid.pro (Backlog & Execution Queue)
Список задач для последовательной реализации в рамках принципов **Ponytail** (максимальная надёжность, нативные средства платформы, zero-dependency, высокая скорость).
---
## 🚀 Очередь реализации (Пошаговый план)
### 1. 📑 Полноценный рендеринг Markdown (.md) в читалке
- [x] **Блоки кода (Fenced Code Blocks ```` ```language ````)**:
- Выделение блоков кода в контейнер `<pre><code class="language-...">`.
- Моноширинный шрифт (`JetBrains Mono`, `Consolas`), сохранение отступов и табуляции (`white-space: pre`).
- Тёмная подложка, горизонтальный скролл при переполнении, отключение красной строки (`text-indent: 0`).
- Кнопка «Копировать код» в шапке блока.
- Отключение создания интерактивных словарных спанов `<span class="reader-word">` для кода, чтобы синтаксис не ломал форматирование.
- [x] **Инлайн-код (`` `variable` ``)**:
- Оборачивание в нативный тег `<code class="reader-inline-code">` с бейджик-стилем.
- [x] **Списки и цитаты**:
- Преобразование маркированных (`- `, `* `) и нумерованных (`1. `) списков в `<ul><li>` / `<ol><li>`.
- Преобразование цитат (`> `) в `<blockquote>` с акцентной левой границей.
- **Слова внутри списков и цитат остаются кликабельными** (интерактивный клик-перевод работает!).
- [x] **Заголовки Markdown (`##`, `###`)**:
- Конвертация в `<h3>`, `<h4>` с удалением сырых решёток `#`.
- [x] **Таблицы Markdown (`| col | col |`)**:
- Адаптивные таблицы с рамками, стилизацией заголовков `<thead>`, зебра-подсветкой строк и горизонтальным скроллом на мобильных.
- Каждая ячейка поддерживает интерактивные слова для изучения и инлайн-код.
- [x] **HTML-блоки и теги в Markdown (`<p align="center">`, `<h1>`, `<em>`, `<strong>`, `<sub>`, `<br>`)**:
- Нативный парсинг через встроенный `DOMParser` без внешних библиотек.
- Устранена утечка сырых тегов (`<picture>`, `<source>`, `<p align=...>`) в словарные слова.
- [x] **Ссылки и изображения (`[text](url)`, `![alt](url)`, `<img>`)**:
- Поддержка бейджей shields.io, тренд-графиков и внешних картинок.
- Автоматическое скрытие битых/отсутствующих локальных путей (`assets/...`) через обработчик `onerror`.
- [x] **Точное позиционирование окна перевода у курсора/слова (Popup Positioning)**:
- Исправлен баг двойного смещения координат: убран `window.scrollY` при `position: fixed`.
- Окно перенесено в корень `<body>` для исключения искажений контекста позиционирования.
- Плавное отслеживание скролла через `requestAnimationFrame` с автозакрытием при уходе слова за границы экрана или под липкую шапку.
- [x] **Стилизация в CSS ([`app/static/css/style.css`](file:///home/ai-projects/services/study.qpid.pro/app/static/css/style.css))**:
- Добавлены стили для `.reader-content-body pre`, `code`, `blockquote`, `ul`, `ol`, `.reader-table`, `.reader-heading`, `.reader-link`, `.reader-img`.
---
### 2. 🗜️ Сжатие статического трафика (`GZipMiddleware`)
- [x] Подключить `GZipMiddleware(minimum_size=1000)` в [`app/main.py`](file:///home/ai-projects/services/study.qpid.pro/app/main.py).
- [x] Сократить объём передаваемых файлов `app.js` (111.8 КБ -> 21.5 КБ), `index.html` (55.2 КБ -> 10.3 КБ) и `style.css` (58.2 КБ -> 9.4 КБ) на 81.7% (суммарно с 225.2 КБ до 41.2 КБ).
- [x] Проверить заголовки `Content-Encoding: gzip` в ответах и добавить регрессионный тест в `tests/test_api.py`.
---
### 3. 🛡️ Дедупликация слов и защита от повторного добавления (Reader + API)
- [x] **Проверка дубликатов на бэкенде (`POST /api/cards`)**:
- Проверка слова без учёта регистра `lower(trim(term))`.
- При повторном добавлении не создаётся дубликат, а дополняется контекстное предложение и объединяются теги, возвращается `already_exists: True`.
- [x] **Индикация в читалке и карточке поиска**:
- В читалке при клике на слово и в карточке поиска выводится бейдж `⭐ В библиотеке`.
- Кнопка «+ В словарь» заменяется на бейдж `⭐ Слово уже в библиотеке` с опциональной кнопкой `+ Добавить эту цитату к карточке`.
- [x] **Индекс целостности в SQLite**:
- Создан `UNIQUE INDEX idx_vocab_user_term_nocase ON vocabulary_cards(user_id, term COLLATE NOCASE);` гарантирующий отсутствие дубликатов на уровне движка БД за $O(\log N)$.
- [x] **Слияние существующих дубликатов в базе**:
- Объединены дубликаты `admired` (ID 5 и ID 7) и `suddenly`/`Suddenly` (ID 2 и ID 9) с объединением контекстных предложений, слиянием тегов и сохранением прогресса SRS.
---
### 4. 📱 Мобильная оптимизация и адаптивная верстка (iPhone 12 / iOS Safari)
- [x] **Системный фундамент iOS и сброс ограничений (Core Foundation)**:
- Метатег `viewport`: добавлен `viewport-fit=cover` для поддержки выреза (notch) и нижней панели iPhone.
- Устранение бага принудительного авто-зума: установлен `font-size: 16px` для `<input>`, `<textarea>`, `<select>` на экранах `≤ 768px`.
- Внедрены динамические единицы `100dvh` (Dynamic Viewport Height) и безопасные отступы `env(safe-area-inset-top)` / `env(safe-area-inset-bottom)`.
- Защита от горизонтального сдвига (`overflow-x: hidden` на `html`, `body` и `#app-shell`).
- [x] **Мобильная навигация: переход к нативному паттерну (App Header + Bottom Tab Bar)**:
- Компактная верхняя шапка для смартфонов: логотип `📖 Notes`, переключатель темы `🌓`, кнопка вызова профиля `⚙️`.
- Фиксированная нижняя панель вкладок (Bottom Tab Bar) с нативным iOS UX:
1. 🔍 **Словарь** (Поиск и словарь)
2. 🎓 **Тренажёр** (SRS с бейджем количества повторений)
3. 📚 **Читалка** (Библиотека книг)
4. 📊 **Прогресс** (Статистика и стрик)
5. ⚙️ **Меню** (Смена пароля, экспорт, выход)
- Полное сохранение десктопного `navbar` без изменений на больших экранах (`> 768px`).
- [x] **Адаптация экрана словаря и карточек (Dictionary & Cards View)**:
- Поле поиска/перевода на полную ширину экрана с кнопкой «Перевести».
- Скрытие неактуальных десктопных подсказок горячих клавиш (`<kbd>Ctrl+Enter</kbd>`) на мобильных устройствах.
- Вертикальная раскладка полей метаданных при редактировании карточки.
- Горизонтальный скролл чипсов коллекций и тегов (`overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch`).
- Увеличенные зоны нажатия (Touch Targets ≥ 44px) для кнопок действий над карточкой.
- [x] **Интерактивная читалка и переход к Mobile Bottom Sheet**:
- Трансформация всплывающего попапа перевода (`reader-word-popup`) в нативную выдвижную нижнюю шторку (Bottom Sheet) на смартфонах с ручкой перетаскивания `sheet-drag-handle`.
- Двухстрочный адаптивный тулбар читалки (навигация по главам + размер шрифта `A-/A+` и подсветка).
- Сетка библиотеки `bookshelf-grid` в одну колонку на экранах `≤ 640px`.
- [x] **Тренажёр интервальных повторений (SRS Trainer)**:
- Ограничение высоты карточки SRS (`max-height: 48dvh`), чтобы карточка и кнопки оценки гарантированно помещались без вертикального скролла на 390px.
- Кнопки оценки (Снова / Трудно / Хорошо / Легко) в сетку 2×2 с высотой от 48px и четкими цветовыми акцентами.
- Вертикальный список вариантов в режиме квиза (4 варианта) на всю ширину экрана.
- [x] **Модальные окна (Статистика, Пароль, Экспорт)**:
- Метрики статистики в сетку 2×2 вместо сжатых 4 колонок.
- Плавный горизонтальный скролл календаря активности (Heatmap).
- Разворачивание модальных окон в адаптивный мобильный слой с удобной кнопкой закрытия.
---
---
### 5. 🔤 Кастомизация оформления читалки (Типографика, темы, отступы, LocalStorage)
- [x] **Панель настроек оформления текста (`Aa`)**:
- Кнопка вызова настроек в тулбаре читалки: поповер на ПК и выдвижная шторка (Bottom Sheet) на смартфонах.
- [x] **Настройка размера шрифта и степпер**:
- Степпер `A-` / `A+` с индикатором точного размера (от 14px до 34px).
- [x] **Семейство шрифтов (Font Family)**:
- Переключение в 1 клик между `Sans` (системный), `Serif` (книжный с засечками), `Mono` (моноширинный).
- [x] **Межстрочный интервал (Line Height)**:
- Компактный (1.35), Оптимальный (1.65), Широкий (1.95).
- [x] **Поля и ширина колонки (Margins & Max Width)**:
- Узкие поля (940px), Стандарт (760px), Книжная колонка (600px).
- [x] **Цветовые темы читалки (Themes)**:
- 🌙 Тёмная (Dark Slate), 📜 Сепия (Тёплый пергамент), ☀️ Светлая (Чистый лист), 🖤 OLED (Абсолютный чёрный).
- [x] **Выравнивание текста (Text Alignment)**:
- По левому краю / По ширине (Justified).
- [x] **Автосохранение и персистентность**:
- Мгновенное реактивное применение стилей через CSS-переменные без перезагрузки и сохранение настроек в `localStorage`.
---
### 6. 🔄 Комплексная переработка навигации, роутинга и честного прогресса чтения (Completed)
- [x] **6.1. Нативная маршрутизация окон (URL Hash Router & Zero-Flicker F5)**:
- Внедрение хэш-роутинга: `#dictionary`, `#srs`, `#bookshelf`, `#reader/:book_id`, `#reader/:book_id/ch/:chapter_idx`.
- Синхронное переключение `view` при старте скрипта (до любых сетевых запросов авторизации): если в URL `#reader/...`, сразу скрывать `#dictionary-view` и показывать скелетон читалки, устраняя мигание словарем.
- Развязка асинхронных вызовов: `loadBookshelf()` больше не должен принудительно гасить `readerInterface`, если уже инициировано открытие книги через `openBook()`.
- Поддержка нативной истории браузера (кнопки «Назад» / «Вперёд» на мыши, клавиатуре и мобильных свайпах).
- Автоматическая синхронизация активного экрана в URL и `localStorage`.
- [x] **6.2. Честный непрерывный расчет прогресса книги и главы (Weighted Reading Progress)**:
- Устранение бага ложных «100%»: полная ликвидация формулы `(current_ch + 1) / total_ch`, выдававшей 100% на первой странице одноглавных документов.
- Прогресс текущей главы в реальном времени: непрерывный расчет от вертикального скролла (`scrollTop / (scrollHeight - clientHeight) * 100`) от 0% до 100%.
- Взвешенный общий прогресс книги: расчет по объему слов прочитанных глав + точная доля текущей главы:
$$\text{TotalProgress} = \frac{\sum_{i=0}^{\text{cur}-1} \text{words}(ch_i) + \text{words}(ch_{\text{cur}}) \times \frac{\text{ChapterPct}}{100}}{\text{TotalWords}} \times 100\%$$
- Для документа на 1000 слов прогресс будет плавно расти: 0% $\rightarrow$ 15% $\rightarrow$ 50% $\rightarrow$ 100%.
- Отображение честного суммарного прогресса на карточках книг в библиотеке и в тулбаре читалки.
- [x] **6.3. Интеллектуальный парсинг глав и оглавление (Smart Chapter & ToC Detection)**:
- Расширение детектора глав в `app/reader.py`:
- Markdown-заголовки всех уровней (`# `, `## `, `### `, `#### `).
- Нумерованные разделы и модули (`1. `, `1.1 `, `2.0 `, `Section 1`, `Part 1`, `Раздел 1`).
- Выделенные строки в верхнем регистре (ALL CAPS) от 3 до 60 символов, обрамленные пустыми строками (`INTRODUCTION`, `ETHERNET BASICS`).
- Жирные заголовки в начале абзацев (`**Chapter 1**`, `**Глава 1**`).
- Адаптивный порог авто-разбивки: снижение порога чанкования текста без явных глав с 3000 до 800–1000 слов, чтобы даже небольшие статьи имели логическое деление на части.
- Интерактивное оглавление со статусами прочтения: `✔ Прочитано`, `▶ В процессе (45%)`, `○ Не начато`.
- [x] **6.4. Устойчивая фиксация позиции чтения (Paragraph Anchor + Scroll)**:
- Сохранение не только пикселей `scrollTop`, но и индекса первого видимого абзаца (`anchor_paragraph_idx`).
- Устойчивость к изменению шрифта `Aa` и ориентации экрана: восстановление скролла к конкретному предложению/абзацу, а не слепым пикселям.
- Фиксация скролл-контейнера `#reader-scroll-area` с аппаратным ускорением на мобильных.
- [x] **6.5. Сквозное чтение и бесшовная автоподгрузка (Continuous Book View & Seamless Infinite Append)**:
- Режим «Вся книга целиком» (`flowMode: 'continuous'`): объединение всех глав книги в один непрерывный свиток со стильными разделителями глав без необходимости перелистывания.
- Режим «По главам» с автоподгрузкой (`autoLoadNext: true`): бесшовное присоединение следующей главы снизу при приближении к концу текущей главы.
- Навигационный блок завершения главы: эргономичные кнопки «◀ Назад» и «Следующая: [Название] ▶» прямо под текстом (без необходимости тянуться наверх).
- Настройка режима чтения («По главам» / «Вся книга») и переключатель автоподгрузки добавлены в панель настроек `Aa` с автосохранением в `localStorage`.
- Карточка завершения книги (`🎉 Книга прочитана!`) с быстрыми ссылками на тренировку слов и возврат в библиотеку.
---
### 7. 📑 Поддержка чтения PDF (Cisco CCNA, O'Reilly & Tech Books с оглавлением и текстовым слоем) (Completed)
- [x] **Легковесный Pure-Python парсер (`pypdf`, Zero C-Dependency)**:
- Подключение `pypdf` (< 400 КБ, чистый Python, без раздувания Docker-образа и без системных библиотек `poppler-utils` / `mupdf`).
- [x] **Извлечение оглавления и закладок PDF (`reader.outline`)**:
- Извлечение авторского оглавления (ToC / Bookmarks), стандартного для книг Cisco Press (CCNA Official Cert Guide), O'Reilly и Wiley.
- Разбивка книги на логические главы и разделы (`Part I`, `Chapter 1`, `Chapter 2`) точно по оглавлению автора.
- [x] **Fallback-разбивка при отсутствии оглавления**:
- При отсутствии PDF-закладок — автоопределение заголовков через regex (`Chapter \d+`, `Глава \d+`) или группировка страниц порциями по ~12–15 страниц.
- [x] **Очистка шума и колонтитулов**:
- Фильтрация повторяющихся верхних/нижних колонтитулов («CCNA 200-301 Official Cert Guide», «Page 45 of 900»).
- [x] **Сохранение CLI/Console блоков**:
- Распознавание консольного вывода и команд сетевого оборудования (`Router# show ip interface brief`, `Switch(config)#`) и оборачивание в блоки кода Markdown для читалки с кнопкой «Копировать».
- [x] **Защита от сканов без OCR (Scanned PDF Guard)**:
- Валидация наличия текстового слоя: если суммарный объём извлечённого текста < 100 слов, возврат информативной ошибки с предложением использовать текстовый PDF или OCR.
- [x] **Интеграция в интерфейс читалки**:
- Разрешение загрузки `.pdf` в `<input type="file" accept=".epub,.pdf,.txt,.md">`.
- Обновление кнопки в библиотеке: `📁 Загрузить EPUB / PDF / TXT`.
---
### 7.1. 🖼️ Извлечение и отображение схем, топологий и диаграмм из PDF / EPUB (Planned)
- [ ] **Извлечение бинарных изображений на диск**:
- При парсинге PDF/EPUB извлекать графические объекты (`page.images` / архивы изображений) на файловую систему в изолированную директорию книги: `data/books/{book_id}/images/{img_name}`.
- Избегать раздувания базы данных SQLite: база хранит только текст и метаданные, бинарная графика хранится на диске.
- [ ] **Кэширующий медиа-эндпоинт**:
- Создать эндпоинт `GET /api/books/{book_id}/images/{image_name}` с заголовками кэширования (`Cache-Control: public, max-age=31536000, immutable`).
- [ ] **Встраивание в текст читалки**:
- Привязывать иллюстрации к соответствующей странице/главе через Markdown-теги: `![Схема топологии](/api/books/{book_id}/images/{img_name})`.
- Интерактивный просмотр и зум схем по клику в читалке.
---
### 8. 🔌 Пул соединений HTTP (`httpx.AsyncClient` с Keep-Alive) (Completed)
- [x] В [`app/dictionary.py`](file:///home/ai-projects/services/study.qpid.pro/app/dictionary.py) создать синглтон-клиент `get_http_client()` с пулом соединений:
```python
limits = httpx.Limits(max_keepalive_connections=10, max_connections=20, keepalive_expiry=60.0)
```
- [x] Заменить вызовы `async with httpx.AsyncClient()` в парсерах на переиспользуемый клиент.
- [x] Устранить 5 повторных TCP/TLS хэндшейков на каждое слово, сократив задержку внешних запросов на 300–600 мс.
- [x] Добавить вызов `close_http_client()` в `lifespan` FastAPI при остановке приложения для чистого закрытия открытых сокетов.
- [x] Добавить юнит-тесты `test_http_connection_pooling` в `tests/test_api.py` (22/22 passed).
---
### 9. 🗄️ Тюнинг SQLite PRAGMA и составных индексов
- [x] В [`app/database.py`](file:///home/ai-projects/services/study.qpid.pro/app/database.py):
- Вынесен `PRAGMA journal_mode=WAL;` в однократную инициализацию `init_db()` и миграции (устранены повторные блокировки и запись заголовка файла на каждый HTTP-запрос).
- Включены высокопроизводительные параметры памяти Linux для каждого соединения:
```python
conn.execute("PRAGMA synchronous = NORMAL;")
conn.execute("PRAGMA mmap_size = 268435456;") # 256MB memory-mapped I/O
conn.execute("PRAGMA cache_size = -32000;") # 32MB RAM page cache
conn.execute("PRAGMA temp_store = MEMORY;") # временные таблицы и сортировки в ОЗУ
conn.execute("PRAGMA foreign_keys = ON;")
```
- [x] **Zero-copy чтение страниц**: включён `mmap_size = 256MB`, позволяющий ядру Linux отдавать страницы SQLite напрямую из системного дискового кэша без лишних системных вызовов `read()` и копирования в буферы пользовательского пространства.
- [x] **Составные индексы (Composite Indexes)** для ускорения частых выборок:
- `vocabulary_cards`:
- `idx_vocab_user_created ON (user_id, created_at DESC)` — мгновенная пагинация списка слов без сортировки в памяти.
- `idx_vocab_user_srs_state ON (user_id, srs_state, srs_next_review)` — выборка очереди интервальных повторений SRS (`/api/srs/queue`).
- `idx_vocab_user_srs_created ON (user_id, srs_state, created_at DESC)` — выборка новых слов для интервального повторения.
- `idx_vocab_collection ON (user_id, collection)` — мгновенная фильтрация по коллекциям.
- `idx_vocab_user_term_nocase ON (user_id, term COLLATE NOCASE)` — предотвращение дубликатов за $O(\log N)$.
- `srs_logs`:
- `idx_srs_card ON (card_id)` — устранение full table scan при каскадном удалении карточек (`ON DELETE CASCADE`).
- `idx_srs_reviewed ON (user_id, reviewed_at)` — выборка журнала повторений и расчёт тепловой карты активности.
- `books`:
- `idx_books_user_updated ON (user_id, updated_at DESC)` — сортировка списка книг библиотеки по дате чтения.
- `sessions`:
- `idx_sessions_user ON (user_id)` — ускорение валидации сессий и каскадной очистки.
- [x] Добавлен регрессионный тест `test_sqlite_pragma_tuning_and_composite_indexes` в `tests/test_api.py` (23/23 тестов успешно).
---
### 10. ✂️ Де-блоатинг внешних парсеров (YAGNI)
- [x] **Удалён сетевой опрос Datamuse (`fetch_datamuse`)**:
- Устранена зависимость от `api.datamuse.com`, которая дублировала определения и перегружала выдачу зашифрованными метками тегов и рифмами.
- Сокращено число исходящих HTTP-запросов на каждый поиск слова на 20% (с 5 до 4 параллельных вызовов).
- [x] **Переход на современный REST API Wiktionary**:
- `fetch_wiktionary_wikitext` переведён на легковесный эндпоинт `https://en.wiktionary.org/w/rest.php/v1/page/{word}` (скорость ответа 0.3с вместо 0.5с у legacy `action=parse`).
- Добавлена строгая изоляция англоязычной секции `==English==`, исключившая утечку переводов и IPA из других языков (например, французское слово *chat* больше не заносит «кот» в английский словарь).
- Реализован автоматический fallback с сохранением регистра (для слов с заглавной буквы в начале предложений).
- [x] **Нормализация определений и примеров**:
- `fetch_wiktionary_definitions` дополнен декодированием HTML-сущностей (`html.unescape`) и нормализацией пробельных символов.
- [x] Добавлен регрессионный тест `test_debloat_dictionary_and_wiktionary_rest_parser` в `tests/test_api.py` (24/24 тестов успешно).
---
### 11. 📚 Локальный оффлайн-словарь (99%+ покрытия со словоформами, SQLite 10–20 МБ) (Completed)
- [x] Создана в SQLite таблица:
```sql
CREATE TABLE IF NOT EXISTS offline_dictionary (
term TEXT PRIMARY KEY COLLATE NOCASE,
translation TEXT NOT NULL,
phonetic TEXT DEFAULT '',
pos TEXT DEFAULT ''
);
CREATE INDEX IF NOT EXISTS idx_offline_dict_term ON offline_dictionary(term COLLATE NOCASE);
```
- [x] **Высококачественная база на 141 772 словарных статьи** ([`data/offline_dict.tsv.gz`](file:///home/ai-projects/services/study.qpid.pro/data/offline_dict.tsv.gz), сжатый размер всего **2.48 МБ**):
- Построена генератором [`scripts/build_offline_dict.py`](file:///home/ai-projects/services/study.qpid.pro/scripts/build_offline_dict.py).
- Интегрированы FreeDict TEI XML (54k+ базовых лемм с IPA и частями речи) и очищенный русско-английский Wiktionary DSL (55k+ лемм).
- Включены все формы неправильных глаголов (16k+ форм: 3rd person singular, past, past participle, present participle).
- Включены множественные числа существительных (18k+ форм: regular & irregular plurals, например *men, women, hypotheses, criteria*).
- Включены регулярные инфлексии глаголов (50k+ форм: *-ed, -ing, -s*).
- Добавлен словарь IT и сетевой терминологии Cisco/O'Reilly (*packet, switch, router, throughput, bandwidth, latency, subnet, vlan*).
- [x] **Архитектура Zero Daemon**:
- Нативный SQLite с `PRAGMA mmap_size = 256MB` и `PRAGMA cache_size = -32000` (32MB RAM page cache).
- Никаких внешних сервисов (Redis/Postgres) — чтение за **0.015–0.030 мс** прямо из оперативной памяти ядра Linux.
- Автоматический сидинг базы при инициализации (`_seed_offline_dictionary()` в `app/database.py`) пачками по 10 000 строк занимает < 0.7 секунды.
- [x] **Иерархическая логика словарного поиска** в [`app/dictionary.py`](file:///home/ai-projects/services/study.qpid.pro/app/dictionary.py):
1. `dictionary_cache` $\rightarrow$ мгновенный возврат при повторном запросе.
2. `offline_dictionary` $\rightarrow$ мгновенный ответ за 0.02 мс с источником `source: "offline"`.
3. Только при отсутствии слова в базе (или для длинных предложений > 4 слов) $\rightarrow$ fallback-запрос во внешнюю сеть (Wiktionary REST / MyMemory).
- [x] Исключено исчерпание квот MyMemory (100 запросов/день) — 99%+ запросов обслуживаются локально.
- [x] Добавлен регрессионный юнит-тест `test_offline_dictionary_lookup_and_coverage` в [`tests/test_api.py`](file:///home/ai-projects/services/study.qpid.pro/tests/test_api.py) (25/25 тестов успешно, 100% green).
---
### 12. 🧩 Модульность фронтенда на нативных ES-модулях (без Webpack/Vite) (Completed)
- [x] Перевести [`app/static/js/app.js`](file:///home/ai-projects/services/study.qpid.pro/app/static/js/app.js) на нативный браузерный синтаксис `<script type="module">`.
- [x] Выделить модули с нативным `import/export`:
- `modules/api.js` — централизованный клиент сетевых вызовов с авто-интерцептором токена.
- `modules/ui.js` — тосты, модальные окна, темы оформления, утилиты экранирования и шафла.
- `modules/dictionary.js` — поиск, карточки, коллекции, теги, пакетные операции, модалки редактирования и экспорта.
- `modules/srs.js` — алгоритм SM-2, 5 режимов тренировки (флэшкарты, квиз, тайпинг, клоуз, диктант), статистика KPI и гитхаб-хитмап.
- `modules/reader.js` — книжная полка, читалка, блок-парсер, виртуализация глав, интерактивный попап слова, настройки Aa.
- `app.js` — главный оркестратор, роутер хэшей `#dictionary`, `#srs`, `#reader/book/:id`, префлайт вида без мерцания, мобильный таббар.
- [x] Сохранить полное отсутствие внешних сборщиков (0 npm-пакетов, чистая платформа, нативные ES6 модули браузера).
- [x] Обновлен Service Worker (`sw.js`) с прекэшированием всех модулей и версией кэша `notes-v5.0`.
---
### 13. 🌐 Контекстный перевод предложений в читалке (In-Context Sentence Translator) (Completed)
- [x] Кнопка `🌐 Перевести предложение` в попапе слова читалки (рядом с контекстной цитатой).
- [x] Интеллектуальный перевод всей фразы целиком с сохранением смысла книги через локальный ИИ (устраняет ловушку оторванных значений слов вроде *draft*, *address*, *run*).
- [x] Разбор и точное значение целевого слова именно в данном контексте (`word_meaning`).
- [x] Мгновенный кэш переводов предложений в SQLite (`sentence_translations`) с SHA-256 хешированием за 0.01 мс.
- [x] Возможность сохранить правильный контекстный перевод прямо в карточку словаря («Вставить в карточку»).
- [x] Развёрнут локальный контейнер Ollama с моделями Qwen 2.5 (`qwen2.5:1.5b` и `qwen2.5:3b`) с автоматической выгрузкой из памяти при простое (`OLLAMA_KEEP_ALIVE=5m`) и 100% автономностью без внешних API.
---
### 14. 🔗 Детектор фразовых глаголов и идиом в читалке (Multi-Word / Phrasal Verb Detector) (Completed)
- [x] База частых английских фразовых глаголов и коллокаций (A2–B2: *take off, run out of, look forward to, figure out, set up, carry out, turn off, put up with* и др.).
- [x] При клике на слово в читалке — автоматический анализ контекстного окна (1–3 слова до и после + разделяемые фразовые глаголы *turn it off*).
- [x] При обнаружении устойчивой связки — карточка связки прямо в окне перевода слова (*took off* $\rightarrow$ «взлетать, снимать», а не «взял»).
- [x] Поддержка переключения между одиночным словом и фразовым глаголом в 1 клик.
- [x] Сохранение готовой устойчивой связки в личный словарь в один клик (в отдельную коллекцию «Фразовые глаголы & Идиомы» с тегом `#phrasal-verbs`).
---
### 15. 🎯 Уровневые тесты CEFR (A1, A2, B1, B2) с авто-добавлением ошибок в активный словарь (Completed)
- [x] **Эталонная база вопросов Oxford 3000/5000** (160 отобранных тестовых единиц: по 40 на каждый уровень A1, A2, B1, B2) в модуле [`app/cefr.py`](file:///home/ai-projects/services/study.qpid.pro/app/cefr.py).
- [x] **Быстрый и удобный мобильный формат: 1-тап выбор ключевого узла (Cloze Tap)**:
- Естественные контекстные предложения с пропуском `___`.
- Подсказка значения слова на русском языке и часть речи.
- 4 сбалансированных варианта ответа одинаковой части речи и уровня сложности (исключены грамматические подсказки).
- Мгновенная тактильная реакция: зеленая/красная подсветка, автоподстановка правильного слова в пропуск, раскрытие транскрипции IPA и пояснения.
- [x] **Автоматический двухсторонний захват пробелов (Dual Gap Ingestion)**:
- При ошибке $\rightarrow$ **автоматически в словарь SRS со статусом «В процессе» (`learning`) добавляются ОБА слова**:
1. Истинное целевое слово (правильный ответ).
2. Выбранный дистрактор (слово, на которое кликнул пользователь, с его переводом и транскрипцией).
- Срочный срок повторения (`srs_next_review = now`), коллекция `Уровни CEFR ({level})` и теги `#level-{level}`, `#cefr-test`, `#test-mistake`.
- [x] **Интерактивный разбор всех вариантов (Options Breakdown & 1-Tap Add)**:
- Карточки всех 4 вариантов ответа с переводом из оффлайн-словаря и частью речи.
- Автоматическая маркировка добавленных слов (`✓ В словаре (SRS)` / `✓ В библиотеке`).
- Быстрая кнопка `+ В словарь` для любых оставшихся незнакомых вариантов вопроса в 1 тап.
- [x] **Расширенная база вопросов (Oxford 3000/5000)**:
- Банк расширен со 160 до **260 эталонных вопросов** (по 65 вопросов на каждый уровень A1, A2, B1, B2).
- [x] **Наглядный прогресс-трекер**:
- Экран обзора со шкалами освоения каждого уровня (A1 Beginner, A2 Elementary, B1 Intermediate, B2 Upper-Intermediate).
- Счётчики: процент закрытия уровня, освоено, ошибок, слов в тренировке SRS.
- Режим «Работа над ошибками» (быстрый прогон только ошибочных вопросов).
- Возможность сброса прогресса уровня.
- [x] Добавлен регрессионный тест `test_cefr_testing_and_gap_ingestion` в [`tests/test_api.py`](file:///home/ai-projects/services/study.qpid.pro/tests/test_api.py) (29/29 тестов успешно, 100% green).
---
### 16. 📖 Оффлайн-модуль грамматики Мерфи и бумажная тетрадь (Murphy Engine & Handwritten Journaling)
- [x] Индексация юнитов *Essential Grammar in Use* (A1–A2) и *English Grammar in Use* (B1–B2) в SQLite (`grammar_units`).
- [x] Поддержка «Вечернего вайба глубокого погружения» (Deep Work Mode): 5–7 минутные микро-уроки.
- [x] Компактная выжимка правил на контрастах жизненных ситуаций (*I do* vs *I am doing*, *I did* vs *I have done* и т.д.).
- [x] 5 интерактивных упражнений с удобным 1-тап выбором на телефоне и мгновенным разбором логики ошибок.
- [x] **Интервальное повторение грамматики в SRS**: ошибки из упражнений Мерфи автоматически улетают в карточки утреннего повторения SRS (`srs_state = 'learning'`, теги `grammar`, `murphy-unit-{N}`)!
- [x] **Блок работы с бумажной тетрадью (мышечная и зрительная память)**:
- Золотая формула правила для записи цветной ручкой/маркером.
- Обязательное составление **2–3 личных примеров от руки из своей реальной жизни**.
- Интерактивный чекбокс: `[✓] Записал от руки в тетрадь` с мгновенной синхронизацией в SQLite.
---
### 16.2. ⚡ Фоновые задачи и генерация тестов CEFR из загруженных книг (Background Tasks & Book Mining)
- [x] **Zero-Daemon менеджер фоновых задач (`app/tasks.py`)**:
- Таблица SQLite `background_tasks` (id, user_id, task_type, status, progress_current, progress_total, message, params_json, result_json, error_message).
- Асинхронное исполнение через чистый `asyncio.create_task` без Celery, Redis и внешних брокеров (100% Ponytail).
- Кооперативная отмена задач (`cancel_task`, `is_task_cancelled`).
- Эндпоинты API: `POST /api/tasks/mine-books`, `GET /api/tasks/active`, `GET /api/tasks/{task_id}`, `POST /api/tasks/{task_id}/cancel`.
- [x] **Движок анализа и майнинга предложений из книг (`app/miner.py`)**:
- Разбиение текста глав на чистые предложения с защитой английских инициалов и сокращений (`Mr.`, `Mrs.`, `Dr.`, `Prof.`, `St.`, `Jr.`).
- Сопоставление с лексической шкалой Oxford 3000/5000 (A1..B2) и базой частей речи.
- Подбор грамматически и лексически корректных дистракторов того же уровня CEFR и части речи.
- Формирование 4-вариантных заданий Cloze Tap со скрытием ключевого слова `___`.
- Защита от дубликатов предложений через `UNIQUE(level, sentence)` в SQLite.
- Автоматическая очистка русскоязычного глоссария (`clean_translation`) от паразитных латинских транслитов Викисловаря.
- [x] **Интерфейс в веб-приложении (PWA)**:
- Кнопка `⚡ Собрать тесты из книг` в шапке тестов CEFR.
- Модальное окно с выбором книги (или всей библиотеки), целевого уровня (A1..B2 / ALL) и количества вопросов.
- Интерактивный фоновый баннер со спиннером, живым счетчиком прогресса `X / Y (%)` и кнопкой отмены задачи.
- Бейджик источника `📖 «Название книги» (Глава)` в карточке вопроса и в разборе ответа.
- [x] Автоматический тест `test_background_tasks_and_book_mining` в `tests/test_api.py` (30/30 тестов успешно пройдены).
---
### 🐛 Устранённые дефекты и хотфиксы (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) (Completed)
- [x] **Проблема перегруженного хеддера полностью решена**:
- Ликвидировано нагромождение из 8+ кнопок в шапке, устранён визуальный шум и горизонтальный сдвиг на планшетах и компактных экранах.
- Устранена разрозненность UX: на десктопе, планшете и смартфоне теперь действует единая система навигации.
- [x] **Единый лаконичный Header (Desktop, Tablet, Mobile)**:
- Слева: Кнопка вызова бокового меню (Бургер `☰` / `ph-list`), кликабельный логотип `📖 Notes` (возврат в словарь).
- Центр: Фокусная кнопка быстрого доступа к главному ежедневному действию (`🎓 Тренажёр SRS` с живым счётчиком повторений `srs-badge`) и компактные счетчики базы (`Всего: X`, `Сегодня: +Y`). На смартфонах счетчики автоматически скрываются, а кнопка сжимается до компактного бейджа `🎓 (N)`.
- Справа: Переключатель темы `🌓`, компактная плашка пользователя/аватар `👤 user` (на смартфонах отображается только иконка аватара).
- [x] **Единый выдвижной Drawer (Боковое меню навигации)**:
- Плавная аппаратная анимация (`transform: translateX`, `backdrop-filter: blur(4px)`).
- Карточка профиля с аватаром, именем пользователя и описанием.
- Интерактивный мини-дашборд статистики (Всего в базе, Сегодня, На повторение).
- Логическая группировка пунктов с красивыми иконками:
- **Обучение & Чтение**: Словарь & Поиск, Тренажёр SRS (с живым бейджем), Читалка & Книги, Тесты CEFR (A1–B2), Грамматика Мерфи & Тетрадь.
- **Аналитика**: Календарь активности & Статистика (Heatmap).
- **Настройки & Профиль**: Экспорт словаря, Сменить пароль, Переключение темы, Выход из аккаунта.
- Закрытие по клику вне области (оверлей), кнопке `✕`, клавише <kbd>Esc</kbd> и свайпу влево на тач-устройствах (Swipe to dismiss).
- Блокировка скролла фона (`overflow: hidden` на `body`) при открытом Drawer.
- Подсветка активного раздела (`.active`), синхронизированная с URL-хэшем и переходами между экранами.
- [x] **Оптимизация мобильного режима**:
- На нижней панели (Bottom Tab Bar) оставлены 4 ключевых быстрых действия: `📖 Словарь`, `🎓 Тренажёр`, `📚 Читалка`, `☰ Меню`.
- Тап по `☰ Меню` на мобильном открывает единый Drawer, полностью заменив устаревший отдельный модальный слой `mobile-menu-modal` без дублирования верстки и стилей.
---
### 18. 🎧 Автономная нейро-озвучка Piper TTS и аудирование
- [x] **Локальный движок Piper TTS**:
- Автономный VITS-инференс, 100% оффлайн, ~120 МБ RAM, сверхбыстрый отклик на AMD Ryzen 9 5950X (RTF ~0.11).
- [x] **Качественные модели дикторов**:
- Английский: `en_US-lessac` (чёткий нативный американский диктор).
- Русский: `ru_RU-dmitri` (естественный русский голос).
- [x] **Проактивное фоновое пред-кэширование (Zero Latency)**:
- При сохранении карточки в словарь (`POST /api/cards`) бэкенд в фоне синтезирует слово и всё предложение-контекст.
- В читалке при переводе предложения (`POST /api/translate-sentence`) аудио фразы пред-кэшируется в фоне.
- [x] **Интеграция в интерфейс**:
- Карточки SRS (лицевая сторона 🔊, обратная сторона 🔊, и отдельная кнопка озвучки контекста 🔊).
- Викторина SRS (кнопка 🔊 озвучки слова).
- Читалка (кнопка 🔊 у слова в попапе, 🔊 у всего предложения, и 🔊 RU для русского перевода).
- Словарь (кнопка 🔊 в результатах поиска).
- [x] **Второй этап**: интерактивный тренажёр аудирования (Listening & Q&A) с ситуационными сценариями, вопросами диктора, проверкой понимания на слух и билингвальными транскриптами.
- [x] **Мульти-голосовой переключатель**: 6 моделей Piper TTS (3 EN: Paula, Ryan, Alan; 3 RU: Irina, Denis, Dmitri) с калибровкой темпа и глубоких пауз на тире/знаках препинания.
---
### 19. 🚀 Конструктор комплексного урока & Сессионный раннер (Beta) (Completed)
- [x] **Гибкий конфигуратор структуры урока**:
- Настройка порядка блоков (стрелки ▲/▼), включение/выключение этапов (Toggle-свитчеры), регулировка объёма (5/10/15/20 слов, 3/5/10 упражнений, 1/2/3 аудио-ситуации).
- Готовые встроенные шаблоны в 1 клик:
- ⚡ «Утренний экспресс (10 мин)» (слова + ретро-тест + 1 ситуация на слух).
- 🎯 «Сбалансированный урок (20 мин)» (слова + закрепление + новая теория Мерфи + 2 ситуации).
- 📖 «Глубокое погружение (30 мин)» (полный стек: слова, грамматика, аудирование и чтение статьи).
- 🎧 «Аудио + Чтение (15 мин)» (понимание на слух и работа с текстом).
- Возможность сохранить любой собственный план в базу SQLite под своим именем.
- [x] **Бесшовный сессионный раннер урока**:
- Единый режим урока с пошаговым хлебным прогресс-баром (`[1] Слова ➔ [2] Закрепление ➔ [3] Новая тема ➔ [4] Аудио ➔ [5] Чтение ➔ [6] ИИ-Итог`).
- Таймер сессии, мгновенный автопереход между блоками без кликов по меню.
- Оффлайн-база познавательных микро-статей (Наука, IT, Природа, Космос, История) с кликабельным переводом слов и кнопкой «+ В словарь».
- Умный адаптивный выбор темы грамматики для повторения: «Самая слабая тема (по ошибкам)», «Предыдущая тема» или выбор любого юнита вручную.
- [x] **Сквозная телеметрия ошибок & ИИ-дебрифинг (Qwen 2.5 3B)**:
- Фиксация ошибок на каждом этапе: забытые слова, нарушенные правила Мерфи, пропущенные детали аудирования.
- Обогащение историческим контекстом: сопоставление с прошлыми ошибками в `srs_logs` и `grammar_progress`.
- Итоговый педагогический разбор от Qwen 2.5 3B: итоговая оценка сессии (A+, A, B, C), разбор паттернов ошибок с формулами Мерфи, сильные стороны и персональный фокус на завтра.
- Мгновенный детерминированный fallback: если Ollama занята или выключена, урок завершается моментальным структурированным математическим отчётом без зависаний.
- [x] **Интеграция в интерфейс & тесты**:
- Отдельный пункт в боковом меню `🚀 Комплексный урок (Beta)` и хэш-маршрут `#lesson`.
- Модальное окно истории комплексных уроков с сохранением результатов сессий в `lesson_sessions`.
- 44/44 проходящих автоматизированных теста в `tests/test_api.py`.
---
### 20. 🏗️ Комплексный технический рефакторинг и оптимизация архитектуры (Completed)
- [x] **Этап 1: Очистка мертвого кода и оптимизация SQL**:
- Удалена старая незавершенная функция `parse_text` в `app/reader.py` (строки 290–326).
- Функция `update_task_progress` в `app/tasks.py` переписана с цикла на единый параметризованный SQL-запрос `UPDATE tasks SET progress = ?, current_item = ?, ...`.
- [x] **Этап 2: Вынос датасетов в JSON (`data/seeds/*.json`)**:
- Из исходников извлечено более 3 100 строк статических данных: `cefr_questions.json` (260 вопросов), `grammar_units.json` (15 юнитов), `phrasal_verbs.json` (157 глаголов), `reading_articles.json` (8 статей).
- Модули `app/cefr.py`, `app/grammar.py`, `app/phrasal.py`, `app/lessons.py` переведены на динамическую ленивую загрузку с сохранением полной обратной совместимости.
- [x] **Этап 3: Оптимизация сети и локального ИИ**:
- Внедрен постоянный пул соединений `httpx.AsyncClient` с Keep-Alive (120s) для локальной Ollama в `app/ai.py`, управляемый в жизненном цикле приложения (`lifespan`).
- Интегрирована встроенная офлайн-лемматизация (`lemmatize_word`) в `lookup_term` (`app/dictionary.py`), предотвращающая лишние сетевые запросы при поиске словоформ (`starts` -> `start`, `studying` -> `study`).
- [x] **Этап 4: Модуляризация бэкенда FastAPI**:
- Монолитный `app/main.py` сокращен с 2 240 до 142 строк.
- Создан пакет `app/routers/` с 11 тематическими APIRouter: `auth.py`, `cards.py`, `dictionary.py`, `srs.py`, `export.py`, `reader.py`, `cefr.py`, `grammar.py`, `tasks.py`, `lessons.py`, `listening.py`.
- [x] **Этап 5: Модуляризация CSS и Service Worker**:
- Монолитный `style.css` (8 746 строк) разделен на 7 предметных таблиц в `app/static/css/`: `base.css`, `components.css`, `dictionary.css`, `srs.css`, `reader.css`, `grammar_cefr.css`, `lesson.css`.
- Мастер-файл `style.css` загружает стили через `@import` и содержит полную архитектурную карту.
- Обновлен Service Worker (`notes-v6.7`) с добавлением всех CSS в `PRECACHE_URLS` для полной офлайн-работы PWA.
- [x] **Инфраструктура быстрого переключения и отката**:
- Добавлены скрипты `scripts/deploy_beta.sh` и `scripts/rollback_to_main.sh` для мгновенного отката или развертывания в 1 клик.
- Создан резервный бэкап базы данных `data/notes.db.backup-main-pre-beta`.
- [x] **100% успешное тестирование**:
- Все 44 интеграционных теста `pytest tests/test_api.py` успешно проходят в Docker-контейнере.
---
## 🐛 Зафиксированные баги (Ждут команды на исправление)
- [ ] **BUG-01: Не открывается окно редактирования слова в словаре на главной**:
- **Симптом**: при клике на иконку ✏️ у карточки слова в списке словаря ничего не происходит.
- **Диагностика**: в `app/static/js/modules/dictionary.js` функция `openEditModal` ищет элемент по id `'edit-card-modal'` (`document.getElementById('edit-card-modal')`), в то время как в `app/static/index.html` модальное окно объявлено с id `'edit-modal'` (`<div id="edit-modal" class="modal-backdrop hidden">`). Из-за несовпадения ID селектор возвращает `null`, и вызов `openModal(null)` тихо игнорируется.
- **Статус**: зафиксировано, ожидает команды на исправление.
- [ ] **BUG-02: Отсутствует возможность прослушать слово и предложение из словаря**:
- **Симптом**: в списке карточек словаря на главной странице нет кнопок 🔊 для воспроизведения аудио слова и контекстного предложения.
- **Диагностика**: в шаблоне рендеринга `renderCardsList` (`app/static/js/modules/dictionary.js`) внутри `.card-top` и `.card-context` не отрисовываются кнопки аудиовызова с вызовом `playAudio()` / `playTextSpeech()`, хотя модуль `ui.js` полностью поддерживает их воспроизведение.
- **Статус**: зафиксировано, ожидает команды на исправление.
- [ ] **BUG-03: Спойлер корня слова в переводе грамматических форм в тестах (например, `(прош. вр. от explain)`)**:
- **Симптом**: при добавлении словоформ в словарь (например, `explained`) в переводе сохраняются пометки словообразования: `объяснять, объяснить (прош. вр. от explain)`. Во время теста (SRS / Урок) отображение этого перевода напрямую спойлерит английское слово `explain`.
- **Диагностика**: в `app/dictionary.py` функция `clean_russian_translation` (строки 235–277) намеренно защищает грамматические пометки вида `(прош. вр. от ...)`, и они попадают в поле `card.translation`. Во время тестирования в `srs.js` и `lesson.js` эта строка выводится в вопросе теста целиком без фильтрации.
- **Варианты решения**: по умолчанию скрывать пометку под интерактивный кликабельный спойлер `[+ грамматика]` или вырезать английский корень из вопроса теста через regex `/\s*\([^)]*от\s+[a-zA-Z\x27-]+\)/gi`.
- **Статус**: зафиксировано, ожидает команды на исправление.
- [ ] **BUG-04: В квизе «4 варианта» правильный ответ всегда находится на 1-м месте**:
- **Симптом**: в комплексном уроке и SRS при выборе режима «4 варианта» правильный ответ всегда гарантированно оказывается первым вариантом.
- **Диагностика**: в `app/static/js/modules/ui.js` (строка 28) функция `shuffleArray(array)` возвращает новый скопированный массив (`return arr;`), не мутируя оригинал на месте. В `app/static/js/modules/lesson.js` (строка 778) и `app/static/js/modules/srs.js` (строка 461) написано `shuffleArray(options);`, но результат возврата игнорируется (`options` не перезаписывается), из-за чего правильный ответ, добавленный первым в `[correctText, ...]`, всегда остаётся на позиции 0.
- **Варианты решения**: переписать вызовы на `options = shuffleArray(options);` (или сделать `shuffleArray` мутирующим).
- **Статус**: зафиксировано, ожидает команды на исправление.
- [ ] **BUG-05: Поломка верстки и отображение сырых HTML-тегов/JSON при импорте статей Википедии (например, Linux)**:
- **Симптом**: при открытии импортированной из Википедии статьи «Linux» в читалке в 1-й главе («Overview») отображается фрагмент сырого JSON (`"},"title":{"wt":"Linux"}...`), а в главе «Pronunciation» — искажённый тег `<tdTimes New Roman...` и сырые теги цитат `{{ cite newsgroup...`.
- **Диагностика**:
1. В `app/reader.py` (`clean_wiki_html`): MediaWiki Parsoid HTML вставляет метаданные шаблонов в атрибут `data-mw='{"parts":...}'` (например, у `<span class="mw-empty-elt">` и `<table class="infobox">`). Внутри значения этого атрибута содержатся незаэкранированные угловые скобки (например, `<ref name="...">`). Регулярное выражение удаления HTML-тегов `re.sub(r'<[^>]+>', ' ', s)` встречает первый же символ `>` внутри кавычек атрибута, преждевременно обрывает удаление тега, и остаток JSON-строки вываливается в текст статьи.
2. В `clean_data_table` regex удаления стилей `style=["'][^"']*["']` спотыкается о вложенные одинарные кавычки в `style="font-family:'Times New Roman'..."`, оставляя сломанный HTML `<tdTimes New Roman...`.
3. В `app/static/js/modules/reader.js` повреждённые теги не распознаются парсером таблиц и выводятся как обычный экранированный текст.
- **Варианты решения**: предварительно полностью вырезать атрибуты `data-mw` и `about` регулярным выражением или через санитизацию до удаления тегов, улучшить regex очистки стилей таблиц и перезагрузить повреждённые статьи.
- **Статус**: зафиксировано, ожидает команды на исправление.
- [ ] **BUG-06: Не работает поиск и обратный перевод с русского на английский в словаре**:
- **Симптом**: при попытке ввести в поле перевода словаря русское слово (например, «собака» или «яблоко») перевод не находится, возвращается пустой результат или ошибка.
- **Диагностика**:
1. В `app/dictionary.py` функция `lookup_term` жестко закодирована только под направление «English -> Russian»:
- В локальной БД поиск ведется только по полю английского слова `WHERE term = ? COLLATE NOCASE` (русские переводы хранятся в поле `translation` и по ним поиск не производится).
- Во внешнем сервисе `fetch_mymemory` жестко зашит параметр `langpair="en|ru"`, из-за чего сервис пытается переводить русское слово как английское и возвращает ошибку или пустую строку.
- Запросы к Wiktionary и FreeDictionaryAPI идут в англоязычные эндпоинты (`en.wiktionary.org`, `api.dictionaryapi.dev`), где нет русских словарных статей.
2. В интерфейсе [`app/static/js/modules/dictionary.js`](file:///home/ai-projects/services/study.qpid.pro/app/static/js/modules/dictionary.js) результат выводится в поле карточки без учёта обратного направления перевода (когда найденный английский эквивалент должен стать `term`, а введенное русское слово — `translation`).
- **Варианты решения**: автоматически определять язык ввода по наличию кириллицы `[\u0400-\u04FF]`. При кириллице: переключать `langpair` MyMemory на `"ru|en"`, выполнять обратный поиск в `offline_dictionary` по `translation`, и корректно заполнять форму добавления карточки (найденное английское слово в `term`, русский запрос в `translation`).
- **Статус**: зафиксировано, ожидает команды на исправление.
---
## ⏳ Coming Soon
- [ ] **🌐 Быстрый захват из браузера (Bookmarklet)**:
- Создать JavaScript-букмарклет для добавления слова/фразы из любого окна браузера в личный словарь [`study.qpid.pro`](https://study.qpid.pro) (низший приоритет).
- [ ] **Нативная автономная озвучка (HTML5 Web Speech API / `speechSynthesis`)**:
- Использовать встроенный в браузер синтезатор речи `window.speechSynthesis` (en-US, en-GB) как дополнительный резервный фолбэк при работе на мобильных без связи.
- [ ] **Расширение библиотеки Murphy**:
- Добавление следующих 15 юнитов грамматики (юниты 16–30: пассивный залог, косвенная речь, модальные глаголы).
---
## 🛠️ Правила и соглашения проекта (Ponytail Rules)
1. **Stdlib & Native First**: стандартная библиотека языка и возможности платформы (браузера, SQLite, Linux) всегда приоритетнее внешних библиотек и микросервисов.
2. **Меньше кода — меньше багов**: удаление кода и упрощение структуры всегда лучше усложнения.
3. **Безопасность**: все мутации данных изолированы по `user_id`, пароли защищены PBKDF2 с уникальной солью.
4. **Тестирование**: после каждого изменения запуск `pytest tests/test_api.py -v` (100% тестов должны быть зелеными).