# 📋 Задачи проекта: 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. `) списков в `- ` / `
- `.
- Преобразование цитат (`> `) в `
` с акцентной левой границей.
- **Слова внутри списков и цитат остаются кликабельными** (интерактивный клик-перевод работает!).
- [x] **Заголовки Markdown (`##`, `###`)**:
- Конвертация в ``, `` с удалением сырых решёток `#`.
- [x] **Точное позиционирование окна перевода у курсора/слова (Popup Positioning)**:
- Исправлен баг двойного смещения координат: убран `window.scrollY` при `position: fixed`.
- Окно позиционируется точно под кликнутым словом (`rect.bottom + 8`) либо над ним (`rect.top - popupHeight - 8`), если снизу недостаточно места.
- Окно центрируется относительно кликнутого слова с проверкой границ вьюпорта (отступы минимум 12px от краёв).
- [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-code-block`, `.reader-inline-code`.
---
### 2. 🗜️ Сжатие статического трафика (`GZipMiddleware`)
- [ ] Подключить `GZipMiddleware(minimum_size=1000)` в [`app/main.py`](file:///home/ai-projects/services/study.qpid.pro/app/main.py).
- [ ] Сократить объём передаваемых файлов `app.js` (95 КБ), `index.html` (56 КБ) и `style.css` (25 КБ) на 70–75% (суммарно со 176 КБ до ~40 КБ).
- [ ] Проверить заголовки `Content-Encoding: gzip` в ответах.
---
### 3. 🔌 Пул соединений 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 мс.
---
### 4. 🗄️ Тюнинг 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
```
- Добавить составной индекс:
```sql
CREATE INDEX IF NOT EXISTS idx_vocab_user_term_nocase
ON vocabulary_cards(user_id, term COLLATE NOCASE);
```
- [ ] Обеспечить Zero-copy чтение страниц БД ядром Linux и мгновенный поиск дубликатов по пользователю за $O(\log N)$.
---
### 5. ✂️ Де-блоатинг внешних парсеров (YAGNI)
- [ ] Удалить лишний сетевой опрос Datamuse (`fetch_datamuse`) — он дублирует определения и перегружает ответ нерелевантными рифмами.
- [ ] Упростить хрупкий regex-парсинг разметки Wikitext, опираясь на стабильные REST JSON эндпоинты.
---
### 6. 📚 Локальный оффлайн-словарь (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).
---
### 7. 🧩 Модульность фронтенда на нативных ES-модулях (без Webpack/Vite)
- [ ] Перевести [`app/static/js/app.js`](file:///home/ai-projects/services/study.qpid.pro/app/static/js/app.js) на нативный браузерный синтаксис `