# 📋 Задачи проекта: study.qpid.pro (Backlog & Execution Queue) Список задач для последовательной реализации в рамках принципов **Ponytail** (максимальная надёжность, нативные средства платформы, zero-dependency, высокая скорость). --- ## 🚀 Очередь реализации (Пошаговый план) ### 1. 📑 Полноценный рендеринг Markdown (.md) в читалке - [x] **Блоки кода (Fenced Code Blocks ```` ```language ````)**: - Выделение блоков кода в контейнер `
`.
  - Моноширинный шрифт (`JetBrains Mono`, `Consolas`), сохранение отступов и табуляции (`white-space: pre`).
  - Тёмная подложка, горизонтальный скролл при переполнении, отключение красной строки (`text-indent: 0`).
  - Кнопка «Копировать код» в шапке блока.
  - Отключение создания интерактивных словарных спанов `` для кода, чтобы синтаксис не ломал форматирование.
- [x] **Инлайн-код (`` `variable` ``)**:
  - Оборачивание в нативный тег `` с бейджик-стилем.
- [x] **Списки и цитаты**:
  - Преобразование маркированных (`- `, `* `) и нумерованных (`1. `) списков в `
  • ` / `
    1. `. - Преобразование цитат (`> `) в `
      ` с акцентной левой границей. - **Слова внутри списков и цитат остаются кликабельными** (интерактивный клик-перевод работает!). - [x] **Заголовки Markdown (`##`, `###`)**: - Конвертация в `

      `, `

      ` с удалением сырых решёток `#`. - [x] **Таблицы Markdown (`| col | col |`)**: - Адаптивные таблицы с рамками, стилизацией заголовков ``, зебра-подсветкой строк и горизонтальным скроллом на мобильных. - Каждая ячейка поддерживает интерактивные слова для изучения и инлайн-код. - [x] **HTML-блоки и теги в Markdown (`

      `, `

      `, ``, ``, ``, `
      `)**: - Нативный парсинг через встроенный `DOMParser` без внешних библиотек. - Устранена утечка сырых тегов (``, ``, `

      `) в словарные слова. - [x] **Ссылки и изображения (`[text](url)`, `![alt](url)`, ``)**: - Поддержка бейджей shields.io, тренд-графиков и внешних картинок. - Автоматическое скрытие битых/отсутствующих локальных путей (`assets/...`) через обработчик `onerror`. - [x] **Точное позиционирование окна перевода у курсора/слова (Popup Positioning)**: - Исправлен баг двойного смещения координат: убран `window.scrollY` при `position: fixed`. - Окно перенесено в корень `` для исключения искажений контекста позиционирования. - Плавное отслеживание скролла через `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) - [ ] **Проверка дубликатов на бэкенде (`POST /api/cards`)**: - Проверять наличие слова для пользователя без учёта регистра: `lower(trim(term))`. - При попытке повторного добавления: не создавать дубликат-клон, а дополнять контекстное предложение (если в новой цитате другой контекст) и объединять теги книги (`tags`), возвращая существующую карточку со статусом `already_exists`. - [ ] **Индикация в читалке (Reader Popup)**: - При клике на слово проверять, есть ли уже `cleanTerm` в словаре пользователя. - Если слово уже сохранено: показывать зелёный бейдж `✓ В словаре` вместо активной кнопки `+ В словарь` (предотвращая случайные повторные клики). - Предоставить кнопку «Добавить контекст» для привязки новой цитаты к существующей карточке. - [ ] **Индекс целостности в SQLite**: - Добавить `CREATE INDEX IF NOT EXISTS idx_vocab_user_term_nocase ON vocabulary_cards(user_id, term COLLATE NOCASE);` для мгновенного поиска за $O(\log N)$. - [ ] **Слияние существующих дубликатов в базе**: - Написать безопасную миграцию/скрипт, объединяющий накопившиеся дубликаты (например, `admired`, `suddenly`/`Suddenly` у пользователя `kopri`), объединяя контексты предложений и сохраняя историю повторений SRS. --- ### 4. 🔌 Пул соединений HTTP (`httpx.AsyncClient` с Keep-Alive) - [ ] В [`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) ``` - [ ] Заменить вызовы `async with httpx.AsyncClient()` в парсерах на переиспользуемый клиент. - [ ] Устранить 5 повторных TCP/TLS хэндшейков на каждое слово, сократив пинг внешних запросов на 300–600 мс. --- ### 5. 🗄️ Тюнинг SQLite PRAGMA и составных индексов - [ ] В [`app/database.py`](file:///home/ai-projects/services/study.qpid.pro/app/database.py): - Вынести `PRAGMA journal_mode=WAL;` в `init_db()` (убрать избыточный вызов при каждом запросе). - Включить параметры производительности памяти Linux: ```python conn.execute("PRAGMA synchronous = NORMAL;") conn.execute("PRAGMA mmap_size = 268435456;") # 256MB mmap conn.execute("PRAGMA cache_size = -32000;") # 32MB cache ``` - [ ] Обеспечить Zero-copy чтение страниц БД ядром Linux. --- ### 6. ✂️ Де-блоатинг внешних парсеров (YAGNI) - [ ] Удалить лишний сетевой опрос Datamuse (`fetch_datamuse`) — он дублирует определения и перегружает ответ нерелевантными рифмами. - [ ] Упростить хрупкий regex-парсинг разметки Wikitext, опираясь на стабильные REST JSON эндпоинты. --- ### 7. 📚 Локальный оффлайн-словарь (99%+ покрытия со словоформами, SQLite 10–20 МБ) - [ ] Создать в 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); ``` - [ ] Сформировать и интегрировать базу на 120 000–180 000 статей (~10–20 МБ в SQLite, сжатый архив ~3.5–5 МБ): - Включить все словоформы (past tense, irregular verbs, plurals, participles). - Включить фразовые глаголы, современный сленг и IT/Tech терминологию. - [ ] Использовать архитектуру **Zero Daemon**: нативный SQLite с `PRAGMA mmap_size` (без отдельного контейнера Redis, чтение < 0.1 мс прямо из оперативной памяти процесса). - [ ] Реализовать логику словарного поиска: 1. Сначала запрос в `offline_dictionary` $\rightarrow$ мгновенный ответ за 0.1 мс. 2. Только при отсутствии слова в базе $\rightarrow$ fallback-запрос во внешнюю сеть. - [ ] Полностью исключить исчерпание лимитов внешних API (MyMemory 100 req/day). --- ### 8. 🧩 Модульность фронтенда на нативных ES-модулях (без Webpack/Vite) - [ ] Перевести [`app/static/js/app.js`](file:///home/ai-projects/services/study.qpid.pro/app/static/js/app.js) на нативный браузерный синтаксис `